Hi Sigrokkers,

I added some features to the spiflash decoder that might be
useful. Patches attached:

- Handle variable-length transactions by adding a callback on CS#
  release.

- Decode Fast Read Dual I/O commands. (Similar approach could be used
  for Fast Read Dual Output but I don't have a suitable capture to
  verify against.)

- Small tweak to build a dictionary once instead of on every SPI event.

There's also this capture featuring Dual I/O commands here, if that's
of interest for the captures repo:
https://www.dropbox.com/s/rt0o69nesvzkzpq/dualioreads.sr?dl=0

Cheers,


Angus
>From d1428304030bc7d7a0766ed39bb8ff6f611111a2 Mon Sep 17 00:00:00 2001
From: Angus Gratton <g...@projectgus.com>
Date: Sun, 22 May 2016 11:42:52 +1000
Subject: [PATCH 1/3] spiflash decoder: Handle CS# transitions, allow
 variable-length transfers

---
 decoders/spiflash/pd.py | 80 +++++++++++++++++++------------------------------
 1 file changed, 30 insertions(+), 50 deletions(-)

diff --git a/decoders/spiflash/pd.py b/decoders/spiflash/pd.py
index 1e3b916..83abf82 100644
--- a/decoders/spiflash/pd.py
+++ b/decoders/spiflash/pd.py
@@ -74,6 +74,13 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.on_end_transaction = None
+        self.end_current_transaction()
+
+    def end_current_transaction(self):
+        if self.on_end_transaction is not None: # callback for CS# transition
+            self.on_end_transaction()
+            self.on_end_transaction = None
         self.state = None
         self.cmdstate = 1
         self.addr = 0
@@ -133,13 +140,8 @@ class Decoder(srd.Decoder):
             self.putx([3, ['Command: %s' % cmds[self.state][1]]])
         elif self.cmdstate >= 2:
             # Bytes 2-x: Slave sends status register as long as master clocks.
-            if self.cmdstate <= 3: # TODO: While CS# asserted.
-                self.putx([24, ['Status register: 0x%02x' % miso]])
-                self.putx([25, [decode_status_reg(miso)]])
-
-            if self.cmdstate == 3: # TODO: If CS# got de-asserted.
-                self.state = None
-                return
+            self.putx([24, ['Status register: 0x%02x' % miso]])
+            self.putx([25, [decode_status_reg(miso)]])
 
         self.cmdstate += 1
 
@@ -162,19 +164,10 @@ class Decoder(srd.Decoder):
                 self.addr = 0
         elif self.cmdstate >= 5:
             # Bytes 5-x: Master reads data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 256 bytes per READ command.
