From: Uwe Hermann Date: Wed, 11 Dec 2019 20:27:13 +0000 (+0100) Subject: Backport recent changes from mainline. X-Git-Tag: libsigrokdecode-0.5.3~5 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=3f5f3a92889e2f297ee6b94362e587a2994f896b;p=libsigrokdecode.git Backport recent changes from mainline. This includes all changes from 4f0d192d748e987af43ec5b811a643eb0a8601b2 "type_decoder.c: Fix trailing whitespace." up to 1d7e79da75afbdfd5d1863de6482bf4cd21e5c7e "ac97/lin: Remove some unneeded code snippets." --- diff --git a/HACKING b/HACKING index 30cd1fe..a4698a6 100644 --- 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 369dbe3..fdd6217 100644 --- 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). diff --git a/configure.ac b/configure.ac index 56f01a2..0e2416f 100644 --- a/configure.ac +++ b/configure.ac @@ -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.])]) diff --git a/decoder.c b/decoder.c index 7374b77..cef4f29 100644 --- 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; diff --git a/decoders/ac97/__init__.py b/decoders/ac97/__init__.py index 8b96e8a..c57c8eb 100644 --- a/decoders/ac97/__init__.py +++ b/decoders/ac97/__init__.py @@ -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 diff --git a/decoders/ac97/pd.py b/decoders/ac97/pd.py index 6cb7e93..68eb955 100644 --- a/decoders/ac97/pd.py +++ b/decoders/ac97/pd.py @@ -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: diff --git a/decoders/ade77xx/__init__.py b/decoders/ade77xx/__init__.py index cbe8689..7da0419 100644 --- a/decoders/ade77xx/__init__.py +++ b/decoders/ade77xx/__init__.py @@ -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 . ## ''' diff --git a/decoders/ade77xx/pd.py b/decoders/ade77xx/pd.py index 0dfd7c8..5a24a25 100644 --- a/decoders/ade77xx/pd.py +++ b/decoders/ade77xx/pd.py @@ -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'), diff --git a/decoders/adf435x/pd.py b/decoders/adf435x/pd.py index dcc08de..f6c6e6e 100644 --- a/decoders/adf435x/pd.py +++ b/decoders/adf435x/pd.py @@ -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'), diff --git a/decoders/adns5020/__init__.py b/decoders/adns5020/__init__.py index d4c260e..e519da4 100644 --- a/decoders/adns5020/__init__.py +++ b/decoders/adns5020/__init__.py @@ -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 diff --git a/decoders/adns5020/pd.py b/decoders/adns5020/pd.py index cd72eca..9ac778e 100644 --- a/decoders/adns5020/pd.py +++ b/decoders/adns5020/pd.py @@ -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'), diff --git a/decoders/am230x/pd.py b/decoders/am230x/pd.py index 81c1f28..fbc68d3 100644 --- a/decoders/am230x/pd.py +++ b/decoders/am230x/pd.py @@ -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 index 0000000..7d2c8c3 --- /dev/null +++ b/decoders/amulet_ascii/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Vesa-Pekka Palmu +## +## 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 . +## + +''' +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 index 0000000..92e27a9 --- /dev/null +++ b/decoders/amulet_ascii/lists.py @@ -0,0 +1,73 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Vesa-Pekka Palmu +## +## 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 . +## + +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 index 0000000..54733ef --- /dev/null +++ b/decoders/amulet_ascii/pd.py @@ -0,0 +1,702 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Vesa-Pekka Palmu +## +## 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 . +## + +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_. + 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 diff --git a/decoders/arm_etmv3/__init__.py b/decoders/arm_etmv3/__init__.py index 2ce2974..617063c 100644 --- a/decoders/arm_etmv3/__init__.py +++ b/decoders/arm_etmv3/__init__.py @@ -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. ''' diff --git a/decoders/arm_etmv3/pd.py b/decoders/arm_etmv3/pd.py index 8de3ce2..6649b46 100644 --- a/decoders/arm_etmv3/pd.py +++ b/decoders/arm_etmv3/pd.py @@ -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'), diff --git a/decoders/arm_itm/pd.py b/decoders/arm_itm/pd.py index 5970f27..6414978 100644 --- a/decoders/arm_itm/pd.py +++ b/decoders/arm_itm/pd.py @@ -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'}, diff --git a/decoders/arm_tpiu/__init__.py b/decoders/arm_tpiu/__init__.py index 3e9ec0e..ce9c374 100644 --- a/decoders/arm_tpiu/__init__.py +++ b/decoders/arm_tpiu/__init__.py @@ -18,10 +18,11 @@ ## ''' -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 diff --git a/decoders/arm_tpiu/pd.py b/decoders/arm_tpiu/pd.py index f50af65..29b4605 100644 --- a/decoders/arm_tpiu/pd.py +++ b/decoders/arm_tpiu/pd.py @@ -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}, diff --git a/decoders/atsha204a/__init__.py b/decoders/atsha204a/__init__.py index fc56bd6..fd0f428 100644 --- a/decoders/atsha204a/__init__.py +++ b/decoders/atsha204a/__init__.py @@ -19,7 +19,12 @@ ''' 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 diff --git a/decoders/atsha204a/pd.py b/decoders/atsha204a/pd.py index a6a5a1d..c666332 100644 --- a/decoders/atsha204a/pd.py +++ b/decoders/atsha204a/pd.py @@ -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' - diff --git a/decoders/aud/pd.py b/decoders/aud/pd.py index 30c32f5..ea19a10 100644 --- a/decoders/aud/pd.py +++ b/decoders/aud/pd.py @@ -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'}, diff --git a/decoders/avr_isp/pd.py b/decoders/avr_isp/pd.py index 2530e8c..a0719b7 100644 --- a/decoders/avr_isp/pd.py +++ b/decoders/avr_isp/pd.py @@ -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'), diff --git a/decoders/avr_pdi/__init__.py b/decoders/avr_pdi/__init__.py index ebe647b..1c61dea 100644 --- a/decoders/avr_pdi/__init__.py +++ b/decoders/avr_pdi/__init__.py @@ -19,9 +19,10 @@ ''' 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 diff --git a/decoders/avr_pdi/pd.py b/decoders/avr_pdi/pd.py index 7fedbbd..164b992 100644 --- a/decoders/avr_pdi/pd.py +++ b/decoders/avr_pdi/pd.py @@ -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'}, diff --git a/decoders/can/__init__.py b/decoders/can/__init__.py index 47f571d..888bb81 100644 --- a/decoders/can/__init__.py +++ b/decoders/can/__init__.py @@ -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 diff --git a/decoders/can/pd.py b/decoders/can/pd.py index d76d649..8817097 100644 --- a/decoders/can/pd.py +++ b/decoders/can/pd.py @@ -2,6 +2,7 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2012-2013 Uwe Hermann +## Copyright (C) 2019 Stephan Thiele ## ## 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 index 0000000..68fc798 --- /dev/null +++ b/decoders/cc1101/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Marco Geisler +## +## 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 . +## + +''' +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 index 0000000..dfb9b07 --- /dev/null +++ b/decoders/cc1101/lists.py @@ -0,0 +1,115 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Marco Geisler +## +## 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 . +## + +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 index 0000000..156d4ce --- /dev/null +++ b/decoders/cc1101/pd.py @@ -0,0 +1,293 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Marco Geisler +## +## 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 . +## + +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)) diff --git a/decoders/cec/__init__.py b/decoders/cec/__init__.py index db288ab..4138b62 100644 --- a/decoders/cec/__init__.py +++ b/decoders/cec/__init__.py @@ -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. ''' diff --git a/decoders/cec/pd.py b/decoders/cec/pd.py index 6e8ddec..b12633e 100644 --- a/decoders/cec/pd.py +++ b/decoders/cec/pd.py @@ -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 diff --git a/decoders/cec/protocoldata.py b/decoders/cec/protocoldata.py index 833ac7f..78c3b6f 100644 --- a/decoders/cec/protocoldata.py +++ b/decoders/cec/protocoldata.py @@ -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 diff --git a/decoders/cfp/pd.py b/decoders/cfp/pd.py index 77e61a9..9638ba1 100644 --- a/decoders/cfp/pd.py +++ b/decoders/cfp/pd.py @@ -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'), diff --git a/decoders/counter/__init__.py b/decoders/counter/__init__.py index e731311..505148d 100644 --- a/decoders/counter/__init__.py +++ b/decoders/counter/__init__.py @@ -18,11 +18,11 @@ ## ''' -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 diff --git a/decoders/counter/pd.py b/decoders/counter/pd.py index 9ed30c8..f1134bd 100644 --- a/decoders/counter/pd.py +++ b/decoders/counter/pd.py @@ -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'}, ) diff --git a/decoders/dali/pd.py b/decoders/dali/pd.py index bf842ad..8be494a 100644 --- a/decoders/dali/pd.py +++ b/decoders/dali/pd.py @@ -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 diff --git a/decoders/dcf77/pd.py b/decoders/dcf77/pd.py index 7b09ce6..7365134 100644 --- a/decoders/dcf77/pd.py +++ b/decoders/dcf77/pd.py @@ -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'}, ) diff --git a/decoders/dmx512/pd.py b/decoders/dmx512/pd.py index 1bcca20..3fd2aba 100644 --- a/decoders/dmx512/pd.py +++ b/decoders/dmx512/pd.py @@ -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,)), ) diff --git a/decoders/ds1307/pd.py b/decoders/ds1307/pd.py index 414da65..f8ebe19 100644 --- a/decoders/ds1307/pd.py +++ b/decoders/ds1307/pd.py @@ -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 index 0000000..b196ce9 --- /dev/null +++ b/decoders/ds2408/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2012 Uwe Hermann +## +## 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 . +## + +''' +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 index 0000000..33f2873 --- /dev/null +++ b/decoders/ds2408/pd.py @@ -0,0 +1,129 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Mariusz Bialonczyk +## +## 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 . +## + +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']]) diff --git a/decoders/ds243x/pd.py b/decoders/ds243x/pd.py index c7869a8..7f9f666 100644 --- a/decoders/ds243x/pd.py +++ b/decoders/ds243x/pd.py @@ -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)]]) diff --git a/decoders/ds28ea00/pd.py b/decoders/ds28ea00/pd.py index a792d95..9a57844 100644 --- a/decoders/ds28ea00/pd.py +++ b/decoders/ds28ea00/pd.py @@ -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'), ) diff --git a/decoders/dsi/pd.py b/decoders/dsi/pd.py index c5d9bf9..c7cab80 100644 --- a/decoders/dsi/pd.py +++ b/decoders/dsi/pd.py @@ -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. diff --git a/decoders/edid/__init__.py b/decoders/edid/__init__.py index f0331cc..256d839 100644 --- a/decoders/edid/__init__.py +++ b/decoders/edid/__init__.py @@ -18,21 +18,18 @@ ## ''' -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 diff --git a/decoders/edid/pd.py b/decoders/edid/pd.py index 034c314..e2e7fee 100644 --- a/decoders/edid/pd.py +++ b/decoders/edid/pd.py @@ -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): diff --git a/decoders/eeprom24xx/pd.py b/decoders/eeprom24xx/pd.py index 49c586d..033a44b 100644 --- a/decoders/eeprom24xx/pd.py +++ b/decoders/eeprom24xx/pd.py @@ -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())}, diff --git a/decoders/eeprom93xx/pd.py b/decoders/eeprom93xx/pd.py index d76b869..8c08fc8 100644 --- a/decoders/eeprom93xx/pd.py +++ b/decoders/eeprom93xx/pd.py @@ -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): diff --git a/decoders/em4100/pd.py b/decoders/em4100/pd.py index 778cfd1..7f42ad7 100644 --- a/decoders/em4100/pd.py +++ b/decoders/em4100/pd.py @@ -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'}, ) diff --git a/decoders/em4305/pd.py b/decoders/em4305/pd.py index 9fac9c6..6297643 100644 --- a/decoders/em4305/pd.py +++ b/decoders/em4305/pd.py @@ -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 index 0000000..42f4377 --- /dev/null +++ b/decoders/enc28j60/__init__.py @@ -0,0 +1,32 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Jiahao Li +## +## 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 index 0000000..59fbc1f --- /dev/null +++ b/decoders/enc28j60/lists.py @@ -0,0 +1,161 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Jiahao Li +## +## 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 index 0000000..e8ce6e7 --- /dev/null +++ b/decoders/enc28j60/pd.py @@ -0,0 +1,294 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Jiahao Li +## +## 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 index 0000000..73dc7fa --- /dev/null +++ b/decoders/flexray/__init__.py @@ -0,0 +1,32 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Stephan Thiele +## +## 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 . +## + +''' +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 index 0000000..e13f2d4 --- /dev/null +++ b/decoders/flexray/pd.py @@ -0,0 +1,413 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Stephan Thiele +## +## 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 . +## + +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) diff --git a/decoders/graycode/pd.py b/decoders/graycode/pd.py index ef5d513..055908c 100644 --- a/decoders/graycode/pd.py +++ b/decoders/graycode/pd.py @@ -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) diff --git a/decoders/guess_bitrate/__init__.py b/decoders/guess_bitrate/__init__.py index 21f0cb8..a02bf18 100644 --- a/decoders/guess_bitrate/__init__.py +++ b/decoders/guess_bitrate/__init__.py @@ -19,9 +19,11 @@ ''' 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 diff --git a/decoders/guess_bitrate/pd.py b/decoders/guess_bitrate/pd.py index 7a3121a..462fa8a 100644 --- a/decoders/guess_bitrate/pd.py +++ b/decoders/guess_bitrate/pd.py @@ -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 index 0000000..f2e10b6 --- /dev/null +++ b/decoders/hdcp/__init__.py @@ -0,0 +1,27 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Dave Craig +## +## 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 . +## + +''' +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 index 0000000..2d000dc --- /dev/null +++ b/decoders/hdcp/pd.py @@ -0,0 +1,191 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Dave Craig +## +## 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 . +## + +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) diff --git a/decoders/i2c/pd.py b/decoders/i2c/pd.py index e70c27d..f31e33e 100644 --- a/decoders/i2c/pd.py +++ b/decoders/i2c/pd.py @@ -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'}, diff --git a/decoders/i2cdemux/pd.py b/decoders/i2cdemux/pd.py index 2495e84..d6841d3 100644 --- a/decoders/i2cdemux/pd.py +++ b/decoders/i2cdemux/pd.py @@ -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() diff --git a/decoders/i2cfilter/pd.py b/decoders/i2cfilter/pd.py index 1dc7fd1..a54baab 100644 --- a/decoders/i2cfilter/pd.py +++ b/decoders/i2cfilter/pd.py @@ -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')} diff --git a/decoders/i2s/pd.py b/decoders/i2s/pd.py index bfb2c9e..054d69e 100644 --- a/decoders/i2s/pd.py +++ b/decoders/i2s/pd.py @@ -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 index 0000000..4240114 --- /dev/null +++ b/decoders/ieee488/__init__.py @@ -0,0 +1,26 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Gerhard Sittig +## +## 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 . +## + +''' +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 index 0000000..e39ecc5 --- /dev/null +++ b/decoders/ieee488/pd.py @@ -0,0 +1,748 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2016 Rudolf Reuter +## Copyright (C) 2017 Marcus Comstedt +## Copyright (C) 2019 Gerhard Sittig +## +## 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 . +## + +# 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: +[, , ] + +This is the list of s and their respective values: + +Raw bits and bytes at the physical transport level: + - 'IEC_BIT': is not applicable, is the transport's bit value. + - 'GPIB_RAW': is not applicable, 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': is not applicable, is the command's byte value. + - 'LISTEN': is the listener address (0-30), is the raw + byte value (including the 0x20 offset). + - 'TALK': is the talker address (0-30), is the raw byte + value (including the 0x40 offset). + - 'SECONDARY': is the secondary address (0-31), is the + raw byte value (including the 0x60 offset). + - 'MSB_SET': as well as 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': is the talker address (when available), + is the raw data byte (transport layer, ATN inactive). + +Extracted payload information (peers and their communicated data): + - 'TALK_LISTEN': is the current talker, 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': is the talker address (when available), + is the accumulated byte sequence between addressing a talker and EOI, + or the next command/address. + - 'TALKER_TEXT': is the talker address (when available), + 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) diff --git a/decoders/ir_nec/pd.py b/decoders/ir_nec/pd.py index 02d70a9..096ffc9 100644 --- a/decoders/ir_nec/pd.py +++ b/decoders/ir_nec/pd.py @@ -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. diff --git a/decoders/ir_rc5/lists.py b/decoders/ir_rc5/lists.py index fa56dbc..4a8c958 100644 --- a/decoders/ir_rc5/lists.py +++ b/decoders/ir_rc5/lists.py @@ -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+'], diff --git a/decoders/ir_rc5/pd.py b/decoders/ir_rc5/pd.py index 60a9416..bbe0ca7 100644 --- a/decoders/ir_rc5/pd.py +++ b/decoders/ir_rc5/pd.py @@ -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 index 0000000..b2cb9f9 --- /dev/null +++ b/decoders/ir_rc6/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Benedikt Otto +## +## 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 . +## + +''' +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 index 0000000..e195dbd --- /dev/null +++ b/decoders/ir_rc6/pd.py @@ -0,0 +1,205 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Benedikt Otto +## +## 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 . +## + +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 diff --git a/decoders/jitter/__init__.py b/decoders/jitter/__init__.py index 63a0fff..3394ad7 100644 --- a/decoders/jitter/__init__.py +++ b/decoders/jitter/__init__.py @@ -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. ''' diff --git a/decoders/jitter/pd.py b/decoders/jitter/pd.py index f492a9f..5343fbf 100644 --- a/decoders/jitter/pd.py +++ b/decoders/jitter/pd.py @@ -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'}, diff --git a/decoders/jtag/pd.py b/decoders/jtag/pd.py index 2d349bc..618613e 100644 --- a/decoders/jtag/pd.py +++ b/decoders/jtag/pd.py @@ -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'}, diff --git a/decoders/jtag_ejtag/pd.py b/decoders/jtag_ejtag/pd.py index ba5db2a..5a537c9 100644 --- a/decoders/jtag_ejtag/pd.py +++ b/decoders/jtag_ejtag/pd.py @@ -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): diff --git a/decoders/jtag_stm32/pd.py b/decoders/jtag_stm32/pd.py index 593ce13..82558b8 100644 --- a/decoders/jtag_stm32/pd.py +++ b/decoders/jtag_stm32/pd.py @@ -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 index 0000000..4e8c579 --- /dev/null +++ b/decoders/lin/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stephan Thiele +## +## 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 . +## + +''' +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 index 0000000..a8b1998 --- /dev/null +++ b/decoders/lin/pd.py @@ -0,0 +1,247 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stephan Thiele +## +## 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 . +## + +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) diff --git a/decoders/lm75/pd.py b/decoders/lm75/pd.py index 29237d7..14df1b5 100644 --- a/decoders/lm75/pd.py +++ b/decoders/lm75/pd.py @@ -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',)}, diff --git a/decoders/lpc/__init__.py b/decoders/lpc/__init__.py index 827ea94..5227758 100644 --- a/decoders/lpc/__init__.py +++ b/decoders/lpc/__init__.py @@ -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". ''' diff --git a/decoders/lpc/pd.py b/decoders/lpc/pd.py index 452e647..095c8b5 100644 --- a/decoders/lpc/pd.py +++ b/decoders/lpc/pd.py @@ -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 diff --git a/decoders/maple_bus/pd.py b/decoders/maple_bus/pd.py index 8306061..c3f1140 100644 --- a/decoders/maple_bus/pd.py +++ b/decoders/maple_bus/pd.py @@ -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'}, diff --git a/decoders/max7219/pd.py b/decoders/max7219/pd.py index c8e99e1..53067a6 100644 --- a/decoders/max7219/pd.py +++ b/decoders/max7219/pd.py @@ -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'), diff --git a/decoders/mcs48/__init__.py b/decoders/mcs48/__init__.py index 54c01cc..b989a2a 100644 --- a/decoders/mcs48/__init__.py +++ b/decoders/mcs48/__init__.py @@ -19,8 +19,11 @@ ''' 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). ''' diff --git a/decoders/mcs48/pd.py b/decoders/mcs48/pd.py index 185fd89..99b2efc 100644 --- a/decoders/mcs48/pd.py +++ b/decoders/mcs48/pd.py @@ -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'}, diff --git a/decoders/mdio/pd.py b/decoders/mdio/pd.py index 2522912..1d060b0 100644 --- a/decoders/mdio/pd.py +++ b/decoders/mdio/pd.py @@ -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() diff --git a/decoders/microwire/__init__.py b/decoders/microwire/__init__.py index da2d045..53f52d0 100644 --- a/decoders/microwire/__init__.py +++ b/decoders/microwire/__init__.py @@ -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. diff --git a/decoders/microwire/pd.py b/decoders/microwire/pd.py index 4180ba2..6650f38 100644 --- a/decoders/microwire/pd.py +++ b/decoders/microwire/pd.py @@ -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'}, diff --git a/decoders/midi/pd.py b/decoders/midi/pd.py index feb581c..ae35e12 100644 --- a/decoders/midi/pd.py +++ b/decoders/midi/pd.py @@ -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)'), diff --git a/decoders/miller/pd.py b/decoders/miller/pd.py index 0ab5c4a..90c7c19 100644 --- a/decoders/miller/pd.py +++ b/decoders/miller/pd.py @@ -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): diff --git a/decoders/mlx90614/pd.py b/decoders/mlx90614/pd.py index 60927f7..f0dbe22 100644 --- a/decoders/mlx90614/pd.py +++ b/decoders/mlx90614/pd.py @@ -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'), diff --git a/decoders/modbus/pd.py b/decoders/modbus/pd.py index d2307b9..1d012e4 100644 --- a/decoders/modbus/pd.py +++ b/decoders/modbus/pd.py @@ -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') diff --git a/decoders/morse/pd.py b/decoders/morse/pd.py index 9a83b63..f6ff718 100644 --- a/decoders/morse/pd.py +++ b/decoders/morse/pd.py @@ -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'}, ) diff --git a/decoders/mrf24j40/pd.py b/decoders/mrf24j40/pd.py index 8ccf473..db3ca98 100644 --- a/decoders/mrf24j40/pd.py +++ b/decoders/mrf24j40/pd.py @@ -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': diff --git a/decoders/mxc6225xu/pd.py b/decoders/mxc6225xu/pd.py index 1e90455..e961778 100644 --- a/decoders/mxc6225xu/pd.py +++ b/decoders/mxc6225xu/pd.py @@ -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 index 0000000..e754374 --- /dev/null +++ b/decoders/nes_gamepad/__init__.py @@ -0,0 +1,54 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Stephan Thiele +## +## 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 . +## + +''' +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 index 0000000..17c57ca --- /dev/null +++ b/decoders/nes_gamepad/pd.py @@ -0,0 +1,105 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Stephan Thiele +## +## 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 . +## + +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) diff --git a/decoders/nrf24l01/pd.py b/decoders/nrf24l01/pd.py index 221b968..64fe577 100644 --- a/decoders/nrf24l01/pd.py +++ b/decoders/nrf24l01/pd.py @@ -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')}, diff --git a/decoders/nunchuk/pd.py b/decoders/nunchuk/pd.py index 4f006f2..59b1028 100644 --- a/decoders/nunchuk/pd.py +++ b/decoders/nunchuk/pd.py @@ -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'), diff --git a/decoders/onewire_link/__init__.py b/decoders/onewire_link/__init__.py index 69b570f..abd5567 100644 --- a/decoders/onewire_link/__init__.py +++ b/decoders/onewire_link/__init__.py @@ -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) ''' diff --git a/decoders/onewire_link/pd.py b/decoders/onewire_link/pd.py index 72aea83..564d6f0 100644 --- a/decoders/onewire_link/pd.py +++ b/decoders/onewire_link/pd.py @@ -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'}, ) diff --git a/decoders/onewire_network/pd.py b/decoders/onewire_network/pd.py index 5d850b5..ef302ae 100644 --- a/decoders/onewire_network/pd.py +++ b/decoders/onewire_network/pd.py @@ -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'), ) diff --git a/decoders/ook/pd.py b/decoders/ook/pd.py index b4971b6..5fc8d01 100644 --- a/decoders/ook/pd.py +++ b/decoders/ook/pd.py @@ -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: diff --git a/decoders/ook_oregon/pd.py b/decoders/ook_oregon/pd.py index c248726..225f598 100644 --- a/decoders/ook_oregon/pd.py +++ b/decoders/ook_oregon/pd.py @@ -30,6 +30,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['ook'] outputs = [] + tags = ['Sensor'] annotations = ( ('bit', 'Bit'), ('field', 'Field'), diff --git a/decoders/ook_vis/pd.py b/decoders/ook_vis/pd.py index 3785331..f985b96 100644 --- a/decoders/ook_vis/pd.py +++ b/decoders/ook_vis/pd.py @@ -29,6 +29,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['ook'] outputs = ['ook'] + tags = ['Encoding'] annotations = ( ('bit', 'Bit'), ('ref', 'Reference'), diff --git a/decoders/pan1321/pd.py b/decoders/pan1321/pd.py index ecaa680..6c93114 100644 --- a/decoders/pan1321/pd.py +++ b/decoders/pan1321/pd.py @@ -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'), diff --git a/decoders/parallel/pd.py b/decoders/parallel/pd.py index 0e81344..405cdeb 100644 --- a/decoders/parallel/pd.py +++ b/decoders/parallel/pd.py @@ -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 index 0000000..28152d9 --- /dev/null +++ b/decoders/pca9571/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Mickael Bosch +## +## 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 . +## + +''' +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 index 0000000..df309e9 --- /dev/null +++ b/decoders/pca9571/pd.py @@ -0,0 +1,102 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Mickael Bosch +## +## 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 . +## + +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' diff --git a/decoders/ps2/pd.py b/decoders/ps2/pd.py index 6ed04c8..194b0b1 100644 --- a/decoders/ps2/pd.py +++ b/decoders/ps2/pd.py @@ -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): diff --git a/decoders/pwm/__init__.py b/decoders/pwm/__init__.py index b81b141..8f03976 100644 --- a/decoders/pwm/__init__.py +++ b/decoders/pwm/__init__.py @@ -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 diff --git a/decoders/pwm/pd.py b/decoders/pwm/pd.py index 9770445..d8626ee 100644 --- a/decoders/pwm/pd.py +++ b/decoders/pwm/pd.py @@ -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'}, ) diff --git a/decoders/qi/pd.py b/decoders/qi/pd.py index 345efd8..b750d9c 100644 --- a/decoders/qi/pd.py +++ b/decoders/qi/pd.py @@ -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'}, ) diff --git a/decoders/rc_encode/pd.py b/decoders/rc_encode/pd.py index 56b60ca..0d7cc8c 100644 --- a/decoders/rc_encode/pd.py +++ b/decoders/rc_encode/pd.py @@ -80,6 +80,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['logic'] outputs = [] + tags = ['IC', 'IR'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) diff --git a/decoders/rfm12/pd.py b/decoders/rfm12/pd.py index e709696..d3df13a 100644 --- a/decoders/rfm12/pd.py +++ b/decoders/rfm12/pd.py @@ -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'), diff --git a/decoders/rgb_led_spi/pd.py b/decoders/rgb_led_spi/pd.py index d087556..ee94c6b 100644 --- a/decoders/rgb_led_spi/pd.py +++ b/decoders/rgb_led_spi/pd.py @@ -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'), ) diff --git a/decoders/rgb_led_ws281x/pd.py b/decoders/rgb_led_ws281x/pd.py index b4f7c58..adf68eb 100644 --- a/decoders/rgb_led_ws281x/pd.py +++ b/decoders/rgb_led_ws281x/pd.py @@ -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. diff --git a/decoders/rtc8564/pd.py b/decoders/rtc8564/pd.py index 3a9dca0..b57fae6 100644 --- a/decoders/rtc8564/pd.py +++ b/decoders/rtc8564/pd.py @@ -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'), diff --git a/decoders/sda2506/__init__.py b/decoders/sda2506/__init__.py index 4e820ef..bf55510 100644 --- a/decoders/sda2506/__init__.py +++ b/decoders/sda2506/__init__.py @@ -18,7 +18,7 @@ ## ''' -Decoder for Siemens EEPROM SDA2506. +Decoder for Siemens EEPROM SDA 2506-5. ''' from .pd import Decoder diff --git a/decoders/sda2506/pd.py b/decoders/sda2506/pd.py index 8b63cf0..9ae5c01 100644 --- a/decoders/sda2506/pd.py +++ b/decoders/sda2506/pd.py @@ -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,)), ) diff --git a/decoders/sdcard_sd/pd.py b/decoders/sdcard_sd/pd.py index 9402f7e..66fa502 100644 --- a/decoders/sdcard_sd/pd.py +++ b/decoders/sdcard_sd/pd.py @@ -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'}, diff --git a/decoders/sdcard_spi/pd.py b/decoders/sdcard_spi/pd.py index 7c59634..5bb446a 100644 --- a/decoders/sdcard_spi/pd.py +++ b/decoders/sdcard_spi/pd.py @@ -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 index 0000000..ea03e4f --- /dev/null +++ b/decoders/seven_segment/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Benedikt Otto +## +## 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 . +## + +''' +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 index 0000000..87714bb --- /dev/null +++ b/decoders/seven_segment/pd.py @@ -0,0 +1,134 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Benedikt Otto +## +## 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 . +## + +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 index 0000000..d37fd4a --- /dev/null +++ b/decoders/signature/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Shirow Miura +## +## 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 . +## + +''' +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 index 0000000..65b86a2 --- /dev/null +++ b/decoders/signature/pd.py @@ -0,0 +1,142 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Shirow Miura +## +## 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 . +## + +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 diff --git a/decoders/spdif/pd.py b/decoders/spdif/pd.py index add2834..bec28cf 100644 --- a/decoders/spdif/pd.py +++ b/decoders/spdif/pd.py @@ -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,)), ) diff --git a/decoders/spi/__init__.py b/decoders/spi/__init__.py index 491b319..dc5cbc0 100644 --- a/decoders/spi/__init__.py +++ b/decoders/spi/__init__.py @@ -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 diff --git a/decoders/spi/pd.py b/decoders/spi/pd.py index 85bb1cb..5f18d72 100644 --- a/decoders/spi/pd.py +++ b/decoders/spi/pd.py @@ -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]) diff --git a/decoders/spiflash/lists.py b/decoders/spiflash/lists.py index b2092bf..49993ad 100644 --- a/decoders/spiflash/lists.py +++ b/decoders/spiflash/lists.py @@ -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. + }, } diff --git a/decoders/spiflash/pd.py b/decoders/spiflash/pd.py index 1263cd8..33fe229 100644 --- a/decoders/spiflash/pd.py +++ b/decoders/spiflash/pd.py @@ -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 diff --git a/decoders/ssi32/pd.py b/decoders/ssi32/pd.py index f2685e2..5160803 100644 --- a/decoders/ssi32/pd.py +++ b/decoders/ssi32/pd.py @@ -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}, ) diff --git a/decoders/st7735/__init__.py b/decoders/st7735/__init__.py index e724af6..771578c 100644 --- a/decoders/st7735/__init__.py +++ b/decoders/st7735/__init__.py @@ -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 diff --git a/decoders/st7735/pd.py b/decoders/st7735/pd.py index 999038a..252b188 100644 --- a/decoders/st7735/pd.py +++ b/decoders/st7735/pd.py @@ -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'}, diff --git a/decoders/stepper_motor/pd.py b/decoders/stepper_motor/pd.py index 0546395..99a4b2e 100644 --- a/decoders/stepper_motor/pd.py +++ b/decoders/stepper_motor/pd.py @@ -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'}, diff --git a/decoders/swd/__init__.py b/decoders/swd/__init__.py index c1b5ae5..a141239 100644 --- a/decoders/swd/__init__.py +++ b/decoders/swd/__init__.py @@ -21,14 +21,14 @@ 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 diff --git a/decoders/swd/pd.py b/decoders/swd/pd.py index 0f7bc22..cc258ef 100644 --- a/decoders/swd/pd.py +++ b/decoders/swd/pd.py @@ -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'}, diff --git a/decoders/swim/__init__.py b/decoders/swim/__init__.py index cd18b85..315b013 100644 --- a/decoders/swim/__init__.py +++ b/decoders/swim/__init__.py @@ -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 . ## ''' diff --git a/decoders/swim/pd.py b/decoders/swim/pd.py index 452805a..fd43f41 100644 --- a/decoders/swim/pd.py +++ b/decoders/swim/pd.py @@ -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 . ## 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') }, ) diff --git a/decoders/t55xx/pd.py b/decoders/t55xx/pd.py index 07df9c2..d345d31 100644 --- a/decoders/t55xx/pd.py +++ b/decoders/t55xx/pd.py @@ -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'}, ) diff --git a/decoders/tca6408a/pd.py b/decoders/tca6408a/pd.py index 0e60767..4924517 100644 --- a/decoders/tca6408a/pd.py +++ b/decoders/tca6408a/pd.py @@ -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 index 0000000..a95e16e --- /dev/null +++ b/decoders/tdm_audio/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Ben Dooks +## +## 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 . +## + +''' +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 index 0000000..6c5869b --- /dev/null +++ b/decoders/tdm_audio/pd.py @@ -0,0 +1,116 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Ben Dooks +## +## 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 . +## + +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 diff --git a/decoders/timing/pd.py b/decoders/timing/pd.py index 61eab1e..d3e0d1f 100644 --- a/decoders/timing/pd.py +++ b/decoders/timing/pd.py @@ -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'}, ) diff --git a/decoders/tlc5620/pd.py b/decoders/tlc5620/pd.py index 6fe8ae1..4d02792 100644 --- a/decoders/tlc5620/pd.py +++ b/decoders/tlc5620/pd.py @@ -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'}, diff --git a/decoders/uart/pd.py b/decoders/uart/pd.py index 6c3d85c..4ce6ef9 100644 --- a/decoders/uart/pd.py +++ b/decoders/uart/pd.py @@ -39,7 +39,11 @@ This is the list of s and their respective 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 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]) diff --git a/decoders/usb_packet/pd.py b/decoders/usb_packet/pd.py index 489e58e..e262074 100644 --- a/decoders/usb_packet/pd.py +++ b/decoders/usb_packet/pd.py @@ -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')}, diff --git a/decoders/usb_power_delivery/pd.py b/decoders/usb_power_delivery/pd.py index 9368429..d9209bf 100644 --- a/decoders/usb_power_delivery/pd.py +++ b/decoders/usb_power_delivery/pd.py @@ -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) diff --git a/decoders/usb_request/pd.py b/decoders/usb_request/pd.py index e77debc..3f46788 100644 --- a/decoders/usb_request/pd.py +++ b/decoders/usb_request/pd.py @@ -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 diff --git a/decoders/usb_signalling/pd.py b/decoders/usb_signalling/pd.py index 424117d..626dcd0 100644 --- a/decoders/usb_signalling/pd.py +++ b/decoders/usb_signalling/pd.py @@ -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)) diff --git a/decoders/wiegand/pd.py b/decoders/wiegand/pd.py index c494789..a93be10 100644 --- a/decoders/wiegand/pd.py +++ b/decoders/wiegand/pd.py @@ -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 index 0000000..70d2146 --- /dev/null +++ b/decoders/x2444m/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stefan Petersen +## +## 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 . +## + +''' +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 index 0000000..290cc36 --- /dev/null +++ b/decoders/x2444m/pd.py @@ -0,0 +1,111 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stefan Petersen +## +## 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 . +## + +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 diff --git a/decoders/xfp/__init__.py b/decoders/xfp/__init__.py index c4fb008..72f3595 100644 --- a/decoders/xfp/__init__.py +++ b/decoders/xfp/__init__.py @@ -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 diff --git a/decoders/xfp/pd.py b/decoders/xfp/pd.py index 0bc6724..ded7694 100644 --- a/decoders/xfp/pd.py +++ b/decoders/xfp/pd.py @@ -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'), diff --git a/decoders/z80/__init__.py b/decoders/z80/__init__.py index aaf7c76..52ff9ba 100644 --- a/decoders/z80/__init__.py +++ b/decoders/z80/__init__.py @@ -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 diff --git a/decoders/z80/pd.py b/decoders/z80/pd.py index b7ff9a5..fba279b 100644 --- a/decoders/z80/pd.py +++ b/decoders/z80/pd.py @@ -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, diff --git a/instance.c b/instance.c index 075bf09..5264bd0 100644 --- a/instance.c +++ b/instance.c @@ -30,11 +30,6 @@ 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); diff --git a/libsigrokdecode-internal.h b/libsigrokdecode-internal.h index 1d0931e..af245f6 100644 --- a/libsigrokdecode-internal.h +++ b/libsigrokdecode-internal.h @@ -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); diff --git a/libsigrokdecode.h b/libsigrokdecode.h index 55620a7..b9cc921 100644 --- a/libsigrokdecode.h +++ b/libsigrokdecode.h @@ -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; }; diff --git a/session.c b/session.c index 69494ea..386fb71 100644 --- 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 5903c6d..22f47fc 100644 --- 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; } /** @} */ diff --git a/tests/decoder.c b/tests/decoder.c index 2298d93..3acabae 100644 --- a/tests/decoder.c +++ b/tests/decoder.c @@ -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 diff --git a/tests/session.c b/tests/session.c index e79496e..73b7669 100644 --- a/tests/session.c +++ b/tests/session.c @@ -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); } /* diff --git a/type_decoder.c b/type_decoder.c index 6692c3d..c097c7f 100644 --- a/type_decoder.c +++ b/type_decoder.c @@ -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 442d0a6..1e914e3 100644 --- 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. *