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, write to the Free Software
18 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 import sigrokdecode as srd
40 class Decoder(srd.Decoder):
44 longname = 'ARM Instrumentation Trace Macroblock'
45 desc = 'Trace data from Cortex-M / ARMv7m ITM module.'
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 information'),
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 ('trace', 'Trace information', (0, 1)),
73 ('software', 'Software trace', (2,)),
74 ('dwt_event', 'DWT event', (3,)),
75 ('dwt_watchpoint', 'DWT watchpoint', (4,)),
76 ('dwt_exc', 'Exception trace', (5,)),
77 ('dwt_pc', 'Program counter', (6,)),
78 ('mode', 'Current mode', (7, 8, 9)),
79 ('location', 'Current location', (10,)),
80 ('function', 'Current function', (11,)),
83 def __init__(self, **kwargs):
88 self.dwt_timestamp = 0
89 self.current_mode = None
94 self.out_ann = self.register(srd.OUTPUT_ANN)
97 def load_objdump(self):
98 '''Parse disassembly obtained from objdump into a lookup tables'''
99 if not (self.options['objdump'] and self.options['elffile']):
102 opts = [self.options['objdump']]
103 opts += self.options['objdump_opts'].split()
104 opts += [self.options['elffile']]
107 disasm = subprocess.check_output(opts)
108 except subprocess.CalledProcessError:
111 disasm = disasm.decode('utf-8', 'replace')
113 instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
114 filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
115 funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
120 for line in disasm.split('\n'):
121 m = instpat.match(line)
123 addr = int(m.group(1), 16)
124 self.file_lookup[addr] = prev_file
125 self.func_lookup[addr] = prev_func
127 m = funcpat.match(line)
129 prev_func = m.group(1)
131 m = filepat.match(line)
133 prev_file = m.group(1)
135 def get_packet_type(self, byte):
136 '''Identify packet type based on its first byte.
137 See ARMv7-M_ARM.pdf section "Debug ITM and DWT" "Packet Types"
143 elif byte & 0x0F == 0 and byte & 0xF0 != 0:
145 elif byte & 0x0F == 0x08:
146 return 'sw_extension'
147 elif byte & 0x0F == 0x0C:
148 return 'hw_extension'
149 elif byte & 0x0F == 0x04:
151 elif byte & 0x04 == 0x00:
156 def mode_change(self, new_mode):
157 if self.current_mode is not None:
158 start, mode = self.current_mode
159 if mode.startswith('Thread'):
161 elif mode.startswith('IRQ'):
165 self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
168 self.current_mode = None
170 self.current_mode = (self.startsample, new_mode)
172 def location_change(self, pc):
173 new_loc = self.file_lookup.get(pc)
174 new_func = self.func_lookup.get(pc)
175 ss = self.startsample
178 if new_loc is not None:
179 self.put(ss, es, self.out_ann, [10, [new_loc]])
181 if new_func is not None:
182 self.put(ss, es, self.out_ann, [11, [new_func]])
184 def fallback(self, buf):
185 ptype = self.get_packet_type(buf[0])
186 return [0, [('Unhandled %s: ' % ptype) + ' '.join(['%02x' % b for b in buf])]]
188 def handle_overflow(self, buf):
189 return [0, ['Overflow']]
191 def handle_hardware(self, buf):
192 '''Handle packets from hardware source, i.e. DWT block.'''
193 plen = (0, 1, 2, 4)[buf[0] & 0x03]
195 if len(buf) != plen + 1:
196 return None # Not complete yet.
214 excnum = ((buf[2] & 1) << 8) | buf[1]
215 event = (buf[2] >> 4)
216 excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
218 self.mode_change(excstr)
219 return [5, ['Enter: ' + excstr, 'E ' + excstr]]
221 self.mode_change(None)
222 return [5, ['Exit: ' + excstr, 'X ' + excstr]]
224 self.mode_change(excstr)
225 return [5, ['Resume: ' + excstr, 'R ' + excstr]]
227 pc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
228 self.location_change(pc)
229 return [6, ['PC: 0x%08x' % pc]]
230 elif (buf[0] & 0xC4) == 0x84:
231 comp = (buf[0] & 0x30) >> 4
232 what = 'Read' if (buf[0] & 0x08) == 0 else 'Write'
234 data = '0x%02x' % (buf[1])
236 data = '0x%04x' % (buf[1] | (buf[2] << 8))
238 data = '0x%08x' % (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24))
239 return [4, ['Watchpoint %d: %s data %s' % (comp, what, data),
240 'WP%d: %s %s' % (comp, what[0], data)]]
241 elif (buf[0] & 0xCF) == 0x47:
242 comp = (buf[0] & 0x30) >> 4
243 addr = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
244 self.location_change(addr)
245 return [4, ['Watchpoint %d: PC 0x%08x' % (comp, addr),
246 'WP%d: PC 0x%08x' % (comp, addr)]]
247 elif (buf[0] & 0xCF) == 0x4E:
248 comp = (buf[0] & 0x30) >> 4
249 offset = buf[1] | (buf[2] << 8)
250 return [4, ['Watchpoint %d: address 0x????%04x' % (comp, offset),
251 'WP%d: A 0x%04x' % (comp, offset)]]
253 return self.fallback(buf)
255 def handle_software(self, buf):
256 '''Handle packets generated by software running on the CPU.'''
257 plen = (0, 1, 2, 4)[buf[0] & 0x03]
259 if len(buf) != plen + 1:
260 return None # Not complete yet.
262 if plen == 1 and chr(buf[1]) in string.printable:
263 self.add_delayed_sw(pid, chr(buf[1]))
264 return [] # Handled but no data to output.
266 self.push_delayed_sw()
269 return [2, ['%d: 0x%02x' % (pid, buf[1])]]
271 return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
273 return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
275 def handle_timestamp(self, buf):
276 '''Handle timestamp packets, which indicate the time of some DWT event packet.'''
277 if buf[-1] & 0x80 != 0:
278 return None # Not complete yet.
280 if buf[0] & 0x80 == 0:
284 tc = (buf[0] & 0x30) >> 4
287 ts |= (buf[2] & 0x7F) << 7
289 ts |= (buf[3] & 0x7F) << 14
291 ts |= (buf[4] & 0x7F) << 21
293 self.dwt_timestamp += ts
298 msg = '(timestamp delayed)'
300 msg = '(event delayed)'
302 msg = '(event and timestamp delayed)'
304 return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
306 def add_delayed_sw(self, pid, c):
307 '''We join printable characters from software source so that printed
308 strings are easy to read. Joining is done by PID so that different
309 sources do not get confused with each other.'''
310 if self.swpackets.get(pid) is not None:
311 self.swpackets[pid][1] = self.prevsample
312 self.swpackets[pid][2] += c
314 self.swpackets[pid] = [self.startsample, self.prevsample, c]
316 def push_delayed_sw(self):
317 for pid, packet in self.swpackets.items():
320 ss, prevtime, text = packet
321 # Heuristic criterion: Text has ended if at least 16 byte
322 # durations after previous received byte. Actual delay depends
323 # on printf implementation on target.
324 if self.prevsample - prevtime > 16 * self.byte_len:
325 self.put(ss, prevtime, self.out_ann, [2, ['%d: "%s"' % (pid, text)]])
326 self.swpackets[pid] = None
328 def decode(self, ss, es, data):
329 ptype, rxtx, pdata = data
331 # For now, ignore all UART packets except the actual data packets.
335 self.byte_len = es - ss
337 # Reset packet if there is a long pause between bytes.
338 # TPIU framing can introduce small pauses, but more than 1 frame
339 # should reset packet.
340 if ss - self.prevsample > 16 * self.byte_len:
341 self.push_delayed_sw()
345 # Build up the current packet byte by byte.
346 self.buf.append(pdata[0])
348 # Store the start time of the packet.
349 if len(self.buf) == 1:
350 self.startsample = ss
352 # Keep separate buffer for detection of sync packets.
353 # Sync packets override everything else, so that we can regain sync
354 # even if some packets are corrupted.
355 self.syncbuf = self.syncbuf[-5:] + [pdata[0]]
356 if self.syncbuf == [0, 0, 0, 0, 0, 0x80]:
357 self.buf = self.syncbuf
359 # See if it is ready to be decoded.
360 ptype = self.get_packet_type(self.buf[0])
361 if hasattr(self, 'handle_' + ptype):
362 func = getattr(self, 'handle_' + ptype)
363 data = func(self.buf)
365 data = self.fallback(self.buf)
369 self.put(self.startsample, es, self.out_ann, data)