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 = 'ARM Cortex-M / ARMv7m ITM trace protocol.'
48 tags = ['Debug/trace']
50 {'id': 'objdump', 'desc': 'objdump path',
51 'default': 'arm-none-eabi-objdump'},
52 {'id': 'objdump_opts', 'desc': 'objdump options',
54 {'id': 'elffile', 'desc': '.elf path',
58 ('trace', 'Trace info'),
59 ('timestamp', 'Timestamp'),
60 ('software', 'Software message'),
61 ('dwt_event', 'DWT event'),
62 ('dwt_watchpoint', 'DWT watchpoint'),
63 ('dwt_exc', 'Exception trace'),
64 ('dwt_pc', 'Program counter'),
65 ('mode_thread', 'Current mode: thread'),
66 ('mode_irq', 'Current mode: IRQ'),
67 ('mode_exc', 'Current mode: Exception'),
68 ('location', 'Current location'),
69 ('function', 'Current function'),
72 ('traces', 'Trace info', (0, 1)),
73 ('softwares', 'Software traces', (2,)),
74 ('dwt_events', 'DWT events', (3,)),
75 ('dwt_watchpoints', 'DWT watchpoints', (4,)),
76 ('dwt_excs', 'Exception traces', (5,)),
77 ('dwt_pcs', 'Program counters', (6,)),
78 ('modes', 'Current modes', (7, 8, 9)),
79 ('locations', 'Current locations', (10,)),
80 ('functions', 'Current functions', (11,)),
91 self.dwt_timestamp = 0
92 self.current_mode = None
97 self.out_ann = self.register(srd.OUTPUT_ANN)
100 def load_objdump(self):
101 '''Parse disassembly obtained from objdump into a lookup tables'''
102 if not (self.options['objdump'] and self.options['elffile']):
105 opts = [self.options['objdump']]
106 opts += self.options['objdump_opts'].split()
107 opts += [self.options['elffile']]
110 disasm = subprocess.check_output(opts)
111 except subprocess.CalledProcessError:
114 disasm = disasm.decode('utf-8', 'replace')
116 instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
117 filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
118 funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
123 for line in disasm.split('\n'):
124 m = instpat.match(line)
126 addr = int(m.group(1), 16)
127 self.file_lookup[addr] = prev_file
128 self.func_lookup[addr] = prev_func
130 m = funcpat.match(line)
132 prev_func = m.group(1)
134 m = filepat.match(line)
136 prev_file = m.group(1)
138 def get_packet_type(self, byte):
139 '''Identify packet type based on its first byte.
140 See ARMv7-M_ARM.pdf section "Debug ITM and DWT" "Packet Types"
146 elif byte & 0x0F == 0 and byte & 0xF0 != 0:
148 elif byte & 0x0F == 0x08:
149 return 'sw_extension'
150 elif byte & 0x0F == 0x0C:
151 return 'hw_extension'
152 elif byte & 0x0F == 0x04:
154 elif byte & 0x04 == 0x00:
159 def mode_change(self, new_mode):
160 if self.current_mode is not None:
161 start, mode = self.current_mode
162 if mode.startswith('Thread'):
164 elif mode.startswith('IRQ'):
168 self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
171 self.current_mode = None
173 self.current_mode = (self.startsample, new_mode)
175 def location_change(self, pc):
176 new_loc = self.file_lookup.get(pc)
177 new_func = self.func_lookup.get(pc)
178 ss = self.startsample
181 if new_loc is not None:
182 self.put(ss, es, self.out_ann, [10, [new_loc]])
184 if new_func is not None:
185 self.put(ss, es, self.out_ann, [11, [new_func]])
187 def fallback(self, buf):
188 ptype = self.get_packet_type(buf[0])
189 return [0, [('Unhandled %s: ' % ptype) + ' '.join(['%02x' % b for b in buf])]]
191 def handle_overflow(self, buf):
192 return [0, ['Overflow']]
194 def handle_hardware(self, buf):
195 '''Handle packets from hardware source, i.e. DWT block.'''
196 plen = (0, 1, 2, 4)[buf[0] & 0x03]
198 if len(buf) != plen + 1:
199 return None # Not complete yet.
217 excnum = ((buf[2] & 1) << 8) | buf[1]
218 event = (buf[2] >> 4)
219 excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
221 self.mode_change(excstr)
222 return [5, ['Enter: ' + excstr, 'E ' + excstr]]
224 self.mode_change(None)
225 return [5, ['Exit: ' + excstr, 'X ' + excstr]]
227 self.mode_change(excstr)
228 return [5, ['Resume: ' + excstr, 'R ' + excstr]]
230 pc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
231 self.location_change(pc)
232 return [6, ['PC: 0x%08x' % pc]]
233 elif (buf[0] & 0xC4) == 0x84:
234 comp = (buf[0] & 0x30) >> 4
235 what = 'Read' if (buf[0] & 0x08) == 0 else 'Write'
237 data = '0x%02x' % (buf[1])
239 data = '0x%04x' % (buf[1] | (buf[2] << 8))
241 data = '0x%08x' % (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24))
242 return [4, ['Watchpoint %d: %s data %s' % (comp, what, data),
243 'WP%d: %s %s' % (comp, what[0], data)]]
244 elif (buf[0] & 0xCF) == 0x47:
245 comp = (buf[0] & 0x30) >> 4
246 addr = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
247 self.location_change(addr)
248 return [4, ['Watchpoint %d: PC 0x%08x' % (comp, addr),
249 'WP%d: PC 0x%08x' % (comp, addr)]]
250 elif (buf[0] & 0xCF) == 0x4E:
251 comp = (buf[0] & 0x30) >> 4
252 offset = buf[1] | (buf[2] << 8)
253 return [4, ['Watchpoint %d: address 0x????%04x' % (comp, offset),
254 'WP%d: A 0x%04x' % (comp, offset)]]
256 return self.fallback(buf)
258 def handle_software(self, buf):
259 '''Handle packets generated by software running on the CPU.'''
260 plen = (0, 1, 2, 4)[buf[0] & 0x03]
262 if len(buf) != plen + 1:
263 return None # Not complete yet.
265 if plen == 1 and chr(buf[1]) in string.printable:
266 self.add_delayed_sw(pid, chr(buf[1]))
267 return [] # Handled but no data to output.
269 self.push_delayed_sw()
272 return [2, ['%d: 0x%02x' % (pid, buf[1])]]
274 return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
276 return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
278 def handle_timestamp(self, buf):
279 '''Handle timestamp packets, which indicate the time of some DWT event packet.'''
280 if buf[-1] & 0x80 != 0:
281 return None # Not complete yet.
283 if buf[0] & 0x80 == 0:
287 tc = (buf[0] & 0x30) >> 4
290 ts |= (buf[2] & 0x7F) << 7
292 ts |= (buf[3] & 0x7F) << 14
294 ts |= (buf[4] & 0x7F) << 21
296 self.dwt_timestamp += ts
301 msg = '(timestamp delayed)'
303 msg = '(event delayed)'
305 msg = '(event and timestamp delayed)'
307 return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
309 def add_delayed_sw(self, pid, c):
310 '''We join printable characters from software source so that printed
311 strings are easy to read. Joining is done by PID so that different
312 sources do not get confused with each other.'''
313 if self.swpackets.get(pid) is not None:
314 self.swpackets[pid][1] = self.prevsample
315 self.swpackets[pid][2] += c
317 self.swpackets[pid] = [self.startsample, self.prevsample, c]
319 def push_delayed_sw(self):
320 for pid, packet in self.swpackets.items():
323 ss, prevtime, text = packet
324 # Heuristic criterion: Text has ended if at least 16 byte
325 # durations after previous received byte. Actual delay depends
326 # on printf implementation on target.
327 if self.prevsample - prevtime > 16 * self.byte_len:
328 self.put(ss, prevtime, self.out_ann, [2, ['%d: "%s"' % (pid, text)]])
329 self.swpackets[pid] = None
331 def decode(self, ss, es, data):
332 ptype, rxtx, pdata = data
334 # For now, ignore all UART packets except the actual data packets.
338 self.byte_len = es - ss
340 # Reset packet if there is a long pause between bytes.
341 # TPIU framing can introduce small pauses, but more than 1 frame
342 # should reset packet.
343 if ss - self.prevsample > 16 * self.byte_len:
344 self.push_delayed_sw()
348 # Build up the current packet byte by byte.
349 self.buf.append(pdata[0])
351 # Store the start time of the packet.
352 if len(self.buf) == 1:
353 self.startsample = ss
355 # Keep separate buffer for detection of sync packets.
356 # Sync packets override everything else, so that we can regain sync
357 # even if some packets are corrupted.
358 self.syncbuf = self.syncbuf[-5:] + [pdata[0]]
359 if self.syncbuf == [0, 0, 0, 0, 0, 0x80]:
360 self.buf = self.syncbuf
362 # See if it is ready to be decoded.
363 ptype = self.get_packet_type(self.buf[0])
364 if hasattr(self, 'handle_' + ptype):
365 func = getattr(self, 'handle_' + ptype)
366 data = func(self.buf)
368 data = self.fallback(self.buf)
372 self.put(self.startsample, es, self.out_ann, data)