]> sigrok.org Git - libsigrokdecode.git/blame - decoders/arm_itm/pd.py
Add ARM TPIU/ITM/ETMv3 decoders
[libsigrokdecode.git] / decoders / arm_itm / pd.py
CommitLineData
686f0c36
PA
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
5##
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.
10##
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.
15##
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
19##
20
21import sigrokdecode as srd
22import string
23import subprocess
24
25ARM_EXCEPTIONS = {
26 0: 'Thread',
27 1: 'Reset',
28 2: 'NMI',
29 3: 'HardFault',
30 4: 'MemManage',
31 5: 'BusFault',
32 6: 'UsageFault',
33 11: 'SVCall',
34 12: 'Debug Monitor',
35 14: 'PendSV',
36 15: 'SysTick',
37}
38
39class Decoder(srd.Decoder):
40 api_version = 2
41 id = 'arm_itm'
42 name = 'ARM ITM'
43 longname = 'ARM Instrumentation Trace Macroblock'
44 desc = 'Trace data from Cortex-M / ARMv7m ITM module.'
45 license = 'gplv2+'
46 inputs = ['uart']
47 outputs = ['arm_itm']
48 options = (
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': ''},
54 )
55 annotations = (
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')
67 )
68 annotation_rows = (
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,)),
77 )
78
79 def __init__(self, **kwargs):
80 self.buf = []
81 self.syncbuf = []
82 self.swpackets = {}
83 self.prevsample = 0
84 self.dwt_timestamp = 0
85 self.current_mode = None
86 self.current_loc = None
87
88 def start(self):
89 self.out_ann = self.register(srd.OUTPUT_ANN)
90
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"
94 '''
95 if byte & 0x7F == 0:
96 return 'sync'
97 elif byte == 0x70:
98 return 'overflow'
99 elif byte & 0x0F == 0 and byte & 0xF0 != 0:
100 return 'timestamp'
101 elif byte & 0x0F == 0x08:
102 return 'sw_extension'
103 elif byte & 0x0F == 0x0C:
104 return 'hw_extension'
105 elif byte & 0x0F == 0x04:
106 return 'reserved'
107 elif byte & 0x04 == 0x00:
108 return 'software'
109 else:
110 return 'hardware'
111
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'):
116 ann_idx = 7
117 elif mode.startswith('IRQ'):
118 ann_idx = 8
119 else:
120 ann_idx = 9
121 self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
122
123 if new_mode is None:
124 self.current_mode = None
125 else:
126 self.current_mode = (self.startsample, new_mode)
127
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]
133
134 try:
135 new_loc = subprocess.check_output(opts)
136 except subprocess.CalledProcessError:
137 return
138
139 new_loc = new_loc.decode('utf-8', 'replace').strip()
140
141 if self.current_loc is not None:
142 start, loc = self.current_loc
143 if loc == new_loc:
144 return # Still on same line.
145 self.put(start, self.startsample, self.out_ann, [10, [loc]])
146
147 self.current_loc = (self.startsample, new_loc)
148
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])]]
152
153 def handle_overflow(self, buf):
154 return [0, ['Overflow']]
155
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]
159 pid = buf[0] >> 3
160 if len(buf) != plen + 1:
161 return None # Not complete yet.
162
163 if pid == 0:
164 text = 'DWT events:'
165 if buf[1] & 0x20:
166 text += ' Cyc'
167 if buf[1] & 0x10:
168 text += ' Fold'
169 if buf[1] & 0x08:
170 text += ' LSU'
171 if buf[1] & 0x04:
172 text += ' Sleep'
173 if buf[1] & 0x02:
174 text += ' Exc'
175 if buf[1] & 0x01:
176 text += ' CPI'
177 return [3, [text]]
178 elif pid == 1:
179 excnum = ((buf[2] & 1) << 8) | buf[1]
180 event = (buf[2] >> 4)
181 excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
182 if event == 1:
183 self.mode_change(excstr)
184 return [5, ['Enter: ' + excstr, 'E ' + excstr]]
185 elif event == 2:
186 self.mode_change(None)
187 return [5, ['Exit: ' + excstr, 'X ' + excstr]]
188 elif event == 3:
189 self.mode_change(excstr)
190 return [5, ['Resume: ' + excstr, 'R ' + excstr]]
191 elif pid == 2:
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'
198 if plen == 1:
199 data = '0x%02x' % (buf[1])
200 elif plen == 2:
201 data = '0x%04x' % (buf[1] | (buf[2] << 8))
202 else:
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)]]
217
218 return self.fallback(buf)
219
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]
223 pid = buf[0] >> 3
224 if len(buf) != plen + 1:
225 return None # Not complete yet.
226
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.
230
231 self.push_delayed_sw()
232
233 if plen == 1:
234 return [2, ['%d: 0x%02x' % (pid, buf[1])]]
235 elif plen == 2:
236 return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
237 elif plen == 4:
238 return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
239
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.
244
245 if buf[0] & 0x80 == 0:
246 tc = 0
247 ts = buf[0] >> 4
248 else:
249 tc = (buf[0] & 0x30) >> 4
250 ts = buf[1] & 0x7F
251 if len(buf) > 2:
252 ts |= (buf[2] & 0x7F) << 7
253 if len(buf) > 3:
254 ts |= (buf[3] & 0x7F) << 14
255 if len(buf) > 4:
256 ts |= (buf[4] & 0x7F) << 21
257
258 self.dwt_timestamp += ts
259
260 if tc == 0:
261 msg = '(exact)'
262 elif tc == 1:
263 msg = '(timestamp delayed)'
264 elif tc == 2:
265 msg = '(event delayed)'
266 elif tc == 3:
267 msg = '(event and timestamp delayed)'
268
269 return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
270
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
278 else:
279 self.swpackets[pid] = [self.startsample, self.prevsample, c]
280
281 def push_delayed_sw(self):
282 for pid, packet in self.swpackets.items():
283 if packet is None:
284 continue
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
292
293 def decode(self, ss, es, data):
294 ptype, rxtx, pdata = data
295
296 # For now, ignore all UART packets except the actual data packets.
297 if ptype != 'DATA':
298 return
299
300 self.byte_len = es - ss
301
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()
307 self.buf = []
308 self.prevsample = es
309
310 # Build up the current packet byte by byte.
311 self.buf.append(pdata[0])
312
313 # Store the start time of the packet.
314 if len(self.buf) == 1:
315 self.startsample = ss
316
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
323
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)
329 else:
330 data = self.fallback(self.buf)
331
332 if data is not None:
333 if data:
334 self.put(self.startsample, es, self.out_ann, data)
335 self.buf = []