Using the SWV Tracer

The Serial Wire Viewer from ARM (Cortex-M microcontroller) provides non-intrusive real-time tracing. Usually this mechanism ist used for printfs only. The impulse SWV Tracer enhances SWV use-cases as it allows to log and trace different kind of data (real, integer, strings, logic) to be stored, visualized and analysed with impulse.

Impulse in the embedded area

Embedded systems care usually about analog and digital signals, about bus signals, timing, states and events. They also often appear to be block boxes, as they are not that easy to debug and transparent as desktop applications. Impulse can trace all these informations and display them together in a convenient form (also from multiple sources). Question here is how to get this data from inside the embedded system into impulse. Typical channels are:

  • Serial line (can be standard signal or log formats (log4j/log4c/..; pattern logs) or custom formats)
  • TCP
  • Logic analyzers
  • USB Scopes
  • Serial Wire Output (this article)
  • Custom channels (you may define your own ports)

1 Installation

Follow the installation instructions in the manual of impulse. Make sure that you installed the embedded tools.

2 Prepare a port

A port defines an entity that can be opened by the impulse viewer streaming in signal data from various sources.

To create a port first open the Signal Ports view.

Now select the ports element, open the context menu and add a new SWV Tracer.

After this step you see the configuration dialog.

The port defines how to connect to the debug device and which signals to read in what format.

Enter the correct port informations, the core frequency and the divider used by SVW.

3 Prepare the application for SVW

In order to sent SWV messages over the output, the ITM and other registers need to be set properly. Here an example:

#define ITM_STIM_U8(port)  (*(volatile unsigned char* )(0xE0000000|(port<<2))) // STIM 0 Register/ 8 bits
#define ITM_STIM_U16(port) (*(volatile unsigned short*)(0xE0000000|(port<<2))) // STIM 0 Register/ 16 bits
#define ITM_STIM_U32(port) (*(volatile unsigned int*  )(0xE0000000|(port<<2))) // STIM 0 Register/ 32 bits

#define ITM_ENA            (*(volatile unsigned int*  )0xE0000E00) // Trace Enable Ports Register
#define ITM_TPR            (*(volatile unsigned int*  )0xE0000E40) // Trace Privilege Register
#define ITM_TCR            (*(volatile unsigned int*  )0xE0000E80) // Trace control register
#define ITM_LSR            (*(volatile unsigned int*  )0xE0000FB0) // ITM Lock Status Register

#define DHCSR              (*(volatile unsigned int*  )0xE000EDF0) // Debug register
#define DEMCR              (*(volatile unsigned int*  )0xE000EDFC) // Debug register

#define TPIU_ACPR          (*(volatile unsigned int*  )0xE0040010) // Async Clock presacler register
#define TPIU_SPPR          (*(volatile unsigned int*  )0xE00400F0) // Selected Pin Protocol Register
#define FFCR               (*(volatile unsigned int*  )0xE0040304) // Formatter and flush Control Register
#define DWT_CTRL           (*(volatile unsigned int*  )0xE0001000) // DWT Control Register

	DBGMCU->CR = 0x00000027;
	DEMCR |= (1 << 24);
	ITM_LSR = 0xC5ACCE55;

	TPIU_SPPR = 0x00000002;     // Select NRZ mode
	TPIU_ACPR = 1;  			// Divider 1 means /2

	ITM_TPR = 0x00000000;
	DWT_CTRL = 0x400003FE;
	FFCR = 0x00000100;

	ITM_TCR = 0x1000F; 			// Enable ITM
	ITM_ENA = 0xffff; 			// Enable ITM stimulus port

4 Instrument the code

To simplify the writing of signals, logs and events, we prepared a simple demo api (you can download the source at itm_write).

#define SEVERITY_ERROR 'e'
#define SEVERITY_WARNING 'w'
#define SEVERITY_INFO 'i'
void itm_log(unsigned char port,unsigned char severity,const char* source,const char* message);
 
void itm_write(unsigned char port,unsigned char b);
void itm_write(unsigned char port,signed char b);
void itm_write(unsigned char port,unsigned short b) ;
void itm_write(unsigned char port,short b) ;
void itm_write(unsigned char port,unsigned long b);
void itm_write(unsigned char port,long b);
void itm_write(unsigned char port,unsigned long long b);
void itm_write(unsigned char port,long long b);
void itm_write(unsigned char port,float f) ;
void itm_write(unsigned char port,double f);
void itm_write(unsigned char port,const char* text);
void itm_write(unsigned char port,const char* text,char e);

See a full example at the end of the article.

4 Open the Port

Select the new Port, open the context menu and press open.

The impulse viewer is opened. On the left side you see the signals of the port. On the right side an emtpy viewer and its configuration. As soon you are connected or started tracing, there will be signals visible on the left side. To see them in the viewer, there must be a configuration with the wanted signals inside.

After you've started the embedded application (waiting in main), press the red button to start trace.

5 Example

int main(void) {

	DBGMCU->CR = 0x00000027;
	DEMCR |= (1 << 24);
	ITM_LSR = 0xC5ACCE55;

	TPIU_SPPR = 0x00000002;     // Select NRZ mode
	TPIU_ACPR = 1;  // /2

	ITM_TPR = 0x00000000;
	DWT_CTRL = 0x400003FE;
	FFCR = 0x00000100;

	ITM_TCR = 0x1000F; // Enable ITM
	ITM_ENA = 0xffff; // Enable ITM stimulus port


	itm_write(0,"Start Demo");
	wait(100);
	itm_write(0,"Measure");

	int i = 0;
	float f=0.0;
	double d=0.0;
	unsigned char uc=0;
	signed char sc=0;
	unsigned short us=0;
	signed short ss=0;
	unsigned long ul=0;
	signed long sl=0;
	unsigned long long ull=0;
	signed long long sll=0;
	unsigned char logic=0;


	while (1) {


		// float
		f = sin(i/100.0)*100.;
		d = cos(i/50.0 + 1.0)*50.+20;
		itm_write(1,f);
		itm_write(2,d);

		wait(10);

		// text
		if (i % 500 == 0)
			itm_write(0,"i%50 reached \nmore to come\n");


		// bytes
		if (i % 100 == 0){
			uc = i/20;
			sc += 1;
			itm_write(3,uc);
			itm_write(4,sc);
		}

		// short
		if (i % 125 == 0){
			us = d + i;
			ss = d *20;
			itm_write(5,us);
			itm_write(6,ss);
		}

		// long
		if (i % 175 == 0){
			ul = i;
			sl = -i;
			itm_write(7,ul);
			itm_write(8,sl);
		}

		// long long
		if (i % 225 == 0){
			ull += rand();
			sll = (((long long )d) << 32) + rand();
			itm_write(9,ull);
			itm_write(10,sll);
		}


		// logic
		if (i % 25 == 0){
			logic = i & 1;
			itm_write(11,logic);
		}

		// logic
		if (i > 0 && i % 200 == 0)
			itm_log(12,SEVERITY_INFO,"Test","i % 200 == 0");
		if (i > 0 && i % 350 == 0)
			itm_log(12,SEVERITY_ERROR,"Test",">= 350");



		i++;
	}
	return 0;
}