Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I capture and view ITM trace information on a Cortex-M4 MCU?

Tags:

embedded

I would like to capture, decode, and view ITM trace information for a Cortex-M4 MCU (in my case, an Atmel SAM4S). In particular, I want to capture exceptions and user trace data relative to other signals on my board (i.e. show all signals and trace information on same timeline).

like image 956
Patrick Avatar asked Jul 28 '15 20:07

Patrick


People also ask

What is ITM trace?

The ITM is a an application driven trace source that supports printf style debugging to trace Operating System (OS) and application events, and emits diagnostic system information. The ITM emits trace information as packets. There are three sources that can generate packets.

What is SWO trace?

SWO Trace is a single pin trace interface that is part of the Cortex M Coresight components from ARM Ltd. It supports profiling hardware events such as periodic sampling of program counter, data variable reads and writes, interrupt entry and exit, counters as well as application generated software messages.


2 Answers

This can be done using the following steps:

  1. Place debugger in SWD mode. If using J-Link Segger on Linux, this can be done with JLinkGDBServer -if swd
  2. Add code to the MCU to enable trace. Set the bit rate to a value suitable for your needs (I used 8 MHz). Example Ada code is below.
  3. Use logic analyzer to capture trace data on TRACESWO line from SAM4S processor. I used a Saleae Logic Pro 16 with 100 MHz sample rate.
  4. Convert the data to a format usable by sigrok. Using the Saleae to capture the data, this comprises the following steps:

    • Capture using only the first 8 channels (so one byte per sample is exported).
    • Export data as binary to trace.bin, writing a byte for every sample.
    • Convert to a trace.sr file using:

      sigrok-cli -i trace.bin -I binary:samplerate=100000000,numchannels=4 -o trace.sr

  5. Open the trace.sr file in PulseView.
  6. Add UART decoder to TRACESWO channel, bit rate 8000000.
  7. Stack ARM-ITM decoder.

See http://www.sigrok.org/blog/new-protocol-decoders-arm-tpiu-itm-etmv3 for more information.

Example Ada code for SAM4S Trace:

sam4s-trace.ads:

with Interfaces;

package SAM4S.Trace is
   pragma Preelaborate;

   type Channel_Type is new Integer range 0 .. 31;
   type Value_Type is new Interfaces.Unsigned_32;

   procedure Initialize;

   procedure Put (Channel : Channel_Type;
                  Value   : Value_Type);

   procedure Put (Channel : Channel_Type;
                  Message : String);
end SAM4S.Trace;

sam4s-trace.adb:

with System;
with System.Storage_Elements; use System.Storage_Elements;

package body SAM4S.Trace is
   procedure Initialize is
      ITM_LAR : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E000_0FB0#), Volatile;

      ITM_TCR : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E000_0E80#), Volatile;

      ITM_TER : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E000_0E00#), Volatile;

      ITM_TPR : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E000_0E40#), Volatile;

      DEMR : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E000_EDFC#), Volatile;

      TPIU_SPP : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E004_00F0#), Volatile;

      TPIU_FFCR : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E004_0304#), Volatile;

      TPIU_ACPR : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E004_0010#), Volatile;

      DWT_CTRL : Interfaces.Unsigned_32
        with Address => System'To_Address (16#E000_1000#), Volatile;

      use Interfaces;

   begin
      --  Enable write access via the Lock Access Register.
      ITM_LAR := 16#C5AC_CE55#;
      --  Enable the ITM, enable SWO mode behavior, enable synchronization
      --  packets, enable DWT event submission, enable timestamps.
      ITM_TCR := 16#0001_001F#;
      --  Enable access in user mode to all 32 channels.
      ITM_TPR := 16#0000_0000#;
      --  Enable all 32 trace channels.
      ITM_TER := 16#FFFF_FFFF#;

      --  Set TRCENA bit to 1 in Debug Exception and Monitor Register.
      DEMR := DEMR or 16#0100_0000#;

      --  Select NRZ serial wire output.
      TPIU_SPP := 16#0000_0002#;

      --  Deactivate formatter.
      TPIU_FFCR := 16#0000_0100#;

      --  Set prescalar (/10).
      --  TPIU_ACPR := 16#0000_0009#;

      --  Set prescalar (/15).
      TPIU_ACPR := 14;

      --  Enable exception trace and exception overhead.
      DWT_CTRL := DWT_CTRL or 16#0005_0000#;

   end Initialize;

   procedure Put (Channel : Channel_Type;
                  Value   : Value_Type) is
      Port_Reg : Value_Type with Address => System'To_Address (16#E000_0000#) +
        4 * Channel_Type'Pos (Channel), Volatile;
   begin
      --  Port register lsb is set when the the FIFO can accept at least one
      --  word.
      while Port_Reg = 0 loop
         null;
      end loop;
      Port_Reg := Value;
   end Put;

   procedure Put (Channel : Channel_Type;
                  Message : String) is
      Port_Reg : Value_Type with Address => System'To_Address (16#E000_0000#) +
        4 * Channel_Type'Pos (Channel), Volatile;
   begin
      --  Port register lsb is set when the the FIFO can accept at least one
      --  word.
      for Index in Message'Range loop
         while Port_Reg = 0 loop
            null;
         end loop;
         Port_Reg := Value_Type (Character'Pos (Message (Index)));
      end loop;
   end Put;

end SAM4S.Trace;
like image 199
Patrick Avatar answered Jan 04 '23 16:01

Patrick


As you tagged it with "logic analyzer", this may be off-topic, but I found the following extremely helpful. With Keil uVision (and probably with other IDEs as well) you can reroute ITM data to file using a custom .ini file.

Enable SWJ in your debugger, use SW Port. Enable Trace, enable the stimulus ports that you want to use.

Write a .ini file with an content like this:

ITMLOG 0 > "debug.log"
ITMLOG 1 > "testlog.xml"

This wil reroute ITM channel 0 to a file named "debug.log", and channel 1 to "testlog.xml" (file syntax from here).

To use the channels easily with fprinf in you c-code I use following setup:

struct __FILE { int channel; };
#include <stdio.h>

#define ITM_Port8(n)    (*((volatile unsigned char *)(0xE0000000+4*n)))
#define ITM_Port16(n)   (*((volatile unsigned short*)(0xE0000000+4*n)))
#define ITM_Port32(n)   (*((volatile unsigned long *)(0xE0000000+4*n)))

#define DEMCR           (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA          0x01000000

int fputc(int ch, FILE *f) {
  if (DEMCR & TRCENA) {
    while (ITM_Port32(f->channel) == 0);
    ITM_Port8(f->channel) = ch;
  }
  return(ch);
}

And usage within the project:

FILE debug_stream = { .channel = 0 };
FILE test_stream = { .channel = 1 };

int main(void) {
    fprinf(&debug_stream, "this is a debug message, it will be rerouted to debug.log");
    fprinf(&test_stream, "this is a test message, it will be placed in testlog.xml");
}

Reference: link

like image 25
jwsc Avatar answered Jan 04 '23 16:01

jwsc