]> sigrok.org Git - libsigrokdecode.git/blame - decoders/arm_etmv3/pd.py
avr_isp: Add more parts
[libsigrokdecode.git] / decoders / arm_etmv3 / 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
4539e9ca 17## along with this program; if not, see <http://www.gnu.org/licenses/>.
686f0c36
PA
18##
19
20import sigrokdecode as srd
21import subprocess
22import re
23
1c023fab
PA
24# See ETMv3 Signal Protocol table 7-11: 'Encoding of Exception[8:0]'.
25exc_names = [
26 'No exception', 'IRQ1', 'IRQ2', 'IRQ3', 'IRQ4', 'IRQ5', 'IRQ6', 'IRQ7',
27 'IRQ0', 'UsageFault', 'NMI', 'SVC', 'DebugMon', 'MemManage', 'PendSV',
28 'SysTick', 'Reserved', 'Reset', 'BusFault', 'Reserved', 'Reserved'
29]
30
31for i in range(8, 496):
32 exc_names.append('IRQ%d' % i)
33
16d2031b 34def parse_varint(bytes_):
686f0c36
PA
35 '''Parse an integer where the top bit is the continuation bit.
36 Returns value and number of parsed bytes.'''
37 v = 0
16d2031b 38 for i, b in enumerate(bytes_):
686f0c36
PA
39 v |= (b & 0x7F) << (i * 7)
40 if b & 0x80 == 0:
41 return v, i+1
16d2031b 42 return v, len(bytes_)
686f0c36 43
16d2031b 44def parse_uint(bytes_):
686f0c36
PA
45 '''Parse little-endian integer.'''
46 v = 0
16d2031b 47 for i, b in enumerate(bytes_):
686f0c36
PA
48 v |= b << (i * 8)
49 return v
50
16d2031b 51def parse_exc_info(bytes_):
686f0c36 52 '''Parse exception information bytes from a branch packet.'''
16d2031b 53 if len(bytes_) < 1:
686f0c36
PA
54 return None
55
16d2031b
RJ
56 excv, exclen = parse_varint(bytes_)
57 if bytes_[exclen - 1] & 0x80 != 0x00:
686f0c36
PA
58 return None # Exception info not complete.
59
60 if exclen == 2 and excv & (1 << 13):
61 # Exception byte 1 was skipped, fix up the decoding.
62 excv = (excv & 0x7F) | ((excv & 0x3F80) << 7)
63
64 ns = excv & 1
65 exc = ((excv >> 1) & 0x0F) | ((excv >> 7) & 0x1F0)
66 cancel = (excv >> 5) & 1
67 altisa = (excv >> 6) & 1
68 hyp = (excv >> 12) & 1
69 resume = (excv >> 14) & 0x0F
70 return (ns, exc, cancel, altisa, hyp, resume)
71
16d2031b 72def parse_branch_addr(bytes_, ref_addr, cpu_state, branch_enc):
686f0c36
PA
73 '''Parse encoded branch address.
74 Returns addr, addrlen, cpu_state, exc_info.
75 Returns None if packet is not yet complete'''
76
16d2031b 77 addr, addrlen = parse_varint(bytes_)
686f0c36 78
16d2031b 79 if bytes_[addrlen - 1] & 0x80 != 0x00:
686f0c36
PA
80 return None # Branch address not complete.
81
1c023fab
PA
82 addr_bits = 7 * addrlen
83
686f0c36
PA
84 have_exc_info = False
85 if branch_enc == 'original':
16d2031b 86 if addrlen == 5 and bytes_[4] & 0x40:
686f0c36
PA
87 have_exc_info = True
88 elif branch_enc == 'alternative':
1c023fab
PA
89 addr_bits -= 1 # Top bit of address indicates exc_info.
90 if addrlen >= 2 and addr & (1 << addr_bits):
686f0c36 91 have_exc_info = True
1c023fab 92 addr &= ~(1 << addr_bits)
686f0c36
PA
93
94 exc_info = None
95 if have_exc_info:
16d2031b 96 exc_info = parse_exc_info(bytes_[addrlen:])
686f0c36
PA
97 if exc_info is None:
98 return None # Exception info not complete.
99
100 if addrlen == 5:
101 # Possible change in CPU state.
16d2031b 102 if bytes_[4] & 0xB8 == 0x08:
686f0c36 103 cpu_state = 'arm'
16d2031b 104 elif bytes_[4] & 0xB0 == 0x10:
686f0c36 105 cpu_state = 'thumb'
16d2031b 106 elif bytes_[4] & 0xA0 == 0x20:
686f0c36
PA
107 cpu_state = 'jazelle'
108 else:
16d2031b 109 raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes_[4])
686f0c36
PA
110
111 # Shift the address according to current CPU state.
112 if cpu_state == 'arm':
113 addr = (addr & 0xFFFFFFFE) << 1
1c023fab 114 addr_bits += 1
686f0c36
PA
115 elif cpu_state == 'thumb':
116 addr = addr & 0xFFFFFFFE
117 elif cpu_state == 'jazelle':
118 addr = (addr & 0xFFFFFFFFE) >> 1
1c023fab 119 addr_bits -= 1
686f0c36
PA
120 else:
121 raise NotImplementedError('Unhandled state: ' + cpu_state)
122
123 # If the address wasn't full, fill in with the previous address.
124 if addrlen < 5:
1c023fab 125 addr |= ref_addr & (0xFFFFFFFF << addr_bits)
686f0c36
PA
126
127 return addr, addrlen, cpu_state, exc_info
128
129class Decoder(srd.Decoder):
b197383c 130 api_version = 3
686f0c36
PA
131 id = 'arm_etmv3'
132 name = 'ARM ETMv3'
2787cf2a
UH
133 longname = 'ARM Embedded Trace Macroblock v3'
134 desc = 'ARM ETM v3 instruction trace protocol.'
686f0c36
PA
135 license = 'gplv2+'
136 inputs = ['uart']
6cbba91f 137 outputs = []
d6d8a8a4 138 tags = ['Debug/trace']
686f0c36
PA
139 annotations = (
140 ('trace', 'Trace info'),
e144452b
UH
141 ('branch', 'Branch'),
142 ('exception', 'Exception'),
686f0c36
PA
143 ('execution', 'Instruction execution'),
144 ('data', 'Data access'),
145 ('pc', 'Program counter'),
e144452b
UH
146 ('instr_e', 'Executed instruction'),
147 ('instr_n', 'Not executed instruction'),
686f0c36
PA
148 ('source', 'Source code'),
149 ('location', 'Current location'),
150 ('function', 'Current function'),
151 )
152 annotation_rows = (
e144452b 153 ('traces', 'Trace info', (0,)),
686f0c36 154 ('flow', 'Code flow', (1, 2, 3,)),
e144452b
UH
155 ('data-vals', 'Data access', (4,)),
156 ('pc-vals', 'Program counters', (5,)),
157 ('instructions', 'Instructions', (6, 7,)),
158 ('sources', 'Source code', (8,)),
159 ('locations', 'Current locations', (9,)),
160 ('functions', 'Current functions', (10,)),
686f0c36
PA
161 )
162 options = (
163 {'id': 'objdump', 'desc': 'objdump path',
164 'default': 'arm-none-eabi-objdump'},
165 {'id': 'objdump_opts', 'desc': 'objdump options',
1c023fab 166 'default': '-lSC'},
686f0c36
PA
167 {'id': 'elffile', 'desc': '.elf path',
168 'default': ''},
169 {'id': 'branch_enc', 'desc': 'Branch encoding',
170 'default': 'alternative', 'values': ('alternative', 'original')},
171 )
172
92b7b49f 173 def __init__(self):
10aeb8ea
GS
174 self.reset()
175
176 def reset(self):
686f0c36
PA
177 self.buf = []
178 self.syncbuf = []
179 self.prevsample = 0
180 self.last_branch = 0
181 self.cpu_state = 'arm'
182 self.current_pc = 0
183 self.current_loc = None
184 self.current_func = None
185 self.next_instr_lookup = {}
186 self.file_lookup = {}
187 self.func_lookup = {}
188 self.disasm_lookup = {}
189 self.source_lookup = {}
190
191 def start(self):
192 self.out_ann = self.register(srd.OUTPUT_ANN)
193 self.load_objdump()
194
195 def load_objdump(self):
196 '''Parse disassembly obtained from objdump into two tables:
197 next_instr_lookup: Find the next PC addr from current PC.
198 disasm_lookup: Find the instruction text from current PC.
199 source_lookup: Find the source code line from current PC.
200 '''
201 if not (self.options['objdump'] and self.options['elffile']):
202 return
203
204 opts = [self.options['objdump']]
205 opts += self.options['objdump_opts'].split()
206 opts += [self.options['elffile']]
207
208 try:
209 disasm = subprocess.check_output(opts)
210 except subprocess.CalledProcessError:
211 return
212
213 disasm = disasm.decode('utf-8', 'replace')
214
215 instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
216 branchpat = re.compile('(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)')
217 filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
1c023fab 218 funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
686f0c36
PA
219
220 prev_src = ''
221 prev_file = ''
222 prev_func = ''
223
224 for line in disasm.split('\n'):
225 m = instpat.match(line)
226 if m:
227 addr = int(m.group(1), 16)
228 raw = m.group(2)
229 disas = m.group(3).strip().replace('\t', ' ')
230 self.disasm_lookup[addr] = disas
231 self.source_lookup[addr] = prev_src
232 self.file_lookup[addr] = prev_file
233 self.func_lookup[addr] = prev_func
234
235 # Next address in direct sequence.
236 ilen = len(raw.replace(' ', '')) // 2
237 next_n = addr + ilen
238
239 # Next address if branch is taken.
240 bm = branchpat.match(disas)
241 if bm:
242 next_e = int(bm.group(2), 16)
243 else:
244 next_e = next_n
245
246 self.next_instr_lookup[addr] = (next_n, next_e)
247 else:
248 m = funcpat.match(line)
249 if m:
250 prev_func = m.group(1)
1c023fab 251 prev_src = None
686f0c36
PA
252 else:
253 m = filepat.match(line)
254 if m:
255 prev_file = m.group(1)
1c023fab 256 prev_src = None
686f0c36
PA
257 else:
258 prev_src = line.strip()
259
260 def flush_current_loc(self):
261 if self.current_loc is not None:
262 ss, es, loc, src = self.current_loc
263 if loc:
264 self.put(ss, es, self.out_ann, [9, [loc]])
265 if src:
266 self.put(ss, es, self.out_ann, [8, [src]])
267 self.current_loc = None
268
269 def flush_current_func(self):
270 if self.current_func is not None:
271 ss, es, func = self.current_func
272 if func:
273 self.put(ss, es, self.out_ann, [10, [func]])
274 self.current_func = None
275
276 def instructions_executed(self, exec_status):
277 '''Advance program counter based on executed instructions.
278 Argument is a list of False for not executed and True for executed
279 instructions.
280 '''
281
1c023fab
PA
282 if len(exec_status) == 0:
283 return
284
686f0c36
PA
285 tdelta = max(1, (self.prevsample - self.startsample) / len(exec_status))
286
287 for i, exec_status in enumerate(exec_status):
288 pc = self.current_pc
289 default_next = pc + 2 if self.cpu_state == 'thumb' else pc + 4
290 target_n, target_e = self.next_instr_lookup.get(pc, (default_next, default_next))
291 ss = self.startsample + round(tdelta * i)
292 es = self.startsample + round(tdelta * (i+1))
293
294 self.put(ss, es, self.out_ann,
295 [5, ['PC 0x%08x' % pc, '0x%08x' % pc, '%08x' % pc]])
296
297 new_loc = self.file_lookup.get(pc)
298 new_src = self.source_lookup.get(pc)
299 new_dis = self.disasm_lookup.get(pc)
300 new_func = self.func_lookup.get(pc)
301
302 # Report source line only when it changes.
303 if self.current_loc is not None:
304 if new_loc != self.current_loc[2] or new_src != self.current_loc[3]:
305 self.flush_current_loc()
306
307 if self.current_loc is None:
308 self.current_loc = [ss, es, new_loc, new_src]
309 else:
310 self.current_loc[1] = es
311
312 # Report function name only when it changes.
313 if self.current_func is not None:
314 if new_func != self.current_func[2]:
315 self.flush_current_func()
316
317 if self.current_func is None:
318 self.current_func = [ss, es, new_func]
319 else:
320 self.current_func[1] = es
321
322 # Report instruction every time.
323 if new_dis:
324 if exec_status:
325 a = [6, ['Executed: ' + new_dis, new_dis, new_dis.split()[0]]]
326 else:
327 a = [7, ['Not executed: ' + new_dis, new_dis, new_dis.split()[0]]]
328 self.put(ss, es, self.out_ann, a)
329
330 if exec_status:
331 self.current_pc = target_e
332 else:
333 self.current_pc = target_n
334
335 def get_packet_type(self, byte):
336 '''Identify packet type based on its first byte.
337 See ARM IHI0014Q section "ETMv3 Signal Protocol" "Packet Types"
338 '''
339 if byte & 0x01 == 0x01:
340 return 'branch'
341 elif byte == 0x00:
342 return 'a_sync'
343 elif byte == 0x04:
344 return 'cyclecount'
345 elif byte == 0x08:
346 return 'i_sync'
347 elif byte == 0x0C:
348 return 'trigger'
349 elif byte & 0xF3 in (0x20, 0x40, 0x60):
350 return 'ooo_data'
351 elif byte == 0x50:
352 return 'store_failed'
353 elif byte == 0x70:
354 return 'i_sync'
355 elif byte & 0xDF in (0x54, 0x58, 0x5C):
356 return 'ooo_place'
357 elif byte == 0x3C:
358 return 'vmid'
359 elif byte & 0xD3 == 0x02:
360 return 'data'
361 elif byte & 0xFB == 0x42:
362 return 'timestamp'
363 elif byte == 0x62:
364 return 'data_suppressed'
365 elif byte == 0x66:
366 return 'ignore'
367 elif byte & 0xEF == 0x6A:
368 return 'value_not_traced'
369 elif byte == 0x6E:
370 return 'context_id'
371 elif byte == 0x76:
372 return 'exception_exit'
373 elif byte == 0x7E:
374 return 'exception_entry'
375 elif byte & 0x81 == 0x80:
376 return 'p_header'
377 else:
378 return 'unknown'
379
380 def fallback(self, buf):
381 ptype = self.get_packet_type(buf[0])
382 return [0, ['Unhandled ' + ptype + ': ' + ' '.join(['%02x' % b for b in buf])]]
383
384 def handle_a_sync(self, buf):
385 if buf[-1] == 0x80:
386 return [0, ['Synchronization']]
387
388 def handle_exception_exit(self, buf):
1c023fab 389 return [2, ['Exception exit']]
686f0c36
PA
390
391 def handle_exception_entry(self, buf):
1c023fab 392 return [2, ['Exception entry']]
686f0c36
PA
393
394 def handle_i_sync(self, buf):
395 contextid_bytes = 0 # This is the default ETM config.
396
397 if len(buf) < 6:
398 return None # Packet definitely not full yet.
399
400 if buf[0] == 0x08: # No cycle count.
401 cyclecount = None
402 idx = 1 + contextid_bytes # Index to info byte.
403 elif buf[0] == 0x70: # With cycle count.
404 cyclecount, cyclen = parse_varint(buf[1:6])
405 idx = 1 + cyclen + contextid_bytes
406
407 if len(buf) <= idx + 4:
408 return None
409 infobyte = buf[idx]
410 addr = parse_uint(buf[idx+1:idx+5])
411
412 reasoncode = (infobyte >> 5) & 3
413 reason = ('Periodic', 'Tracing enabled', 'After overflow', 'Exit from debug')[reasoncode]
414 jazelle = (infobyte >> 4) & 1
415 nonsec = (infobyte >> 3) & 1
416 altisa = (infobyte >> 2) & 1
417 hypervisor = (infobyte >> 1) & 1
418 thumb = addr & 1
419 addr &= 0xFFFFFFFE
420
421 if reasoncode == 0 and self.current_pc != addr:
422 self.put(self.startsample, self.prevsample, self.out_ann,
423 [0, ['WARN: Unexpected PC change 0x%08x -> 0x%08x' % \
424 (self.current_pc, addr)]])
425 elif reasoncode != 0:
426 # Reset location when the trace has been interrupted.
427 self.flush_current_loc()
428 self.flush_current_func()
429
430 self.last_branch = addr
431 self.current_pc = addr
432
433 if jazelle:
434 self.cpu_state = 'jazelle'
435 elif thumb:
436 self.cpu_state = 'thumb'
437 else:
438 self.cpu_state = 'arm'
439
440 cycstr = ''
441 if cyclecount is not None:
442 cycstr = ', cyclecount %d' % cyclecount
443
444 if infobyte & 0x80: # LSIP packet
445 self.put(self.startsample, self.prevsample, self.out_ann,
446 [0, ['WARN: LSIP I-Sync packet not implemented']])
447
448 return [0, ['I-Sync: %s, PC 0x%08x, %s state%s' % \
449 (reason, addr, self.cpu_state, cycstr), \
450 'I-Sync: %s 0x%08x' % (reason, addr)]]
451
452 def handle_trigger(self, buf):
453 return [0, ['Trigger event', 'Trigger']]
454
455 def handle_p_header(self, buf):
456 # Only non cycle-accurate mode supported.
457 if buf[0] & 0x83 == 0x80:
458 n = (buf[0] >> 6) & 1
459 e = (buf[0] >> 2) & 15
460
461 self.instructions_executed([1] * e + [0] * n)
462
463 if n:
464 return [3, ['%d instructions executed, %d skipped due to ' \
465 'condition codes' % (e, n),
466 '%d ins exec, %d skipped' % (e, n),
467 '%dE,%dN' % (e, n)]]
468 else:
469 return [3, ['%d instructions executed' % e,
470 '%d ins exec' % e, '%dE' % e]]
471 elif buf[0] & 0xF3 == 0x82:
472 i1 = (buf[0] >> 3) & 1
473 i2 = (buf[0] >> 2) & 1
474 self.instructions_executed([not i1, not i2])
475 txt1 = ('executed', 'skipped')
476 txt2 = ('E', 'S')
477 return [3, ['Instruction 1 %s, instruction 2 %s' % (txt1[i1], txt1[i2]),
478 'I1 %s, I2 %s' % (txt2[i1], txt2[i2]),
479 '%s,%s' % (txt2[i1], txt2[i2])]]
480 else:
481 return self.fallback(buf)
482
483 def handle_branch(self, buf):
484 if buf[-1] & 0x80 != 0x00:
485 return None # Not complete yet.
486
487 brinfo = parse_branch_addr(buf, self.last_branch, self.cpu_state,
488 self.options['branch_enc'])
489
490 if brinfo is None:
491 return None # Not complete yet.
492
493 addr, addrlen, cpu_state, exc_info = brinfo
494 self.last_branch = addr
495 self.current_pc = addr
496
497 txt = ''
498
499 if cpu_state != self.cpu_state:
500 txt += ', to %s state' % cpu_state
501 self.cpu_state = cpu_state
502
1c023fab
PA
503 annidx = 1
504
686f0c36 505 if exc_info:
1c023fab 506 annidx = 2
686f0c36
PA
507 ns, exc, cancel, altisa, hyp, resume = exc_info
508 if ns:
509 txt += ', to non-secure state'
510 if exc:
1c023fab
PA
511 if exc < len(exc_names):
512 txt += ', exception %s' % exc_names[exc]
513 else:
514 txt += ', exception 0x%02x' % exc
686f0c36
PA
515 if cancel:
516 txt += ', instr cancelled'
517 if altisa:
518 txt += ', to AltISA'
519 if hyp:
520 txt += ', to hypervisor'
521 if resume:
522 txt += ', instr resume 0x%02x' % resume
523
1c023fab
PA
524 return [annidx, ['Branch to 0x%08x%s' % (addr, txt),
525 'B 0x%08x%s' % (addr, txt)]]
686f0c36
PA
526
527 def decode(self, ss, es, data):
528 ptype, rxtx, pdata = data
529
530 if ptype != 'DATA':
531 return
532
533 # Reset packet if there is a long pause between bytes.
534 # This helps getting the initial synchronization.
535 self.byte_len = es - ss
536 if ss - self.prevsample > 16 * self.byte_len:
537 self.flush_current_loc()
538 self.flush_current_func()
539 self.buf = []
540 self.prevsample = es
541
542 self.buf.append(pdata[0])
543
544 # Store the start time of the packet.
545 if len(self.buf) == 1:
546 self.startsample = ss
547
548 # Keep separate buffer for detection of sync packets.
549 # Sync packets override everything else, so that we can regain sync
550 # even if some packets are corrupted.
551 self.syncbuf = self.syncbuf[-4:] + [pdata[0]]
552 if self.syncbuf == [0x00, 0x00, 0x00, 0x00, 0x80]:
553 self.buf = self.syncbuf
554 self.syncbuf = []
555
556 # See if it is ready to be decoded.
557 ptype = self.get_packet_type(self.buf[0])
558 if hasattr(self, 'handle_' + ptype):
559 func = getattr(self, 'handle_' + ptype)
560 data = func(self.buf)
561 else:
562 data = self.fallback(self.buf)
563
564 if data is not None:
565 if data:
566 self.put(self.startsample, es, self.out_ann, data)
567 self.buf = []