Sienda TSN Stack ARM M4 Reference Project

Updated March 2021. Content subject to change. Copyright (c) 2021 Sienda Multimedia Ltd. All rights reserved.

Introduction

This document describes the Sienda TSN stack ARM M4 reference project that runs on an STM32F429ZI Nucleo-144.

Read this guide with the souce code to hand in order to understand how the system works and how to interface with the TSN stack. The getting started guide for this project can be found here.

It is also recommended to read the Sienda Stack Architecture Guide in order to understand the basic requirements for AVB endpoint hardware, and how the stack is used.

Architecture Overview

Below we will talk about the audio and PTP timing that drives the system, followed by the application tasks and how they interact. This should provide a high level understanding of how the application runs before we look at the how the ethernet packets and audio samples are handled.

Timing

Audio and ‘system’ time

It is generally recommended to drive the system from the audio clock because this allows the entire system to be synchronised with the AVB network clock and makes it easy to synchronously update the SAI DMA buffers. This reference design does not implement clock recovery because the Nucleo does not feature clock recovery hardware.

This system is driven by the SAI FS clock (Serial Audio Interface Frame Select Clock). As shown on the diagram below, this 48kHz clock is manually wired to the timer 1 ETR pin via the jumper wire between pins PE4 and PE7.

Timer 1 is configured to count up to 6 using ETR as its clock, and then reset the counter and trigger an update event. This update event will happen at 8kHz (48,000 / 6).

The update event is internally connected to timer 2’s ITR0 input. Timer 2 is configured to capture its value on a rising edge of ITR0 and generate an interrupt. In this interrupt, we read the captured value of timer 2 and use it to update the global ‘system time’, and release all RTOS tasks. This means that packet reception, audio handling and all other application responsibilities are done every 125uS.

gPTP time

Another time critical part of the system is gPTP. Audio streams require accurate gPTP time for the system to work. In this design, this means that the timer in the ETH peripheral must be synchronised with the ‘system time’ described above. Here’s how it’s done:

ⓘ The Sienda TSN Stack maintains gPTP time internally in software. It provides a ‘virtual’ gPTP clock, and so doesn’t require a hardware gPTP clock.

The STM32F429’s ETH peripheral contains a timer that is driven by the 180MHz system clock and is used for timestamping gPTP packets. The peripheral allows the application to set a value and trigger an interrupt when the time matches that value. This interrupt is also connected internally to timer 2’s ITR1 input, which allows the application to capture the value of timer 2 at exactly the point that the ETH peripheral’s timer reaches the desired value.

During start-up, this project does exactly this, calculates the difference, and adds the offset to the ETH timer to ensure they are in sync. Since the ETH timer and timer 2 are both driven by the same system clock, they will continue to be in sync.

Tasks

This example project uses FreeRTOS for tasks, semaphores, and an event queue. The important tasks are listed below. All tasks are released by semaphore in the ISR generated by the FS clock at 8kHz.

AVBStackTask (Priority normal)

The task in which the stack is created and initialised. There is a small run loop in this task to service the main event queue. This includes processing non-priority ethernet packets, timer elapsed events, PTP timestamps, and ethernet link status changes.

packetHandlerTask (Priority above normal)

This task polls the incoming ethernet descriptors for priority packets, passing them immediately to the stack. Other packets will be added to the event queue for processing in AVBStackTask.

audioTask (Priority high)

This task contains a loop that processes audio samples every system ‘tick’ (8kHz). It is responsible for updating the stack stream read / write heads by calling setPlayheadPosition and transmitOpportunity. Any required DSP on incoming or outgoing samples can happen here.

Other tasks (Priority below normal / low)

The reference design implements a console task for UART communication, and an idle task for measuring CPU usage, but these are not critical to the implementation.

Processing packets and audio data

Ethernet packets

