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.
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.
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.
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:
!!!info 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
app.cpp/h
These files include the main initialisation functions, event queue processing, and some ISR callbacks.audio.cpp/h
These handle audio related functionality including sample marshalling, updating the stack audio service, and the main 8kHz tick ISR callback (Generated from the SAI FS clock edge).stm32f4xx_avb_ethernet.cpp/h
These files contain routines to read incoming packets, send packets, and handle timestamps.StackCallbacks.cpp
This file contains all the stack callback functions that the application requires, such as packet sending, fetching the device MAC address, and processing AVDECC controls.console.cpp/h
This adds a UART output fro printing debug information from the stack and application.syscallscpp.cpp
This provides implementations of new, new[], delete, delete[], malloc and free that allocate memory from within the FreeRTOS heap.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.
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:
libstack.a
The library itselfTSNStack.h
The TSN stack public interfaceAVBPlatformSupport.h
Platform-specific macros and functionsethTypes.h
Ethernet-related type definitions used by the stack public interfacestackTypes.h
Various types used by the stack public interfaceIn 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.
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()
))
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.
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.
For more Sienda TSN Stack documentation, including API reference and a programming guide, please visit the stack documentation.