blob: ba12b901e8f0d7c03d7357a16a10c7a33d0a05c2 [file] [log] [blame]
Brian Silverman4787a6e2018-10-06 16:00:54 -07001#!/usr/bin/python3
2
3# This is a program to parse output from the ITM and DWT.
4# The "Debug ITM and DWT Packet Protocol" section of the ARMv7-M Architecture
5# Reference Manual is a good reference.
6#
7# This seems like it might be a poster child for using coroutines, but those
8# look scary so we're going to stick with generators.
9
10import io
11import os
12import sys
13
Ravago Jones5127ccc2022-07-31 16:32:45 -070014
Brian Silverman4787a6e2018-10-06 16:00:54 -070015def open_file_for_bytes(path):
Ravago Jones5127ccc2022-07-31 16:32:45 -070016 '''Returns a file-like object which reads bytes without buffering.'''
17 # Not using `open` because it's unclear from the docs how (if it's possible at
18 # all) to get something that will only do one read call and return what that
19 # gets on a fifo.
Brian Silverman4787a6e2018-10-06 16:00:54 -070020 try:
Ravago Jones5127ccc2022-07-31 16:32:45 -070021 return io.FileIO(path, 'r')
22 except FileNotFoundError:
23 # If it wasn't found, try (once) to create it and then open again.
24 try:
25 os.mkfifo(path)
26 except FileExistsError:
27 pass
28 return io.FileIO(path, 'r')
29
Brian Silverman4787a6e2018-10-06 16:00:54 -070030
31def read_bytes(path):
Ravago Jones5127ccc2022-07-31 16:32:45 -070032 '''Reads bytes from a file. This is appropriate both for regular files and
Brian Silverman4787a6e2018-10-06 16:00:54 -070033 fifos.
34 Args:
35 path: A path-like object to open.
36 Yields:
37 Individual bytes from the file, until hitting EOF.
38 '''
Ravago Jones5127ccc2022-07-31 16:32:45 -070039 with open_file_for_bytes(path) as f:
40 while True:
41 buf = f.read(1024)
42 if not buf:
43 return
44 for byte in buf:
45 yield byte
46
Brian Silverman4787a6e2018-10-06 16:00:54 -070047
48def parse_packets(source):
Ravago Jones5127ccc2022-07-31 16:32:45 -070049 '''Parses a stream of bytes into packets.
Brian Silverman4787a6e2018-10-06 16:00:54 -070050 Args:
51 source: A generator of individual bytes.
52 Generates:
53 Packets as bytes objects.
54 '''
Ravago Jones5127ccc2022-07-31 16:32:45 -070055 try:
56 while True:
57 header = next(source)
58 if header == 0:
59 # Synchronization packets consist of a bunch of 0 bits (not necessarily
60 # a whole number of bytes), followed by a 128 byte. This is for hardware
61 # to synchronize on, but we're not in a position to do that, so
62 # presumably those should get filtered out before getting here?
63 raise 'Not sure how to handle synchronization packets'
64 packet = bytearray()
65 packet.append(header)
66 header_size = header & 3
67 if header_size == 0:
68 while packet[-1] & 128 and len(packet) < 7:
69 packet.append(next(source))
70 else:
71 if header_size == 3:
72 header_size = 4
73 for _ in range(header_size):
74 packet.append(next(source))
75 yield bytes(packet)
76 except StopIteration:
77 return
78
Brian Silverman4787a6e2018-10-06 16:00:54 -070079
80class PacketParser(object):
Brian Silverman4787a6e2018-10-06 16:00:54 -070081
Ravago Jones5127ccc2022-07-31 16:32:45 -070082 def __init__(self):
83 self.stimulus_handlers = {}
Brian Silverman4787a6e2018-10-06 16:00:54 -070084
Ravago Jones5127ccc2022-07-31 16:32:45 -070085 def register_stimulus_handler(self, port_number, handler):
86 '''Registers a function to call on packets to the specified port.'''
87 self.stimulus_handlers[port_number] = handler
88
89 def process(self, path):
90 for packet in parse_packets(read_bytes(path)):
91 header = packet[0]
92 header_size = header & 3
93 if header_size == 0:
94 # TODO(Brian): At least handle overflow packets here.
95 pass
96 else:
97 port_number = header >> 3
98 if port_number in self.stimulus_handlers:
99 self.stimulus_handlers[port_number](packet[1:])
100 else:
101 print('Warning: unhandled stimulus port %d' % port_number,
102 file=sys.stderr)
103 self.stimulus_handlers[port_number] = lambda _: None
104
Brian Silverman4787a6e2018-10-06 16:00:54 -0700105
106if __name__ == '__main__':
Ravago Jones5127ccc2022-07-31 16:32:45 -0700107 parser = PacketParser()
Brian Silverman4787a6e2018-10-06 16:00:54 -0700108
Ravago Jones5127ccc2022-07-31 16:32:45 -0700109 def print_byte(payload):
110 sys.stdout.write(payload.decode('ascii'))
111
112 parser.register_stimulus_handler(0, print_byte)
113
114 for path in sys.argv[1:]:
115 parser.process(path)