Getting started with Tracealyzer for FreeRTOS on Xilinx Zynq
This article shows you how to understand what your real-time system is really doing by adding trace capabilities to a Xilinx Zynq-7000 example project. The trace will then be analyzed using Percepio’s Tracealyzer.
The starting point is a Xilinx Zynq-7000 All Programmable SoC ZC702 Evaluation Kit, a board populated with a XC7Z020 CLG484 -1 AP SoC. The board also boasts 1GB of RAM, so it is possible to add a large trace buffer onboard.
Since this is a programmable logic device you can actually do much more, but for this article we’ll only look at programming the system already programmed onto the device. The starting point I used is a demo for FreeRTOS. This is part of the normal FreeRTOS distribution, in this case I used v9.0 of FreeRTOS.
Before I start modifying the example, I make sure I can download and debug the prewritten example. This is done following the instructions found on FreeRTOS. After compiling, setting up the debug session and starting it, I get the anticipated behavior in a terminal window. I see the prompt and after playing around here I get an output similar to this:
The Xilinx Software Development Kit is based on Eclipse, so many of the necessary steps are self-explanatory. However the following are the steps I need to take to enable trace recording in the existing system:
Decide which recorder to use.
As I do not have a J-Link or a working TCP/IP session running in the example, I select the Snapshot recorder. While the snapshot recorder requires only a few kilobytes of RAM for the buffer, with 1GB of RAM I can keep the onboard buffer large enough to capture a sizable trace.
Add recorder source code to the project
As this is an Eclipse based IDE, I need only to add the recorder source code files to the existing project files and they will be included in the build process. I add a folder named ‘Trace’ under the ‘src’ folder and in this empty folder I copy all the source code from the Tracealyzer recorder library.
Adding search path to compiler settings
The compiler needs to be informed where header files are located, this needs to be done in the projects compiler settings:
Adjust trace settings
In the file trcConfig.h a few settings need to be considered. I set a fitting value to
This will make sure that time-stamping of traces is done in an efficient way for this device.
In this file a lot of values can be configured depending on the systems complexity, such as number of tasks, number of ISRs and mutexes. Another thing that is configurable using this file is the number of events to keep in the trace buffer. Here I decide to use a large 100000 event buffer by changing the value of EVENT_BUFFER_SIZE to 100000.
Make sure the trace capabilities are enabled in the RTOS
In the file FreeRTOSConfig.h I verify that the trace hooks are enabled, and add an include statement so that the trace macros defined in FreeRTOS are populated by functions in the Tracealyzer trace recorder.
#define configUSE_TRACE_FACILITY 1
At the very end of the file add the statement:
Call Trace initialization and start the trace
A call to the trace data initialization function vTraceInitTraceData() must be added before any functionality will try to use the data buffer. Often it is best placed after initialization of the system clock, but before any RTOS API calls are done.
Recording of trace data is started by a call to uiTraceStart(), this should be placed before the part of the system you want to investigate. For this demo I place the call before starting the RTOS. Usually both these places are found in the main()function of the system.
Cortex A9 Peripheral Base Address
On this family of devices, the base address of private memory needs to be defined, this is done in the file trcKernelPort.h,
#define CA9_MPCORE_PERIPHERAL_BASE_ADDRESS 0xF8F00000
The exact value here depends on the implementation.
Debugging and extracting trace data
The project will hopefully now build, so build download and start execution. Since the extra overhead introduced by the recorder code is often negligible, you will probably not notice any difference until you extract the trace data from the system.
After the software has executed past the points you wish to analyze, halt execution. Dumping the content of memory to a file is possible using a menu item in the Xilinx IDE.Search for Xilinx Tools -> Dump/Restore Data File. In the menu, select the core you are running the RTOS on, Processor -> Select -> Name=Xilinx Hardware ->APU -> ARM Cortex-A9 MPCore #0 in my case. File Location -> Select where to store the file.
To find the start address, the easiest way is to select the entire RAM. This could be perceived as wasteful, so let us narrow the range down slightly. Open the map-file, it is available from the C/C++ view, under Project explorer -> [Project Name] -> Debug -> [output name].map
The memory selected must include the entire structure RecorderData, but it can be larger than that, as Tracealyzer will find the structure by searching start and end markers. In my case the location is 0x1452dc and the size is 0x62b8c. I select to dump memory from 0x145000 and dump 0x65000 bytes. It should cover the buffer area even if it grows a bit.
Here Tracealyzer will warn you if any of the Ndefines in trcConfig.h is too small, so follow the advice in the file:
“It can be wise to start with large values for these constants, unless you are very confident on these numbers. Then do a recording and check the actual usage by selecting View menu -> Trace Details -> Resource Usage -> Object Table.”
Once you have saved the file you can start to analyze it in Tracealyzer. File -> Open. The result should be something similar to this view:
To learn what you really can do with the trace, look at our other technical blogs, or download an evaluation of Tracealyzer.
Tracealyzer comes with a prebuilt data sample that you can experiment with to understand what Tracealyzer can reveal from a run using an RTOS.