]> sigrok.org Git - libsigrokdecode.git/blobdiff - decoders/arm_etmv3/pd.py
decoders: Fix incorrect 'outputs' fields.
[libsigrokdecode.git] / decoders / arm_etmv3 / pd.py
index c63714279acbb5b3c029e82d2cdd43d2f0acd337..6649b46e021dd781c0a5a06098fb5fc0af012fb8 100644 (file)
 ## 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):
@@ -60,68 +69,73 @@ def parse_exc_info(bytes):
     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'),
@@ -149,14 +163,17 @@ class Decoder(srd.Decoder):
         {'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
@@ -198,7 +215,7 @@ class Decoder(srd.Decoder):
         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 = ''
@@ -231,10 +248,12 @@ class Decoder(srd.Decoder):
                 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()
 
@@ -260,6 +279,9 @@ class Decoder(srd.Decoder):
         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):
@@ -364,10 +386,10 @@ class Decoder(srd.Decoder):
             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.
@@ -478,13 +500,18 @@ class Decoder(srd.Decoder):
             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:
@@ -494,8 +521,8 @@ class Decoder(srd.Decoder):
             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