|
|
@@ -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) |