]> sigrok.org Git - libsigrokdecode.git/commitdiff
Backport recent changes from mainline.
authorUwe Hermann <redacted>
Wed, 11 Dec 2019 20:27:13 +0000 (21:27 +0100)
committerUwe Hermann <redacted>
Wed, 11 Dec 2019 20:27:13 +0000 (21:27 +0100)
This includes all changes from

  4f0d192d748e987af43ec5b811a643eb0a8601b2
  "type_decoder.c: Fix trailing whitespace."

up to

  1d7e79da75afbdfd5d1863de6482bf4cd21e5c7e
  "ac97/lin: Remove some unneeded code snippets."

168 files changed:
HACKING
NEWS
configure.ac
decoder.c
decoders/ac97/__init__.py
decoders/ac97/pd.py
decoders/ade77xx/__init__.py
decoders/ade77xx/pd.py
decoders/adf435x/pd.py
decoders/adns5020/__init__.py
decoders/adns5020/pd.py
decoders/am230x/pd.py
decoders/amulet_ascii/__init__.py [new file with mode: 0644]
decoders/amulet_ascii/lists.py [new file with mode: 0644]
decoders/amulet_ascii/pd.py [new file with mode: 0644]
decoders/arm_etmv3/__init__.py
decoders/arm_etmv3/pd.py
decoders/arm_itm/pd.py
decoders/arm_tpiu/__init__.py
decoders/arm_tpiu/pd.py
decoders/atsha204a/__init__.py
decoders/atsha204a/pd.py
decoders/aud/pd.py
decoders/avr_isp/pd.py
decoders/avr_pdi/__init__.py
decoders/avr_pdi/pd.py
decoders/can/__init__.py
decoders/can/pd.py
decoders/cc1101/__init__.py [new file with mode: 0644]
decoders/cc1101/lists.py [new file with mode: 0644]
decoders/cc1101/pd.py [new file with mode: 0644]
decoders/cec/__init__.py
decoders/cec/pd.py
decoders/cec/protocoldata.py
decoders/cfp/pd.py
decoders/counter/__init__.py
decoders/counter/pd.py
decoders/dali/pd.py
decoders/dcf77/pd.py
decoders/dmx512/pd.py
decoders/ds1307/pd.py
decoders/ds2408/__init__.py [new file with mode: 0644]
decoders/ds2408/pd.py [new file with mode: 0644]
decoders/ds243x/pd.py
decoders/ds28ea00/pd.py
decoders/dsi/pd.py
decoders/edid/__init__.py
decoders/edid/pd.py
decoders/eeprom24xx/pd.py
decoders/eeprom93xx/pd.py
decoders/em4100/pd.py
decoders/em4305/pd.py
decoders/enc28j60/__init__.py [new file with mode: 0644]
decoders/enc28j60/lists.py [new file with mode: 0644]
decoders/enc28j60/pd.py [new file with mode: 0644]
decoders/flexray/__init__.py [new file with mode: 0644]
decoders/flexray/pd.py [new file with mode: 0644]
decoders/graycode/pd.py
decoders/guess_bitrate/__init__.py
decoders/guess_bitrate/pd.py
decoders/hdcp/__init__.py [new file with mode: 0644]
decoders/hdcp/pd.py [new file with mode: 0644]
decoders/i2c/pd.py
decoders/i2cdemux/pd.py
decoders/i2cfilter/pd.py
decoders/i2s/pd.py
decoders/ieee488/__init__.py [new file with mode: 0644]
decoders/ieee488/pd.py [new file with mode: 0644]
decoders/ir_nec/pd.py
decoders/ir_rc5/lists.py
decoders/ir_rc5/pd.py
decoders/ir_rc6/__init__.py [new file with mode: 0644]
decoders/ir_rc6/pd.py [new file with mode: 0644]
decoders/jitter/__init__.py
decoders/jitter/pd.py
decoders/jtag/pd.py
decoders/jtag_ejtag/pd.py
decoders/jtag_stm32/pd.py
decoders/lin/__init__.py [new file with mode: 0644]
decoders/lin/pd.py [new file with mode: 0644]
decoders/lm75/pd.py
decoders/lpc/__init__.py
decoders/lpc/pd.py
decoders/maple_bus/pd.py
decoders/max7219/pd.py
decoders/mcs48/__init__.py
decoders/mcs48/pd.py
decoders/mdio/pd.py
decoders/microwire/__init__.py
decoders/microwire/pd.py
decoders/midi/pd.py
decoders/miller/pd.py
decoders/mlx90614/pd.py
decoders/modbus/pd.py
decoders/morse/pd.py
decoders/mrf24j40/pd.py
decoders/mxc6225xu/pd.py
decoders/nes_gamepad/__init__.py [new file with mode: 0644]
decoders/nes_gamepad/pd.py [new file with mode: 0644]
decoders/nrf24l01/pd.py
decoders/nunchuk/pd.py
decoders/onewire_link/__init__.py
decoders/onewire_link/pd.py
decoders/onewire_network/pd.py
decoders/ook/pd.py
decoders/ook_oregon/pd.py
decoders/ook_vis/pd.py
decoders/pan1321/pd.py
decoders/parallel/pd.py
decoders/pca9571/__init__.py [new file with mode: 0644]
decoders/pca9571/pd.py [new file with mode: 0644]
decoders/ps2/pd.py
decoders/pwm/__init__.py
decoders/pwm/pd.py
decoders/qi/pd.py
decoders/rc_encode/pd.py
decoders/rfm12/pd.py
decoders/rgb_led_spi/pd.py
decoders/rgb_led_ws281x/pd.py
decoders/rtc8564/pd.py
decoders/sda2506/__init__.py
decoders/sda2506/pd.py
decoders/sdcard_sd/pd.py
decoders/sdcard_spi/pd.py
decoders/seven_segment/__init__.py [new file with mode: 0644]
decoders/seven_segment/pd.py [new file with mode: 0644]
decoders/signature/__init__.py [new file with mode: 0644]
decoders/signature/pd.py [new file with mode: 0644]
decoders/spdif/pd.py
decoders/spi/__init__.py
decoders/spi/pd.py
decoders/spiflash/lists.py
decoders/spiflash/pd.py
decoders/ssi32/pd.py
decoders/st7735/__init__.py
decoders/st7735/pd.py
decoders/stepper_motor/pd.py
decoders/swd/__init__.py
decoders/swd/pd.py
decoders/swim/__init__.py
decoders/swim/pd.py
decoders/t55xx/pd.py
decoders/tca6408a/pd.py
decoders/tdm_audio/__init__.py [new file with mode: 0644]
decoders/tdm_audio/pd.py [new file with mode: 0644]
decoders/timing/pd.py
decoders/tlc5620/pd.py
decoders/uart/pd.py
decoders/usb_packet/pd.py
decoders/usb_power_delivery/pd.py
decoders/usb_request/pd.py
decoders/usb_signalling/pd.py
decoders/wiegand/pd.py
decoders/x2444m/__init__.py [new file with mode: 0644]
decoders/x2444m/pd.py [new file with mode: 0644]
decoders/xfp/__init__.py
decoders/xfp/pd.py
decoders/z80/__init__.py
decoders/z80/pd.py
instance.c
libsigrokdecode-internal.h
libsigrokdecode.h
session.c
srd.c
tests/decoder.c
tests/session.c
type_decoder.c
util.c

diff --git a/HACKING b/HACKING
index 30cd1fe5425db6da688743adec6f5830610a8468..a4698a62719290dc238e657d9dafc176ff195b95 100644 (file)
--- a/HACKING
+++ b/HACKING
@@ -176,6 +176,47 @@ Protocol decoder guidelines
    Not recommended:
      'FIND_ADDRESS', 'Get Temperature', 'start'
 
    Not recommended:
      'FIND_ADDRESS', 'Get Temperature', 'start'
 
+ - Protocol decoder tags:
+
+   - Every decoder must have a "tags" list (>= 1 items, alphabetically sorted).
+
+   - All tag names start with a capital letter. Subsequent words of the name
+     are not capitalized, e.g. "Retro computing", "Debug/trace".
+
+   - All tag names should use singular form ("Sensor", not "Sensors").
+
+   Common tags:
+
+   - Analog/digital: Decoders related A/D conversion, e.g. ADCs and DACs.
+   - Audio: Decoders related to audio protocols, e.g. I²S, S/PDIF.
+   - Automotive: Decoders related to automotive protocols, e.g. CAN, FlexRay.
+   - Clock/timing: Decoders related to time keeping, timing, and clocks/RTCs.
+   - Debug/trace: Decoders related to microcontroller/CPU debugging, tracing,
+     programming/flashing protocols, e.g. SWD, JTAG, AVR ISP, ARM ETMv3.
+   - Display: Decoders related to display technologies, e.g. DVI, HDMI,
+     TFT, OLED, LCD, HD44780, EDID, and various LED protocols.
+   - Embedded/industrial: Decoders related to protocols used in embedded
+     systems, industrial systems, or automation (e.g. SPI, Modbus, Profibus).
+   - Encoding: Decoders related to generic encoding / line coding systems,
+     e.g. Manchester, Miller, Gray code, OOK, and similar.
+   - IC: Decoders for specific (families of) ICs (i.e. not IC-independent,
+     generic protocols like UART, SPI, CAN, or USB).
+   - IR: Decoders related to infrared (e.g. remote control) protocols.
+   - Lighting: Decoders related to lighting technologies, e.g. DALI, DMX512.
+   - Memory: Decoders related to memories (e.g. NOR/NAND flash, EEPROM,
+     SDRAM, SRAM, various other volatile or non-volatile memories).
+   - Networking: Decoders related to (wired) networking technologies.
+   - PC: Decoders related to protocols used in personal computers (desktop,
+     workstation, laptop, server). This is not meant to be restricted to
+     "IBM PC" or "x86/Intel", Apple/Commodore/Atari/SPARC etc. are fine too.
+   - RFID: Decoders related to RFID protocols, e.g. EM4100, T55xx.
+   - Retro computing: Decoders related to retro computing, e.g. MCS-48, Z80.
+   - Security/crypto: Decoders related to security or cryptography.
+   - Sensor: Decoders for sensors or all kinds, e.g. temperature or humidity.
+   - Util: Random utility/helper decoders.
+   - Wireless/RF: Decoders related to various wireless/RF technologies, e.g.
+     Bluetooth, BLE, Wifi, or 2.4GHz/433MHz custom protocols.
+
 
 Testsuite
 ---------
 
 Testsuite
 ---------
diff --git a/NEWS b/NEWS
index 369dbe342036557654c3f9b8980cc9714a138c44..fdd62174a5ffeb69f5275d12be168a4262b6c052 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -309,7 +309,7 @@ means it is NOT backwards-compatible and frontends will need updates.
  * jitter:
    - Avoid Unicode string literals (bug #569).
  * jtag:
  * jitter:
    - Avoid Unicode string literals (bug #569).
  * jtag:
-   - Fix/enable OUT_PYTHON output.
+   - Fix/enable OUTPUT_PYTHON output.
    - Add more annotations, fix a SHIFT-IR/-DR issue.
  * jtag_stm32:
    - Fix incorrect handling of registers.
    - Add more annotations, fix a SHIFT-IR/-DR issue.
  * jtag_stm32:
    - Fix incorrect handling of registers.
@@ -331,7 +331,7 @@ means it is NOT backwards-compatible and frontends will need updates.
  * pwm:
    - Avoid Unicode string literals (bug #569).
  * spi:
  * pwm:
    - Avoid Unicode string literals (bug #569).
  * spi:
-   - OUT_PYTHON docs: Fix order of MISO/MOSI data items.
+   - OUTPUT_PYTHON docs: Fix order of MISO/MOSI data items.
    - Tell stacked decoders about missing CS# signal.
    - Add binary output facilities for MISO/MOSI (bug #424).
    - Don't decode data lines if CS# isn't asserted (bug #559).
    - Tell stacked decoders about missing CS# signal.
    - Add binary output facilities for MISO/MOSI (bug #424).
    - Don't decode data lines if CS# isn't asserted (bug #559).
index 56f01a248d4640b38055b0624f6ab4eb5d1b0ee4..0e2416f8fa29eb7039ca1f1c44c130c8897bb4ec 100644 (file)
@@ -89,15 +89,18 @@ SRD_PKGLIBS_TESTS=
 SR_PKG_CHECK_SUMMARY([srd_pkglibs_summary])
 
 # Python 3 is always needed.
 SR_PKG_CHECK_SUMMARY([srd_pkglibs_summary])
 
 # Python 3 is always needed.
+# Starting with Python 3.8 we need to check for "python-3.8-embed"
+# first, since usually only that variant will add "-lpython3.8".
+# https://docs.python.org/3/whatsnew/3.8.html#debug-build-uses-the-same-abi-as-release-build
 SR_PKG_CHECK([python3], [SRD_PKGLIBS],
 SR_PKG_CHECK([python3], [SRD_PKGLIBS],
-       [python3 >= 3.2], [python-3.7 >= 3.7], [python-3.6 >= 3.6], [python-3.5 >= 3.5], [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2])
+       [python-3.8-embed], [python-3.8 >= 3.8], [python-3.7 >= 3.7], [python-3.6 >= 3.6], [python-3.5 >= 3.5], [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2], [python3 >= 3.2])
 AS_IF([test "x$sr_have_python3" = xno],
        [AC_MSG_ERROR([Cannot find Python 3 development headers.])])
 
 # We also need to find the name of the python3 executable (for 'make install').
 # Some OSes call this python3, some call it python3.2, etc. etc.
 AC_ARG_VAR([PYTHON3], [Python 3 interpreter])
 AS_IF([test "x$sr_have_python3" = xno],
        [AC_MSG_ERROR([Cannot find Python 3 development headers.])])
 
 # We also need to find the name of the python3 executable (for 'make install').
 # Some OSes call this python3, some call it python3.2, etc. etc.
 AC_ARG_VAR([PYTHON3], [Python 3 interpreter])
-AC_CHECK_PROGS([PYTHON3], [python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3])
+AC_CHECK_PROGS([PYTHON3], [python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3])
 AS_IF([test "x$PYTHON3" = x],
        [AC_MSG_ERROR([Cannot find Python 3 interpreter.])])
 
 AS_IF([test "x$PYTHON3" = x],
        [AC_MSG_ERROR([Cannot find Python 3 interpreter.])])
 
index 7374b779453e015025b59ae6dc4b5f16e5416e09..cef4f299aea927d76d64adbb8ba9dcc39a2bf927 100644 (file)
--- a/decoder.c
+++ b/decoder.c
@@ -171,6 +171,7 @@ static void decoder_free(struct srd_decoder *dec)
 
        g_slist_free_full(dec->outputs, g_free);
        g_slist_free_full(dec->inputs, g_free);
 
        g_slist_free_full(dec->outputs, g_free);
        g_slist_free_full(dec->inputs, g_free);
+       g_slist_free_full(dec->tags, g_free);
        g_free(dec->license);
        g_free(dec->desc);
        g_free(dec->longname);
        g_free(dec->license);
        g_free(dec->desc);
        g_free(dec->longname);
@@ -751,6 +752,11 @@ SRD_API int srd_decoder_load(const char *module_name)
 
        /* Check Decoder class for required methods. */
 
 
        /* Check Decoder class for required methods. */
 
+       if (check_method(d->py_dec, module_name, "reset") != SRD_OK) {
+               fail_txt = "no 'reset()' method";
+               goto err_out;
+       }
+
        if (check_method(d->py_dec, module_name, "start") != SRD_OK) {
                fail_txt = "no 'start()' method";
                goto err_out;
        if (check_method(d->py_dec, module_name, "start") != SRD_OK) {
                fail_txt = "no 'start()' method";
                goto err_out;
@@ -797,6 +803,11 @@ SRD_API int srd_decoder_load(const char *module_name)
                goto err_out;
        }
 
                goto err_out;
        }
 
+       if (py_attr_as_strlist(d->py_dec, "tags", &(d->tags)) != SRD_OK) {
+               fail_txt = "missing or malformed 'tags' attribute";
+               goto err_out;
+       }
+
        /* All options and their default values. */
        if (get_options(d) != SRD_OK) {
                fail_txt = "cannot get options";
        /* All options and their default values. */
        if (get_options(d) != SRD_OK) {
                fail_txt = "cannot get options";
@@ -858,7 +869,7 @@ err_out:
 /**
  * Return a protocol decoder's docstring.
  *
 /**
  * Return a protocol decoder's docstring.
  *
- * @param dec The loaded protocol decoder.
+ * @param dec The loaded protocol decoder. Must not be NULL.
  *
  * @return A newly allocated buffer containing the protocol decoder's
  *         documentation. The caller is responsible for free'ing the buffer.
  *
  * @return A newly allocated buffer containing the protocol decoder's
  *         documentation. The caller is responsible for free'ing the buffer.
@@ -874,7 +885,7 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec)
        if (!srd_check_init())
                return NULL;
 
        if (!srd_check_init())
                return NULL;
 
-       if (!dec)
+       if (!dec || !dec->py_mod)
                return NULL;
 
        gstate = PyGILState_Ensure();
                return NULL;
 
        gstate = PyGILState_Ensure();
@@ -1068,6 +1079,13 @@ SRD_API int srd_decoder_load_all(void)
        return SRD_OK;
 }
 
        return SRD_OK;
 }
 
+static void srd_decoder_unload_cb(void *arg, void *ignored)
+{
+       (void)ignored;
+
+       srd_decoder_unload((struct srd_decoder *)arg);
+}
+
 /**
  * Unload all loaded protocol decoders.
  *
 /**
  * Unload all loaded protocol decoders.
  *
@@ -1077,8 +1095,7 @@ SRD_API int srd_decoder_load_all(void)
  */
 SRD_API int srd_decoder_unload_all(void)
 {
  */
 SRD_API int srd_decoder_unload_all(void)
 {
-       for (GSList *l = pd_list; l; l = l->next)
-               srd_decoder_unload(l->data);
+       g_slist_foreach(pd_list, srd_decoder_unload_cb, NULL);
        g_slist_free(pd_list);
        pd_list = NULL;
 
        g_slist_free(pd_list);
        pd_list = NULL;
 
index 8b96e8aa445d18d537d49e3c97dbc06602728551..c57c8ebaa4aa6a91eec5dc01dc69ed7472b20b74 100644 (file)
@@ -18,9 +18,8 @@
 ##
 
 '''
 ##
 
 '''
-AC'97 (Audio Codec '97) was specifically designed by Intel for audio and
-modem I/O functionality in mainstream PC systems. See the specification in
-http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
+AC'97 (Audio Codec '97) is a protocol for audio and modem I/O functionality
+in mainstream PC systems.
 
 AC'97 communicates full duplex data (SDATA_IN, SDATA_OUT), where bits
 are clocked by the BIT_CLK and frames are signalled by the SYNC signals.
 
 AC'97 communicates full duplex data (SDATA_IN, SDATA_OUT), where bits
 are clocked by the BIT_CLK and frames are signalled by the SYNC signals.
@@ -31,6 +30,9 @@ each. One 16bit slot contains management information, twelve 20bit slots
 follow which carry data for three management and nine audio/modem channels.
 Optionally two slots of one frame can get combined for higher resolution
 on fewer channels, or double data rate.
 follow which carry data for three management and nine audio/modem channels.
 Optionally two slots of one frame can get combined for higher resolution
 on fewer channels, or double data rate.
+
+Details:
+http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 6cb7e934ac19bc929d41bdfa14e81e1003e5d940..68eb955baa1e5204496d87ccf0d68adc4ebf1329 100644 (file)
@@ -59,7 +59,8 @@ class Decoder(srd.Decoder):
     desc = 'Audio and modem control for PC systems.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Audio and modem control for PC systems.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['ac97']
+    outputs = []
+    tags = ['Audio', 'PC']
     channels = (
         {'id': 'sync', 'name': 'SYNC', 'desc': 'Frame synchronization'},
         {'id': 'clk', 'name': 'BIT_CLK', 'desc': 'Data bits clock'},
     channels = (
         {'id': 'sync', 'name': 'SYNC', 'desc': 'Frame synchronization'},
         {'id': 'clk', 'name': 'BIT_CLK', 'desc': 'Data bits clock'},
@@ -152,8 +153,6 @@ class Decoder(srd.Decoder):
         self.put(ss, es, self.out_binary, [cls, data])
 
     def __init__(self):
         self.put(ss, es, self.out_binary, [cls, data])
 
     def __init__(self):
-        self.out_binary = None
-        self.out_ann = None
         self.reset()
 
     def reset(self):
         self.reset()
 
     def reset(self):
@@ -167,10 +166,8 @@ class Decoder(srd.Decoder):
         }
 
     def start(self):
         }
 
     def start(self):
-        if not self.out_binary:
-            self.out_binary = self.register(srd.OUTPUT_BINARY)
-        if not self.out_ann:
-            self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
+        self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
index cbe8689d407db173328e080d7c8ba2f5d18099bd..7da0419a6a75e9b0e0643da4efdb8d78e2cb17e6 100644 (file)
@@ -14,8 +14,7 @@
 ## GNU General Public License for more details.
 ##
 ## You should have received a copy of the GNU General Public License
 ## 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/>.
 ##
 
 '''
 ##
 
 '''
index 0dfd7c86bf08fa0ceb25656ab97b00f5daa07909..5a24a25e5091dea3f1c8ac8c8a09196b1e9aa866 100644 (file)
@@ -33,7 +33,8 @@ class Decoder(srd.Decoder):
     desc = 'Poly phase multifunction energy metering IC protocol.'
     license = 'mit'
     inputs = ['spi']
     desc = 'Poly phase multifunction energy metering IC protocol.'
     license = 'mit'
     inputs = ['spi']
-    outputs = ['ade77xx']
+    outputs = []
+    tags = ['Analog/digital', 'IC', 'Sensor']
     annotations = (
         ('read', 'Register read commands'),
         ('write', 'Register write commands'),
     annotations = (
         ('read', 'Register read commands'),
         ('write', 'Register write commands'),
index dcc08dec55fd52eb1de3dc34b49ab4aa61ef0067..f6c6e6e01f99e66f5a5659a16dc1ee873ca60740 100644 (file)
@@ -95,7 +95,8 @@ class Decoder(srd.Decoder):
     desc = 'Wideband synthesizer with integrated VCO.'
     license = 'gplv3+'
     inputs = ['spi']
     desc = 'Wideband synthesizer with integrated VCO.'
     license = 'gplv3+'
     inputs = ['spi']
-    outputs = ['adf435x']
+    outputs = []
+    tags = ['Clock/timing', 'IC', 'Wireless/RF']
     annotations = (
         # Sent from the host to the chip.
         ('register', 'Register written to the device'),
     annotations = (
         # Sent from the host to the chip.
         ('register', 'Register written to the device'),
index d4c260eb19aedd72009d8d654b56cf39629761a6..e519da4449872ec439abbda06f34e0b33087d59c 100644 (file)
@@ -19,7 +19,9 @@
 
 '''
 This decoder stacks on top of the 'spi' PD and decodes ADNS-5020 optical mouse
 
 '''
 This decoder stacks on top of the 'spi' PD and decodes ADNS-5020 optical mouse
-sensor commands and data. Use MOSI for the SDIO shared line.
+sensor commands and data.
+
+Use MOSI for the SDIO shared line.
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index cd72eca8b46d93432379469b33d10031296b9d5f..9ac778e038b87c6f9c7ada936310cbc5a7de4181 100644 (file)
@@ -42,11 +42,12 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'adns5020'
     name = 'ADNS-5020'
     api_version = 3
     id = 'adns5020'
     name = 'ADNS-5020'
-    longname = 'Avago ADNS-5020 optical mouse sensor'
-    desc = 'Bidirectional command and data over an SPI-like protocol.'
+    longname = 'Avago ADNS-5020'
+    desc = 'Bidirectional optical mouse sensor protocol.'
     license = 'gplv2+'
     inputs = ['spi']
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['adns5020']
+    outputs = []
+    tags = ['IC', 'PC', 'Sensor']
     annotations = (
         ('read', 'Register read commands'),
         ('write', 'Register write commands'),
     annotations = (
         ('read', 'Register read commands'),
         ('write', 'Register write commands'),
index 81c1f28da39506e4d714c779593b3ac77d60f5d3..fbc68d396db060f1db1278272891bc71c683fcab 100644 (file)
@@ -36,12 +36,13 @@ class SamplerateError(Exception):
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'am230x'
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'am230x'
-    name = 'AM230x/DHTxx/RHTxx'
+    name = 'AM230x'
     longname = 'Aosong AM230x/DHTxx/RHTxx'
     longname = 'Aosong AM230x/DHTxx/RHTxx'
-    desc = 'Aosong AM230x/DHTxx/RHTxx humidity/temperature sensor protocol.'
+    desc = 'Aosong AM230x/DHTxx/RHTxx humidity/temperature sensor.'
     license = 'gplv2+'
     inputs = ['logic']
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['am230x']
+    outputs = []
+    tags = ['IC', 'Sensor']
     channels = (
         {'id': 'sda', 'name': 'SDA', 'desc': 'Single wire serial data line'},
     )
     channels = (
         {'id': 'sda', 'name': 'SDA', 'desc': 'Single wire serial data line'},
     )
diff --git a/decoders/amulet_ascii/__init__.py b/decoders/amulet_ascii/__init__.py
new file mode 100644 (file)
index 0000000..7d2c8c3
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'uart' PD and decodes the ASCII protocol
+for Amulet LCD display controllers.
+
+Currently the decoder treats both RX and TX the same way, decoding all
+message types.
+'''
+
+from .pd import Decoder
diff --git a/decoders/amulet_ascii/lists.py b/decoders/amulet_ascii/lists.py
new file mode 100644 (file)
index 0000000..92e27a9
--- /dev/null
@@ -0,0 +1,73 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+from collections import OrderedDict
+
+# OrderedDict which maps command IDs to their names and descriptions.
+cmds = OrderedDict([
+    (0xA0, ('PAGE', 'Jump to page')),
+    (0xD0, ('GBV', 'Get byte variable')),
+    (0xD1, ('GWV', 'Get word variable')),
+    (0xD2, ('GSV', 'Get string variable')),
+    (0xD3, ('GLV', 'Get label variable')),
+    (0xD4, ('GRPC', 'Get RPC buffer')),
+    (0xD5, ('SBV', 'Set byte variable')),
+    (0xD6, ('SWV', 'Set word variable')),
+    (0xD7, ('SSV', 'Set string variable')),
+    (0xD8, ('RPC', 'Invoke RPC')),
+    (0xD9, ('LINE', 'Draw line')),
+    (0xDA, ('RECT', 'Draw rectangle')),
+    (0xDB, ('FRECT', 'Draw filled rectangle')),
+    (0xDC, ('PIXEL', 'Draw pixel')),
+    (0xDD, ('GBVA', 'Get byte variable array')),
+    (0xDE, ('GWVA', 'Get word variable array')),
+    (0xDF, ('SBVA', 'Set byte variable array')),
+    (0xE0, ('GBVR', 'Get byte variable reply')),
+    (0xE1, ('GWVR', 'Get word variable reply')),
+    (0xE2, ('GSVR', 'Get string variable reply')),
+    (0xE3, ('GLVR', 'Get label variable reply')),
+    (0xE4, ('GRPCR', 'Get RPC buffer reply')),
+    (0xE5, ('SBVR', 'Set byte variable reply')),
+    (0xE6, ('SWVR', 'Set word variable reply')),
+    (0xE7, ('SSVR', 'Set string variable reply')),
+    (0xE8, ('RPCR', 'Invoke RPC reply')),
+    (0xE9, ('LINER', 'Draw line reply')),
+    (0xEA, ('RECTR', 'Draw rectangle')),
+    (0xEB, ('FRECTR', 'Draw filled rectangle reply')),
+    (0xEC, ('PIXELR', 'Draw pixel reply')),
+    (0xED, ('GBVAR', 'Get byte variable array reply')),
+    (0xEE, ('GWVAR', 'Get word variable array reply')),
+    (0xEF, ('SBVAR', 'Set byte variable array reply')),
+    (0xF0, ('ACK', 'Acknowledgment')),
+    (0xF1, ('NACK', 'Negative acknowledgment')),
+    (0xF2, ('SWVA', 'Set word variable array')),
+    (0xF3, ('SWVAR', 'Set word variable array reply')),
+    (0xF4, ('GCV', 'Get color variable')),
+    (0xF5, ('GCVR', 'Get color variable reply')),
+    (0xF6, ('SCV', 'Set color variable')),
+    (0xF7, ('SCVR', 'Set color variable reply')),
+])
+
+cmds_with_high_bytes = [
+    0xA0, # PAGE - Page change
+    0xD7, # SVV - Set string variable
+    0xE7, # SVVR - Set string variable reply
+    0xE2, # GSVR - Get string variable reply
+    0xE3, # GLVR - Get label variable reply
+]
diff --git a/decoders/amulet_ascii/pd.py b/decoders/amulet_ascii/pd.py
new file mode 100644 (file)
index 0000000..54733ef
--- /dev/null
@@ -0,0 +1,702 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from math import ceil
+from .lists import *
+
+L = len(cmds)
+RX = 0
+TX = 1
+
+# Don't forget to keep this in sync with 'cmds' is lists.py.
+class Ann:
+    PAGE, GBV, GWV, GSV, GLV, GRPC, SBV, SWV, SSV, RPC, LINE, RECT, FRECT, \
+    PIXEL, GBVA, GWVA, SBVA, GBVR, GWVR, GSVR, GLVR, GRPCR, SBVR, SWVR, SSVR, \
+    RPCR, LINER, RECTR, FRECTR, PIXELR, GBVAR, GWVAR, SBVAR, ACK, NACK, SWVA, \
+    SWVAR, GCV, GCVR, SCV, SCVR, BIT, FIELD, WARN = range(L + 3)
+
+def cmd_annotation_classes():
+    return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'amulet_ascii'
+    name = 'Amulet ASCII'
+    longname = 'Amulet LCD ASCII'
+    desc = 'Amulet Technologies LCD controller ASCII protocol.'
+    license = 'gplv3+'
+    inputs = ['uart']
+    outputs = []
+    tags = ['Display']
+    annotations = cmd_annotation_classes() + (
+        ('bit', 'Bit'),
+        ('field', 'Field'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (L + 0,)),
+        ('fields', 'Fields', (L + 1,)),
+        ('commands', 'Commands', tuple(range(len(cmds)))),
+        ('warnings', 'Warnings', (L + 2,)),
+    )
+    options = (
+        {'id': 'ms_chan', 'desc': 'Master -> slave channel',
+            'default': 'RX', 'values': ('RX', 'TX')},
+        {'id': 'sm_chan', 'desc': 'Slave -> master channel',
+            'default': 'TX', 'values': ('RX', 'TX')},
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.state = None
+        self.cmdstate = None
+
+        # 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 start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        # Simplification, most annotations span exactly one SPI byte/packet.
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def putf(self, data):
+        self.put(self.ss_field, self.es_field, self.out_ann, data)
+
+    def putc(self, data):
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+
+    def cmd_ann_list(self):
+        x, s = cmds[self.state][0], cmds[self.state][1]
+        return ['Command: %s (%s)' % (s, x), 'Command: %s' % s,
+                'Cmd: %s' % s, 'Cmd: %s' % x, x]
+
+    def emit_cmd_byte(self):
+        self.ss_cmd = self.ss
+        self.putx([Ann.FIELD, self.cmd_ann_list()])
+
+    def emit_addr_bytes(self, pdata):
+        if self.cmdstate == 2:
+            self.ss_field = self.ss
+            self.addr = chr(pdata)
+            self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+                 'Addr high 0x%c' % pdata, 'Addr h 0x%c' % pdata]])
+        elif self.cmdstate == 3:
+            self.es_field = self.es
+            self.addr += chr(pdata)
+            self.addr = int(self.addr, 16)
+            self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+                 'Addr low 0x%c' % pdata, 'Addr l 0x%c' % pdata]])
+            self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+                'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+
+    def emit_cmd_end(self, data):
+        self.es_cmd = self.es
+        self.putc(data)
+        self.state = None
+
+    def handle_read(self, data):
+        if self.cmdstate == 1:
+            self.emit_cmd_byte()
+            self.addr = 0
+        elif self.cmdstate == 2:
+            self.emit_addr_bytes(pdata)
+        elif self.cmdstate == 3:
+            self.emit_addr_bytes(pdata)
+        self.cmdstate += 1
+
+    def handle_set_common(self, pdata):
+        if self.cmdstate == 1:
+            self.addr = 0
+        self.emit_addr_bytes(pdata)
+
+    def emit_not_implemented(self, data):
+        self.es_cmd = self.es
+        self.putc([Ann.WARN, ['Command not decoded', 'Not decoded']])
+        self.emit_cmd_end(data)
+
+    def handle_string(self, pdata, ann_class):
+        # TODO: unicode / string modifiers...
+        self.handle_set_common(pdata)
+        if self.cmdstate == 4:
+            self.ss_field = self.ss
+            self.value = ''
+        if pdata == 0x00:
+            # Null terminated string ends.
+            self.es_field = self.es
+            self.putx([Ann.BIT, ['NULL']])
+            self.putf([Ann.FIELD, ['Value: %s' % self.value,
+                'Val: %s' % self.value, '%s' % self.value]])
+            self.emit_cmd_end([ann_class, self.cmd_ann_list()])
+            return
+        if self.cmdstate > 3:
+            self.value += chr(pdata)
+            self.putx([Ann.BIT, ['%c' % pdata]])
+        self.cmdstate += 1
+
+    # Command handlers
+
+    # Page change 0xA0, 0x02, index_high, index_low, checksum
+    def handle_page(self, pdata):
+        if self.cmdstate == 2:
+            if pdata == 0x02:
+                self.ss_field = self.ss_cmd
+                self.es_field = self.es
+                self.putf([Ann.FIELD, self.cmd_ann_list()])
+                self.checksum = 0xA0 + 0x02
+            else:
+                self.putx([Ann.WARN, ['Illegal second byte for page change',
+                                      'Illegal byte']])
+                self.state = None
+        elif self.cmdstate == 3:
+            self.ss_field = self.ss
+            self.checksum += pdata
+            self.page[0] = pdata
+        elif self.cmdstate == 4:
+            self.checksum += pdata
+            self.page[1] = pdata
+            self.es_field = self.es
+            if self.page[0] == self.page [1] == 0xFF:
+                # Soft reset trigger
+                self.putf(Ann.WARN, ['Soft reset', 'Reset'])
+            else:
+                page = chr(self.page[0]) + chr(self.page[1])
+                self.putf(Ann.FIELD, ['Page index: 0x%s' % page,
+                                      'Page: 0x%s' % page, '0x%s' % page])
+        elif self.cmdstate == 5:
+            self.checksum += pdata
+            if (self.checksum & 0xFF) != 0:
+                self.putx([Ann.WARN, ['Checksum error', 'Error', 'ERR']])
+            else:
+                self.putx([Ann.FIELD, ['Checksum OK', 'OK']])
+            self.emit_cmd_end(Ann.PAGE)
+        self.cmdstate += 1
+
+    # Value reads: command byte, address high nibble, address low nibble
+
+    # Get byte value
+    def handle_gbv(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.GBV, self.cmd_ann_list()])
+
+    # Get word value
+    def handle_gwv(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.GWV, self.cmd_ann_list()])
+
+    # Get string value
+    def handle_gsv(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.GSV, self.cmd_ann_list()])
+
+    # Get label value
+    def handle_glv(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.GLV, self.cmd_ann_list()])
+
+    # Get RPC buffer
+    def handle_grpc(self, pdata):
+        if self.cmdstate == 2:
+            self.ss_field = self.ss
+            self.flags = int(chr(pdata), 16) << 4
+        elif self.cmdstate == 3:
+            self.flags += int(chr(pdata), 16)
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['RPC flag: 0x%02X' % self.flags]])
+            self.emit_cmd_end([Ann.GRPC, self.cmd_ann_list()])
+
+    # Get byte value array
+    def handle_gbva(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.GBVA, self.cmd_ann_list()])
+
+    # Get word value array
+    def handle_gwva(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.GWVA, self.cmd_ann_list()])
+
+    # Get color variable
+    def handle_gcv(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.GCV, self.cmd_ann_list()])
+
+    # Value setters: command byte, address high nibble, address low nibble, data bytes
+
+    # Set byte value data = high nibble, low nibble
+    def handle_sbv(self, pdata):
+        self.handle_set_common(pdata)
+        if self.cmdstate == 4:
+            self.ss_field = self.ss
+            self.value = chr(pdata)
+        elif self.cmdstate == 5:
+            self.value += chr(pdata)
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
+                'Val: 0x%s' % self.value, '0x%s' % self.value]])
+            self.emit_cmd_end([Ann.SBV, self.cmd_ann_list()])
+        self.cmdstate += 1
+
+    # Set word value, msb high, msb low, lsb high, lsb low
+    def handle_swv(self, pdata):
+        self.handle_set_common(pdata)
+        if self.cmdstate > 3:
+            nibble = self.cmdstate - 4
+            if nibble == 0:
+                self.ss_field = self.ss
+                self.value = 0
+            self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+            if nibble == 3:
+                self.es_field = self.es
+                self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+                    'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
+                self.emit_cmd_end([Ann.SWV, self.cmd_ann_list()])
+                return
+        self.cmdstate += 1
+
+    # Set string value, null terminated utf8 strings
+    def handle_ssv(self, pdata):
+        self.handle_string(pdata, Ann.SSV)
+
+    # Set byte value array
+    def handle_sbva(self, pdata):
+        nibble = (self.cmdstate - 3) % 2
+        if self.cmdstate == 2:
+            self.addr = int(chr(pdata), 16) << 4
+            self.ss_field = self.ss
+            self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+                 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+        elif self.cmdstate == 3:
+            self.addr += int(chr(pdata), 16)
+            self.es_field = self.ss
+            self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+                 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+            self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+                'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+        elif stage == 2:
+            if pdata == 0x00:
+                # Null terminated list
+                self.emit_cmd_end([Ann.SBVA, self.cmd_ann_list()])
+                return
+            self.value = int(chr(pdata), 16) << 4
+        else:
+            self.value += int(chr(pdata), 16)
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+                                   '0x%02X' % self.value]])
+        self.cmdstate += 1
+
+    # Set word value array
+    def handle_swva(self, pdata):
+        nibble = (self.cmdstate - 3) % 4
+        if self.cmdstate == 2:
+            self.addr = int(chr(pdata), 16) << 4
+            self.ss_field = self.ss
+            self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+                  'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+        elif self.cmdstate == 3:
+            self.addr += int(chr(pdata), 16)
+            self.es_field = self.ss
+            self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+                 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+            self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+                 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+            self.value = 0
+        else:
+            self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+            if nibble == 0:
+                if pdata == 0x00:
+                    # Null terminated list
+                    self.emit_cmd_end([Ann.SWVA, self.cmd_ann_list()])
+                    return
+                self.ss_field = self.ss
+            if nibble == 3:
+                self.es_field = self.es
+                self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+                                       '0x%04X' % self.value]])
+                self.cmdstate += 1
+
+    # Set color variable
+    def handle_scv(self, pdata):
+        if self.cmdstate == 8:
+            self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+        self.cmdstate += 1
+
+    # RPC trigger
+    def handle_rpc(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.RPC, self.cmd_ann_list()])
+
+    # Drawing
+
+    # Decode pair of (x,y) 16bit coordinates
+    def decode_coords(self, pdata):
+        if self.cmdstate == 1:
+            self.coords[0] = 0
+            self.coords[1] = 0
+            self.coords[2] = 0
+            self.coords[3] = 0
+        if self.cmdstate < 18:
+            # Coordinates
+            nibble = (self.cmdstate - 1) % 4
+            i = (self.cmdstate - 1) / 4
+            self.coords[i] += int(chr(pdata), 16) << 12 - (4 * nibble)
+            if nibble == 0:
+                self.ss_field = self.ss
+            elif nibble == 3:
+                self.es_field = self.es
+                self.putf([Ann.FIELD, ['Coordinate 0x%04X' % self.coords[i]],
+                                      ['0x%04X' % self.coords[i]]])
+
+    # TODO: There are actually two protocol revisions for drawing.
+    # Both use 4 bytes for 16bit x and y pairs for start and end.
+    # The older follows this by a pattern selector and then line weight.
+    # Newer version has 6 bytes for 8bit RGB color...
+
+    # Draw line
+    def handle_line(self, pdata):
+        decode_coords(pdata)
+        if self.cmdstate == 18:
+            self.es_cmd = self.es
+            self.putc([Ann.LINE, self.cmd_ann_list()])
+            self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+            self.state = None
+        self.cmdstate += 1
+
+    # Draw rectange
+    def handle_rect(self, pdata):
+        decode_coords(pdata)
+        if self.cmdstate == 18:
+            self.es_cmd = self.es
+            self.putc([Ann.RECT, self.cmd_ann_list()])
+            self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+            self.state = None
+        self.cmdstate += 1
+
+    # Draw filled rectangle
+    def handle_frect(self, pdata):
+        decode_coords(pdata)
+        if self.cmdstate == 18:
+            self.es_cmd = self.es
+            self.putc([Ann.FRECT, self.cmd_ann_list()])
+            self.putc([Ann.WARN, ['Fill pattern / Color not implemented']])
+            self.state = None
+        self.cmdstate += 1
+
+    # Draw pixel
+    def handle_pixel(self, pdata):
+        self.es_cmd = self.es
+        self.putc([Ann.WARN, ['Draw pixel documentation is missing.', 'Undocumented']])
+        self.state = None
+
+    # Replies
+    def handle_gbvr(self, pdata):
+        self.emit_add_bytes(pdata)
+        if self.cmdstate == 4:
+            self.ss_field = self.ss
+            self.value = int(chr(pdata), 16) << 4
+            self.putx([Ann.BIT, ['High nibble 0x%s' % pdata, '0x%s' % pdata]])
+        elif self.cmdstate == 5:
+            self.value += int(chr(pdata), 16)
+            self.putx([Ann.BIT, ['Low nibble 0x%s' % pdata, '0x%s' % pdata]])
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Value: 0x%02X' % self.value,
+                                   '0x%02X' % self.value]])
+            self.emit_cmd_end([Ann.GBVR, self.cmd_ann_list()])
+        self.cmdstate += 1
+
+    def handle_gwvr(self, pdata):
+        self.emit_add_bytes(pdata)
+        if self.cmdstate > 3:
+            nibble = self.cmdstate - 3
+            if nibble == 0:
+                self.value = 0
+                self.ss_field = self.ss
+            self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+            self.putx([Ann.BIT, ['0x%s' % pdata]])
+            if nibble == 3:
+                self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+                                       '0x%04X' % self.value]])
+                self.es_cmd = self.ss
+                self.emit_cmd_end([Ann.GWVR, self.cmd_ann_list()])
+        self.cmdstate += 1
+
+    def handle_gsvr(self, pdata):
+        self.handle_string(pdata, Ann.GSVR)
+
+    def handle_glvr(self, pdata):
+        self.handle_string(pdata, Ann.GLVR)
+
+    def handle_grpcr(self, pdata):
+        self.handle_addr(pdata)
+        if self.cmdstate > 3:
+            nibble = (self.cmdstate - 3) % 2
+            if nibble == 0:
+                if pdata == 0x00:
+                    self.emit_cmd_end([Ann.GRPCR, self.cmd_ann_list()])
+                    return
+                self.value = int(chr(pdata), 16) << 4
+                self.ss_field = self.ss
+                self.putx([Ann.BIT, ['0x%s' % pdata]])
+            if nibble == 2:
+                self.value += int(chr(pdata), 16)
+                self.es_field = self.es
+                self.putx([Ann.BIT, ['0x%s' % pdata]])
+                self.putf([Ann.FIELD, ['0x%02X' % self.value]])
+        self.cmdstate += 1
+
+    def handle_sbvr(self, pdata):
+        self.handle_set_common(pdata)
+        if self.cmdstate == 4:
+            self.ss_field = self.ss
+            self.value = chr(pdata)
+        elif self.cmdstate == 5:
+            self.value += chr(pdata)
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
+                'Val: 0x%s' % self.value, '0x%s' % self.value]])
+            self.emit_cmd_end([Ann.SBVR, self.cmd_ann_list()])
+        self.cmdstate += 1
+
+    def handle_swvr(self, pdata):
+        self.handle_set_common(pdata)
+        if self.cmdstate == 4:
+            self.ss_field = self.ss
+            self.value = (pdata - 0x30) << 4
+        elif self.cmdstate == 5:
+            self.value += (pdata - 0x30)
+            self.value = self.value << 8
+        elif self.cmdstate == 6:
+            self.value += (pdata - 0x30) << 4
+        elif self.cmdstate == 7:
+            self.value += (pdata - 0x30)
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+                'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
+            self.emit_cmd_end([Ann.SWVR, self.cmd_ann_list()])
+            self.state = None
+        self.cmdstate += 1
+
+    def handle_ssvr(self, pdata):
+        self.handle_string(pdata, Ann.SSVR)
+
+    def handle_rpcr(self, pdata):
+        self.handle_read(pdata)
+        self.emit_cmd_end([Ann.RPCR, self.cmd_ann_list()])
+
+    def handle_liner(self, pdata):
+        decode_coords(pdata)
+        if self.cmdstate == 18:
+            self.es_cmd = self.es
+            self.putc([Ann.LINER, self.cmd_ann_list()])
+            self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+            self.state = None
+        self.cmdstate += 1
+
+    def handle_rectr(self, pdata):
+        decode_coords(pdata)
+        if self.cmdstate == 18:
+            self.es_cmd = self.es
+            self.putc([Ann.RECTR, self.cmd_ann_list()])
+            self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+            self.state = None
+        self.cmdstate += 1
+
+    def handle_frectr(self, pdata):
+        decode_coords(pdata)
+        if self.cmdstate == 18:
+            self.es_cmd = self.es
+            self.putc([Ann.FRECTR, self.cmd_ann_list()])
+            self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+            self.state = None
+        self.cmdstate += 1
+
+    def handle_pixelr(self, pdata):
+        self.es_cmd = self.es
+        self.putc([Ann.WARN,['Draw pixel documentation is missing.', 'Undocumented']])
+        self.state = None
+
+    def handle_gbvar(self, pdata):
+        nibble = (self.cmdstate - 3) % 2
+        if self.cmdstate == 2:
+            self.addr = int(chr(pdata), 16) << 4
+            self.ss_field = self.ss
+            self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+                 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+        elif self.cmdstate == 3:
+            self.addr += int(chr(pdata), 16)
+            self.es_field = self.ss
+            self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+                 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+            self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+                'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+        elif stage == 2:
+            if pdata == 0x00:
+                # Null terminated list
+                self.emit_cmd_end([Ann.GBVAR, self.cmd_ann_list()])
+                return
+            self.value = int(chr(pdata), 16) << 4
+        else:
+            self.value += int(chr(pdata), 16)
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+                                   '0x%02X' % self.value]])
+        self.cmdstate += 1
+
+    def handle_gwvar(self, pdata):
+        nibble = (self.cmdstate - 3) % 4
+        if self.cmdstate == 2:
+            self.addr = int(chr(pdata), 16) << 4
+            self.ss_field = self.ss
+            self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+                  'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+        elif self.cmdstate == 3:
+            self.addr += int(chr(pdata), 16)
+            self.es_field = self.ss
+            self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+                 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+            self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+                 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+            self.value = 0
+        else:
+            self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+            if nibble == 0:
+                if pdata == 0x00:
+                    # Null terminated list
+                    self.emit_cmd_end([Ann.GWVAR, self.cmd_ann_list()])
+                    return
+                self.ss_field = self.ss
+            if nibble == 3:
+                self.es_field = self.es
+                self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+                                       '0x%04X' % self.value]])
+                self.cmdstate += 1
+
+    # Get byte variable array reply
+    def handle_sbvar(self, pdata):
+        nibble = (self.cmdstate - 3) % 2
+        if self.cmdstate == 2:
+            self.addr = int(chr(pdata), 16) << 4
+            self.ss_field = self.ss
+            self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+                 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+        elif self.cmdstate == 3:
+            self.addr += int(chr(pdata), 16)
+            self.es_field = self.ss
+            self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+                 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+            self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+                'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+        elif stage == 2:
+            if pdata == 0x00:
+                # Null terminated list
+                self.emit_cmd_end([Ann.SBVAR, self.cmd_ann_list()])
+                return
+            self.value = int(chr(pdata), 16) << 4
+        else:
+            self.value += int(chr(pdata), 16)
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+                                   '0x%02X' % self.value]])
+        self.cmdstate += 1
+
+    # Set word variable array reply
+    def handle_swvar(self, pdata):
+        nibble = (self.cmdstate - 3) % 4
+        if self.cmdstate == 2:
+            self.addr = int(chr(pdata), 16) << 4
+            self.ss_field = self.ss
+            self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+                  'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+        elif self.cmdstate == 3:
+            self.addr += int(chr(pdata), 16)
+            self.es_field = self.ss
+            self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+                 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+            self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+                 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+            self.value = 0
+        else:
+            self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+            if nibble == 0:
+                if pdata == 0x00:
+                    # Null terminated list
+                    self.emit_cmd_end([Ann.SWVAR, self.cmd_ann_list()])
+                    return
+                self.ss_field = self.ss
+            if nibble == 3:
+                self.es_field = self.es
+                self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+                                       '0x%04X' % self.value]])
+                self.cmdstate += 1
+
+    def handle_gcvr(self, pdata):
+        if self.cmdstate == 8:
+            self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+        self.cmdstate += 1
+
+    def handle_scvr(self, pdata):
+        if self.cmdstate == 8:
+            self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+        self.cmdstate += 1
+
+    # ACK & NACK
+
+    def handle_ack(self, pdata):
+        self.putx([Ann.ACK, self.cmd_ann_list()])
+        self.state = None
+
+    def handle_nack(self, pdata):
+        self.putx([Ann.NACK, self.cmd_ann_list()])
+        self.state = None
+
+    def decode(self, ss, es, data):
+        ptype, rxtx, pdata = data
+
+        self.ss, self.es = ss, es
+
+        if ptype != 'DATA':
+            return
+
+        # Handle commands.
+        try:
+            abort_current = (0xD0 <= pdata[0] <= 0xF7) and \
+                (not (self.state in cmds_with_high_bytes)) and \
+                self.state != None
+            if abort_current:
+                self.putx([Ann.WARN, ['Command aborted by invalid byte', 'Abort']])
+                self.state = pdata[0]
+                self.emit_cmd_byte()
+                self.cmdstate = 1
+            if self.state is None:
+                self.state = pdata[0]
+                self.emit_cmd_byte()
+                self.cmdstate = 1
+            self.cmd_handlers[self.state](pdata[0])
+        except KeyError:
+            self.putx([Ann.WARN, ['Unknown command: 0x%02x' % pdata[0]]])
+            self.state = None
index 2ce2974c0f9a126576b92262f780d10b4bf7c047..617063ca7825b035253105f58b78a9318c2d6274 100644 (file)
@@ -18,7 +18,7 @@
 ##
 
 '''
 ##
 
 '''
-This decoder stacks on top of the 'uart' decoder and decodes packets of
+This decoder stacks on top of the 'uart' PD and decodes packets of
 the ARMv7m Embedded Trace Macroblock v3.x.
 '''
 
 the ARMv7m Embedded Trace Macroblock v3.x.
 '''
 
index 8de3ce2c8d40915af88ef580f1a645a88d2e62b0..6649b46e021dd781c0a5a06098fb5fc0af012fb8 100644 (file)
@@ -31,30 +31,30 @@ exc_names = [
 for i in range(8, 496):
     exc_names.append('IRQ%d' % i)
 
 for i in range(8, 496):
     exc_names.append('IRQ%d' % i)
 
-def parse_varint(bytes):
+def parse_varint(bytes_):
     '''Parse an integer where the top bit is the continuation bit.
     Returns value and number of parsed bytes.'''
     v = 0
     '''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
         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
     '''Parse little-endian integer.'''
     v = 0
-    for i, b in enumerate(bytes):
+    for i, b in enumerate(bytes_):
         v |= b << (i * 8)
     return v
 
         v |= b << (i * 8)
     return v
 
-def parse_exc_info(bytes):
+def parse_exc_info(bytes_):
     '''Parse exception information bytes from a branch packet.'''
     '''Parse exception information bytes from a branch packet.'''
-    if len(bytes) < 1:
+    if len(bytes_) < 1:
         return None
 
         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):
         return None # Exception info not complete.
 
     if exclen == 2 and excv & (1 << 13):
@@ -69,21 +69,21 @@ def parse_exc_info(bytes):
     resume = (excv >> 14) & 0x0F
     return (ns, exc, cancel, altisa, hyp, resume)
 
     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'''
 
     '''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':
         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':
         addr_bits -= 1 # Top bit of address indicates exc_info.
             have_exc_info = True
     elif branch_enc == 'alternative':
         addr_bits -= 1 # Top bit of address indicates exc_info.
@@ -93,20 +93,20 @@ def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc):
 
     exc_info = None
     if have_exc_info:
 
     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 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'
             cpu_state = 'arm'
-        elif bytes[4] & 0xB0 == 0x10:
+        elif bytes_[4] & 0xB0 == 0x10:
             cpu_state = 'thumb'
             cpu_state = 'thumb'
-        elif bytes[4] & 0xA0 == 0x20:
+        elif bytes_[4] & 0xA0 == 0x20:
             cpu_state = 'jazelle'
         else:
             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':
 
     # Shift the address according to current CPU state.
     if cpu_state == 'arm':
@@ -130,11 +130,12 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'arm_etmv3'
     name = 'ARM ETMv3'
     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']
     license = 'gplv2+'
     inputs = ['uart']
-    outputs = ['arm_etmv3']
+    outputs = []
+    tags = ['Debug/trace']
     annotations = (
         ('trace', 'Trace info'),
         ('branch', 'Branches'),
     annotations = (
         ('trace', 'Trace info'),
         ('branch', 'Branches'),
index 5970f27c0f9c2b97120562e6cf075b2cac7e24c8..641497878a351459a70421217c50b08b109bf15c 100644 (file)
@@ -41,10 +41,11 @@ class Decoder(srd.Decoder):
     id = 'arm_itm'
     name = 'ARM ITM'
     longname = 'ARM Instrumentation Trace Macroblock'
     id = 'arm_itm'
     name = 'ARM ITM'
     longname = 'ARM Instrumentation Trace Macroblock'
-    desc = 'Trace data from Cortex-M / ARMv7m ITM module.'
+    desc = 'ARM Cortex-M / ARMv7m ITM trace protocol.'
     license = 'gplv2+'
     inputs = ['uart']
     license = 'gplv2+'
     inputs = ['uart']
-    outputs = ['arm_itm']
+    outputs = []
+    tags = ['Debug/trace']
     options = (
         {'id': 'objdump', 'desc': 'objdump path',
             'default': 'arm-none-eabi-objdump'},
     options = (
         {'id': 'objdump', 'desc': 'objdump path',
             'default': 'arm-none-eabi-objdump'},
index 3e9ec0efbeb8e010f115a04f2d14c48349b061fe..ce9c3744efc5ffced5fd54c8362a70fa0c53796a 100644 (file)
 ##
 
 '''
 ##
 
 '''
-This decoder stacks on top of the 'uart' decoder and decodes the frame format
-of ARMv7m Trace Port Interface Unit. It filters the data coming from various
-trace sources (such as ARMv7m ITM and ETM blocks) into separate streams that
-can be further decoded by other PDs.
+This decoder stacks on top of the 'uart' PD and decodes the frame format
+of ARMv7m Trace Port Interface Unit.
+
+It filters the data coming from various trace sources (such as ARMv7m ITM
+and ETM blocks) into separate streams that can be further decoded by other PDs.
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index f50af65b471b652814aaa45f22185a8afc8ea7e3..29b4605fe72a3f134497b2663ae0f394c35853b8 100644 (file)
@@ -28,6 +28,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['uart']
     outputs = ['uart'] # Emulate uart output so that arm_itm/arm_etm can stack.
     license = 'gplv2+'
     inputs = ['uart']
     outputs = ['uart'] # Emulate uart output so that arm_itm/arm_etm can stack.
+    tags = ['Debug/trace']
     options = (
         {'id': 'stream', 'desc': 'Stream index', 'default': 1},
         {'id': 'sync_offset', 'desc': 'Initial sync offset', 'default': 0},
     options = (
         {'id': 'stream', 'desc': 'Stream index', 'default': 1},
         {'id': 'sync_offset', 'desc': 'Initial sync offset', 'default': 0},
index fc56bd669ea17d6fec040185e13e637d490f3136..fd0f4288e308e414e0fd0ce914b529395a86b094 100644 (file)
 
 '''
 This decoder stacks on top of the 'i2c' PD and decodes the
 
 '''
 This decoder stacks on top of the 'i2c' PD and decodes the
-Microchip ATSHA204A CryptoAuthentication protocol.
+Microchip ATSHA204A and ATECC508A crypto authentication protocol.
+
+The decoder might also support the following devices (untested):
+ * ATSHA204
+ * ATECC108
+ * ATECC108A
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index a6a5a1dfa328953355fb02ef8673538485891f89..c666332a63917f8bff535a7d9dc3b8f60cffc3e4 100644 (file)
@@ -26,19 +26,25 @@ WORD_ADDR_COMMAND       = 0x03
 
 WORD_ADDR = {0x00: 'RESET', 0x01: 'SLEEP', 0x02: 'IDLE', 0x03: 'COMMAND'}
 
 
 WORD_ADDR = {0x00: 'RESET', 0x01: 'SLEEP', 0x02: 'IDLE', 0x03: 'COMMAND'}
 
+OPCODE_COUNTER          = 0x24
 OPCODE_DERIVE_KEY       = 0x1c
 OPCODE_DEV_REV          = 0x30
 OPCODE_DERIVE_KEY       = 0x1c
 OPCODE_DEV_REV          = 0x30
+OPCODE_ECDH             = 0x43
 OPCODE_GEN_DIG          = 0x15
 OPCODE_GEN_DIG          = 0x15
+OPCODE_GEN_KEY          = 0x40
 OPCODE_HMAC             = 0x11
 OPCODE_CHECK_MAC        = 0x28
 OPCODE_LOCK             = 0x17
 OPCODE_MAC              = 0x08
 OPCODE_NONCE            = 0x16
 OPCODE_PAUSE            = 0x01
 OPCODE_HMAC             = 0x11
 OPCODE_CHECK_MAC        = 0x28
 OPCODE_LOCK             = 0x17
 OPCODE_MAC              = 0x08
 OPCODE_NONCE            = 0x16
 OPCODE_PAUSE            = 0x01
+OPCODE_PRIVWRITE        = 0x46
 OPCODE_RANDOM           = 0x1b
 OPCODE_READ             = 0x02
 OPCODE_SHA              = 0x47
 OPCODE_RANDOM           = 0x1b
 OPCODE_READ             = 0x02
 OPCODE_SHA              = 0x47
+OPCODE_SIGN             = 0x41
 OPCODE_UPDATE_EXTRA     = 0x20
 OPCODE_UPDATE_EXTRA     = 0x20
+OPCODE_VERIFY           = 0x45
 OPCODE_WRITE            = 0x12
 
 OPCODES = {
 OPCODE_WRITE            = 0x12
 
 OPCODES = {
@@ -53,8 +59,14 @@ OPCODES = {
     0x1b: 'Random',
     0x1c: 'DeriveKey',
     0x20: 'UpdateExtra',
     0x1b: 'Random',
     0x1c: 'DeriveKey',
     0x20: 'UpdateExtra',
+    0x24: 'Counter',
     0x28: 'CheckMac',
     0x30: 'DevRev',
     0x28: 'CheckMac',
     0x30: 'DevRev',
+    0x40: 'GenKey',
+    0x41: 'Sign',
+    0x43: 'ECDH',
+    0x45: 'Verify',
+    0x46: 'PrivWrite',
     0x47: 'SHA',
 }
 
     0x47: 'SHA',
 }
 
@@ -85,10 +97,11 @@ class Decoder(srd.Decoder):
     id = 'atsha204a'
     name = 'ATSHA204A'
     longname = 'Microchip ATSHA204A'
     id = 'atsha204a'
     name = 'ATSHA204A'
     longname = 'Microchip ATSHA204A'
-    desc = 'Microchip ATSHA204A CryptoAuthentication device.'
+    desc = 'Microchip ATSHA204A family crypto authentication protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['atsha204a']
+    outputs = []
+    tags = ['Security/crypto', 'IC', 'Memory']
     annotations = (
         ('waddr', 'Word address'),
         ('count', 'Count'),
     annotations = (
         ('waddr', 'Word address'),
         ('count', 'Count'),
@@ -176,11 +189,15 @@ class Decoder(srd.Decoder):
 
     def put_param1(self, s):
         op = self.opcode
 
     def put_param1(self, s):
         op = self.opcode
-        if op in (OPCODE_CHECK_MAC, OPCODE_DEV_REV, OPCODE_HMAC, \
-                OPCODE_MAC, OPCODE_NONCE, OPCODE_RANDOM, OPCODE_SHA):
+        if op in (OPCODE_CHECK_MAC, OPCODE_COUNTER, OPCODE_DEV_REV,     \
+                  OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_HMAC, OPCODE_MAC, \
+                  OPCODE_NONCE, OPCODE_RANDOM, OPCODE_SHA, OPCODE_SIGN, \
+                  OPCODE_VERIFY):
             self.putx(s, [3, ['Mode: %02X' % s[2]]])
         elif op == OPCODE_DERIVE_KEY:
             self.putx(s, [3, ['Random: %s' % s[2]]])
             self.putx(s, [3, ['Mode: %02X' % s[2]]])
         elif op == OPCODE_DERIVE_KEY:
             self.putx(s, [3, ['Random: %s' % s[2]]])
+        elif op == OPCODE_PRIVWRITE:
+            self.putx(s, [3, ['Encrypted: {}'.format('Yes' if s[2] & 0x40 else 'No')]])
         elif op == OPCODE_GEN_DIG:
             self.putx(s, [3, ['Zone: %s' % ZONES[s[2]]]])
         elif op == OPCODE_LOCK:
         elif op == OPCODE_GEN_DIG:
             self.putx(s, [3, ['Zone: %s' % ZONES[s[2]]]])
         elif op == OPCODE_LOCK:
@@ -202,6 +219,9 @@ class Decoder(srd.Decoder):
         op = self.opcode
         if op == OPCODE_DERIVE_KEY:
             self.puty(s, [4, ['TargetKey: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
         op = self.opcode
         if op == OPCODE_DERIVE_KEY:
             self.puty(s, [4, ['TargetKey: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
+        elif op in (OPCODE_COUNTER, OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_PRIVWRITE, \
+                    OPCODE_SIGN, OPCODE_VERIFY):
+            self.puty(s, [4, ['KeyID: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
         elif op in (OPCODE_NONCE, OPCODE_PAUSE, OPCODE_RANDOM):
             self.puty(s, [4, ['Zero: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
         elif op in (OPCODE_HMAC, OPCODE_MAC, OPCODE_CHECK_MAC, OPCODE_GEN_DIG):
         elif op in (OPCODE_NONCE, OPCODE_PAUSE, OPCODE_RANDOM):
             self.puty(s, [4, ['Zero: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
         elif op in (OPCODE_HMAC, OPCODE_MAC, OPCODE_CHECK_MAC, OPCODE_GEN_DIG):
@@ -220,15 +240,33 @@ class Decoder(srd.Decoder):
             return
         op = self.opcode
         if op == OPCODE_CHECK_MAC:
             return
         op = self.opcode
         if op == OPCODE_CHECK_MAC:
-            self.putz(s[0][0], s[31][1], [5, ['ClientChal: %s' % ' '.join(format(i[2], '02x') for i in s[0:31])]])
-            self.putz(s[32][0], s[63][1], [5, ['ClientResp: %s' % ' '.join(format(i[2], '02x') for i in s[32:63])]])
-            self.putz(s[64][0], s[76][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:76])]])
+            self.putz(s[0][0], s[31][1], [5, ['ClientChal: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
+            self.putz(s[32][0], s[63][1], [5, ['ClientResp: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
+            self.putz(s[64][0], s[76][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:77])]])
         elif op == OPCODE_DERIVE_KEY:
             self.putz(s[0][0], s[31][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
         elif op == OPCODE_DERIVE_KEY:
             self.putz(s[0][0], s[31][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
-        elif op == OPCODE_GEN_DIG:
+        elif op == OPCODE_ECDH:
+            self.putz(s[0][0], s[31][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
+            self.putz(s[32][0], s[63][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
+        elif op in (OPCODE_GEN_DIG, OPCODE_GEN_KEY):
             self.putz(s[0][0], s[3][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s)]])
         elif op == OPCODE_MAC:
             self.putz(s[0][0], s[31][1], [5, ['Challenge: %s' % ' '.join(format(i[2], '02x') for i in s)]])
             self.putz(s[0][0], s[3][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s)]])
         elif op == OPCODE_MAC:
             self.putz(s[0][0], s[31][1], [5, ['Challenge: %s' % ' '.join(format(i[2], '02x') for i in s)]])
+        elif op == OPCODE_PRIVWRITE:
+            if len(s) > 36: # Key + MAC.
+                self.putz(s[0][0], s[-35][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
+                self.putz(s[-32][0], s[-1][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
+            else: # Just value.
+                self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
+        elif op == OPCODE_VERIFY:
+            if len(s) >= 64: # ECDSA components (always present)
+                self.putz(s[0][0], s[31][1], [5, ['ECDSA R: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
+                self.putz(s[32][0], s[63][1], [5, ['ECDSA S: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
+            if len(s) == 83: # OtherData (follow ECDSA components in validate / invalidate mode)
+                self.putz(s[64][0], s[82][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:83])]])
+            if len(s) == 128: # Public key components (follow ECDSA components in external mode)
+                self.putz(s[64][0], s[95][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[64:96])]])
+                self.putz(s[96][0], s[127][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[96:128])]])
         elif op == OPCODE_WRITE:
             if len(s) > 32: # Value + MAC.
                 self.putz(s[0][0], s[-31][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
         elif op == OPCODE_WRITE:
             if len(s) > 32: # Value + MAC.
                 self.putz(s[0][0], s[-31][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
@@ -249,7 +287,6 @@ class Decoder(srd.Decoder):
 
     def decode(self, ss, es, data):
         cmd, databyte = data
 
     def decode(self, ss, es, data):
         cmd, databyte = data
-
         # State machine.
         if self.state == 'IDLE':
             # Wait for an I²C START condition.
         # State machine.
         if self.state == 'IDLE':
             # Wait for an I²C START condition.
@@ -271,7 +308,8 @@ class Decoder(srd.Decoder):
                 # Reset the opcode before received data, as this causes
                 # responses to be displayed incorrectly.
                 self.opcode = -1
                 # Reset the opcode before received data, as this causes
                 # responses to be displayed incorrectly.
                 self.opcode = -1
-                self.output_rx_bytes()
+                if len(self.bytes) > 0:
+                    self.output_rx_bytes()
                 self.waddr = -1
                 self.bytes = []
                 self.state = 'IDLE'
                 self.waddr = -1
                 self.bytes = []
                 self.state = 'IDLE'
@@ -283,4 +321,3 @@ class Decoder(srd.Decoder):
                 self.output_tx_bytes()
                 self.bytes = []
                 self.state = 'IDLE'
                 self.output_tx_bytes()
                 self.bytes = []
                 self.state = 'IDLE'
-
index 30c32f5fcc89d46d89a6555f00a5fcaac2ac4723..ea19a100d3eec30e8ce3716b0a75c534a3b2d58a 100644 (file)
@@ -32,7 +32,8 @@ class Decoder(srd.Decoder):
     desc = 'Renesas/Hitachi Advanced User Debugger (AUD) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Renesas/Hitachi Advanced User Debugger (AUD) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['aud']
+    outputs = []
+    tags = ['Debug/trace']
     channels = (
         {'id': 'audck', 'name': 'AUDCK', 'desc': 'AUD clock'},
         {'id': 'naudsync', 'name': 'nAUDSYNC', 'desc': 'AUD sync'},
     channels = (
         {'id': 'audck', 'name': 'AUDCK', 'desc': 'AUD clock'},
         {'id': 'naudsync', 'name': 'nAUDSYNC', 'desc': 'AUD sync'},
index 2530e8cc23b4c5bb0bb44a3281f96e90977e189d..a0719b73dd5614a21332eccd71ef44bf5397d929 100644 (file)
@@ -27,10 +27,11 @@ class Decoder(srd.Decoder):
     id = 'avr_isp'
     name = 'AVR ISP'
     longname = 'AVR In-System Programming'
     id = 'avr_isp'
     name = 'AVR ISP'
     longname = 'AVR In-System Programming'
-    desc = 'Protocol for in-system programming Atmel AVR MCUs.'
+    desc = 'Atmel AVR In-System Programming (ISP) protocol.'
     license = 'gplv2+'
     inputs = ['spi']
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['avr_isp']
+    outputs = []
+    tags = ['Debug/trace']
     annotations = (
         ('pe', 'Programming enable'),
         ('rsb0', 'Read signature byte 0'),
     annotations = (
         ('pe', 'Programming enable'),
         ('rsb0', 'Read signature byte 0'),
index ebe647bdbed754ea620583be6266bcb90ec7b2a9..1c61dea7961e4c86ea2f295c4354129de37831e7 100644 (file)
 
 '''
 PDI (Program and Debug Interface) is an Atmel proprietary interface for
 
 '''
 PDI (Program and Debug Interface) is an Atmel proprietary interface for
-external programming and on-chip debugging of the device. See the Atmel
-Application Note AVR1612 "PDI programming driver" and the "Program and
-Debug Interface" section in the Xmega A manual for details.
+external programming and on-chip debugging of the device.
+
+See the Atmel Application Note AVR1612 "PDI programming driver" and the
+"Program and Debug Interface" section in the Xmega A manual for details.
 
 The protocol uses two pins: the RESET pin and one dedicated DATA pin.
 The RESET pin provides a clock, the DATA pin communicates serial frames
 
 The protocol uses two pins: the RESET pin and one dedicated DATA pin.
 The RESET pin provides a clock, the DATA pin communicates serial frames
index 7fedbbdfe18e4e18b8447134adc271591c143d21..164b9926a6756332b2550a8da8ff94708dd2fdb1 100644 (file)
@@ -116,10 +116,11 @@ class Decoder(srd.Decoder):
     id = 'avr_pdi'
     name = 'AVR PDI'
     longname = 'Atmel Program and Debug Interface'
     id = 'avr_pdi'
     name = 'AVR PDI'
     longname = 'Atmel Program and Debug Interface'
-    desc = 'Atmel proprietary interface for the ATxmega MCU.'
+    desc = 'Atmel ATxmega Program and Debug Interface (PDI) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['pdi']
+    outputs = []
+    tags = ['Debug/trace']
     channels = (
         {'id': 'reset', 'name': 'RESET', 'desc': 'RESET / PDI_CLK'},
         {'id': 'data', 'name': 'DATA', 'desc': 'PDI_DATA'},
     channels = (
         {'id': 'reset', 'name': 'RESET', 'desc': 'RESET / PDI_CLK'},
         {'id': 'data', 'name': 'DATA', 'desc': 'PDI_DATA'},
index 47f571d62072b0b8f9adddfd56b6dbb674ed78ea..888bb81928bcf4e229a94d5278810795e6b4a346 100644 (file)
@@ -24,6 +24,8 @@ real-time control.
 This decoder assumes that a single CAN_RX line is sampled (e.g. on
 the digital output side of a CAN transceiver IC such as the Microchip
 MCP-2515DM-BM).
 This decoder assumes that a single CAN_RX line is sampled (e.g. on
 the digital output side of a CAN transceiver IC such as the Microchip
 MCP-2515DM-BM).
+
+It also has support for CAN-FD.
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index d76d649cc593bfeb7101f636cbb5b4d476d64619..8817097b50e48cef770c031fe2f0efcb7113def9 100644 (file)
@@ -2,6 +2,7 @@
 ## This file is part of the libsigrokdecode project.
 ##
 ## Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
 ## This file is part of the libsigrokdecode project.
 ##
 ## Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
 ##
 ## This program is free software; you can redistribute it and/or modify
 ## it under the terms of the GNU General Public License as published by
 ##
 ## This program is free software; you can redistribute it and/or modify
 ## it under the terms of the GNU General Public License as published by
@@ -22,6 +23,9 @@ import sigrokdecode as srd
 class SamplerateError(Exception):
     pass
 
 class SamplerateError(Exception):
     pass
 
+def dlc2len(dlc):
+    return [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64][dlc]
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'can'
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'can'
@@ -30,12 +34,14 @@ class Decoder(srd.Decoder):
     desc = 'Field bus protocol for distributed realtime control.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Field bus protocol for distributed realtime control.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['can']
+    outputs = []
+    tags = ['Automotive']
     channels = (
         {'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'},
     )
     options = (
     channels = (
         {'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'},
     )
     options = (
-        {'id': 'bitrate', 'desc': 'Bitrate (bits/s)', 'default': 1000000},
+        {'id': 'nominal_bitrate', 'desc': 'Nominal bitrate (bits/s)', 'default': 1000000},
+        {'id': 'fast_bitrate', 'desc': 'Fast bitrate (bits/s)', 'default': 2000000},
         {'id': 'sample_point', 'desc': 'Sample point (%)', 'default': 70.0},
     )
     annotations = (
         {'id': 'sample_point', 'desc': 'Sample point (%)', 'default': 70.0},
     )
     annotations = (
@@ -74,10 +80,20 @@ class Decoder(srd.Decoder):
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
+    def set_bit_rate(self, bitrate):
+        self.bit_width = float(self.samplerate) / float(bitrate)
+        self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
+
+    def set_nominal_bitrate(self):
+        self.set_bit_rate(self.options['nominal_bitrate'])
+
+    def set_fast_bitrate(self):
+        self.set_bit_rate(self.options['fast_bitrate'])
+
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
-            self.bit_width = float(self.samplerate) / float(self.options['bitrate'])
+            self.bit_width = float(self.samplerate) / float(self.options['nominal_bitrate'])
             self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
 
     # Generic helper for CAN bit annotations.
             self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
 
     # Generic helper for CAN bit annotations.
@@ -93,6 +109,10 @@ class Decoder(srd.Decoder):
     def put12(self, data):
         self.putg(self.ss_bit12, self.ss_bit12, data)
 
     def put12(self, data):
         self.putg(self.ss_bit12, self.ss_bit12, data)
 
+    # Single-CAN-bit annotation using the samplenum of CAN bit 32.
+    def put32(self, data):
+        self.putg(self.ss_bit32, self.ss_bit32, data)
+
     # Multi-CAN-bit annotation from self.ss_block to current samplenum.
     def putb(self, data):
         self.putg(self.ss_block, self.samplenum, data)
     # Multi-CAN-bit annotation from self.ss_block to current samplenum.
     def putb(self, data):
         self.putg(self.ss_block, self.samplenum, data)
@@ -106,7 +126,10 @@ class Decoder(srd.Decoder):
         self.last_databit = 999 # Positive value that bitnum+x will never match
         self.ss_block = None
         self.ss_bit12 = None
         self.last_databit = 999 # Positive value that bitnum+x will never match
         self.ss_block = None
         self.ss_bit12 = None
+        self.ss_bit32 = None
         self.ss_databytebits = []
         self.ss_databytebits = []
+        self.fd = False
+        self.rtr = None
 
     # Poor man's clock synchronization. Use signal edges which change to
     # dominant state in rather simple ways. This naive approach is neither
 
     # Poor man's clock synchronization. Use signal edges which change to
     # dominant state in rather simple ways. This naive approach is neither
@@ -124,9 +147,9 @@ class Decoder(srd.Decoder):
     # Determine the position of the next desired bit's sample point.
     def get_sample_point(self, bitnum):
         samplenum = self.dom_edge_snum
     # Determine the position of the next desired bit's sample point.
     def get_sample_point(self, bitnum):
         samplenum = self.dom_edge_snum
-        samplenum += int(self.bit_width * (bitnum - self.dom_edge_bcount))
-        samplenum += int(self.sample_point)
-        return samplenum
+        samplenum += self.bit_width * (bitnum - self.dom_edge_bcount)
+        samplenum += self.sample_point
+        return int(samplenum)
 
     def is_stuff_bit(self):
         # CAN uses NRZ encoding and bit stuffing.
 
     def is_stuff_bit(self):
         # CAN uses NRZ encoding and bit stuffing.
@@ -159,42 +182,60 @@ class Decoder(srd.Decoder):
         # Remember start of CRC sequence (see below).
         if bitnum == (self.last_databit + 1):
             self.ss_block = self.samplenum
         # Remember start of CRC sequence (see below).
         if bitnum == (self.last_databit + 1):
             self.ss_block = self.samplenum
+            if self.fd:
+                if dlc2len(self.dlc) < 16:
+                    self.crc_len = 27 # 17 + SBC + stuff bits
+                else:
+                    self.crc_len = 32 # 21 + SBC + stuff bits
+            else:
+                self.crc_len = 15
+
+        # CRC sequence (15 bits, 17 bits or 21 bits)
+        elif bitnum == (self.last_databit + self.crc_len):
+            if self.fd:
+                if dlc2len(self.dlc) < 16:
+                    crc_type = "CRC-17"
+                else:
+                    crc_type = "CRC-21"
+            else:
+                crc_type = "CRC-15"
 
 
-        # CRC sequence (15 bits)
-        elif bitnum == (self.last_databit + 15):
             x = self.last_databit + 1
             x = self.last_databit + 1
-            crc_bits = self.bits[x:x + 15 + 1]
+            crc_bits = self.bits[x:x + self.crc_len + 1]
             self.crc = int(''.join(str(d) for d in crc_bits), 2)
             self.crc = int(''.join(str(d) for d in crc_bits), 2)
-            self.putb([11, ['CRC sequence: 0x%04x' % self.crc,
-                            'CRC: 0x%04x' % self.crc, 'CRC']])
+            self.putb([11, ['%s sequence: 0x%04x' % (crc_type, self.crc),
+                            '%s: 0x%04x' % (crc_type, self.crc), '%s' % crc_type]])
             if not self.is_valid_crc(crc_bits):
                 self.putb([16, ['CRC is invalid']])
 
         # CRC delimiter bit (recessive)
             if not self.is_valid_crc(crc_bits):
                 self.putb([16, ['CRC is invalid']])
 
         # CRC delimiter bit (recessive)
-        elif bitnum == (self.last_databit + 16):
+        elif bitnum == (self.last_databit + self.crc_len + 1):
             self.putx([12, ['CRC delimiter: %d' % can_rx,
                             'CRC d: %d' % can_rx, 'CRC d']])
             if can_rx != 1:
                 self.putx([16, ['CRC delimiter must be a recessive bit']])
 
             self.putx([12, ['CRC delimiter: %d' % can_rx,
                             'CRC d: %d' % can_rx, 'CRC d']])
             if can_rx != 1:
                 self.putx([16, ['CRC delimiter must be a recessive bit']])
 
+            if self.fd:
+                self.set_nominal_bitrate()
+
         # ACK slot bit (dominant: ACK, recessive: NACK)
         # ACK slot bit (dominant: ACK, recessive: NACK)
-        elif bitnum == (self.last_databit + 17):
+        elif bitnum == (self.last_databit + self.crc_len + 2):
             ack = 'ACK' if can_rx == 0 else 'NACK'
             self.putx([13, ['ACK slot: %s' % ack, 'ACK s: %s' % ack, 'ACK s']])
 
         # ACK delimiter bit (recessive)
             ack = 'ACK' if can_rx == 0 else 'NACK'
             self.putx([13, ['ACK slot: %s' % ack, 'ACK s: %s' % ack, 'ACK s']])
 
         # ACK delimiter bit (recessive)
-        elif bitnum == (self.last_databit + 18):
+        elif bitnum == (self.last_databit + self.crc_len + 3):
             self.putx([14, ['ACK delimiter: %d' % can_rx,
                             'ACK d: %d' % can_rx, 'ACK d']])
             if can_rx != 1:
                 self.putx([16, ['ACK delimiter must be a recessive bit']])
 
         # Remember start of EOF (see below).
             self.putx([14, ['ACK delimiter: %d' % can_rx,
                             'ACK d: %d' % can_rx, 'ACK d']])
             if can_rx != 1:
                 self.putx([16, ['ACK delimiter must be a recessive bit']])
 
         # Remember start of EOF (see below).
-        elif bitnum == (self.last_databit + 19):
+        elif bitnum == (self.last_databit + self.crc_len + 4):
             self.ss_block = self.samplenum
 
         # End of frame (EOF), 7 recessive bits
             self.ss_block = self.samplenum
 
         # End of frame (EOF), 7 recessive bits
-        elif bitnum == (self.last_databit + 25):
+        elif bitnum == (self.last_databit + self.crc_len + 10):
             self.putb([2, ['End of frame', 'EOF', 'E']])
             if self.rawbits[-7:] != [1, 1, 1, 1, 1, 1, 1]:
                 self.putb([16, ['End of frame (EOF) must be 7 recessive bits']])
             self.putb([2, ['End of frame', 'EOF', 'E']])
             if self.rawbits[-7:] != [1, 1, 1, 1, 1, 1, 1]:
                 self.putb([16, ['End of frame (EOF) must be 7 recessive bits']])
@@ -206,42 +247,63 @@ class Decoder(srd.Decoder):
     # Returns True if the frame ended (EOF), False otherwise.
     def decode_standard_frame(self, can_rx, bitnum):
 
     # Returns True if the frame ended (EOF), False otherwise.
     def decode_standard_frame(self, can_rx, bitnum):
 
-        # Bit 14: RB0 (reserved bit)
-        # Has to be sent dominant, but receivers should accept recessive too.
+        # Bit 14: FDF (Flexible data format)
+        # Has to be sent dominant when FD frame, has to be sent recessive
+        # when classic CAN frame.
         if bitnum == 14:
         if bitnum == 14:
-            self.putx([7, ['Reserved bit 0: %d' % can_rx,
-                           'RB0: %d' % can_rx, 'RB0']])
+            self.fd = True if can_rx else False
+            if self.fd:
+                self.putx([7, ['Flexible data format: %d' % can_rx,
+                               'FDF: %d' % can_rx, 'FDF']])
+            else:
+                self.putx([7, ['Reserved bit 0: %d' % can_rx,
+                               'RB0: %d' % can_rx, 'RB0']])
 
 
-            # Bit 12: Remote transmission request (RTR) bit
-            # Data frame: dominant, remote frame: recessive
-            # Remote frames do not contain a data field.
-            rtr = 'remote' if self.bits[12] == 1 else 'data'
-            self.put12([8, ['Remote transmission request: %s frame' % rtr,
-                            'RTR: %s frame' % rtr, 'RTR']])
+            if self.fd:
+                # Bit 12: Substitute remote request (SRR) bit
+                self.put12([8, ['Substitute remote request', 'SRR']])
+                self.dlc_start = 18
+            else:
+                # Bit 12: Remote transmission request (RTR) bit
+                # Data frame: dominant, remote frame: recessive
+                # Remote frames do not contain a data field.
+                rtr = 'remote' if self.bits[12] == 1 else 'data'
+                self.put12([8, ['Remote transmission request: %s frame' % rtr,
+                                'RTR: %s frame' % rtr, 'RTR']])
+                self.dlc_start = 15
+
+        if bitnum == 15 and self.fd:
+            self.putx([7, ['Reserved: %d' % can_rx, 'R0: %d' % can_rx, 'R0']])
+
+        if bitnum == 16 and self.fd:
+            self.putx([7, ['Bit rate switch: %d' % can_rx, 'BRS: %d' % can_rx, 'BRS']])
+
+        if bitnum == 17 and self.fd:
+            self.putx([7, ['Error state indicator: %d' % can_rx, 'ESI: %d' % can_rx, 'ESI']])
 
         # Remember start of DLC (see below).
 
         # Remember start of DLC (see below).
-        elif bitnum == 15:
+        elif bitnum == self.dlc_start:
             self.ss_block = self.samplenum
 
         # Bits 15-18: Data length code (DLC), in number of bytes (0-8).
             self.ss_block = self.samplenum
 
         # Bits 15-18: Data length code (DLC), in number of bytes (0-8).
-        elif bitnum == 18:
-            self.dlc = int(''.join(str(d) for d in self.bits[15:18 + 1]), 2)
+        elif bitnum == self.dlc_start + 3:
+            self.dlc = int(''.join(str(d) for d in self.bits[self.dlc_start:self.dlc_start + 4]), 2)
             self.putb([10, ['Data length code: %d' % self.dlc,
                             'DLC: %d' % self.dlc, 'DLC']])
             self.putb([10, ['Data length code: %d' % self.dlc,
                             'DLC: %d' % self.dlc, 'DLC']])
-            self.last_databit = 18 + (self.dlc * 8)
-            if self.dlc > 8:
+            self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
+            if self.dlc > 8 and not self.fd:
                 self.putb([16, ['Data length code (DLC) > 8 is not allowed']])
 
         # Remember all databyte bits, except the very last one.
                 self.putb([16, ['Data length code (DLC) > 8 is not allowed']])
 
         # Remember all databyte bits, except the very last one.
-        elif bitnum in range(19, self.last_databit):
+        elif bitnum in range(self.dlc_start + 4, self.last_databit):
             self.ss_databytebits.append(self.samplenum)
 
         # Bits 19-X: Data field (0-8 bytes, depending on DLC)
         # The bits within a data byte are transferred MSB-first.
         elif bitnum == self.last_databit:
             self.ss_databytebits.append(self.samplenum) # Last databyte bit.
             self.ss_databytebits.append(self.samplenum)
 
         # Bits 19-X: Data field (0-8 bytes, depending on DLC)
         # The bits within a data byte are transferred MSB-first.
         elif bitnum == self.last_databit:
             self.ss_databytebits.append(self.samplenum) # Last databyte bit.
-            for i in range(self.dlc):
-                x = 18 + (8 * i) + 1
+            for i in range(dlc2len(self.dlc)):
+                x = self.dlc_start + 4 + (8 * i)
                 b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
                 ss = self.ss_databytebits[i * 8]
                 es = self.ss_databytebits[((i + 1) * 8) - 1]
                 b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
                 ss = self.ss_databytebits[i * 8]
                 es = self.ss_databytebits[((i + 1) * 8) - 1]
@@ -260,6 +322,8 @@ class Decoder(srd.Decoder):
         # Remember start of EID (see below).
         if bitnum == 14:
             self.ss_block = self.samplenum
         # Remember start of EID (see below).
         if bitnum == 14:
             self.ss_block = self.samplenum
+            self.fd = False
+            self.dlc_start = 35
 
         # Bits 14-31: Extended identifier (EID[17..0])
         elif bitnum == 31:
 
         # Bits 14-31: Extended identifier (EID[17..0])
         elif bitnum == 31:
@@ -280,42 +344,64 @@ class Decoder(srd.Decoder):
         # Bit 32: Remote transmission request (RTR) bit
         # Data frame: dominant, remote frame: recessive
         # Remote frames do not contain a data field.
         # Bit 32: Remote transmission request (RTR) bit
         # Data frame: dominant, remote frame: recessive
         # Remote frames do not contain a data field.
+
+        # Remember start of RTR (see below).
         if bitnum == 32:
         if bitnum == 32:
-            rtr = 'remote' if can_rx == 1 else 'data'
-            self.putx([8, ['Remote transmission request: %s frame' % rtr,
-                           'RTR: %s frame' % rtr, 'RTR']])
+            self.ss_bit32 = self.samplenum
+            self.rtr = can_rx
+
+            if not self.fd:
+               rtr = 'remote' if can_rx == 1 else 'data'
+               self.putx([8, ['Remote transmission request: %s frame' % rtr,
+                              'RTR: %s frame' % rtr, 'RTR']])
 
         # Bit 33: RB1 (reserved bit)
         elif bitnum == 33:
 
         # Bit 33: RB1 (reserved bit)
         elif bitnum == 33:
-            self.putx([7, ['Reserved bit 1: %d' % can_rx,
-                           'RB1: %d' % can_rx, 'RB1']])
+            self.fd = True if can_rx else False
+            if self.fd:
+                self.dlc_start = 37
+                self.putx([7, ['Flexible data format: %d' % can_rx,
+                               'FDF: %d' % can_rx, 'FDF']])
+                self.put32([7, ['Reserved bit 1: %d' % self.rtr,
+                                'RB1: %d' % self.rtr, 'RB1']])
+            else:
+                self.putx([7, ['Reserved bit 1: %d' % can_rx,
+                               'RB1: %d' % can_rx, 'RB1']])
 
         # Bit 34: RB0 (reserved bit)
         elif bitnum == 34:
             self.putx([7, ['Reserved bit 0: %d' % can_rx,
                            'RB0: %d' % can_rx, 'RB0']])
 
 
         # Bit 34: RB0 (reserved bit)
         elif bitnum == 34:
             self.putx([7, ['Reserved bit 0: %d' % can_rx,
                            'RB0: %d' % can_rx, 'RB0']])
 
+        elif bitnum == 35 and self.fd:
+            self.putx([7, ['Bit rate switch: %d' % can_rx,
+                           'BRS: %d' % can_rx, 'BRS']])
+
+        elif bitnum == 36 and self.fd:
+            self.putx([7, ['Error state indicator: %d' % can_rx,
+                           'ESI: %d' % can_rx, 'ESI']])
+
         # Remember start of DLC (see below).
         # Remember start of DLC (see below).
-        elif bitnum == 35:
+        elif bitnum == self.dlc_start:
             self.ss_block = self.samplenum
 
         # Bits 35-38: Data length code (DLC), in number of bytes (0-8).
             self.ss_block = self.samplenum
 
         # Bits 35-38: Data length code (DLC), in number of bytes (0-8).
-        elif bitnum == 38:
-            self.dlc = int(''.join(str(d) for d in self.bits[35:38 + 1]), 2)
+        elif bitnum == self.dlc_start + 3:
+            self.dlc = int(''.join(str(d) for d in self.bits[self.dlc_start:self.dlc_start + 4]), 2)
             self.putb([10, ['Data length code: %d' % self.dlc,
                             'DLC: %d' % self.dlc, 'DLC']])
             self.putb([10, ['Data length code: %d' % self.dlc,
                             'DLC: %d' % self.dlc, 'DLC']])
-            self.last_databit = 38 + (self.dlc * 8)
+            self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
 
         # Remember all databyte bits, except the very last one.
 
         # Remember all databyte bits, except the very last one.
-        elif bitnum in range(39, self.last_databit):
+        elif bitnum in range(self.dlc_start + 4, self.last_databit):
             self.ss_databytebits.append(self.samplenum)
 
         # Bits 39-X: Data field (0-8 bytes, depending on DLC)
         # The bits within a data byte are transferred MSB-first.
         elif bitnum == self.last_databit:
             self.ss_databytebits.append(self.samplenum) # Last databyte bit.
             self.ss_databytebits.append(self.samplenum)
 
         # Bits 39-X: Data field (0-8 bytes, depending on DLC)
         # The bits within a data byte are transferred MSB-first.
         elif bitnum == self.last_databit:
             self.ss_databytebits.append(self.samplenum) # Last databyte bit.
-            for i in range(self.dlc):
-                x = 38 + (8 * i) + 1
+            for i in range(dlc2len(self.dlc)):
+                x = self.dlc_start + 4 + (8 * i)
                 b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
                 ss = self.ss_databytebits[i * 8]
                 es = self.ss_databytebits[((i + 1) * 8) - 1]
                 b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
                 ss = self.ss_databytebits[i * 8]
                 es = self.ss_databytebits[((i + 1) * 8) - 1]
@@ -335,6 +421,12 @@ class Decoder(srd.Decoder):
         # Get the index of the current CAN frame bit (without stuff bits).
         bitnum = len(self.bits) - 1
 
         # Get the index of the current CAN frame bit (without stuff bits).
         bitnum = len(self.bits) - 1
 
+        if self.fd and can_rx:
+            if bitnum == 16 and self.frame_type == 'standard' \
+                    or bitnum == 35 and self.frame_type == 'extended':
+                self.dom_edge_seen(force=True)
+                self.set_fast_bitrate()
+
         # If this is a stuff bit, remove it from self.bits and ignore it.
         if self.is_stuff_bit():
             self.putx([15, [str(can_rx)]])
         # If this is a stuff bit, remove it from self.bits and ignore it.
         if self.is_stuff_bit():
             self.putx([15, [str(can_rx)]])
diff --git a/decoders/cc1101/__init__.py b/decoders/cc1101/__init__.py
new file mode 100644 (file)
index 0000000..68fc798
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
+by the Texas Instruments low-power sub-1GHz RF transceiver chips.
+
+Details:
+http://www.ti.com/lit/ds/symlink/cc1101.pdf
+'''
+
+from .pd import Decoder
diff --git a/decoders/cc1101/lists.py b/decoders/cc1101/lists.py
new file mode 100644 (file)
index 0000000..dfb9b07
--- /dev/null
@@ -0,0 +1,115 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+regs = {
+#   addr: 'name'
+    0x00: 'IOCFG2',
+    0x01: 'IOCFG1',
+    0x02: 'IOCFG0',
+    0x03: 'FIFOTHR',
+    0x04: 'SYNC1',
+    0x05: 'SYNC0',
+    0x06: 'PKTLEN',
+    0x07: 'PKTCTRL1',
+    0x08: 'PKTCTRL0',
+    0x09: 'ADDR',
+    0x0A: 'CHANNR',
+    0x0B: 'FSCTRL1',
+    0x0C: 'FSCTRL0',
+    0x0D: 'FREQ2',
+    0x0E: 'FREQ1',
+    0x0F: 'FREQ0',
+    0x10: 'MDMCFG4',
+    0x11: 'MDMCFG3',
+    0x12: 'MDMCFG2',
+    0x13: 'MDMCFG1',
+    0x14: 'MDMCFG0',
+    0x15: 'DEVIATN',
+    0x16: 'MCSM2',
+    0x17: 'MCSM1',
+    0x18: 'MCSM0',
+    0x19: 'FOCCFG',
+    0x1A: 'BSCFG',
+    0x1B: 'AGCTRL2',
+    0x1C: 'AGCTRL1',
+    0x1D: 'AGCTRL0',
+    0x1E: 'WOREVT1',
+    0x1F: 'WOREVT0',
+    0x20: 'WORCTRL',
+    0x21: 'FREND1',
+    0x22: 'FREND0',
+    0x23: 'FSCAL3',
+    0x24: 'FSCAL2',
+    0x25: 'FSCAL1',
+    0x26: 'FSCAL0',
+    0x27: 'RCCTRL1',
+    0x28: 'RCCTRL0',
+    0x29: 'FSTEST',
+    0x2A: 'PTEST',
+    0x2B: 'AGCTEST',
+    0x2C: 'TEST2',
+    0x2D: 'TEST1',
+    0x2E: 'TEST0',
+    0x30: 'PARTNUM',
+    0x31: 'VERSION',
+    0x32: 'FREQEST',
+    0x33: 'LQI',
+    0x34: 'RSSI',
+    0x35: 'MARCSTATE',
+    0x36: 'WORTIME1',
+    0x37: 'WORTIME0',
+    0x38: 'PKTSTATUS',
+    0x39: 'VCO_VC_DAC',
+    0x3A: 'TXBYTES',
+    0x3B: 'RXBYTES',
+    0x3C: 'RCCTRL1_STATUS',
+    0x3D: 'RCCTRL0_STATUS',
+    0x3E: 'PATABLE',
+    0x3F: 'FIFO'
+}
+
+strobes = {
+#   addr: 'name'
+    0x30: 'SRES',
+    0x31: 'SFSTXON',
+    0x32: 'SXOFF',
+    0x33: 'SCAL',
+    0x34: 'SRX',
+    0x35: 'STX',
+    0x36: 'SIDLE',
+    0x37: '',
+    0x38: 'SWOR',
+    0x39: 'SPWD',
+    0x3A: 'SFRX',
+    0x3B: 'SFTX',
+    0x3C: 'SWORRST',
+    0x3D: 'SNOP'
+}
+
+status_reg_states = {
+#   value: 'state name'
+    0b000: 'IDLE',
+    0b001: 'RX',
+    0b010: 'TX',
+    0b011: 'FSTXON',
+    0b100: 'CALIBRATE',
+    0b101: 'SETTLING',
+    0b110: 'RXFIFO_OVERFLOW',
+    0b111: 'TXFIFO_OVERFLOW'
+}
diff --git a/decoders/cc1101/pd.py b/decoders/cc1101/pd.py
new file mode 100644 (file)
index 0000000..156d4ce
--- /dev/null
@@ -0,0 +1,293 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from collections import namedtuple
+from .lists import *
+
+ANN_STROBE, ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ, \
+    ANN_BURST_WRITE, ANN_STATUS_READ, ANN_STATUS, ANN_WARN = range(8)
+
+Pos = namedtuple('Pos', ['ss', 'es'])
+Data = namedtuple('Data', ['mosi', 'miso'])
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'cc1101'
+    name = 'CC1101'
+    longname = 'Texas Instruments CC1101'
+    desc = 'Low-power sub-1GHz RF transceiver chip.'
+    license = 'gplv2+'
+    inputs = ['spi']
+    outputs = []
+    tags = ['IC', 'Wireless/RF']
+    annotations = (
+        ('strobe', 'Command strobe'),
+        ('single_read', 'Single register read'),
+        ('single_write', 'Single register write'),
+        ('burst_read', 'Burst register read'),
+        ('burst_write', 'Burst register write'),
+        ('status', 'Status register'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('cmd', 'Commands', (ANN_STROBE,)),
+        ('data', 'Data', (ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ,
+                            ANN_BURST_WRITE, ANN_STATUS_READ)),
+        ('status', 'Status register', (ANN_STATUS,)),
+        ('warnings', 'Warnings', (ANN_WARN,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.next()
+        self.requirements_met = True
+        self.cs_was_released = False
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def warn(self, pos, msg):
+        '''Put a warning message 'msg' at 'pos'.'''
+        self.put(pos.ss, pos.es, self.out_ann, [ANN_WARN, [msg]])
+
+    def putp(self, pos, ann, msg):
+        '''Put an annotation message 'msg' at 'pos'.'''
+        self.put(pos.ss, pos.es, self.out_ann, [ann, [msg]])
+
+    def putp2(self, pos, ann, msg1, msg2):
+        '''Put an annotation message 'msg' at 'pos'.'''
+        self.put(pos.ss, pos.es, self.out_ann, [ann, [msg1, msg2]])
+
+    def next(self):
+        '''Resets the decoder after a complete command was decoded.'''
+        # 'True' for the first byte after CS# went low.
+        self.first = True
+
+        # The current command, and the minimum and maximum number
+        # of data bytes to follow.
+        self.cmd = None
+        self.min = 0
+        self.max = 0
+
+        # Used to collect the bytes after the command byte
+        # (and the start/end sample number).
+        self.mb = []
+        self.ss_mb = -1
+        self.es_mb = -1
+
+    def mosi_bytes(self):
+        '''Returns the collected MOSI bytes of a multi byte command.'''
+        return [b.mosi for b in self.mb]
+
+    def miso_bytes(self):
+        '''Returns the collected MISO bytes of a multi byte command.'''
+        return [b.miso for b in self.mb]
+
+    def decode_command(self, pos, b):
+        '''Decodes the command byte 'b' at position 'pos' and prepares
+        the decoding of the following data bytes.'''
+        c = self.parse_command(b)
+        if c is None:
+            self.warn(pos, 'unknown command')
+            return
+
+        self.cmd, self.dat, self.min, self.max = c
+
+        if self.cmd == 'Strobe':
+            self.putp(pos, ANN_STROBE, self.format_command())
+        else:
+            # Don't output anything now, the command is merged with
+            # the data bytes following it.
+            self.ss_mb = pos.ss
+
+    def format_command(self):
+        '''Returns the label for the current command.'''
+        if self.cmd in ('Read', 'Burst read', 'Write', 'Burst write', 'Status read'):
+            return self.cmd
+        if self.cmd == 'Strobe':
+            reg = strobes.get(self.dat, 'unknown strobe')
+            return '{} {}'.format(self.cmd, reg)
+        else:
+            return 'TODO Cmd {}'.format(self.cmd)
+
+    def parse_command(self, b):
+        '''Parses the command byte.
+
+        Returns a tuple consisting of:
+        - the name of the command
+        - additional data needed to dissect the following bytes
+        - minimum number of following bytes
+        - maximum number of following bytes (None for infinite)
+        '''
+
+        addr = b & 0x3F
+        if (addr < 0x30) or (addr == 0x3E) or (addr == 0x3F):
+            if (b & 0xC0) == 0x00:
+                return ('Write', addr, 1, 1)
+            if (b & 0xC0) == 0x40:
+                return ('Burst write', addr, 1, 99999)
+            if (b & 0xC0) == 0x80:
+                return ('Read', addr, 1, 1)
+            if (b & 0xC0) == 0xC0:
+                return ('Burst read', addr, 1, 99999)
+            else:
+                self.warn(pos, 'unknown address/command combination')
+        else:
+            if (b & 0x40) == 0x00:
+                return ('Strobe', addr, 0, 0)
+            if (b & 0xC0) == 0xC0:
+                return ('Status read', addr, 1, 99999)
+            else:
+                self.warn(pos, 'unknown address/command combination')
+
+    def decode_reg(self, pos, ann, regid, data):
+        '''Decodes a register.
+
+        pos   -- start and end sample numbers of the register
+        ann   -- the annotation number that is used to output the register.
+        regid -- may be either an integer used as a key for the 'regs'
+                 dictionary, or a string directly containing a register name.'
+        data  -- the register content.
+        '''
+
+        if type(regid) == int:
+            # Get the name of the register.
+            if regid not in regs:
+                self.warn(pos, 'unknown register')
+                return
+            name = '{} ({:02X})'.format(regs[regid], regid)
+        else:
+            name = regid
+
+        if regid == 'STATUS' and ann == ANN_STATUS:
+            label = 'Status'
+            self.decode_status_reg(pos, ann, data, label)
+        else:
+            if self.cmd in ('Write', 'Read', 'Status read', 'Burst read', 'Burst write'):
+                label = '{}: {}'.format(self.format_command(), name)
+            else:
+                label = 'Reg ({}) {}'.format(self.cmd, name)
+            self.decode_mb_data(pos, ann, data, label)
+
+    def decode_status_reg(self, pos, ann, data, label):
+        '''Decodes the data bytes 'data' of a status register at position
+        'pos'. The decoded data is prefixed with 'label'.'''
+        status = data[0]
+        # bit 7 --> CHIP_RDYn
+        if status & 0b10000000 == 0b10000000:
+            longtext_chiprdy = 'CHIP_RDYn is high! '
+        else:
+            longtext_chiprdy = ''
+        # bits 6:4 --> STATE
+        state = (status & 0x70) >> 4
+        longtext_state = 'STATE is {}, '.format(status_reg_states[state])
+        # bits 3:0 --> FIFO_BYTES_AVAILABLE
+        fifo_bytes = status & 0x0F
+        if self.cmd in ('Single read', 'Status read', 'Burst read'):
+            longtext_fifo = '{} bytes available in RX FIFO'.format(fifo_bytes)
+        else:
+            longtext_fifo = '{} bytes free in TX FIFO'.format(fifo_bytes)
+
+        text = '{} = {:02X}'.format(label, status)
+        longtext = ''.join([text, '; ', longtext_chiprdy, longtext_state, longtext_fifo])
+        self.putp2(pos, ann, longtext, text)
+
+    def decode_mb_data(self, pos, ann, data, label):
+        '''Decodes the data bytes 'data' of a multibyte command at position
+        'pos'. The decoded data is prefixed with 'label'.'''
+
+        def escape(b):
+            return '{:02X}'.format(b)
+
+        data = ' '.join([escape(b) for b in data])
+        text = '{} = {}'.format(label, data)
+        self.putp(pos, ann, text)
+
+    def finish_command(self, pos):
+        '''Decodes the remaining data bytes at position 'pos'.'''
+
+        if self.cmd == 'Write':
+            self.decode_reg(pos, ANN_SINGLE_WRITE, self.dat, self.mosi_bytes())
+        elif self.cmd == 'Burst write':
+            self.decode_reg(pos, ANN_BURST_WRITE, self.dat, self.mosi_bytes())
+        elif self.cmd == 'Read':
+            self.decode_reg(pos, ANN_SINGLE_READ, self.dat, self.miso_bytes())
+        elif self.cmd == 'Burst read':
+            self.decode_reg(pos, ANN_BURST_READ, self.dat, self.miso_bytes())
+        elif self.cmd == 'Strobe':
+            self.decode_reg(pos, ANN_STROBE, self.dat, self.mosi_bytes())
+        elif self.cmd == 'Status read':
+            self.decode_reg(pos, ANN_STATUS_READ, self.dat, self.miso_bytes())
+        else:
+            self.warn(pos, 'unhandled command')
+
+    def decode(self, ss, es, data):
+        if not self.requirements_met:
+            return
+
+        ptype, data1, data2 = data
+
+        if ptype == 'CS-CHANGE':
+            if data1 is None:
+                if data2 is None:
+                    self.requirements_met = False
+                    raise ChannelError('CS# pin required.')
+                elif data2 == 1:
+                    self.cs_was_released = True
+
+            if data1 == 0 and data2 == 1:
+                # Rising edge, the complete command is transmitted, process
+                # the bytes that were sent after the command byte.
+                if self.cmd:
+                    # Check if we got the minimum number of data bytes
+                    # after the command byte.
+                    if len(self.mb) < self.min:
+                        self.warn((ss, ss), 'missing data bytes')
+                    elif self.mb:
+                        self.finish_command(Pos(self.ss_mb, self.es_mb))
+
+                self.next()
+                self.cs_was_released = True
+
+        elif ptype == 'DATA' and self.cs_was_released:
+            mosi, miso = data1, data2
+            pos = Pos(ss, es)
+
+            if miso is None or mosi is None:
+                self.requirements_met = False
+                raise ChannelError('Both MISO and MOSI pins required.')
+
+            if self.first:
+                self.first = False
+                # First MOSI byte is always the command.
+                self.decode_command(pos, mosi)
+                # First MISO byte is always the status register.
+                self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+            else:
+                if not self.cmd or len(self.mb) >= self.max:
+                    self.warn(pos, 'excess byte')
+                else:
+                    # Collect the bytes after the command byte.
+                    if self.ss_mb == -1:
+                        self.ss_mb = ss
+                    self.es_mb = es
+                    self.mb.append(Data(mosi, miso))
index db288ab61307edb2ce32b3d233900792c1061e88..4138b62b36bd2f48a125d09f09d38a20343bb10e 100644 (file)
@@ -18,7 +18,7 @@
 ##
 
 '''
 ##
 
 '''
-Consumer Electronics Control (CEC) protocol allows users to command and
+The Consumer Electronics Control (CEC) protocol allows users to command and
 control devices connected through HDMI.
 '''
 
 control devices connected through HDMI.
 '''
 
index 6e8ddecf44d83981a0a8395d712d0c91d7dadd4a..b12633e75d224a14ae9909f19fdb3af9c5de9edc 100644 (file)
@@ -55,7 +55,8 @@ class Decoder(srd.Decoder):
     desc = 'HDMI Consumer Electronics Control (CEC) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'HDMI Consumer Electronics Control (CEC) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['cec']
+    outputs = []
+    tags = ['Display', 'PC']
     channels = (
         {'id': 'cec', 'name': 'CEC', 'desc': 'CEC bus data'},
     )
     channels = (
         {'id': 'cec', 'name': 'CEC', 'desc': 'CEC bus data'},
     )
@@ -116,41 +117,41 @@ class Decoder(srd.Decoder):
             return
 
         i = 0
             return
 
         i = 0
-        str = ''
+        string = ''
         while i < len(self.cmd_bytes):
         while i < len(self.cmd_bytes):
-            str += '{:02x}'.format(self.cmd_bytes[i]['val'])
+            string += '{:02x}'.format(self.cmd_bytes[i]['val'])
             if i != (len(self.cmd_bytes) - 1):
             if i != (len(self.cmd_bytes) - 1):
-                str += ':'
+                string += ':'
             i += 1
 
             i += 1
 
-        self.put(self.frame_start, self.frame_end, self.out_ann, [7, [str]])
+        self.put(self.frame_start, self.frame_end, self.out_ann, [7, [string]])
 
         i = 0
         operands = 0
 
         i = 0
         operands = 0
-        str = ''
+        string = ''
         while i < len(self.cmd_bytes):
             if i == 0: # Parse header
                 (src, dst) = decode_header(self.cmd_bytes[i]['val'])
         while i < len(self.cmd_bytes):
             if i == 0: # Parse header
                 (src, dst) = decode_header(self.cmd_bytes[i]['val'])
-                str = 'HDR: ' + src + ', ' + dst
+                string = 'HDR: ' + src + ', ' + dst
             elif i == 1: # Parse opcode
             elif i == 1: # Parse opcode
-                str += ' | OPC: ' + opcodes.get(self.cmd_bytes[i]['val'], 'Invalid')
+                string += ' | OPC: ' + opcodes.get(self.cmd_bytes[i]['val'], 'Invalid')
             else: # Parse operands
                 if operands == 0:
             else: # Parse operands
                 if operands == 0:
-                    str += ' | OPS: '
+                    string += ' | OPS: '
                 operands += 1
                 operands += 1
-                str += '0x{:02x}'.format(self.cmd_bytes[i]['val'])
+                string += '0x{:02x}'.format(self.cmd_bytes[i]['val'])
                 if i != len(self.cmd_bytes) - 1:
                 if i != len(self.cmd_bytes) - 1:
-                    str += ', '
+                    string += ', '
             i += 1
 
         # Header only commands are PINGS
         if i == 1:
             i += 1
 
         # Header only commands are PINGS
         if i == 1:
-            str += ' | OPC: PING' if self.eom else ' | OPC: NONE. Aborted cmd'
+            string += ' | OPC: PING' if self.eom else ' | OPC: NONE. Aborted cmd'
 
         # Add extra information (ack of the command from the destination)
 
         # Add extra information (ack of the command from the destination)
-        str += ' | R: NACK' if is_nack else ' | R: ACK'
+        string += ' | R: NACK' if is_nack else ' | R: ACK'
 
 
-        self.put(self.frame_start, self.frame_end, self.out_ann, [8, [str]])
+        self.put(self.frame_start, self.frame_end, self.out_ann, [8, [string]])
 
     def process(self):
         zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0
 
     def process(self):
         zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0
index 833ac7f5e9f4fe2a5b4451bfb343eb5254a89488..78c3b6f537d7ab5b4c1fa4cec5ff96a3eefeb1a3 100644 (file)
@@ -101,15 +101,15 @@ opcodes = {
     0x9A: 'SET_AUDIO_RATE',
 }
 
     0x9A: 'SET_AUDIO_RATE',
 }
 
-def resolve_logical_address(id, is_initiator):
-    if id < 0 or id > 0x0F:
+def resolve_logical_address(id_, is_initiator):
+    if id_ < 0 or id_ > 0x0F:
         return 'Invalid'
 
     # Special handling of 0x0F.
         return 'Invalid'
 
     # Special handling of 0x0F.
-    if id == 0x0F:
+    if id_ == 0x0F:
         return 'Unregistered' if is_initiator else 'Broadcast'
 
         return 'Unregistered' if is_initiator else 'Broadcast'
 
-    return logical_adresses[id]
+    return logical_adresses[id_]
 
 def decode_header(header):
     src = (header & 0xF0) >> 4
 
 def decode_header(header):
     src = (header & 0xF0) >> 4
index 77e61a9952a6e425fc404223ba668d91cd2c04d6..9638ba19072ad6a33c04551decab090836e9c240 100644 (file)
@@ -57,7 +57,8 @@ class Decoder(srd.Decoder):
     desc = '100 Gigabit C form-factor pluggable (CFP) protocol.'
     license = 'BSD'
     inputs = ['mdio']
     desc = '100 Gigabit C form-factor pluggable (CFP) protocol.'
     license = 'BSD'
     inputs = ['mdio']
-    outputs = ['cfp']
+    outputs = []
+    tags = ['Networking']
     annotations = (
         ('register', 'Register'),
         ('decode', 'Decode'),
     annotations = (
         ('register', 'Register'),
         ('decode', 'Decode'),
index e731311213e85674c48a0c56a8f93c233e9fb7c2..505148ddf037fcf2d8114aa6c6e20d6ba39633ad 100644 (file)
 ##
 
 '''
 ##
 
 '''
-This PD is a simple counter.
+This decoder is a simple edge counter.
 
 It can count rising and/or falling edges, provides an optional reset
 
 It can count rising and/or falling edges, provides an optional reset
-signal. It can also divide the count to e.g. count the numger of
-fixed length words (where a word corresponds to e.g. 9 clock edges).
+signal. It can also divide the count to e.g. count the number of
+fixed-length words (where a word corresponds to e.g. 9 clock edges).
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 9ed30c813e711b31e4794592ee39a243bdc718a0..f1134bd14f61cb3b6bba3049608992baf249bc89 100644 (file)
@@ -27,10 +27,11 @@ class Decoder(srd.Decoder):
     id = 'counter'
     name = 'Counter'
     longname = 'Edge counter'
     id = 'counter'
     name = 'Counter'
     longname = 'Edge counter'
-    desc = 'Count number of edges.'
+    desc = 'Count the number of edges in a signal.'
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
+    tags = ['Util']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index bf842ad403586418b03eacd920a4337bce3f2e64..8be494af52759d9bd1c04ab0f35b23b1d2bba881 100644 (file)
@@ -28,10 +28,11 @@ class Decoder(srd.Decoder):
     id = 'dali'
     name = 'DALI'
     longname = 'Digital Addressable Lighting Interface'
     id = 'dali'
     name = 'DALI'
     longname = 'Digital Addressable Lighting Interface'
-    desc = 'DALI lighting control protocol.'
+    desc = 'Digital Addressable Lighting Interface (DALI) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['dali']
+    outputs = []
+    tags = ['Embedded/industrial', 'Lighting']
     channels = (
         {'id': 'dali', 'name': 'DALI', 'desc': 'DALI data line'},
     )
     channels = (
         {'id': 'dali', 'name': 'DALI', 'desc': 'DALI data line'},
     )
@@ -60,7 +61,6 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
 
     def reset(self):
         self.samplerate = None
-        self.samplenum = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
         self.dev_type = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
         self.dev_type = None
index 7b09ce6ab9968f15de09ace7823df0a1e0c4ccd8..7365134efc0b82bfea5ba7ed84bd6e99f7eb8376 100644 (file)
@@ -32,7 +32,8 @@ class Decoder(srd.Decoder):
     desc = 'European longwave time signal (77.5kHz carrier signal).'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'European longwave time signal (77.5kHz carrier signal).'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['dcf77']
+    outputs = []
+    tags = ['Clock/timing']
     channels = (
         {'id': 'data', 'name': 'DATA', 'desc': 'DATA line'},
     )
     channels = (
         {'id': 'data', 'name': 'DATA', 'desc': 'DATA line'},
     )
index 1bcca20ff67cd108882fbf528b3fa4628655ad02..3fd2aba0d5668be7f76c43f52c6681cd89782416 100644 (file)
@@ -24,10 +24,11 @@ class Decoder(srd.Decoder):
     id = 'dmx512'
     name = 'DMX512'
     longname = 'Digital MultipleX 512'
     id = 'dmx512'
     name = 'DMX512'
     longname = 'Digital MultipleX 512'
-    desc = 'Professional lighting control protocol.'
+    desc = 'Digital MultipleX 512 (DMX512) lighting protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['dmx512']
+    outputs = []
+    tags = ['Embedded/industrial', 'Lighting']
     channels = (
         {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'},
     )
     channels = (
         {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'},
     )
@@ -45,9 +46,9 @@ class Decoder(srd.Decoder):
         ('error', 'Error'),
     )
     annotation_rows = (
         ('error', 'Error'),
     )
     annotation_rows = (
-        ('name', 'Logical', (1, 2, 5, 6, 7, 8)),
-        ('data', 'Data', (9,)),
         ('bits', 'Bits', (0, 3, 4)),
         ('bits', 'Bits', (0, 3, 4)),
+        ('data', 'Data', (9,)),
+        ('name', 'Logical', (1, 2, 5, 6, 7, 8)),
         ('errors', 'Errors', (10,)),
     )
 
         ('errors', 'Errors', (10,)),
     )
 
index 414da6531c31998977dab755af66dd3ce421257e..f8ebe195ec5008562745e4fb93a1cfaad88feb08 100644 (file)
@@ -39,9 +39,9 @@ bits = (
 
 rates = {
     0b00: '1Hz',
 
 rates = {
     0b00: '1Hz',
-    0b01: '4096kHz',
-    0b10: '8192kHz',
-    0b11: '32768kHz',
+    0b01: '4096Hz',
+    0b10: '8192Hz',
+    0b11: '32768Hz',
 }
 
 DS1307_I2C_ADDRESS = 0x68
 }
 
 DS1307_I2C_ADDRESS = 0x68
@@ -56,10 +56,11 @@ class Decoder(srd.Decoder):
     id = 'ds1307'
     name = 'DS1307'
     longname = 'Dallas DS1307'
     id = 'ds1307'
     name = 'DS1307'
     longname = 'Dallas DS1307'
-    desc = 'Realtime clock module protocol.'
+    desc = 'Dallas DS1307 realtime clock module protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['ds1307']
+    outputs = []
+    tags = ['Clock/timing', 'IC']
     annotations =  regs_and_bits() + (
         ('read-datetime', 'Read date/time'),
         ('write-datetime', 'Write date/time'),
     annotations =  regs_and_bits() + (
         ('read-datetime', 'Read date/time'),
         ('write-datetime', 'Write date/time'),
@@ -121,7 +122,7 @@ class Decoder(srd.Decoder):
         ampm_mode = True if (b & (1 << 6)) else False
         if ampm_mode:
             self.putd(6, 6, [13, ['12-hour mode', '12h mode', '12h']])
         ampm_mode = True if (b & (1 << 6)) else False
         if ampm_mode:
             self.putd(6, 6, [13, ['12-hour mode', '12h mode', '12h']])
-            a = 'AM' if (b & (1 << 6)) else 'PM'
+            a = 'PM' if (b & (1 << 5)) else 'AM'
             self.putd(5, 5, [14, [a, a[0]]])
             h = self.hours = bcd2int(b & 0x1f)
             self.putd(4, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
             self.putd(5, 5, [14, [a, a[0]]])
             h = self.hours = bcd2int(b & 0x1f)
             self.putd(4, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
diff --git a/decoders/ds2408/__init__.py b/decoders/ds2408/__init__.py
new file mode 100644 (file)
index 0000000..b196ce9
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'onewire_network' PD and decodes the
+Maxim DS2408 1-Wire 8-channel addressable switch protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/ds2408/pd.py b/decoders/ds2408/pd.py
new file mode 100644 (file)
index 0000000..33f2873
--- /dev/null
@@ -0,0 +1,129 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Mariusz Bialonczyk <manio@skyboo.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+# Dictionary of FUNCTION commands and their names.
+command = {
+    0xf0: 'Read PIO Registers',
+    0xf5: 'Channel Access Read',
+    0x5a: 'Channel Access Write',
+    0xcc: 'Write Conditional Search Register',
+    0xc3: 'Reset Activity Latches',
+    0x3c: 'Disable Test Mode',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'ds2408'
+    name = 'DS2408'
+    longname = 'Maxim DS2408'
+    desc = '1-Wire 8-channel addressable switch.'
+    license = 'gplv2+'
+    inputs = ['onewire_network']
+    outputs = []
+    tags = ['Embedded/industrial', 'IC']
+    annotations = (
+        ('text', 'Human-readable text'),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        # Bytes for function command.
+        self.bytes = []
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def decode(self, ss, es, data):
+        code, val = data
+
+        if code == 'RESET/PRESENCE':
+            self.ss, self.es = ss, es
+            self.putx([0, ['Reset/presence: %s'
+                           % ('true' if val else 'false')]])
+            self.bytes = []
+        elif code == 'ROM':
+            self.ss, self.es = ss, es
+            family_code = val & 0xff
+            self.putx([0, ['ROM: 0x%016x (family code 0x%02x)' % (val, family_code)]])
+            self.bytes = []
+        elif code == 'DATA':
+            self.bytes.append(val)
+            if 1 == len(self.bytes):
+                self.ss, self.es = ss, es
+                if val not in command:
+                    self.putx([0, ['Unrecognized command: 0x%02x' % val]])
+                else:
+                    self.putx([0, ['%s (0x%02x)' % (command[val], val)]])
+            elif 0xf0 == self.bytes[0]: # Read PIO Registers
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Target address: 0x%04x'
+                                   % ((self.bytes[2] << 8) + self.bytes[1])]])
+                elif 3 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    self.putx([0, ['Data: 0x%02x' % self.bytes[-1]]])
+            elif 0xf5 == self.bytes[0]: # Channel Access Read
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 2 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    self.putx([0, ['PIO sample: 0x%02x' % self.bytes[-1]]])
+            elif 0x5a == self.bytes[0]: # Channel Access Write
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    if (self.bytes[-1] == (self.bytes[-2] ^ 0xff)):
+                      self.putx([0, ['Data: 0x%02x (bit-inversion correct: 0x%02x)' % (self.bytes[-2], self.bytes[-1])]])
+                    else:
+                      self.putx([0, ['Data error: second byte (0x%02x) is not bit-inverse of first (0x%02x)' % (self.bytes[-1], self.bytes[-2])]])
+                elif 3 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    if 0xaa == self.bytes[-1]:
+                      self.putx([0, ['Success']])
+                    elif 0xff == self.bytes[-1]:
+                      self.putx([0, ['Fail New State']])
+            elif 0xcc == self.bytes[0]: # Write Conditional Search Register
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Target address: 0x%04x'
+                                   % ((self.bytes[2] << 8) + self.bytes[1])]])
+                elif 3 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    self.putx([0, ['Data: 0x%02x' % self.bytes[-1]]])
+            elif 0xc3 == self.bytes[0]: # Reset Activity Latches
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 2 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    if 0xaa == self.bytes[-1]:
+                      self.putx([0, ['Success']])
+                    else:
+                      self.putx([0, ['Invalid byte']])
index c7869a8d9c4af3ab2b39dcd4d4edc50b4ccdce58..7f9f6660cc86ec2293c866b4ecf04d50e05a2d12 100644 (file)
@@ -64,11 +64,12 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'ds243x'
     name = 'DS243x'
     api_version = 3
     id = 'ds243x'
     name = 'DS243x'
-    longname = 'Maxim DS2432/2433'
+    longname = 'Maxim DS2432/3'
     desc = 'Maxim DS243x series 1-Wire EEPROM protocol.'
     license = 'gplv2+'
     inputs = ['onewire_network']
     desc = 'Maxim DS243x series 1-Wire EEPROM protocol.'
     license = 'gplv2+'
     inputs = ['onewire_network']
-    outputs = ['ds243x']
+    outputs = []
+    tags = ['IC', 'Memory']
     annotations = (
         ('text', 'Human-readable text'),
     )
     annotations = (
         ('text', 'Human-readable text'),
     )
@@ -110,7 +111,7 @@ class Decoder(srd.Decoder):
                 self.family, self.commands = family_codes[val & 0xff]
                 s = 'is 0x%02x, %s detected' % (self.family_code, self.family)
             else:
                 self.family, self.commands = family_codes[val & 0xff]
                 s = 'is 0x%02x, %s detected' % (self.family_code, self.family)
             else:
-                s = '%x%02x unknown' % (self.family_code)
+                s = '0x%02x unknown' % (self.family_code)
 
             self.putx([0, ['ROM: 0x%016x (%s)' % (val, 'family code ' + s),
                            'ROM: 0x%016x (%s)' % (val, self.family)]])
 
             self.putx([0, ['ROM: 0x%016x (%s)' % (val, 'family code ' + s),
                            'ROM: 0x%016x (%s)' % (val, self.family)]])
index a792d95956f3533094e50583e548bf8cfeded180..9a578449c6956b7761896d0de17abeb7a42dc2f5 100644 (file)
@@ -42,7 +42,8 @@ class Decoder(srd.Decoder):
     desc = '1-Wire digital thermometer with Sequence Detect and PIO.'
     license = 'gplv2+'
     inputs = ['onewire_network']
     desc = '1-Wire digital thermometer with Sequence Detect and PIO.'
     license = 'gplv2+'
     inputs = ['onewire_network']
-    outputs = ['ds28ea00']
+    outputs = []
+    tags = ['IC', 'Sensor']
     annotations = (
         ('text', 'Human-readable text'),
     )
     annotations = (
         ('text', 'Human-readable text'),
     )
index c5d9bf9c25c34c4f2d2375a577114f288ca6c5e8..c7cab8082c37f94e1e85bca09781572117e41833 100644 (file)
@@ -27,10 +27,11 @@ class Decoder(srd.Decoder):
     id = 'dsi'
     name = 'DSI'
     longname = 'Digital Serial Interface'
     id = 'dsi'
     name = 'DSI'
     longname = 'Digital Serial Interface'
-    desc = 'DSI lighting control protocol.'
+    desc = 'Digital Serial Interface (DSI) lighting protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['dsi']
+    outputs = []
+    tags = ['Embedded/industrial', 'Lighting']
     channels = (
         {'id': 'dsi', 'name': 'DSI', 'desc': 'DSI data line'},
     )
     channels = (
         {'id': 'dsi', 'name': 'DSI', 'desc': 'DSI data line'},
     )
@@ -55,7 +56,6 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
 
     def reset(self):
         self.samplerate = None
-        self.samplenum = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
@@ -111,6 +111,7 @@ class Decoder(srd.Decoder):
             raise SamplerateError('Cannot decode without samplerate.')
         bit = 0
         while True:
             raise SamplerateError('Cannot decode without samplerate.')
         bit = 0
         while True:
+            # TODO: Come up with more appropriate self.wait() conditions.
             (self.dsi,) = self.wait()
             if self.options['polarity'] == 'active-high':
                 self.dsi ^= 1 # Invert.
             (self.dsi,) = self.wait()
             if self.options['polarity'] == 'active-high':
                 self.dsi ^= 1 # Invert.
index f0331cc89e719e662aa86515804c33630e83b0af..256d839db43196f9366318038c5a0b451df333ed 100644 (file)
 ##
 
 '''
 ##
 
 '''
-EDID 1.3 structure decoder.
+Extended Display Identification Data (EDID) 1.3 structure decoder.
 
 The three-character vendor ID as specified in the EDID standard refers to
 a Plug and Play ID (PNPID). The list of PNPID assignments is done by Microsoft.
 
 The three-character vendor ID as specified in the EDID standard refers to
 a Plug and Play ID (PNPID). The list of PNPID assignments is done by Microsoft.
-More information is available on this page:
-
- http://msdn.microsoft.com/en-us/windows/hardware/gg463195
 
 The 'pnpids.txt' file included with this protocol decoder is derived from
 the list of assignments downloadable from that page. It was retrieved in
 January 2012.
 
 
 The 'pnpids.txt' file included with this protocol decoder is derived from
 the list of assignments downloadable from that page. It was retrieved in
 January 2012.
 
-More information on EDID is available here:
-
- https://en.wikipedia.org/wiki/Extended_display_identification_data
+Details:
+https://en.wikipedia.org/wiki/Extended_display_identification_data
+http://msdn.microsoft.com/en-us/windows/hardware/gg463195
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 034c314f4d28941e205f64a74110415da046dafa..e2e7feef7f23f1930c3223924094e20f755b1f58 100644 (file)
@@ -80,14 +80,15 @@ class Decoder(srd.Decoder):
     desc = 'Data structure describing display device capabilities.'
     license = 'gplv3+'
     inputs = ['i2c']
     desc = 'Data structure describing display device capabilities.'
     license = 'gplv3+'
     inputs = ['i2c']
-    outputs = ['edid']
+    outputs = []
+    tags = ['Display', 'Memory', 'PC']
     annotations = (
         ('fields', 'EDID structure fields'),
         ('sections', 'EDID structure sections'),
     )
     annotation_rows = (
     annotations = (
         ('fields', 'EDID structure fields'),
         ('sections', 'EDID structure sections'),
     )
     annotation_rows = (
-        ('sections', 'Sections', (1,)),
         ('fields', 'Fields', (0,)),
         ('fields', 'Fields', (0,)),
+        ('sections', 'Sections', (1,)),
     )
 
     def __init__(self):
     )
 
     def __init__(self):
index 49c586d23b417ebbbd8729e05ae2dfbbc6193e24..033a44b2c777d5644ae7eff623b3c8ce224f41e6 100644 (file)
@@ -28,7 +28,8 @@ class Decoder(srd.Decoder):
     desc = '24xx series I²C EEPROM protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
     desc = '24xx series I²C EEPROM protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['eeprom24xx']
+    outputs = []
+    tags = ['IC', 'Memory']
     options = (
         {'id': 'chip', 'desc': 'Chip', 'default': 'generic',
             'values': tuple(chips.keys())},
     options = (
         {'id': 'chip', 'desc': 'Chip', 'default': 'generic',
             'values': tuple(chips.keys())},
index d76b869756c25fbb6fb23a513ba641f46b280819..8c08fc804c3d13b83ff2b50ee48768ed951d1300 100644 (file)
@@ -27,10 +27,13 @@ class Decoder(srd.Decoder):
     desc = '93xx series Microwire EEPROM protocol.'
     license = 'gplv2+'
     inputs = ['microwire']
     desc = '93xx series Microwire EEPROM protocol.'
     license = 'gplv2+'
     inputs = ['microwire']
-    outputs = ['eeprom93xx']
+    outputs = []
+    tags = ['IC', 'Memory']
     options = (
         {'id': 'addresssize', 'desc': 'Address size', 'default': 8},
         {'id': 'wordsize', 'desc': 'Word size', 'default': 16},
     options = (
         {'id': 'addresssize', 'desc': 'Address size', 'default': 8},
         {'id': 'wordsize', 'desc': 'Word size', 'default': 16},
+        {'id': 'format', 'desc': 'Data format', 'default': 'hex',
+            'values': ('ascii', 'hex')},
     )
     annotations = (
         ('si-data', 'SI data'),
     )
     annotations = (
         ('si-data', 'SI data'),
@@ -41,6 +44,10 @@ class Decoder(srd.Decoder):
         ('data', 'Data', (0, 1)),
         ('warnings', 'Warnings', (2,)),
     )
         ('data', 'Data', (0, 1)),
         ('warnings', 'Warnings', (2,)),
     )
+    binary = (
+        ('address', 'Address'),
+        ('data', 'Data'),
+    )
 
     def __init__(self):
         self.reset()
 
     def __init__(self):
         self.reset()
@@ -50,6 +57,7 @@ class Decoder(srd.Decoder):
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.addresssize = self.options['addresssize']
         self.wordsize = self.options['wordsize']
 
         self.addresssize = self.options['addresssize']
         self.wordsize = self.options['wordsize']
 
@@ -59,7 +67,8 @@ class Decoder(srd.Decoder):
         for b in range(len(data)):
             a += (data[b].si << (len(data) - b - 1))
         self.put(data[0].ss, data[-1].es, self.out_ann,
         for b in range(len(data)):
             a += (data[b].si << (len(data) - b - 1))
         self.put(data[0].ss, data[-1].es, self.out_ann,
-                 [0, ['Address: 0x%x' % a, 'Addr: 0x%x' % a, '0x%x' % a]])
+                 [0, ['Address: 0x%04x' % a, 'Addr: 0x%04x' % a, '0x%04x' % a]])
+        self.put(data[0].ss, data[-1].es, self.out_binary, [0, bytes([a])])
 
     def put_word(self, si, data):
         # Decode word (MSb first).
 
     def put_word(self, si, data):
         # Decode word (MSb first).
@@ -68,8 +77,22 @@ class Decoder(srd.Decoder):
             d = data[b].si if si else data[b].so
             word += (d << (len(data) - b - 1))
         idx = 0 if si else 1
             d = data[b].si if si else data[b].so
             word += (d << (len(data) - b - 1))
         idx = 0 if si else 1
-        self.put(data[0].ss, data[-1].es,
-                 self.out_ann, [idx, ['Data: 0x%x' % word, '0x%x' % word]])
+
+        if self.options['format'] == 'ascii':
+            word_str = ''
+            for s in range(0, len(data), 8):
+                c = 0xff & (word >> s)
+                if c in range(32, 126 + 1):
+                    word_str = chr(c) + word_str
+                else:
+                    word_str = '[{:02X}]'.format(c) + word_str
+            self.put(data[0].ss, data[-1].es,
+                     self.out_ann, [idx, ['Data: %s' % word_str, '%s' % word_str]])
+        else:
+            self.put(data[0].ss, data[-1].es,
+                     self.out_ann, [idx, ['Data: 0x%04x' % word, '0x%04x' % word]])
+            self.put(data[0].ss, data[-1].es, self.out_binary,
+                     [1, bytes([(word & 0xff00) >> 8, word & 0xff])])
 
     def decode(self, ss, es, data):
         if len(data) < (2 + self.addresssize):
 
     def decode(self, ss, es, data):
         if len(data) < (2 + self.addresssize):
index 778cfd1621e6a918977a9567b0f3898dd877c4f6..7f42ad70db579abb436ad3a0acfa2a18dcda3e9b 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'EM4100 100-150kHz RFID protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'EM4100 100-150kHz RFID protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['em4100']
+    outputs = []
+    tags = ['IC', 'RFID']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index 9fac9c6da0d582e601576a5eed818f4311145e8a..6297643ce0618e29222f5dcbf2964605074bda10 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'EM4205/EM4305 100-150kHz RFID protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'EM4205/EM4305 100-150kHz RFID protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['em4305']
+    outputs = []
+    tags = ['IC', 'RFID']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
diff --git a/decoders/enc28j60/__init__.py b/decoders/enc28j60/__init__.py
new file mode 100644 (file)
index 0000000..42f4377
--- /dev/null
@@ -0,0 +1,32 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Jiahao Li <reg@ljh.me>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
+by the Microchip ENC28J60 Ethernet chip.
+
+Details:
+http://ww1.microchip.com/downloads/en/DeviceDoc/39662e.pdf
+'''
+
+from .pd import Decoder
diff --git a/decoders/enc28j60/lists.py b/decoders/enc28j60/lists.py
new file mode 100644 (file)
index 0000000..59fbc1f
--- /dev/null
@@ -0,0 +1,161 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Jiahao Li <reg@ljh.me>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+REGS = [
+    [
+        'ERDPTL',
+        'ERDPTH',
+        'EWRPTL',
+        'EWRPTH',
+        'ETXSTL',
+        'ETXSTH',
+        'ETXNDL',
+        'ETXNDH',
+        'ERXSTL',
+        'ERXSTH',
+        'ERXNDL',
+        'ERXNDH',
+        'ERXRDPTL',
+        'ERXRDPTH',
+        'ERXWRPTL',
+        'ERXWRPTH',
+        'EDMASTL',
+        'EDMASTH',
+        'EDMANDL',
+        'EDMANDH',
+        'EDMADSTL',
+        'EDMADSTH',
+        'EDMACSL',
+        'EDMACSH',
+        '—',
+        '—',
+        'Reserved',
+        'EIE',
+        'EIR',
+        'ESTAT',
+        'ECON2',
+        'ECON1',
+    ],
+    [
+        'EHT0',
+        'EHT1',
+        'EHT2',
+        'EHT3',
+        'EHT4',
+        'EHT5',
+        'EHT6',
+        'EHT7',
+        'EPMM0',
+        'EPMM1',
+        'EPMM2',
+        'EPMM3',
+        'EPMM4',
+        'EPMM5',
+        'EPMM6',
+        'EPMM7',
+        'EPMCSL',
+        'EPMCSH',
+        '—',
+        '—',
+        'EPMOL',
+        'EPMOH',
+        'Reserved',
+        'Reserved',
+        'ERXFCON',
+        'EPKTCNT',
+        'Reserved',
+        'EIE',
+        'EIR',
+        'ESTAT',
+        'ECON2',
+        'ECON1',
+    ],
+    [
+        'MACON1',
+        'Reserved',
+        'MACON3',
+        'MACON4',
+        'MABBIPG',
+        '—',
+        'MAIPGL',
+        'MAIPGH',
+        'MACLCON1',
+        'MACLCON2',
+        'MAMXFLL',
+        'MAMXFLH',
+        'Reserved',
+        'Reserved',
+        'Reserved',
+        '—',
+        'Reserved',
+        'Reserved',
+        'MICMD',
+        '—',
+        'MIREGADR',
+        'Reserved',
+        'MIWRL',
+        'MIWRH',
+        'MIRDL',
+        'MIRDH',
+        'Reserved',
+        'EIE',
+        'EIR',
+        'ESTAT',
+        'ECON2',
+        'ECON1',
+    ],
+    [
+        'MAADR5',
+        'MAADR6',
+        'MAADR3',
+        'MAADR4',
+        'MAADR1',
+        'MAADR2',
+        'EBSTSD',
+        'EBSTCON',
+        'EBSTCSL',
+        'EBSTCSH',
+        'MISTAT',
+        '—',
+        '—',
+        '—',
+        '—',
+        '—',
+        '—',
+        '—',
+        'EREVID',
+        '—',
+        '—',
+        'ECOCON',
+        'Reserved',
+        'EFLOCON',
+        'EPAUSL',
+        'EPAUSH',
+        'Reserved',
+        'EIE',
+        'EIR',
+        'ESTAT',
+        'ECON2',
+        'ECON1',
+    ],
+]
diff --git a/decoders/enc28j60/pd.py b/decoders/enc28j60/pd.py
new file mode 100644 (file)
index 0000000..e8ce6e7
--- /dev/null
@@ -0,0 +1,294 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Jiahao Li <reg@ljh.me>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+import sigrokdecode as srd
+from .lists import *
+
+OPCODE_MASK = 0b11100000
+REG_ADDR_MASK = 0b00011111
+
+OPCODE_HANDLERS = {
+    0b00000000: '_process_rcr',
+    0b00100000: '_process_rbm',
+    0b01000000: '_process_wcr',
+    0b01100000: '_process_wbm',
+    0b10000000: '_process_bfs',
+    0b10100000: '_process_bfc',
+    0b11100000: '_process_src',
+}
+
+(ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC, ANN_DATA,
+ANN_REG_ADDR, ANN_WARNING) = range(10)
+
+REG_ADDR_ECON1 = 0x1F
+BIT_ECON1_BSEL0 = 0b00000001
+BIT_ECON1_BSEL1 = 0b00000010
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'enc28j60'
+    name = 'ENC28J60'
+    longname = 'Microchip ENC28J60'
+    desc = 'Microchip ENC28J60 10Base-T Ethernet controller protocol.'
+    license = 'mit'
+    inputs = ['spi']
+    outputs = []
+    tags = ['Embedded/industrial', 'Networking']
+    annotations = (
+        ('rcr', 'Read Control Register'),
+        ('rbm', 'Read Buffer Memory'),
+        ('wcr', 'Write Control Register'),
+        ('wbm', 'Write Buffer Memory'),
+        ('bfs', 'Bit Field Set'),
+        ('bfc', 'Bit Field Clear'),
+        ('src', 'System Reset Command'),
+        ('data', 'Data'),
+        ('reg-addr', 'Register Address'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
+        ('commands', 'Commands',
+            (ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC)),
+        ('warnings', 'Warnings', (ANN_WARNING,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.mosi = []
+        self.miso = []
+        self.ranges = []
+        self.cmd_ss = None
+        self.cmd_es = None
+        self.range_ss = None
+        self.range_es = None
+        self.active = False
+        self.bsel0 = None
+        self.bsel1 = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putc(self, data):
+        self.put(self.cmd_ss, self.cmd_es, self.out_ann, data)
+
+    def putr(self, data):
+        self.put(self.range_ss, self.range_es, self.out_ann, data)
+
+    def _process_command(self):
+        if len(self.mosi) == 0:
+            self.active = False
+            return
+
+        header = self.mosi[0]
+        opcode = header & OPCODE_MASK
+
+        if opcode not in OPCODE_HANDLERS:
+            self._put_command_warning("Unknown opcode.")
+            self.active = False
+            return
+
+        getattr(self, OPCODE_HANDLERS[opcode])()
+
+        self.active = False
+
+    def _get_register_name(self, reg_addr):
+        if (self.bsel0 is None) or (self.bsel1 is None):
+            # We don't know the bank we're in yet.
+            return None
+        else:
+            bank = (self.bsel1 << 1) + self.bsel0
+            return REGS[bank][reg_addr]
+
+    def _put_register_header(self):
+        reg_addr = self.mosi[0] & REG_ADDR_MASK
+        reg_name = self._get_register_name(reg_addr)
+
+        self.range_ss, self.range_es = self.cmd_ss, self.ranges[1][0]
+
+        if reg_name is None:
+            # We don't know the bank we're in yet.
+            self.putr([ANN_REG_ADDR, [
+                'Reg Bank ? Addr 0x{0:02X}'.format(reg_addr),
+                '?:{0:02X}'.format(reg_addr)]])
+            self.putr([ANN_WARNING, ['Warning: Register bank not known yet.',
+                                     'Warning']])
+        else:
+            self.putr([ANN_REG_ADDR, ['Reg {0}'.format(reg_name),
+                                      '{0}'.format(reg_name)]])
+
+            if (reg_name == '-') or (reg_name == 'Reserved'):
+                self.putr([ANN_WARNING, ['Warning: Invalid register accessed.',
+                                         'Warning']])
+
+    def _put_data_byte(self, data, byte_index, binary=False):
+        self.range_ss = self.ranges[byte_index][0]
+        if byte_index == len(self.mosi) - 1:
+            self.range_es = self.cmd_es
+        else:
+            self.range_es = self.ranges[byte_index + 1][0]
+
+        if binary:
+            self.putr([ANN_DATA, ['Data 0b{0:08b}'.format(data),
+                                  '{0:08b}'.format(data)]])
+        else:
+            self.putr([ANN_DATA, ['Data 0x{0:02X}'.format(data),
+                                  '{0:02X}'.format(data)]])
+
+    def _put_command_warning(self, reason):
+        self.putc([ANN_WARNING, ['Warning: {0}'.format(reason), 'Warning']])
+
+    def _process_rcr(self):
+        self.putc([ANN_RCR, ['Read Control Register', 'RCR']])
+
+        if (len(self.mosi) != 2) and (len(self.mosi) != 3):
+            self._put_command_warning('Invalid command length.')
+            return
+
+        self._put_register_header()
+
+        reg_name = self._get_register_name(self.mosi[0] & REG_ADDR_MASK)
+        if reg_name is None:
+            # We can't tell if we're accessing MAC/MII registers or not
+            # Let's trust the user in this case.
+            pass
+        else:
+            if (reg_name[0] == 'M') and (len(self.mosi) != 3):
+                self._put_command_warning('Attempting to read a MAC/MII '
+                    + 'register without using the dummy byte.')
+                return
+
+            if (reg_name[0] != 'M') and (len(self.mosi) != 2):
+                self._put_command_warning('Attempting to read a non-MAC/MII '
+                                          + 'register using the dummy byte.')
+                return
+
+        if len(self.mosi) == 2:
+            self._put_data_byte(self.miso[1], 1)
+        else:
+            self.range_ss, self.range_es = self.ranges[1][0], self.ranges[2][0]
+            self.putr([ANN_DATA, ['Dummy Byte', 'Dummy']])
+            self._put_data_byte(self.miso[2], 2)
+
+    def _process_rbm(self):
+        if self.mosi[0] != 0b00111010:
+            self._put_command_warning('Invalid header byte.')
+            return
+
+        self.putc([ANN_RBM, ['Read Buffer Memory: Length {0}'.format(
+                             len(self.mosi) - 1), 'RBM']])
+
+        for i in range(1, len(self.miso)):
+            self._put_data_byte(self.miso[i], i)
+
+    def _process_wcr(self):
+        self.putc([ANN_WCR, ['Write Control Register', 'WCR']])
+
+        if len(self.mosi) != 2:
+            self._put_command_warning('Invalid command length.')
+            return
+
+        self._put_register_header()
+        self._put_data_byte(self.mosi[1], 1)
+
+        if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
+            self.bsel0 = (self.mosi[1] & BIT_ECON1_BSEL0) >> 0
+            self.bsel1 = (self.mosi[1] & BIT_ECON1_BSEL1) >> 1
+
+    def _process_wbm(self):
+        if self.mosi[0] != 0b01111010:
+            self._put_command_warning('Invalid header byte.')
+            return
+
+        self.putc([ANN_WBM, ['Write Buffer Memory: Length {0}'.format(
+                             len(self.mosi) - 1), 'WBM']])
+
+        for i in range(1, len(self.mosi)):
+            self._put_data_byte(self.mosi[i], i)
+
+    def _process_bfc(self):
+        self.putc([ANN_BFC, ['Bit Field Clear', 'BFC']])
+
+        if len(self.mosi) != 2:
+            self._put_command_warning('Invalid command length.')
+            return
+
+        self._put_register_header()
+        self._put_data_byte(self.mosi[1], 1, True)
+
+        if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
+            if self.mosi[1] & BIT_ECON1_BSEL0:
+                self.bsel0 = 0
+            if self.mosi[1] & BIT_ECON1_BSEL1:
+                self.bsel1 = 0
+
+    def _process_bfs(self):
+        self.putc([ANN_BFS, ['Bit Field Set', 'BFS']])
+
+        if len(self.mosi) != 2:
+            self._put_command_warning('Invalid command length.')
+            return
+
+        self._put_register_header()
+        self._put_data_byte(self.mosi[1], 1, True)
+
+        if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
+            if self.mosi[1] & BIT_ECON1_BSEL0:
+                self.bsel0 = 1
+            if self.mosi[1] & BIT_ECON1_BSEL1:
+                self.bsel1 = 1
+
+    def _process_src(self):
+        self.putc([ANN_SRC, ['System Reset Command', 'SRC']])
+
+        if len(self.mosi) != 1:
+            self._put_command_warning('Invalid command length.')
+            return
+
+        self.bsel0 = 0
+        self.bsel1 = 0
+
+    def decode(self, ss, es, data):
+        ptype, data1, data2 = data
+
+        if ptype == 'CS-CHANGE':
+            new_cs = data2
+
+            if new_cs == 0:
+                self.active = True
+                self.cmd_ss = ss
+                self.mosi = []
+                self.miso = []
+                self.ranges = []
+            elif new_cs == 1:
+                if self.active:
+                    self.cmd_es = es
+                    self._process_command()
+        elif ptype == 'DATA':
+            mosi, miso = data1, data2
+
+            self.mosi.append(mosi)
+            self.miso.append(miso)
+            self.ranges.append((ss, es))
diff --git a/decoders/flexray/__init__.py b/decoders/flexray/__init__.py
new file mode 100644 (file)
index 0000000..73dc7fa
--- /dev/null
@@ -0,0 +1,32 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+FlexRay is a fast, deterministic and fault-tolerant fieldbus system
+which is used in cars in high security related areas like X-by-Wire.
+
+It is the result of the FlexRay consortium which consisted of BMW,
+Daimler, Motorola (today Freescale) and Philips, with the goal of
+working out a common standard automotive bus system.
+
+This decoder assumes that at least one channel of a logic level RX line
+of a transceiver is sampled (e.g. NXP TJA1080).
+'''
+
+from .pd import Decoder
diff --git a/decoders/flexray/pd.py b/decoders/flexray/pd.py
new file mode 100644 (file)
index 0000000..e13f2d4
--- /dev/null
@@ -0,0 +1,413 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+# Selection of constants as defined in FlexRay specification 3.0.1 Chapter A.1:
+class Const:
+    cChannelIdleDelimiter = 11
+    cCrcInitA = 0xFEDCBA
+    cCrcInitB = 0xABCDEF
+    cCrcPolynomial = 0x5D6DCB
+    cCrcSize = 24
+    cCycleCountMax = 63
+    cdBSS = 2
+    cdCAS = 30
+    cdFES = 2
+    cdFSS = 1
+    cHCrcInit = 0x01A
+    cHCrcPolynomial = 0x385
+    cHCrcSize = 11
+    cSamplesPerBit = 8
+    cSlotIDMax = 2047
+    cStaticSlotIDMax = 1023
+    cVotingSamples = 5
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'flexray'
+    name = 'FlexRay'
+    longname = 'FlexRay'
+    desc = 'Automotive network communications protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['Automotive']
+    channels = (
+        {'id': 'channel', 'name': 'Channel', 'desc': 'FlexRay bus channel'},
+    )
+    options = (
+        {'id': 'channel_type', 'desc': 'Channel type', 'default': 'A',
+            'values': ('A', 'B')},
+        {'id': 'bitrate', 'desc': 'Bitrate (bit/s)', 'default': 10000000,
+            'values': (10000000, 5000000, 2500000)},
+    )
+    annotations = (
+        ('data', 'FlexRay payload data'),
+        ('tss', 'Transmission start sequence'),
+        ('fss', 'Frame start sequence'),
+        ('reserved-bit', 'Reserved bit'),
+        ('ppi', 'Payload preamble indicator'),
+        ('null-frame', 'Nullframe indicator'),
+        ('sync-frame', 'Full identifier'),
+        ('startup-frame', 'Startup frame indicator'),
+        ('id', 'Frame ID'),
+        ('length', 'Data length'),
+        ('header-crc', 'Header CRC'),
+        ('cycle', 'Cycle code'),
+        ('data-byte', 'Data byte'),
+        ('frame-crc', 'Frame CRC'),
+        ('cid-delimiter', 'Channel idle delimiter'),
+        ('bss', 'Byte start sequence'),
+        ('warnings', 'Human-readable warnings'),
+        ('bit', 'Bit'),
+        ('cid', 'Channel idle delimiter'),
+        ('dts', 'Dynamic trailing sequence'),
+        ('cas', 'Collision avoidance symbol'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (15, 17)),
+        ('fields', 'Fields', tuple(range(15)) + (18, 19, 20)),
+        ('warnings', 'Warnings', (16,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
+        self.reset_variables()
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            bitrate = float(self.options['bitrate'])
+            self.samplerate = value
+            self.bit_width = float(self.samplerate) / bitrate
+            self.sample_point = (self.bit_width / 100.0) * self.sample_point_percent
+
+    # Generic helper for FlexRay bit annotations.
+    def putg(self, ss, es, data):
+        left, right = int(self.sample_point), int(self.bit_width - self.sample_point)
+        self.put(ss - left, es + right, self.out_ann, data)
+
+    # Single-FlexRay-bit annotation using the current samplenum.
+    def putx(self, data):
+        self.putg(self.samplenum, self.samplenum, data)
+
+    # Multi-FlexRay-bit annotation from self.ss_block to current samplenum.
+    def putb(self, data):
+        self.putg(self.ss_block, self.samplenum, data)
+
+    # Generic CRC algorithm for any bit size and any data length. Used for
+    # 11-bit header and 24-bit trailer. Not very efficient but at least it
+    # works for now.
+    #
+    # TODO:
+    # - use precalculated tables to increase performance.
+    # - Add support for reverse CRC calculations.
+
+    @staticmethod
+    def crc(data, data_len_bits, polynom, crc_len_bits, iv=0, xor=0):
+        reg = iv ^ xor
+
+        for i in range(data_len_bits - 1, -1, -1):
+            bit = ((reg >> (crc_len_bits - 1)) & 0x1) ^ ((data >> i) & 0x1)
+            reg <<= 1
+            if bit:
+                reg ^= polynom
+
+        mask = (1 << crc_len_bits) - 1
+        crc = reg & mask
+
+        return crc ^ xor
+
+    def reset_variables(self):
+        self.sample_point_percent = 50 # TODO: use vote based sampling
+        self.state = 'IDLE'
+        self.tss_start = self.tss_end = self.frame_type = self.dlc = None
+        self.rawbits = [] # All bits, including byte start sequence bits
+        self.bits = [] # Only actual FlexRay frame bits (no byte start sequence bits)
+        self.curbit = 0 # Current bit of FlexRay frame (bit 0 == FSS)
+        self.last_databit = 999 # Positive value that bitnum+x will never match
+        self.last_xmit_bit = 999 # Positive value that bitnum+x will never match
+        self.ss_block = None
+        self.ss_databytebits = []
+        self.end_of_frame = False
+        self.dynamic_frame = False
+        self.ss_bit0 = None
+        self.ss_bit1 = None
+        self.ss_bit2 = None
+
+    # Poor man's clock synchronization. Use signal edges which change to
+    # dominant state in rather simple ways. This naive approach is neither
+    # aware of the SYNC phase's width nor the specific location of the edge,
+    # but improves the decoder's reliability when the input signal's bitrate
+    # does not exactly match the nominal rate.
+    def dom_edge_seen(self, force=False):
+        self.dom_edge_snum = self.samplenum
+        self.dom_edge_bcount = self.curbit
+
+    # Determine the position of the next desired bit's sample point.
+    def get_sample_point(self, bitnum):
+        samplenum = self.dom_edge_snum
+        samplenum += self.bit_width * (bitnum - self.dom_edge_bcount)
+        samplenum += self.sample_point
+        return int(samplenum)
+
+    def is_bss_sequence(self):
+        # FlexRay uses NRZ encoding and adds a binary 10 sequence before each
+        # byte. After each 8 data bits, a BSS sequence is added but not after
+        # frame CRC.
+
+        if self.end_of_frame:
+          return False
+
+        if (len(self.rawbits) - 2) % 10 == 0:
+            return True
+        elif (len(self.rawbits) - 3) % 10 == 0:
+            return True
+
+        return False
+
+    def handle_bit(self, fr_rx):
+        self.rawbits.append(fr_rx)
+        self.bits.append(fr_rx)
+
+        # Get the index of the current FlexRay frame bit.
+        bitnum = len(self.bits) - 1
+
+        # If this is a byte start sequence remove it from self.bits and ignore it.
+        if self.is_bss_sequence():
+            self.bits.pop()
+
+            if bitnum > 1:
+                self.putx([15, [str(fr_rx)]])
+            else:
+                if len(self.rawbits) == 2:
+                    self.ss_bit1 = self.samplenum
+                elif len(self.rawbits) == 3:
+                    self.ss_bit2 = self.samplenum
+
+            self.curbit += 1 # Increase self.curbit (bitnum is not affected).
+            return
+        else:
+            if bitnum > 1:
+                self.putx([17, [str(fr_rx)]])
+
+        # Bit 0: Frame start sequence (FSS) bit
+        if bitnum == 0:
+            self.ss_bit0 = self.samplenum
+
+        # Bit 1: Start of header
+        elif bitnum == 1:
+            if self.rawbits[:3] == [1, 1, 0]:
+                self.put(self.tss_start, self.tss_end, self.out_ann,
+                        [1, ['Transmission start sequence', 'TSS']])
+
+                self.putg(self.ss_bit0, self.ss_bit0, [17, [str(self.rawbits[:3][0])]])
+                self.putg(self.ss_bit0, self.ss_bit0, [2,  ['FSS', 'Frame start sequence']])
+                self.putg(self.ss_bit1, self.ss_bit1, [15, [str(self.rawbits[:3][1])]])
+                self.putg(self.ss_bit2, self.ss_bit2, [15, [str(self.rawbits[:3][2])]])
+                self.putx([17, [str(fr_rx)]])
+                self.putx([3, ['Reserved bit: %d' % fr_rx, 'RB: %d' % fr_rx, 'RB']])
+            else:
+                self.put(self.tss_start, self.tss_end, self.out_ann,
+                        [20, ['Collision avoidance symbol', 'CAS']])
+                self.reset_variables()
+
+            # TODO: warning, if sequence is neither [1, 1, 0] nor [1, 1, 1]
+
+        # Bit 2: Payload preamble indicator. Must be 0 if null frame indicator is 0.
+        elif bitnum == 2:
+            self.putx([4, ['Payload preamble indicator: %d' % fr_rx,
+                           'PPI: %d' % fr_rx]])
+
+        # Bit 3: Null frame indicator (inversed)
+        elif bitnum == 3:
+            data_type = 'data frame' if fr_rx else 'null frame'
+            self.putx([5, ['Null frame indicator: %s' % data_type,
+                           'NF: %d' % fr_rx, 'NF']])
+
+        # Bit 4: Sync frame indicator
+        # Must be 1 if startup frame indicator is 1.
+        elif bitnum == 4:
+            self.putx([6, ['Sync frame indicator: %d' % fr_rx,
+                           'Sync: %d' % fr_rx, 'Sync']])
+
+        # Bit 5: Startup frame indicator
+        elif bitnum == 5:
+            self.putx([7, ['Startup frame indicator: %d' % fr_rx,
+                           'Startup: %d' % fr_rx, 'Startup']])
+
+        # Remember start of ID (see below).
+        elif bitnum == 6:
+            self.ss_block = self.samplenum
+
+        # Bits 6-16: Frame identifier (ID[10..0])
+        # ID must NOT be 0.
+        elif bitnum == 16:
+            self.id = int(''.join(str(d) for d in self.bits[6:]), 2)
+            self.putb([8, ['Frame ID: %d' % self.id, 'ID: %d' % self.id,
+                           '%d' % self.id]])
+
+        # Remember start of payload length (see below).
+        elif bitnum == 17:
+            self.ss_block = self.samplenum
+
+        # Bits 17-23: Payload length (Length[7..0])
+        # Payload length in header is the half of the real payload size.
+        elif bitnum == 23:
+            self.payload_length = int(''.join(str(d) for d in self.bits[17:]), 2)
+            self.putb([9, ['Payload length: %d' % self.payload_length,
+                           'Length: %d' % self.payload_length,
+                           '%d' % self.payload_length]])
+
+        # Remember start of header CRC (see below).
+        elif bitnum == 24:
+            self.ss_block = self.samplenum
+
+        # Bits 24-34: Header CRC (11-bit) (HCRC[11..0])
+        # Calculation of header CRC is equal on both channels.
+        elif bitnum == 34:
+            bits = ''.join([str(b) for b in self.bits[4:24]])
+            header_to_check = int(bits, 2)
+            expected_crc = self.crc(header_to_check, len(bits),
+                Const.cHCrcPolynomial, Const.cHCrcSize, Const.cHCrcInit)
+            self.header_crc = int(''.join(str(d) for d in self.bits[24:]), 2)
+
+            crc_ok = self.header_crc == expected_crc
+            crc_ann = "OK" if crc_ok else "bad"
+
+            self.putb([10, ['Header CRC: 0x%X (%s)' % (self.header_crc, crc_ann),
+                            '0x%X (%s)' % (self.header_crc, crc_ann),
+                            '0x%X' % self.header_crc]])
+
+        # Remember start of cycle code (see below).
+        elif bitnum == 35:
+            self.ss_block = self.samplenum
+
+        # Bits 35-40: Cycle code (Cyc[6..0])
+        # Cycle code. Must be between 0 and 63.
+        elif bitnum == 40:
+            self.cycle = int(''.join(str(d) for d in self.bits[35:]), 2)
+            self.putb([11, ['Cycle: %d' % self.cycle, 'Cyc: %d' % self.cycle,
+                            '%d' % self.cycle]])
+            self.last_databit = 41 + 2 * self.payload_length * 8
+
+        # Remember all databyte bits, except the very last one.
+        elif bitnum in range(41, self.last_databit):
+            self.ss_databytebits.append(self.samplenum)
+
+        # Bits 41-X: Data field (0-254 bytes, depending on length)
+        # The bits within a data byte are transferred MSB-first.
+        elif bitnum == self.last_databit:
+            self.ss_databytebits.append(self.samplenum) # Last databyte bit.
+            for i in range(2 * self.payload_length):
+                x = 40 + (8 * i) + 1
+                b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+                ss = self.ss_databytebits[i * 8]
+                es = self.ss_databytebits[((i + 1) * 8) - 1]
+                self.putg(ss, es, [12, ['Data byte %d: 0x%02x' % (i, b),
+                                        'DB%d: 0x%02x' % (i, b), '%02X' % b]])
+            self.ss_databytebits = []
+            self.ss_block = self.samplenum # Remember start of trailer CRC.
+
+        # Trailer CRC (24-bit) (CRC[11..0])
+        # Initialization vector of channel A and B are different, so CRCs are
+        # different for same data.
+        elif bitnum == self.last_databit + 23:
+            bits = ''.join([str(b) for b in self.bits[1:-24]])
+            frame_to_check = int(bits, 2)
+            iv = Const.cCrcInitA if self.options['channel_type'] == 'A' else Const.cCrcInitB
+            expected_crc = self.crc(frame_to_check, len(bits),
+                Const.cCrcPolynomial, Const.cCrcSize, iv=iv)
+            self.frame_crc = int(''.join(str(d) for d in self.bits[self.last_databit:]), 2)
+
+            crc_ok = self.frame_crc == expected_crc
+            crc_ann = "OK" if crc_ok else "bad"
+
+            self.putb([13, ['Frame CRC: 0x%X (%s)' % (self.frame_crc, crc_ann),
+                            '0x%X (%s)' % (self.frame_crc, crc_ann),
+                            '0x%X' % self.frame_crc]])
+            self.end_of_frame = True
+
+        # Remember start of frame end sequence (see below).
+        elif bitnum == self.last_databit + 24:
+            self.ss_block = self.samplenum
+
+        # Frame end sequence, must be 1 followed by 0.
+        elif bitnum == self.last_databit + 25:
+            self.putb([14, ['Frame end sequence', 'FES']])
+
+        # Check for DTS
+        elif bitnum == self.last_databit + 26:
+            if not fr_rx:
+                self.dynamic_frame = True
+            else:
+                self.last_xmit_bit = bitnum
+            self.ss_block = self.samplenum
+
+        # Remember start of channel idle delimiter (see below).
+        elif bitnum == self.last_xmit_bit:
+            self.ss_block = self.samplenum
+
+        # Channel idle limiter (CID[11..0])
+        elif bitnum == self.last_xmit_bit + Const.cChannelIdleDelimiter - 1:
+            self.putb([18, ['Channel idle delimiter', 'CID']])
+            self.reset_variables()
+
+        # DTS if dynamic frame
+        elif bitnum > self.last_databit + 27:
+            if self.dynamic_frame:
+                if fr_rx:
+                    if self.last_xmit_bit == 999:
+                        self.putb([19, ['Dynamic trailing sequence', 'DTS']])
+                        self.last_xmit_bit = bitnum + 1
+                        self.ss_block = self.samplenum
+
+        self.curbit += 1
+
+    def decode(self):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        while True:
+            # State machine.
+            if self.state == 'IDLE':
+                # Wait for a dominant state (logic 0) on the bus.
+                (fr_rx,) = self.wait({0: 'l'})
+                self.tss_start = self.samplenum
+                (fr_rx,) = self.wait({0: 'h'})
+                self.tss_end = self.samplenum
+                self.dom_edge_seen(force = True)
+                self.state = 'GET BITS'
+            elif self.state == 'GET BITS':
+                # Wait until we're in the correct bit/sampling position.
+                pos = self.get_sample_point(self.curbit)
+                (fr_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
+                if self.matched[1]:
+                    self.dom_edge_seen()
+                if self.matched[0]:
+                    self.handle_bit(fr_rx)
index ef5d51352fe25356da1e9c47faf9c0487a5c3f4c..055908c8a4f3bfac7fbf1a31c3419b246581312f 100644 (file)
@@ -77,10 +77,11 @@ class Decoder(srd.Decoder):
     id = 'graycode'
     name = 'Gray code'
     longname = 'Gray code and rotary encoder'
     id = 'graycode'
     name = 'Gray code'
     longname = 'Gray code and rotary encoder'
-    desc = 'Accumulate rotary encoder increments, provide timing statistics.'
+    desc = 'Accumulate rotary encoder increments, provide statistics.'
     license = 'gplv2+'
     inputs = ['logic']
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['graycode']
+    outputs = []
+    tags = ['Encoding']
     optional_channels = tuple(
         {'id': 'd{}'.format(i), 'name': 'D{}'.format(i), 'desc': 'Data line {}'.format(i)}
         for i in range(MAX_CHANNELS)
     optional_channels = tuple(
         {'id': 'd{}'.format(i), 'name': 'D{}'.format(i), 'desc': 'Data line {}'.format(i)}
         for i in range(MAX_CHANNELS)
index 21f0cb8bbd933ef701170ac01917b8351c029dae..a02bf18324b724bf8d776a6902775acf27792b65 100644 (file)
 
 '''
 This protocol decoder tries to guess the bitrate / baudrate of the
 
 '''
 This protocol decoder tries to guess the bitrate / baudrate of the
-communication on the specified channel. Typically this will be used to
-guess / detect the baudrate used in a UART communication snippet, but it
-could also be used to guess bitrates of certain other protocols or buses.
+communication on the specified channel.
+
+Typically this will be used to guess / detect the baudrate used in a UART
+communication snippet, but it could also be used to guess bitrates of certain
+other protocols or buses.
 
 It should be noted that this is nothing more than a simple guess / heuristic,
 and that there are various cases in practice where the detection of the
 
 It should be noted that this is nothing more than a simple guess / heuristic,
 and that there are various cases in practice where the detection of the
@@ -31,6 +33,8 @@ The precision of the estimated bitrate / baudrate will also depend on the
 samplerate used to sample the respective channel. For good results it is
 recommended to use a logic analyzer samplerate that is much higher than
 the expected bitrate/baudrate that might be used on the channel.
 samplerate used to sample the respective channel. For good results it is
 recommended to use a logic analyzer samplerate that is much higher than
 the expected bitrate/baudrate that might be used on the channel.
+
+The last annotation emitted by the decoder will be the best bitrate guess.
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 7a3121a3009e0f5c062bb95c390366fb1483b3e4..462fa8aac33ae6e473be146edfbc92763088685f 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'Guess the bitrate/baudrate of a UART (or other) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Guess the bitrate/baudrate of a UART (or other) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['guess_bitrate']
+    outputs = []
+    tags = ['Clock/timing', 'Util']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
diff --git a/decoders/hdcp/__init__.py b/decoders/hdcp/__init__.py
new file mode 100644 (file)
index 0000000..f2e10b6
--- /dev/null
@@ -0,0 +1,27 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Dave Craig <dcraig@brightsign.biz>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes HDCP messages.
+
+Details:
+https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf
+'''
+
+from .pd import Decoder
diff --git a/decoders/hdcp/pd.py b/decoders/hdcp/pd.py
new file mode 100644 (file)
index 0000000..2d000dc
--- /dev/null
@@ -0,0 +1,191 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Dave Craig <dcraig@brightsign.biz>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+msg_ids = {
+    2: 'AKE_Init',
+    3: 'AKE_Send_Cert',
+    4: 'AKE_No_stored_km',
+    5: 'AKE_Stored_km',
+
+    7: 'AKE_Send_H_prime',
+    8: 'AKE_Send_Pairing_Info',
+
+    9: 'LC_Init',
+    10: 'LC_Send_L_prime',
+
+    11: 'SKE_Send_Eks',
+    12: 'RepeaterAuth_Send_ReceiverID_List',
+
+    15: 'RepeaterAuth_Send_Ack',
+    16: 'RepeaterAuth_Stream_Manage',
+    17: 'RepeaterAuth_Stream_Ready',
+}
+
+write_items = {
+    0x00: '1.4 Bksv - Receiver KSV',
+    0x08: '1.4 Ri\' - Link Verification',
+    0x0a: '1.4 Pj\' - Enhanced Link Verification',
+    0x10: '1.4 Aksv - Transmitter KSV',
+    0x15: '1.4 Ainfo - Transmitter KSV',
+    0x18: '1.4 An - Session random number',
+    0x20: '1.4 V\'H0',
+    0x24: '1.4 V\'H1',
+    0x28: '1.4 V\'H2',
+    0x2c: '1.4 V\'H3',
+    0x30: '1.4 V\'H4',
+    0x40: '1.4 Bcaps',
+    0x41: '1.4 Bstatus',
+    0x43: '1.4 KSV FIFO',
+    0x50: 'HDCP2Version',
+    0x60: 'Write_Message',
+    0x70: 'RxStatus',
+    0x80: 'Read_Message',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'hdcp'
+    name = 'HDCP'
+    longname = 'HDCP over HDMI'
+    desc = 'HDCP protocol over HDMI.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['hdcp']
+    tags = ['PC', 'Security/crypto']
+    annotations = \
+        tuple(('message-0x%02X' % i, 'Message 0x%02X' % i) for i in range(18)) + (
+        ('summary', 'Summary'),
+        ('warnings', 'Warnings'),
+    )
+    annotation_rows = (
+        ('messages', 'Messages', tuple(range(18))),
+        ('summary', 'Summary', (18,)),
+        ('warnings', 'Warnings', (19,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.state = 'IDLE'
+        self.stack = []
+        self.msg = -1
+        self.ss = self.es = self.ss_block = self.es_block = 0
+        self.init_seq = []
+        self.valid = 0
+        self.type = ''
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putb(self, data):
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+
+        # Collect the 'BITS' packet, then return. The next packet is
+        # guaranteed to belong to these bits we just stored.
+        if cmd == 'BITS':
+            self.bits = databyte
+            return
+
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if self.state == 'IDLE':
+            # Wait for an I2C START condition.
+            if cmd == 'START':
+                self.reset()
+                self.ss_block = ss
+            elif cmd != 'START REPEAT':
+                return
+            self.state = 'GET SLAVE ADDR'
+        elif self.state == 'GET SLAVE ADDR':
+            if cmd == 'ADDRESS READ':
+                self.state = 'BUFFER DATA'
+                if databyte != 0x3a:
+                    self.state = 'IDLE'
+            elif cmd == 'ADDRESS WRITE':
+                self.state = 'WRITE OFFSET'
+                if databyte != 0x3a:
+                    self.state = 'IDLE'
+        elif self.state == 'WRITE OFFSET':
+            if cmd == 'DATA WRITE':
+                if databyte in write_items:
+                    self.type = write_items[databyte]
+                if databyte in (0x10, 0x15, 0x18, 0x60):
+                    self.state = 'BUFFER DATA'
+                # If we are reading, then jump back to IDLE for a start repeat.
+                # If we are writing, then just continue onwards.
+                if self.state == 'BUFFER DATA':
+                    pass
+                elif self.type != '':
+                    self.state = 'IDLE'
+        elif self.state == 'BUFFER DATA':
+            if cmd in ('STOP', 'NACK'):
+                self.es_block = es
+                self.state = 'IDLE'
+                if self.type == '':
+                    return
+                if not self.stack:
+                    self.putb([18, ['%s' % (self.type)]])
+                    return
+                if self.type == 'RxStatus':
+                    rxstatus = (self.stack.pop() << 8) | self.stack.pop()
+                    reauth_req = (rxstatus & 0x800) != 0
+                    ready = (rxstatus & 0x400) != 0
+                    length = rxstatus & 0x3ff
+                    text = '%s, reauth %s, ready %s, length %s' % \
+                        (self.type, reauth_req, ready, length)
+                    self.putb([18, [text]])
+                elif self.type == '1.4 Bstatus':
+                    bstatus = (self.stack.pop() << 8) | self.stack.pop()
+                    device_count = bstatus & 0x7f
+                    max_devs_exceeded = (bstatus & 0x80) != 0
+                    depth = ((bstatus & 0x700) >> 8)
+                    max_cascase_exceeded = bstatus & 0x800
+                    hdmi_mode = (bstatus & 0x1000) != 0
+                    text = '%s, %s devices, depth %s, hdmi mode %s' % \
+                        (self.type, device_count, depth, hdmi_mode)
+                    self.putb([18, [text]])
+                elif self.type == 'Read_Message':
+                    msg = self.stack.pop(0)
+                    self.putb([msg, ['%s, %s' % (self.type,
+                        msg_ids.get(msg, 'Invalid'))]])
+                elif self.type == 'Write_Message':
+                    msg = self.stack.pop(0)
+                    self.putb([msg, ['%s, %s' % (self.type,
+                        msg_ids.get(msg, 'Invalid'))]])
+                elif self.type == 'HDCP2Version':
+                    version = self.stack.pop(0)
+                    if (version & 0x4):
+                        self.putb([18, ['HDCP2']])
+                    else:
+                        self.putb([18, ['NOT HDCP2']])
+                else:
+                    self.putb([18, ['%s' % (self.type)]])
+            elif cmd == 'DATA READ':
+                # Stack up our data bytes.
+                self.stack.append(databyte)
+            elif cmd == 'DATA WRITE':
+                # Stack up our data bytes.
+                self.stack.append(databyte)
index e70c27de27b301ffcc52d12bf800f141263d3eb1..f31e33ef85fb38341cb631c82b3dd912dccf7f87 100644 (file)
@@ -70,6 +70,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['i2c']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['i2c']
+    tags = ['Embedded/industrial']
     channels = (
         {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'},
         {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
     channels = (
         {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'},
         {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
index 2495e84931a3a42d0af015672d154bd32fd46d5e..d6841d32b5d9ee5082801b6e4b29975012b71747 100644 (file)
@@ -28,6 +28,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['i2c']
     outputs = [] # TODO: Only known at run-time.
     license = 'gplv2+'
     inputs = ['i2c']
     outputs = [] # TODO: Only known at run-time.
+    tags = ['Util']
 
     def __init__(self):
         self.reset()
 
     def __init__(self):
         self.reset()
index 1dc7fd1ddfc3e73cb1a2b53f3d38fa091aec0922..a54baab247b88a474db2c08a3649d097ba2c5e52 100644 (file)
@@ -31,8 +31,9 @@ class Decoder(srd.Decoder):
     license = 'gplv3+'
     inputs = ['i2c']
     outputs = ['i2c']
     license = 'gplv3+'
     inputs = ['i2c']
     outputs = ['i2c']
+    tags = ['Util']
     options = (
     options = (
-        {'id': 'address', 'desc': 'Address to filter out of the I²C stream',
+        {'id': 'address', 'desc': 'Slave address to filter (decimal)',
             'default': 0},
         {'id': 'direction', 'desc': 'Direction to filter', 'default': 'both',
             'values': ('read', 'write', 'both')}
             'default': 0},
         {'id': 'direction', 'desc': 'Direction to filter', 'default': 'both',
             'values': ('read', 'write', 'both')}
index bfb2c9e9f71b3952551d46499a957b7066c663ff..054d69e11a36f2cf5966d976f06d38e1a32555b6 100644 (file)
@@ -42,6 +42,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['i2s']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['i2s']
+    tags = ['Audio', 'PC']
     channels = (
         {'id': 'sck', 'name': 'SCK', 'desc': 'Bit clock line'},
         {'id': 'ws', 'name': 'WS', 'desc': 'Word select line'},
     channels = (
         {'id': 'sck', 'name': 'SCK', 'desc': 'Bit clock line'},
         {'id': 'ws', 'name': 'WS', 'desc': 'Word select line'},
@@ -145,10 +146,12 @@ class Decoder(srd.Decoder):
 
                 self.samplesreceived += 1
 
 
                 self.samplesreceived += 1
 
-                idx = 0 if self.oldws else 1
-                c1 = 'Left channel' if self.oldws else 'Right channel'
-                c2 = 'Left' if self.oldws else 'Right'
-                c3 = 'L' if self.oldws else 'R'
+                sck = self.wait({0: 'f'})
+
+                idx = 0 if not self.oldws else 1
+                c1 = 'Left channel' if not self.oldws else 'Right channel'
+                c2 = 'Left' if not self.oldws else 'Right'
+                c3 = 'L' if not self.oldws else 'R'
                 v = '%08x' % self.data
                 self.putpb(['DATA', [c3, self.data]])
                 self.putb([idx, ['%s: %s' % (c1, v), '%s: %s' % (c2, v),
                 v = '%08x' % self.data
                 self.putpb(['DATA', [c3, self.data]])
                 self.putb([idx, ['%s: %s' % (c1, v), '%s: %s' % (c2, v),
@@ -161,6 +164,8 @@ class Decoder(srd.Decoder):
                                    'word' % (self.bitcount, self.wordlength)]])
 
                 self.wordlength = self.bitcount
                                    'word' % (self.bitcount, self.wordlength)]])
 
                 self.wordlength = self.bitcount
+            else:
+                sck = self.wait({0: 'f'})
 
             # Reset decoder state.
             self.data = 0
 
             # Reset decoder state.
             self.data = 0
diff --git a/decoders/ieee488/__init__.py b/decoders/ieee488/__init__.py
new file mode 100644 (file)
index 0000000..4240114
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This protocol decoder can decode the GPIB (IEEE-488) protocol. Both variants
+of (up to) 16 parallel lines as well as serial communication (IEC bus) are
+supported in this implementation.
+'''
+
+from .pd import Decoder
diff --git a/decoders/ieee488/pd.py b/decoders/ieee488/pd.py
new file mode 100644 (file)
index 0000000..e39ecc5
--- /dev/null
@@ -0,0 +1,748 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
+## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
+## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+# This file was created from earlier implementations of the 'gpib' and
+# the 'iec' protocol decoders. It combines the parallel and the serial
+# transmission variants in a single instance with optional inputs for
+# maximum code re-use.
+
+# TODO
+# - Extend annotations for improved usability.
+#   - Keep talkers' data streams on separate annotation rows? Is this useful
+#     here at the GPIB level, or shall stacked decoders dispatch these? May
+#     depend on how often captures get inspected which involve multiple peers.
+#   - Make serial bit annotations optional? Could slow down interactive
+#     exploration for long captures (see USB).
+# - Move the inlined Commodore IEC peripherals support to a stacked decoder
+#   when more peripherals get added.
+# - SCPI over GPIB may "represent somewhat naturally" here already when
+#   text lines are a single run of data at the GPIB layer (each line between
+#   the address spec and either EOI or ATN). So a stacked SCPI decoder may
+#   only become necessary when the text lines' content shall get inspected.
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+
+'''
+OUTPUT_PYTHON format for stacked decoders:
+
+General packet format:
+[<ptype>, <addr>, <pdata>]
+
+This is the list of <ptype>s and their respective <pdata> values:
+
+Raw bits and bytes at the physical transport level:
+ - 'IEC_BIT': <addr> is not applicable, <pdata> is the transport's bit value.
+ - 'GPIB_RAW': <addr> is not applicable, <pdata> is the transport's
+   byte value. Data bytes are in the 0x00-0xff range, command/address
+   bytes are in the 0x100-0x1ff range.
+
+GPIB level byte fields (commands, addresses, pieces of data):
+ - 'COMMAND': <addr> is not applicable, <pdata> is the command's byte value.
+ - 'LISTEN': <addr> is the listener address (0-30), <pdata> is the raw
+   byte value (including the 0x20 offset).
+ - 'TALK': <addr> is the talker address (0-30), <pdata> is the raw byte
+   value (including the 0x40 offset).
+ - 'SECONDARY': <addr> is the secondary address (0-31), <pdata> is the
+   raw byte value (including the 0x60 offset).
+ - 'MSB_SET': <addr> as well as <pdata> are the raw byte value (including
+   the 0x80 offset). This usually does not happen for GPIB bytes with ATN
+   active, but was observed with the IEC bus and Commodore floppy drives,
+   when addressing channels within the device.
+ - 'DATA_BYTE': <addr> is the talker address (when available), <pdata>
+   is the raw data byte (transport layer, ATN inactive).
+
+Extracted payload information (peers and their communicated data):
+ - 'TALK_LISTEN': <addr> is the current talker, <pdata> is the list of
+   current listeners. These updates for the current "connected peers"
+   are sent when the set of peers changes, i.e. after talkers/listeners
+   got selected or deselected. Of course the data only covers what could
+   be gathered from the input data. Some controllers may not explicitly
+   address themselves, or captures may not include an early setup phase.
+ - 'TALKER_BYTES': <addr> is the talker address (when available), <pdata>
+   is the accumulated byte sequence between addressing a talker and EOI,
+   or the next command/address.
+ - 'TALKER_TEXT': <addr> is the talker address (when available), <pdata>
+   is the accumulated text sequence between addressing a talker and EOI,
+   or the next command/address.
+'''
+
+class ChannelError(Exception):
+    pass
+
+def _format_ann_texts(fmts, **args):
+    if not fmts:
+        return None
+    return [fmt.format(**args) for fmt in fmts]
+
+_cmd_table = {
+    # Command codes in the 0x00-0x1f range.
+    0x01: ['Go To Local', 'GTL'],
+    0x04: ['Selected Device Clear', 'SDC'],
+    0x05: ['Parallel Poll Configure', 'PPC'],
+    0x08: ['Global Execute Trigger', 'GET'],
+    0x09: ['Take Control', 'TCT'],
+    0x11: ['Local Lock Out', 'LLO'],
+    0x14: ['Device Clear', 'DCL'],
+    0x15: ['Parallel Poll Unconfigure', 'PPU'],
+    0x18: ['Serial Poll Enable', 'SPE'],
+    0x19: ['Serial Poll Disable', 'SPD'],
+    # Unknown type of command.
+    None: ['Unknown command 0x{cmd:02x}', 'command 0x{cmd:02x}', 'cmd {cmd:02x}', 'C{cmd_ord:c}'],
+    # Special listener/talker "addresses" (deselecting previous peers).
+    0x3f: ['Unlisten', 'UNL'],
+    0x5f: ['Untalk', 'UNT'],
+}
+
+def _is_command(b):
+    # Returns a tuple of booleans (or None when not applicable) whether
+    # the raw GPIB byte is: a command, an un-listen, an un-talk command.
+    if b in range(0x00, 0x20):
+        return True, None, None
+    if b in range(0x20, 0x40) and (b & 0x1f) == 31:
+        return True, True, False
+    if b in range(0x40, 0x60) and (b & 0x1f) == 31:
+        return True, False, True
+    return False, None, None
+
+def _is_listen_addr(b):
+    if b in range(0x20, 0x40):
+        return b & 0x1f
+    return None
+
+def _is_talk_addr(b):
+    if b in range(0x40, 0x60):
+        return b & 0x1f
+    return None
+
+def _is_secondary_addr(b):
+    if b in range(0x60, 0x80):
+        return b & 0x1f
+    return None
+
+def _is_msb_set(b):
+    if b & 0x80:
+        return b
+    return None
+
+def _get_raw_byte(b, atn):
+    # "Decorate" raw byte values for stacked decoders.
+    return b | 0x100 if atn else b
+
+def _get_raw_text(b, atn):
+    return ['{leader}{data:02x}'.format(leader = '/' if atn else '', data = b)]
+
+def _get_command_texts(b):
+    fmts = _cmd_table.get(b, None)
+    known = fmts is not None
+    if not fmts:
+        fmts = _cmd_table.get(None, None)
+    if not fmts:
+        return known, None
+    return known, _format_ann_texts(fmts, cmd = b, cmd_ord = ord('0') + b)
+
+def _get_address_texts(b):
+    laddr = _is_listen_addr(b)
+    taddr = _is_talk_addr(b)
+    saddr = _is_secondary_addr(b)
+    msb = _is_msb_set(b)
+    fmts = None
+    if laddr is not None:
+        fmts = ['Listen {addr:d}', 'L {addr:d}', 'L{addr_ord:c}']
+        addr = laddr
+    elif taddr is not None:
+        fmts = ['Talk {addr:d}', 'T {addr:d}', 'T{addr_ord:c}']
+        addr = taddr
+    elif saddr is not None:
+        fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
+        addr = saddr
+    elif msb is not None: # For IEC bus compat.
+        fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
+        addr = msb
+    return _format_ann_texts(fmts, addr = addr, addr_ord = ord('0') + addr)
+
+def _get_data_text(b):
+    # TODO Move the table of ASCII control characters to a common location?
+    # TODO Move the "printable with escapes" logic to a common helper?
+    _control_codes = {
+        0x00: 'NUL',
+        0x01: 'SOH',
+        0x02: 'STX',
+        0x03: 'ETX',
+        0x04: 'EOT',
+        0x05: 'ENQ',
+        0x06: 'ACK',
+        0x07: 'BEL',
+        0x08: 'BS',
+        0x09: 'TAB',
+        0x0a: 'LF',
+        0x0b: 'VT',
+        0x0c: 'FF',
+        0x0d: 'CR',
+        0x0e: 'SO',
+        0x0f: 'SI',
+        0x10: 'DLE',
+        0x11: 'DC1',
+        0x12: 'DC2',
+        0x13: 'DC3',
+        0x14: 'DC4',
+        0x15: 'NAK',
+        0x16: 'SYN',
+        0x17: 'ETB',
+        0x18: 'CAN',
+        0x19: 'EM',
+        0x1a: 'SUB',
+        0x1b: 'ESC',
+        0x1c: 'FS',
+        0x1d: 'GS',
+        0x1e: 'RS',
+        0x1f: 'US',
+    }
+    # Yes, exclude 0x7f (DEL) here. It's considered non-printable.
+    if b in range(0x20, 0x7f) and b not in ('[', ']'):
+        return '{:s}'.format(chr(b))
+    elif b in _control_codes:
+        return '[{:s}]'.format(_control_codes[b])
+    # Use a compact yet readable and unambigous presentation for bytes
+    # which contain non-printables. The format that is used here is
+    # compatible with 93xx EEPROM and UART decoders.
+    return '[{:02x}]'.format(b)
+
+(
+    PIN_DIO1, PIN_DIO2, PIN_DIO3, PIN_DIO4,
+    PIN_DIO5, PIN_DIO6, PIN_DIO7, PIN_DIO8,
+    PIN_EOI, PIN_DAV, PIN_NRFD, PIN_NDAC,
+    PIN_IFC, PIN_SRQ, PIN_ATN, PIN_REN,
+    PIN_CLK,
+) = range(17)
+PIN_DATA = PIN_DIO1
+
+(
+    ANN_RAW_BIT, ANN_RAW_BYTE,
+    ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,
+    ANN_EOI,
+    ANN_TEXT,
+    # TODO Want to provide one annotation class per talker address (0-30)?
+    ANN_IEC_PERIPH,
+    ANN_WARN,
+) = range(11)
+
+(
+    BIN_RAW,
+    BIN_DATA,
+    # TODO Want to provide one binary annotation class per talker address (0-30)?
+) = range(2)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'ieee488'
+    name = 'IEEE-488'
+    longname = 'IEEE-488 GPIB/HPIB/IEC'
+    desc = 'IEEE-488 General Purpose Interface Bus (GPIB/HPIB or IEC).'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['ieee488']
+    tags = ['PC', 'Retro computing']
+    channels = (
+        {'id': 'dio1' , 'name': 'DIO1/DATA',
+            'desc': 'Data I/O bit 1, or serial data'},
+    )
+    optional_channels = (
+        {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'},
+        {'id': 'dio3' , 'name': 'DIO3', 'desc': 'Data I/O bit 3'},
+        {'id': 'dio4' , 'name': 'DIO4', 'desc': 'Data I/O bit 4'},
+        {'id': 'dio5' , 'name': 'DIO5', 'desc': 'Data I/O bit 5'},
+        {'id': 'dio6' , 'name': 'DIO6', 'desc': 'Data I/O bit 6'},
+        {'id': 'dio7' , 'name': 'DIO7', 'desc': 'Data I/O bit 7'},
+        {'id': 'dio8' , 'name': 'DIO8', 'desc': 'Data I/O bit 8'},
+        {'id': 'eoi', 'name': 'EOI', 'desc': 'End or identify'},
+        {'id': 'dav', 'name': 'DAV', 'desc': 'Data valid'},
+        {'id': 'nrfd', 'name': 'NRFD', 'desc': 'Not ready for data'},
+        {'id': 'ndac', 'name': 'NDAC', 'desc': 'Not data accepted'},
+        {'id': 'ifc', 'name': 'IFC', 'desc': 'Interface clear'},
+        {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
+        {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
+        {'id': 'ren', 'name': 'REN', 'desc': 'Remote enable'},
+        {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock'},
+    )
+    options = (
+        {'id': 'iec_periph', 'desc': 'Decode Commodore IEC bus peripherals details',
+            'default': 'no', 'values': ('no', 'yes')},
+        {'id': 'delim', 'desc': 'Payload data delimiter',
+            'default': 'eol', 'values': ('none', 'eol')},
+    )
+    annotations = (
+        ('bit', 'IEC bit'),
+        ('raw', 'Raw byte'),
+        ('cmd', 'Command'),
+        ('laddr', 'Listener address'),
+        ('taddr', 'Talker address'),
+        ('saddr', 'Secondary address'),
+        ('data', 'Data byte'),
+        ('eoi', 'EOI'),
+        ('text', 'Talker text'),
+        ('periph', 'IEC bus peripherals'),
+        ('warn', 'Warning'),
+    )
+    annotation_rows = (
+        ('bits', 'IEC bits', (ANN_RAW_BIT,)),
+        ('raws', 'Raw bytes', (ANN_RAW_BYTE,)),
+        ('gpib', 'Commands/data', (ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,)),
+        ('eois', 'EOI', (ANN_EOI,)),
+        ('texts', 'Talker texts', (ANN_TEXT,)),
+        ('periphs', 'IEC peripherals', (ANN_IEC_PERIPH,)),
+        ('warns', 'Warnings', (ANN_WARN,)),
+    )
+    binary = (
+        ('raw', 'Raw bytes'),
+        ('data', 'Talker bytes'),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.curr_raw = None
+        self.curr_atn = None
+        self.curr_eoi = None
+        self.latch_atn = None
+        self.latch_eoi = None
+        self.accu_bytes = []
+        self.accu_text = []
+        self.ss_raw = None
+        self.es_raw = None
+        self.ss_eoi = None
+        self.es_eoi = None
+        self.ss_text = None
+        self.es_text = None
+        self.last_talker = None
+        self.last_listener = []
+        self.last_iec_addr = None
+        self.last_iec_sec = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_bin = self.register(srd.OUTPUT_BINARY)
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+    def putg(self, ss, es, data):
+        self.put(ss, es, self.out_ann, data)
+
+    def putbin(self, ss, es, data):
+        self.put(ss, es, self.out_bin, data)
+
+    def putpy(self, ss, es, ptype, addr, pdata):
+        self.put(ss, es, self.out_python, [ptype, addr, pdata])
+
+    def emit_eoi_ann(self, ss, es):
+        self.putg(ss, es, [ANN_EOI, ['EOI']])
+
+    def emit_bin_ann(self, ss, es, ann_cls, data):
+        self.putbin(ss, es, [ann_cls, bytes(data)])
+
+    def emit_data_ann(self, ss, es, ann_cls, data):
+        self.putg(ss, es, [ann_cls, data])
+
+    def emit_warn_ann(self, ss, es, data):
+        self.putg(ss, es, [ANN_WARN, data])
+
+    def flush_bytes_text_accu(self):
+        if self.accu_bytes and self.ss_text is not None and self.es_text is not None:
+            self.emit_bin_ann(self.ss_text, self.es_text, BIN_DATA, bytearray(self.accu_bytes))
+            self.putpy(self.ss_text, self.es_text, 'TALKER_BYTES', self.last_talker, bytearray(self.accu_bytes))
+            self.accu_bytes = []
+        if self.accu_text and self.ss_text is not None and self.es_text is not None:
+            text = ''.join(self.accu_text)
+            self.emit_data_ann(self.ss_text, self.es_text, ANN_TEXT, [text])
+            self.putpy(self.ss_text, self.es_text, 'TALKER_TEXT', self.last_talker, text)
+            self.accu_text = []
+        self.ss_text = self.es_text = None
+
+    def check_extra_flush(self, b):
+        # Optionally flush previously accumulated runs of payload data
+        # according to user specified conditions.
+        if self.options['delim'] == 'none':
+            return
+        if not self.accu_bytes:
+            return
+
+        # This implementation exlusively handles "text lines", but adding
+        # support for more variants here is straight forward.
+        #
+        # Search for the first data byte _after_ a user specified text
+        # line termination sequence was seen. The termination sequence's
+        # alphabet may be variable, and the sequence may span multiple
+        # data bytes. We accept either CR or LF, and combine the CR+LF
+        # sequence to strive for maximum length annotations for improved
+        # readability at different zoom levels. It's acceptable that this
+        # implementation would also combine multiple line terminations
+        # like LF+LF.
+        term_chars = (10, 13)
+        is_eol = b in term_chars
+        had_eol = self.accu_bytes[-1] in term_chars
+        if had_eol and not is_eol:
+            self.flush_bytes_text_accu()
+
+    def handle_ifc_change(self, ifc):
+        # Track IFC line for parallel input.
+        # Assertion of IFC de-selects all talkers and listeners.
+        if ifc:
+            self.last_talker = None
+            self.last_listener = []
+            self.flush_bytes_text_accu()
+
+    def handle_eoi_change(self, eoi):
+        # Track EOI line for parallel and serial input.
+        if eoi:
+            self.ss_eoi = self.samplenum
+            self.curr_eoi = eoi
+        else:
+            self.es_eoi = self.samplenum
+            if self.ss_eoi and self.latch_eoi:
+               self.emit_eoi_ann(self.ss_eoi, self.es_eoi)
+            self.es_text = self.es_eoi
+            self.flush_bytes_text_accu()
+            self.ss_eoi = self.es_eoi = None
+            self.curr_eoi = None
+
+    def handle_atn_change(self, atn):
+        # Track ATN line for parallel and serial input.
+        self.curr_atn = atn
+        if atn:
+            self.flush_bytes_text_accu()
+
+    def handle_iec_periph(self, ss, es, addr, sec, data):
+        # The annotation is optional.
+        if self.options['iec_periph'] != 'yes':
+            return
+        # Void internal state.
+        if addr is None and sec is None and data is None:
+            self.last_iec_addr = None
+            self.last_iec_sec = None
+            return
+        # Grab and evaluate new input.
+        _iec_addr_names = {
+            # TODO Add more items here. See the "Device numbering" section
+            # of the https://en.wikipedia.org/wiki/Commodore_bus page.
+            8: 'Disk 0',
+            9: 'Disk 1',
+        }
+        _iec_disk_range = range(8, 16)
+        if addr is not None:
+            self.last_iec_addr = addr
+            name = _iec_addr_names.get(addr, None)
+            if name:
+                self.emit_data_ann(ss, es, ANN_IEC_PERIPH, [name])
+        addr = self.last_iec_addr # Simplify subsequent logic.
+        if sec is not None:
+            # BEWARE! The secondary address is a full byte and includes
+            # the 0x60 offset, to also work when the MSB was set.
+            self.last_iec_sec = sec
+            subcmd, channel = sec & 0xf0, sec & 0x0f
+            channel_ord = ord('0') + channel
+            if addr is not None and addr in _iec_disk_range:
+                subcmd_fmts = {
+                    0x60: ['Reopen {ch:d}', 'Re {ch:d}', 'R{ch_ord:c}'],
+                    0xe0: ['Close {ch:d}', 'Cl {ch:d}', 'C{ch_ord:c}'],
+                    0xf0: ['Open {ch:d}', 'Op {ch:d}', 'O{ch_ord:c}'],
+                }.get(subcmd, None)
+                if subcmd_fmts:
+                    texts = _format_ann_texts(subcmd_fmts, ch = channel, ch_ord = channel_ord)
+                    self.emit_data_ann(ss, es, ANN_IEC_PERIPH, texts)
+        sec = self.last_iec_sec # Simplify subsequent logic.
+        if data is not None:
+            if addr is None or sec is None:
+                return
+            # TODO Process data depending on peripheral type and channel?
+
+    def handle_data_byte(self):
+        if not self.curr_atn:
+            self.check_extra_flush(self.curr_raw)
+        b = self.curr_raw
+        texts = _get_raw_text(b, self.curr_atn)
+        self.emit_data_ann(self.ss_raw, self.es_raw, ANN_RAW_BYTE, texts)
+        self.emit_bin_ann(self.ss_raw, self.es_raw, BIN_RAW, b.to_bytes(1, byteorder='big'))
+        self.putpy(self.ss_raw, self.es_raw, 'GPIB_RAW', None, _get_raw_byte(b, self.curr_atn))
+        if self.curr_atn:
+            ann_cls = None
+            upd_iec = False,
+            py_type = None
+            py_peers = False
+            is_cmd, is_unl, is_unt = _is_command(b)
+            laddr = _is_listen_addr(b)
+            taddr = _is_talk_addr(b)
+            saddr = _is_secondary_addr(b)
+            msb = _is_msb_set(b)
+            if is_cmd:
+                known, texts = _get_command_texts(b)
+                if not known:
+                    warn_texts = ['Unknown GPIB command', 'unknown', 'UNK']
+                    self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
+                ann_cls = ANN_CMD
+                py_type, py_addr = 'COMMAND', None
+                if is_unl:
+                    self.last_listener = []
+                    py_peers = True
+                if is_unt:
+                    self.last_talker = None
+                    py_peers = True
+                if is_unl or is_unt:
+                    upd_iec = True, None, None, None
+            elif laddr is not None:
+                addr = laddr
+                texts = _get_address_texts(b)
+                ann_cls = ANN_LADDR
+                py_type, py_addr = 'LISTEN', addr
+                if addr == self.last_talker:
+                    self.last_talker = None
+                self.last_listener.append(addr)
+                upd_iec = True, addr, None, None
+                py_peers = True
+            elif taddr is not None:
+                addr = taddr
+                texts = _get_address_texts(b)
+                ann_cls = ANN_TADDR
+                py_type, py_addr = 'TALK', addr
+                if addr in self.last_listener:
+                    self.last_listener.remove(addr)
+                self.last_talker = addr
+                upd_iec = True, addr, None, None
+                py_peers = True
+            elif saddr is not None:
+                addr = saddr
+                texts = _get_address_texts(b)
+                ann_cls = ANN_SADDR
+                upd_iec = True, None, b, None
+                py_type, py_addr = 'SECONDARY', addr
+            elif msb is not None:
+                # These are not really "secondary addresses", but they
+                # are used by the Commodore IEC bus (floppy channels).
+                texts = _get_address_texts(b)
+                ann_cls = ANN_SADDR
+                upd_iec = True, None, b, None
+                py_type, py_addr = 'MSB_SET', b
+            if ann_cls is not None and texts is not None:
+                self.emit_data_ann(self.ss_raw, self.es_raw, ann_cls, texts)
+            if upd_iec[0]:
+                self.handle_iec_periph(self.ss_raw, self.es_raw, upd_iec[1], upd_iec[2], upd_iec[3])
+            if py_type:
+                self.putpy(self.ss_raw, self.es_raw, py_type, py_addr, b)
+            if py_peers:
+                self.last_listener.sort()
+                self.putpy(self.ss_raw, self.es_raw, 'TALK_LISTEN', self.last_talker, self.last_listener)
+        else:
+            self.accu_bytes.append(b)
+            text = _get_data_text(b)
+            if not self.accu_text:
+                self.ss_text = self.ss_raw
+            self.accu_text.append(text)
+            self.es_text = self.es_raw
+            self.emit_data_ann(self.ss_raw, self.es_raw, ANN_DATA, [text])
+            self.handle_iec_periph(self.ss_raw, self.es_raw, None, None, b)
+            self.putpy(self.ss_raw, self.es_raw, 'DATA_BYTE', self.last_talker, b)
+
+    def handle_dav_change(self, dav, data):
+        if dav:
+            # Data availability starts when the flag goes active.
+            self.ss_raw = self.samplenum
+            self.curr_raw = bitpack(data)
+            self.latch_atn = self.curr_atn
+            self.latch_eoi = self.curr_eoi
+            return
+        # Data availability ends when the flag goes inactive. Handle the
+        # previously captured data byte according to associated flags.
+        self.es_raw = self.samplenum
+        self.handle_data_byte()
+        self.ss_raw = self.es_raw = None
+        self.curr_raw = None
+
+    def inject_dav_phase(self, ss, es, data):
+        # Inspection of serial input has resulted in one raw byte which
+        # spans a given period of time. Pretend we had seen a DAV active
+        # phase, to re-use code for the parallel transmission.
+        self.ss_raw = ss
+        self.curr_raw = bitpack(data)
+        self.latch_atn = self.curr_atn
+        self.latch_eoi = self.curr_eoi
+        self.es_raw = es
+        self.handle_data_byte()
+        self.ss_raw = self.es_raw = None
+        self.curr_raw = None
+
+    def invert_pins(self, pins):
+        # All lines (including data bits!) are low active and thus need
+        # to get inverted to receive their logical state (high active,
+        # regular data bit values). Cope with inputs being optional.
+        return [1 - p if p in (0, 1) else p for p in pins]
+
+    def decode_serial(self, has_clk, has_data_1, has_atn, has_srq):
+        if not has_clk or not has_data_1 or not has_atn:
+            raise ChannelError('IEC bus needs at least ATN and serial CLK and DATA.')
+
+        # This is a rephrased version of decoders/iec/pd.py:decode().
+        # SRQ was not used there either. Magic numbers were eliminated.
+        (
+            STEP_WAIT_READY_TO_SEND,
+            STEP_WAIT_READY_FOR_DATA,
+            STEP_PREP_DATA_TEST_EOI,
+            STEP_CLOCK_DATA_BITS,
+        ) = range(4)
+        step_wait_conds = (
+            [{PIN_ATN: 'f'}, {PIN_DATA: 'l', PIN_CLK: 'h'}],
+            [{PIN_ATN: 'f'}, {PIN_DATA: 'h', PIN_CLK: 'h'}, {PIN_CLK: 'l'}],
+            [{PIN_ATN: 'f'}, {PIN_DATA: 'f'}, {PIN_CLK: 'l'}],
+            [{PIN_ATN: 'f'}, {PIN_CLK: 'e'}],
+        )
+        step = STEP_WAIT_READY_TO_SEND
+        bits = []
+
+        while True:
+
+            # Sample input pin values. Keep DATA/CLK in verbatim form to
+            # re-use 'iec' decoder logic. Turn ATN to positive logic for
+            # easier processing. The data bits get handled during byte
+            # accumulation.
+            pins = self.wait(step_wait_conds[step])
+            data, clk = pins[PIN_DATA], pins[PIN_CLK]
+            atn, = self.invert_pins([pins[PIN_ATN]])
+
+            if self.matched[0]:
+                # Falling edge on ATN, reset step.
+                step = STEP_WAIT_READY_TO_SEND
+
+            if step == STEP_WAIT_READY_TO_SEND:
+                # Don't use self.matched[1] here since we might come from
+                # a step with different conds due to the code above.
+                if data == 0 and clk == 1:
+                    # Rising edge on CLK while DATA is low: Ready to send.
+                    step = STEP_WAIT_READY_FOR_DATA
+            elif step == STEP_WAIT_READY_FOR_DATA:
+                if data == 1 and clk == 1:
+                    # Rising edge on DATA while CLK is high: Ready for data.
+                    ss_byte = self.samplenum
+                    self.handle_atn_change(atn)
+                    if self.curr_eoi:
+                        self.handle_eoi_change(False)
+                    bits = []
+                    step = STEP_PREP_DATA_TEST_EOI
+                elif clk == 0:
+                    # CLK low again, transfer aborted.
+                    step = STEP_WAIT_READY_TO_SEND
+            elif step == STEP_PREP_DATA_TEST_EOI:
+                if data == 0 and clk == 1:
+                    # DATA goes low while CLK is still high, EOI confirmed.
+                    self.handle_eoi_change(True)
+                elif clk == 0:
+                    step = STEP_CLOCK_DATA_BITS
+                    ss_bit = self.samplenum
+            elif step == STEP_CLOCK_DATA_BITS:
+                if self.matched[1]:
+                    if clk == 1:
+                        # Rising edge on CLK; latch DATA.
+                        bits.append(data)
+                    elif clk == 0:
+                        # Falling edge on CLK; end of bit.
+                        es_bit = self.samplenum
+                        self.emit_data_ann(ss_bit, es_bit, ANN_RAW_BIT, ['{:d}'.format(bits[-1])])
+                        self.putpy(ss_bit, es_bit, 'IEC_BIT', None, bits[-1])
+                        ss_bit = self.samplenum
+                        if len(bits) == 8:
+                            es_byte = self.samplenum
+                            self.inject_dav_phase(ss_byte, es_byte, bits)
+                            if self.curr_eoi:
+                                self.handle_eoi_change(False)
+                            step = STEP_WAIT_READY_TO_SEND
+
+    def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
+
+        if False in has_data_n or not has_dav or not has_atn:
+            raise ChannelError('IEEE-488 needs at least ATN and DAV and eight DIO lines.')
+        has_ifc = self.has_channel(PIN_IFC)
+
+        # Capture data lines at the falling edge of DAV, process their
+        # values at rising DAV edge (when data validity ends). Also make
+        # sure to start inspection when the capture happens to start with
+        # low signal levels, i.e. won't include the initial falling edge.
+        # Scan for ATN/EOI edges as well (including the trick which works
+        # around initial pin state).
+        # Map low-active physical transport lines to positive logic here,
+        # to simplify logical inspection/decoding of communicated data,
+        # and to avoid redundancy and inconsistency in later code paths.
+        waitcond = []
+        idx_dav = len(waitcond)
+        waitcond.append({PIN_DAV: 'l'})
+        idx_atn = len(waitcond)
+        waitcond.append({PIN_ATN: 'l'})
+        idx_eoi = None
+        if has_eoi:
+            idx_eoi = len(waitcond)
+            waitcond.append({PIN_EOI: 'l'})
+        idx_ifc = None
+        if has_ifc:
+            idx_ifc = len(waitcond)
+            waitcond.append({PIN_IFC: 'l'})
+        while True:
+            pins = self.wait(waitcond)
+            pins = self.invert_pins(pins)
+
+            # BEWARE! Order of evaluation does matter. For low samplerate
+            # captures, many edges fall onto the same sample number. So
+            # we process active edges of flags early (before processing
+            # data bits), and inactive edges late (after data got processed).
+            if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 1:
+                self.handle_ifc_change(pins[PIN_IFC])
+            if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 1:
+                self.handle_eoi_change(pins[PIN_EOI])
+            if self.matched[idx_atn] and pins[PIN_ATN] == 1:
+                self.handle_atn_change(pins[PIN_ATN])
+            if self.matched[idx_dav]:
+                self.handle_dav_change(pins[PIN_DAV], pins[PIN_DIO1:PIN_DIO8 + 1])
+            if self.matched[idx_atn] and pins[PIN_ATN] == 0:
+                self.handle_atn_change(pins[PIN_ATN])
+            if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 0:
+                self.handle_eoi_change(pins[PIN_EOI])
+            if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 0:
+                self.handle_ifc_change(pins[PIN_IFC])
+
+            waitcond[idx_dav][PIN_DAV] = 'e'
+            waitcond[idx_atn][PIN_ATN] = 'e'
+            if has_eoi:
+                waitcond[idx_eoi][PIN_EOI] = 'e'
+            if has_ifc:
+                waitcond[idx_ifc][PIN_IFC] = 'e'
+
+    def decode(self):
+        # The decoder's boilerplate declares some of the input signals as
+        # optional, but only to support both serial and parallel variants.
+        # The CLK signal discriminates the two. For either variant some
+        # of the "optional" signals are not really optional for proper
+        # operation of the decoder. Check these conditions here.
+        has_clk = self.has_channel(PIN_CLK)
+        has_data_1 = self.has_channel(PIN_DIO1)
+        has_data_n = [bool(self.has_channel(pin) for pin in range(PIN_DIO1, PIN_DIO8 + 1))]
+        has_dav = self.has_channel(PIN_DAV)
+        has_atn = self.has_channel(PIN_ATN)
+        has_eoi = self.has_channel(PIN_EOI)
+        has_srq = self.has_channel(PIN_SRQ)
+        if has_clk:
+            self.decode_serial(has_clk, has_data_1, has_atn, has_srq)
+        else:
+            self.decode_parallel(has_data_n, has_dav, has_atn, has_eoi, has_srq)
index 02d70a9c299aa36ce5fffcb6fc41075715cfeb9c..096ffc95a41428b9246f19899f5b96e799ce178f 100644 (file)
@@ -31,7 +31,8 @@ class Decoder(srd.Decoder):
     desc = 'NEC infrared remote control protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'NEC infrared remote control protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['ir_nec']
+    outputs = []
+    tags = ['IR']
     channels = (
         {'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
     )
@@ -110,7 +111,6 @@ class Decoder(srd.Decoder):
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
-        self.active = 0 if self.options['polarity'] == 'active-low' else 1
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -163,7 +163,9 @@ class Decoder(srd.Decoder):
         cd_count = None
         if self.options['cd_freq']:
             cd_count = int(self.samplerate / self.options['cd_freq']) + 1
         cd_count = None
         if self.options['cd_freq']:
             cd_count = int(self.samplerate / self.options['cd_freq']) + 1
-            prev_ir = None
+        prev_ir = None
+
+        self.active = 0 if self.options['polarity'] == 'active-low' else 1
 
         while True:
             # Detect changes in the presence of an active input signal.
 
         while True:
             # Detect changes in the presence of an active input signal.
index fa56dbca7ddbdc89d3251f0612a96615e221829d..4a8c958d88bb627be9f3810c132da3e5a3c6f053 100644 (file)
@@ -69,7 +69,7 @@ command = {
         12: ['Standby', 'StBy'],
         13: ['Mute', 'M'],
         14: ['Personal preferences', 'PP'],
         12: ['Standby', 'StBy'],
         13: ['Mute', 'M'],
         14: ['Personal preferences', 'PP'],
-        14: ['Display', 'Disp'],
+        15: ['Display', 'Disp'],
         16: ['Volume up', 'Vol+'],
         17: ['Volume down', 'Vol-'],
         18: ['Brightness up', 'Br+'],
         16: ['Volume up', 'Vol+'],
         17: ['Volume down', 'Vol-'],
         18: ['Brightness up', 'Br+'],
index 60a94160498a3beac0e74f31c1eec5b67c57d288..bbe0ca76c70f35ec02ac859a7e94cd04fac40be1 100644 (file)
@@ -31,7 +31,8 @@ class Decoder(srd.Decoder):
     desc = 'RC-5 infrared remote control protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'RC-5 infrared remote control protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['ir_rc5']
+    outputs = []
+    tags = ['IR']
     channels = (
         {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'},
     )
     channels = (
         {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'},
     )
@@ -60,13 +61,12 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
 
     def reset(self):
         self.samplerate = None
-        self.samplenum = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
-        self.old_ir = 1 if self.options['polarity'] == 'active-low' else 0
+        self.next_edge = 'l' if self.options['polarity'] == 'active-low' else 'h'
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -142,11 +142,7 @@ class Decoder(srd.Decoder):
             raise SamplerateError('Cannot decode without samplerate.')
         while True:
 
             raise SamplerateError('Cannot decode without samplerate.')
         while True:
 
-            (self.ir,) = self.wait()
-
-            # Wait for any edge (rising or falling).
-            if self.old_ir == self.ir:
-                continue
+            (self.ir,) = self.wait({0: self.next_edge})
 
             # State machine.
             if self.state == 'IDLE':
 
             # State machine.
             if self.state == 'IDLE':
@@ -154,7 +150,7 @@ class Decoder(srd.Decoder):
                 self.edges.append(self.samplenum)
                 self.bits.append([self.samplenum, bit])
                 self.state = 'MID1'
                 self.edges.append(self.samplenum)
                 self.bits.append([self.samplenum, bit])
                 self.state = 'MID1'
-                self.old_ir = self.ir
+                self.next_edge = 'l' if self.ir else 'h'
                 continue
             edge = self.edge_type()
             if edge == 'e':
                 continue
             edge = self.edge_type()
             if edge == 'e':
@@ -183,4 +179,4 @@ class Decoder(srd.Decoder):
                 self.handle_bits()
                 self.reset_decoder_state()
 
                 self.handle_bits()
                 self.reset_decoder_state()
 
-            self.old_ir = self.ir
+            self.next_edge = 'l' if self.ir else 'h'
diff --git a/decoders/ir_rc6/__init__.py b/decoders/ir_rc6/__init__.py
new file mode 100644 (file)
index 0000000..b2cb9f9
--- /dev/null
@@ -0,0 +1,24 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+RC-6 is a biphase/manchester based infrared remote control protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/ir_rc6/pd.py b/decoders/ir_rc6/pd.py
new file mode 100644 (file)
index 0000000..e195dbd
--- /dev/null
@@ -0,0 +1,205 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'ir_rc6'
+    name = 'IR RC-6'
+    longname = 'IR RC-6'
+    desc = 'RC-6 infrared remote control protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['IR']
+    channels = (
+        {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'},
+    )
+    options = (
+        {'id': 'polarity', 'desc': 'Polarity', 'default': 'auto',
+            'values': ('auto', 'active-low', 'active-high')},
+    )
+    annotations = (
+        ('bit', 'Bit'),
+        ('sync', 'Sync'),
+        ('startbit', 'Startbit'),
+        ('field', 'Field'),
+        ('togglebit', 'Togglebit'),
+        ('address', 'Address'),
+        ('command', 'Command'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0,)),
+        ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
+        self.edges, self.deltas, self.bits = [], [], []
+        self.state = 'IDLE'
+        self.mode = 0
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+            # One bit: 0.889ms (one half low, one half high).
+            self.halfbit = int((self.samplerate * 0.000889) / 2.0)
+
+    def putb(self, bit, data):
+        self.put(bit[0], bit[1], self.out_ann, data)
+
+    def putbits(self, bit1, bit2, data):
+        self.put(bit1[0], bit2[1], self.out_ann, data)
+
+    def putx(self, ss, es, data):
+        self.put(ss, es, self.out_ann, data)
+
+    def handle_bit(self):
+        if len(self.bits) != 6:
+            return
+        if self.bits[0][2] == 8 and self.bits[0][3] == 1:
+            self.putb(self.bits[0], [1, ['Synchronisation', 'Sync']])
+        else:
+            return
+        if self.bits[1][3] == 1:
+            self.putb(self.bits[1], [2, ['Startbit', 'Start']])
+        else:
+            return
+        self.mode = sum([self.bits[2 + i][3] << (2 - i) for i in range(3)])
+        self.putbits(self.bits[2], self.bits[4], [3, ['Field: %d' % self.mode]])
+        self.putb(self.bits[5], [4, ['Toggle: %d' % self.bits[5][3]]])
+
+    def handle_package(self):
+        # Sync and start bits have to be 1.
+        if self.bits[0][3] == 0 or self.bits[1][3] == 0:
+            return
+        if len(self.bits) <= 6:
+            return
+
+        if self.mode == 0 and len(self.bits) == 22: # Mode 0 standard
+            value = sum([self.bits[6 + i][3] << (7 - i) for i in range(8)])
+            self.putbits(self.bits[6], self.bits[13], [5, ['Address: %0.2X' % value]])
+
+            value = sum([self.bits[14 + i][3] << (7 - i) for i in range(8)])
+            self.putbits(self.bits[14], self.bits[21], [6, ['Data: %0.2X' % value]])
+
+            self.bits = []
+
+        if self.mode == 6 and len(self.bits) >= 15: # Mode 6
+            if self.bits[6][3] == 0: # Short addr, Mode 6A
+                value = sum([self.bits[6 + i][3] << (7 - i) for i in range(8)])
+                self.putbits(self.bits[6], self.bits[13], [5, ['Address: %0.2X' % value]])
+
+                num_data_bits = len(self.bits) - 14
+                value = sum([self.bits[14 + i][3] << (num_data_bits - 1 - i) for i in range(num_data_bits)])
+                self.putbits(self.bits[14], self.bits[-1], [6, ['Data: %X' % value]])
+
+                self.bits = []
+
+            elif len(self.bits) >= 23: # Long addr, Mode 6B
+                value = sum([self.bits[6 + i][3] << (15 - i) for i in range(16)])
+                self.putbits(self.bits[6], self.bits[21], [5, ['Address: %0.2X' % value]])
+
+                num_data_bits = len(self.bits) - 22
+                value = sum([self.bits[22 + i][3] << (num_data_bits - 1 - i) for i in range(num_data_bits)])
+                self.putbits(self.bits[22], self.bits[-1], [6, ['Data: %X' % value]])
+
+                self.bits = []
+
+    def decode(self):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        value = 0
+        num_edges = -1
+        self.invert = False
+
+        while True:
+            conditions = [{0: 'e'}]
+            if self.state == 'DATA':
+                conditions.append({'skip': self.halfbit * 6})
+            (self.ir,) = self.wait(conditions)
+
+            if len(conditions) == 2:
+                if self.matched[1]:
+                    self.state = 'IDLE'
+
+            self.edges.append(self.samplenum)
+            if len(self.edges) < 2:
+                continue
+
+            delta = (self.edges[-1] - self.edges[-2]) / self.halfbit
+            delta = int(delta + 0.5)
+            self.deltas.append(delta)
+
+            if len(self.deltas) < 2:
+                continue
+
+            if self.deltas[-2:] == [6, 2]:
+                self.state = 'SYNC'
+                num_edges = 0
+                self.bits = []
+
+                if self.options['polarity'] == 'auto':
+                    value = 1
+                else:
+                    value = self.ir if self.options['polarity'] == 'active-high' else 1 - self.ir
+
+                self.bits.append((self.edges[-3], self.edges[-1], 8, value))
+                self.invert = self.ir == 0
+                self.putb(self.bits[-1], [0, ['%d' % value]]) # Add bit.
+
+            if (num_edges % 2) == 0: # Only count every second edge.
+                if self.deltas[-2] in [1, 2, 3] and self.deltas[-1] in [1, 2, 3, 6]:
+                    self.state = 'DATA'
+                    if self.deltas[-2] != self.deltas[-1]:
+                        # Insert border between 2 bits.
+                        self.edges.insert(-1, self.edges[-2] + self.deltas[-2] * self.halfbit)
+                        total = self.deltas[-1]
+                        self.deltas[-1] = self.deltas[-2]
+                        self.deltas.append(total - self.deltas[-1])
+
+                        self.bits.append((self.edges[-4], self.edges[-2], self.deltas[-2] * 2, value))
+
+                        num_edges += 1
+                    else:
+                        self.bits.append((self.edges[-3], self.edges[-1], self.deltas[-1] * 2, value))
+
+                    self.putb(self.bits[-1], [0, ['%d' % value]]) # Add bit.
+
+            if len(self.bits) > 0:
+                self.handle_bit()
+                if self.state == 'IDLE':
+                    self.handle_package()
+
+            if self.options['polarity'] == 'auto':
+                value = self.ir if self.invert else 1 - self.ir
+            else:
+                value = self.ir if self.options['polarity'] == 'active-low' else 1 - self.ir
+
+            num_edges += 1
index 63a0fff1c4c87c826e58e1d0cc7ff262029938ad..3394ad759bc2f03dcc1ba5395632da961e854d84 100644 (file)
@@ -21,6 +21,7 @@
 This protocol decoder retrieves the timing jitter between two digital signals.
 
 It allows to define a clock source channel and a resulting signal channel.
 This protocol decoder retrieves the timing jitter between two digital signals.
 
 It allows to define a clock source channel and a resulting signal channel.
+
 Each time a significant edge is detected in the clock source, we calculate the
 elapsed time before the resulting signal answers and report the timing jitter.
 '''
 Each time a significant edge is detected in the clock source, we calculate the
 elapsed time before the resulting signal answers and report the timing jitter.
 '''
index f492a9fa077c9027965a856b8809dabee2699191..5343fbf0fbc069145514b916b7deb2e1f9d32a91 100644 (file)
@@ -37,7 +37,8 @@ class Decoder(srd.Decoder):
     desc = 'Retrieves the timing jitter between two digital signals.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Retrieves the timing jitter between two digital signals.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['jitter']
+    outputs = []
+    tags = ['Clock/timing', 'Util']
     channels = (
         {'id': 'clk', 'name': 'Clock', 'desc': 'Clock reference channel'},
         {'id': 'sig', 'name': 'Resulting signal', 'desc': 'Resulting signal controlled by the clock'},
     channels = (
         {'id': 'clk', 'name': 'Clock', 'desc': 'Clock reference channel'},
         {'id': 'sig', 'name': 'Resulting signal', 'desc': 'Resulting signal controlled by the clock'},
index 2d349bcb16b03479cd01bccaa6d309486580fa39..618613e403ef568f8801cd2b9b87abffbc8d7d33 100644 (file)
@@ -62,6 +62,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['jtag']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['jtag']
+    tags = ['Debug/trace']
     channels = (
         {'id': 'tdi',  'name': 'TDI',  'desc': 'Test data input'},
         {'id': 'tdo',  'name': 'TDO',  'desc': 'Test data output'},
     channels = (
         {'id': 'tdi',  'name': 'TDI',  'desc': 'Test data input'},
         {'id': 'tdo',  'name': 'TDO',  'desc': 'Test data output'},
index ba5db2a05897c3291b5b4c70c8221b4976938568..5a537c993cd22befbe4868e2ca1a9a9789b9fdb7 100644 (file)
@@ -191,12 +191,13 @@ regs_items = {
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'jtag_ejtag'
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'jtag_ejtag'
-    name = 'JTAG / EJTAG (MIPS)'
+    name = 'JTAG / EJTAG'
     longname = 'Joint Test Action Group / EJTAG (MIPS)'
     desc = 'MIPS EJTAG protocol.'
     license = 'gplv2+'
     inputs = ['jtag']
     longname = 'Joint Test Action Group / EJTAG (MIPS)'
     desc = 'MIPS EJTAG protocol.'
     license = 'gplv2+'
     inputs = ['jtag']
-    outputs = ['jtag_ejtag']
+    outputs = []
+    tags = ['Debug/trace']
     annotations = (
         ('instruction', 'Instruction'),
     ) + regs_items['ann'] + (
     annotations = (
         ('instruction', 'Instruction'),
     ) + regs_items['ann'] + (
@@ -206,9 +207,9 @@ class Decoder(srd.Decoder):
     )
     annotation_rows = (
         ('instructions', 'Instructions', (0,)),
     )
     annotation_rows = (
         ('instructions', 'Instructions', (0,)),
-        ('regs', 'Registers', regs_items['rows_range']),
         ('control_fields_in', 'Control fields in', (10,)),
         ('control_fields_out', 'Control fields out', (11,)),
         ('control_fields_in', 'Control fields in', (10,)),
         ('control_fields_out', 'Control fields out', (11,)),
+        ('regs', 'Registers', regs_items['rows_range']),
         ('pracc', 'PrAcc', (12,)),
     )
 
         ('pracc', 'PrAcc', (12,)),
     )
 
@@ -223,7 +224,7 @@ class Decoder(srd.Decoder):
         self.put(self.ss, self.es, self.out_ann, data)
 
     def put_at(self, ss: int, es: int, data):
         self.put(self.ss, self.es, self.out_ann, data)
 
     def put_at(self, ss: int, es: int, data):
-        self.put(ss, es, self.out_ann, data);
+        self.put(ss, es, self.out_ann, data)
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -247,14 +248,14 @@ class Decoder(srd.Decoder):
         s += 'Store' if pracc_write else 'Load/Fetch'
 
         if pracc_write:
         s += 'Store' if pracc_write else 'Load/Fetch'
 
         if pracc_write:
-            if self.pracc_state.address_out != None:
+            if self.pracc_state.address_out is not None:
                 s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out)
                 s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out)
-            if self.pracc_state.data_out != None:
+            if self.pracc_state.data_out is not None:
                 s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_out)
         else:
                 s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_out)
         else:
-            if self.pracc_state.address_out != None:
+            if self.pracc_state.address_out is not None:
                 s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out)
                 s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out)
-            if self.pracc_state.data_in != None:
+            if self.pracc_state.data_in is not None:
                 s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_in)
 
         self.pracc_state.reset()
                 s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_in)
 
         self.pracc_state.reset()
@@ -357,16 +358,16 @@ class Decoder(srd.Decoder):
 
     def handle_ir_tdi(self, val):
         code = bin2int(val[0])
 
     def handle_ir_tdi(self, val):
         code = bin2int(val[0])
-        hex = '0x{:02X}'.format(code)
+        hexval = '0x{:02X}'.format(code)
         if code in ejtag_insn:
             # Format instruction name.
             insn = ejtag_insn[code]
             s_short = insn[0]
         if code in ejtag_insn:
             # Format instruction name.
             insn = ejtag_insn[code]
             s_short = insn[0]
-            s_long = insn[0] + ': ' + insn[1] + ' (' + hex + ')'
+            s_long = insn[0] + ': ' + insn[1] + ' (' + hexval + ')'
             # Display it and select data register.
             self.put_current([Ann.INSTRUCTION, [s_long, s_short]])
         else:
             # Display it and select data register.
             self.put_current([Ann.INSTRUCTION, [s_long, s_short]])
         else:
-            self.put_current([Ann.INSTRUCTION, [hex, 'IR TDI ({})'.format(hex)]])
+            self.put_current([Ann.INSTRUCTION, [hexval, 'IR TDI ({})'.format(hexval)]])
         self.select_reg(code)
 
     def handle_new_state(self, new_state):
         self.select_reg(code)
 
     def handle_new_state(self, new_state):
index 593ce137bc8dc25aad587f442b3baec110dd8b20..82558b820f97577b768637f1da74c8e7921a0e63 100644 (file)
@@ -146,7 +146,8 @@ class Decoder(srd.Decoder):
     desc = 'ST STM32-specific JTAG protocol.'
     license = 'gplv2+'
     inputs = ['jtag']
     desc = 'ST STM32-specific JTAG protocol.'
     license = 'gplv2+'
     inputs = ['jtag']
-    outputs = ['jtag_stm32']
+    outputs = []
+    tags = ['Debug/trace']
     annotations = (
         ('item', 'Item'),
         ('field', 'Field'),
     annotations = (
         ('item', 'Item'),
         ('field', 'Field'),
diff --git a/decoders/lin/__init__.py b/decoders/lin/__init__.py
new file mode 100644 (file)
index 0000000..4e8c579
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'uart' PD and decodes the LIN
+(Local Interconnect Network) protocol.
+
+LIN is layered on top of the UART (async serial) protocol, with 8n1 settings.
+Bytes are sent LSB-first.
+'''
+
+from .pd import Decoder
diff --git a/decoders/lin/pd.py b/decoders/lin/pd.py
new file mode 100644 (file)
index 0000000..a8b1998
--- /dev/null
@@ -0,0 +1,247 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class LinFsm:
+    class State:
+        WaitForBreak = 'WAIT_FOR_BREAK'
+        Sync = 'SYNC'
+        Pid = 'PID'
+        Data = 'DATA'
+        Checksum = 'CHECKSUM'
+        Error = 'ERROR'
+
+    def transit(self, target_state):
+        if not self._transition_allowed(target_state):
+            return False
+        self.state = target_state
+        return True
+
+    def _transition_allowed(self, target_state):
+        if target_state == LinFsm.State.Error:
+            return True
+        return target_state in self.allowed_state[self.state]
+
+    def reset(self):
+        self.state = LinFsm.State.WaitForBreak
+        self.uart_idle_count = 0
+
+    def __init__(self):
+        a = dict()
+        a[LinFsm.State.WaitForBreak] = (LinFsm.State.Sync,)
+        a[LinFsm.State.Sync]         = (LinFsm.State.Pid,)
+        a[LinFsm.State.Pid]          = (LinFsm.State.Data,)
+        a[LinFsm.State.Data]         = (LinFsm.State.Data, LinFsm.State.Checksum)
+        a[LinFsm.State.Checksum]     = (LinFsm.State.WaitForBreak,)
+        a[LinFsm.State.Error]        = (LinFsm.State.Sync,)
+        self.allowed_state = a
+
+        self.state = None
+        self.uart_idle_count = 0
+        self.reset()
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'lin'
+    name = 'LIN'
+    longname = 'Local Interconnect Network'
+    desc = 'Local Interconnect Network (LIN) protocol.'
+    license = 'gplv2+'
+    inputs = ['uart']
+    outputs = []
+    tags = ['Automotive']
+    options = (
+        {'id': 'version', 'desc': 'Protocol version', 'default': 2, 'values': (1, 2)},
+    )
+    annotations = (
+        ('data', 'LIN data'),
+        ('control', 'Protocol info'),
+        ('error', 'Error descriptions'),
+        ('inline_error', 'Protocol violations and errors'),
+    )
+    annotation_rows = (
+        ('data', 'Data', (0, 1, 3)),
+        ('error', 'Error', (2,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.fsm = LinFsm()
+        self.lin_header = []
+        self.lin_rsp = []
+        self.lin_version = None
+        self.ss_block = None
+        self.es_block = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.lin_version = self.options['version']
+
+    def putx(self, data):
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+    def wipe_break_null_byte(self, value):
+        # Upon a break condition a null byte is received which must be ignored.
+        if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+            if len(self.lin_rsp):
+                value = self.lin_rsp.pop()[2]
+            else:
+                self.lin_header.pop()
+
+        if value != 0:
+            self.fsm.transit(LinFsm.State.Error)
+            self.handle_error(None)
+            return False
+
+        return True
+
+    def handle_uart_idle(self):
+        if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+            self.fsm.uart_idle_count += 1
+
+            if self.fsm.uart_idle_count == 2:
+                self.fsm.transit(LinFsm.State.Checksum)
+                self.handle_checksum()
+                self.fsm.reset()
+
+    def handle_wait_for_break(self, value):
+        self.wipe_break_null_byte(value)
+
+    def handle_break(self, value):
+        if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+            if self.wipe_break_null_byte(value):
+                self.fsm.transit(LinFsm.State.Checksum)
+                self.handle_checksum()
+
+        self.fsm.reset()
+        self.fsm.transit(LinFsm.State.Sync)
+
+        self.putx([1, ['Break condition', 'Break', 'Brk', 'B']])
+
+    def handle_sync(self, value):
+        self.fsm.transit(LinFsm.State.Pid)
+        self.lin_header.append((self.ss_block, self.es_block, value))
+
+    def handle_pid(self, value):
+        self.fsm.transit(LinFsm.State.Data)
+        self.lin_header.append((self.ss_block, self.es_block, value))
+
+    def handle_data(self, value):
+        self.lin_rsp.append((self.ss_block, self.es_block, value))
+
+    def handle_checksum(self):
+        sync = self.lin_header.pop(0) if len(self.lin_header) else None
+
+        self.put(sync[0], sync[1], self.out_ann, [0, ['Sync', 'S']])
+
+        if sync[2] != 0x55:
+            self.put(sync[0], sync[1], self.out_ann,
+                     [2, ['Sync is not 0x55', 'Not 0x55', '!= 0x55']])
+
+        pid = self.lin_header.pop(0) if len(self.lin_header) else None
+        checksum = self.lin_rsp.pop() if len(self.lin_rsp) else None
+
+        if pid:
+            id_ = pid[2] & 0x3F
+            parity = pid[2] >> 6
+
+            expected_parity = self.calc_parity(pid[2])
+            parity_valid = parity == expected_parity
+
+            if not parity_valid:
+                self.put(pid[0], pid[1], self.out_ann, [2, ['P != %d' % expected_parity]])
+
+            ann_class = 0 if parity_valid else 3
+            self.put(pid[0], pid[1], self.out_ann, [ann_class, [
+                'ID: %02X Parity: %d (%s)' % (id_, parity, 'ok' if parity_valid else 'bad'),
+                'ID: 0x%02X' % id_, 'I: %d' % id_
+            ]])
+
+        if len(self.lin_rsp):
+            checksum_valid = self.checksum_is_valid(pid[2], self.lin_rsp, checksum[2])
+
+            for b in self.lin_rsp:
+                self.put(b[0], b[1], self.out_ann, [0, ['Data: 0x%02X' % b[2], 'D: 0x%02X' % b[2]]])
+
+            ann_class = 0 if checksum_valid else 3
+            self.put(checksum[0], checksum[1], self.out_ann,
+                 [ann_class, ['Checksum: 0x%02X' % checksum[2], 'Checksum', 'Chk', 'C']])
+
+            if not checksum_valid:
+                self.put(checksum[0], checksum[1], self.out_ann, [2, ['Checksum invalid']])
+        else:
+            pass # No response.
+
+        self.lin_header.clear()
+        self.lin_rsp.clear()
+
+    def handle_error(self, dummy):
+        self.putx([3, ['Error', 'Err', 'E']])
+
+    def checksum_is_valid(self, pid, data, checksum):
+        if self.lin_version == 2:
+            id_ = pid & 0x3F
+
+            if id_ != 60 and id_ != 61:
+                checksum += pid
+
+        for d in data:
+            checksum += d[2]
+
+        carry_bits = int(checksum / 256)
+        checksum += carry_bits
+
+        return checksum & 0xFF == 0xFF
+
+    @staticmethod
+    def calc_parity(pid):
+        id_ = [((pid & 0x3F) >> i) & 1 for i in range(8)]
+
+        p0 = id_[0] ^ id_[1] ^ id_[2] ^ id_[4]
+        p1 = not (id_[1] ^ id_[3] ^ id_[4] ^ id_[5])
+
+        return (p0 << 0) | (p1 << 1)
+
+    def decode(self, ss, es, data):
+        ptype, rxtx, pdata = data
+
+        self.ss_block, self.es_block = ss, es
+
+        # Ignore all UART packets except the actual data packets or BREAK.
+        if ptype == 'IDLE':
+            self.handle_uart_idle()
+        if ptype == 'BREAK':
+            self.handle_break(pdata)
+        if ptype != 'DATA':
+            return
+
+        # We're only interested in the byte value (not individual bits).
+        pdata = pdata[0]
+
+        # Short LIN overview:
+        #  - Message begins with a BREAK (0x00) for at least 13 bittimes.
+        #  - Break is always followed by a SYNC byte (0x55).
+        #  - Sync byte is followed by a PID byte (Protected Identifier).
+        #  - PID byte is followed by 1 - 8 data bytes and a final checksum byte.
+
+        handler = getattr(self, 'handle_%s' % self.fsm.state.lower())
+        handler(pdata)
index 29237d7adaf17528fb3907330d48286cd247ff59..14df1b52e9a704bfc3a72981b9a11fc0ab38aa70 100644 (file)
@@ -46,7 +46,8 @@ class Decoder(srd.Decoder):
     desc = 'National LM75 (and compatibles) temperature sensor.'
     license = 'gplv2+'
     inputs = ['i2c']
     desc = 'National LM75 (and compatibles) temperature sensor.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['lm75']
+    outputs = []
+    tags = ['Sensor']
     options = (
         {'id': 'sensor', 'desc': 'Sensor type', 'default': 'lm75',
             'values': ('lm75',)},
     options = (
         {'id': 'sensor', 'desc': 'Sensor type', 'default': 'lm75',
             'values': ('lm75',)},
index 827ea94c435f3be3ee68d2bc1f44bbe66c710f22..5227758723364e9e73150011e578896905d157aa 100644 (file)
@@ -18,7 +18,7 @@
 ##
 
 '''
 ##
 
 '''
-LPC (Low-Pin Count) is a protocol for low-bandwidth devices used on
+LPC (Low Pin Count) is a protocol for low-bandwidth devices used on
 some PC mainboards, such as the "BIOS chip" or the so-called "Super I/O".
 '''
 
 some PC mainboards, such as the "BIOS chip" or the so-called "Super I/O".
 '''
 
index 452e647118131428a5cebf42fcf22f0ab24f7858..095c8b576f412a44be95e5d9a74e5554fab60a05 100644 (file)
@@ -98,11 +98,12 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'lpc'
     name = 'LPC'
     api_version = 3
     id = 'lpc'
     name = 'LPC'
-    longname = 'Low-Pin-Count'
+    longname = 'Low Pin Count'
     desc = 'Protocol for low-bandwidth devices on PC mainboards.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Protocol for low-bandwidth devices on PC mainboards.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['lpc']
+    outputs = []
+    tags = ['PC']
     channels = (
         {'id': 'lframe', 'name': 'LFRAME#', 'desc': 'Frame'},
         {'id': 'lclk',   'name': 'LCLK',    'desc': 'Clock'},
     channels = (
         {'id': 'lframe', 'name': 'LFRAME#', 'desc': 'Frame'},
         {'id': 'lclk',   'name': 'LCLK',    'desc': 'Clock'},
@@ -141,7 +142,6 @@ class Decoder(srd.Decoder):
     def reset(self):
         self.state = 'IDLE'
         self.oldlclk = -1
     def reset(self):
         self.state = 'IDLE'
         self.oldlclk = -1
-        self.samplenum = 0
         self.lad = -1
         self.addr = 0
         self.cur_nibble = 0
         self.lad = -1
         self.addr = 0
         self.cur_nibble = 0
index 830606185f71fddd94b004258681ee83415343dc..c3f1140198ecaae5f6439ab67ffa90369e406e78 100644 (file)
@@ -36,7 +36,8 @@ class Decoder(srd.Decoder):
     desc = 'Maple bus peripheral protocol for SEGA Dreamcast.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Maple bus peripheral protocol for SEGA Dreamcast.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['maple_bus']
+    outputs = []
+    tags = ['Retro computing']
     channels = (
         {'id': 'sdcka', 'name': 'SDCKA', 'desc': 'Data/clock line A'},
         {'id': 'sdckb', 'name': 'SDCKB', 'desc': 'Data/clock line B'},
     channels = (
         {'id': 'sdcka', 'name': 'SDCKA', 'desc': 'Data/clock line A'},
         {'id': 'sdckb', 'name': 'SDCKB', 'desc': 'Data/clock line B'},
index c8e99e1cc948e9fe548ab39158ff8aa905612b23..53067a679f060b7c3964f746aa0648585a9e6130 100644 (file)
@@ -45,10 +45,11 @@ class Decoder(srd.Decoder):
     id = 'max7219'
     name = 'MAX7219'
     longname = 'Maxim MAX7219/MAX7221'
     id = 'max7219'
     name = 'MAX7219'
     longname = 'Maxim MAX7219/MAX7221'
-    desc = '8-digit LED display driver.'
+    desc = 'Maxim MAX72xx series 8-digit LED display driver.'
     license = 'gplv2+'
     inputs = ['spi']
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['max7219']
+    outputs = []
+    tags = ['Display']
     annotations = (
         ('register', 'Registers written to the device'),
         ('digit', 'Digits displayed on the device'),
     annotations = (
         ('register', 'Registers written to the device'),
         ('digit', 'Digits displayed on the device'),
index 54c01ccb2f3d17d47b1a90f847d4566191ed04d5..b989a2a8e30457f7aff3173c11d78684f9310597 100644 (file)
 
 '''
 This protocol decoder de-multiplexes Intel MCS-48 (8039, 8048, etc.) external
 
 '''
 This protocol decoder de-multiplexes Intel MCS-48 (8039, 8048, etc.) external
-program memory accesses. This requires 14 channels: 8 for D0-D7 (data and
-lower 8 bits of address), 4 for A8-A11 (output on port P2), ALE and PSEN.
+program memory accesses.
+
+This requires 14 channels: 8 for D0-D7 (data and lower 8 bits of address),
+4 for A8-A11 (output on port P2), ALE and PSEN.
+
 An optional A12 is supported, which may be an arbitrary I/O pin driven by
 software (use case is dumping ROM of an HP 3478A).
 '''
 An optional A12 is supported, which may be an arbitrary I/O pin driven by
 software (use case is dumping ROM of an HP 3478A).
 '''
index 185fd89437ebde3197737d0688ce241855c30c9e..99b2efc8e868544dc194200946ba97ce45ca2c73 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'Intel MCS-48 external memory access protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Intel MCS-48 external memory access protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['mcs48']
+    outputs = []
+    tags = ['Retro computing']
     channels = (
         {'id': 'ale', 'name': 'ALE', 'desc': 'Address latch enable'},
         {'id': 'psen', 'name': '/PSEN', 'desc': 'Program store enable'},
     channels = (
         {'id': 'ale', 'name': 'ALE', 'desc': 'Address latch enable'},
         {'id': 'psen', 'name': '/PSEN', 'desc': 'Program store enable'},
index 25229129516c748548539b0775efa455af441a47..1d060b0b99150687ee3481ff91ec00fd895bc50b 100644 (file)
@@ -33,10 +33,11 @@ class Decoder(srd.Decoder):
     id = 'mdio'
     name = 'MDIO'
     longname = 'Management Data Input/Output'
     id = 'mdio'
     name = 'MDIO'
     longname = 'Management Data Input/Output'
-    desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.'
+    desc = 'MII management bus between MAC and PHY.'
     license = 'bsd'
     inputs = ['logic']
     outputs = ['mdio']
     license = 'bsd'
     inputs = ['logic']
     outputs = ['mdio']
+    tags = ['Networking']
     channels = (
         {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
         {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
     channels = (
         {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
         {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
@@ -66,7 +67,6 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.illegal_bus = 0
 
     def reset(self):
         self.illegal_bus = 0
-        self.samplenum = -1
         self.clause45_addr = -1 # Clause 45 is context sensitive.
         self.reset_decoder_state()
 
         self.clause45_addr = -1 # Clause 45 is context sensitive.
         self.reset_decoder_state()
 
index da2d045e4fc4a8f3993b47c97df5fd7cdce4532f..53f52d092edb88792f7923a0730d8772561b97ef 100644 (file)
@@ -19,6 +19,7 @@
 
 '''
 Microwire is a 3-wire half-duplex synchronous serial communication protocol.
 
 '''
 Microwire is a 3-wire half-duplex synchronous serial communication protocol.
+
 Originally from National Semiconductor, it is often used in EEPROM chips with
 device specific commands on top of the bit stream.
 
 Originally from National Semiconductor, it is often used in EEPROM chips with
 device specific commands on top of the bit stream.
 
index 4180ba2f318b1e3f661b72662a87c25379fc4a96..6650f383d2d57408cf283d84ca653893f9d556c6 100644 (file)
@@ -50,6 +50,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['microwire']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['microwire']
+    tags = ['Embedded/industrial']
     channels = (
         {'id': 'cs', 'name': 'CS', 'desc': 'Chip select'},
         {'id': 'sk', 'name': 'SK', 'desc': 'Clock'},
     channels = (
         {'id': 'cs', 'name': 'CS', 'desc': 'Chip select'},
         {'id': 'sk', 'name': 'SK', 'desc': 'Clock'},
index feb581c6684522c8d87a7fc449310403332976b3..ae35e1231ee2d6c1413efa2df6001beceb15d731 100644 (file)
@@ -32,7 +32,8 @@ class Decoder(srd.Decoder):
     desc = 'Musical Instrument Digital Interface (MIDI) protocol.'
     license = 'gplv2+'
     inputs = ['uart']
     desc = 'Musical Instrument Digital Interface (MIDI) protocol.'
     license = 'gplv2+'
     inputs = ['uart']
-    outputs = ['midi']
+    outputs = []
+    tags = ['Audio', 'PC']
     annotations = (
         ('text-verbose', 'Human-readable text (verbose)'),
         ('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'),
     annotations = (
         ('text-verbose', 'Human-readable text (verbose)'),
         ('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'),
index 0ab5c4aae23fb324d84bb22a4279e324118f09a6..90c7c19af8bc4e9be79e30d7a6d0439255e320f3 100644 (file)
@@ -38,7 +38,8 @@ class Decoder(srd.Decoder):
     desc = 'Miller encoding protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Miller encoding protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['miller']
+    outputs = []
+    tags = ['Encoding']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data signal'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data signal'},
     )
@@ -56,6 +57,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
 
     def metadata(self, key, value):
         self.samplerate = None
 
     def metadata(self, key, value):
index 60927f7324bb5d96c390f27837f1c4845ca7968a..f0dbe22a240f4cceb030405f3fa4b8849e3e598f 100644 (file)
@@ -24,10 +24,11 @@ class Decoder(srd.Decoder):
     id = 'mlx90614'
     name = 'MLX90614'
     longname = 'Melexis MLX90614'
     id = 'mlx90614'
     name = 'MLX90614'
     longname = 'Melexis MLX90614'
-    desc = 'Infrared Thermometer protocol.'
+    desc = 'Melexis MLX90614 infrared thermometer protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['mlx90614']
+    outputs = []
+    tags = ['IC', 'Sensor']
     annotations = (
         ('celsius', 'Temperature in degrees Celsius'),
         ('kelvin', 'Temperature in Kelvin'),
     annotations = (
         ('celsius', 'Temperature in degrees Celsius'),
         ('kelvin', 'Temperature in Kelvin'),
index d2307b9ec5506855331131c2e0d035b2dafc088d..1d012e4560170aa05b3cebfc47defa2f3a9d3b8f 100644 (file)
@@ -22,6 +22,7 @@ from math import ceil
 
 RX = 0
 TX = 1
 
 RX = 0
 TX = 1
+rxtx_channels = ('RX', 'TX')
 
 class No_more_data(Exception):
     '''This exception is a signal that we should stop parsing an ADU as there
 
 class No_more_data(Exception):
     '''This exception is a signal that we should stop parsing an ADU as there
@@ -124,18 +125,18 @@ class Modbus_ADU:
                 self.annotation_prefix + 'error',
                 'Message too short or not finished')
             self.hasError = True
                 self.annotation_prefix + 'error',
                 'Message too short or not finished')
             self.hasError = True
-        if self.hasError and self.parent.options['channel'] == 'RX':
-            # If we are on RX mode (so client->server and server->client
-            # messages can be seperated) we like to mark blocks containing
-            # errors. We don't do this in TX mode, because then we interpret
-            # each frame as both a client->server and server->client frame, and
+        if self.hasError and self.parent.options['scchannel'] != self.parent.options['cschannel']:
+            # If we are decoding different channels (so client->server and
+            # server->client messages can be separated) we like to mark blocks
+            # containing errors. We don't do this when decoding the same
+            # channel as both a client->server and server->client frame, and
             # one of those is bound to contain an error, making highlighting
             # frames useless.
             self.parent.puta(data[0].start, data[-1].end,
                              'error-indication', 'Frame contains error')
         if len(data) > 256:
             try:
             # one of those is bound to contain an error, making highlighting
             # frames useless.
             self.parent.puta(data[0].start, data[-1].end,
                              'error-indication', 'Frame contains error')
         if len(data) > 256:
             try:
-                self.puti(len(data) - 1, self.annotation_prefix + 'error',
+                self.puti(len(data) - 1, 'error',
                     'Modbus data frames are limited to 256 bytes')
             except No_more_data:
                 pass
                     'Modbus data frames are limited to 256 bytes')
             except No_more_data:
                 pass
@@ -819,6 +820,7 @@ class Decoder(srd.Decoder):
     license = 'gplv3+'
     inputs = ['uart']
     outputs = ['modbus']
     license = 'gplv3+'
     inputs = ['uart']
     outputs = ['modbus']
+    tags = ['Embedded/industrial']
     annotations = (
         ('sc-server-id', ''),
         ('sc-function', ''),
     annotations = (
         ('sc-server-id', ''),
         ('sc-function', ''),
@@ -842,8 +844,11 @@ class Decoder(srd.Decoder):
         ('error-indicator', 'Errors in frame', (14,)),
     )
     options = (
         ('error-indicator', 'Errors in frame', (14,)),
     )
     options = (
-        {'id': 'channel', 'desc': 'Server -> client channel', 'default': 'RX',
-            'values': ('RX', 'TX')},
+        {'id': 'scchannel', 'desc': 'Server -> client channel',
+            'default': rxtx_channels[0], 'values': rxtx_channels},
+        {'id': 'cschannel', 'desc': 'Client -> server channel',
+            'default': rxtx_channels[1], 'values': rxtx_channels},
+        {'id': 'framegap', 'desc': 'Inter-frame bit gap', 'default': 28},
     )
 
     def __init__(self):
     )
 
     def __init__(self):
@@ -907,7 +912,7 @@ class Decoder(srd.Decoder):
         # somewhere between seems fine.
         # A character is 11 bits long, so (3.5 + 1.5)/2 * 11 ~= 28
         # TODO: Display error for too short or too long.
         # somewhere between seems fine.
         # A character is 11 bits long, so (3.5 + 1.5)/2 * 11 ~= 28
         # TODO: Display error for too short or too long.
-        if (ss - ADU.last_read) <= self.bitlength * 28:
+        if (ss - ADU.last_read) <= self.bitlength * self.options['framegap']:
             ADU.add_data(ss, es, data)
         else:
             # It's been too long since the last part of the ADU!
             ADU.add_data(ss, es, data)
         else:
             # It's been too long since the last part of the ADU!
@@ -924,11 +929,13 @@ class Decoder(srd.Decoder):
     def decode(self, ss, es, data):
         ptype, rxtx, pdata = data
 
     def decode(self, ss, es, data):
         ptype, rxtx, pdata = data
 
+        # Ignore unknown/unsupported ptypes.
+        if ptype not in ('STARTBIT', 'DATA', 'STOPBIT'):
+            return
+
         # Decide what ADU(s) we need this packet to go to.
         # Note that it's possible to go to both ADUs.
         # Decide what ADU(s) we need this packet to go to.
         # Note that it's possible to go to both ADUs.
-        if rxtx == TX:
-            self.decode_adu(ss, es, data, 'Cs')
-        if rxtx == TX and self.options['channel'] == 'TX':
-            self.decode_adu(ss, es, data, 'Sc')
-        if rxtx == RX and self.options['channel'] == 'RX':
+        if rxtx_channels[rxtx] == self.options['scchannel']:
             self.decode_adu(ss, es, data, 'Sc')
             self.decode_adu(ss, es, data, 'Sc')
+        if rxtx_channels[rxtx] == self.options['cschannel']:
+            self.decode_adu(ss, es, data, 'Cs')
index 9a83b63a84b390df109cd55af1e694d88791da03..f6ff7188d4498f1232313d3fe418728e049efec1 100644 (file)
@@ -120,7 +120,8 @@ class Decoder(srd.Decoder):
     desc = 'Demodulated morse code protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Demodulated morse code protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['morse']
+    outputs = []
+    tags = ['Encoding']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index 8ccf47384a67f4b54b3f9b2c353aa545d0e9bd47..db3ca98e93b4b017feaf7d9eed298ccad9406104 100644 (file)
@@ -20,6 +20,8 @@
 import sigrokdecode as srd
 from .lists import *
 
 import sigrokdecode as srd
 from .lists import *
 
+TX, RX = range(2)
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'mrf24j40'
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'mrf24j40'
@@ -28,18 +30,33 @@ class Decoder(srd.Decoder):
     desc = 'IEEE 802.15.4 2.4 GHz RF tranceiver chip.'
     license = 'gplv2+'
     inputs = ['spi']
     desc = 'IEEE 802.15.4 2.4 GHz RF tranceiver chip.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['mrf24j40']
+    outputs = []
+    tags = ['IC', 'Wireless/RF']
     annotations = (
         ('sread', 'Short register read commands'),
         ('swrite', 'Short register write commands'),
         ('lread', 'Long register read commands'),
         ('lwrite', 'Long register write commands'),
         ('warning', 'Warnings'),
     annotations = (
         ('sread', 'Short register read commands'),
         ('swrite', 'Short register write commands'),
         ('lread', 'Long register read commands'),
         ('lwrite', 'Long register write commands'),
         ('warning', 'Warnings'),
+        ('tx-frame', 'TX frame'),
+        ('rx-frame', 'RX frame'),
+        ('tx-retry-1', '1x TX retry'),
+        ('tx-retry-2', '2x TX retry'),
+        ('tx-retry-3', '3x TX retry'),
+        ('tx-fail', 'TX fail (too many retries)'),
+        ('ccafail', 'CCAFAIL (channel busy)'),
     )
     annotation_rows = (
         ('read', 'Read', (0, 2)),
         ('write', 'Write', (1, 3)),
         ('warnings', 'Warnings', (4,)),
     )
     annotation_rows = (
         ('read', 'Read', (0, 2)),
         ('write', 'Write', (1, 3)),
         ('warnings', 'Warnings', (4,)),
+        ('tx-frames', 'TX frames', (5,)),
+        ('rx-frames', 'RX frames', (6,)),
+        ('tx-retries-1', '1x TX retries', (7,)),
+        ('tx-retries-2', '2x TX retries', (8,)),
+        ('tx-retries-3', '3x TX retries', (9,)),
+        ('tx-fails', 'TX fails', (10,)),
+        ('ccafails', 'CCAFAILs', (11,)),
     )
 
     def __init__(self):
     )
 
     def __init__(self):
@@ -47,8 +64,9 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.ss_cmd, self.es_cmd = 0, 0
 
     def reset(self):
         self.ss_cmd, self.es_cmd = 0, 0
-        self.mosi_bytes = []
-        self.miso_bytes = []
+        self.ss_frame, self.es_frame = [0, 0], [0, 0]
+        self.mosi_bytes, self.miso_bytes = [], []
+        self.framecache = [[], []]
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -67,10 +85,34 @@ class Decoder(srd.Decoder):
         write = self.mosi_bytes[0] & 0x1
         reg = (self.mosi_bytes[0] >> 1) & 0x3f
         reg_desc = sregs.get(reg, 'illegal')
         write = self.mosi_bytes[0] & 0x1
         reg = (self.mosi_bytes[0] >> 1) & 0x3f
         reg_desc = sregs.get(reg, 'illegal')
+        for rxtx in (RX, TX):
+            if self.framecache[rxtx] == []:
+                continue
+            bit0 = self.mosi_bytes[1] & (1 << 0)
+            if rxtx == TX and not (reg_desc == 'TXNCON' and bit0 == 1):
+                continue
+            if rxtx == RX and not (reg_desc == 'RXFLUSH' and bit0 == 1):
+                continue
+            idx = 5 if rxtx == TX else 6
+            xmitdir = 'TX' if rxtx == TX else 'RX'
+            frame = ' '.join(['%02X' % b for b in self.framecache[rxtx]])
+            self.put(self.ss_frame[rxtx], self.es_frame[rxtx], self.out_ann,
+                [idx, ['%s frame: %s' % (xmitdir, frame)]])
+            self.framecache[rxtx] = []
         if write:
             self.putx([1, ['%s: %#x' % (reg_desc, self.mosi_bytes[1])]])
         else:
             self.putx([0, ['%s: %#x' % (reg_desc, self.miso_bytes[1])]])
         if write:
             self.putx([1, ['%s: %#x' % (reg_desc, self.mosi_bytes[1])]])
         else:
             self.putx([0, ['%s: %#x' % (reg_desc, self.miso_bytes[1])]])
+            numretries = (self.miso_bytes[1] & 0xc0) >> 6
+            if reg_desc == 'TXSTAT' and numretries > 0:
+                txfail = 1 if ((self.miso_bytes[1] & (1 << 0)) != 0) else 0
+                idx = 6 + numretries + txfail
+                if txfail:
+                    self.putx([idx, ['TX fail (>= 4 retries)', 'TX fail']])
+                else:
+                    self.putx([idx, ['TX retries: %d' % numretries]])
+            if reg_desc == 'TXSTAT' and (self.miso_bytes[1] & (1 << 5)) != 0:
+                self.putx([11, ['CCAFAIL (channel busy)', 'CCAFAIL']])
 
     def handle_long(self):
         dword = self.mosi_bytes[0] << 8 | self.mosi_bytes[1]
 
     def handle_long(self):
         dword = self.mosi_bytes[0] << 8 | self.mosi_bytes[1]
@@ -98,6 +140,16 @@ class Decoder(srd.Decoder):
         else:
             self.putx([2, ['%s: %#x' % (reg_desc, self.miso_bytes[2])]])
 
         else:
             self.putx([2, ['%s: %#x' % (reg_desc, self.miso_bytes[2])]])
 
+        for rxtx in (RX, TX):
+            if rxtx == RX and reg_desc[:3] != 'RX:':
+                continue
+            if rxtx == TX and reg_desc[:3] != 'TX:':
+                continue
+            if len(self.framecache[rxtx]) == 0:
+                self.ss_frame[rxtx] = self.ss_cmd
+            self.es_frame[rxtx] = self.es_cmd
+            self.framecache[rxtx] += [self.mosi_bytes[2]] if rxtx == TX else [self.miso_bytes[2]]
+
     def decode(self, ss, es, data):
         ptype = data[0]
         if ptype == 'CS-CHANGE':
     def decode(self, ss, es, data):
         ptype = data[0]
         if ptype == 'CS-CHANGE':
index 1e9045519c90ce41bfecef1dd3b7ac8b9f7872d8..e9617782d6154471c48c937b7a0ede40dc84073c 100644 (file)
@@ -66,7 +66,8 @@ class Decoder(srd.Decoder):
     desc = 'Digital Thermal Orientation Sensor (DTOS) protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
     desc = 'Digital Thermal Orientation Sensor (DTOS) protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['mxc6225xu']
+    outputs = []
+    tags = ['IC', 'Sensor']
     annotations = (
         ('text', 'Human-readable text'),
     )
     annotations = (
         ('text', 'Human-readable text'),
     )
diff --git a/decoders/nes_gamepad/__init__.py b/decoders/nes_gamepad/__init__.py
new file mode 100644 (file)
index 0000000..e754374
--- /dev/null
@@ -0,0 +1,54 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the button states
+of an NES gamepad.
+
+The SPI decoder needs to be configured as follows:
+
+Clock polarity = 1
+Clock phase    = 0
+Bit order      = msb-first
+Word size      = 8
+
+Chip-select is not used and must not be assigned to any channel.
+
+Hardware setup is as follows:
+        ___
+   GND |o  \
+   CUP |o o| VCC
+ OUT 0 |o o| D3
+    D1 |o o| D4
+       -----
+NES Gamepad Connector
+
+VCC   - Power 5V
+GND   - Ground
+CUP   - Shift register clock (CLK)
+OUT 0 - Shift register latch (optional)
+D1    - Gamepad data (MOSI)
+D3    - Data (unused)
+D4    - Data (unused)
+
+Data pins D3 and D4 are not used by the standard gamepad but
+by special controllers like the Nintento Zapper light gun.
+'''
+
+from .pd import Decoder
diff --git a/decoders/nes_gamepad/pd.py b/decoders/nes_gamepad/pd.py
new file mode 100644 (file)
index 0000000..17c57ca
--- /dev/null
@@ -0,0 +1,105 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'nes_gamepad'
+    name = 'NES gamepad'
+    longname = 'Nintendo Entertainment System gamepad'
+    desc = 'NES gamepad button states.'
+    license = 'gplv2+'
+    inputs = ['spi']
+    outputs = []
+    tags = ['Retro computing']
+    options = (
+        # Currently only the standard controller is supported. This might be
+        # extended by special controllers like the Nintendo Zapper light gun.
+        {'id': 'variant', 'desc': 'Gamepad variant',
+            'default': 'Standard gamepad', 'values': ('Standard gamepad',)},
+    )
+    annotations = (
+        ('button', 'Button states'),
+        ('no-press', 'No button press'),
+        ('not-connected', 'Gamepad unconnected')
+    )
+    annotation_rows = (
+        ('buttons', 'Button states', (0,)),
+        ('no-press', 'No button press', (1,)),
+        ('not-connected', 'Gamepad unconnected', (2,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.variant = None
+        self.ss_block = None
+        self.es_block = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.variant = self.options['variant']
+
+    def putx(self, data):
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+    def handle_data(self, value):
+        if value == 0xFF:
+          self.putx([1, ['No button is pressed']])
+          return
+
+        if value == 0x00:
+          self.putx([2, ['Gamepad is not connected']])
+          return
+
+        buttons = [
+            'A',
+            'B',
+            'Select',
+            'Start',
+            'North',
+            'South',
+            'West',
+            'East'
+        ]
+
+        bits = format(value, '08b')
+        button_str = ''
+
+        for b in enumerate(bits):
+            button_index = b[0]
+            button_is_pressed = b[1] == '0'
+
+            if button_is_pressed:
+                if button_str != '':
+                    button_str += ' + '
+                button_str += buttons[button_index]
+
+        self.putx([0, ['%s' % button_str]])
+
+    def decode(self, ss, es, data):
+        ptype, mosi, miso = data
+        self.ss_block, self.es_block = ss, es
+
+        if ptype != 'DATA':
+          return
+
+        self.handle_data(miso)
index 221b968ea2ac0db672e2a5a30761120b16f86f3b..64fe5778ca15bc445b5fe88795143388aa963059 100644 (file)
@@ -62,11 +62,12 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'nrf24l01'
     name = 'nRF24L01(+)'
     api_version = 3
     id = 'nrf24l01'
     name = 'nRF24L01(+)'
-    longname = 'Nordic Semiconductor nRF24L01/nRF24L01+'
-    desc = '2.4GHz transceiver chip.'
+    longname = 'Nordic Semiconductor nRF24L01(+)'
+    desc = '2.4GHz RF transceiver chip.'
     license = 'gplv2+'
     inputs = ['spi']
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['nrf24l01']
+    outputs = []
+    tags = ['IC', 'Wireless/RF']
     options = (
         {'id': 'chip', 'desc': 'Chip type',
             'default': 'nrf24l01', 'values': ('nrf24l01', 'xn297')},
     options = (
         {'id': 'chip', 'desc': 'Chip type',
             'default': 'nrf24l01', 'values': ('nrf24l01', 'xn297')},
index 4f006f2f8f0816546124b288982781eec5cb3f49..59b102890dcd0ea507f954d18084847562d0cf97 100644 (file)
@@ -27,7 +27,8 @@ class Decoder(srd.Decoder):
     desc = 'Nintendo Wii Nunchuk controller protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
     desc = 'Nintendo Wii Nunchuk controller protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['nunchuck']
+    outputs = []
+    tags = ['Sensor']
     annotations = \
         tuple(('reg-0x%02X' % i, 'Register 0x%02X' % i) for i in range(6)) + (
         ('bit-bz', 'BZ bit'),
     annotations = \
         tuple(('reg-0x%02X' % i, 'Register 0x%02X' % i) for i in range(6)) + (
         ('bit-bz', 'BZ bit'),
index 69b570fc412007b57a70e026ed62190f3d3e8c8b..abd55671416df0ae9fb1985e0c89c8389b717da6 100644 (file)
@@ -45,10 +45,10 @@ as an example.
  - owr (1-Wire signal line)
 
 Options:
  - owr (1-Wire signal line)
 
 Options:
-1-Wire is an asynchronous protocol with fixed timing values, so the decoder must
-know the samplerate.
+1-Wire is an asynchronous protocol with fixed timing values, so the decoder
+must know the samplerate.
 Two speeds are available: normal and overdrive. The decoder detects when
 Two speeds are available: normal and overdrive. The decoder detects when
-switching from one to another but the user can set which to start decoding with:
+switching speed, but the user can set which to start decoding with:
 
  - overdrive (to decode starting with overdrive speed)
 '''
 
  - overdrive (to decode starting with overdrive speed)
 '''
index 72aea83f7248952e91cda83823aa3605f7d09156..564d6f0bc86895725b37116f683dbfe15bfae9eb 100644 (file)
@@ -97,6 +97,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['onewire_link']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['onewire_link']
+    tags = ['Embedded/industrial']
     channels = (
         {'id': 'owr', 'name': 'OWR', 'desc': '1-Wire signal line'},
     )
     channels = (
         {'id': 'owr', 'name': 'OWR', 'desc': '1-Wire signal line'},
     )
index 5d850b5897abc5912843d20d5c2dc27335391dba..ef302aea4435cfde5f4e6b1d921fa17385546430 100644 (file)
@@ -21,14 +21,16 @@ import sigrokdecode as srd
 
 # Dictionary of ROM commands and their names, next state.
 command = {
 
 # Dictionary of ROM commands and their names, next state.
 command = {
-    0x33: ['Read ROM'              , 'GET ROM'   ],
-    0x0f: ['Conditional read ROM'  , 'GET ROM'   ],
-    0xcc: ['Skip ROM'              , 'TRANSPORT' ],
-    0x55: ['Match ROM'             , 'GET ROM'   ],
-    0xf0: ['Search ROM'            , 'SEARCH ROM'],
-    0xec: ['Conditional search ROM', 'SEARCH ROM'],
-    0x3c: ['Overdrive skip ROM'    , 'TRANSPORT' ],
-    0x69: ['Overdrive match ROM'   , 'GET ROM'   ],
+    0x33: ['Read ROM'                  , 'GET ROM'   ],
+    0x0f: ['Conditional read ROM'      , 'GET ROM'   ],
+    0xcc: ['Skip ROM'                  , 'TRANSPORT' ],
+    0x55: ['Match ROM'                 , 'GET ROM'   ],
+    0xf0: ['Search ROM'                , 'SEARCH ROM'],
+    0xec: ['Conditional search ROM'    , 'SEARCH ROM'],
+    0x3c: ['Overdrive skip ROM'        , 'TRANSPORT' ],
+    0x69: ['Overdrive match ROM'       , 'GET ROM'   ],
+    0xa5: ['Resume'                    , 'TRANSPORT' ],
+    0x96: ['DS2408: Disable Test Mode' , 'GET ROM'   ],
 }
 
 class Decoder(srd.Decoder):
 }
 
 class Decoder(srd.Decoder):
@@ -40,6 +42,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['onewire_link']
     outputs = ['onewire_network']
     license = 'gplv2+'
     inputs = ['onewire_link']
     outputs = ['onewire_network']
+    tags = ['Embedded/industrial']
     annotations = (
         ('text', 'Human-readable text'),
     )
     annotations = (
         ('text', 'Human-readable text'),
     )
index b4971b621d3a330391c92e5388bfb35297794ffb..5fc8d01252b6f0abc30b2c04b1efd3d4cfffffa8 100644 (file)
@@ -54,6 +54,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['ook']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['ook']
+    tags = ['Encoding']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
@@ -189,7 +190,7 @@ class Decoder(srd.Decoder):
         # Filter incoming pulses to remove random noise.
         if self.state == 'DECODE_TIMEOUT':
             self.preamble = []
         # Filter incoming pulses to remove random noise.
         if self.state == 'DECODE_TIMEOUT':
             self.preamble = []
-            self.edge_count == 0
+            self.edge_count = 0
             self.word_first = self.samplenum
             self.sample_first = self.samplenum - self.samplenumber_last
             self.state = 'WAITING_FOR_PREAMBLE'
             self.word_first = self.samplenum
             self.sample_first = self.samplenum - self.samplenumber_last
             self.state = 'WAITING_FOR_PREAMBLE'
@@ -205,7 +206,7 @@ class Decoder(srd.Decoder):
                 self.preamble = [] # Clear buffer.
                 self.preamble.append([self.samplenumber_last,
                                      pre_samples, state])
                 self.preamble = [] # Clear buffer.
                 self.preamble.append([self.samplenumber_last,
                                      pre_samples, state])
-                self.edge_count == 0
+                self.edge_count = 0
                 self.samplenumber_last = self.samplenum
                 self.word_first = self.samplenum
             else:
                 self.samplenumber_last = self.samplenum
                 self.word_first = self.samplenum
             else:
index c248726b91d928a6a2fe1ce53b5c363ae6303a8d..225f59837029a0170fe7c6c091323603625fa80a 100644 (file)
@@ -30,6 +30,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['ook']
     outputs = []
     license = 'gplv2+'
     inputs = ['ook']
     outputs = []
+    tags = ['Sensor']
     annotations = (
         ('bit', 'Bit'),
         ('field', 'Field'),
     annotations = (
         ('bit', 'Bit'),
         ('field', 'Field'),
index 37853319a358276912a18b7187a07cb0cf105edd..f985b96f1909e96edc0face8294df51068cf0375 100644 (file)
@@ -29,6 +29,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['ook']
     outputs = ['ook']
     license = 'gplv2+'
     inputs = ['ook']
     outputs = ['ook']
+    tags = ['Encoding']
     annotations = (
         ('bit', 'Bit'),
         ('ref', 'Reference'),
     annotations = (
         ('bit', 'Bit'),
         ('ref', 'Reference'),
index ecaa6807f752c20d0ade4bce2c56081ba27e0d58..6c931147a1eddab7a793df702b5109f0116942ae 100644 (file)
@@ -31,7 +31,8 @@ class Decoder(srd.Decoder):
     desc = 'Bluetooth RF module with Serial Port Profile (SPP).'
     license = 'gplv2+'
     inputs = ['uart']
     desc = 'Bluetooth RF module with Serial Port Profile (SPP).'
     license = 'gplv2+'
     inputs = ['uart']
-    outputs = ['pan1321']
+    outputs = []
+    tags = ['Wireless/RF']
     annotations = (
         ('text-verbose', 'Human-readable text (verbose)'),
         ('text', 'Human-readable text'),
     annotations = (
         ('text-verbose', 'Human-readable text (verbose)'),
         ('text', 'Human-readable text'),
index 0e81344855a204c2407cc285f6102e2f3dc14943..405cdebf9170f684daeb5b9a9d21f248ec0ba669 100644 (file)
@@ -75,6 +75,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['parallel']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['parallel']
+    tags = ['Util']
     optional_channels = channel_list(NUM_CHANNELS)
     options = (
         {'id': 'clock_edge', 'desc': 'Clock edge to sample on',
     optional_channels = channel_list(NUM_CHANNELS)
     options = (
         {'id': 'clock_edge', 'desc': 'Clock edge to sample on',
diff --git a/decoders/pca9571/__init__.py b/decoders/pca9571/__init__.py
new file mode 100644 (file)
index 0000000..28152d9
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Mickael Bosch <mickael.bosch@linux.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes the NXP Semiconductors 
+PCA9571 8-bit I²C output expander protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/pca9571/pd.py b/decoders/pca9571/pd.py
new file mode 100644 (file)
index 0000000..df309e9
--- /dev/null
@@ -0,0 +1,102 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Mickael Bosch <mickael.bosch@linux.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+NUM_OUTPUT_CHANNELS = 8
+
+# TODO: Other I²C functions: general call / reset address, device ID address.
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'pca9571'
+    name = 'PCA9571'
+    longname = 'NXP PCA9571'
+    desc = 'NXP PCA9571 8-bit I²C output expander.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = []
+    tags = ['Embedded/industrial', 'IC']
+    annotations = (
+        ('register', 'Register type'),
+        ('value', 'Register value'),
+        ('warning', 'Warning messages'),
+    )
+    annotation_rows = (
+        ('regs', 'Registers', (0, 1)),
+        ('warnings', 'Warnings', (2,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.state = 'IDLE'
+        self.last_write = 0xFF # Chip port default state is high.
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def handle_io(self, b):
+        if self.state == 'READ DATA':
+            operation = ['Outputs read', 'R']
+            if b != self.last_write:
+                self.putx([2, ['Warning: read value and last write value '
+                               '(%02X) are different' % self.last_write]])
+        else:
+            operation = ['Outputs set', 'W']
+            self.last_write = b
+        self.putx([1, [operation[0] + ': %02X' % b,
+                       operation[1] + ': %02X' % b]])
+
+    def check_correct_chip(self, addr):
+        if addr != 0x25:
+            self.putx([2, ['Warning: I²C slave 0x%02X not a PCA9571 '
+                           'compatible chip.' % addr]])
+            return False
+        return True
+
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if cmd in ('ACK', 'BITS'): # Discard 'ACK' and 'BITS'.
+            pass
+        elif cmd in ('START', 'START REPEAT'): # Start a communication.
+            self.state = 'GET SLAVE ADDR'
+        elif cmd in ('NACK', 'STOP'): # Reset the state machine.
+            self.state = 'IDLE'
+        elif cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
+            if ((self.state == 'GET SLAVE ADDR') and
+                    self.check_correct_chip(databyte)):
+                if cmd == 'ADDRESS READ':
+                    self.state = 'READ DATA'
+                else:
+                    self.state = 'WRITE DATA'
+            else:
+                self.state = 'IDLE'
+        elif cmd in ('DATA READ', 'DATA WRITE'):
+            if self.state in ('READ DATA', 'WRITE DATA'):
+                self.handle_io(databyte)
+            else:
+                self.state = 'IDLE'
index 6ed04c80af0b40770113caf5e1c84eeab3fbacd0..194b0b1f9b1f0069666e98e82508368288fd7632 100644 (file)
@@ -33,7 +33,8 @@ class Decoder(srd.Decoder):
     desc = 'PS/2 keyboard/mouse interface.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'PS/2 keyboard/mouse interface.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['ps2']
+    outputs = []
+    tags = ['PC']
     channels = (
         {'id': 'clk', 'name': 'Clock', 'desc': 'Clock line'},
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     channels = (
         {'id': 'clk', 'name': 'Clock', 'desc': 'Clock line'},
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
@@ -57,7 +58,6 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.bits = []
 
     def reset(self):
         self.bits = []
-        self.samplenum = 0
         self.bitcount = 0
 
     def start(self):
         self.bitcount = 0
 
     def start(self):
index b81b141c9cb55bcd62fa80e1d918315838a0007b..8f0397668655e039151d695b2e7fee7512e0a804 100644 (file)
@@ -18,7 +18,7 @@
 ##
 
 '''
 ##
 
 '''
-Pulse-width modulation (a.k.a pulse-duration modulation, PDM) decoder.
+Pulse-width modulation (PWM) a.k.a pulse-duration modulation (PDM) decoder.
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 97704450b7701f5a01387f234eea6e9d16cc0a8e..d8626ee00824d427a53672e76b298e84f9233191 100644 (file)
@@ -31,7 +31,8 @@ class Decoder(srd.Decoder):
     desc = 'Analog level encoded in duty cycle percentage.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Analog level encoded in duty cycle percentage.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['pwm']
+    outputs = []
+    tags = ['Encoding']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index 345efd89250e2ec5472417c107927597a04a085a..b750d9cec68d06a3eed7729503d368a1f6c5faa8 100644 (file)
@@ -52,7 +52,8 @@ class Decoder(srd.Decoder):
     desc = 'Protocol used by Qi receiver.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Protocol used by Qi receiver.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['qi']
+    outputs = []
+    tags = ['Embedded/industrial', 'Wireless/RF']
     channels = (
         {'id': 'qi', 'name': 'Qi', 'desc': 'Demodulated Qi data line'},
     )
     channels = (
         {'id': 'qi', 'name': 'Qi', 'desc': 'Demodulated Qi data line'},
     )
index 56b60caed3c1067fa8bdbca294620a5709e2c92f..0d7cc8c55d6a55047c14fbbe821324872496e5f2 100644 (file)
@@ -80,6 +80,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
+    tags = ['IC', 'IR']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index e709696c9ef7fbbc28fc3c86cd2b17126875cd7d..d3df13a9eb3dfeb024cf7cf836b43a672d269920 100644 (file)
@@ -23,11 +23,12 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'rfm12'
     name = 'RFM12'
     api_version = 3
     id = 'rfm12'
     name = 'RFM12'
-    longname = 'RFM12 control protocol'
+    longname = 'HopeRF RFM12'
     desc = 'HopeRF RFM12 wireless transceiver control protocol.'
     license = 'gplv2+'
     inputs = ['spi']
     desc = 'HopeRF RFM12 wireless transceiver control protocol.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['rfm12']
+    outputs = []
+    tags = ['Wireless/RF']
     annotations = (
         ('cmd', 'Command'),
         ('params', 'Command parameters'),
     annotations = (
         ('cmd', 'Command'),
         ('params', 'Command parameters'),
index d0875561138ae43ecff813f244091df8b8bcf627..ee94c6bf5382b6b5a666486878217fc9e7a687c6 100644 (file)
@@ -27,7 +27,8 @@ class Decoder(srd.Decoder):
     desc = 'RGB LED string protocol (RGB values clocked over SPI).'
     license = 'gplv2+'
     inputs = ['spi']
     desc = 'RGB LED string protocol (RGB values clocked over SPI).'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['rgb_led_spi']
+    outputs = []
+    tags = ['Display']
     annotations = (
         ('rgb', 'RGB values'),
     )
     annotations = (
         ('rgb', 'RGB values'),
     )
index b4f7c581d7e8f14b971fef5a912a56c9c42be64b..adf68eb17d616c835023755a574dd14d1c7745b3 100644 (file)
@@ -31,7 +31,8 @@ class Decoder(srd.Decoder):
     desc = 'RGB LED string protocol (WS281x).'
     license = 'gplv3+'
     inputs = ['logic']
     desc = 'RGB LED string protocol (WS281x).'
     license = 'gplv3+'
     inputs = ['logic']
-    outputs = ['rgb_led_ws281x']
+    outputs = []
+    tags = ['Display', 'IC']
     channels = (
         {'id': 'din', 'name': 'DIN', 'desc': 'DIN data line'},
     )
     channels = (
         {'id': 'din', 'name': 'DIN', 'desc': 'DIN data line'},
     )
@@ -88,6 +89,7 @@ class Decoder(srd.Decoder):
             # Check RESET condition (manufacturer recommends 50 usec minimal,
             # but real minimum is ~10 usec).
             if not self.inreset and not pin and self.es is not None and \
             # Check RESET condition (manufacturer recommends 50 usec minimal,
             # but real minimum is ~10 usec).
             if not self.inreset and not pin and self.es is not None and \
+                    self.ss is not None and \
                     (self.samplenum - self.es) / self.samplerate > 50e-6:
 
                 # Decode last bit value.
                     (self.samplenum - self.es) / self.samplerate > 50e-6:
 
                 # Decode last bit value.
index 3a9dca0c48b36d67fbd44250059dd21855009a42..b57fae645f8598f9c9ce67bd0d165f1c7873c24d 100644 (file)
@@ -35,7 +35,8 @@ class Decoder(srd.Decoder):
     desc = 'Realtime clock module protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
     desc = 'Realtime clock module protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['rtc8564']
+    outputs = []
+    tags = ['Clock/timing']
     annotations = reg_list() + (
         ('read', 'Read date/time'),
         ('write', 'Write date/time'),
     annotations = reg_list() + (
         ('read', 'Read date/time'),
         ('write', 'Write date/time'),
index 4e820efeb8ad4f3ef280b3d6a5fabfad863ec052..bf555109425a01523cbc7200c58663b3da52c2ad 100644 (file)
@@ -18,7 +18,7 @@
 ##
 
 '''
 ##
 
 '''
-Decoder for Siemens EEPROM SDA2506.
+Decoder for Siemens EEPROM SDA 2506-5.
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 8b63cf0ec000f4c5d70df9a3d03722226cd3a6ba..9ae5c0142d53a47ef075556e6dbf3bfd6fe4be95 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'Serial nonvolatile 1-Kbit EEPROM.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Serial nonvolatile 1-Kbit EEPROM.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['sda2506']
+    outputs = []
+    tags = ['IC', 'Memory']
     channels = (
         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
         {'id': 'd', 'name': 'DATA', 'desc': 'Data'},
     channels = (
         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
         {'id': 'd', 'name': 'DATA', 'desc': 'Data'},
@@ -45,8 +46,8 @@ class Decoder(srd.Decoder):
     )
     annotation_rows = (
         ('bits', 'Bits', (ann_cmdbit, ann_databit)),
     )
     annotation_rows = (
         ('bits', 'Bits', (ann_cmdbit, ann_databit)),
-        ('commands', 'Commands', (ann_cmd,)),
         ('data', 'Data', (ann_data,)),
         ('data', 'Data', (ann_data,)),
+        ('commands', 'Commands', (ann_cmd,)),
         ('warnings', 'Warnings', (ann_warning,)),
     )
 
         ('warnings', 'Warnings', (ann_warning,)),
     )
 
index 9402f7ef7692bc486cc9b6adf9b5e3ba379d8bdd..66fa502fedac3a0bab00ccc42b97add7b1960bb7 100644 (file)
@@ -28,7 +28,8 @@ class Decoder(srd.Decoder):
     desc = 'Secure Digital card (SD mode) low-level protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Secure Digital card (SD mode) low-level protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['sdcard_sd']
+    outputs = []
+    tags = ['Memory']
     channels = (
         {'id': 'cmd',  'name': 'CMD',  'desc': 'Command'},
         {'id': 'clk',  'name': 'CLK',  'desc': 'Clock'},
     channels = (
         {'id': 'cmd',  'name': 'CMD',  'desc': 'Command'},
         {'id': 'clk',  'name': 'CLK',  'desc': 'Clock'},
index 7c596345b1c1b8c655dab78e10a4748d0a0ee80f..5bb446ad1aa25f83a9cb1256b278c144948156e6 100644 (file)
@@ -28,7 +28,8 @@ class Decoder(srd.Decoder):
     desc = 'Secure Digital card (SPI mode) low-level protocol.'
     license = 'gplv2+'
     inputs = ['spi']
     desc = 'Secure Digital card (SPI mode) low-level protocol.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['sdcard_spi']
+    outputs = []
+    tags = ['Memory']
     annotations = \
         tuple(('cmd%d' % i, 'CMD%d' % i) for i in range(64)) + \
         tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + ( \
     annotations = \
         tuple(('cmd%d' % i, 'CMD%d' % i) for i in range(64)) + \
         tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + ( \
@@ -41,8 +42,8 @@ class Decoder(srd.Decoder):
         ('bit-warnings', 'Bit warnings'),
     )
     annotation_rows = (
         ('bit-warnings', 'Bit warnings'),
     )
     annotation_rows = (
-        ('bits', 'Bits', (134, 135)),
-        ('cmd-reply', 'Commands/replies', tuple(range(134))),
+        ('bits', 'Bits', (133, 134)),
+        ('cmd-reply', 'Commands/replies', tuple(range(133))),
     )
 
     def __init__(self):
     )
 
     def __init__(self):
@@ -53,12 +54,18 @@ class Decoder(srd.Decoder):
         self.ss, self.es = 0, 0
         self.ss_bit, self.es_bit = 0, 0
         self.ss_cmd, self.es_cmd = 0, 0
         self.ss, self.es = 0, 0
         self.ss_bit, self.es_bit = 0, 0
         self.ss_cmd, self.es_cmd = 0, 0
+        self.ss_busy, self.es_busy = 0, 0
         self.cmd_token = []
         self.cmd_token_bits = []
         self.is_acmd = False # Indicates CMD vs. ACMD
         self.blocklen = 0
         self.read_buf = []
         self.cmd_str = ''
         self.cmd_token = []
         self.cmd_token_bits = []
         self.is_acmd = False # Indicates CMD vs. ACMD
         self.blocklen = 0
         self.read_buf = []
         self.cmd_str = ''
+        self.is_cmd24 = False
+        self.cmd24_start_token_found = False
+        self.is_cmd17 = False
+        self.cmd17_start_token_found = False
+        self.busy_first_byte = False
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -144,14 +151,13 @@ class Decoder(srd.Decoder):
 
         # Bits[0:0]: End bit (always 1)
         bit, self.ss_bit, self.es_bit = tb(0, 0)[0], tb(0, 0)[1], tb(0, 0)[2]
 
         # Bits[0:0]: End bit (always 1)
         bit, self.ss_bit, self.es_bit = tb(0, 0)[0], tb(0, 0)[1], tb(0, 0)[2]
-        self.putb([134, ['End bit: %d' % bit]])
         if bit == 1:
             self.putb([134, ['End bit: %d' % bit]])
         else:
             self.putb([135, ['End bit: %d (Warning: Must be 1!)' % bit]])
 
         # Handle command.
         if bit == 1:
             self.putb([134, ['End bit: %d' % bit]])
         else:
             self.putb([135, ['End bit: %d (Warning: Must be 1!)' % bit]])
 
         # Handle command.
-        if cmd in (0, 1, 9, 16, 17, 41, 49, 55, 59):
+        if cmd in (0, 1, 9, 16, 17, 24, 41, 49, 55, 59):
             self.state = 'HANDLE CMD%d' % cmd
             self.cmd_str = '%s%d (%s)' % (s, cmd, self.cmd_name(cmd))
         else:
             self.state = 'HANDLE CMD%d' % cmd
             self.cmd_str = '%s%d (%s)' % (s, cmd, self.cmd_name(cmd))
         else:
@@ -212,15 +218,13 @@ class Decoder(srd.Decoder):
     def handle_cmd17(self):
         # CMD17: READ_SINGLE_BLOCK
         self.putc(17, 'Read a block from address 0x%04x' % self.arg)
     def handle_cmd17(self):
         # CMD17: READ_SINGLE_BLOCK
         self.putc(17, 'Read a block from address 0x%04x' % self.arg)
-        if len(self.read_buf) == 0:
-            self.ss_cmd = self.ss
-        self.read_buf.append(self.miso)
-        if len(self.read_buf) < self.blocklen + 2: # FIXME
-            return
-        self.es_cmd = self.es
-        self.read_buf = self.read_buf[2:] # FIXME
-        self.putx([17, ['Block data: %s' % self.read_buf]])
-        self.read_buf = []
+        self.is_cmd17 = True
+        self.state = 'GET RESPONSE R1'
+
+    def handle_cmd24(self):
+        # CMD24: WRITE_BLOCK
+        self.putc(24, 'Write a block to address 0x%04x' % self.arg)
+        self.is_cmd24 = True
         self.state = 'GET RESPONSE R1'
 
     def handle_cmd49(self):
         self.state = 'GET RESPONSE R1'
 
     def handle_cmd49(self):
@@ -327,7 +331,10 @@ class Decoder(srd.Decoder):
         # Bit 7: Always set to 0
         putbit(7, ['Bit 7 (always 0)'])
 
         # Bit 7: Always set to 0
         putbit(7, ['Bit 7 (always 0)'])
 
-        self.state = 'IDLE'
+        if self.is_cmd17:
+            self.state = 'HANDLE DATA BLOCK CMD17'
+        if self.is_cmd24:
+            self.state = 'HANDLE DATA BLOCK CMD24'
 
     def handle_response_r1b(self, res):
         # TODO
 
     def handle_response_r1b(self, res):
         # TODO
@@ -349,6 +356,113 @@ class Decoder(srd.Decoder):
         # TODO
         pass
 
         # TODO
         pass
 
+    def handle_data_cmd17(self, miso):
+        # CMD17 returns one byte R1, then some bytes 0xff, then a Start Block
+        # (single byte 0xfe), then self.blocklen bytes of data, then always
+        # 2 bytes of CRC.
+        if self.cmd17_start_token_found:
+            if len(self.read_buf) == 0:
+                self.ss_data = self.ss
+                if not self.blocklen:
+                    # Assume a fixed block size when inspection of the previous
+                    # traffic did not provide the respective parameter value.
+                    # TODO: Make the default block size a PD option?
+                    self.blocklen = 512
+            self.read_buf.append(miso)
+            # Wait until block transfer completed.
+            if len(self.read_buf) < self.blocklen:
+                return
+            if len(self.read_buf) == self.blocklen:
+                self.es_data = self.es
+                self.put(self.ss_data, self.es_data, self.out_ann, [17, ['Block data: %s' % self.read_buf]])
+            elif len(self.read_buf) == (self.blocklen + 1):
+                self.ss_crc = self.ss
+            elif len(self.read_buf) == (self.blocklen + 2):
+                self.es_crc = self.es
+                # TODO: Check CRC.
+                self.put(self.ss_crc, self.es_crc, self.out_ann, [17, ['CRC']])
+                self.state = 'IDLE'
+        elif miso == 0xfe:
+            self.put(self.ss, self.es, self.out_ann, [17, ['Start Block']])
+            self.cmd17_start_token_found = True
+
+    def handle_data_cmd24(self, mosi):
+        if self.cmd24_start_token_found:
+            if len(self.read_buf) == 0:
+                self.ss_data = self.ss
+                if not self.blocklen:
+                    # Assume a fixed block size when inspection of the
+                    # previous traffic did not provide the respective
+                    # parameter value.
+                    # TODO Make the default block size a user adjustable option?
+                    self.blocklen = 512
+            self.read_buf.append(mosi)
+            # Wait until block transfer completed.
+            if len(self.read_buf) < self.blocklen:
+                return
+            self.es_data = self.es
+            self.put(self.ss_data, self.es_data, self.out_ann, [24, ['Block data: %s' % self.read_buf]])
+            self.read_buf = []
+            self.state = 'DATA RESPONSE'
+        elif mosi == 0xfe:
+            self.put(self.ss, self.es, self.out_ann, [24, ['Start Block']])
+            self.cmd24_start_token_found = True
+
+    def handle_data_response(self, miso):
+        # Data Response token (1 byte).
+        #
+        # Format:
+        #  - Bits[7:5]: Don't care.
+        #  - Bits[4:4]: Always 0.
+        #  - Bits[3:1]: Status.
+        #    - 010: Data accepted.
+        #    - 101: Data rejected due to a CRC error.
+        #    - 110: Data rejected due to a write error.
+        #  - Bits[0:0]: Always 1.
+        miso &= 0x1f
+        if miso & 0x11 != 0x01:
+            # This is not the byte we are waiting for.
+            # Should we return to IDLE here?
+            return
+        m = self.miso_bits
+        self.put(m[7][1], m[5][2], self.out_ann, [134, ['Don\'t care']])
+        self.put(m[4][1], m[4][2], self.out_ann, [134, ['Always 0']])
+        if miso == 0x05:
+            self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data accepted']])
+        elif miso == 0x0b:
+            self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data rejected (CRC error)']])
+        elif miso == 0x0d:
+            self.put(m[3][1], m[1][2], self.out_ann, [134, ['Data rejected (write error)']])
+        self.put(m[0][1], m[0][2], self.out_ann, [134, ['Always 1']])
+        ann_class = None
+        if self.is_cmd24:
+            ann_class = 24
+        if ann_class is not None:
+            self.put(self.ss, self.es, self.out_ann, [ann_class, ['Data Response']])
+        if self.is_cmd24:
+            # We just send a block of data to be written to the card,
+            # this takes some time.
+            self.state = 'WAIT WHILE CARD BUSY'
+            self.busy_first_byte = True
+        else:
+            self.state = 'IDLE'
+
+    def wait_while_busy(self, miso):
+        if miso != 0x00:
+            ann_class = None
+            if self.is_cmd24:
+                ann_class = 24
+            if ann_class is not None:
+                self.put(self.ss_busy, self.es_busy, self.out_ann, [24, ['Card is busy']])
+            self.state = 'IDLE'
+            return
+        else:
+            if self.busy_first_byte:
+                self.ss_busy = self.ss
+                self.busy_first_byte = False
+            else:
+                self.es_busy = self.es
+
     def decode(self, ss, es, data):
         ptype, mosi, miso = data
 
     def decode(self, ss, es, data):
         ptype, mosi, miso = data
 
@@ -388,10 +502,18 @@ class Decoder(srd.Decoder):
             # Ignore stray 0xff bytes, some devices seem to send those!?
             if miso == 0xff: # TODO?
                 return
             # Ignore stray 0xff bytes, some devices seem to send those!?
             if miso == 0xff: # TODO?
                 return
-
             # Call the respective handler method for the response.
             # Call the respective handler method for the response.
+            # Assume return to IDLE state, but allow response handlers
+            # to advance to some other state when applicable.
             s = 'handle_response_%s' % self.state[13:].lower()
             handle_response = getattr(self, s)
             s = 'handle_response_%s' % self.state[13:].lower()
             handle_response = getattr(self, s)
-            handle_response(miso)
-
             self.state = 'IDLE'
             self.state = 'IDLE'
+            handle_response(miso)
+        elif self.state == 'HANDLE DATA BLOCK CMD17':
+            self.handle_data_cmd17(miso)
+        elif self.state == 'HANDLE DATA BLOCK CMD24':
+            self.handle_data_cmd24(mosi)
+        elif self.state == 'DATA RESPONSE':
+            self.handle_data_response(miso)
+        elif self.state == 'WAIT WHILE CARD BUSY':
+            self.wait_while_busy(miso)
diff --git a/decoders/seven_segment/__init__.py b/decoders/seven_segment/__init__.py
new file mode 100644 (file)
index 0000000..ea03e4f
--- /dev/null
@@ -0,0 +1,24 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder decodes the output of a 7-segment display.
+'''
+
+from .pd import Decoder
diff --git a/decoders/seven_segment/pd.py b/decoders/seven_segment/pd.py
new file mode 100644 (file)
index 0000000..87714bb
--- /dev/null
@@ -0,0 +1,134 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class ChannelError(Exception):
+    pass
+
+digits = {
+    (0, 0, 0, 0, 0, 0, 0): ' ',
+    (1, 1, 1, 1, 1, 1, 0): '0',
+    (0, 1, 1, 0, 0, 0, 0): '1',
+    (1, 1, 0, 1, 1, 0, 1): '2',
+    (1, 1, 1, 1, 0, 0, 1): '3',
+    (0, 1, 1, 0, 0, 1, 1): '4',
+    (1, 0, 1, 1, 0, 1, 1): '5',
+    (1, 0, 1, 1, 1, 1, 1): '6',
+    (1, 1, 1, 0, 0, 0, 0): '7',
+    (1, 1, 1, 1, 1, 1, 1): '8',
+    (1, 1, 1, 1, 0, 1, 1): '9',
+    (1, 1, 1, 0, 1, 1, 1): 'A',
+    (0, 0, 1, 1, 1, 1, 1): 'B',
+    (1, 0, 0, 1, 1, 1, 0): 'C',
+    (0, 1, 1, 1, 1, 0, 1): 'D',
+    (1, 0, 0, 1, 1, 1, 1): 'E',
+    (1, 0, 0, 0, 1, 1, 1): 'F',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'seven_segment'
+    name = '7-segment'
+    longname = '7-segment display'
+    desc = '7-segment display protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['Display']
+    channels = (
+        {'id': 'a', 'name': 'A', 'desc': 'Segment A'},
+        {'id': 'b', 'name': 'B', 'desc': 'Segment B'},
+        {'id': 'c', 'name': 'C', 'desc': 'Segment C'},
+        {'id': 'd', 'name': 'D', 'desc': 'Segment D'},
+        {'id': 'e', 'name': 'E', 'desc': 'Segment E'},
+        {'id': 'f', 'name': 'F', 'desc': 'Segment F'},
+        {'id': 'g', 'name': 'G', 'desc': 'Segment G'},
+    )
+    optional_channels = (
+        {'id': 'dp', 'name': 'DP', 'desc': 'Decimal point'},
+    )
+    options = (
+        {'id': 'polarity', 'desc': 'Expected polarity',
+            'default': 'common-cathode', 'values': ('common-cathode', 'common-anode')},
+    )
+    annotations = (
+        ('decoded-digit', 'Decoded digit'),
+    )
+    annotation_rows = (
+        ('decoded-digits', 'Decoded digits', (0,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        pass
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putb(self, ss_block, es_block, data):
+        self.put(ss_block, es_block, self.out_ann, data)
+
+    def pins_to_hex(self, pins):
+        return digits.get(pins, None)
+
+    def decode(self):
+        oldpins = self.wait()
+
+        # Check if at least the 7 signals are present.
+        if False in [p in (0, 1) for p in oldpins[:7]]:
+            raise ChannelError('7 or 8 pins have to be present.')
+
+        lastpos = self.samplenum
+
+        self.have_dp = self.has_channel(7)
+
+        conditions = [{0: 'e'}, {1: 'e'}, {2: 'e'}, {3: 'e'}, {4: 'e'}, {5: 'e'}, {6: 'e'}]
+
+        if self.have_dp:
+            conditions.append({7: 'e'})
+
+        while True:
+            # Wait for any change.
+            pins = self.wait(conditions)
+
+            if self.options['polarity'] == 'common-anode':
+                # Invert all data lines if a common anode display is used.
+                if self.have_dp:
+                    oldpins = tuple((1 - state for state in oldpins))
+                else:
+                    oldpins = tuple((1 - state for state in oldpins[:7]))
+
+            # Convert to character string.
+            digit = self.pins_to_hex(oldpins[:7])
+
+            if digit is not None:
+                dp = oldpins[7]
+
+                # Check if decimal point is present and active.
+                if self.have_dp and dp == 1:
+                    digit += '.'
+
+                self.putb(lastpos, self.samplenum, [0, [digit]])
+
+            lastpos = self.samplenum
+
+            oldpins = pins
diff --git a/decoders/signature/__init__.py b/decoders/signature/__init__.py
new file mode 100644 (file)
index 0000000..d37fd4a
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Shirow Miura <shirowmiura@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Signature analysis function for troubleshooting logic circuits.
+This generates the same signature as Hewlett-Packard 5004A.
+'''
+
+from .pd import Decoder
diff --git a/decoders/signature/pd.py b/decoders/signature/pd.py
new file mode 100644 (file)
index 0000000..65b86a2
--- /dev/null
@@ -0,0 +1,142 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Shirow Miura <shirowmiura@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+symbol_map = {
+    0b0000: '0',
+    0b1000: '1',
+    0b0100: '2',
+    0b1100: '3',
+    0b0010: '4',
+    0b1010: '5',
+    0b0110: '6',
+    0b1110: '7',
+    0b0001: '8',
+    0b1001: '9',
+    0b0101: 'A',
+    0b1101: 'C',
+    0b0011: 'F',
+    0b1011: 'H',
+    0b0111: 'P',
+    0b1111: 'U',
+}
+
+START, STOP, CLOCK, DATA = range(4)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'signature'
+    name = 'Signature'
+    longname = 'Signature analysis'
+    desc = 'Annotate signature of logic patterns.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['Debug/trace', 'Util', 'Encoding']
+    channels = (
+        {'id': 'start', 'name': 'START', 'desc': 'START channel'},
+        {'id': 'stop', 'name': 'STOP', 'desc': 'STOP channel'},
+        {'id': 'clk', 'name': 'CLOCK', 'desc': 'CLOCK channel'},
+        {'id': 'data', 'name': 'DATA', 'desc': 'DATA channel'},
+    )
+    options = (
+        {'id': 'start_edge', 'desc': 'START edge polarity',
+            'default': 'rising', 'values': ('rising', 'falling')},
+        {'id': 'stop_edge', 'desc': 'STOP edge polarity',
+            'default': 'rising', 'values': ('rising', 'falling')},
+        {'id': 'clk_edge', 'desc': 'CLOCK edge polarity',
+            'default': 'falling', 'values': ('rising', 'falling')},
+        {'id': 'annbits', 'desc': 'Enable bit level annotations',
+            'default': 'no', 'values': ('yes', 'no')},
+    )
+    annotations = (
+        ('bit0', 'Bit0'),
+        ('bit1', 'Bit1'),
+        ('start', 'START'),
+        ('stop', 'STOP'),
+        ('sig', 'Sig')
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0, 1, 2, 3)),
+        ('sig', 'Sig', (4,))
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        pass
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putsig(self, ss, es, signature):
+        s = ''.join([symbol_map[(signature >>  0) & 0x0f],
+                     symbol_map[(signature >>  4) & 0x0f],
+                     symbol_map[(signature >>  8) & 0x0f],
+                     symbol_map[(signature >> 12) & 0x0f]])
+        self.put(ss, es, self.out_ann, [4, [s]])
+
+    def putb(self, ss, ann):
+        self.put(ss, self.samplenum, self.out_ann, ann)
+
+    def decode(self):
+        opt = self.options
+        start_edge_mode_rising = opt['start_edge'] == 'rising'
+        stop_edge_mode_rising = opt['stop_edge'] == 'rising'
+        annbits = opt['annbits'] == 'yes'
+        gate_is_open = False
+        sample_start = None
+        started = False
+        last_samplenum = 0
+        prev_start = 0 if start_edge_mode_rising else 1
+        prev_stop = 0 if stop_edge_mode_rising else 1
+        shiftreg = 0
+
+        while True:
+            start, stop, _, data = self.wait({CLOCK: opt['clk_edge']})
+            if start != prev_start and not gate_is_open:
+                gate_is_open = (start == 1) if start_edge_mode_rising else (start == 0)
+                if gate_is_open:
+                    # Start sampling.
+                    sample_start = self.samplenum
+                    started = True
+            elif stop != prev_stop and gate_is_open:
+                gate_is_open = not ((stop == 1) if stop_edge_mode_rising else (stop == 0))
+                if not gate_is_open:
+                    # Stop sampling.
+                    if annbits:
+                        self.putb(last_samplenum, [3, ['STOP', 'STP', 'P']])
+                    self.putsig(sample_start, self.samplenum, shiftreg)
+                    shiftreg = 0
+                    sample_start = None
+            if gate_is_open:
+                if annbits:
+                    if started:
+                        s = '<{}>'.format(data)
+                        self.putb(last_samplenum, [2, ['START' + s, 'STR' + s, 'S' + s]])
+                        started = False
+                    else:
+                        self.putb(last_samplenum, [data, [str(data)]])
+                incoming = (bin(shiftreg & 0b0000_0010_1001_0001).count('1') + data) & 1
+                shiftreg = (incoming << 15) | (shiftreg >> 1)
+            prev_start = start
+            prev_stop = stop
+            last_samplenum = self.samplenum
index add2834e2f232869ab962c4cd7b9d3ee329887eb..bec28cf0c184d9fcbea8daf5e5739d963bc219d9 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'Serial bus for connecting digital audio devices.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Serial bus for connecting digital audio devices.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['spdif']
+    outputs = []
+    tags = ['Audio', 'PC']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
@@ -46,8 +47,8 @@ class Decoder(srd.Decoder):
         ('parity', 'Parity Bit'),
     )
     annotation_rows = (
         ('parity', 'Parity Bit'),
     )
     annotation_rows = (
-        ('info', 'Info', (0, 1, 3, 5, 6, 7, 8)),
         ('bits', 'Bits', (2,)),
         ('bits', 'Bits', (2,)),
+        ('info', 'Info', (0, 1, 3, 5, 6, 7, 8)),
         ('samples', 'Samples', (4,)),
     )
 
         ('samples', 'Samples', (4,)),
     )
 
index 491b319d3338d86b534357031c52d816b1c6ecff..dc5cbc0596a8d753c6f67b8d7404babe3351055f 100644 (file)
@@ -21,6 +21,7 @@
 The SPI (Serial Peripheral Interface) protocol decoder supports synchronous
 SPI(-like) protocols with a clock line, a MISO and MOSI line for data
 transfer in two directions, and an optional CS# pin.
 The SPI (Serial Peripheral Interface) protocol decoder supports synchronous
 SPI(-like) protocols with a clock line, a MISO and MOSI line for data
 transfer in two directions, and an optional CS# pin.
+
 Either MISO or MOSI (but not both) can be optional.
 
 If CS# is supplied, data is only decoded when CS# is asserted (clock
 Either MISO or MOSI (but not both) can be optional.
 
 If CS# is supplied, data is only decoded when CS# is asserted (clock
index 85bb1cb9e3195464fc55a983d3e87ecc78cf0baf..5f18d7291de17d568652c0c112327b7d28d6dcd3 100644 (file)
@@ -82,6 +82,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['spi']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['spi']
+    tags = ['Embedded/industrial']
     channels = (
         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
     )
     channels = (
         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
     )
@@ -107,12 +108,16 @@ class Decoder(srd.Decoder):
         ('miso-bits', 'MISO bits'),
         ('mosi-bits', 'MOSI bits'),
         ('warnings', 'Human-readable warnings'),
         ('miso-bits', 'MISO bits'),
         ('mosi-bits', 'MOSI bits'),
         ('warnings', 'Human-readable warnings'),
+        ('miso-transfer', 'MISO transfer'),
+        ('mosi-transfer', 'MOSI transfer'),
     )
     annotation_rows = (
     )
     annotation_rows = (
-        ('miso-data', 'MISO data', (0,)),
         ('miso-bits', 'MISO bits', (2,)),
         ('miso-bits', 'MISO bits', (2,)),
-        ('mosi-data', 'MOSI data', (1,)),
+        ('miso-data', 'MISO data', (0,)),
+        ('miso-transfer', 'MISO transfer', (5,)),
         ('mosi-bits', 'MOSI bits', (3,)),
         ('mosi-bits', 'MOSI bits', (3,)),
+        ('mosi-data', 'MOSI data', (1,)),
+        ('mosi-transfer', 'MOSI transfer', (6,)),
         ('other', 'Other', (4,)),
     )
     binary = (
         ('other', 'Other', (4,)),
     )
     binary = (
@@ -132,7 +137,6 @@ class Decoder(srd.Decoder):
         self.misobytes = []
         self.mosibytes = []
         self.ss_block = -1
         self.misobytes = []
         self.mosibytes = []
         self.ss_block = -1
-        self.samplenum = -1
         self.ss_transfer = -1
         self.cs_was_deasserted = False
         self.have_cs = self.have_miso = self.have_mosi = None
         self.ss_transfer = -1
         self.cs_was_deasserted = False
         self.have_cs = self.have_miso = self.have_mosi = None
@@ -274,7 +278,13 @@ class Decoder(srd.Decoder):
                 self.ss_transfer = self.samplenum
                 self.misobytes = []
                 self.mosibytes = []
                 self.ss_transfer = self.samplenum
                 self.misobytes = []
                 self.mosibytes = []
-            else:
+            elif self.ss_transfer != -1:
+                if self.have_miso:
+                    self.put(self.ss_transfer, self.samplenum, self.out_ann,
+                        [5, [' '.join(format(x.val, '02X') for x in self.misobytes)]])
+                if self.have_mosi:
+                    self.put(self.ss_transfer, self.samplenum, self.out_ann,
+                        [6, [' '.join(format(x.val, '02X') for x in self.mosibytes)]])
                 self.put(self.ss_transfer, self.samplenum, self.out_python,
                     ['TRANSFER', self.mosibytes, self.misobytes])
 
                 self.put(self.ss_transfer, self.samplenum, self.out_python,
                     ['TRANSFER', self.mosibytes, self.misobytes])
 
index b2092bfff5b658e4c17a93e17ac7769ab17933db..49993ad3cb642637f38964792f519561a55d5b06 100644 (file)
@@ -65,6 +65,9 @@ device_name = {
         0x15: 'MX25L3205D',
         0x16: 'MX25L6405D',
     },
         0x15: 'MX25L3205D',
         0x16: 'MX25L6405D',
     },
+    'winbond': {
+        0x13: 'W25Q80DV',
+    },
 }
 
 chips = {
 }
 
 chips = {
@@ -72,14 +75,37 @@ chips = {
     'adesto_at45db161e': {
         'vendor': 'Adesto',
         'model': 'AT45DB161E',
     'adesto_at45db161e': {
         'vendor': 'Adesto',
         'model': 'AT45DB161E',
-        'res_id': 0xff, # The chip doesn't emit an ID here.
-        'rems_id': 0xffff, # Not supported by the chip.
-        'rems2_id': 0xffff, # Not supported by the chip.
+        'res_id': None, # The chip doesn't emit an ID here.
+        'rems_id': None, # Not supported by the chip.
+        'rems2_id': None, # Not supported by the chip.
         'rdid_id': 0x1f26000100, # RDID and 2 extra "EDI" bytes.
         'page_size': 528, # Configurable, could also be 512 bytes.
         'sector_size': 128 * 1024,
         'block_size': 4 * 1024,
     },
         'rdid_id': 0x1f26000100, # RDID and 2 extra "EDI" bytes.
         'page_size': 528, # Configurable, could also be 512 bytes.
         'sector_size': 128 * 1024,
         'block_size': 4 * 1024,
     },
+    # Atmel
+    'atmel_at25128': {
+        'vendor': 'Atmel',
+        'model': 'AT25128',
+        'res_id': None, # Not supported by the chip.
+        'rems_id': None, # Not supported by the chip.
+        'rems2_id': None, # Not supported by the chip.
+        'rdid_id': None, # Not supported by the chip.
+        'page_size': 64,
+        'sector_size': None, # The chip doesn't have sectors.
+        'block_size': None, # The chip doesn't have blocks.
+    },
+    'atmel_at25256': {
+        'vendor': 'Atmel',
+        'model': 'AT25256',
+        'res_id': None, # Not supported by the chip.
+        'rems_id': None, # Not supported by the chip.
+        'rems2_id': None, # Not supported by the chip.
+        'rdid_id': None, # Not supported by the chip.
+        'page_size': 64,
+        'sector_size': None, # The chip doesn't have sectors.
+        'block_size': None, # The chip doesn't have blocks.
+    },
     # FIDELIX
     'fidelix_fm25q32': {
         'vendor': 'FIDELIX',
     # FIDELIX
     'fidelix_fm25q32': {
         'vendor': 'FIDELIX',
@@ -126,4 +152,16 @@ chips = {
         'sector_size': 4 * 1024,
         'block_size': 64 * 1024,
     },
         'sector_size': 4 * 1024,
         'block_size': 64 * 1024,
     },
+    # Winbond
+    'winbond_w25q80dv': {
+        'vendor': 'Winbond',
+        'model': 'W25Q80DV',
+        'res_id': 0x13,
+        'rems_id': 0xef13,
+        'rems2_id': None, # Not supported by the chip.
+        'rdid_id': 0xef4014,
+        'page_size': 256,
+        'sector_size': 4 * 1024,
+        'block_size': 64 * 1024, # Configurable, could also be 32 * 1024 bytes.
+    },
 }
 }
index 1263cd8057845559cad085ef29fa3ed7e9f024db..33fe2298040740af3b8812d983c6d2644176ea9b 100644 (file)
@@ -22,7 +22,7 @@ from .lists import *
 
 L = len(cmds)
 
 
 L = len(cmds)
 
-# Don't forget to keep this in sync with 'cmds' is lists.py.
+# Don't forget to keep this in sync with 'cmds' in lists.py.
 class Ann:
     WRSR, PP, READ, WRDI, RDSR, WREN, FAST_READ, SE, RDSCUR, WRSCUR, \
     RDSR2, CE, ESRY, DSRY, WRITE1, WRITE2, REMS, RDID, RDP_RES, CP, ENSO, DP, \
 class Ann:
     WRSR, PP, READ, WRDI, RDSR, WREN, FAST_READ, SE, RDSCUR, WRSCUR, \
     RDSR2, CE, ESRY, DSRY, WRITE1, WRITE2, REMS, RDID, RDP_RES, CP, ENSO, DP, \
@@ -73,12 +73,13 @@ def decode_status_reg(data):
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'spiflash'
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'spiflash'
-    name = 'SPI flash'
-    longname = 'SPI flash chips'
-    desc = 'xx25 series SPI (NOR) flash chip protocol.'
+    name = 'SPI flash/EEPROM'
+    longname = 'SPI flash/EEPROM chips'
+    desc = 'xx25 series SPI (NOR) flash/EEPROM chip protocol.'
     license = 'gplv2+'
     inputs = ['spi']
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['spiflash']
+    outputs = []
+    tags = ['IC', 'Memory']
     annotations = cmd_annotation_classes() + (
         ('bit', 'Bit'),
         ('field', 'Field'),
     annotations = cmd_annotation_classes() + (
         ('bit', 'Bit'),
         ('field', 'Field'),
@@ -104,6 +105,7 @@ class Decoder(srd.Decoder):
         self.device_id = -1
         self.on_end_transaction = None
         self.end_current_transaction()
         self.device_id = -1
         self.on_end_transaction = None
         self.end_current_transaction()
+        self.writestate = 0
 
         # Build dict mapping command keys to handler functions. Each
         # command in 'cmds' (defined in lists.py) has a matching
 
         # Build dict mapping command keys to handler functions. Each
         # command in 'cmds' (defined in lists.py) has a matching
@@ -174,10 +176,11 @@ class Decoder(srd.Decoder):
 
     def handle_wren(self, mosi, miso):
         self.putx([Ann.WREN, self.cmd_ann_list()])
 
     def handle_wren(self, mosi, miso):
         self.putx([Ann.WREN, self.cmd_ann_list()])
-        self.state = None
+        self.writestate = 1
 
     def handle_wrdi(self, mosi, miso):
 
     def handle_wrdi(self, mosi, miso):
-        pass # TODO
+        self.putx([Ann.WRDI, self.cmd_ann_list()])
+        self.writestate = 0
 
     def handle_rdid(self, mosi, miso):
         if self.cmdstate == 1:
 
     def handle_rdid(self, mosi, miso):
         if self.cmdstate == 1:
@@ -215,6 +218,8 @@ class Decoder(srd.Decoder):
             self.putx([Ann.BIT, [decode_status_reg(miso)]])
             self.putx([Ann.FIELD, ['Status register']])
             self.putc([Ann.RDSR, self.cmd_ann_list()])
             self.putx([Ann.BIT, [decode_status_reg(miso)]])
             self.putx([Ann.FIELD, ['Status register']])
             self.putc([Ann.RDSR, self.cmd_ann_list()])
+            # Set write latch state.
+            self.writestate = 1 if (miso & (1 << 1)) else 0
         self.cmdstate += 1
 
     def handle_rdsr2(self, mosi, miso):
         self.cmdstate += 1
 
     def handle_rdsr2(self, mosi, miso):
@@ -244,12 +249,14 @@ class Decoder(srd.Decoder):
             self.emit_cmd_byte()
         elif self.cmdstate == 2:
             # Byte 2: Master sends status register 1.
             self.emit_cmd_byte()
         elif self.cmdstate == 2:
             # Byte 2: Master sends status register 1.
-            self.putx([Ann.BIT, [decode_status_reg(miso)]])
+            self.putx([Ann.BIT, [decode_status_reg(mosi)]])
             self.putx([Ann.FIELD, ['Status register 1']])
             self.putx([Ann.FIELD, ['Status register 1']])
+            # Set write latch state.
+            self.writestate = 1 if (miso & (1 << 1)) else 0
         elif self.cmdstate == 3:
             # Byte 3: Master sends status register 2.
             # TODO: Decode status register 2 correctly.
         elif self.cmdstate == 3:
             # Byte 3: Master sends status register 2.
             # TODO: Decode status register 2 correctly.
-            self.putx([Ann.BIT, [decode_status_reg(miso)]])
+            self.putx([Ann.BIT, [decode_status_reg(mosi)]])
             self.putx([Ann.FIELD, ['Status register 2']])
             self.es_cmd = self.es
             self.putc([Ann.WRSR, self.cmd_ann_list()])
             self.putx([Ann.FIELD, ['Status register 2']])
             self.es_cmd = self.es
             self.putc([Ann.WRSR, self.cmd_ann_list()])
@@ -279,6 +286,8 @@ class Decoder(srd.Decoder):
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
             self.emit_cmd_byte()
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
             self.emit_cmd_byte()
+            if self.writestate == 0:
+                self.putc([Ann.WARN, ['Warning: WREN might be missing']])
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends write address (24bits, MSB-first).
             self.emit_addr_bytes(mosi)
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends write address (24bits, MSB-first).
             self.emit_addr_bytes(mosi)
@@ -363,11 +372,12 @@ class Decoder(srd.Decoder):
         self.cmdstate += 1
 
     # TODO: Warn/abort if we don't see the necessary amount of bytes.
         self.cmdstate += 1
 
     # TODO: Warn/abort if we don't see the necessary amount of bytes.
-    # TODO: Warn if WREN was not seen before.
     def handle_se(self, mosi, miso):
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
             self.emit_cmd_byte()
     def handle_se(self, mosi, miso):
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
             self.emit_cmd_byte()
+            if self.writestate == 0:
+                self.putx([Ann.WARN, ['Warning: WREN might be missing']])
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends sector address (24bits, MSB-first).
             self.emit_addr_bytes(mosi)
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends sector address (24bits, MSB-first).
             self.emit_addr_bytes(mosi)
@@ -388,10 +398,14 @@ class Decoder(srd.Decoder):
         pass # TODO
 
     def handle_ce(self, mosi, miso):
         pass # TODO
 
     def handle_ce(self, mosi, miso):
-        pass # TODO
+        self.putx([Ann.CE, self.cmd_ann_list()])
+        if self.writestate == 0:
+            self.putx([Ann.WARN, ['Warning: WREN might be missing']])
 
     def handle_ce2(self, mosi, miso):
 
     def handle_ce2(self, mosi, miso):
-        pass # TODO
+        self.putx([Ann.CE2, self.cmd_ann_list()])
+        if self.writestate == 0:
+            self.putx([Ann.WARN, ['Warning: WREN might be missing']])
 
     def handle_pp(self, mosi, miso):
         # Page program: Master asserts CS#, sends PP command, sends 3-byte
 
     def handle_pp(self, mosi, miso):
         # Page program: Master asserts CS#, sends PP command, sends 3-byte
@@ -460,8 +474,8 @@ class Decoder(srd.Decoder):
             self.putx([Ann.FIELD, ['%s ID: 0x%02x' % (d, miso)]])
 
         if self.cmdstate == 6:
             self.putx([Ann.FIELD, ['%s ID: 0x%02x' % (d, miso)]])
 
         if self.cmdstate == 6:
-            id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
-            self.device_id = id
+            id_ = self.ids[1] if self.manufacturer_id_first else self.ids[0]
+            self.device_id = id_
             self.es_cmd = self.es
             self.putc([Ann.REMS, self.cmd_vendor_dev_list()])
             self.state = None
             self.es_cmd = self.es
             self.putc([Ann.REMS, self.cmd_vendor_dev_list()])
             self.state = None
index f2685e2869dc3010520633b98a511457724b482b..51608039ea205bd685800dfb29583d2849dd6f40 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'Synchronous Serial Interface (32bit) protocol.'
     license = 'gplv2+'
     inputs = ['spi']
     desc = 'Synchronous Serial Interface (32bit) protocol.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['ssi32']
+    outputs = []
+    tags = ['Embedded/industrial']
     options = (
         {'id': 'msgsize', 'desc': 'Message size', 'default': 64},
     )
     options = (
         {'id': 'msgsize', 'desc': 'Message size', 'default': 64},
     )
index e724af6256185fc11ad190ae15feed9ffcc63c19..771578c64924c221e5f1784e6af42948c7927bc1 100644 (file)
@@ -18,7 +18,7 @@
 ##
 
 '''
 ##
 
 '''
-This decoder decodes the ST7735 TFT controller protocol.
+This decoder decodes the Sitronix ST7735 TFT controller protocol.
 
 Details:
 http://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf
 
 Details:
 http://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf
index 999038a3a18b7178b8747a52d9f8e85460f08abb..252b188763c4faf0ca2eed738f5a0be7ac9df0c4 100644 (file)
@@ -72,7 +72,8 @@ class Decoder(srd.Decoder):
     desc = 'Sitronix ST7735 TFT controller protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Sitronix ST7735 TFT controller protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['st7735']
+    outputs = []
+    tags = ['Display', 'IC']
     channels = (
         {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
     channels = (
         {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
index 05463958a28797b196405843cda420f4fd7d543d..99a4b2eec24148b00769c0e350bb2defe49d32eb 100644 (file)
@@ -27,7 +27,8 @@ class Decoder(srd.Decoder):
     desc = 'Absolute position and movement speed from step/dir.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Absolute position and movement speed from step/dir.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['stepper_motor']
+    outputs = []
+    tags = ['Embedded/industrial']
     channels = (
         {'id': 'step', 'name': 'Step', 'desc': 'Step pulse'},
         {'id': 'dir', 'name': 'Direction', 'desc': 'Direction select'},
     channels = (
         {'id': 'step', 'name': 'Step', 'desc': 'Step pulse'},
         {'id': 'dir', 'name': 'Direction', 'desc': 'Direction select'},
index c1b5ae593b1c6b59bfa90d8770d3529421be7897..a141239baadac999b7296aa3a66694be361e13ca 100644 (file)
 This PD decodes the ARM SWD (version 1) protocol, as described in the
 "ARM Debug Interface v5.2" Architecture Specification.
 
 This PD decodes the ARM SWD (version 1) protocol, as described in the
 "ARM Debug Interface v5.2" Architecture Specification.
 
-Details:
-http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0031c/index.html
-(Registration required)
-
 Not supported:
  * Turnaround periods other than the default 1, as set in DLCR.TURNROUND
    (should be trivial to add)
  * SWD protocol version 2 (multi-drop support, etc.)
 Not supported:
  * Turnaround periods other than the default 1, as set in DLCR.TURNROUND
    (should be trivial to add)
  * SWD protocol version 2 (multi-drop support, etc.)
+
+Details:
+http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0031c/index.html
+(Registration required)
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 0f7bc22aab281eaa8b418604f6970131a5524d69..cc258ef3304854118291b1d93a2881a035d89c95 100644 (file)
@@ -72,6 +72,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['swd']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['swd']
+    tags = ['Debug/trace']
     channels = (
         {'id': 'swclk', 'name': 'SWCLK', 'desc': 'Master clock'},
         {'id': 'swdio', 'name': 'SWDIO', 'desc': 'Data input/output'},
     channels = (
         {'id': 'swclk', 'name': 'SWCLK', 'desc': 'Master clock'},
         {'id': 'swdio', 'name': 'SWDIO', 'desc': 'Data input/output'},
index cd18b8577cca50574e8dfdb975d606b5a7d9024e..315b013a8dac3e4565a20e22fde02b6bdec7825f 100644 (file)
@@ -14,8 +14,7 @@
 ## GNU General Public License for more details.
 ##
 ## You should have received a copy of the GNU General Public License
 ## 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/>.
 ##
 
 '''
 ##
 
 '''
index 452805a0377988fff28cf8a668f1f27b062ce0e9..fd43f418b36f2ec2c91ba679a789e438d0b3ea0b 100644 (file)
@@ -14,8 +14,7 @@
 ## GNU General Public License for more details.
 ##
 ## You should have received a copy of the GNU General Public License
 ## 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 math
 ##
 
 import math
@@ -33,6 +32,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
+    tags = ['Debug/trace']
     options = (
         {'id': 'debug', 'desc': 'Debug', 'default': 'no', 'values': ('yes', 'no') },
     )
     options = (
         {'id': 'debug', 'desc': 'Debug', 'default': 'no', 'values': ('yes', 'no') },
     )
index 07df9c2cddd8eb9a38f6a05f648939da51693373..d345d318bf1dfb35c9fe57de3a5c86b137f2a0b7 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'T55xx 100-150kHz RFID protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'T55xx 100-150kHz RFID protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['t55xx']
+    outputs = []
+    tags = ['IC', 'RFID']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index 0e607675f0465f3df0f7e3b36cd8da8da28bb493..49245174fc17e94f934c6161c547d1b4c08bdb3a 100644 (file)
@@ -29,7 +29,8 @@ class Decoder(srd.Decoder):
     desc = 'Texas Instruments TCA6408A 8-bit I²C I/O expander.'
     license = 'gplv2+'
     inputs = ['i2c']
     desc = 'Texas Instruments TCA6408A 8-bit I²C I/O expander.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['tca6408a']
+    outputs = []
+    tags = ['Embedded/industrial', 'IC']
     annotations = (
         ('register', 'Register type'),
         ('value', 'Register value'),
     annotations = (
         ('register', 'Register type'),
         ('value', 'Register value'),
diff --git a/decoders/tdm_audio/__init__.py b/decoders/tdm_audio/__init__.py
new file mode 100644 (file)
index 0000000..a95e16e
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Ben Dooks <ben.dooks@codethink.co.uk>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+TDM Audio is an audio serial bus for moving audio data between devices
+(usually on the same board) which can carry one or more channels of data.
+'''
+
+from .pd import Decoder
diff --git a/decoders/tdm_audio/pd.py b/decoders/tdm_audio/pd.py
new file mode 100644 (file)
index 0000000..6c5869b
--- /dev/null
@@ -0,0 +1,116 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Ben Dooks <ben.dooks@codethink.co.uk>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+MAX_CHANNELS = 8
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'tdm_audio'
+    name = 'TDM audio'
+    longname = 'Time division multiplex audio'
+    desc = 'TDM multi-channel audio protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['Audio']
+    channels = (
+        { 'id': 'clock', 'name': 'Bitclk', 'desc': 'Data bit clock' },
+        { 'id': 'frame', 'name': 'Framesync', 'desc': 'Frame sync' },
+        { 'id': 'data', 'name': 'Data', 'desc': 'Serial data' },
+    )
+    options = (
+        {'id': 'bps', 'desc': 'Bits per sample', 'default': 16 },
+        {'id': 'channels', 'desc': 'Channels per frame', 'default': MAX_CHANNELS },
+        {'id': 'edge', 'desc': 'Clock edge to sample on', 'default': 'rising', 'values': ('rising', 'falling') }
+    )
+    annotations = tuple(('ch%d' % i, 'Ch%d' % i) for i in range(MAX_CHANNELS))
+    annotation_rows = tuple(('ch%d' % i, 'Ch%d' % i, (i,)) for i in range(MAX_CHANNELS))
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
+        self.channels = MAX_CHANNELS
+        self.channel = 0
+        self.bitdepth = 16
+        self.bitcount = 0
+        self.samplecount = 0
+        self.lastsync = 0
+        self.lastframe = 0
+        self.data = 0
+        self.ss_block = None
+
+    def metdatadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.bitdepth = self.options['bps']
+        self.edge = self.options['edge']
+
+    def decode(self):
+        while True:
+            # Wait for edge of clock (sample on rising/falling edge).
+            clock, frame, data = self.wait({0: self.edge[0]})
+
+            self.data = (self.data << 1) | data
+            self.bitcount += 1
+
+            if self.ss_block is not None:
+                if self.bitcount >= self.bitdepth:
+                    self.bitcount = 0
+                    self.channel += 1
+
+                    c1 = 'Channel %d' % self.channel
+                    c2 = 'C%d' % self.channel
+                    c3 = '%d' % self.channel
+                    if self.bitdepth <= 8:
+                        v = '%02x' % self.data
+                    elif self.bitdepth <= 16:
+                        v = '%04x' % self.data
+                    else:
+                        v = '%08x' % self.data
+
+                    if self.channel < self.channels:
+                        ch = self.channel
+                    else:
+                        ch = 0
+
+                    self.put(self.ss_block, self.samplenum, self.out_ann,
+                             [ch, ['%s: %s' % (c1, v), '%s: %s' % (c2, v),
+                                   '%s: %s' % (c3, v)]])
+                    self.data = 0
+                    self.ss_block = self.samplenum
+                    self.samplecount += 1
+
+            # Check for new frame.
+            # Note, frame may be a single clock, or active for the first
+            # sample in the frame.
+            if frame != self.lastframe and frame == 1:
+                self.channel = 0
+                self.bitcount = 0
+                self.data = 0
+                if self.ss_block is None:
+                    self.ss_block = 0
+
+            self.lastframe = frame
index 61eab1ea104c03b1a45b8f646f2b16df697cf4eb..d3e0d1f0530b53ab8bcd6541b9c8a35ddf754434 100644 (file)
@@ -53,7 +53,8 @@ class Decoder(srd.Decoder):
     desc = 'Calculate time between edges.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Calculate time between edges.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['timing']
+    outputs = []
+    tags = ['Clock/timing', 'Util']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index 6fe8ae10d651c836a83b9dd13e4424a51307a902..4d02792c6091dfbe4f2437262b3f76c425a36271 100644 (file)
@@ -34,7 +34,8 @@ class Decoder(srd.Decoder):
     desc = 'Texas Instruments TLC5620 8-bit quad DAC.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Texas Instruments TLC5620 8-bit quad DAC.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['tlc5620']
+    outputs = []
+    tags = ['IC', 'Analog/digital']
     channels = (
         {'id': 'clk', 'name': 'CLK', 'desc': 'Serial interface clock'},
         {'id': 'data', 'name': 'DATA', 'desc': 'Serial interface data'},
     channels = (
         {'id': 'clk', 'name': 'CLK', 'desc': 'Serial interface clock'},
         {'id': 'data', 'name': 'DATA', 'desc': 'Serial interface data'},
index 6c3d85cf9b06c9f30db1d59c33aeab85b9ecb631..4ce6ef9dbe14a301a7b2a92f637f99cb51e1f317 100644 (file)
@@ -39,7 +39,11 @@ This is the list of <ptype>s and their respective <pdata> values:
  - 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1).
  - 'PARITY ERROR': The data is a tuple with two entries. The first one is
    the expected parity value, the second is the actual parity value.
  - 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1).
  - 'PARITY ERROR': The data is a tuple with two entries. The first one is
    the expected parity value, the second is the actual parity value.
- - TODO: Frame error?
+ - 'BREAK': The data is always 0.
+ - 'FRAME': The data is always a tuple containing two items: The (integer)
+   value of the UART data, and a boolean which reflects the validity of the
+   UART frame.
+ - 'IDLE': The data is always 0.
 
 The <rxtx> field is 0 for RX packets, 1 for TX packets.
 '''
 
 The <rxtx> field is 0 for RX packets, 1 for TX packets.
 '''
@@ -52,7 +56,10 @@ TX = 1
 # parity bit, the value of the data, and the length of the data (5-9 bits,
 # usually 8 bits) return True if the parity is correct, False otherwise.
 # 'none' is _not_ allowed as value for 'parity_type'.
 # parity bit, the value of the data, and the length of the data (5-9 bits,
 # usually 8 bits) return True if the parity is correct, False otherwise.
 # 'none' is _not_ allowed as value for 'parity_type'.
-def parity_ok(parity_type, parity_bit, data, num_data_bits):
+def parity_ok(parity_type, parity_bit, data, data_bits):
+
+    if parity_type == 'ignore':
+        return True
 
     # Handle easy cases first (parity bit is always 1 or 0).
     if parity_type == 'zero':
 
     # Handle easy cases first (parity bit is always 1 or 0).
     if parity_type == 'zero':
@@ -84,6 +91,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['uart']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['uart']
+    tags = ['Embedded/industrial']
     optional_channels = (
         # Allow specifying only one of the signals, e.g. if only one data
         # direction exists (or is relevant).
     optional_channels = (
         # Allow specifying only one of the signals, e.g. if only one data
         # direction exists (or is relevant).
@@ -92,22 +100,26 @@ class Decoder(srd.Decoder):
     )
     options = (
         {'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200},
     )
     options = (
         {'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200},
-        {'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8,
+        {'id': 'data_bits', 'desc': 'Data bits', 'default': 8,
             'values': (5, 6, 7, 8, 9)},
             'values': (5, 6, 7, 8, 9)},
-        {'id': 'parity_type', 'desc': 'Parity type', 'default': 'none',
-            'values': ('none', 'odd', 'even', 'zero', 'one')},
-        {'id': 'parity_check', 'desc': 'Check parity?', 'default': 'yes',
-            'values': ('yes', 'no')},
-        {'id': 'num_stop_bits', 'desc': 'Stop bits', 'default': 1.0,
+        {'id': 'parity', 'desc': 'Parity', 'default': 'none',
+            'values': ('none', 'odd', 'even', 'zero', 'one', 'ignore')},
+        {'id': 'stop_bits', 'desc': 'Stop bits', 'default': 1.0,
             'values': (0.0, 0.5, 1.0, 1.5)},
         {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
             'values': ('lsb-first', 'msb-first')},
         {'id': 'format', 'desc': 'Data format', 'default': 'hex',
             'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
             'values': (0.0, 0.5, 1.0, 1.5)},
         {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
             'values': ('lsb-first', 'msb-first')},
         {'id': 'format', 'desc': 'Data format', 'default': 'hex',
             'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
-        {'id': 'invert_rx', 'desc': 'Invert RX?', 'default': 'no',
+        {'id': 'invert_rx', 'desc': 'Invert RX', 'default': 'no',
             'values': ('yes', 'no')},
             'values': ('yes', 'no')},
-        {'id': 'invert_tx', 'desc': 'Invert TX?', 'default': 'no',
+        {'id': 'invert_tx', 'desc': 'Invert TX', 'default': 'no',
             'values': ('yes', 'no')},
             'values': ('yes', 'no')},
+        {'id': 'rx_packet_delim', 'desc': 'RX packet delimiter (decimal)',
+            'default': -1},
+        {'id': 'tx_packet_delim', 'desc': 'TX packet delimiter (decimal)',
+            'default': -1},
+        {'id': 'rx_packet_len', 'desc': 'RX packet length', 'default': -1},
+        {'id': 'tx_packet_len', 'desc': 'TX packet length', 'default': -1},
     )
     annotations = (
         ('rx-data', 'RX data'),
     )
     annotations = (
         ('rx-data', 'RX data'),
@@ -124,14 +136,22 @@ class Decoder(srd.Decoder):
         ('tx-warnings', 'TX warnings'),
         ('rx-data-bits', 'RX data bits'),
         ('tx-data-bits', 'TX data bits'),
         ('tx-warnings', 'TX warnings'),
         ('rx-data-bits', 'RX data bits'),
         ('tx-data-bits', 'TX data bits'),
+        ('rx-break', 'RX break'),
+        ('tx-break', 'TX break'),
+        ('rx-packet', 'RX packet'),
+        ('tx-packet', 'TX packet'),
     )
     annotation_rows = (
     )
     annotation_rows = (
-        ('rx-data', 'RX', (0, 2, 4, 6, 8)),
         ('rx-data-bits', 'RX bits', (12,)),
         ('rx-data-bits', 'RX bits', (12,)),
+        ('rx-data', 'RX', (0, 2, 4, 6, 8)),
         ('rx-warnings', 'RX warnings', (10,)),
         ('rx-warnings', 'RX warnings', (10,)),
-        ('tx-data', 'TX', (1, 3, 5, 7, 9)),
+        ('rx-break', 'RX break', (14,)),
+        ('rx-packets', 'RX packets', (16,)),
         ('tx-data-bits', 'TX bits', (13,)),
         ('tx-data-bits', 'TX bits', (13,)),
+        ('tx-data', 'TX', (1, 3, 5, 7, 9)),
         ('tx-warnings', 'TX warnings', (11,)),
         ('tx-warnings', 'TX warnings', (11,)),
+        ('tx-break', 'TX break', (15,)),
+        ('tx-packets', 'TX packets', (17,)),
     )
     binary = (
         ('rx', 'RX dump'),
     )
     binary = (
         ('rx', 'RX dump'),
@@ -144,6 +164,10 @@ class Decoder(srd.Decoder):
         s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
 
         s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
 
+    def putx_packet(self, rxtx, data):
+        s, halfbit = self.ss_packet[rxtx], self.bit_width / 2.0
+        self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
+
     def putpx(self, rxtx, data):
         s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_python, data)
     def putpx(self, rxtx, data):
         s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_python, data)
@@ -156,6 +180,12 @@ class Decoder(srd.Decoder):
         s, halfbit = self.samplenum, self.bit_width / 2.0
         self.put(s - floor(halfbit), s + ceil(halfbit), self.out_python, data)
 
         s, halfbit = self.samplenum, self.bit_width / 2.0
         self.put(s - floor(halfbit), s + ceil(halfbit), self.out_python, data)
 
+    def putgse(self, ss, es, data):
+        self.put(ss, es, self.out_ann, data)
+
+    def putpse(self, ss, es, data):
+        self.put(ss, es, self.out_python, data)
+
     def putbin(self, rxtx, data):
         s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
     def putbin(self, rxtx, data):
         s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
@@ -165,8 +195,8 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
 
     def reset(self):
         self.samplerate = None
-        self.samplenum = 0
         self.frame_start = [-1, -1]
         self.frame_start = [-1, -1]
+        self.frame_valid = [None, None]
         self.startbit = [-1, -1]
         self.cur_data_bit = [0, 0]
         self.datavalue = [0, 0]
         self.startbit = [-1, -1]
         self.cur_data_bit = [0, 0]
         self.datavalue = [0, 0]
@@ -175,12 +205,16 @@ class Decoder(srd.Decoder):
         self.startsample = [-1, -1]
         self.state = ['WAIT FOR START BIT', 'WAIT FOR START BIT']
         self.databits = [[], []]
         self.startsample = [-1, -1]
         self.state = ['WAIT FOR START BIT', 'WAIT FOR START BIT']
         self.databits = [[], []]
+        self.break_start = [None, None]
+        self.packet_cache = [[], []]
+        self.ss_packet, self.es_packet = [None, None], [None, None]
+        self.idle_start = [None, None]
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_ann = self.register(srd.OUTPUT_ANN)
-        self.bw = (self.options['num_data_bits'] + 7) // 8
+        self.bw = (self.options['data_bits'] + 7) // 8
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -202,6 +236,7 @@ class Decoder(srd.Decoder):
     def wait_for_start_bit(self, rxtx, signal):
         # Save the sample number where the start bit begins.
         self.frame_start[rxtx] = self.samplenum
     def wait_for_start_bit(self, rxtx, signal):
         # Save the sample number where the start bit begins.
         self.frame_start[rxtx] = self.samplenum
+        self.frame_valid[rxtx] = True
 
         self.state[rxtx] = 'GET START BIT'
 
 
         self.state[rxtx] = 'GET START BIT'
 
@@ -213,6 +248,10 @@ class Decoder(srd.Decoder):
         if self.startbit[rxtx] != 0:
             self.putp(['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
             self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
         if self.startbit[rxtx] != 0:
             self.putp(['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
             self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
+            self.frame_valid[rxtx] = False
+            es = self.samplenum + ceil(self.bit_width / 2.0)
+            self.putpse(self.frame_start[rxtx], es, ['FRAME', rxtx,
+                (self.datavalue[rxtx], self.frame_valid[rxtx])])
             self.state[rxtx] = 'WAIT FOR START BIT'
             return
 
             self.state[rxtx] = 'WAIT FOR START BIT'
             return
 
@@ -225,6 +264,30 @@ class Decoder(srd.Decoder):
 
         self.state[rxtx] = 'GET DATA BITS'
 
 
         self.state[rxtx] = 'GET DATA BITS'
 
+    def handle_packet(self, rxtx):
+        d = 'rx' if (rxtx == RX) else 'tx'
+        delim = self.options[d + '_packet_delim']
+        plen = self.options[d + '_packet_len']
+        if delim == -1 and plen == -1:
+            return
+
+        # Cache data values until we see the delimiter and/or the specified
+        # packet length has been reached (whichever happens first).
+        if len(self.packet_cache[rxtx]) == 0:
+            self.ss_packet[rxtx] = self.startsample[rxtx]
+        self.packet_cache[rxtx].append(self.datavalue[rxtx])
+        if self.datavalue[rxtx] == delim or len(self.packet_cache[rxtx]) == plen:
+            self.es_packet[rxtx] = self.samplenum
+            s = ''
+            for b in self.packet_cache[rxtx]:
+                s += self.format_value(b)
+                if self.options['format'] != 'ascii':
+                    s += ' '
+            if self.options['format'] != 'ascii' and s[-1] == ' ':
+                s = s[:-1] # Drop trailing space.
+            self.putx_packet(rxtx, [16 + rxtx, [s]])
+            self.packet_cache[rxtx] = []
+
     def get_data_bits(self, rxtx, signal):
         # Save the sample number of the middle of the first data bit.
         if self.startsample[rxtx] == -1:
     def get_data_bits(self, rxtx, signal):
         # Save the sample number of the middle of the first data bit.
         if self.startsample[rxtx] == -1:
@@ -238,7 +301,7 @@ class Decoder(srd.Decoder):
 
         # Return here, unless we already received all data bits.
         self.cur_data_bit[rxtx] += 1
 
         # Return here, unless we already received all data bits.
         self.cur_data_bit[rxtx] += 1
-        if self.cur_data_bit[rxtx] < self.options['num_data_bits']:
+        if self.cur_data_bit[rxtx] < self.options['data_bits']:
             return
 
         # Convert accumulated data bits to a data value.
             return
 
         # Convert accumulated data bits to a data value.
@@ -258,12 +321,14 @@ class Decoder(srd.Decoder):
         self.putbin(rxtx, [rxtx, bdata])
         self.putbin(rxtx, [2, bdata])
 
         self.putbin(rxtx, [rxtx, bdata])
         self.putbin(rxtx, [2, bdata])
 
+        self.handle_packet(rxtx)
+
         self.databits[rxtx] = []
 
         # Advance to either reception of the parity bit, or reception of
         # the STOP bits if parity is not applicable.
         self.state[rxtx] = 'GET PARITY BIT'
         self.databits[rxtx] = []
 
         # Advance to either reception of the parity bit, or reception of
         # the STOP bits if parity is not applicable.
         self.state[rxtx] = 'GET PARITY BIT'
-        if self.options['parity_type'] == 'none':
+        if self.options['parity'] == 'none':
             self.state[rxtx] = 'GET STOP BITS'
 
     def format_value(self, v):
             self.state[rxtx] = 'GET STOP BITS'
 
     def format_value(self, v):
@@ -271,7 +336,7 @@ class Decoder(srd.Decoder):
         # Reflects the user selected kind of representation, as well as
         # the number of data bits in the UART frames.
 
         # Reflects the user selected kind of representation, as well as
         # the number of data bits in the UART frames.
 
-        fmt, bits = self.options['format'], self.options['num_data_bits']
+        fmt, bits = self.options['format'], self.options['data_bits']
 
         # Assume "is printable" for values from 32 to including 126,
         # below 32 is "control" and thus not printable, above 127 is
 
         # Assume "is printable" for values from 32 to including 126,
         # below 32 is "control" and thus not printable, above 127 is
@@ -311,14 +376,15 @@ class Decoder(srd.Decoder):
     def get_parity_bit(self, rxtx, signal):
         self.paritybit[rxtx] = signal
 
     def get_parity_bit(self, rxtx, signal):
         self.paritybit[rxtx] = signal
 
-        if parity_ok(self.options['parity_type'], self.paritybit[rxtx],
-                     self.datavalue[rxtx], self.options['num_data_bits']):
+        if parity_ok(self.options['parity'], self.paritybit[rxtx],
+                     self.datavalue[rxtx], self.options['data_bits']):
             self.putp(['PARITYBIT', rxtx, self.paritybit[rxtx]])
             self.putg([rxtx + 4, ['Parity bit', 'Parity', 'P']])
         else:
             # TODO: Return expected/actual parity values.
             self.putp(['PARITY ERROR', rxtx, (0, 1)]) # FIXME: Dummy tuple...
             self.putg([rxtx + 6, ['Parity error', 'Parity err', 'PE']])
             self.putp(['PARITYBIT', rxtx, self.paritybit[rxtx]])
             self.putg([rxtx + 4, ['Parity bit', 'Parity', 'P']])
         else:
             # TODO: Return expected/actual parity values.
             self.putp(['PARITY ERROR', rxtx, (0, 1)]) # FIXME: Dummy tuple...
             self.putg([rxtx + 6, ['Parity error', 'Parity err', 'PE']])
+            self.frame_valid[rxtx] = False
 
         self.state[rxtx] = 'GET STOP BITS'
 
 
         self.state[rxtx] = 'GET STOP BITS'
 
@@ -330,11 +396,24 @@ class Decoder(srd.Decoder):
         if self.stopbit1[rxtx] != 1:
             self.putp(['INVALID STOPBIT', rxtx, self.stopbit1[rxtx]])
             self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
         if self.stopbit1[rxtx] != 1:
             self.putp(['INVALID STOPBIT', rxtx, self.stopbit1[rxtx]])
             self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
-            # TODO: Abort? Ignore the frame? Other?
+            self.frame_valid[rxtx] = False
 
         self.putp(['STOPBIT', rxtx, self.stopbit1[rxtx]])
         self.putg([rxtx + 4, ['Stop bit', 'Stop', 'T']])
 
 
         self.putp(['STOPBIT', rxtx, self.stopbit1[rxtx]])
         self.putg([rxtx + 4, ['Stop bit', 'Stop', 'T']])
 
+        # Pass the complete UART frame to upper layers.
+        es = self.samplenum + ceil(self.bit_width / 2.0)
+        self.putpse(self.frame_start[rxtx], es, ['FRAME', rxtx,
+            (self.datavalue[rxtx], self.frame_valid[rxtx])])
+
+        self.state[rxtx] = 'WAIT FOR START BIT'
+        self.idle_start[rxtx] = self.frame_start[rxtx] + self.frame_len_sample_count
+
+    def handle_break(self, rxtx):
+        self.putpse(self.frame_start[rxtx], self.samplenum,
+                ['BREAK', rxtx, 0])
+        self.putgse(self.frame_start[rxtx], self.samplenum,
+                [rxtx + 14, ['Break condition', 'Break', 'Brk', 'B']])
         self.state[rxtx] = 'WAIT FOR START BIT'
 
     def get_wait_cond(self, rxtx, inv):
         self.state[rxtx] = 'WAIT FOR START BIT'
 
     def get_wait_cond(self, rxtx, inv):
@@ -349,13 +428,24 @@ class Decoder(srd.Decoder):
         elif state == 'GET DATA BITS':
             bitnum = 1 + self.cur_data_bit[rxtx]
         elif state == 'GET PARITY BIT':
         elif state == 'GET DATA BITS':
             bitnum = 1 + self.cur_data_bit[rxtx]
         elif state == 'GET PARITY BIT':
-            bitnum = 1 + self.options['num_data_bits']
+            bitnum = 1 + self.options['data_bits']
         elif state == 'GET STOP BITS':
         elif state == 'GET STOP BITS':
-            bitnum = 1 + self.options['num_data_bits']
-            bitnum += 0 if self.options['parity_type'] == 'none' else 1
+            bitnum = 1 + self.options['data_bits']
+            bitnum += 0 if self.options['parity'] == 'none' else 1
         want_num = ceil(self.get_sample_point(rxtx, bitnum))
         return {'skip': want_num - self.samplenum}
 
         want_num = ceil(self.get_sample_point(rxtx, bitnum))
         return {'skip': want_num - self.samplenum}
 
+    def get_idle_cond(self, rxtx, inv):
+        # Return a condition that corresponds to the (expected) end of
+        # the next frame, assuming that it will be an "idle frame"
+        # (constant high input level for the frame's length).
+        if self.idle_start[rxtx] is None:
+            return None
+        end_of_frame = self.idle_start[rxtx] + self.frame_len_sample_count
+        if end_of_frame < self.samplenum:
+            return None
+        return {'skip': end_of_frame - self.samplenum}
+
     def inspect_sample(self, rxtx, signal, inv):
         # Inspect a sample returned by .wait() for the specified UART line.
         if inv:
     def inspect_sample(self, rxtx, signal, inv):
         # Inspect a sample returned by .wait() for the specified UART line.
         if inv:
@@ -373,28 +463,99 @@ class Decoder(srd.Decoder):
         elif state == 'GET STOP BITS':
             self.get_stop_bits(rxtx, signal)
 
         elif state == 'GET STOP BITS':
             self.get_stop_bits(rxtx, signal)
 
+    def inspect_edge(self, rxtx, signal, inv):
+        # Inspect edges, independently from traffic, to detect break conditions.
+        if inv:
+            signal = not signal
+        if not signal:
+            # Signal went low. Start another interval.
+            self.break_start[rxtx] = self.samplenum
+            return
+        # Signal went high. Was there an extended period with low signal?
+        if self.break_start[rxtx] is None:
+            return
+        diff = self.samplenum - self.break_start[rxtx]
+        if diff >= self.break_min_sample_count:
+            self.handle_break(rxtx)
+        self.break_start[rxtx] = None
+
+    def inspect_idle(self, rxtx, signal, inv):
+        # Check each edge and each period of stable input (either level).
+        # Can derive the "idle frame period has passed" condition.
+        if inv:
+            signal = not signal
+        if not signal:
+            # Low input, cease inspection.
+            self.idle_start[rxtx] = None
+            return
+        # High input, either just reached, or still stable.
+        if self.idle_start[rxtx] is None:
+            self.idle_start[rxtx] = self.samplenum
+        diff = self.samplenum - self.idle_start[rxtx]
+        if diff < self.frame_len_sample_count:
+            return
+        ss, es = self.idle_start[rxtx], self.samplenum
+        self.putpse(ss, es, ['IDLE', rxtx, 0])
+        self.idle_start[rxtx] = self.samplenum
+
     def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
 
         has_pin = [self.has_channel(ch) for ch in (RX, TX)]
     def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
 
         has_pin = [self.has_channel(ch) for ch in (RX, TX)]
-        if has_pin == [False, False]:
-            raise ChannelError('Either TX or RX (or both) pins required.')
+        if not True in has_pin:
+            raise ChannelError('Need at least one of TX or RX pins.')
 
         opt = self.options
         inv = [opt['invert_rx'] == 'yes', opt['invert_tx'] == 'yes']
 
         opt = self.options
         inv = [opt['invert_rx'] == 'yes', opt['invert_tx'] == 'yes']
-        cond_idx = [None] * len(has_pin)
+        cond_data_idx = [None] * len(has_pin)
+
+        # Determine the number of samples for a complete frame's time span.
+        # A period of low signal (at least) that long is a break condition.
+        frame_samples = 1 # START
+        frame_samples += self.options['data_bits']
+        frame_samples += 0 if self.options['parity'] == 'none' else 1
+        frame_samples += self.options['stop_bits']
+        frame_samples *= self.bit_width
+        self.frame_len_sample_count = ceil(frame_samples)
+        self.break_min_sample_count = self.frame_len_sample_count
+        cond_edge_idx = [None] * len(has_pin)
+        cond_idle_idx = [None] * len(has_pin)
 
         while True:
             conds = []
             if has_pin[RX]:
 
         while True:
             conds = []
             if has_pin[RX]:
-                cond_idx[RX] = len(conds)
+                cond_data_idx[RX] = len(conds)
                 conds.append(self.get_wait_cond(RX, inv[RX]))
                 conds.append(self.get_wait_cond(RX, inv[RX]))
+                cond_edge_idx[RX] = len(conds)
+                conds.append({RX: 'e'})
+                cond_idle_idx[RX] = None
+                idle_cond = self.get_idle_cond(RX, inv[RX])
+                if idle_cond:
+                    cond_idle_idx[RX] = len(conds)
+                    conds.append(idle_cond)
             if has_pin[TX]:
             if has_pin[TX]:
-                cond_idx[TX] = len(conds)
+                cond_data_idx[TX] = len(conds)
                 conds.append(self.get_wait_cond(TX, inv[TX]))
                 conds.append(self.get_wait_cond(TX, inv[TX]))
+                cond_edge_idx[TX] = len(conds)
+                conds.append({TX: 'e'})
+                cond_idle_idx[TX] = None
+                idle_cond = self.get_idle_cond(TX, inv[TX])
+                if idle_cond:
+                    cond_idle_idx[TX] = len(conds)
+                    conds.append(idle_cond)
             (rx, tx) = self.wait(conds)
             (rx, tx) = self.wait(conds)
-            if cond_idx[RX] is not None and self.matched[cond_idx[RX]]:
+            if cond_data_idx[RX] is not None and self.matched[cond_data_idx[RX]]:
                 self.inspect_sample(RX, rx, inv[RX])
                 self.inspect_sample(RX, rx, inv[RX])
-            if cond_idx[TX] is not None and self.matched[cond_idx[TX]]:
+            if cond_edge_idx[RX] is not None and self.matched[cond_edge_idx[RX]]:
+                self.inspect_edge(RX, rx, inv[RX])
+                self.inspect_idle(RX, rx, inv[RX])
+            if cond_idle_idx[RX] is not None and self.matched[cond_idle_idx[RX]]:
+                self.inspect_idle(RX, rx, inv[RX])
+            if cond_data_idx[TX] is not None and self.matched[cond_data_idx[TX]]:
                 self.inspect_sample(TX, tx, inv[TX])
                 self.inspect_sample(TX, tx, inv[TX])
+            if cond_edge_idx[TX] is not None and self.matched[cond_edge_idx[TX]]:
+                self.inspect_edge(TX, tx, inv[TX])
+                self.inspect_idle(TX, tx, inv[TX])
+            if cond_idle_idx[TX] is not None and self.matched[cond_idle_idx[TX]]:
+                self.inspect_idle(TX, tx, inv[TX])
index 489e58eb81419b10b548de0d12865413acfc5ad4..e262074e0ba7d4bbd6a0fb57d022f22bdfc6159b 100644 (file)
@@ -147,7 +147,7 @@ def reverse_number(num, count):
     out = list(count * '0')
     for i in range(0, count):
         if num >> i & 1:
     out = list(count * '0')
     for i in range(0, count):
         if num >> i & 1:
-            out[i] = '1';
+            out[i] = '1'
     return int(''.join(out), 2)
 
 def calc_crc5(bitstr):
     return int(''.join(out), 2)
 
 def calc_crc5(bitstr):
@@ -181,6 +181,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['usb_signalling']
     outputs = ['usb_packet']
     license = 'gplv2+'
     inputs = ['usb_signalling']
     outputs = ['usb_packet']
+    tags = ['PC']
     options = (
         {'id': 'signalling', 'desc': 'Signalling',
             'default': 'full-speed', 'values': ('full-speed', 'low-speed')},
     options = (
         {'id': 'signalling', 'desc': 'Signalling',
             'default': 'full-speed', 'values': ('full-speed', 'low-speed')},
index 936842921ba7463283c8f921e992ea0ea916d30f..d9209bfd0e6a4074618e4c6c5922453fd2de86ec 100644 (file)
@@ -203,6 +203,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['usb_pd']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['usb_pd']
+    tags = ['PC']
     channels = (
         {'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'},
     )
     channels = (
         {'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'},
     )
@@ -270,7 +271,7 @@ class Decoder(srd.Decoder):
         if pos in self.stored_pdos.keys():
             t_pdo = '#%d: %s' % (pos, self.stored_pdos[pos])
         else:
         if pos in self.stored_pdos.keys():
             t_pdo = '#%d: %s' % (pos, self.stored_pdos[pos])
         else:
-            t_pdo = '#d' % (pos)
+            t_pdo = '#%d' % (pos)
 
         return '(PDO %s) %s%s' % (t_pdo, t_settings, t_flags)
 
 
         return '(PDO %s) %s%s' % (t_pdo, t_settings, t_flags)
 
index e77debc46d784a06372960285cea3844bfab8585..3f46788fd56ba84eb2d670a315c45183e516d4a0 100644 (file)
@@ -116,10 +116,15 @@ class Decoder(srd.Decoder):
     id = 'usb_request'
     name = 'USB request'
     longname = 'Universal Serial Bus (LS/FS) transaction/request'
     id = 'usb_request'
     name = 'USB request'
     longname = 'Universal Serial Bus (LS/FS) transaction/request'
-    desc = 'USB (low-speed and full-speed) transaction/request protocol.'
+    desc = 'USB (low-speed/full-speed) transaction/request protocol.'
     license = 'gplv2+'
     inputs = ['usb_packet']
     outputs = ['usb_request']
     license = 'gplv2+'
     inputs = ['usb_packet']
     outputs = ['usb_request']
+    options = (
+        {'id': 'in_request_start', 'desc': 'Start IN requests on',
+            'default': 'submit', 'values': ('submit', 'first-ack')},
+    )
+    tags = ['PC']
     annotations = (
         ('request-setup-read', 'Setup: Device-to-host'),
         ('request-setup-write', 'Setup: Host-to-device'),
     annotations = (
         ('request-setup-read', 'Setup: Device-to-host'),
         ('request-setup-write', 'Setup: Host-to-device'),
@@ -128,7 +133,9 @@ class Decoder(srd.Decoder):
         ('errors', 'Unexpected packets'),
     )
     annotation_rows = (
         ('errors', 'Unexpected packets'),
     )
     annotation_rows = (
-        ('request', 'USB requests', tuple(range(4))),
+        ('request-setup', 'USB SETUP', (0, 1)),
+        ('request-in', 'USB BULK IN', (2,)),
+        ('request-out', 'USB BULK OUT', (3,)),
         ('errors', 'Errors', (4,)),
     )
     binary = (
         ('errors', 'Errors', (4,)),
     )
     binary = (
@@ -177,6 +184,7 @@ class Decoder(srd.Decoder):
     def start(self):
         self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_ann = self.register(srd.OUTPUT_ANN)
     def start(self):
         self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.in_request_start = self.options['in_request_start']
 
     def handle_transfer(self):
         request_started = 0
 
     def handle_transfer(self):
         request_started = 0
@@ -194,7 +202,7 @@ class Decoder(srd.Decoder):
         if not (addr, ep) in self.request:
             self.request[(addr, ep)] = {'setup_data': [], 'data': [],
                 'type': None, 'ss': self.ss_transaction, 'es': None,
         if not (addr, ep) in self.request:
             self.request[(addr, ep)] = {'setup_data': [], 'data': [],
                 'type': None, 'ss': self.ss_transaction, 'es': None,
-                'id': self.request_id, 'addr': addr, 'ep': ep}
+                'ss_data': None, 'id': self.request_id, 'addr': addr, 'ep': ep}
             self.request_id += 1
             request_started = 1
         request = self.request[(addr,ep)]
             self.request_id += 1
             request_started = 1
         request = self.request[(addr,ep)]
@@ -206,11 +214,14 @@ class Decoder(srd.Decoder):
         # BULK or INTERRUPT transfer
         if request['type'] in (None, 'BULK IN') and self.transaction_type == 'IN':
             request['type'] = 'BULK IN'
         # BULK or INTERRUPT transfer
         if request['type'] in (None, 'BULK IN') and self.transaction_type == 'IN':
             request['type'] = 'BULK IN'
+            if len(request['data']) == 0 and len(self.transaction_data) > 0:
+                request['ss_data'] = self.ss_transaction
             request['data'] += self.transaction_data
             self.handle_request(request_started, request_end)
         elif request['type'] in (None, 'BULK OUT') and self.transaction_type == 'OUT':
             request['type'] = 'BULK OUT'
             request['data'] += self.transaction_data
             self.handle_request(request_started, request_end)
         elif request['type'] in (None, 'BULK OUT') and self.transaction_type == 'OUT':
             request['type'] = 'BULK OUT'
-            request['data'] += self.transaction_data
+            if self.handshake == 'ACK':
+                request['data'] += self.transaction_data
             self.handle_request(request_started, request_end)
 
         # CONTROL, SETUP stage
             self.handle_request(request_started, request_end)
 
         # CONTROL, SETUP stage
@@ -230,7 +241,8 @@ class Decoder(srd.Decoder):
             request['data'] += self.transaction_data
 
         elif request['type'] == 'SETUP OUT' and self.transaction_type == 'OUT':
             request['data'] += self.transaction_data
 
         elif request['type'] == 'SETUP OUT' and self.transaction_type == 'OUT':
-            request['data'] += self.transaction_data
+            if self.handshake == 'ACK':
+                request['data'] += self.transaction_data
             if request['wLength'] == len(request['data']):
                 self.handle_request(1, 0)
 
             if request['wLength'] == len(request['data']):
                 self.handle_request(1, 0)
 
@@ -274,7 +286,9 @@ class Decoder(srd.Decoder):
         addr = self.transaction_addr
         request = self.request[(addr, ep)]
 
         addr = self.transaction_addr
         request = self.request[(addr, ep)]
 
-        ss, es = request['ss'], request['es']
+        ss, es, ss_data = request['ss'], request['es'], request['ss_data']
+        if self.in_request_start == 'submit':
+            ss_data = ss
 
         if request_start == 1:
             # Issue PCAP 'SUBMIT' packet.
 
         if request_start == 1:
             # Issue PCAP 'SUBMIT' packet.
@@ -291,7 +305,7 @@ class Decoder(srd.Decoder):
             elif request['type'] == 'SETUP OUT':
                 self.putr(ss, es, [1, ['SETUP out: %s' % summary]])
             elif request['type'] == 'BULK IN':
             elif request['type'] == 'SETUP OUT':
                 self.putr(ss, es, [1, ['SETUP out: %s' % summary]])
             elif request['type'] == 'BULK IN':
-                self.putr(ss, es, [2, ['BULK in: %s' % summary]])
+                self.putr(ss_data, es, [2, ['BULK in: %s' % summary]])
             elif request['type'] == 'BULK OUT':
                 self.putr(ss, es, [3, ['BULK out: %s' % summary]])
 
             elif request['type'] == 'BULK OUT':
                 self.putr(ss, es, [3, ['BULK out: %s' % summary]])
 
@@ -338,6 +352,8 @@ class Decoder(srd.Decoder):
             self.es_transaction = es
             self.transaction_state = 'TOKEN RECEIVED'
             self.transaction_ep = ep
             self.es_transaction = es
             self.transaction_state = 'TOKEN RECEIVED'
             self.transaction_ep = ep
+            if ep > 0 and pname == 'IN':
+                self.transaction_ep = ep + 0x80
             self.transaction_addr = addr
             self.transaction_type = pname # IN OUT SETUP
 
             self.transaction_addr = addr
             self.transaction_type = pname # IN OUT SETUP
 
index 424117dbe2cb5613744f6f54afd88b7022fa892c..626dcd0a3d00c40809227802d7f4f61fb3f813c1 100644 (file)
@@ -104,10 +104,11 @@ class Decoder(srd.Decoder):
     id = 'usb_signalling'
     name = 'USB signalling'
     longname = 'Universal Serial Bus (LS/FS) signalling'
     id = 'usb_signalling'
     name = 'USB signalling'
     longname = 'Universal Serial Bus (LS/FS) signalling'
-    desc = 'USB (low-speed and full-speed) signalling protocol.'
+    desc = 'USB (low-speed/full-speed) signalling protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['usb_signalling']
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['usb_signalling']
+    tags = ['PC']
     channels = (
         {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'},
         {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
     channels = (
         {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'},
         {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
@@ -141,7 +142,6 @@ class Decoder(srd.Decoder):
         self.samplerate = None
         self.oldsym = 'J' # The "idle" state is J.
         self.ss_block = None
         self.samplerate = None
         self.oldsym = 'J' # The "idle" state is J.
         self.ss_block = None
-        self.samplenum = 0
         self.bitrate = None
         self.bitwidth = None
         self.samplepos = None
         self.bitrate = None
         self.bitwidth = None
         self.samplepos = None
@@ -193,7 +193,7 @@ class Decoder(srd.Decoder):
         self.put(s, e, self.out_ann, data)
 
     def set_new_target_samplenum(self):
         self.put(s, e, self.out_ann, data)
 
     def set_new_target_samplenum(self):
-        self.samplepos += self.bitwidth;
+        self.samplepos += self.bitwidth
         self.samplenum_target = int(self.samplepos)
         self.samplenum_lastedge = self.samplenum_edge
         self.samplenum_edge = int(self.samplepos - (self.bitwidth / 2))
         self.samplenum_target = int(self.samplepos)
         self.samplenum_lastedge = self.samplenum_edge
         self.samplenum_edge = int(self.samplepos - (self.bitwidth / 2))
index c494789840131fb11d2b083145bc34188032596d..a93be109802ee6eea571ea5c68f6ca92a452f474 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'Wiegand interface for electronic entry systems.'
     license = 'gplv2+'
     inputs = ['logic']
     desc = 'Wiegand interface for electronic entry systems.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['wiegand']
+    outputs = []
+    tags = ['Embedded/industrial', 'RFID']
     channels = (
         {'id': 'd0', 'name': 'D0', 'desc': 'Data 0 line'},
         {'id': 'd1', 'name': 'D1', 'desc': 'Data 1 line'},
     channels = (
         {'id': 'd0', 'name': 'D0', 'desc': 'Data 0 line'},
         {'id': 'd1', 'name': 'D1', 'desc': 'Data 1 line'},
diff --git a/decoders/x2444m/__init__.py b/decoders/x2444m/__init__.py
new file mode 100644 (file)
index 0000000..70d2146
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Petersen <spe@ciellt.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the Xicor X2444M/P
+nonvolatile static RAM protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/x2444m/pd.py b/decoders/x2444m/pd.py
new file mode 100644 (file)
index 0000000..290cc36
--- /dev/null
@@ -0,0 +1,111 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Petersen <spe@ciellt.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## 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, see <http://www.gnu.org/licenses/>.
+##
+
+import re
+import sigrokdecode as srd
+
+registers = {
+    0x80: ['WRDS',  0, lambda _: ''],
+    0x81: ['STO',   1, lambda _: ''],
+    0x82: ['SLEEP', 2, lambda _: ''],
+    0x83: ['WRITE', 3, lambda v: '0x%x' % v],
+    0x84: ['WREN',  4, lambda _: ''],
+    0x85: ['RCL',   5, lambda _: ''],
+    0x86: ['READ',  6, lambda v: '0x%x' % v],
+    0x87: ['READ',  7, lambda v: '0x%x' % v],
+}
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'x2444m'
+    name = 'X2444M/P'
+    longname = 'Xicor X2444M/P'
+    desc = 'Xicor X2444M/P nonvolatile static RAM protocol.'
+    license = 'gplv2+'
+    inputs = ['spi']
+    outputs = []
+    tags = ['IC', 'Memory']
+    annotations = (
+        ('wrds', 'Write disable'),
+        ('sto', 'Store RAM data in EEPROM'),
+        ('sleep', 'Enter sleep mode'),
+        ('write', 'Write data into RAM'),
+        ('wren', 'Write enable'),
+        ('rcl', 'Recall EEPROM data into RAM'),
+        ('read', 'Data read from RAM'),
+        ('read', 'Data read from RAM'),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.cs_start = 0
+        self.cs_asserted = False
+        self.cmd_digit = 0
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putreadwrite(self, ss, es, reg, idx, addr, value):
+        self.put(ss, es, self.out_ann,
+                 [idx, ['%s: %s => 0x%4.4x' % (reg, addr, value),
+                        '%s: %s => 0x%4.4x' % (reg[0], addr, value), reg[0]]])
+
+    def putcmd(self, ss, es, reg, idx):
+        self.put(ss, es, self.out_ann, [idx, [reg, reg[0]]])
+
+    def decode(self, ss, es, data):
+        ptype, mosi, miso = data
+
+        if ptype == 'DATA':
+            if not self.cs_asserted:
+                return
+
+            if self.cmd_digit == 0:
+                self.addr = mosi
+                self.addr_start = ss
+            elif self.cmd_digit > 0:
+                self.read_value = (self.read_value << 8) + miso
+                self.write_value = (self.write_value << 8) + mosi
+            self.cmd_digit += 1
+        elif ptype == 'CS-CHANGE':
+            self.cs_asserted = (miso == 1)
+            # When not asserted, CS has just changed from asserted to deasserted.
+            if not self.cs_asserted:
+                # Only one digit, simple command. Else read/write.
+                if self.cmd_digit == 1:
+                    name, idx, decoder = registers[self.addr & 0x87]
+                    self.putcmd(self.addr_start, es, name, idx)
+                elif self.cmd_digit > 1:
+                    name, idx, decoder = registers[self.addr & 0x87]
+                    if name == 'READ':
+                        value = self.read_value
+                    elif name == 'WRITE':
+                        value = self.write_value
+                    else:
+                        value = 0
+                    self.putreadwrite(self.addr_start, es, name, idx,
+                                      decoder((self.addr >> 3) & 0x0f), value)
+
+            if self.cs_asserted:
+                self.cs_start = ss
+                self.cmd_digit = 0
+                self.read_value = 0
+                self.write_value = 0
index c4fb008062c56ffa202a60a468d3037cfe57766a..72f359505b1171f1931e6068c367da5edce8327a 100644 (file)
@@ -31,9 +31,8 @@ module startup. Other table are either reserved for future expansion, or
 available for vendor-specific extensions. This decoder supports both lower
 memory and table 0x01.
 
 available for vendor-specific extensions. This decoder supports both lower
 memory and table 0x01.
 
-The XFP specification is available here:
-
-  ftp://ftp.seagate.com/sff/INF-8077.PDF
+Details:
+ftp://ftp.seagate.com/sff/INF-8077.PDF (XFP specification)
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index 0bc6724511966f8ba43ba8f94076372b40d3209e..ded76946702fd41eb285caec63f84bfb4c500505 100644 (file)
@@ -27,10 +27,11 @@ class Decoder(srd.Decoder):
     id = 'xfp'
     name = 'XFP'
     longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
     id = 'xfp'
     name = 'XFP'
     longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
-    desc = 'Data structure describing display device capabilities.'
+    desc = 'XFP I²C management interface structures/protocol'
     license = 'gplv3+'
     inputs = ['i2c']
     license = 'gplv3+'
     inputs = ['i2c']
-    outputs = ['xfp']
+    outputs = []
+    tags = ['Networking']
     annotations = (
         ('fieldnames-and-values', 'XFP structure field names and values'),
         ('fields', 'XFP structure fields'),
     annotations = (
         ('fieldnames-and-values', 'XFP structure field names and values'),
         ('fields', 'XFP structure fields'),
index aaf7c76e76ed3e41e422f86f014d64f4507469b5..52ff9baccf646d0e74f527a8bbb0945aa7e323b5 100644 (file)
@@ -28,8 +28,9 @@ sampling clock, if applicable.
 Notes on the Z80 opcode format and descriptions of both documented and
 "undocumented" opcodes are available here:
 
 Notes on the Z80 opcode format and descriptions of both documented and
 "undocumented" opcodes are available here:
 
-  http://www.z80.info/decoding.htm
-  http://clrhome.org/table/
+Details:
+http://www.z80.info/decoding.htm
+http://clrhome.org/table/
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index b7ff9a56b16ac83cc62bba2e6781876241b306dc..fba279bf7406ad876a33d406e58f004ccf3995a0 100644 (file)
@@ -71,7 +71,8 @@ class Decoder(srd.Decoder):
     desc     = 'Zilog Z80 microprocessor disassembly.'
     license  = 'gplv3+'
     inputs   = ['logic']
     desc     = 'Zilog Z80 microprocessor disassembly.'
     license  = 'gplv3+'
     inputs   = ['logic']
-    outputs  = ['z80']
+    outputs  = []
+    tags     = ['Retro computing']
     channels = tuple({
             'id': 'd%d' % i,
             'name': 'D%d' % i,
     channels = tuple({
             'id': 'd%d' % i,
             'name': 'D%d' % i,
index 075bf09e45a3298662a82068b8c186dfe643c40f..5264bd0bd3a54d72f8503e926931f542ab310d96 100644 (file)
 
 extern SRD_PRIV GSList *sessions;
 
 
 extern SRD_PRIV GSList *sessions;
 
-static void srd_inst_join_decode_thread(struct srd_decoder_inst *di);
-static void srd_inst_reset_state(struct srd_decoder_inst *di);
-SRD_PRIV void oldpins_array_seed(struct srd_decoder_inst *di);
-SRD_PRIV void oldpins_array_free(struct srd_decoder_inst *di);
-
 /** @endcond */
 
 /**
 /** @endcond */
 
 /**
@@ -51,6 +46,37 @@ SRD_PRIV void oldpins_array_free(struct srd_decoder_inst *di);
  * @{
  */
 
  * @{
  */
 
+static void oldpins_array_seed(struct srd_decoder_inst *di)
+{
+       size_t count;
+       GArray *arr;
+
+       if (!di)
+               return;
+       if (di->old_pins_array)
+               return;
+
+       count = di->dec_num_channels;
+       arr = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), count);
+       g_array_set_size(arr, count);
+       if (arr->data)
+               memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count);
+       di->old_pins_array = arr;
+}
+
+static void oldpins_array_free(struct srd_decoder_inst *di)
+{
+       if (!di)
+               return;
+       if (!di->old_pins_array)
+               return;
+
+       srd_dbg("%s: Releasing initial pin state.", di->inst_id);
+
+       g_array_free(di->old_pins_array, TRUE);
+       di->old_pins_array = NULL;
+}
+
 /**
  * Set one or more options in a decoder instance.
  *
 /**
  * Set one or more options in a decoder instance.
  *
@@ -115,6 +141,7 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di,
        Py_DECREF(py_di_options);
        py_di_options = PyDict_New();
        PyObject_SetAttrString(di->py_inst, "options", py_di_options);
        Py_DECREF(py_di_options);
        py_di_options = PyDict_New();
        PyObject_SetAttrString(di->py_inst, "options", py_di_options);
+       Py_DECREF(py_di_options);
 
        for (l = di->decoder->options; l; l = l->next) {
                sdo = l->data;
 
        for (l = di->decoder->options; l; l = l->next) {
                sdo = l->data;
@@ -156,10 +183,13 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di,
                                goto err_out;
                        }
                }
                                goto err_out;
                        }
                }
-               if (PyDict_SetItemString(py_di_options, sdo->id, py_optval) == -1)
+               if (PyDict_SetItemString(py_di_options, sdo->id, py_optval) == -1) {
+                       Py_XDECREF(py_optval);
                        goto err_out;
                        goto err_out;
+               }
                /* Not harmful even if we used the default. */
                g_hash_table_remove(options, sdo->id);
                /* Not harmful even if we used the default. */
                g_hash_table_remove(options, sdo->id);
+               Py_XDECREF(py_optval);
        }
        if (g_hash_table_size(options) != 0)
                srd_warn("Unknown options specified for '%s'", di->inst_id);
        }
        if (g_hash_table_size(options) != 0)
                srd_warn("Unknown options specified for '%s'", di->inst_id);
@@ -167,7 +197,6 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di,
        ret = SRD_OK;
 
 err_out:
        ret = SRD_OK;
 
 err_out:
-       Py_XDECREF(py_optval);
        if (PyErr_Occurred()) {
                srd_exception_catch("Stray exception in srd_inst_option_set()");
                ret = SRD_ERR_PYTHON;
        if (PyErr_Occurred()) {
                srd_exception_catch("Stray exception in srd_inst_option_set()");
                ret = SRD_ERR_PYTHON;
@@ -314,7 +343,6 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess,
        PyGILState_STATE gstate;
 
        i = 1;
        PyGILState_STATE gstate;
 
        i = 1;
-       srd_dbg("Creating new %s instance.", decoder_id);
 
        if (!sess)
                return NULL;
 
        if (!sess)
                return NULL;
@@ -412,7 +440,7 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess,
 
        /* Instance takes input from a frontend by default. */
        sess->di_list = g_slist_append(sess->di_list, di);
 
        /* Instance takes input from a frontend by default. */
        sess->di_list = g_slist_append(sess->di_list, di);
-       srd_dbg("Created new %s instance with ID %s.", decoder_id, di->inst_id);
+       srd_dbg("Creating new %s instance %s.", decoder_id, di->inst_id);
 
        return di;
 }
 
        return di;
 }
@@ -505,10 +533,31 @@ SRD_API int srd_inst_stack(struct srd_session *sess,
                sess->di_list = g_slist_remove(sess->di_list, di_top);
        }
 
                sess->di_list = g_slist_remove(sess->di_list, di_top);
        }
 
+       /*
+        * Check if there's at least one matching input/output pair
+        * for the stacked PDs. We warn if that's not the case, but it's
+        * not a hard error for the time being.
+        */
+       gboolean at_least_one_match = FALSE;
+       for (GSList *out = di_bottom->decoder->outputs; out; out = out->next) {
+               const char *o = out->data;
+               for (GSList *in = di_top->decoder->inputs; in; in = in->next) {
+                       const char *i = in->data;
+                       if (!strcmp(o, i)) {
+                               at_least_one_match = TRUE;
+                               break;
+                       }
+               }
+       }
+
+       if (!at_least_one_match)
+               srd_warn("No matching in-/output when stacking %s onto %s.",
+                       di_top->inst_id, di_bottom->inst_id);
+
        /* Stack on top of source di. */
        di_bottom->next_di = g_slist_append(di_bottom->next_di, di_top);
 
        /* Stack on top of source di. */
        di_bottom->next_di = g_slist_append(di_bottom->next_di, di_top);
 
-       srd_dbg("Stacked %s onto %s.", di_top->inst_id, di_bottom->inst_id);
+       srd_dbg("Stacking %s onto %s.", di_top->inst_id, di_bottom->inst_id);
 
        return SRD_OK;
 }
 
        return SRD_OK;
 }
@@ -627,50 +676,16 @@ SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, GArray *i
        return SRD_OK;
 }
 
        return SRD_OK;
 }
 
-/** @private */
-SRD_PRIV void oldpins_array_seed(struct srd_decoder_inst *di)
-{
-       size_t count;
-       GArray *arr;
-
-       if (!di)
-               return;
-       if (di->old_pins_array)
-               return;
-
-       srd_dbg("%s: Seeding old pins, %s().", di->inst_id, __func__);
-       count = di->dec_num_channels;
-       arr = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), count);
-       g_array_set_size(arr, count);
-       memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count);
-       di->old_pins_array = arr;
-}
-
-/** @private */
-SRD_PRIV void oldpins_array_free(struct srd_decoder_inst *di)
-{
-       if (!di)
-               return;
-       if (!di->old_pins_array)
-               return;
-
-       srd_dbg("%s: Releasing initial pin state.", di->inst_id);
-
-       g_array_free(di->old_pins_array, TRUE);
-       di->old_pins_array = NULL;
-}
-
 /** @private */
 SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
 {
 /** @private */
 SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
 {
-       PyObject *py_res;
+       PyObject *py_res, *py_samplenum;
        GSList *l;
        struct srd_decoder_inst *next_di;
        int ret;
        PyGILState_STATE gstate;
 
        GSList *l;
        struct srd_decoder_inst *next_di;
        int ret;
        PyGILState_STATE gstate;
 
-       srd_dbg("Calling start() method on protocol decoder instance %s.",
-                       di->inst_id);
+       srd_dbg("Calling start() of instance %s.", di->inst_id);
 
        gstate = PyGILState_Ensure();
 
 
        gstate = PyGILState_Ensure();
 
@@ -681,10 +696,12 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
                PyGILState_Release(gstate);
                return SRD_ERR_PYTHON;
        }
                PyGILState_Release(gstate);
                return SRD_ERR_PYTHON;
        }
-       Py_DecRef(py_res);
+       Py_DECREF(py_res);
 
        /* Set self.samplenum to 0. */
 
        /* Set self.samplenum to 0. */
-       PyObject_SetAttrString(di->py_inst, "samplenum", PyLong_FromLong(0));
+       py_samplenum = PyLong_FromLong(0);
+       PyObject_SetAttrString(di->py_inst, "samplenum", py_samplenum);
+       Py_DECREF(py_samplenum);
 
        /* Set self.matched to None. */
        PyObject_SetAttrString(di->py_inst, "matched", Py_None);
 
        /* Set self.matched to None. */
        PyObject_SetAttrString(di->py_inst, "matched", Py_None);
@@ -784,6 +801,7 @@ SRD_PRIV void condition_list_free(struct srd_decoder_inst *di)
                        g_slist_free_full(ll, g_free);
        }
 
                        g_slist_free_full(ll, g_free);
        }
 
+       g_slist_free(di->condition_list);
        di->condition_list = NULL;
 }
 
        di->condition_list = NULL;
 }
 
@@ -814,6 +832,8 @@ static void update_old_pins_array(struct srd_decoder_inst *di,
 
        oldpins_array_seed(di);
        for (i = 0; i < di->dec_num_channels; i++) {
 
        oldpins_array_seed(di);
        for (i = 0; i < di->dec_num_channels; i++) {
+               if (di->dec_channelmap[i] == -1)
+                       continue; /* Ignore unused optional channels. */
                byte_offset = di->dec_channelmap[i] / 8;
                bit_offset = di->dec_channelmap[i] % 8;
                sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
                byte_offset = di->dec_channelmap[i] / 8;
                bit_offset = di->dec_channelmap[i] % 8;
                sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
@@ -836,6 +856,8 @@ static void update_old_pins_array_initial_pins(struct srd_decoder_inst *di)
        for (i = 0; i < di->dec_num_channels; i++) {
                if (di->old_pins_array->data[i] != SRD_INITIAL_PIN_SAME_AS_SAMPLE0)
                        continue;
        for (i = 0; i < di->dec_num_channels; i++) {
                if (di->old_pins_array->data[i] != SRD_INITIAL_PIN_SAME_AS_SAMPLE0)
                        continue;
+               if (di->dec_channelmap[i] == -1)
+                       continue; /* Ignore unused optional channels. */
                byte_offset = di->dec_channelmap[i] / 8;
                bit_offset = di->dec_channelmap[i] % 8;
                sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
                byte_offset = di->dec_channelmap[i] / 8;
                bit_offset = di->dec_channelmap[i] % 8;
                sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
@@ -873,6 +895,8 @@ static gboolean all_terms_match(const struct srd_decoder_inst *di,
 
        for (l = cond; l; l = l->next) {
                term = l->data;
 
        for (l = cond; l; l = l->next) {
                term = l->data;
+               if (term->type == SRD_TERM_ALWAYS_FALSE)
+                       return FALSE;
                if (!term_matches(di, term, sample_pos))
                        return FALSE;
        }
                if (!term_matches(di, term, sample_pos))
                        return FALSE;
        }
@@ -1029,10 +1053,10 @@ static gpointer di_thread(gpointer data)
         * Call self.decode(). Only returns if the PD throws an exception.
         * "Regular" termination of the decode() method is not expected.
         */
         * Call self.decode(). Only returns if the PD throws an exception.
         * "Regular" termination of the decode() method is not expected.
         */
-       Py_IncRef(di->py_inst);
-       srd_dbg("%s: Calling decode() method.", di->inst_id);
+       Py_INCREF(di->py_inst);
+       srd_dbg("%s: Calling decode().", di->inst_id);
        py_res = PyObject_CallMethod(di->py_inst, "decode", NULL);
        py_res = PyObject_CallMethod(di->py_inst, "decode", NULL);
-       srd_dbg("%s: decode() method terminated.", di->inst_id);
+       srd_dbg("%s: decode() terminated.", di->inst_id);
 
        if (!py_res)
                di->decoder_state = SRD_ERR;
 
        if (!py_res)
                di->decoder_state = SRD_ERR;
@@ -1085,7 +1109,7 @@ static gpointer di_thread(gpointer data)
         * decode() will re-start another thread transparently.
         */
        srd_dbg("%s: decode() terminated (req %d).", di->inst_id, wanted_term);
         * decode() will re-start another thread transparently.
         */
        srd_dbg("%s: decode() terminated (req %d).", di->inst_id, wanted_term);
-       Py_DecRef(py_res);
+       Py_DECREF(py_res);
        PyErr_Clear();
 
        PyGILState_Release(gstate);
        PyErr_Clear();
 
        PyGILState_Release(gstate);
@@ -1264,7 +1288,7 @@ SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di)
         */
        gstate = PyGILState_Ensure();
        if (PyObject_HasAttrString(di->py_inst, "reset")) {
         */
        gstate = PyGILState_Ensure();
        if (PyObject_HasAttrString(di->py_inst, "reset")) {
-               srd_dbg("Calling .reset() of instance %s", di->inst_id);
+               srd_dbg("Calling reset() of instance %s", di->inst_id);
                py_ret = PyObject_CallMethod(di->py_inst, "reset", NULL);
                Py_XDECREF(py_ret);
        }
                py_ret = PyObject_CallMethod(di->py_inst, "reset", NULL);
                Py_XDECREF(py_ret);
        }
@@ -1294,7 +1318,7 @@ SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di)
        srd_inst_reset_state(di);
 
        gstate = PyGILState_Ensure();
        srd_inst_reset_state(di);
 
        gstate = PyGILState_Ensure();
-       Py_DecRef(di->py_inst);
+       Py_DECREF(di->py_inst);
        PyGILState_Release(gstate);
 
        g_free(di->inst_id);
        PyGILState_Release(gstate);
 
        g_free(di->inst_id);
index 1d0931e0715df2bac1fdf3ada2a2b8fd6e064c0a..af245f6ad197f5b75ab8a2782a0399bb43b2a95c 100644 (file)
@@ -28,6 +28,7 @@
 #include "libsigrokdecode.h"
 
 enum {
 #include "libsigrokdecode.h"
 
 enum {
+       SRD_TERM_ALWAYS_FALSE,
        SRD_TERM_HIGH,
        SRD_TERM_LOW,
        SRD_TERM_RISING_EDGE,
        SRD_TERM_HIGH,
        SRD_TERM_LOW,
        SRD_TERM_RISING_EDGE,
@@ -108,6 +109,7 @@ SRD_PRIV long srd_decoder_apiver(const struct srd_decoder *d);
 
 /* type_decoder.c */
 SRD_PRIV PyObject *srd_Decoder_type_new(void);
 
 /* type_decoder.c */
 SRD_PRIV PyObject *srd_Decoder_type_new(void);
+SRD_PRIV const char *output_type_name(unsigned int idx);
 
 /* type_logic.c */
 SRD_PRIV PyObject *srd_logic_type_new(void);
 
 /* type_logic.c */
 SRD_PRIV PyObject *srd_logic_type_new(void);
@@ -122,7 +124,7 @@ SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **out
 SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, char **outstr);
 SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx, char **outstr);
 SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, char **outstr);
 SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, char **outstr);
 SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx, char **outstr);
 SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, char **outstr);
-SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out);
+SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, int64_t *out);
 SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr);
 SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv);
 SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj);
 SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr);
 SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv);
 SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj);
index 55620a7f52551df6e01ea1521711ce34fdf12496..b9cc921287e876347ceea9d1ea08f61a46046f2e 100644 (file)
@@ -161,6 +161,9 @@ struct srd_decoder {
        /** List of possible decoder output IDs. */
        GSList *outputs;
 
        /** List of possible decoder output IDs. */
        GSList *outputs;
 
+       /** List of tags associated with this decoder. */
+       GSList *tags;
+
        /** List of channels required by this decoder. */
        GSList *channels;
 
        /** List of channels required by this decoder. */
        GSList *channels;
 
@@ -168,8 +171,8 @@ struct srd_decoder {
        GSList *opt_channels;
 
        /**
        GSList *opt_channels;
 
        /**
-        * List of NULL-terminated char[], containing descriptions of the
-        * supported annotation output.
+        * List of annotation classes. Each list item is a GSList itself, with
+        * two NUL-terminated strings: name and description.
         */
        GSList *annotations;
 
         */
        GSList *annotations;
 
@@ -180,8 +183,8 @@ struct srd_decoder {
        GSList *annotation_rows;
 
        /**
        GSList *annotation_rows;
 
        /**
-        * List of NULL-terminated char[], containing descriptions of the
-        * supported binary output.
+        * List of binary classes. Each list item is a GSList itself, with
+        * two NUL-terminated strings: name and description.
         */
        GSList *binary;
 
         */
        GSList *binary;
 
@@ -303,11 +306,11 @@ struct srd_proto_data {
        void *data;
 };
 struct srd_proto_data_annotation {
        void *data;
 };
 struct srd_proto_data_annotation {
-       int ann_class;
+       int ann_class; /* Index into "struct srd_decoder"->annotations. */
        char **ann_text;
 };
 struct srd_proto_data_binary {
        char **ann_text;
 };
 struct srd_proto_data_binary {
-       int bin_class;
+       int bin_class; /* Index into "struct srd_decoder"->binary. */
        uint64_t size;
        const unsigned char *data;
 };
        uint64_t size;
        const unsigned char *data;
 };
index 69494eac3f3fde083572ff589342cbd0677b3dbe..386fb710f5cb7a73506a15e5ea047a7b4f715cbb 100644 (file)
--- a/session.c
+++ b/session.c
@@ -70,7 +70,7 @@ SRD_API int srd_session_new(struct srd_session **sess)
        /* Keep a list of all sessions, so we can clean up as needed. */
        sessions = g_slist_append(sessions, *sess);
 
        /* Keep a list of all sessions, so we can clean up as needed. */
        sessions = g_slist_append(sessions, *sess);
 
-       srd_dbg("Created session %d.", (*sess)->session_id);
+       srd_dbg("Creating session %d.", (*sess)->session_id);
 
        return SRD_OK;
 }
 
        return SRD_OK;
 }
@@ -96,9 +96,9 @@ SRD_API int srd_session_start(struct srd_session *sess)
        if (!sess)
                return SRD_ERR_ARG;
 
        if (!sess)
                return SRD_ERR_ARG;
 
-       srd_dbg("Calling start() on all instances in session %d.", sess->session_id);
+       srd_dbg("Calling start() of all instances in session %d.", sess->session_id);
 
 
-       /* Run the start() method on all decoders receiving frontend data. */
+       /* Run the start() method of all decoders receiving frontend data. */
        ret = SRD_OK;
        for (d = sess->di_list; d; d = d->next) {
                di = d->data;
        ret = SRD_OK;
        for (d = sess->di_list; d; d = d->next) {
                di = d->data;
@@ -372,7 +372,8 @@ SRD_API int srd_pd_output_callback_add(struct srd_session *sess,
        if (!sess)
                return SRD_ERR_ARG;
 
        if (!sess)
                return SRD_ERR_ARG;
 
-       srd_dbg("Registering new callback for output type %d.", output_type);
+       srd_dbg("Registering new callback for output type %s.",
+               output_type_name(output_type));
 
        pd_cb = g_malloc(sizeof(struct srd_pd_callback));
        pd_cb->output_type = output_type;
 
        pd_cb = g_malloc(sizeof(struct srd_pd_callback));
        pd_cb->output_type = output_type;
diff --git a/srd.c b/srd.c
index 5903c6d7e75b748149218ffa459323f5d8ae804b..22f47fcf26a869ad8856e90b6f7f34bc76be5741 100644 (file)
--- a/srd.c
+++ b/srd.c
@@ -172,6 +172,7 @@ static int print_searchpaths(void)
                py_path = PyList_GetItem(py_paths, i);
                py_bytes = PyUnicode_AsUTF8String(py_path);
                g_string_append_printf(s, " - %s\n", PyBytes_AsString(py_bytes));
                py_path = PyList_GetItem(py_paths, i);
                py_bytes = PyUnicode_AsUTF8String(py_path);
                g_string_append_printf(s, " - %s\n", PyBytes_AsString(py_bytes));
+               Py_DECREF(py_bytes);
        }
        s->str[s->len - 1] = '\0';
        srd_dbg("%s", s->str);
        }
        s->str[s->len - 1] = '\0';
        srd_dbg("%s", s->str);
@@ -289,6 +290,12 @@ SRD_API int srd_init(const char *path)
        return SRD_OK;
 }
 
        return SRD_OK;
 }
 
+static void srd_session_destroy_cb(void *arg, void *ignored)
+{
+       (void)ignored; // Prevent unused warning
+       srd_session_destroy((struct srd_session *)arg);
+}
+
 /**
  * Shutdown libsigrokdecode.
  *
 /**
  * Shutdown libsigrokdecode.
  *
@@ -307,8 +314,9 @@ SRD_API int srd_exit(void)
 {
        srd_dbg("Exiting libsigrokdecode.");
 
 {
        srd_dbg("Exiting libsigrokdecode.");
 
-       for (GSList *l = sessions; l; l = l->next)
-               srd_session_destroy(l->data);
+       g_slist_foreach(sessions, srd_session_destroy_cb, NULL);
+       g_slist_free(sessions);
+       sessions = NULL;
 
        srd_decoder_unload_all();
        g_slist_free_full(searchpaths, g_free);
 
        srd_decoder_unload_all();
        g_slist_free_full(searchpaths, g_free);
@@ -347,8 +355,6 @@ SRD_API int srd_exit(void)
  * @return SRD_OK upon success, a (negative) error code otherwise.
  *
  * @private
  * @return SRD_OK upon success, a (negative) error code otherwise.
  *
  * @private
- *
- * @since 0.1.0
  */
 SRD_PRIV int srd_decoder_searchpath_add(const char *path)
 {
  */
 SRD_PRIV int srd_decoder_searchpath_add(const char *path)
 {
@@ -396,7 +402,12 @@ err:
  */
 SRD_API GSList *srd_searchpaths_get(void)
 {
  */
 SRD_API GSList *srd_searchpaths_get(void)
 {
-       return g_slist_copy_deep(searchpaths, (GCopyFunc)g_strdup, NULL);
+       GSList *paths = NULL;
+
+       for (GSList *l = searchpaths; l; l = l->next)
+               paths = g_slist_append(paths, g_strdup(l->data));
+
+       return paths;
 }
 
 /** @} */
 }
 
 /** @} */
index 2298d9334698af70e0cd4406cb16f825c1ce06f7..3acabaeca30e2c7c30c910eec8ff0c61323596e1 100644 (file)
@@ -388,11 +388,14 @@ END_TEST
 START_TEST(test_doc_get)
 {
        struct srd_decoder *dec;
 START_TEST(test_doc_get)
 {
        struct srd_decoder *dec;
+       char *doc;
 
        srd_init(DECODERS_TESTDIR);
        srd_decoder_load("uart");
        dec = srd_decoder_get_by_id("uart");
 
        srd_init(DECODERS_TESTDIR);
        srd_decoder_load("uart");
        dec = srd_decoder_get_by_id("uart");
-       fail_unless(srd_decoder_doc_get(dec) != NULL);
+       doc = srd_decoder_doc_get(dec);
+       fail_unless(doc != NULL);
+       g_free(doc);
        srd_exit();
 }
 END_TEST
        srd_exit();
 }
 END_TEST
@@ -401,11 +404,19 @@ END_TEST
  * Check whether srd_decoder_doc_get() fails with NULL as argument.
  * If it returns a value != NULL (or segfaults) this test will fail.
  * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=179
  * Check whether srd_decoder_doc_get() fails with NULL as argument.
  * If it returns a value != NULL (or segfaults) this test will fail.
  * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=179
+ * Check whether srd_decoder_doc_get() fails with dec->py_mod == NULL.
+ * If it returns a value != NULL (or segfaults) this test will fail.
+ * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=180
  */
 START_TEST(test_doc_get_null)
 {
  */
 START_TEST(test_doc_get_null)
 {
+       struct srd_decoder dec;
+
+       dec.py_mod = NULL;
+
        srd_init(DECODERS_TESTDIR);
        fail_unless(srd_decoder_doc_get(NULL) == NULL);
        srd_init(DECODERS_TESTDIR);
        fail_unless(srd_decoder_doc_get(NULL) == NULL);
+       fail_unless(srd_decoder_doc_get(&dec) == NULL);
        srd_exit();
 }
 END_TEST
        srd_exit();
 }
 END_TEST
index e79496e576da56e949c80b75d68ae26c9e444b40..73b7669e20cea6d4c8ff83f41594228e2f23a71c 100644 (file)
@@ -148,10 +148,13 @@ static void conf_check_ok(struct srd_session *sess, int key, uint64_t x)
 static void conf_check_fail(struct srd_session *sess, int key, uint64_t x)
 {
        int ret;
 static void conf_check_fail(struct srd_session *sess, int key, uint64_t x)
 {
        int ret;
+       GVariant *value = g_variant_new_uint64(x);
 
 
-       ret = srd_session_metadata_set(sess, key, g_variant_new_uint64(x));
+       ret = srd_session_metadata_set(sess, key, value);
        fail_unless(ret != SRD_OK, "srd_session_metadata_set(%p, %d, %"
                PRIu64 ") worked.", sess, key, x);
        fail_unless(ret != SRD_OK, "srd_session_metadata_set(%p, %d, %"
                PRIu64 ") worked.", sess, key, x);
+       if (ret != SRD_OK)
+               g_variant_unref(value);
 }
 
 static void conf_check_fail_null(struct srd_session *sess, int key)
 }
 
 static void conf_check_fail_null(struct srd_session *sess, int key)
@@ -166,10 +169,13 @@ static void conf_check_fail_null(struct srd_session *sess, int key)
 static void conf_check_fail_str(struct srd_session *sess, int key, const char *s)
 {
        int ret;
 static void conf_check_fail_str(struct srd_session *sess, int key, const char *s)
 {
        int ret;
+       GVariant *value = g_variant_new_string(s);
 
 
-       ret = srd_session_metadata_set(sess, key, g_variant_new_string(s));
+       ret = srd_session_metadata_set(sess, key, value);
        fail_unless(ret != SRD_OK, "srd_session_metadata_set() for key %d "
                "failed: %d.", key, ret);
        fail_unless(ret != SRD_OK, "srd_session_metadata_set() for key %d "
                "failed: %d.", key, ret);
+       if (ret != SRD_OK)
+               g_variant_unref(value);
 }
 
 /*
 }
 
 /*
index 6692c3d44e34c54960381aa56812748726e81406..c097c7fce5b94cb950d3cb03c0fb4e52371a0df2 100644 (file)
@@ -31,7 +31,7 @@ typedef struct {
 } srd_Decoder;
 
 /* This is only used for nicer srd_dbg() output. */
 } srd_Decoder;
 
 /* This is only used for nicer srd_dbg() output. */
-static const char *output_type_name(unsigned int idx)
+SRD_PRIV const char *output_type_name(unsigned int idx)
 {
        static const char names[][16] = {
                "OUTPUT_ANN",
 {
        static const char names[][16] = {
                "OUTPUT_ANN",
@@ -254,7 +254,7 @@ static inline struct srd_decoder_inst *srd_inst_find_by_obj(
        sess = sessions->data;
        di = sess->di_list->data;
        if (di->py_inst == obj)
        sess = sessions->data;
        di = sess->di_list->data;
        if (di->py_inst == obj)
-               return di; 
+               return di;
 
        di = NULL;
        for (l = sessions; di == NULL && l != NULL; l = l->next) {
 
        di = NULL;
        for (l = sessions; di == NULL && l != NULL; l = l->next) {
@@ -353,9 +353,13 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
        }
        pdo = l->data;
 
        }
        pdo = l->data;
 
-       srd_spew("Instance %s put %" PRIu64 "-%" PRIu64 " %s on oid %d.",
-                di->inst_id, start_sample, end_sample,
-                output_type_name(pdo->output_type), output_id);
+       /* Upon SRD_OUTPUT_PYTHON for stacked PDs, we have a nicer log message later. */
+       if (pdo->output_type != SRD_OUTPUT_PYTHON && di->next_di != NULL) {
+               srd_spew("Instance %s put %" PRIu64 "-%" PRIu64 " %s on "
+                        "oid %d (%s).", di->inst_id, start_sample, end_sample,
+                        output_type_name(pdo->output_type), output_id,
+                        pdo->proto_id);
+       }
 
        pdata.start_sample = start_sample;
        pdata.end_sample = end_sample;
 
        pdata.start_sample = start_sample;
        pdata.end_sample = end_sample;
@@ -381,8 +385,11 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
        case SRD_OUTPUT_PYTHON:
                for (l = di->next_di; l; l = l->next) {
                        next_di = l->data;
        case SRD_OUTPUT_PYTHON:
                for (l = di->next_di; l; l = l->next) {
                        next_di = l->data;
-                       srd_spew("Sending %" PRIu64 "-%" PRIu64 " to instance %s",
-                                start_sample, end_sample, next_di->inst_id);
+                       srd_spew("Instance %s put %" PRIu64 "-%" PRIu64 " %s "
+                                "on oid %d (%s) to instance %s.", di->inst_id,
+                                start_sample,
+                                end_sample, output_type_name(pdo->output_type),
+                                output_id, pdo->proto_id, next_di->inst_id);
                        if (!(py_res = PyObject_CallMethod(
                                next_di->py_inst, "decode", "KKO", start_sample,
                                end_sample, py_data))) {
                        if (!(py_res = PyObject_CallMethod(
                                next_di->py_inst, "decode", "KKO", start_sample,
                                end_sample, py_data))) {
@@ -514,9 +521,6 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
                return py_new_output_id;
        }
 
                return py_new_output_id;
        }
 
-       srd_dbg("Instance %s creating new output type %d for %s.",
-               di->inst_id, output_type, proto_id);
-
        pdo = g_malloc(sizeof(struct srd_pd_output));
 
        /* pdo_id is just a simple index, nothing is deleted from this list anyway. */
        pdo = g_malloc(sizeof(struct srd_pd_output));
 
        /* pdo_id is just a simple index, nothing is deleted from this list anyway. */
@@ -536,6 +540,10 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
 
        PyGILState_Release(gstate);
 
 
        PyGILState_Release(gstate);
 
+       srd_dbg("Instance %s creating new output type %s as oid %d (%s).",
+               di->inst_id, output_type_name(output_type), pdo->pdo_id,
+               proto_id);
+
        return py_new_output_id;
 
 err:
        return py_new_output_id;
 
 err:
@@ -617,18 +625,20 @@ static PyObject *get_current_pinvalues(const struct srd_decoder_inst *di)
  *
  * If there are no terms in the condition, 'term_list' will be NULL.
  *
  *
  * If there are no terms in the condition, 'term_list' will be NULL.
  *
+ * @param di The decoder instance to use. Must not be NULL.
  * @param py_dict A Python dict containing terms. Must not be NULL.
  * @param term_list Pointer to a GSList which will be set to the newly
  *                  created list of terms. Must not be NULL.
  *
  * @return SRD_OK upon success, a negative error code otherwise.
  */
  * @param py_dict A Python dict containing terms. Must not be NULL.
  * @param term_list Pointer to a GSList which will be set to the newly
  *                  created list of terms. Must not be NULL.
  *
  * @return SRD_OK upon success, a negative error code otherwise.
  */
-static int create_term_list(PyObject *py_dict, GSList **term_list)
+static int create_term_list(struct srd_decoder_inst *di,
+       PyObject *py_dict, GSList **term_list)
 {
        Py_ssize_t pos = 0;
        PyObject *py_key, *py_value;
        struct srd_term *term;
 {
        Py_ssize_t pos = 0;
        PyObject *py_key, *py_value;
        struct srd_term *term;
-       uint64_t num_samples_to_skip;
+       int64_t num_samples_to_skip;
        char *term_str;
        PyGILState_STATE gstate;
 
        char *term_str;
        PyGILState_STATE gstate;
 
@@ -645,7 +655,6 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
                /* Check whether the current key is a string or a number. */
                if (PyLong_Check(py_key)) {
                        /* The key is a number. */
                /* Check whether the current key is a string or a number. */
                if (PyLong_Check(py_key)) {
                        /* The key is a number. */
-                       /* TODO: Check if the number is a valid channel. */
                        /* Get the value string. */
                        if ((py_pydictitem_as_str(py_dict, py_key, &term_str)) != SRD_OK) {
                                srd_err("Failed to get the value.");
                        /* Get the value string. */
                        if ((py_pydictitem_as_str(py_dict, py_key, &term_str)) != SRD_OK) {
                                srd_err("Failed to get the value.");
@@ -654,10 +663,12 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
                        term = g_malloc(sizeof(struct srd_term));
                        term->type = get_term_type(term_str);
                        term->channel = PyLong_AsLong(py_key);
                        term = g_malloc(sizeof(struct srd_term));
                        term->type = get_term_type(term_str);
                        term->channel = PyLong_AsLong(py_key);
+                       if (term->channel < 0 || term->channel >= di->dec_num_channels)
+                               term->type = SRD_TERM_ALWAYS_FALSE;
                        g_free(term_str);
                } else if (PyUnicode_Check(py_key)) {
                        /* The key is a string. */
                        g_free(term_str);
                } else if (PyUnicode_Check(py_key)) {
                        /* The key is a string. */
-                       /* TODO: Check if it's "skip". */
+                       /* TODO: Check if the key is "skip". */
                        if ((py_pydictitem_as_long(py_dict, py_key, &num_samples_to_skip)) != SRD_OK) {
                                srd_err("Failed to get number of samples to skip.");
                                goto err;
                        if ((py_pydictitem_as_long(py_dict, py_key, &num_samples_to_skip)) != SRD_OK) {
                                srd_err("Failed to get number of samples to skip.");
                                goto err;
@@ -666,6 +677,8 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
                        term->type = SRD_TERM_SKIP;
                        term->num_samples_to_skip = num_samples_to_skip;
                        term->num_samples_already_skipped = 0;
                        term->type = SRD_TERM_SKIP;
                        term->num_samples_to_skip = num_samples_to_skip;
                        term->num_samples_already_skipped = 0;
+                       if (num_samples_to_skip < 0)
+                               term->type = SRD_TERM_ALWAYS_FALSE;
                } else {
                        srd_err("Term key is neither a string nor a number.");
                        goto err;
                } else {
                        srd_err("Term key is neither a string nor a number.");
                        goto err;
@@ -745,14 +758,14 @@ static int set_new_condition_list(PyObject *self, PyObject *args)
                num_conditions = PyList_Size(py_conditionlist);
                if (num_conditions == 0)
                        goto ret_9999; /* The PD invoked self.wait([]). */
                num_conditions = PyList_Size(py_conditionlist);
                if (num_conditions == 0)
                        goto ret_9999; /* The PD invoked self.wait([]). */
-               Py_IncRef(py_conditionlist);
+               Py_INCREF(py_conditionlist);
        } else if (PyDict_Check(py_conds)) {
                /* 'py_conds' is a dict. */
                if (PyDict_Size(py_conds) == 0)
                        goto ret_9999; /* The PD invoked self.wait({}). */
                /* Make a list and put the dict in there for convenience. */
                py_conditionlist = PyList_New(1);
        } else if (PyDict_Check(py_conds)) {
                /* 'py_conds' is a dict. */
                if (PyDict_Size(py_conds) == 0)
                        goto ret_9999; /* The PD invoked self.wait({}). */
                /* Make a list and put the dict in there for convenience. */
                py_conditionlist = PyList_New(1);
-               Py_IncRef(py_conds);
+               Py_INCREF(py_conds);
                PyList_SetItem(py_conditionlist, 0, py_conds);
                num_conditions = 1;
        } else {
                PyList_SetItem(py_conditionlist, 0, py_conds);
                num_conditions = 1;
        } else {
@@ -776,7 +789,7 @@ static int set_new_condition_list(PyObject *self, PyObject *args)
                }
 
                /* Create the list of terms in this condition. */
                }
 
                /* Create the list of terms in this condition. */
-               if ((ret = create_term_list(py_dict, &term_list)) < 0)
+               if ((ret = create_term_list(di, py_dict, &term_list)) < 0)
                        break;
 
                /* Add the new condition to the PD instance's condition list. */
                        break;
 
                /* Add the new condition to the PD instance's condition list. */
@@ -842,7 +855,7 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
        unsigned int i;
        gboolean found_match;
        struct srd_decoder_inst *di;
        unsigned int i;
        gboolean found_match;
        struct srd_decoder_inst *di;
-       PyObject *py_pinvalues, *py_matched;
+       PyObject *py_pinvalues, *py_matched, *py_samplenum;
        PyGILState_STATE gstate;
 
        if (!self || !args)
        PyGILState_STATE gstate;
 
        if (!self || !args)
@@ -909,14 +922,16 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
                /* If there's a match, set self.samplenum etc. and return. */
                if (found_match) {
                        /* Set self.samplenum to the (absolute) sample number that matched. */
                /* If there's a match, set self.samplenum etc. and return. */
                if (found_match) {
                        /* Set self.samplenum to the (absolute) sample number that matched. */
-                       PyObject_SetAttrString(di->py_inst, "samplenum",
-                               PyLong_FromLong(di->abs_cur_samplenum));
+                       py_samplenum = PyLong_FromLong(di->abs_cur_samplenum);
+                       PyObject_SetAttrString(di->py_inst, "samplenum", py_samplenum);
+                       Py_DECREF(py_samplenum);
 
                        if (di->match_array && di->match_array->len > 0) {
                                py_matched = PyTuple_New(di->match_array->len);
                                for (i = 0; i < di->match_array->len; i++)
                                        PyTuple_SetItem(py_matched, i, PyBool_FromLong(di->match_array->data[i]));
                                PyObject_SetAttrString(di->py_inst, "matched", py_matched);
 
                        if (di->match_array && di->match_array->len > 0) {
                                py_matched = PyTuple_New(di->match_array->len);
                                for (i = 0; i < di->match_array->len; i++)
                                        PyTuple_SetItem(py_matched, i, PyBool_FromLong(di->match_array->data[i]));
                                PyObject_SetAttrString(di->py_inst, "matched", py_matched);
+                               Py_DECREF(py_matched);
                                match_array_free(di);
                        } else {
                                PyObject_SetAttrString(di->py_inst, "matched", Py_None);
                                match_array_free(di);
                        } else {
                                PyObject_SetAttrString(di->py_inst, "matched", Py_None);
diff --git a/util.c b/util.c
index 442d0a6e1433c47abd035cc0f42839b8df9b4e73..1e914e3e44a1ee0ae2b0d735cd87f29345f66551 100644 (file)
--- a/util.c
+++ b/util.c
@@ -303,7 +303,7 @@ err:
  *
  * @private
  */
  *
  * @private
  */
-SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out)
+SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, int64_t *out)
 {
        PyObject *py_value;
        PyGILState_STATE gstate;
 {
        PyObject *py_value;
        PyGILState_STATE gstate;
@@ -328,7 +328,7 @@ SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t
                goto err;
        }
 
                goto err;
        }
 
-       *out = PyLong_AsUnsignedLongLong(py_value);
+       *out = PyLong_AsLongLong(py_value);
 
        PyGILState_Release(gstate);
 
 
        PyGILState_Release(gstate);
 
@@ -385,7 +385,7 @@ SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr)
 
 /**
  * Convert a Python list of unicode strings to a C string vector.
 
 /**
  * Convert a Python list of unicode strings to a C string vector.
- * On success, a pointer to a newly allocated NULL-terminated array of
+ * On success, a pointer to a newly allocated NUL-terminated array of
  * allocated C strings is written to @a out_strv. The caller must g_free()
  * each string and the array itself.
  *
  * allocated C strings is written to @a out_strv. The caller must g_free()
  * each string and the array itself.
  *