processPriorityPacket is called from the packet handler task as soon as a packet is received by the application. This allows the stack to process high priority packets immediately. If it returns false, the packet was not priority and therefore not processed, and is placed in the event queue to be processed later. The event queue is processed in the AVBStack task, where packets are passed to the stack using processPacket. This allows the stack to process non-priority TSN / AVB traffic including AVDECC, MAAP, MSRP, and PTP traffic. If this function returns false, the packet was not processed and is not required by the stack.

If a PTP timestamp is captured by the hardware, either for outgoing or incoming PTP packets, it is added to the event queue in the packet handler task, and subsequently passed to the stack using processPTPTimestamp in the AVBStack task.

Audio

The stack is provided 2 audio buffers by the application - the mix buffer, where the stack puts audio data received over the network, and the rec buffer, where the application puts captured or generated audio data ready for transmission over the network. These are passed to the stack through the init function.

Talker stream

In this project, the talker stream sends a sine wave. The sample data for this sine wave is manually put in the rec buffer in the audio task. transmitOpportunity is then called immediately afterwards. It informs the stack of the latest audio stream position and at what time the sample pointed to by the stream position was generated. It also gives the stack the opportunity to transmit this data across the network.

Listener stream

The SAI is configured to transmit two channels of 32-bit audio over I2S, using circular DMA. While one side of the DMA buffer is being clocked out the I2S data line, the other side is being filled manually from the mix buffer in the audio task. setPlayheadPosition is then called to inform the stack of the current playhead position in the audio output stream and the time that the sample at the set playhead position is expected to be played out.

Visual Studio Project

Overview

This project is a VisualGDB CMake project generated by CubeMX and VisualGDB.

You can view the STM32F4 configuration by opening arm0m4-ep.ioc with STM32CubeMX. The Visual Studio solution file is arm-m4-ep.sln.

The source files (in the Src folder) are all generated by CubeMX, with the exception of:

Generated code Modifications

The following modifications are required to the HAL files generated by CubeMX in order to work with this project. Please bear this in mind if re-generating the project files.

Using the stack library

Files

This project uses the Sienda TSN stack compiled as a shared library. All the files required to use it are included in the ‘stack’ folder:

Entity Configuration

In this project, the stack is configured with binary data found in the file entity.c in the ‘Resources’ folder. In this folder you will also find an XML file. This XML file contains a human-readable configuration, which is converted into a binary, and then into a C file, automatically in the pre-build steps configured in the Visual GDB project. The pre-buid steps use the ‘entityConvert’ and ‘Bin2C executables’ in this folder to do this. To make changes to the configuration, simply change the XML and rebuild the project. The stack is initialised with a BinaryConfigLoader.

Stack Initalisation

The following code shows how the stack is created and initialised in the project’s ‘app.cpp’. See the Sienda Stack Programming Guide for more information.

pStack = TSNStack::newInstance();

BinaryConfigLoader* loader = pStack->createBinaryConfigLoader((uint8_t*)_acentity, sizeof(_acentity));

pStack->init(
        (IConfigLoader*)loader,
        (IPersister*)nullptr,
        getMixBuf(),
        getMixBufNbChannels(),
        getMixBufNbFrames(),
        getRecBuf(),
        getRecBufNbChannels(),
        getRecBufNbFrames()
        ))

Stack callbacks

The TSN Stack needs to call into the application to do certain things such as send a packet, get the device MAC address, or set a timer. The file stackCallbacks.cpp in the ‘Src’ folder contains just the functions required for this applciation. All callback functions are weakly declared in the stack.

See the stack callbacks API For a full list of functions.

Other stack functions

Most of the critical stack functions have been mentioned in this guide already, but here are others that are used in the application:

timerElapsed is used in conjunction with the appSetTimer callback to allow the stack to set a timer using the application hardware, and be informed of it elapsing.

setEthernetLinkStatus informs the stack of a change to the ethernet link status.

Further reading

For more Sienda TSN Stack documentation, including API reference and a programming guide, please visit the stack documentation.