]> 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'
 
+ - 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
 ---------
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:
-   - 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.
@@ -331,7 +331,7 @@ means it is NOT backwards-compatible and frontends will need updates.
  * 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).
index 56f01a248d4640b38055b0624f6ab4eb5d1b0ee4..0e2416f8fa29eb7039ca1f1c44c130c8897bb4ec 100644 (file)
@@ -89,15 +89,18 @@ SRD_PKGLIBS_TESTS=
 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],
-       [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])
-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.])])
 
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->tags, g_free);
        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. */
 
+       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;
@@ -797,6 +803,11 @@ SRD_API int srd_decoder_load(const char *module_name)
                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";
@@ -858,7 +869,7 @@ err_out:
 /**
  * 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.
@@ -874,7 +885,7 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec)
        if (!srd_check_init())
                return NULL;
 
-       if (!dec)
+       if (!dec || !dec->py_mod)
                return NULL;
 
        gstate = PyGILState_Ensure();
@@ -1068,6 +1079,13 @@ SRD_API int srd_decoder_load_all(void)
        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.
  *
@@ -1077,8 +1095,7 @@ SRD_API int srd_decoder_load_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;
 
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.
@@ -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.
+
+Details:
+http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
 '''
 
 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']
-    outputs = ['ac97']
+    outputs = []
+    tags = ['Audio', 'PC']
     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.out_binary = None
-        self.out_ann = None
         self.reset()
 
     def reset(self):
@@ -167,10 +166,8 @@ class Decoder(srd.Decoder):
         }
 
     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:
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
-## 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']
-    outputs = ['ade77xx']
+    outputs = []
+    tags = ['Analog/digital', 'IC', 'Sensor']
     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']
-    outputs = ['adf435x']
+    outputs = []
+    tags = ['Clock/timing', 'IC', 'Wireless/RF']
     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
-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
index cd72eca8b46d93432379469b33d10031296b9d5f..9ac778e038b87c6f9c7ada936310cbc5a7de4181 100644 (file)
@@ -42,11 +42,12 @@ class Decoder(srd.Decoder):
     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']
-    outputs = ['adns5020']
+    outputs = []
+    tags = ['IC', 'PC', 'Sensor']
     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'
-    name = 'AM230x/DHTxx/RHTxx'
+    name = 'AM230x'
     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']
-    outputs = ['am230x']
+    outputs = []
+    tags = ['IC', 'Sensor']
     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.
 '''
 
index 8de3ce2c8d40915af88ef580f1a645a88d2e62b0..6649b46e021dd781c0a5a06098fb5fc0af012fb8 100644 (file)
@@ -31,30 +31,30 @@ exc_names = [
 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
-    for i, b in enumerate(bytes):
+    for i, b in enumerate(bytes_):
         v |= (b & 0x7F) << (i * 7)
         if b & 0x80 == 0:
             return v, i+1
-    return v, len(bytes)
+    return v, len(bytes_)
 
-def parse_uint(bytes):
+def parse_uint(bytes_):
     '''Parse little-endian integer.'''
     v = 0
-    for i, b in enumerate(bytes):
+    for i, b in enumerate(bytes_):
         v |= b << (i * 8)
     return v
 
-def parse_exc_info(bytes):
+def parse_exc_info(bytes_):
     '''Parse exception information bytes from a branch packet.'''
-    if len(bytes) < 1:
+    if len(bytes_) < 1:
         return None
 
-    excv, exclen = parse_varint(bytes)
-    if bytes[exclen - 1] & 0x80 != 0x00:
+    excv, exclen = parse_varint(bytes_)
+    if bytes_[exclen - 1] & 0x80 != 0x00:
         return None # Exception info not complete.
 
     if exclen == 2 and excv & (1 << 13):
@@ -69,21 +69,21 @@ def parse_exc_info(bytes):
     resume = (excv >> 14) & 0x0F
     return (ns, exc, cancel, altisa, hyp, resume)
 
-def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc):
+def parse_branch_addr(bytes_, ref_addr, cpu_state, branch_enc):
     '''Parse encoded branch address.
        Returns addr, addrlen, cpu_state, exc_info.
        Returns None if packet is not yet complete'''
 
-    addr, addrlen = parse_varint(bytes)
+    addr, addrlen = parse_varint(bytes_)
 
-    if bytes[addrlen-1] & 0x80 != 0x00:
+    if bytes_[addrlen - 1] & 0x80 != 0x00:
         return None # Branch address not complete.
 
     addr_bits = 7 * addrlen
 
     have_exc_info = False
     if branch_enc == 'original':
-        if addrlen == 5 and bytes[4] & 0x40:
+        if addrlen == 5 and bytes_[4] & 0x40:
             have_exc_info = True
     elif branch_enc == 'alternative':
         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 = parse_exc_info(bytes[addrlen:])
+        exc_info = parse_exc_info(bytes_[addrlen:])
         if exc_info is None:
             return None # Exception info not complete.
 
     if addrlen == 5:
         # Possible change in CPU state.
-        if bytes[4] & 0xB8 == 0x08:
+        if bytes_[4] & 0xB8 == 0x08:
             cpu_state = 'arm'
-        elif bytes[4] & 0xB0 == 0x10:
+        elif bytes_[4] & 0xB0 == 0x10:
             cpu_state = 'thumb'
-        elif bytes[4] & 0xA0 == 0x20:
+        elif bytes_[4] & 0xA0 == 0x20:
             cpu_state = 'jazelle'
         else:
-            raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes[4])
+            raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes_[4])
 
     # Shift the address according to current CPU state.
     if cpu_state == 'arm':
@@ -130,11 +130,12 @@ class Decoder(srd.Decoder):
     api_version = 3
     id = 'arm_etmv3'
     name = 'ARM ETMv3'
-    longname = 'ARM Embedded Trace Macroblock'
-    desc = 'Decode ETM instruction trace packets.'
+    longname = 'ARM Embedded Trace Macroblock v3'
+    desc = 'ARM ETM v3 instruction trace protocol.'
     license = 'gplv2+'
     inputs = ['uart']
-    outputs = ['arm_etmv3']
+    outputs = []
+    tags = ['Debug/trace']
     annotations = (
         ('trace', 'Trace info'),
         ('branch', 'Branches'),
index 5970f27c0f9c2b97120562e6cf075b2cac7e24c8..641497878a351459a70421217c50b08b109bf15c 100644 (file)
@@ -41,10 +41,11 @@ class Decoder(srd.Decoder):
     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']
-    outputs = ['arm_itm']
+    outputs = []
+    tags = ['Debug/trace']
     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
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.
+    tags = ['Debug/trace']
     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
-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
index a6a5a1dfa328953355fb02ef8673538485891f89..c666332a63917f8bff535a7d9dc3b8f60cffc3e4 100644 (file)
@@ -26,19 +26,25 @@ WORD_ADDR_COMMAND       = 0x03
 
 WORD_ADDR = {0x00: 'RESET', 0x01: 'SLEEP', 0x02: 'IDLE', 0x03: 'COMMAND'}
 
+OPCODE_COUNTER          = 0x24
 OPCODE_DERIVE_KEY       = 0x1c
 OPCODE_DEV_REV          = 0x30
+OPCODE_ECDH             = 0x43
 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_PRIVWRITE        = 0x46
 OPCODE_RANDOM           = 0x1b
 OPCODE_READ             = 0x02
 OPCODE_SHA              = 0x47
+OPCODE_SIGN             = 0x41
 OPCODE_UPDATE_EXTRA     = 0x20
+OPCODE_VERIFY           = 0x45
 OPCODE_WRITE            = 0x12
 
 OPCODES = {
@@ -53,8 +59,14 @@ OPCODES = {
     0x1b: 'Random',
     0x1c: 'DeriveKey',
     0x20: 'UpdateExtra',
+    0x24: 'Counter',
     0x28: 'CheckMac',
     0x30: 'DevRev',
+    0x40: 'GenKey',
+    0x41: 'Sign',
+    0x43: 'ECDH',
+    0x45: 'Verify',
+    0x46: 'PrivWrite',
     0x47: 'SHA',
 }
 
@@ -85,10 +97,11 @@ class Decoder(srd.Decoder):
     id = 'atsha204a'
     name = 'ATSHA204A'
     longname = 'Microchip ATSHA204A'
-    desc = 'Microchip ATSHA204A CryptoAuthentication device.'
+    desc = 'Microchip ATSHA204A family crypto authentication protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['atsha204a']
+    outputs = []
+    tags = ['Security/crypto', 'IC', 'Memory']
     annotations = (
         ('waddr', 'Word address'),
         ('count', 'Count'),
@@ -176,11 +189,15 @@ class Decoder(srd.Decoder):
 
     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]]])
+        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:
@@ -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])]])
+        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):
@@ -220,15 +240,33 @@ class Decoder(srd.Decoder):
             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_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)]])
+        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)]])
@@ -249,7 +287,6 @@ class Decoder(srd.Decoder):
 
     def decode(self, ss, es, data):
         cmd, databyte = data
-
         # 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
-                self.output_rx_bytes()
+                if len(self.bytes) > 0:
+                    self.output_rx_bytes()
                 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'
-
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']
-    outputs = ['aud']
+    outputs = []
+    tags = ['Debug/trace']
     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'
-    desc = 'Protocol for in-system programming Atmel AVR MCUs.'
+    desc = 'Atmel AVR In-System Programming (ISP) protocol.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['avr_isp']
+    outputs = []
+    tags = ['Debug/trace']
     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
-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
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'
-    desc = 'Atmel proprietary interface for the ATxmega MCU.'
+    desc = 'Atmel ATxmega Program and Debug Interface (PDI) protocol.'
     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'},
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).
+
+It also has support for CAN-FD.
 '''
 
 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>
+## 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
@@ -22,6 +23,9 @@ import sigrokdecode as srd
 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'
@@ -30,12 +34,14 @@ class Decoder(srd.Decoder):
     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 = (
-        {'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 = (
@@ -74,10 +80,20 @@ class Decoder(srd.Decoder):
     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
-            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.
@@ -93,6 +109,10 @@ class Decoder(srd.Decoder):
     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)
@@ -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.ss_bit32 = None
         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
@@ -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
-        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.
@@ -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
+            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
-            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.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)
-        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']])
 
+            if self.fd:
+                self.set_nominal_bitrate()
+
         # 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)
-        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).
-        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
-        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']])
@@ -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):
 
-        # 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:
-            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).
-        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).
-        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.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.
-        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.
-            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]
@@ -260,6 +322,8 @@ class Decoder(srd.Decoder):
         # 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:
@@ -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.
+
+        # Remember start of RTR (see below).
         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:
-            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']])
 
+        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).
-        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).
-        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.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.
-        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.
-            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]
@@ -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
 
+        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)]])
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.
 '''
 
index 6e8ddecf44d83981a0a8395d712d0c91d7dadd4a..b12633e75d224a14ae9909f19fdb3af9c5de9edc 100644 (file)
@@ -55,7 +55,8 @@ class Decoder(srd.Decoder):
     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'},
     )
@@ -116,41 +117,41 @@ class Decoder(srd.Decoder):
             return
 
         i = 0
-        str = ''
+        string = ''
         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):
-                str += ':'
+                string += ':'
             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
-        str = ''
+        string = ''
         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
-                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:
-                    str += ' | OPS: '
+                    string += ' | OPS: '
                 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:
-                    str += ', '
+                    string += ', '
             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)
-        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
index 833ac7f5e9f4fe2a5b4451bfb343eb5254a89488..78c3b6f537d7ab5b4c1fa4cec5ff96a3eefeb1a3 100644 (file)
@@ -101,15 +101,15 @@ opcodes = {
     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.
-    if id == 0x0F:
+    if id_ == 0x0F:
         return 'Unregistered' if is_initiator else 'Broadcast'
 
-    return logical_adresses[id]
+    return logical_adresses[id_]
 
 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']
-    outputs = ['cfp']
+    outputs = []
+    tags = ['Networking']
     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
-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
index 9ed30c813e711b31e4794592ee39a243bdc718a0..f1134bd14f61cb3b6bba3049608992baf249bc89 100644 (file)
@@ -27,10 +27,11 @@ class Decoder(srd.Decoder):
     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 = []
+    tags = ['Util']
     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'
-    desc = 'DALI lighting control protocol.'
+    desc = 'Digital Addressable Lighting Interface (DALI) protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['dali']
+    outputs = []
+    tags = ['Embedded/industrial', 'Lighting']
     channels = (
         {'id': 'dali', 'name': 'DALI', 'desc': 'DALI data line'},
     )
@@ -60,7 +61,6 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
-        self.samplenum = 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']
-    outputs = ['dcf77']
+    outputs = []
+    tags = ['Clock/timing']
     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'
-    desc = 'Professional lighting control protocol.'
+    desc = 'Digital MultipleX 512 (DMX512) lighting protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['dmx512']
+    outputs = []
+    tags = ['Embedded/industrial', 'Lighting']
     channels = (
         {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'},
     )
@@ -45,9 +46,9 @@ class Decoder(srd.Decoder):
         ('error', 'Error'),
     )
     annotation_rows = (
-        ('name', 'Logical', (1, 2, 5, 6, 7, 8)),
-        ('data', 'Data', (9,)),
         ('bits', 'Bits', (0, 3, 4)),
+        ('data', 'Data', (9,)),
+        ('name', 'Logical', (1, 2, 5, 6, 7, 8)),
         ('errors', 'Errors', (10,)),
     )
 
index 414da6531c31998977dab755af66dd3ce421257e..f8ebe195ec5008562745e4fb93a1cfaad88feb08 100644 (file)
@@ -39,9 +39,9 @@ bits = (
 
 rates = {
     0b00: '1Hz',
-    0b01: '4096kHz',
-    0b10: '8192kHz',
-    0b11: '32768kHz',
+    0b01: '4096Hz',
+    0b10: '8192Hz',
+    0b11: '32768Hz',
 }
 
 DS1307_I2C_ADDRESS = 0x68
@@ -56,10 +56,11 @@ class Decoder(srd.Decoder):
     id = 'ds1307'
     name = 'DS1307'
     longname = 'Dallas DS1307'
-    desc = 'Realtime clock module protocol.'
+    desc = 'Dallas DS1307 realtime clock module protocol.'
     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'),
@@ -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']])
-            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']])
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'
-    longname = 'Maxim DS2432/2433'
+    longname = 'Maxim DS2432/3'
     desc = 'Maxim DS243x series 1-Wire EEPROM protocol.'
     license = 'gplv2+'
     inputs = ['onewire_network']
-    outputs = ['ds243x']
+    outputs = []
+    tags = ['IC', 'Memory']
     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:
-                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)]])
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']
-    outputs = ['ds28ea00']
+    outputs = []
+    tags = ['IC', 'Sensor']
     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'
-    desc = 'DSI lighting control protocol.'
+    desc = 'Digital Serial Interface (DSI) lighting protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['dsi']
+    outputs = []
+    tags = ['Embedded/industrial', 'Lighting']
     channels = (
         {'id': 'dsi', 'name': 'DSI', 'desc': 'DSI data line'},
     )
@@ -55,7 +56,6 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
-        self.samplenum = None
         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:
+            # TODO: Come up with more appropriate self.wait() conditions.
             (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.
-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.
 
-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
index 034c314f4d28941e205f64a74110415da046dafa..e2e7feef7f23f1930c3223924094e20f755b1f58 100644 (file)
@@ -80,14 +80,15 @@ class Decoder(srd.Decoder):
     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 = (
-        ('sections', 'Sections', (1,)),
         ('fields', 'Fields', (0,)),
+        ('sections', 'Sections', (1,)),
     )
 
     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']
-    outputs = ['eeprom24xx']
+    outputs = []
+    tags = ['IC', 'Memory']
     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']
-    outputs = ['eeprom93xx']
+    outputs = []
+    tags = ['IC', 'Memory']
     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'),
@@ -41,6 +44,10 @@ class Decoder(srd.Decoder):
         ('data', 'Data', (0, 1)),
         ('warnings', 'Warnings', (2,)),
     )
+    binary = (
+        ('address', 'Address'),
+        ('data', 'Data'),
+    )
 
     def __init__(self):
         self.reset()
@@ -50,6 +57,7 @@ class Decoder(srd.Decoder):
 
     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']
 
@@ -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,
-                 [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).
@@ -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
-        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):
index 778cfd1621e6a918977a9567b0f3898dd877c4f6..7f42ad70db579abb436ad3a0acfa2a18dcda3e9b 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'EM4100 100-150kHz RFID protocol.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = ['em4100']
+    outputs = []
+    tags = ['IC', 'RFID']
     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']
-    outputs = ['em4305']
+    outputs = []
+    tags = ['IC', 'RFID']
     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'
-    desc = 'Accumulate rotary encoder increments, provide timing statistics.'
+    desc = 'Accumulate rotary encoder increments, provide statistics.'
     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)
index 21f0cb8bbd933ef701170ac01917b8351c029dae..a02bf18324b724bf8d776a6902775acf27792b65 100644 (file)
 
 '''
 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
@@ -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.
+
+The last annotation emitted by the decoder will be the best bitrate guess.
 '''
 
 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']
-    outputs = ['guess_bitrate']
+    outputs = []
+    tags = ['Clock/timing', 'Util']
     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']
+    tags = ['Embedded/industrial']
     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.
+    tags = ['Util']
 
     def __init__(self):
         self.reset()
index 1dc7fd1ddfc3e73cb1a2b53f3d38fa091aec0922..a54baab247b88a474db2c08a3649d097ba2c5e52 100644 (file)
@@ -31,8 +31,9 @@ class Decoder(srd.Decoder):
     license = 'gplv3+'
     inputs = ['i2c']
     outputs = ['i2c']
+    tags = ['Util']
     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')}
index bfb2c9e9f71b3952551d46499a957b7066c663ff..054d69e11a36f2cf5966d976f06d38e1a32555b6 100644 (file)
@@ -42,6 +42,7 @@ class Decoder(srd.Decoder):
     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'},
@@ -145,10 +146,12 @@ class Decoder(srd.Decoder):
 
                 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),
@@ -161,6 +164,8 @@ class Decoder(srd.Decoder):
                                    'word' % (self.bitcount, self.wordlength)]])
 
                 self.wordlength = self.bitcount
+            else:
+                sck = self.wait({0: 'f'})
 
             # 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']
-    outputs = ['ir_nec']
+    outputs = []
+    tags = ['IR']
     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)
-        self.active = 0 if self.options['polarity'] == 'active-low' else 1
 
     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
-            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.
index fa56dbca7ddbdc89d3251f0612a96615e221829d..4a8c958d88bb627be9f3810c132da3e5a3c6f053 100644 (file)
@@ -69,7 +69,7 @@ command = {
         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+'],
index 60a94160498a3beac0e74f31c1eec5b67c57d288..bbe0ca76c70f35ec02ac859a7e94cd04fac40be1 100644 (file)
@@ -31,7 +31,8 @@ class Decoder(srd.Decoder):
     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'},
     )
@@ -60,13 +61,12 @@ class Decoder(srd.Decoder):
 
     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.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:
@@ -142,11 +142,7 @@ class Decoder(srd.Decoder):
             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':
@@ -154,7 +150,7 @@ class Decoder(srd.Decoder):
                 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':
@@ -183,4 +179,4 @@ class Decoder(srd.Decoder):
                 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.
+
 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']
-    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'},
index 2d349bcb16b03479cd01bccaa6d309486580fa39..618613e403ef568f8801cd2b9b87abffbc8d7d33 100644 (file)
@@ -62,6 +62,7 @@ class Decoder(srd.Decoder):
     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'},
index ba5db2a05897c3291b5b4c70c8221b4976938568..5a537c993cd22befbe4868e2ca1a9a9789b9fdb7 100644 (file)
@@ -191,12 +191,13 @@ regs_items = {
 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']
-    outputs = ['jtag_ejtag']
+    outputs = []
+    tags = ['Debug/trace']
     annotations = (
         ('instruction', 'Instruction'),
     ) + regs_items['ann'] + (
@@ -206,9 +207,9 @@ class Decoder(srd.Decoder):
     )
     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,)),
+        ('regs', 'Registers', regs_items['rows_range']),
         ('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(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)
@@ -247,14 +248,14 @@ class Decoder(srd.Decoder):
         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)
-            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:
-            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)
-            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()
@@ -357,16 +358,16 @@ class Decoder(srd.Decoder):
 
     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]
-            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:
-            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):
index 593ce137bc8dc25aad587f442b3baec110dd8b20..82558b820f97577b768637f1da74c8e7921a0e63 100644 (file)
@@ -146,7 +146,8 @@ class Decoder(srd.Decoder):
     desc = 'ST STM32-specific JTAG protocol.'
     license = 'gplv2+'
     inputs = ['jtag']
-    outputs = ['jtag_stm32']
+    outputs = []
+    tags = ['Debug/trace']
     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']
-    outputs = ['lm75']
+    outputs = []
+    tags = ['Sensor']
     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".
 '''
 
index 452e647118131428a5cebf42fcf22f0ab24f7858..095c8b576f412a44be95e5d9a74e5554fab60a05 100644 (file)
@@ -98,11 +98,12 @@ class Decoder(srd.Decoder):
     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']
-    outputs = ['lpc']
+    outputs = []
+    tags = ['PC']
     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
-        self.samplenum = 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']
-    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'},
index c8e99e1cc948e9fe548ab39158ff8aa905612b23..53067a679f060b7c3964f746aa0648585a9e6130 100644 (file)
@@ -45,10 +45,11 @@ class Decoder(srd.Decoder):
     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']
-    outputs = ['max7219']
+    outputs = []
+    tags = ['Display']
     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
-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).
 '''
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']
-    outputs = ['mcs48']
+    outputs = []
+    tags = ['Retro computing']
     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'
-    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']
+    tags = ['Networking']
     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
-        self.samplenum = -1
         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.
+
 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']
+    tags = ['Embedded/industrial']
     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']
-    outputs = ['midi']
+    outputs = []
+    tags = ['Audio', 'PC']
     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']
-    outputs = ['miller']
+    outputs = []
+    tags = ['Encoding']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data signal'},
     )
@@ -56,6 +57,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         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'
-    desc = 'Infrared Thermometer protocol.'
+    desc = 'Melexis MLX90614 infrared thermometer protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['mlx90614']
+    outputs = []
+    tags = ['IC', 'Sensor']
     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
+rxtx_channels = ('RX', 'TX')
 
 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
-        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:
-                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
@@ -819,6 +820,7 @@ class Decoder(srd.Decoder):
     license = 'gplv3+'
     inputs = ['uart']
     outputs = ['modbus']
+    tags = ['Embedded/industrial']
     annotations = (
         ('sc-server-id', ''),
         ('sc-function', ''),
@@ -842,8 +844,11 @@ class Decoder(srd.Decoder):
         ('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):
@@ -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.
-        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!
@@ -924,11 +929,13 @@ class Decoder(srd.Decoder):
     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.
-        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')
+        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']
-    outputs = ['morse']
+    outputs = []
+    tags = ['Encoding']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
index 8ccf47384a67f4b54b3f9b2c353aa545d0e9bd47..db3ca98e93b4b017feaf7d9eed298ccad9406104 100644 (file)
@@ -20,6 +20,8 @@
 import sigrokdecode as srd
 from .lists import *
 
+TX, RX = range(2)
+
 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']
-    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'),
+        ('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,)),
+        ('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):
@@ -47,8 +64,9 @@ class Decoder(srd.Decoder):
 
     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)
@@ -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')
+        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])]])
+            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]
@@ -98,6 +140,16 @@ class Decoder(srd.Decoder):
         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':
index 1e9045519c90ce41bfecef1dd3b7ac8b9f7872d8..e9617782d6154471c48c937b7a0ede40dc84073c 100644 (file)
@@ -66,7 +66,8 @@ class Decoder(srd.Decoder):
     desc = 'Digital Thermal Orientation Sensor (DTOS) protocol.'
     license = 'gplv2+'
     inputs = ['i2c']
-    outputs = ['mxc6225xu']
+    outputs = []
+    tags = ['IC', 'Sensor']
     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(+)'
-    longname = 'Nordic Semiconductor nRF24L01/nRF24L01+'
-    desc = '2.4GHz transceiver chip.'
+    longname = 'Nordic Semiconductor nRF24L01(+)'
+    desc = '2.4GHz RF transceiver chip.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['nrf24l01']
+    outputs = []
+    tags = ['IC', 'Wireless/RF']
     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']
-    outputs = ['nunchuck']
+    outputs = []
+    tags = ['Sensor']
     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:
-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
-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)
 '''
index 72aea83f7248952e91cda83823aa3605f7d09156..564d6f0bc86895725b37116f683dbfe15bfae9eb 100644 (file)
@@ -97,6 +97,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['onewire_link']
+    tags = ['Embedded/industrial']
     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 = {
-    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):
@@ -40,6 +42,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['onewire_link']
     outputs = ['onewire_network']
+    tags = ['Embedded/industrial']
     annotations = (
         ('text', 'Human-readable text'),
     )
index b4971b621d3a330391c92e5388bfb35297794ffb..5fc8d01252b6f0abc30b2c04b1efd3d4cfffffa8 100644 (file)
@@ -54,6 +54,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['ook']
+    tags = ['Encoding']
     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 = []
-            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'
@@ -205,7 +206,7 @@ class Decoder(srd.Decoder):
                 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:
index c248726b91d928a6a2fe1ce53b5c363ae6303a8d..225f59837029a0170fe7c6c091323603625fa80a 100644 (file)
@@ -30,6 +30,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['ook']
     outputs = []
+    tags = ['Sensor']
     annotations = (
         ('bit', 'Bit'),
         ('field', 'Field'),
index 37853319a358276912a18b7187a07cb0cf105edd..f985b96f1909e96edc0face8294df51068cf0375 100644 (file)
@@ -29,6 +29,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['ook']
     outputs = ['ook']
+    tags = ['Encoding']
     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']
-    outputs = ['pan1321']
+    outputs = []
+    tags = ['Wireless/RF']
     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']
+    tags = ['Util']
     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']
-    outputs = ['ps2']
+    outputs = []
+    tags = ['PC']
     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 = []
-        self.samplenum = 0
         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
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']
-    outputs = ['pwm']
+    outputs = []
+    tags = ['Encoding']
     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']
-    outputs = ['qi']
+    outputs = []
+    tags = ['Embedded/industrial', 'Wireless/RF']
     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 = []
+    tags = ['IC', 'IR']
     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'
-    longname = 'RFM12 control protocol'
+    longname = 'HopeRF RFM12'
     desc = 'HopeRF RFM12 wireless transceiver control protocol.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['rfm12']
+    outputs = []
+    tags = ['Wireless/RF']
     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']
-    outputs = ['rgb_led_spi']
+    outputs = []
+    tags = ['Display']
     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']
-    outputs = ['rgb_led_ws281x']
+    outputs = []
+    tags = ['Display', 'IC']
     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 \
+                    self.ss is not None and \
                     (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']
-    outputs = ['rtc8564']
+    outputs = []
+    tags = ['Clock/timing']
     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
index 8b63cf0ec000f4c5d70df9a3d03722226cd3a6ba..9ae5c0142d53a47ef075556e6dbf3bfd6fe4be95 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     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'},
@@ -45,8 +46,8 @@ class Decoder(srd.Decoder):
     )
     annotation_rows = (
         ('bits', 'Bits', (ann_cmdbit, ann_databit)),
-        ('commands', 'Commands', (ann_cmd,)),
         ('data', 'Data', (ann_data,)),
+        ('commands', 'Commands', (ann_cmd,)),
         ('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']
-    outputs = ['sdcard_sd']
+    outputs = []
+    tags = ['Memory']
     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']
-    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)) + ( \
@@ -41,8 +42,8 @@ class Decoder(srd.Decoder):
         ('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):
@@ -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_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.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)
@@ -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]
-        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 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:
@@ -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)
-        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):
@@ -327,7 +331,10 @@ class Decoder(srd.Decoder):
         # 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
@@ -349,6 +356,113 @@ class Decoder(srd.Decoder):
         # 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
 
@@ -388,10 +502,18 @@ class Decoder(srd.Decoder):
             # Ignore stray 0xff bytes, some devices seem to send those!?
             if miso == 0xff: # TODO?
                 return
-
             # 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)
-            handle_response(miso)
-
             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']
-    outputs = ['spdif']
+    outputs = []
+    tags = ['Audio', 'PC']
     channels = (
         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
     )
@@ -46,8 +47,8 @@ class Decoder(srd.Decoder):
         ('parity', 'Parity Bit'),
     )
     annotation_rows = (
-        ('info', 'Info', (0, 1, 3, 5, 6, 7, 8)),
         ('bits', 'Bits', (2,)),
+        ('info', 'Info', (0, 1, 3, 5, 6, 7, 8)),
         ('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.
+
 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']
+    tags = ['Embedded/industrial']
     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-transfer', 'MISO transfer'),
+        ('mosi-transfer', 'MOSI transfer'),
     )
     annotation_rows = (
-        ('miso-data', 'MISO data', (0,)),
         ('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-data', 'MOSI data', (1,)),
+        ('mosi-transfer', 'MOSI transfer', (6,)),
         ('other', 'Other', (4,)),
     )
     binary = (
@@ -132,7 +137,6 @@ class Decoder(srd.Decoder):
         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
@@ -274,7 +278,13 @@ class Decoder(srd.Decoder):
                 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])
 
index b2092bfff5b658e4c17a93e17ac7769ab17933db..49993ad3cb642637f38964792f519561a55d5b06 100644 (file)
@@ -65,6 +65,9 @@ device_name = {
         0x15: 'MX25L3205D',
         0x16: 'MX25L6405D',
     },
+    'winbond': {
+        0x13: 'W25Q80DV',
+    },
 }
 
 chips = {
@@ -72,14 +75,37 @@ chips = {
     '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,
     },
+    # 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',
@@ -126,4 +152,16 @@ chips = {
         '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)
 
-# 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, \
@@ -73,12 +73,13 @@ def decode_status_reg(data):
 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']
-    outputs = ['spiflash']
+    outputs = []
+    tags = ['IC', 'Memory']
     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.writestate = 0
 
         # 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()])
-        self.state = None
+        self.writestate = 1
 
     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:
@@ -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()])
+            # Set write latch state.
+            self.writestate = 1 if (miso & (1 << 1)) else 0
         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.putx([Ann.BIT, [decode_status_reg(miso)]])
+            self.putx([Ann.BIT, [decode_status_reg(mosi)]])
             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.
-            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()])
@@ -279,6 +286,8 @@ class Decoder(srd.Decoder):
         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)
@@ -363,11 +372,12 @@ class Decoder(srd.Decoder):
         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()
+            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)
@@ -388,10 +398,14 @@ class Decoder(srd.Decoder):
         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):
-        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
@@ -460,8 +474,8 @@ class Decoder(srd.Decoder):
             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
index f2685e2869dc3010520633b98a511457724b482b..51608039ea205bd685800dfb29583d2849dd6f40 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     desc = 'Synchronous Serial Interface (32bit) protocol.'
     license = 'gplv2+'
     inputs = ['spi']
-    outputs = ['ssi32']
+    outputs = []
+    tags = ['Embedded/industrial']
     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
index 999038a3a18b7178b8747a52d9f8e85460f08abb..252b188763c4faf0ca2eed738f5a0be7ac9df0c4 100644 (file)
@@ -72,7 +72,8 @@ class Decoder(srd.Decoder):
     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'},
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']
-    outputs = ['stepper_motor']
+    outputs = []
+    tags = ['Embedded/industrial']
     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.
 
-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.)
+
+Details:
+http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0031c/index.html
+(Registration required)
 '''
 
 from .pd import Decoder
index 0f7bc22aab281eaa8b418604f6970131a5524d69..cc258ef3304854118291b1d93a2881a035d89c95 100644 (file)
@@ -72,6 +72,7 @@ class Decoder(srd.Decoder):
     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'},
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
-## 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
-## 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
@@ -33,6 +32,7 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
+    tags = ['Debug/trace']
     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']
-    outputs = ['t55xx']
+    outputs = []
+    tags = ['IC', 'RFID']
     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']
-    outputs = ['tca6408a']
+    outputs = []
+    tags = ['Embedded/industrial', 'IC']
     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']
-    outputs = ['timing']
+    outputs = []
+    tags = ['Clock/timing', 'Util']
     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']
-    outputs = ['tlc5620']
+    outputs = []
+    tags = ['IC', 'Analog/digital']
     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.
- - 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.
 '''
@@ -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'.
-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':
@@ -84,6 +91,7 @@ class Decoder(srd.Decoder):
     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).
@@ -92,22 +100,26 @@ class Decoder(srd.Decoder):
     )
     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)},
-        {'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')},
-        {'id': 'invert_rx', 'desc': 'Invert RX?', 'default': 'no',
+        {'id': 'invert_rx', 'desc': 'Invert RX', 'default': 'no',
             'values': ('yes', 'no')},
-        {'id': 'invert_tx', 'desc': 'Invert TX?', 'default': 'no',
+        {'id': 'invert_tx', 'desc': 'Invert TX', 'default': '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'),
@@ -124,14 +136,22 @@ class Decoder(srd.Decoder):
         ('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 = (
-        ('rx-data', 'RX', (0, 2, 4, 6, 8)),
         ('rx-data-bits', 'RX bits', (12,)),
+        ('rx-data', 'RX', (0, 2, 4, 6, 8)),
         ('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', 'TX', (1, 3, 5, 7, 9)),
         ('tx-warnings', 'TX warnings', (11,)),
+        ('tx-break', 'TX break', (15,)),
+        ('tx-packets', 'TX packets', (17,)),
     )
     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)
 
+    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)
@@ -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)
 
+    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)
@@ -165,8 +195,8 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
-        self.samplenum = 0
         self.frame_start = [-1, -1]
+        self.frame_valid = [None, None]
         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.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)
-        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:
@@ -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
+        self.frame_valid[rxtx] = True
 
         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']])
+            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
 
@@ -225,6 +264,30 @@ class Decoder(srd.Decoder):
 
         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:
@@ -238,7 +301,7 @@ class Decoder(srd.Decoder):
 
         # 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.
@@ -258,12 +321,14 @@ class Decoder(srd.Decoder):
         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'
-        if self.options['parity_type'] == 'none':
+        if self.options['parity'] == 'none':
             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.
 
-        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
@@ -311,14 +376,15 @@ class Decoder(srd.Decoder):
     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.frame_valid[rxtx] = False
 
         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']])
-            # 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']])
 
+        # 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):
@@ -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':
-            bitnum = 1 + self.options['num_data_bits']
+            bitnum = 1 + self.options['data_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}
 
+    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:
@@ -373,28 +463,99 @@ class Decoder(srd.Decoder):
         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)]
-        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']
-        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]:
-                cond_idx[RX] = len(conds)
+                cond_data_idx[RX] = len(conds)
                 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]:
-                cond_idx[TX] = len(conds)
+                cond_data_idx[TX] = len(conds)
                 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)
-            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])
-            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])
+            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[i] = '1';
+            out[i] = '1'
     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']
+    tags = ['PC']
     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']
+    tags = ['PC']
     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:
-            t_pdo = '#d' % (pos)
+            t_pdo = '#%d' % (pos)
 
         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'
-    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']
+    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'),
@@ -128,7 +133,9 @@ class Decoder(srd.Decoder):
         ('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 = (
@@ -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)
+        self.in_request_start = self.options['in_request_start']
 
     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,
-                '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)]
@@ -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'
+            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
+            if self.handshake == 'ACK':
+                request['data'] += self.transaction_data
             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
+            if self.handshake == 'ACK':
+                request['data'] += self.transaction_data
             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)]
 
-        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.
@@ -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':
-                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]])
 
@@ -338,6 +352,8 @@ class Decoder(srd.Decoder):
             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
 
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'
-    desc = 'USB (low-speed and full-speed) signalling protocol.'
+    desc = 'USB (low-speed/full-speed) signalling protocol.'
     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'},
@@ -141,7 +142,6 @@ class Decoder(srd.Decoder):
         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
@@ -193,7 +193,7 @@ class Decoder(srd.Decoder):
         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))
index c494789840131fb11d2b083145bc34188032596d..a93be109802ee6eea571ea5c68f6ca92a452f474 100644 (file)
@@ -30,7 +30,8 @@ class Decoder(srd.Decoder):
     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'},
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.
 
-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
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)'
-    desc = 'Data structure describing display device capabilities.'
+    desc = 'XFP I²C management interface structures/protocol'
     license = 'gplv3+'
     inputs = ['i2c']
-    outputs = ['xfp']
+    outputs = []
+    tags = ['Networking']
     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:
 
-  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
index b7ff9a56b16ac83cc62bba2e6781876241b306dc..fba279bf7406ad876a33d406e58f004ccf3995a0 100644 (file)
@@ -71,7 +71,8 @@ class Decoder(srd.Decoder):
     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,
index 075bf09e45a3298662a82068b8c186dfe643c40f..5264bd0bd3a54d72f8503e926931f542ab310d96 100644 (file)
 
 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 */
 
 /**
@@ -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.
  *
@@ -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);
 
        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;
                        }
                }
-               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;
+               }
                /* 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);
@@ -167,7 +197,6 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di,
        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;
@@ -314,7 +343,6 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess,
        PyGILState_STATE gstate;
 
        i = 1;
-       srd_dbg("Creating new %s instance.", decoder_id);
 
        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);
-       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;
 }
@@ -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);
        }
 
+       /*
+        * 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);
 
-       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;
 }
@@ -627,50 +676,16 @@ SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, GArray *i
        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)
 {
-       PyObject *py_res;
+       PyObject *py_res, *py_samplenum;
        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();
 
@@ -681,10 +696,12 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
                PyGILState_Release(gstate);
                return SRD_ERR_PYTHON;
        }
-       Py_DecRef(py_res);
+       Py_DECREF(py_res);
 
        /* 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);
@@ -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(di->condition_list);
        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++) {
+               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;
@@ -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;
+               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;
@@ -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;
+               if (term->type == SRD_TERM_ALWAYS_FALSE)
+                       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.
         */
