Browse Source

Add Python script to decode ITM data from SWD

master
Jim Paris 1 month ago
commit
e63bc84139
2 changed files with 170 additions and 0 deletions
  1. +2
    -0
      Makefile
  2. +168
    -0
      itm-decode.py

+ 2
- 0
Makefile View File

@@ -0,0 +1,2 @@
all:
python3 itm-decode.py -b 4000000 /dev/serial/by-id/usb-Bolt_Bolt_JTAG_119591B-if03-port0

+ 168
- 0
itm-decode.py View File

@@ -0,0 +1,168 @@
#!/usr/bin/env python3

# Open a serial port and decode ARM ITM trace (SWO) data sent with the
# UART encoding.

import sys
import serial

def printf(str, *args):
print(str % args, end='')

color_lookup = { "red": 31, "green": 32, "cyan": 36, "yellow": 33 }
def color(name, text):
return "\033[%dm%s\033[0m" % (color_lookup[name], text);

class ResyncException(Exception):
pass

class TimeoutException(Exception):
pass

class ITMParser:

def __init__(self, stream):
self.input_stream = stream

def read_loop(self):
"""
Yields bytes from the input stream one at a time. Raises an
ResyncException if the bytes were a resync sequence, or
TimeoutException if there was an input timeout.
"""
sync_count = 0
while True:
data = next(self.input_stream)

if data is None:
raise TimeoutException()

# Resync
if data == 0x00:
sync_count += 1
elif sync_count == 7 and data == 0x80:
raise ResyncException()
else:
sync_count = 0

yield data

def process(self):
"""
Main processing loop; restarts the parser any time we get
a resync sequence.
"""
while True:
try:
self.synced_stream = self.read_loop()
while True:
try:
c = self.next()
except TimeoutException as e:
# Timeout for the first byte can be ignored.
break

try:
text = self.parse(c)
if text:
print(text)
except TimeoutException as e:
# Timeout inside a parser should be reported.
print(color("red", "Timeout"))
except ResyncException as e:
print(color("red", "Resync"))

def next(self):
return next(self.synced_stream)

def parse(self, c):
"""
Process top-level ITM packets, returning a string that should be
printed to describe it.
"""

if c & 0x7f == 0:
return color("yellow", "sync")

if c == 0x70:
return color("yellow", "overflow")

if c & 0x0f == 0x00 and c & 0x70 != 0x00:
return self.parse_timestamp(c)

if c & 0x0b == 0x80:
return self.parse_extension(c)

if c & 0x0f == 0x04:
return color("yellow", "reserved %02x" % c)

if c & 0x04 == 0x00 and c & 0x03 != 0:
return self.parse_sw(c)

if c & 0x04 == 0x04 and c & 0x03 != 0:
return self.parse_hw(c)

return color("red", "unknown %02x" % c)

def parse_sw(self, c):
"""
Parse SWIT packet
"""
port = (c & 0x81) >> 3
length = 1 << ((c & 0x3) - 1)
payload = 0
for i in range(length):
payload |= self.next() << (i * 8)
if port == 0 and length == 1:
# Dump directly to stdout
print(chr(payload), end='')
return None
msg = "SWIT port %d payload %0*x" % (port, 2 * length, payload)
return color('cyan', msg)

def parse_hw(self, c):
"""
Parse HWIT packet
"""
return color("red", "TODO hw %02x" % c)

def parse_timestamp(self, c):
"""
Parse timestamp packet
"""
return color("red", "TODO timestamp %02x" % c)

def parse_extension(self, c):
"""
Parse extension packet
"""
return color("red", "TODO extension %02x" % c)

def main(argv):
import argparse

parser = argparse.ArgumentParser(
prog = argv[0],
description = "Decode ARM ITM data sent via SWO with UART encoding",
formatter_class = argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument("--baudrate", "-b", metavar="BAUD", default=4000000,
help="SWO data baudrate")
parser.add_argument("device", metavar="PORT",
help="Serial port for SWO input")
args = parser.parse_args()

ser = serial.Serial(args.device, args.baudrate)
ser.timeout = 1

def input_stream():
while True:
data = ser.read(1)
if len(data) == 0:
yield None
else:
yield data[0]
ITMParser(input_stream()).process()

if __name__ == "__main__":
main(sys.argv)

Loading…
Cancel
Save