How to enable performance and debug tracing on RT1060 EVK using SWO back-channel

Following on from the RT1060: Enabling faster flashing article which discusses how to enable the advanced LPC-Link2 JTAG debugger features on the i.MXRT1060EVK board I was inspired to explore how we could make good use of this with Storyboard applications.

There are a few resources and references that you may find useful:

https://www.nxp.com/docs/en/user-guide/LPCXpresso_IDE_SWO_Trace.pdf

https://www.nxp.com/docs/en/quick-reference-guide/MCUXpresso_IDE_SWO_Trace.pdf

https://community.nxp.com/community/mcuxpresso/mcuxpresso-ide/blog/2019/06/10/swo-with-nxp-imx-rt1064-evk-board

Configuring the SWO hardware

The first thing that needs to be done is to bring the SWO pin to the outside world and make this available to the onboard JTAG adapter. This requires a change to the pin MUX configuration for the single wire debug function [Pad G13] on the RT1060 device.

You can do this one of two ways:

Option 1: Using the MCUExpresso Pins configuration tool and then use the update option to insert the code automatically as follows:

* TEXT BELOW IS USED AS SETTING FOR TOOLS *************************************

CopiedFunction:
- options: {}
- pin_list:
- pinItem: {pin_num: G13, peripheral: ARM, signal: arm_trace_swo, pin_signal: GPIO_AD_B0_10, pull_up_down_config: Pull_Down_100K_Ohm, pull_keeper_select: Keeper,
pull_keeper_enable: Disable, open_drain: Disable, speed: MHZ_200, drive_strength: R0_7}

* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS ***********

image2.png

Option 2: Manually insert the code below into the BOARD_InitPins() function within the pin_mux.c file to set the pin MUX and route the SWO TRACE function to the pin:

    IOMUXC_SetPinMux(
    IOMUXC_GPIO_AD_B0_10_ARM_TRACE_SWO,     /* GPIO_AD_B0_10 is configured as ARM_TRACE_SWO */
    0U);                                    /* Software Input On Field: Input Path is determined by functionality */
 
    IOMUXC_SetPinConfig(
    IOMUXC_GPIO_AD_B0_10_ARM_TRACE_SWO,     /* GPIO_AD_B0_10 PAD functional properties : */
    0xF9U);                                 /* Slew Rate Field: Fast Slew Rate
                                                 Drive Strength Field: R0/7
                                                 Speed Field: max(200MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Disabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */

Next the TRACE clock settings must be checked and or modified in clock_config.c within function BOARD_BootClockRUN() to be the following:

/* Set TRACE_PODF. */
CLOCK_SetDiv(kCLOCK_TraceDiv, 0);

/* Set Trace clock source. */
CLOCK_SetMux(kCLOCK_TraceMux, 3);
    

Finally the TRACE clock must be enabled on boot so insert the following in main() after BOARD_BootClockRUN():

// Enable SWO TRACE clock
*((uint32_t *)(0x400E0600)) = (1 << 11); /* enable TPIU clock */
CLOCK_EnableClock(kCLOCK_Trace);

Adding SWO tracing utility functions

Once the hardware is configured we can now make use of the SWO pin to act as a JTAG back-channel for trace output instead of the much slower UART method. The utility functions enable you to send strings out of the SWO debug channel that can be read using the SWO ITM Console.

SWO.h:

/*
 * SWO.h
 *
 *  Created on: 27 Mar 2020
 */
 
#ifndef SWO_H_
#define SWO_H_
 
void SWO_PrintChar  (char c);
void SWO_PrintString(const char *s);
 
 
#endif /* SWO_H_ */

SWO.c:

/*
 * SWO.c
 *
 *  Created on: 27 Mar 2020
 */
 
#include "SWO.h"
 
/*******************************************************************************
 * Code
 ******************************************************************************/
 
 
/*********************************************************************
*
* Defines for Cortex-M debug unit
*/
 
/*********************************************************************
*
*       Defines for Cortex-M debug unit
*/
#define ITM_STIM_U32 (*(volatile unsigned int*)0xE0000000)    // Stimulus Port Register word acces
#define ITM_STIM_U8  (*(volatile         char*)0xE0000000)    // Stimulus Port Register byte acces
#define ITM_ENA      (*(volatile unsigned int*)0xE0000E00)    // Trace Enable Ports Register
#define ITM_TCR      (*(volatile unsigned int*)0xE0000E80)    // Trace control register
 
