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,)),
90 self.dwt_timestamp = 0
91 self.current_mode = None
96 self.out_ann = self.register(srd.OUTPUT_ANN)
99 def load_objdump(self):
100 '''Parse disassembly obtained from objdump into a lookup tables'''
101 if not (self.options['objdump'] and self.options['elffile']):
104 opts = [self.options['objdump']]
105 opts += self.options['objdump_opts'].split()
106 opts += [self.options['elffile']]
109 disasm = subprocess.check_output(opts)
110 except subprocess.CalledProcessError:
113 disasm = disasm.decode('utf-8', 'replace')
115 instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
116 filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
117 funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
122 for line in disasm.split('\n'):
123 m = instpat.match(line)
125 addr = int(m.group(1), 16)
126 self.file_lookup[addr] = prev_file
127 self.func_lookup[addr] = prev_func
129 m = funcpat.match(line)
131 prev_func = m.group(1)
133 m = filepat.match(line)
135 prev_file = m.group(1)
137 def get_packet_type(self, byte):
138 '''Identify packet type based on its first byte.
139 See ARMv7-M_ARM.pdf section "Debug ITM and DWT" "Packet Types"
145 elif byte & 0x0F == 0 and byte & 0xF0 != 0:
147 elif byte & 0x0F == 0x08:
148 return 'sw_extension'
149 elif byte & 0x0F == 0x0C:
150 return 'hw_extension'
151 elif byte & 0x0F == 0x04:
153 elif byte & 0x04 == 0x00:
158 def mode_change(self, new_mode):
159 if self.current_mode is not None:
160 start, mode = self.current_mode
161 if mode.startswith('Thread'):
163 elif mode.startswith('IRQ'):
167 self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
170 self.current_mode = None
172 self.current_mode = (self.startsample, new_mode)
174 def location_change(self, pc):
175 new_loc = self.file_lookup.get(pc)
176 new_func = self.func_lookup.get(pc)
177 ss = self.startsample
180 if new_loc is not None:
181 self.put(ss, es, self.out_ann, [10, [new_loc]])
183 if new_func is not None:
184 self.put(ss, es, self.out_ann, [11, [new_func]])
186 def fallback(self, buf):
187 ptype = self.get_packet_type(buf[0])
188 return [0, [('Unhandled %s: ' % ptype) + ' '.join(['%02x' % b for b in buf])]]
190 def handle_overflow(self, buf):
191 return [0, ['Overflow']]
193 def handle_hardware(self, buf):
194 '''Handle packets from hardware source, i.e. DWT block.'''
195 plen = (0, 1, 2, 4)[buf[0] & 0x03]
197 if len(buf) != plen + 1:
198 return None # Not complete yet.
216 excnum = ((buf[2] & 1) << 8) | buf[1]
217 event = (buf[2] >> 4)
218 excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
220 self.mode_change(excstr)
221 return [5, ['Enter: ' + excstr, 'E ' + excstr]]
223 self.mode_change(None)
224 return [5, ['Exit: ' + excstr, 'X ' + excstr]]
226 self.mode_change(excstr)
227 return [5, ['Resume: ' + excstr, 'R ' + excstr]]
229 pc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
230 self.location_change(pc)
231 return [6, ['PC: 0x%08x' % pc]]
232 elif (buf[0] & 0xC4) == 0x84:
233 comp = (buf[0] & 0x30) >> 4
234 what = 'Read' if (buf[0] & 0x08) == 0 else 'Write'
236 data = '0x%02x' % (buf[1])
238 data = '0x%04x' % (buf[1] | (buf[2] << 8))
240 data = '0x%08x' % (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24))
241 return [4, ['Watchpoint %d: %s data %s' % (comp, what, data),
242 'WP%d: %s %s' % (comp, what[0], data)]]
243 elif (buf[0] & 0xCF) == 0x47:
244 comp = (buf[0] & 0x30) >> 4
245 addr = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
246 self.location_change(addr)
247 return [4, ['Watchpoint %d: PC 0x%08x' % (comp, addr),
248 'WP%d: PC 0x%08x' % (comp, addr)]]
249 elif (buf[0] & 0xCF) == 0x4E:
250 comp = (buf[0] & 0x30) >> 4
251 offset = buf[1] | (buf[2] << 8)
252 return [4, ['Watchpoint %d: address 0x????%04x' % (comp, offset),
253 'WP%d: A 0x%04x' % (comp, offset)]]
255 return self.fallback(buf)
257 def handle_software(self, buf):
258 '''Handle packets generated by software running on the CPU.'''
259 plen = (0, 1, 2, 4)[buf[0] & 0x03]
261 if len(buf) != plen + 1:
262 return None # Not complete yet.
264 if plen == 1 and chr(buf[1]) in string.printable:
265 self.add_delayed_sw(pid, chr(buf[1]))
266 return [] # Handled but no data to output.
268 self.push_delayed_sw()
271 return [2, ['%d: 0x%02x' % (pid, buf[1])]]
273 return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
275 return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
277 def handle_timestamp(self, buf):
278 '''Handle timestamp packets, which indicate the time of some DWT event packet.'''
279 if buf[-1] & 0x80 != 0:
280 return None # Not complete yet.
282 if buf[0] & 0x80 == 0:
286 tc = (buf[0] & 0x30) >> 4
289 ts |= (buf[2] & 0x7F) << 7
291 ts |= (buf[3] & 0x7F) << 14
293 ts |= (buf[4] & 0x7F) << 21
295 self.dwt_timestamp += ts
300 msg = '(timestamp delayed)'
302 msg = '(event delayed)'
304 msg = '(event and timestamp delayed)'
306 return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
308 def add_delayed_sw(self, pid, c):
309 '''We join printable characters from software source so that printed
310 strings are easy to read. Joining is done by PID so that different
311 sources do not get confused with each other.'''
312 if self.swpackets.get(pid) is not None:
313 self.swpackets[pid][1] = self.prevsample
314 self.swpackets[pid][2] += c
316 self.swpackets[pid] = [self.startsample, self.prevsample, c]
318 def push_delayed_sw(self):
319 for pid, packet in self.swpackets.items():
322 ss, prevtime, text = packet
323 # Heuristic criterion: Text has ended if at least 16 byte
324 # durations after previous received byte. Actual delay depends
325 # on printf implementation on target.
326 if self.prevsample - prevtime > 16 * self.byte_len:
327 self.put(ss, prevtime, self.out_ann, [2, ['%d: "%s"' % (pid, text)]])
328 self.swpackets[pid] = None
330 def decode(self, ss, es, data):
331 ptype, rxtx, pdata = data
333 # For now, ignore all UART packets except the actual data packets.
337 self.byte_len = es - ss
339 # Reset packet if there is a long pause between bytes.
340 # TPIU framing can introduce small pauses, but more than 1 frame
341 # should reset packet.
342 if ss - self.prevsample > 16 * self.byte_len:
343 self.push_delayed_sw()
347 # Build up the current packet byte by byte.
348 self.buf.append(pdata[0])
350 # Store the start time of the packet.
351 if len(self.buf) == 1:
352 self.startsample = ss
354 # Keep separate buffer for detection of sync packets.
355 # Sync packets override everything else, so that we can regain sync
356 # even if some packets are corrupted.
357 self.syncbuf = self.syncbuf[-5:] + [pdata[0]]
358 if self.syncbuf == [0, 0, 0, 0, 0, 0x80]:
359 self.buf = self.syncbuf
361 # See if it is ready to be decoded.
362 ptype = self.get_packet_type(self.buf[0])
363 if hasattr(self, 'handle_' + ptype):
364 func = getattr(self, 'handle_' + ptype)
365 data = func(self.buf)
367 data = self.fallback(self.buf)
371 self.put(self.startsample, es, self.out_ann, data)