Useful debugging help via SWO for Cortex-Mx

When you’ll want to go native with TinyCLR, debugging gcc/gdb is dreadful to setup…

I’ve done a very basic help using SWV options embedded in many programmer/debugger, like ST-Link or Segger JLINK. It uses ITM feature embedded in most Cortex-M3-M4-M7 cores. I’m using on STM32F7 at the moment. It is created as an additional driver under TinyCLR source code. This means that it is not included by default in the device compilation, unless explicitly enabled in the device’s BuildConfiguration.txt.
The code:

//********************************************
// Enable SWO DEBUGGING with ST-LINK
//********************************************
#include <STM32F7.h>

#ifndef STLINK_FREQ
#define STLINK_FREQ	2000000
#endif

/*!
 * \brief Initialize the SWO trace port for debug message printing
 * \param portBits Port bit mask to be configured
 * \param cpuCoreFreqHz CPU core clock frequency in Hz
 */
void SWO_Init(uint32_t portBits, uint32_t cpuCoreFreqHz) {
	uint32_t SWOSpeed = STLINK_FREQ; /* default 64k baud rate */
	uint32_t SWOPrescaler = (cpuCoreFreqHz / SWOSpeed) - 1; /* SWOSpeed in Hz, note that cpuCoreFreqHz is expected to be match the CPU core clock */

	CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk; /* enable trace in core debug */
	*((volatile unsigned *)(ITM_BASE + 0x400F0)) = 0x00000002; /* "Selected PIN Protocol Register": Select which protocol to use for trace output (2: SWO NRZ, 1: SWO Manchester encoding) */
	*((volatile unsigned *)(ITM_BASE + 0x40010)) = SWOPrescaler; /* "Async Clock Prescaler Register". Scale the baud rate of the asynchronous output */
	*((volatile unsigned *)(ITM_BASE + 0x00FB0)) = 0xC5ACCE55; /* ITM Lock Access Register, C5ACCE55 enables more write access to Control Register 0xE00 :: 0xFFC */
	ITM->TCR = ITM_TCR_TraceBusID_Msk | ITM_TCR_SWOENA_Msk | ITM_TCR_SYNCENA_Msk | ITM_TCR_ITMENA_Msk; /* ITM Trace Control Register */
	ITM->TPR = ITM_TPR_PRIVMASK_Msk; /* ITM Trace Privilege Register */
	ITM->TER = portBits; /* ITM Trace Enable Register. Enabled tracing on stimulus ports. One bit per stimulus port. */
	*((volatile unsigned *)(ITM_BASE + 0x01000)) = 0x400003FE; /* DWT_CTRL */
	*((volatile unsigned *)(ITM_BASE + 0x40304)) = 0x00000100; /* Formatter and Flush Control Register */
}

/*!
 * \brief Sends a character over the SWO channel
 * \param c Character to be sent
 * \param portNo SWO channel number, value in the range of 0 to 31
 */
void SWO_PrintChar(char c, uint8_t portNo) {
	volatile int timeout;

	/* Check if Trace Control Register (ITM->TCR at 0xE0000E80) is set */
	if ((ITM->TCR&ITM_TCR_ITMENA_Msk) == 0) { /* check Trace Control Register if ITM trace is enabled*/
		return; /* not enabled? */
	}
	/* Check if the requested channel stimulus port (ITM->TER at 0xE0000E00) is enabled */
	if ((ITM->TER & (1ul << portNo)) == 0) { /* check Trace Enable Register if requested port is enabled */
		return; /* requested port not enabled? */
	}
	timeout = 5000; /* arbitrary timeout value */
	while (ITM->PORT[portNo].u32 == 0) {
		/* Wait until STIMx is ready, then send data */
		timeout--;
		if (timeout == 0) {
			return; /* not able to send */
		}
	}
	//ITM->PORT[portNo].u16 = 0x08 | (c << 8);
	ITM->PORT[portNo].u8 = c;
}

/*!
 * \brief Sends a string over SWO to the host
 * \param s String to send
 * \param portNumber Port number, 0-31, use 0 for normal debug strings
 */
void SWO_PrintString(const char *s, uint8_t portNumber) {
	while (*s != '\0') {
		SWO_PrintChar(*s++, portNumber);
	}
}

Using function SWO_PrintString() you can output a string to ST-Link SWV windows. What you need is to set the following define in device.h (and add “SWO_Debug” in the BuildConfiguration.txt).

#define SWO_DEBUG   1
#define SWO_BITMASK 0x01
#define SWO_PORT    0

I borrowed most of the code from what generates CubeMX.
The code will appear on github as soon as it will ready to go.

1 Like

Very cool. I learned something new today :slight_smile:

1 Like