/*********************************************************************
*
*       SWO_PrintChar()
*
* Function description
*   Checks if SWO is set up. If it is not, return,
*    to avoid program hangs if no debugger is connected.
*   If it is set up, print a character to the ITM_STIM register
*    in order to provide data for SWO.
* Parameters
*   c:    The Character to be printed.
* Notes
*   Additional checks for device specific registers can be added.
*/
void SWO_PrintChar(char c) {
  volatile int timeout;
  //
  // Check if ITM_TCR.ITMENA is set
  //
  if ((ITM_TCR & 1) == 0) {
    return;
  }
  //
  // Check if stimulus port is enabled
  //
  if ((ITM_ENA & 1) == 0) {
    return;
  }
  //
  // Wait until STIMx is ready,
  // then send data
  //
  timeout = 5000; /* arbitrary timeout value */
  while ((ITM_STIM_U8 & 1) == 0)
  {
	/* Wait until STIMx is ready, then send data */
	timeout--;
	if (timeout==0) {
		return; /* not able to send */
	}
  }
  ITM_STIM_U8 = c;
}
 
/*********************************************************************
*
*       SWO_PrintString()
*
* Function description
*   Print a string via SWO.
*
*/
void SWO_PrintString(const char *s) {
  //
  // Print out character per character
  //
  while (*s) {
    SWO_PrintChar(*s++);
  }
}

To test the SWO output with your application include the SWO.h header file in your source and call the utility function to emit some sample trace output:

:
#include "board.h"
#include "peripherals.h"
#include "SWO.h"
:
SWO_PrintString("Hello world from SWO!");

Build the application and flash this to the board. When the board starts-up and halts with a breakpoint on main() then move on to the next step.

Configuring MCUXpresso for SWO ITM Console trace output

With MCUXpresso the Analysis menu gives access to the advanced trace windows for the IDE:

image3.png

First the SWO Trace Config needs to be configured to detect the link speed and add the ITM Console.  You may need to detect the SWO link by clicking Change -> Detect :

image5.png

If you select the SWO ITM Console and start the ITM Trace then allow the debugger to run then you should see the trace output appear:

image6.png

Note you can stop and start the SWO trace without impacting the performance of the target application. You can even capture and save the ITM Trace Console contents to a file.

Similarly if you are using J-Link with IAR then the i.MXRT1060 setup below can be used to emit the SWO trace data  in real time and be viewed through the Terminal IO window and also logged to the local file ITM.log. Note the default CPU CPU clock speed of 528MHz.

J-Link -> SWO Configuration:

image1.png

Using SWO Tracing for Storyboard Performance logging:

Now that the SWO back-channel is proven we can reconfigure the project to use the SWO as the default route for the STDOUT and STDERR pipes so that application printf() content will appear out of on SWO ITM Console.

With NEWLIB this can be achieved by re-implementing the _write() and re-entrant _write_r() library functions.  Inserting the code snippet below will override the default library behaviour:

#ifdef __NEWLIB__
/* For GCC compiler revise _write() and _write_r() functions for printf functionality 
 * which enables the output from STDOUT and STDERR */
int _write(int file, char *ptr, int len)
{
    int i;
	file = file;
 
	//
	// Print out character per character
	//
    for( i=0; i < len; i++) {
    	SWO_PrintChar( ptr[i] );
    }
    return len;
}
 
_ssize_t _write_r (struct _reent *reent, int fd, const void *ptr, size_t len)
{
	portENTER_CRITICAL();
        _write(fd, ptr,len);
	portEXIT_CRITICAL();
	return (_ssize_t)len;
}
#endif

Now it will be possible to enable performance tracing from a Storyboard application which emits PERF data to STDOUT and now have this streamed out of the SWO ITM Console:

void
sbengine_main_task(void *arg) {
	char *args[20];
	int n = 0;
 
	// args[n++] = "screen_mgr";
	// args[n++] = "fps";
            
        args[n++] = "logger";
        args[n++] = "perf=1";
image7.png

The same can be done for Storyboard debug output by elevating the trace verbosity:

static void
run_storyboard_app(const char *bundle, int flags, char * const *options, int option_count) {
	
	app = gr_application_create_args(bundle, flags, options, option_count);
 
	if(!app) {
		return;
	}
 
	// Sets the application logging verbosity. For more logging verbosities, refer to gre.h.
	gr_application_debug(app, GR_DEBUG_CMD_VERBOSITY, GR_LOG_EVENT2 );//GR_LOG_ERROR);
image4.png

Issues:

There is a slight issue with MCUXpresso where the trace line output gets truncated every 100ms or so which will need some investigation as to the cause.

Was this article helpful?
0 out of 0 found this helpful
Have more questions? Submit a request

Comments

0 comments

Article is closed for comments.