-            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
-                self.data.append(miso)
-                # self.putx([0, ['New read byte: 0x%02x' % miso]])
-
-            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
-                # s = ', '.join(map(hex, self.data))
-                s = ''.join(map(chr, self.data))
-                self.putx([24, ['Read data']])
-                self.putx([25, ['Read data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
+            if self.cmdstate == 5:
+                self.block_ss = self.ss
+                self.on_end_transaction = lambda: self.output_data_block("Read")
+            self.data.append(miso)
 
         self.cmdstate += 1
 
@@ -197,18 +190,10 @@ class Decoder(srd.Decoder):
             self.addr = 0
         elif self.cmdstate >= 6:
             # Bytes 6-x: Master reads data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 32 bytes per FAST READ command.
             if self.cmdstate == 6:
                 self.block_ss = self.ss
-            if self.cmdstate <= 32 + 5: # TODO: While CS# asserted.
-                self.data.append(miso)
-            if self.cmdstate == 32 + 5: # TODO: If CS# got de-asserted.
-                self.block_es = self.es
-                s = ' '.join([hex(b)[2:] for b in self.data])
-                self.putb([25, ['Read data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
+                self.on_end_transaction = lambda: self.output_block("Read")
+            self.data.append(miso)
 
         self.cmdstate += 1
 
@@ -266,19 +251,10 @@ class Decoder(srd.Decoder):
                 self.addr = 0
         elif self.cmdstate >= 5:
             # Bytes 5-x: Master sends data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 256 bytes per page / PP command.
-            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
-                self.data.append(mosi)
-                # self.putx([0, ['New data byte: 0x%02x' % mosi]])
-
-            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
-                # s = ', '.join(map(hex, self.data))
-                s = ''.join(map(chr, self.data))
-                self.putx([24, ['Page data']])
-                self.putx([25, ['Page data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
+            if self.cmdstate == 5:
+                self.block_ss = self.ss
+                self.on_end_transaction = lambda: self.output_data_block("Page data")
+            self.data.append(mosi)
 
         self.cmdstate += 1
 
@@ -346,24 +322,28 @@ class Decoder(srd.Decoder):
     def handle_dsry(self, mosi, miso):
         pass # TODO
 
+    def output_data_block(self, label):
+        """ Print accumulated block of data
+            (called on CS# de-assert via self.on_end_transaction callback)
+        """
+        self.block_es = self.es # ends on the CS# de-assert sample
+        s = ' '.join([("%02x"%b) for b in self.data])
+        self.putb([25, ['%s %d bytes: %s' % (label, len(self.data), s)]])
+
     def decode(self, ss, es, data):
 
         ptype, mosi, miso = data
 
         # if ptype == 'DATA':
         #     self.putx([0, ['MOSI: 0x%02x, MISO: 0x%02x' % (mosi, miso)]])
+        self.ss, self.es = ss, es
 
-        # if ptype == 'CS-CHANGE':
-        #     if mosi == 1 and miso == 0:
-        #         self.putx([0, ['Asserting CS#']])
-        #     elif mosi == 0 and miso == 1:
-        #         self.putx([0, ['De-asserting CS#']])
+        if ptype == 'CS-CHANGE':
+            self.end_current_transaction()
 
         if ptype != 'DATA':
             return
 
-        self.ss, self.es = ss, es
-
         # If we encountered a known chip command, enter the resp. state.
         if self.state is None:
             self.state = mosi
-- 
2.8.3

>From 11826641debdadd1301431253a5ac6d674b14b32 Mon Sep 17 00:00:00 2001
From: Angus Gratton <g...@projectgus.com>
Date: Sun, 22 May 2016 11:43:45 +1000
Subject: [PATCH 2/3] spiflash decoder: Handle "Fast Read Dual I/O"

---
 decoders/spiflash/pd.py | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/decoders/spiflash/pd.py b/decoders/spiflash/pd.py
index 83abf82..b8b29ad 100644
--- a/decoders/spiflash/pd.py
+++ b/decoders/spiflash/pd.py
@@ -24,6 +24,20 @@ from .lists import *
 def cmd_annotation_classes():
     return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
 
+def decode_dual_bytes(sio0, sio1):
+    """ Given a byte in SIO0 (MOSI) of even bits and a byte in
+        SIO1 (MISO) of odd bits, return a tuple of two bytes.
+    """
+    def combine_byte(even, odd):
+        result = 0
+        for bit in range(4):
+            if even & (1 << bit):
+                result |= 1 << (bit*2)
+            if odd & (1 << bit):
+                result |= 1 << ((bit*2) + 1)
+        return result
+    return ( combine_byte(sio0>>4, sio1>>4), combine_byte(sio0, sio1) )
+
 def decode_status_reg(data):
     # TODO: Additional per-bit(s) self.put() calls with correct start/end.
 
@@ -192,13 +206,26 @@ class Decoder(srd.Decoder):
             # Bytes 6-x: Master reads data bytes (until CS# de-asserted).
             if self.cmdstate == 6:
                 self.block_ss = self.ss
-                self.on_end_transaction = lambda: self.output_block("Read")
+                self.on_end_transaction = lambda: self.output_data_block("Read")
             self.data.append(miso)
 
         self.cmdstate += 1
 
     def handle_2read(self, mosi, miso):
-        pass # TODO
+        # Fast Read Dual I/O: Same as fast read, but all data
+        # after the command is sent via two I/O pins.
+        #
+        # MOSI=SIO0=even bits, MISO=SIO1=odd bits
+        # recombine the bytes and pass them up to the handle_fast_read command
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.putx([5, ['Command: %s' % cmds[self.state][1]]])
+            self.cmdstate = 2
+        else: # dual I/O mode
+            a,b = decode_dual_bytes(mosi, miso)
+            # pass same byte in as both MISO & MOSI, parser state determines which one it cares about
+            self.handle_fast_read(a, a)
+            self.handle_fast_read(b, b)
 
     # TODO: Warn/abort if we don't see the necessary amount of bytes.
     # TODO: Warn if WREN was not seen before.
-- 
2.8.3

>From 70c005d97a7add6d921b303a887f149bdc6ab5c6 Mon Sep 17 00:00:00 2001
From: Angus Gratton <g...@projectgus.com>
Date: Sun, 22 May 2016 11:47:57 +1000
Subject: [PATCH 3/3] spiflash decoder: Perf tweak: Build handler lookup table
 once per decoder

---
 decoders/spiflash/pd.py | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/decoders/spiflash/pd.py b/decoders/spiflash/pd.py
index b8b29ad..e501037 100644
--- a/decoders/spiflash/pd.py
+++ b/decoders/spiflash/pd.py
@@ -91,6 +91,15 @@ class Decoder(srd.Decoder):
         self.on_end_transaction = None
         self.end_current_transaction()
 
+        # build dict mapping command keys to handler functions. Each
+        # command in 'cmds' (defined in lists.py) has a matching
+        # handler self.handle_<shortname>
+        def get_handler(cmd):
+            s = 'handle_%s' % cmds[cmd][0].lower().replace('/', '_')
+            return getattr(self, s)
+        self.cmd_handlers = dict((cmd, get_handler(cmd)) for cmd in cmds.keys())
+
+
     def end_current_transaction(self):
         if self.on_end_transaction is not None: # callback for CS# transition
             self.on_end_transaction()
@@ -377,10 +386,8 @@ class Decoder(srd.Decoder):
             self.cmdstate = 1
 
         # Handle commands.
-        if self.state in cmds:
-            s = 'handle_%s' % cmds[self.state][0].lower().replace('/', '_')
-            handle_reg = getattr(self, s)
-            handle_reg(mosi, miso)
-        else:
+        try:
+            self.cmd_handlers[self.state](mosi, miso)
+        except KeyError:
             self.putx([24, ['Unknown command: 0x%02x' % mosi]])
             self.state = None
-- 
2.8.3

------------------------------------------------------------------------------
What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic
patterns at an interface-level. Reveals which users, apps, and protocols are 
consuming the most bandwidth. Provides multi-vendor support for NetFlow, 
J-Flow, sFlow and other flows. Make informed decisions using capacity 
planning reports. https://ad.doubleclick.net/ddm/clk/305295220;132659582;e
_______________________________________________
sigrok-devel mailing list
sigrok-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sigrok-devel

Reply via email to