2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ## GNU General Public License for more details.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 import sigrokdecode as srd
39 class Decoder(srd.Decoder):
43 longname = 'ARM Instrumentation Trace Macroblock'
44 desc = 'Trace data from Cortex-M / ARMv7m ITM module.'
49 {'id': 'objdump', 'desc': 'objdump path',
50 'default': 'arm-none-eabi-objdump'},
51 {'id': 'objdump_opts', 'desc': 'objdump options',
53 {'id': 'elffile', 'desc': '.elf path',
57 ('trace', 'Trace information'),
58 ('timestamp', 'Timestamp'),
59 ('software', 'Software message'),
60 ('dwt_event', 'DWT event'),
61 ('dwt_watchpoint', 'DWT watchpoint'),
62 ('dwt_exc', 'Exception trace'),
63 ('dwt_pc', 'Program counter'),
64 ('mode_thread', 'Current mode: thread'),
65 ('mode_irq', 'Current mode: IRQ'),
66 ('mode_exc', 'Current mode: Exception'),
67 ('location', 'Current location'),
68 ('function', 'Current function'),
71 ('trace', 'Trace information', (0, 1)),
72 ('software', 'Software trace', (2,)),
73 ('dwt_event', 'DWT event', (3,)),
74 ('dwt_watchpoint', 'DWT watchpoint', (4,)),
75 ('dwt_exc', 'Exception trace', (5,)),
76 ('dwt_pc', 'Program counter', (6,)),
77 ('mode', 'Current mode', (7, 8, 9)),
78 ('location', 'Current location', (10,)),
79 ('function', 'Current function', (11,)),
87 self.dwt_timestamp = 0
88 self.current_mode = None
93 self.out_ann = self.register(srd.OUTPUT_ANN)
96 def load_objdump(self):
97 '''Parse disassembly obtained from objdump into a lookup tables'''
98 if not (self.options['objdump'] and self.options['elffile']):
101 opts = [self.options['objdump']]
102 opts += self.options['objdump_opts'].split()
103 opts += [self.options['elffile']]
106 disasm = subprocess.check_output(opts)
107 except subprocess.CalledProcessError:
110 disasm = disasm.decode('utf-8', 'replace')
112 instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
113 filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
114 funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
119 for line in disasm.split('\n'):
120 m = instpat.match(line)
122 addr = int(m.group(1), 16)
123 self.file_lookup[addr] = prev_file
124 self.func_lookup[addr] = prev_func
126 m = funcpat.match(line)
128 prev_func = m.group(1)
130 m = filepat.match(line)
132 prev_file = m.group(1)
134 def get_packet_type(self, byte):
135 '''Identify packet type based on its first byte.
136 See ARMv7-M_ARM.pdf section "Debug ITM and DWT" "Packet Types"
142 elif byte & 0x0F == 0 and byte & 0xF0 != 0:
144 elif byte & 0x0F == 0x08:
145 return 'sw_extension'
146 elif byte & 0x0F == 0x0C:
147 return 'hw_extension'
148 elif byte & 0x0F == 0x04:
150 elif byte & 0x04 == 0x00:
155 def mode_change(self, new_mode):
156 if self.current_mode is not None:
157 start, mode = self.current_mode
158 if mode.startswith('Thread'):
160 elif mode.startswith('IRQ'):
164 self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
167 self.current_mode = None
169 self.current_mode = (self.startsample, new_mode)
171 def location_change(self, pc):
172 new_loc = self.file_lookup.get(pc)
173 new_func = self.func_lookup.get(pc)
174 ss = self.startsample
177 if new_loc is not None:
178 self.put(ss, es, self.out_ann, [10, [new_loc]])
180 if new_func is not None:
181 self.put(ss, es, self.out_ann, [11, [new_func]])
183 def fallback(self, buf):
184 ptype = self.get_packet_type(buf[0])
185 return [0, [('Unhandled %s: ' % ptype) + ' '.join(['%02x' % b for b in buf])]]
187 def handle_overflow(self, buf):
188 return [0, ['Overflow']]
190 def handle_hardware(self, buf):
191 '''Handle packets from hardware source, i.e. DWT block.'''
192 plen = (0, 1, 2, 4)[buf[0] & 0x03]
194 if len(buf) != plen + 1:
195 return None # Not complete yet.
213 excnum = ((buf[2] & 1) << 8) | buf[1]
214 event = (buf[2] >> 4)
215 excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
217 self.mode_change(excstr)
218 return [5, ['Enter: ' + excstr, 'E ' + excstr]]
220 self.mode_change(None)
221 return [5, ['Exit: ' + excstr, 'X ' + excstr]]
223 self.mode_change(excstr)
224 return [5, ['Resume: ' + excstr, 'R ' + excstr]]
226 pc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
227 self.location_change(pc)
228 return [6, ['PC: 0x%08x' % pc]]
229 elif (buf[0] & 0xC4) == 0x84:
230 comp = (buf[0] & 0x30) >> 4
231 what = 'Read' if (buf[0] & 0x08) == 0 else 'Write'
233 data = '0x%02x' % (buf[1])
235 data = '0x%04x' % (buf[1] | (buf[2] << 8))
237 data = '0x%08x' % (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24))
238 return [4, ['Watchpoint %d: %s data %s' % (comp, what, data),
239 'WP%d: %s %s' % (comp, what[0], data)]]
240 elif (buf[0] & 0xCF) == 0x47:
241 comp = (buf[0] & 0x30) >> 4
242 addr = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
243 self.location_change(addr)
244 return [4, ['Watchpoint %d: PC 0x%08x' % (comp, addr),
245 'WP%d: PC 0x%08x' % (comp, addr)]]
246 elif (buf[0] & 0xCF) == 0x4E:
247 comp = (buf[0] & 0x30) >> 4
248 offset = buf[1] | (buf[2] << 8)
249 return [4, ['Watchpoint %d: address 0x????%04x' % (comp, offset),
250 'WP%d: A 0x%04x' % (comp, offset)]]
252 return self.fallback(buf)
254 def handle_software(self, buf):
255 '''Handle packets generated by software running on the CPU.'''
256 plen = (0, 1, 2, 4)[buf[0] & 0x03]
258 if len(buf) != plen + 1:
259 return None # Not complete yet.
261 if plen == 1 and chr(buf[1]) in string.printable:
262 self.add_delayed_sw(pid, chr(buf[1]))
263 return [] # Handled but no data to output.
265 self.push_delayed_sw()
268 return [2, ['%d: 0x%02x' % (pid, buf[1])]]
270 return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
272 return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
274 def handle_timestamp(self, buf):
275 '''Handle timestamp packets, which indicate the time of some DWT event packet.'''
276 if buf[-1] & 0x80 != 0:
277 return None # Not complete yet.
279 if buf[0] & 0x80 == 0:
283 tc = (buf[0] & 0x30) >> 4
286 ts |= (buf[2] & 0x7F) << 7
288 ts |= (buf[3] & 0x7F) << 14
290 ts |= (buf[4] & 0x7F) << 21
292 self.dwt_timestamp += ts
297 msg = '(timestamp delayed)'
299 msg = '(event delayed)'
301 msg = '(event and timestamp delayed)'
303 return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
305 def add_delayed_sw(self, pid, c):
306 '''We join printable characters from software source so that printed
307 strings are easy to read. Joining is done by PID so that different
308 sources do not get confused with each other.'''
309 if self.swpackets.get(pid) is not None:
310 self.swpackets[pid][1] = self.prevsample
311 self.swpackets[pid][2] += c
313 self.swpackets[pid] = [self.startsample, self.prevsample, c]
315 def push_delayed_sw(self):
316 for pid, packet in self.swpackets.items():
319 ss, prevtime, text = packet
320 # Heuristic criterion: Text has ended if at least 16 byte
321 # durations after previous received byte. Actual delay depends
322 # on printf implementation on target.
323 if self.prevsample - prevtime > 16 * self.byte_len:
324 self.put(ss, prevtime, self.out_ann, [2, ['%d: "%s"' % (pid, text)]])
325 self.swpackets[pid] = None
327 def decode(self, ss, es, data):
328 ptype, rxtx, pdata = data
330 # For now, ignore all UART packets except the actual data packets.
334 self.byte_len = es - ss
336 # Reset packet if there is a long pause between bytes.
337 # TPIU framing can introduce small pauses, but more than 1 frame
338 # should reset packet.
339 if ss - self.prevsample > 16 * self.byte_len:
340 self.push_delayed_sw()
344 # Build up the current packet byte by byte.
345 self.buf.append(pdata[0])
347 # Store the start time of the packet.
348 if len(self.buf) == 1:
349 self.startsample = ss
351 # Keep separate buffer for detection of sync packets.
352 # Sync packets override everything else, so that we can regain sync
353 # even if some packets are corrupted.
354 self.syncbuf = self.syncbuf[-5:] + [pdata[0]]
355 if self.syncbuf == [0, 0, 0, 0, 0, 0x80]:
356 self.buf = self.syncbuf
358 # See if it is ready to be decoded.
359 ptype = self.get_packet_type(self.buf[0])
360 if hasattr(self, 'handle_' + ptype):
361 func = getattr(self, 'handle_' + ptype)
362 data = func(self.buf)
364 data = self.fallback(self.buf)
368 self.put(self.startsample, es, self.out_ann, data)