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
39 class Decoder(srd.Decoder):
43 longname = 'ARM Instrumentation Trace Macroblock'
44 desc = 'Trace data from Cortex-M / ARMv7m ITM module.'
49 {'id': 'addr2line', 'desc': 'addr2line path',
50 'default': 'arm-none-eabi-addr2line'},
51 {'id': 'addr2line_opts', 'desc': 'addr2line options',
52 'default': '-f -C -s -p'},
53 {'id': 'elffile', 'desc': '.elf path', 'default': ''},
56 ('trace', 'Trace information'),
57 ('timestamp', 'Timestamp'),
58 ('software', 'Software message'),
59 ('dwt_event', 'DWT event'),
60 ('dwt_watchpoint', 'DWT watchpoint'),
61 ('dwt_exc', 'Exception trace'),
62 ('dwt_pc', 'Program counter'),
63 ('mode_thread', 'Current mode: thread'),
64 ('mode_irq', 'Current mode: IRQ'),
65 ('mode_exc', 'Current mode: Exception'),
66 ('location', 'Current location')
69 ('trace', 'Trace information', (0, 1)),
70 ('software', 'Software trace', (2,)),
71 ('dwt_event', 'DWT event', (3,)),
72 ('dwt_watchpoint', 'DWT watchpoint', (4,)),
73 ('dwt_exc', 'Exception trace', (5,)),
74 ('dwt_pc', 'Program counter', (6,)),
75 ('mode', 'Current mode', (7, 8, 9,)),
76 ('location', 'Current location', (10,)),
79 def __init__(self, **kwargs):
84 self.dwt_timestamp = 0
85 self.current_mode = None
86 self.current_loc = None
89 self.out_ann = self.register(srd.OUTPUT_ANN)
91 def get_packet_type(self, byte):
92 '''Identify packet type based on its first byte.
93 See ARMv7-M_ARM.pdf section "Debug ITM and DWT" "Packet Types"
99 elif byte & 0x0F == 0 and byte & 0xF0 != 0:
101 elif byte & 0x0F == 0x08:
102 return 'sw_extension'
103 elif byte & 0x0F == 0x0C:
104 return 'hw_extension'
105 elif byte & 0x0F == 0x04:
107 elif byte & 0x04 == 0x00:
112 def mode_change(self, new_mode):
113 if self.current_mode is not None:
114 start, mode = self.current_mode
115 if mode.startswith('Thread'):
117 elif mode.startswith('IRQ'):
121 self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
124 self.current_mode = None
126 self.current_mode = (self.startsample, new_mode)
128 def location_change(self, new_pc):
129 if self.options['addr2line'] and self.options['elffile']:
130 opts = [self.options['addr2line'], '-e', self.options['elffile']]
131 opts += self.options['addr2line_opts'].split()
132 opts += ['0x%08x' % new_pc]
135 new_loc = subprocess.check_output(opts)
136 except subprocess.CalledProcessError:
139 new_loc = new_loc.decode('utf-8', 'replace').strip()
141 if self.current_loc is not None:
142 start, loc = self.current_loc
144 return # Still on same line.
145 self.put(start, self.startsample, self.out_ann, [10, [loc]])
147 self.current_loc = (self.startsample, new_loc)
149 def fallback(self, buf):
150 ptype = self.get_packet_type(buf[0])
151 return [0, [('Unhandled %s: ' % ptype) + ' '.join(['%02x' % b for b in buf])]]
153 def handle_overflow(self, buf):
154 return [0, ['Overflow']]
156 def handle_hardware(self, buf):
157 '''Handle packets from hardware source, i.e. DWT block.'''
158 plen = (0, 1, 2, 4)[buf[0] & 0x03]
160 if len(buf) != plen + 1:
161 return None # Not complete yet.
179 excnum = ((buf[2] & 1) << 8) | buf[1]
180 event = (buf[2] >> 4)
181 excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
183 self.mode_change(excstr)
184 return [5, ['Enter: ' + excstr, 'E ' + excstr]]
186 self.mode_change(None)
187 return [5, ['Exit: ' + excstr, 'X ' + excstr]]
189 self.mode_change(excstr)
190 return [5, ['Resume: ' + excstr, 'R ' + excstr]]
192 pc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
193 self.location_change(pc)
194 return [6, ['PC: 0x%08x' % pc]]
195 elif (buf[0] & 0xC4) == 0x84:
196 comp = (buf[0] & 0x30) >> 4
197 what = 'Read' if (buf[0] & 0x08) == 0 else 'Write'
199 data = '0x%02x' % (buf[1])
201 data = '0x%04x' % (buf[1] | (buf[2] << 8))
203 data = '0x%08x' % (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24))
204 return [4, ['Watchpoint %d: %s data %s' % (comp, what, data),
205 'WP%d: %s %s' % (comp, what[0], data)]]
206 elif (buf[0] & 0xCF) == 0x47:
207 comp = (buf[0] & 0x30) >> 4
208 addr = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
209 self.location_change(addr)
210 return [4, ['Watchpoint %d: PC 0x%08x' % (comp, addr),
211 'WP%d: PC 0x%08x' % (comp, addr)]]
212 elif (buf[0] & 0xCF) == 0x4E:
213 comp = (buf[0] & 0x30) >> 4
214 offset = buf[1] | (buf[2] << 8)
215 return [4, ['Watchpoint %d: address 0x????%04x' % (comp, offset),
216 'WP%d: A 0x%04x' % (comp, offset)]]
218 return self.fallback(buf)
220 def handle_software(self, buf):
221 '''Handle packets generated by software running on the CPU.'''
222 plen = (0, 1, 2, 4)[buf[0] & 0x03]
224 if len(buf) != plen + 1:
225 return None # Not complete yet.
227 if plen == 1 and chr(buf[1]) in string.printable:
228 self.add_delayed_sw(pid, chr(buf[1]))
229 return [] # Handled but no data to output.
231 self.push_delayed_sw()
234 return [2, ['%d: 0x%02x' % (pid, buf[1])]]
236 return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
238 return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
240 def handle_timestamp(self, buf):
241 '''Handle timestamp packets, which indicate the time of some DWT event packet.'''
242 if buf[-1] & 0x80 != 0:
243 return None # Not complete yet.
245 if buf[0] & 0x80 == 0:
249 tc = (buf[0] & 0x30) >> 4
252 ts |= (buf[2] & 0x7F) << 7
254 ts |= (buf[3] & 0x7F) << 14
256 ts |= (buf[4] & 0x7F) << 21
258 self.dwt_timestamp += ts
263 msg = '(timestamp delayed)'
265 msg = '(event delayed)'
267 msg = '(event and timestamp delayed)'
269 return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
271 def add_delayed_sw(self, pid, c):
272 '''We join printable characters from software source so that printed
273 strings are easy to read. Joining is done by PID so that different
274 sources do not get confused with each other.'''
275 if self.swpackets.get(pid) is not None:
276 self.swpackets[pid][1] = self.prevsample
277 self.swpackets[pid][2] += c
279 self.swpackets[pid] = [self.startsample, self.prevsample, c]
281 def push_delayed_sw(self):
282 for pid, packet in self.swpackets.items():
285 ss, prevtime, text = packet
286 # Heuristic criterion: Text has ended if at least 16 byte
287 # durations after previous received byte. Actual delay depends
288 # on printf implementation on target.
289 if self.prevsample - prevtime > 16 * self.byte_len:
290 self.put(ss, prevtime, self.out_ann, [2, ['%d: "%s"' % (pid, text)]])
291 self.swpackets[pid] = None
293 def decode(self, ss, es, data):
294 ptype, rxtx, pdata = data
296 # For now, ignore all UART packets except the actual data packets.
300 self.byte_len = es - ss
302 # Reset packet if there is a long pause between bytes.
303 # TPIU framing can introduce small pauses, but more than 1 frame
304 # should reset packet.
305 if ss - self.prevsample > 16 * self.byte_len:
306 self.push_delayed_sw()
310 # Build up the current packet byte by byte.
311 self.buf.append(pdata[0])
313 # Store the start time of the packet.
314 if len(self.buf) == 1:
315 self.startsample = ss
317 # Keep separate buffer for detection of sync packets.
318 # Sync packets override everything else, so that we can regain sync
319 # even if some packets are corrupted.
320 self.syncbuf = self.syncbuf[-5:] + [pdata[0]]
321 if self.syncbuf == [0, 0, 0, 0, 0, 0x80]:
322 self.buf = self.syncbuf
324 # See if it is ready to be decoded.
325 ptype = self.get_packet_type(self.buf[0])
326 if hasattr(self, 'handle_' + ptype):
327 func = getattr(self, 'handle_' + ptype)
328 data = func(self.buf)
330 data = self.fallback(self.buf)
334 self.put(self.startsample, es, self.out_ann, data)