The easiest way to stream Tracealyzer traces from an RTOS is often via a SEGGER J-Link debug probe, using the RTT feature. This relies on a RAM buffer that is read by the J-Link probe while the target system is executing. This is usually fast and reliable, but in case you encounter issues, please check the following.

1. General Things to Check

1.1. Have you integrated the trace recorder library properly, including the extra steps needed to for streaming? This is described in the User Manual, under Creating and Loading Traces. For the FreeRTOS version, see below documents.

Quick Start Guide for FreeRTOS users

– User Manual: FreeRTOS/Integrating the Recorder

– User Manual: FreeRTOS/Setting up Streaming

1.2. Install the latest J-Link driver. The driver that comes with Tracealyzer or your IDE is typically not the very latest version, as SEGGER makes frequent updates. It is important that the driver in your IDE and the driver in Tracealyzer are of the same version, if the tools are used in parallel.

1.3. Make sure Tracealyzer “PSF Streaming Settings” (see File -> Settings) is matching your recorder configuration.

– If using vTraceEnable(TRC_INIT), make sure that the checkboxes “Target Starts Tracing” and “Reset Target on Connect” are UNCHECKED.

– If using vTraceEnable(TRC_START), make sure that “Target Starts Tracing” is CHECKED.

1.4. In Tracealyzer, check that the J-Link Settings (File -> Settings) matches the debugger settings in your IDE regarding the J-Link speed and protocol (JTAG/SWD).

1.5. Check that your trcStreamingPort.h really is the version intended for J-Link streaming (i.e. from the /streamport/JLink_RTT folder).

1.6. Check that all .c and .h files in your trace recorder library are of the same version. This may occur if the recorder code has been upgraded but some files were missed, e.g. the stream port files or the config files. The recorder might still compile, but is perhaps not working properly.

2. In case no data is received

2.1. If the Live Stream window does not receive any data or reports an error after you selected “Start Recording”, the problem is most likely that the J-Link driver is not able to auto-detect the address of the RTT Control Block. To check this, open the J-Link Control Panel. On Windows, this is found among the task bar icons (a green icon with a “J”). Then open the “RTT” page, where you can find the “Status” field.

The Status field should read “Located RTT control block“, as shown above. In case the Status field reads “RTT handled by other J-Link instance“, locate the other instance of the J-Link Control Panel and check the Status field there instead.

In case the Status reads “Looking for RTT CB @…” it means that the J-Link driver has not been able to auto-detect the location of the RTT control block. In that case, look up the address to the _SEGGER_RTT symbol. This can be done in your IDE by adding “&_SEGGER_RTT” in a watch/expression, or simply add a print statement that outputs the address to a serial console.

Enter this address into the J-Link Control Panel on the RTT page (“Control block adress”) and press “Start”, alternatively in Tracealyzer PSF Streaming Settings. Reconnect. Now it should establish the connection.

This however requires that you specify the exact address of _SEGGER_RTT. Thus, in case this symbol would move in between builds, this must be updated.

A better solution is to instruct your toolchain to place the _SEGGER_RTT data in a memory region where the J-Link driver will find it automatically. You can see the address range that is scanned by the J-Link driver in the J-Link Control Panel (“RTT” page, “Status” field). In the current version the J-Link driver, the text field is however too narrow to show the whole string. To see the last part of the string, hold down the left mouse button in the Status field and move the cursor towards the right edge. Then you should see the address update frequently, within the scanned range.

Now when we know the memory range that is scanned by the J-Link driver, we can place the _SEGGER_RTT data structure in this memory range. This way, the J-Link driver should find it automatically. You can use the macro SEGGER_RTT_SECTION for this purpose. This allows you to specify a linker section where to place the RTT control block (_SEGGER_RTT).  Simply add a definition of SEGGER_RTT_SECTION in your SEGGER_RTT_Conf.h, like in the below example.

#define SEGGER_RTT_SECTION ".bss.$RAM2" /* Assuming gcc and some particular project */

Note that the section name is specific to your system.  What section that is used does not really matter for this purpose, as long as _SEGGER_RTT ends up in the scanned memory range. You can define a new section in your linker settings, hard-coded to a suitable address, or use some existing section that happens to be located in this memory range.

Note: The _SEGGER_RTT data is not very large, so moving it should typically not be a problem. In my project, sizeof(_SEGGER_RTT) gives 168 bytes. It does not contain the actual RTT data buffers, only pointers to it.

2.2. Verify the J-Link RTT transfer on its own, without Tracealyzer in the loop.

2.2.1. Close any open Tracealyzer session.

2.2.2. Download the J-Link driver package if not already done, and locate the JLinkRTTLogger tool.

2.2.3. Disable the Tracealyzer recorder in FreeRTOSConfig.h by setting configUSE_TRACE_FACILITY to 0. You can let the recorder calls remain in your code (vTraceEnable etc.) assuming trcRecorder.h is still included. All calls to the recorder are then disabled and don’t generate any code.

2.2.4. Add the following somewhere in your code, e.g. in “main.c” or similar:

#include “SEGGER_RTT.h”

SEGGER_RTT_Init();
SEGGER_RTT_WriteString(0, “Hello!”);

2.2.5. Rebuild your project.

2.2.6. Start your target system and ensure that the RTT code has executed (e.g. by placing a breakpoint just after).