-       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);
-       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;
@@ -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);
-       Py_DecRef(py_res);
+       Py_DECREF(py_res);
        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")) {
-               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);
        }
@@ -1294,7 +1318,7 @@ SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di)
        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);
index 1d0931e0715df2bac1fdf3ada2a2b8fd6e064c0a..af245f6ad197f5b75ab8a2782a0399bb43b2a95c 100644 (file)
@@ -28,6 +28,7 @@
 #include "libsigrokdecode.h"
 
 enum {
+       SRD_TERM_ALWAYS_FALSE,
        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);
+SRD_PRIV const char *output_type_name(unsigned int idx);
 
 /* 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_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);
index 55620a7f52551df6e01ea1521711ce34fdf12496..b9cc921287e876347ceea9d1ea08f61a46046f2e 100644 (file)
@@ -161,6 +161,9 @@ struct srd_decoder {
        /** 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;
 
@@ -168,8 +171,8 @@ struct srd_decoder {
        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;
 
@@ -180,8 +183,8 @@ struct srd_decoder {
        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;
 
@@ -303,11 +306,11 @@ struct srd_proto_data {
        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 {
-       int bin_class;
+       int bin_class; /* Index into "struct srd_decoder"->binary. */
        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);
 
-       srd_dbg("Created session %d.", (*sess)->session_id);
+       srd_dbg("Creating session %d.", (*sess)->session_id);
 
        return SRD_OK;
 }
