## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
import subprocess
import re
-def parse_varint(bytes):
+# See ETMv3 Signal Protocol table 7-11: 'Encoding of Exception[8:0]'.
+exc_names = [
+ 'No exception', 'IRQ1', 'IRQ2', 'IRQ3', 'IRQ4', 'IRQ5', 'IRQ6', 'IRQ7',
+ 'IRQ0', 'UsageFault', 'NMI', 'SVC', 'DebugMon', 'MemManage', 'PendSV',
+ 'SysTick', 'Reserved', 'Reset', 'BusFault', 'Reserved', 'Reserved'
+]
+
+for i in range(8, 496):
+ exc_names.append('IRQ%d' % i)
+
+def parse_varint(bytes_):
'''Parse an integer where the top bit is the continuation bit.
Returns value and number of parsed bytes.'''
v = 0
- for i, b in enumerate(bytes):
+ for i, b in enumerate(bytes_):
v |= (b & 0x7F) << (i * 7)
if b & 0x80 == 0:
return v, i+1
- return v, len(bytes)
+ return v, len(bytes_)
-def parse_uint(bytes):
+def parse_uint(bytes_):
'''Parse little-endian integer.'''
v = 0
- for i, b in enumerate(bytes):
+ for i, b in enumerate(bytes_):
v |= b << (i * 8)
return v
-def parse_exc_info(bytes):
+def parse_exc_info(bytes_):
'''Parse exception information bytes from a branch packet.'''
- if len(bytes) < 1:
+ if len(bytes_) < 1:
return None
- excv, exclen = parse_varint(bytes)
- if bytes[exclen - 1] & 0x80 != 0x00:
+ excv, exclen = parse_varint(bytes_)
+ if bytes_[exclen - 1] & 0x80 != 0x00:
return None # Exception info not complete.
if exclen == 2 and excv & (1 << 13):
resume = (excv >> 14) & 0x0F
return (ns, exc, cancel, altisa, hyp, resume)
-def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc):
+def parse_branch_addr(bytes_, ref_addr, cpu_state, branch_enc):
'''Parse encoded branch address.
Returns addr, addrlen, cpu_state, exc_info.
Returns None if packet is not yet complete'''
- addr, addrlen = parse_varint(bytes)
+ addr, addrlen = parse_varint(bytes_)
- if bytes[addrlen-1] & 0x80 != 0x00:
+ if bytes_[addrlen - 1] & 0x80 != 0x00:
return None # Branch address not complete.
+ addr_bits = 7 * addrlen
+
have_exc_info = False
if branch_enc == 'original':
- if addrlen == 5 and bytes[4] & 0x40:
+ if addrlen == 5 and bytes_[4] & 0x40:
have_exc_info = True
elif branch_enc == 'alternative':
- if addrlen >= 2 and bytes[addrlen - 1] & 0x40:
+ addr_bits -= 1 # Top bit of address indicates exc_info.
+ if addrlen >= 2 and addr & (1 << addr_bits):
have_exc_info = True
- addr &= ~(1 << (addrlen * 7 - 1))
+ addr &= ~(1 << addr_bits)
exc_info = None
if have_exc_info:
- exc_info = parse_exc_info(bytes[addrlen:])
+ exc_info = parse_exc_info(bytes_[addrlen:])
if exc_info is None:
return None # Exception info not complete.
if addrlen == 5:
# Possible change in CPU state.
- if bytes[4] & 0xB8 == 0x08:
+ if bytes_[4] & 0xB8 == 0x08:
cpu_state = 'arm'
- elif bytes[4] & 0xB0 == 0x10:
+ elif bytes_[4] & 0xB0 == 0x10:
cpu_state = 'thumb'
- elif bytes[4] & 0xA0 == 0x20:
+ elif bytes_[4] & 0xA0 == 0x20:
cpu_state = 'jazelle'
else:
- raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes[4])
+ raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes_[4])
# Shift the address according to current CPU state.
if cpu_state == 'arm':
addr = (addr & 0xFFFFFFFE) << 1
+ addr_bits += 1
elif cpu_state == 'thumb':
addr = addr & 0xFFFFFFFE
elif cpu_state == 'jazelle':
addr = (addr & 0xFFFFFFFFE) >> 1
+ addr_bits -= 1
else:
raise NotImplementedError('Unhandled state: ' + cpu_state)
# If the address wasn't full, fill in with the previous address.
if addrlen < 5:
- bits = 7 * addrlen
- addr |= ref_addr & (0xFFFFFFFF << bits)
+ addr |= ref_addr & (0xFFFFFFFF << addr_bits)
return addr, addrlen, cpu_state, exc_info
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'arm_etmv3'
name = 'ARM ETMv3'
- longname = 'ARM Embedded Trace Macroblock'
- desc = 'Decode ETM instruction trace packets.'
+ longname = 'ARM Embedded Trace Macroblock v3'
+ desc = 'ARM ETM v3 instruction trace protocol.'
license = 'gplv2+'
inputs = ['uart']
- outputs = ['arm_etmv3']
+ outputs = []
+ tags = ['Debug/trace']
annotations = (
('trace', 'Trace info'),
('branch', 'Branches'),
{'id': 'objdump', 'desc': 'objdump path',
'default': 'arm-none-eabi-objdump'},
{'id': 'objdump_opts', 'desc': 'objdump options',
- 'default': '-lS'},
+ 'default': '-lSC'},
{'id': 'elffile', 'desc': '.elf path',
'default': ''},
{'id': 'branch_enc', 'desc': 'Branch encoding',
'default': 'alternative', 'values': ('alternative', 'original')},
)
- def __init__(self, **kwargs):
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
self.buf = []
self.syncbuf = []
self.prevsample = 0
instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
branchpat = re.compile('(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)')
filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
- funcpat = re.compile('[0-9a-fA-F]+\s*<([a-zA-Z0-9_-]+)>:.*')
+ funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
prev_src = ''
prev_file = ''
m = funcpat.match(line)
if m:
prev_func = m.group(1)
+ prev_src = None
else:
m = filepat.match(line)
if m:
prev_file = m.group(1)
+ prev_src = None
else:
prev_src = line.strip()
instructions.
'''
+ if len(exec_status) == 0:
+ return
+
tdelta = max(1, (self.prevsample - self.startsample) / len(exec_status))
for i, exec_status in enumerate(exec_status):
return [0, ['Synchronization']]
def handle_exception_exit(self, buf):
- return [2, 'Exception exit']
+ return [2, ['Exception exit']]
def handle_exception_entry(self, buf):
- return [1, 'Exception entry']
+ return [2, ['Exception entry']]
def handle_i_sync(self, buf):
contextid_bytes = 0 # This is the default ETM config.
txt += ', to %s state' % cpu_state
self.cpu_state = cpu_state
+ annidx = 1
+
if exc_info:
+ annidx = 2
ns, exc, cancel, altisa, hyp, resume = exc_info
if ns:
txt += ', to non-secure state'
if exc:
- # TODO: Parse the exception value to text.
- txt += ', exception 0x%02x' % exc
+ if exc < len(exc_names):
+ txt += ', exception %s' % exc_names[exc]
+ else:
+ txt += ', exception 0x%02x' % exc
if cancel:
txt += ', instr cancelled'
if altisa:
if resume:
txt += ', instr resume 0x%02x' % resume
- return [1, ['Branch to 0x%08x%s' % (addr, txt),
- 'B 0x%08x%s' % (addr, txt)]]
+ return [annidx, ['Branch to 0x%08x%s' % (addr, txt),
+ 'B 0x%08x%s' % (addr, txt)]]
def decode(self, ss, es, data):
ptype, rxtx, pdata = data