2.2.7. Start JLinkRTTLogger and enter the information asked for. The defaults are typically fine, just hit enter to accept them. However, make sure to specify channel 0 and an output path where you have write permissions. The below screenshot shows a successful transfer.

J-Link RTT Logger

If this doesn’t work, the problem is in the actual J-Link RTT connection, which is provided by SEGGER. However, if you can get data via JLinkRTTLogger like shown above, the problem is most likely related to the integration of the trace recorder library. In that case, see the troubleshooting section in the Tracealyzer User Manual or contact Percepio Support.

3. In case of “Missed Events”

In some situations, the tracing may generate data faster than the J-Link is able to read, which causes the RTT buffer to become full. In that case, the default behavior is to discard all new events until there is room in the RTT buffer again. Such “Missed Events” are however reported by Tracealyzer in the “Live Stream” window. Moreover, red areas appear in the live views showing where data is missing. An example is shown below.

The RTT buffer is located in the target system RAM and is polled frequently by the J-Link debug probe. The default size is 5000 bytes, which is typically more than enough, at least if using a stand-alone J-Link and high interface speed.

If you see Missed Events when using J-Link streaming, check the following:

3.1. Open the J-Link Control Panel while the Tracealyzer is receiving data, like in the below screenshot. Make sure to select the one with “Process: Tracealyzer.exe”. Check that the reported “Target interface” speed and make sure it is matching the “J-Link Speed” setting in the Tracealyzer J-Link Settings.

These may differ for at least two reasons:

Reason 1: If specifying a higher speed than what is supported by the J-Link debugger, the driver uses the highest supported speed (in the above example, 2667 Khz instead of 4000 Khz). In such cases, there is no point in trying to increase the J-Link Speed, unless you get a faster J-Link probe. The above example is using a “J-Link Onboard” interface, which are a lot slower than the stand-alone J-Link models.

Reason 2: Under certain circumstances, the J-Link driver seems to ignore the “J-Link Speed” setting in Tracealyzer and instead uses a default value of 200 Khz, which is likely to cause poor throughput and thereby missed events. To avoid this issue, make sure to set the “Debugger Interface” setting to SWD or JTAG instead of using the “Default (don’t change)” setting. If also using other tools, like an IDE debugger, make sure this setting (JTAG/SWD) matches the corresponding setting in your IDE.

3.2. If the J-Link Control Panel reports a Target Interface speed matching the Tracealyzer “J-Link Speed” setting (see 3.1), you may increase the “J-Link Speed” setting for better throughput. The default is 4000 KHz but much higher speeds are supported if using a stand-alone J-Link unit. See “max. target interface speed” in the Segger J-Link Model Overview.

3.3. Increase the RTT buffer size (TRC_CFG_RTT_BUFFER_SIZE_UP in trcStreamingPort.h). Note that the buffer size settings in trcStreamingConfig.h are not used by the J-Link RTT stream port.

3.4. Reduce the amount of data produced, e.g., using the settings in trcConfig.h and/or using vTraceSetFilterMask() and vTraceSetFilterGroup().

3.5. Consider upgrading to a faster J-Link debug probe. The onboard J-Link debuggers found on many development boards can only manage 100-150 KB/s at best (if using a large RTT buffer) while a stand-alone J-Link is several times faster. See “max. target interface speed” in the Segger J-Link Model Overview.

Note: If you don’t see any Missed Event, you can try reducing the RTT buffer size. This will reduce RAM usage.

4. In case your system runs slower

If you experience that your system runs noticeably slower when using Tracealyzer with J-Link RTT trace streaming, the most important factors are:

4.1. Blocking RTT mode?

Make sure the TRC_CFG_RTT_MODE in trcStreamingPort.h is set to non-blocking mode (SEGGER_RTT_MODE_NO_BLOCK_SKIP). The other option, blocking mode, can be useful if you don’t have enough RAM to increase the RTT buffer size, but this may result in occasional blocking of several milliseconds. Non-blocking mode is therefore to prefer in most cases, as Missed Events are clearly visible in Tracealyzer while blocking is much harder to detect.

4.2. Too many events?

For some applications, it is not suitable to record ALL events, as some might be very frequent and may result in a significant combined overhead. For instance, be careful with tracing interrupt handlers. Since they are typically short and run frequently, adding additional tracing code may cause a significant overhead.

Moreover, it might be necessary to exclude certain kernel objects from the tracing. For instance, in one case we found that a TCP/IP stack was polling a FreeRTOS semaphore in a tight loop, causing intense bursts of thousands of Semaphore events. In such cases you can exclude that particular object from the trace by using vTraceSetFilterMask() and vTraceSetFilterGroup().

4.3. Compiler Optimizations?

The tracing recorder library becomes considerably more efficient if compiler optimizations are enabled. If not using compiler optimizations, consider using at least basic optimizations (-O1) when tracing your system.

Note that IDEs typically allow for using individual settings for groups of source files. Thus, you can skip optimizations on your application code (e.g. to facilitate debugging) but still apply optimizations to the trace recorder library. You may also want to apply compiler optimizations to your RTOS kernel, as some of the tracing code is macros executing within the RTOS kernel code.

In case of other issues or questions, feel free to contact support@percepio.com.