@@ -96,9 +96,9 @@ SRD_API int srd_session_start(struct srd_session *sess)
        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;
@@ -372,7 +372,8 @@ SRD_API int srd_pd_output_callback_add(struct srd_session *sess,
        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;
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_DECREF(py_bytes);
        }
        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;
 }
 
+static void srd_session_destroy_cb(void *arg, void *ignored)
+{
+       (void)ignored; // Prevent unused warning
+       srd_session_destroy((struct srd_session *)arg);
+}
+
 /**
  * Shutdown libsigrokdecode.
  *
@@ -307,8 +314,9 @@ SRD_API int srd_exit(void)
 {
        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);
@@ -347,8 +355,6 @@ SRD_API int srd_exit(void)
  * @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)
 {
@@ -396,7 +402,12 @@ err:
  */
 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;
+       char *doc;
 
        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
@@ -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 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)
 {
+       struct srd_decoder dec;
+
+       dec.py_mod = 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
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;
+       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);
+       if (ret != SRD_OK)
+               g_variant_unref(value);
 }
 
 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;
+       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);
+       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. */
-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",
@@ -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)
-               return di; 
+               return di;
 
        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;
 
-       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;
@@ -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;
-                       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))) {
@@ -514,9 +521,6 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
                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. */
@@ -536,6 +540,10 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
 
        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:
@@ -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.
  *
+ * @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.
  */
-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;
-       uint64_t num_samples_to_skip;
+       int64_t num_samples_to_skip;
        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. */
-                       /* 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.");
@@ -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);
+                       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. */
-                       /* 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;
@@ -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;
+                       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;
@@ -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([]). */
-               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);
-               Py_IncRef(py_conds);
+               Py_INCREF(py_conds);
                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. */
-               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. */
@@ -842,7 +855,7 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
        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)
@@ -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. */
-                       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);
+                               Py_DECREF(py_matched);
                                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
  */
-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;
@@ -328,7 +328,7 @@ SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t
                goto err;
        }
 
-       *out = PyLong_AsUnsignedLongLong(py_value);
+       *out = PyLong_AsLongLong(py_value);
 
        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.
- * 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.
  *