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
---------
* 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.
* 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).
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.])])
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);
/* 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;
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";
/**
* 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.
if (!srd_check_init())
return NULL;
- if (!dec)
+ if (!dec || !dec->py_mod)
return NULL;
gstate = PyGILState_Ensure();
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.
*
*/
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;
##
'''
-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.
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
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'},
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):
}
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:
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
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'),
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'),
'''
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
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'),
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'},
)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'uart' PD and decodes the ASCII protocol
+for Amulet LCD display controllers.
+
+Currently the decoder treats both RX and TX the same way, decoding all
+message types.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+from collections import OrderedDict
+
+# OrderedDict which maps command IDs to their names and descriptions.
+cmds = OrderedDict([
+ (0xA0, ('PAGE', 'Jump to page')),
+ (0xD0, ('GBV', 'Get byte variable')),
+ (0xD1, ('GWV', 'Get word variable')),
+ (0xD2, ('GSV', 'Get string variable')),
+ (0xD3, ('GLV', 'Get label variable')),
+ (0xD4, ('GRPC', 'Get RPC buffer')),
+ (0xD5, ('SBV', 'Set byte variable')),
+ (0xD6, ('SWV', 'Set word variable')),
+ (0xD7, ('SSV', 'Set string variable')),
+ (0xD8, ('RPC', 'Invoke RPC')),
+ (0xD9, ('LINE', 'Draw line')),
+ (0xDA, ('RECT', 'Draw rectangle')),
+ (0xDB, ('FRECT', 'Draw filled rectangle')),
+ (0xDC, ('PIXEL', 'Draw pixel')),
+ (0xDD, ('GBVA', 'Get byte variable array')),
+ (0xDE, ('GWVA', 'Get word variable array')),
+ (0xDF, ('SBVA', 'Set byte variable array')),
+ (0xE0, ('GBVR', 'Get byte variable reply')),
+ (0xE1, ('GWVR', 'Get word variable reply')),
+ (0xE2, ('GSVR', 'Get string variable reply')),
+ (0xE3, ('GLVR', 'Get label variable reply')),
+ (0xE4, ('GRPCR', 'Get RPC buffer reply')),
+ (0xE5, ('SBVR', 'Set byte variable reply')),
+ (0xE6, ('SWVR', 'Set word variable reply')),
+ (0xE7, ('SSVR', 'Set string variable reply')),
+ (0xE8, ('RPCR', 'Invoke RPC reply')),
+ (0xE9, ('LINER', 'Draw line reply')),
+ (0xEA, ('RECTR', 'Draw rectangle')),
+ (0xEB, ('FRECTR', 'Draw filled rectangle reply')),
+ (0xEC, ('PIXELR', 'Draw pixel reply')),
+ (0xED, ('GBVAR', 'Get byte variable array reply')),
+ (0xEE, ('GWVAR', 'Get word variable array reply')),
+ (0xEF, ('SBVAR', 'Set byte variable array reply')),
+ (0xF0, ('ACK', 'Acknowledgment')),
+ (0xF1, ('NACK', 'Negative acknowledgment')),
+ (0xF2, ('SWVA', 'Set word variable array')),
+ (0xF3, ('SWVAR', 'Set word variable array reply')),
+ (0xF4, ('GCV', 'Get color variable')),
+ (0xF5, ('GCVR', 'Get color variable reply')),
+ (0xF6, ('SCV', 'Set color variable')),
+ (0xF7, ('SCVR', 'Set color variable reply')),
+])
+
+cmds_with_high_bytes = [
+ 0xA0, # PAGE - Page change
+ 0xD7, # SVV - Set string variable
+ 0xE7, # SVVR - Set string variable reply
+ 0xE2, # GSVR - Get string variable reply
+ 0xE3, # GLVR - Get label variable reply
+]
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from math import ceil
+from .lists import *
+
+L = len(cmds)
+RX = 0
+TX = 1
+
+# Don't forget to keep this in sync with 'cmds' is lists.py.
+class Ann:
+ PAGE, GBV, GWV, GSV, GLV, GRPC, SBV, SWV, SSV, RPC, LINE, RECT, FRECT, \
+ PIXEL, GBVA, GWVA, SBVA, GBVR, GWVR, GSVR, GLVR, GRPCR, SBVR, SWVR, SSVR, \
+ RPCR, LINER, RECTR, FRECTR, PIXELR, GBVAR, GWVAR, SBVAR, ACK, NACK, SWVA, \
+ SWVAR, GCV, GCVR, SCV, SCVR, BIT, FIELD, WARN = range(L + 3)
+
+def cmd_annotation_classes():
+ return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'amulet_ascii'
+ name = 'Amulet ASCII'
+ longname = 'Amulet LCD ASCII'
+ desc = 'Amulet Technologies LCD controller ASCII protocol.'
+ license = 'gplv3+'
+ inputs = ['uart']
+ outputs = []
+ tags = ['Display']
+ annotations = cmd_annotation_classes() + (
+ ('bit', 'Bit'),
+ ('field', 'Field'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (L + 0,)),
+ ('fields', 'Fields', (L + 1,)),
+ ('commands', 'Commands', tuple(range(len(cmds)))),
+ ('warnings', 'Warnings', (L + 2,)),
+ )
+ options = (
+ {'id': 'ms_chan', 'desc': 'Master -> slave channel',
+ 'default': 'RX', 'values': ('RX', 'TX')},
+ {'id': 'sm_chan', 'desc': 'Slave -> master channel',
+ 'default': 'TX', 'values': ('RX', 'TX')},
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.state = None
+ self.cmdstate = None
+
+ # Build dict mapping command keys to handler functions. Each
+ # command in 'cmds' (defined in lists.py) has a matching
+ # handler self.handle_<shortname>.
+ def get_handler(cmd):
+ s = 'handle_%s' % cmds[cmd][0].lower().replace('/', '_')
+ return getattr(self, s)
+ self.cmd_handlers = dict((cmd, get_handler(cmd)) for cmd in cmds.keys())
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putx(self, data):
+ # Simplification, most annotations span exactly one SPI byte/packet.
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def putf(self, data):
+ self.put(self.ss_field, self.es_field, self.out_ann, data)
+
+ def putc(self, data):
+ self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+
+ def cmd_ann_list(self):
+ x, s = cmds[self.state][0], cmds[self.state][1]
+ return ['Command: %s (%s)' % (s, x), 'Command: %s' % s,
+ 'Cmd: %s' % s, 'Cmd: %s' % x, x]
+
+ def emit_cmd_byte(self):
+ self.ss_cmd = self.ss
+ self.putx([Ann.FIELD, self.cmd_ann_list()])
+
+ def emit_addr_bytes(self, pdata):
+ if self.cmdstate == 2:
+ self.ss_field = self.ss
+ self.addr = chr(pdata)
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, 'Addr h 0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.es_field = self.es
+ self.addr += chr(pdata)
+ self.addr = int(self.addr, 16)
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, 'Addr l 0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+
+ def emit_cmd_end(self, data):
+ self.es_cmd = self.es
+ self.putc(data)
+ self.state = None
+
+ def handle_read(self, data):
+ if self.cmdstate == 1:
+ self.emit_cmd_byte()
+ self.addr = 0
+ elif self.cmdstate == 2:
+ self.emit_addr_bytes(pdata)
+ elif self.cmdstate == 3:
+ self.emit_addr_bytes(pdata)
+ self.cmdstate += 1
+
+ def handle_set_common(self, pdata):
+ if self.cmdstate == 1:
+ self.addr = 0
+ self.emit_addr_bytes(pdata)
+
+ def emit_not_implemented(self, data):
+ self.es_cmd = self.es
+ self.putc([Ann.WARN, ['Command not decoded', 'Not decoded']])
+ self.emit_cmd_end(data)
+
+ def handle_string(self, pdata, ann_class):
+ # TODO: unicode / string modifiers...
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = ''
+ if pdata == 0x00:
+ # Null terminated string ends.
+ self.es_field = self.es
+ self.putx([Ann.BIT, ['NULL']])
+ self.putf([Ann.FIELD, ['Value: %s' % self.value,
+ 'Val: %s' % self.value, '%s' % self.value]])
+ self.emit_cmd_end([ann_class, self.cmd_ann_list()])
+ return
+ if self.cmdstate > 3:
+ self.value += chr(pdata)
+ self.putx([Ann.BIT, ['%c' % pdata]])
+ self.cmdstate += 1
+
+ # Command handlers
+
+ # Page change 0xA0, 0x02, index_high, index_low, checksum
+ def handle_page(self, pdata):
+ if self.cmdstate == 2:
+ if pdata == 0x02:
+ self.ss_field = self.ss_cmd
+ self.es_field = self.es
+ self.putf([Ann.FIELD, self.cmd_ann_list()])
+ self.checksum = 0xA0 + 0x02
+ else:
+ self.putx([Ann.WARN, ['Illegal second byte for page change',
+ 'Illegal byte']])
+ self.state = None
+ elif self.cmdstate == 3:
+ self.ss_field = self.ss
+ self.checksum += pdata
+ self.page[0] = pdata
+ elif self.cmdstate == 4:
+ self.checksum += pdata
+ self.page[1] = pdata
+ self.es_field = self.es
+ if self.page[0] == self.page [1] == 0xFF:
+ # Soft reset trigger
+ self.putf(Ann.WARN, ['Soft reset', 'Reset'])
+ else:
+ page = chr(self.page[0]) + chr(self.page[1])
+ self.putf(Ann.FIELD, ['Page index: 0x%s' % page,
+ 'Page: 0x%s' % page, '0x%s' % page])
+ elif self.cmdstate == 5:
+ self.checksum += pdata
+ if (self.checksum & 0xFF) != 0:
+ self.putx([Ann.WARN, ['Checksum error', 'Error', 'ERR']])
+ else:
+ self.putx([Ann.FIELD, ['Checksum OK', 'OK']])
+ self.emit_cmd_end(Ann.PAGE)
+ self.cmdstate += 1
+
+ # Value reads: command byte, address high nibble, address low nibble
+
+ # Get byte value
+ def handle_gbv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GBV, self.cmd_ann_list()])
+
+ # Get word value
+ def handle_gwv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GWV, self.cmd_ann_list()])
+
+ # Get string value
+ def handle_gsv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GSV, self.cmd_ann_list()])
+
+ # Get label value
+ def handle_glv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GLV, self.cmd_ann_list()])
+
+ # Get RPC buffer
+ def handle_grpc(self, pdata):
+ if self.cmdstate == 2:
+ self.ss_field = self.ss
+ self.flags = int(chr(pdata), 16) << 4
+ elif self.cmdstate == 3:
+ self.flags += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['RPC flag: 0x%02X' % self.flags]])
+ self.emit_cmd_end([Ann.GRPC, self.cmd_ann_list()])
+
+ # Get byte value array
+ def handle_gbva(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GBVA, self.cmd_ann_list()])
+
+ # Get word value array
+ def handle_gwva(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GWVA, self.cmd_ann_list()])
+
+ # Get color variable
+ def handle_gcv(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.GCV, self.cmd_ann_list()])
+
+ # Value setters: command byte, address high nibble, address low nibble, data bytes
+
+ # Set byte value data = high nibble, low nibble
+ def handle_sbv(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = chr(pdata)
+ elif self.cmdstate == 5:
+ self.value += chr(pdata)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
+ 'Val: 0x%s' % self.value, '0x%s' % self.value]])
+ self.emit_cmd_end([Ann.SBV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ # Set word value, msb high, msb low, lsb high, lsb low
+ def handle_swv(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate > 3:
+ nibble = self.cmdstate - 4
+ if nibble == 0:
+ self.ss_field = self.ss
+ self.value = 0
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+ 'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
+ self.emit_cmd_end([Ann.SWV, self.cmd_ann_list()])
+ return
+ self.cmdstate += 1
+
+ # Set string value, null terminated utf8 strings
+ def handle_ssv(self, pdata):
+ self.handle_string(pdata, Ann.SSV)
+
+ # Set byte value array
+ def handle_sbva(self, pdata):
+ nibble = (self.cmdstate - 3) % 2
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ elif stage == 2:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SBVA, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ else:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ # Set word value array
+ def handle_swva(self, pdata):
+ nibble = (self.cmdstate - 3) % 4
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ self.value = 0
+ else:
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SWVA, self.cmd_ann_list()])
+ return
+ self.ss_field = self.ss
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+ '0x%04X' % self.value]])
+ self.cmdstate += 1
+
+ # Set color variable
+ def handle_scv(self, pdata):
+ if self.cmdstate == 8:
+ self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ # RPC trigger
+ def handle_rpc(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.RPC, self.cmd_ann_list()])
+
+ # Drawing
+
+ # Decode pair of (x,y) 16bit coordinates
+ def decode_coords(self, pdata):
+ if self.cmdstate == 1:
+ self.coords[0] = 0
+ self.coords[1] = 0
+ self.coords[2] = 0
+ self.coords[3] = 0
+ if self.cmdstate < 18:
+ # Coordinates
+ nibble = (self.cmdstate - 1) % 4
+ i = (self.cmdstate - 1) / 4
+ self.coords[i] += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ self.ss_field = self.ss
+ elif nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Coordinate 0x%04X' % self.coords[i]],
+ ['0x%04X' % self.coords[i]]])
+
+ # TODO: There are actually two protocol revisions for drawing.
+ # Both use 4 bytes for 16bit x and y pairs for start and end.
+ # The older follows this by a pattern selector and then line weight.
+ # Newer version has 6 bytes for 8bit RGB color...
+
+ # Draw line
+ def handle_line(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.LINE, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ # Draw rectange
+ def handle_rect(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.RECT, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ # Draw filled rectangle
+ def handle_frect(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.FRECT, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Fill pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ # Draw pixel
+ def handle_pixel(self, pdata):
+ self.es_cmd = self.es
+ self.putc([Ann.WARN, ['Draw pixel documentation is missing.', 'Undocumented']])
+ self.state = None
+
+ # Replies
+ def handle_gbvr(self, pdata):
+ self.emit_add_bytes(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = int(chr(pdata), 16) << 4
+ self.putx([Ann.BIT, ['High nibble 0x%s' % pdata, '0x%s' % pdata]])
+ elif self.cmdstate == 5:
+ self.value += int(chr(pdata), 16)
+ self.putx([Ann.BIT, ['Low nibble 0x%s' % pdata, '0x%s' % pdata]])
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.emit_cmd_end([Ann.GBVR, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_gwvr(self, pdata):
+ self.emit_add_bytes(pdata)
+ if self.cmdstate > 3:
+ nibble = self.cmdstate - 3
+ if nibble == 0:
+ self.value = 0
+ self.ss_field = self.ss
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ self.putx([Ann.BIT, ['0x%s' % pdata]])
+ if nibble == 3:
+ self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+ '0x%04X' % self.value]])
+ self.es_cmd = self.ss
+ self.emit_cmd_end([Ann.GWVR, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_gsvr(self, pdata):
+ self.handle_string(pdata, Ann.GSVR)
+
+ def handle_glvr(self, pdata):
+ self.handle_string(pdata, Ann.GLVR)
+
+ def handle_grpcr(self, pdata):
+ self.handle_addr(pdata)
+ if self.cmdstate > 3:
+ nibble = (self.cmdstate - 3) % 2
+ if nibble == 0:
+ if pdata == 0x00:
+ self.emit_cmd_end([Ann.GRPCR, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['0x%s' % pdata]])
+ if nibble == 2:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putx([Ann.BIT, ['0x%s' % pdata]])
+ self.putf([Ann.FIELD, ['0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ def handle_sbvr(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = chr(pdata)
+ elif self.cmdstate == 5:
+ self.value += chr(pdata)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
+ 'Val: 0x%s' % self.value, '0x%s' % self.value]])
+ self.emit_cmd_end([Ann.SBVR, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_swvr(self, pdata):
+ self.handle_set_common(pdata)
+ if self.cmdstate == 4:
+ self.ss_field = self.ss
+ self.value = (pdata - 0x30) << 4
+ elif self.cmdstate == 5:
+ self.value += (pdata - 0x30)
+ self.value = self.value << 8
+ elif self.cmdstate == 6:
+ self.value += (pdata - 0x30) << 4
+ elif self.cmdstate == 7:
+ self.value += (pdata - 0x30)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
+ 'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
+ self.emit_cmd_end([Ann.SWVR, self.cmd_ann_list()])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_ssvr(self, pdata):
+ self.handle_string(pdata, Ann.SSVR)
+
+ def handle_rpcr(self, pdata):
+ self.handle_read(pdata)
+ self.emit_cmd_end([Ann.RPCR, self.cmd_ann_list()])
+
+ def handle_liner(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.LINER, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_rectr(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.RECTR, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_frectr(self, pdata):
+ decode_coords(pdata)
+ if self.cmdstate == 18:
+ self.es_cmd = self.es
+ self.putc([Ann.FRECTR, self.cmd_ann_list()])
+ self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
+ self.state = None
+ self.cmdstate += 1
+
+ def handle_pixelr(self, pdata):
+ self.es_cmd = self.es
+ self.putc([Ann.WARN,['Draw pixel documentation is missing.', 'Undocumented']])
+ self.state = None
+
+ def handle_gbvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 2
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ elif stage == 2:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.GBVAR, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ else:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ def handle_gwvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 4
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ self.value = 0
+ else:
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.GWVAR, self.cmd_ann_list()])
+ return
+ self.ss_field = self.ss
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+ '0x%04X' % self.value]])
+ self.cmdstate += 1
+
+ # Get byte variable array reply
+ def handle_sbvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 2
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ elif stage == 2:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SBVAR, self.cmd_ann_list()])
+ return
+ self.value = int(chr(pdata), 16) << 4
+ else:
+ self.value += int(chr(pdata), 16)
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
+ '0x%02X' % self.value]])
+ self.cmdstate += 1
+
+ # Set word variable array reply
+ def handle_swvar(self, pdata):
+ nibble = (self.cmdstate - 3) % 4
+ if self.cmdstate == 2:
+ self.addr = int(chr(pdata), 16) << 4
+ self.ss_field = self.ss
+ self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
+ 'Addr high 0x%c' % pdata, '0x%c' % pdata]])
+ elif self.cmdstate == 3:
+ self.addr += int(chr(pdata), 16)
+ self.es_field = self.ss
+ self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
+ 'Addr low 0x%c' % pdata, '0x%c' % pdata]])
+ self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
+ 'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
+ self.value = 0
+ else:
+ self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
+ if nibble == 0:
+ if pdata == 0x00:
+ # Null terminated list
+ self.emit_cmd_end([Ann.SWVAR, self.cmd_ann_list()])
+ return
+ self.ss_field = self.ss
+ if nibble == 3:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
+ '0x%04X' % self.value]])
+ self.cmdstate += 1
+
+ def handle_gcvr(self, pdata):
+ if self.cmdstate == 8:
+ self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ def handle_scvr(self, pdata):
+ if self.cmdstate == 8:
+ self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
+ self.cmdstate += 1
+
+ # ACK & NACK
+
+ def handle_ack(self, pdata):
+ self.putx([Ann.ACK, self.cmd_ann_list()])
+ self.state = None
+
+ def handle_nack(self, pdata):
+ self.putx([Ann.NACK, self.cmd_ann_list()])
+ self.state = None
+
+ def decode(self, ss, es, data):
+ ptype, rxtx, pdata = data
+
+ self.ss, self.es = ss, es
+
+ if ptype != 'DATA':
+ return
+
+ # Handle commands.
+ try:
+ abort_current = (0xD0 <= pdata[0] <= 0xF7) and \
+ (not (self.state in cmds_with_high_bytes)) and \
+ self.state != None
+ if abort_current:
+ self.putx([Ann.WARN, ['Command aborted by invalid byte', 'Abort']])
+ self.state = pdata[0]
+ self.emit_cmd_byte()
+ self.cmdstate = 1
+ if self.state is None:
+ self.state = pdata[0]
+ self.emit_cmd_byte()
+ self.cmdstate = 1
+ self.cmd_handlers[self.state](pdata[0])
+ except KeyError:
+ self.putx([Ann.WARN, ['Unknown command: 0x%02x' % pdata[0]]])
+ self.state = None
##
'''
-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.
'''
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):
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.
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':
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'),
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'},
##
'''
-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
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},
'''
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
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 = {
0x1b: 'Random',
0x1c: 'DeriveKey',
0x20: 'UpdateExtra',
+ 0x24: 'Counter',
0x28: 'CheckMac',
0x30: 'DevRev',
+ 0x40: 'GenKey',
+ 0x41: 'Sign',
+ 0x43: 'ECDH',
+ 0x45: 'Verify',
+ 0x46: 'PrivWrite',
0x47: 'SHA',
}
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'),
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:
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):
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)]])
def decode(self, ss, es, data):
cmd, databyte = data
-
# State machine.
if self.state == 'IDLE':
# Wait for an I²C START condition.
# Reset the opcode before received data, as this causes
# responses to be displayed incorrectly.
self.opcode = -1
- self.output_rx_bytes()
+ if len(self.bytes) > 0:
+ self.output_rx_bytes()
self.waddr = -1
self.bytes = []
self.state = 'IDLE'
self.output_tx_bytes()
self.bytes = []
self.state = 'IDLE'
-
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'},
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'),
'''
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
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'},
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
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
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'
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 = (
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.
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)
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
# 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.
# 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']])
# 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]
# 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:
# 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]
# 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)]])
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
+by the Texas Instruments low-power sub-1GHz RF transceiver chips.
+
+Details:
+http://www.ti.com/lit/ds/symlink/cc1101.pdf
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+regs = {
+# addr: 'name'
+ 0x00: 'IOCFG2',
+ 0x01: 'IOCFG1',
+ 0x02: 'IOCFG0',
+ 0x03: 'FIFOTHR',
+ 0x04: 'SYNC1',
+ 0x05: 'SYNC0',
+ 0x06: 'PKTLEN',
+ 0x07: 'PKTCTRL1',
+ 0x08: 'PKTCTRL0',
+ 0x09: 'ADDR',
+ 0x0A: 'CHANNR',
+ 0x0B: 'FSCTRL1',
+ 0x0C: 'FSCTRL0',
+ 0x0D: 'FREQ2',
+ 0x0E: 'FREQ1',
+ 0x0F: 'FREQ0',
+ 0x10: 'MDMCFG4',
+ 0x11: 'MDMCFG3',
+ 0x12: 'MDMCFG2',
+ 0x13: 'MDMCFG1',
+ 0x14: 'MDMCFG0',
+ 0x15: 'DEVIATN',
+ 0x16: 'MCSM2',
+ 0x17: 'MCSM1',
+ 0x18: 'MCSM0',
+ 0x19: 'FOCCFG',
+ 0x1A: 'BSCFG',
+ 0x1B: 'AGCTRL2',
+ 0x1C: 'AGCTRL1',
+ 0x1D: 'AGCTRL0',
+ 0x1E: 'WOREVT1',
+ 0x1F: 'WOREVT0',
+ 0x20: 'WORCTRL',
+ 0x21: 'FREND1',
+ 0x22: 'FREND0',
+ 0x23: 'FSCAL3',
+ 0x24: 'FSCAL2',
+ 0x25: 'FSCAL1',
+ 0x26: 'FSCAL0',
+ 0x27: 'RCCTRL1',
+ 0x28: 'RCCTRL0',
+ 0x29: 'FSTEST',
+ 0x2A: 'PTEST',
+ 0x2B: 'AGCTEST',
+ 0x2C: 'TEST2',
+ 0x2D: 'TEST1',
+ 0x2E: 'TEST0',
+ 0x30: 'PARTNUM',
+ 0x31: 'VERSION',
+ 0x32: 'FREQEST',
+ 0x33: 'LQI',
+ 0x34: 'RSSI',
+ 0x35: 'MARCSTATE',
+ 0x36: 'WORTIME1',
+ 0x37: 'WORTIME0',
+ 0x38: 'PKTSTATUS',
+ 0x39: 'VCO_VC_DAC',
+ 0x3A: 'TXBYTES',
+ 0x3B: 'RXBYTES',
+ 0x3C: 'RCCTRL1_STATUS',
+ 0x3D: 'RCCTRL0_STATUS',
+ 0x3E: 'PATABLE',
+ 0x3F: 'FIFO'
+}
+
+strobes = {
+# addr: 'name'
+ 0x30: 'SRES',
+ 0x31: 'SFSTXON',
+ 0x32: 'SXOFF',
+ 0x33: 'SCAL',
+ 0x34: 'SRX',
+ 0x35: 'STX',
+ 0x36: 'SIDLE',
+ 0x37: '',
+ 0x38: 'SWOR',
+ 0x39: 'SPWD',
+ 0x3A: 'SFRX',
+ 0x3B: 'SFTX',
+ 0x3C: 'SWORRST',
+ 0x3D: 'SNOP'
+}
+
+status_reg_states = {
+# value: 'state name'
+ 0b000: 'IDLE',
+ 0b001: 'RX',
+ 0b010: 'TX',
+ 0b011: 'FSTXON',
+ 0b100: 'CALIBRATE',
+ 0b101: 'SETTLING',
+ 0b110: 'RXFIFO_OVERFLOW',
+ 0b111: 'TXFIFO_OVERFLOW'
+}
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+from collections import namedtuple
+from .lists import *
+
+ANN_STROBE, ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ, \
+ ANN_BURST_WRITE, ANN_STATUS_READ, ANN_STATUS, ANN_WARN = range(8)
+
+Pos = namedtuple('Pos', ['ss', 'es'])
+Data = namedtuple('Data', ['mosi', 'miso'])
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'cc1101'
+ name = 'CC1101'
+ longname = 'Texas Instruments CC1101'
+ desc = 'Low-power sub-1GHz RF transceiver chip.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Wireless/RF']
+ annotations = (
+ ('strobe', 'Command strobe'),
+ ('single_read', 'Single register read'),
+ ('single_write', 'Single register write'),
+ ('burst_read', 'Burst register read'),
+ ('burst_write', 'Burst register write'),
+ ('status', 'Status register'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('cmd', 'Commands', (ANN_STROBE,)),
+ ('data', 'Data', (ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ,
+ ANN_BURST_WRITE, ANN_STATUS_READ)),
+ ('status', 'Status register', (ANN_STATUS,)),
+ ('warnings', 'Warnings', (ANN_WARN,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.next()
+ self.requirements_met = True
+ self.cs_was_released = False
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def warn(self, pos, msg):
+ '''Put a warning message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [ANN_WARN, [msg]])
+
+ def putp(self, pos, ann, msg):
+ '''Put an annotation message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [ann, [msg]])
+
+ def putp2(self, pos, ann, msg1, msg2):
+ '''Put an annotation message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [ann, [msg1, msg2]])
+
+ def next(self):
+ '''Resets the decoder after a complete command was decoded.'''
+ # 'True' for the first byte after CS# went low.
+ self.first = True
+
+ # The current command, and the minimum and maximum number
+ # of data bytes to follow.
+ self.cmd = None
+ self.min = 0
+ self.max = 0
+
+ # Used to collect the bytes after the command byte
+ # (and the start/end sample number).
+ self.mb = []
+ self.ss_mb = -1
+ self.es_mb = -1
+
+ def mosi_bytes(self):
+ '''Returns the collected MOSI bytes of a multi byte command.'''
+ return [b.mosi for b in self.mb]
+
+ def miso_bytes(self):
+ '''Returns the collected MISO bytes of a multi byte command.'''
+ return [b.miso for b in self.mb]
+
+ def decode_command(self, pos, b):
+ '''Decodes the command byte 'b' at position 'pos' and prepares
+ the decoding of the following data bytes.'''
+ c = self.parse_command(b)
+ if c is None:
+ self.warn(pos, 'unknown command')
+ return
+
+ self.cmd, self.dat, self.min, self.max = c
+
+ if self.cmd == 'Strobe':
+ self.putp(pos, ANN_STROBE, self.format_command())
+ else:
+ # Don't output anything now, the command is merged with
+ # the data bytes following it.
+ self.ss_mb = pos.ss
+
+ def format_command(self):
+ '''Returns the label for the current command.'''
+ if self.cmd in ('Read', 'Burst read', 'Write', 'Burst write', 'Status read'):
+ return self.cmd
+ if self.cmd == 'Strobe':
+ reg = strobes.get(self.dat, 'unknown strobe')
+ return '{} {}'.format(self.cmd, reg)
+ else:
+ return 'TODO Cmd {}'.format(self.cmd)
+
+ def parse_command(self, b):
+ '''Parses the command byte.
+
+ Returns a tuple consisting of:
+ - the name of the command
+ - additional data needed to dissect the following bytes
+ - minimum number of following bytes
+ - maximum number of following bytes (None for infinite)
+ '''
+
+ addr = b & 0x3F
+ if (addr < 0x30) or (addr == 0x3E) or (addr == 0x3F):
+ if (b & 0xC0) == 0x00:
+ return ('Write', addr, 1, 1)
+ if (b & 0xC0) == 0x40:
+ return ('Burst write', addr, 1, 99999)
+ if (b & 0xC0) == 0x80:
+ return ('Read', addr, 1, 1)
+ if (b & 0xC0) == 0xC0:
+ return ('Burst read', addr, 1, 99999)
+ else:
+ self.warn(pos, 'unknown address/command combination')
+ else:
+ if (b & 0x40) == 0x00:
+ return ('Strobe', addr, 0, 0)
+ if (b & 0xC0) == 0xC0:
+ return ('Status read', addr, 1, 99999)
+ else:
+ self.warn(pos, 'unknown address/command combination')
+
+ def decode_reg(self, pos, ann, regid, data):
+ '''Decodes a register.
+
+ pos -- start and end sample numbers of the register
+ ann -- the annotation number that is used to output the register.
+ regid -- may be either an integer used as a key for the 'regs'
+ dictionary, or a string directly containing a register name.'
+ data -- the register content.
+ '''
+
+ if type(regid) == int:
+ # Get the name of the register.
+ if regid not in regs:
+ self.warn(pos, 'unknown register')
+ return
+ name = '{} ({:02X})'.format(regs[regid], regid)
+ else:
+ name = regid
+
+ if regid == 'STATUS' and ann == ANN_STATUS:
+ label = 'Status'
+ self.decode_status_reg(pos, ann, data, label)
+ else:
+ if self.cmd in ('Write', 'Read', 'Status read', 'Burst read', 'Burst write'):
+ label = '{}: {}'.format(self.format_command(), name)
+ else:
+ label = 'Reg ({}) {}'.format(self.cmd, name)
+ self.decode_mb_data(pos, ann, data, label)
+
+ def decode_status_reg(self, pos, ann, data, label):
+ '''Decodes the data bytes 'data' of a status register at position
+ 'pos'. The decoded data is prefixed with 'label'.'''
+ status = data[0]
+ # bit 7 --> CHIP_RDYn
+ if status & 0b10000000 == 0b10000000:
+ longtext_chiprdy = 'CHIP_RDYn is high! '
+ else:
+ longtext_chiprdy = ''
+ # bits 6:4 --> STATE
+ state = (status & 0x70) >> 4
+ longtext_state = 'STATE is {}, '.format(status_reg_states[state])
+ # bits 3:0 --> FIFO_BYTES_AVAILABLE
+ fifo_bytes = status & 0x0F
+ if self.cmd in ('Single read', 'Status read', 'Burst read'):
+ longtext_fifo = '{} bytes available in RX FIFO'.format(fifo_bytes)
+ else:
+ longtext_fifo = '{} bytes free in TX FIFO'.format(fifo_bytes)
+
+ text = '{} = {:02X}'.format(label, status)
+ longtext = ''.join([text, '; ', longtext_chiprdy, longtext_state, longtext_fifo])
+ self.putp2(pos, ann, longtext, text)
+
+ def decode_mb_data(self, pos, ann, data, label):
+ '''Decodes the data bytes 'data' of a multibyte command at position
+ 'pos'. The decoded data is prefixed with 'label'.'''
+
+ def escape(b):
+ return '{:02X}'.format(b)
+
+ data = ' '.join([escape(b) for b in data])
+ text = '{} = {}'.format(label, data)
+ self.putp(pos, ann, text)
+
+ def finish_command(self, pos):
+ '''Decodes the remaining data bytes at position 'pos'.'''
+
+ if self.cmd == 'Write':
+ self.decode_reg(pos, ANN_SINGLE_WRITE, self.dat, self.mosi_bytes())
+ elif self.cmd == 'Burst write':
+ self.decode_reg(pos, ANN_BURST_WRITE, self.dat, self.mosi_bytes())
+ elif self.cmd == 'Read':
+ self.decode_reg(pos, ANN_SINGLE_READ, self.dat, self.miso_bytes())
+ elif self.cmd == 'Burst read':
+ self.decode_reg(pos, ANN_BURST_READ, self.dat, self.miso_bytes())
+ elif self.cmd == 'Strobe':
+ self.decode_reg(pos, ANN_STROBE, self.dat, self.mosi_bytes())
+ elif self.cmd == 'Status read':
+ self.decode_reg(pos, ANN_STATUS_READ, self.dat, self.miso_bytes())
+ else:
+ self.warn(pos, 'unhandled command')
+
+ def decode(self, ss, es, data):
+ if not self.requirements_met:
+ return
+
+ ptype, data1, data2 = data
+
+ if ptype == 'CS-CHANGE':
+ if data1 is None:
+ if data2 is None:
+ self.requirements_met = False
+ raise ChannelError('CS# pin required.')
+ elif data2 == 1:
+ self.cs_was_released = True
+
+ if data1 == 0 and data2 == 1:
+ # Rising edge, the complete command is transmitted, process
+ # the bytes that were sent after the command byte.
+ if self.cmd:
+ # Check if we got the minimum number of data bytes
+ # after the command byte.
+ if len(self.mb) < self.min:
+ self.warn((ss, ss), 'missing data bytes')
+ elif self.mb:
+ self.finish_command(Pos(self.ss_mb, self.es_mb))
+
+ self.next()
+ self.cs_was_released = True
+
+ elif ptype == 'DATA' and self.cs_was_released:
+ mosi, miso = data1, data2
+ pos = Pos(ss, es)
+
+ if miso is None or mosi is None:
+ self.requirements_met = False
+ raise ChannelError('Both MISO and MOSI pins required.')
+
+ if self.first:
+ self.first = False
+ # First MOSI byte is always the command.
+ self.decode_command(pos, mosi)
+ # First MISO byte is always the status register.
+ self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+ else:
+ if not self.cmd or len(self.mb) >= self.max:
+ self.warn(pos, 'excess byte')
+ else:
+ # Collect the bytes after the command byte.
+ if self.ss_mb == -1:
+ self.ss_mb = ss
+ self.es_mb = es
+ self.mb.append(Data(mosi, miso))
##
'''
-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.
'''
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'},
)
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
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
desc = '100 Gigabit C form-factor pluggable (CFP) protocol.'
license = 'BSD'
inputs = ['mdio']
- outputs = ['cfp']
+ outputs = []
+ tags = ['Networking']
annotations = (
('register', 'Register'),
('decode', 'Decode'),
##
'''
-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
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'},
)
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'},
)
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
self.dev_type = None
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'},
)
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'},
)
('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,)),
)
rates = {
0b00: '1Hz',
- 0b01: '4096kHz',
- 0b10: '8192kHz',
- 0b11: '32768kHz',
+ 0b01: '4096Hz',
+ 0b10: '8192Hz',
+ 0b11: '32768Hz',
}
DS1307_I2C_ADDRESS = 0x68
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'),
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']])
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'onewire_network' PD and decodes the
+Maxim DS2408 1-Wire 8-channel addressable switch protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Mariusz Bialonczyk <manio@skyboo.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+# Dictionary of FUNCTION commands and their names.
+command = {
+ 0xf0: 'Read PIO Registers',
+ 0xf5: 'Channel Access Read',
+ 0x5a: 'Channel Access Write',
+ 0xcc: 'Write Conditional Search Register',
+ 0xc3: 'Reset Activity Latches',
+ 0x3c: 'Disable Test Mode',
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ds2408'
+ name = 'DS2408'
+ longname = 'Maxim DS2408'
+ desc = '1-Wire 8-channel addressable switch.'
+ license = 'gplv2+'
+ inputs = ['onewire_network']
+ outputs = []
+ tags = ['Embedded/industrial', 'IC']
+ annotations = (
+ ('text', 'Human-readable text'),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ # Bytes for function command.
+ self.bytes = []
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putx(self, data):
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def decode(self, ss, es, data):
+ code, val = data
+
+ if code == 'RESET/PRESENCE':
+ self.ss, self.es = ss, es
+ self.putx([0, ['Reset/presence: %s'
+ % ('true' if val else 'false')]])
+ self.bytes = []
+ elif code == 'ROM':
+ self.ss, self.es = ss, es
+ family_code = val & 0xff
+ self.putx([0, ['ROM: 0x%016x (family code 0x%02x)' % (val, family_code)]])
+ self.bytes = []
+ elif code == 'DATA':
+ self.bytes.append(val)
+ if 1 == len(self.bytes):
+ self.ss, self.es = ss, es
+ if val not in command:
+ self.putx([0, ['Unrecognized command: 0x%02x' % val]])
+ else:
+ self.putx([0, ['%s (0x%02x)' % (command[val], val)]])
+ elif 0xf0 == self.bytes[0]: # Read PIO Registers
+ if 2 == len(self.bytes):
+ self.ss = ss
+ elif 3 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['Target address: 0x%04x'
+ % ((self.bytes[2] << 8) + self.bytes[1])]])
+ elif 3 < len(self.bytes):
+ self.ss, self.es = ss, es
+ self.putx([0, ['Data: 0x%02x' % self.bytes[-1]]])
+ elif 0xf5 == self.bytes[0]: # Channel Access Read
+ if 2 == len(self.bytes):
+ self.ss = ss
+ elif 2 < len(self.bytes):
+ self.ss, self.es = ss, es
+ self.putx([0, ['PIO sample: 0x%02x' % self.bytes[-1]]])
+ elif 0x5a == self.bytes[0]: # Channel Access Write
+ if 2 == len(self.bytes):
+ self.ss = ss
+ elif 3 == len(self.bytes):
+ self.es = es
+ if (self.bytes[-1] == (self.bytes[-2] ^ 0xff)):
+ self.putx([0, ['Data: 0x%02x (bit-inversion correct: 0x%02x)' % (self.bytes[-2], self.bytes[-1])]])
+ else:
+ self.putx([0, ['Data error: second byte (0x%02x) is not bit-inverse of first (0x%02x)' % (self.bytes[-1], self.bytes[-2])]])
+ elif 3 < len(self.bytes):
+ self.ss, self.es = ss, es
+ if 0xaa == self.bytes[-1]:
+ self.putx([0, ['Success']])
+ elif 0xff == self.bytes[-1]:
+ self.putx([0, ['Fail New State']])
+ elif 0xcc == self.bytes[0]: # Write Conditional Search Register
+ if 2 == len(self.bytes):
+ self.ss = ss
+ elif 3 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['Target address: 0x%04x'
+ % ((self.bytes[2] << 8) + self.bytes[1])]])
+ elif 3 < len(self.bytes):
+ self.ss, self.es = ss, es
+ self.putx([0, ['Data: 0x%02x' % self.bytes[-1]]])
+ elif 0xc3 == self.bytes[0]: # Reset Activity Latches
+ if 2 == len(self.bytes):
+ self.ss = ss
+ elif 2 < len(self.bytes):
+ self.ss, self.es = ss, es
+ if 0xaa == self.bytes[-1]:
+ self.putx([0, ['Success']])
+ else:
+ self.putx([0, ['Invalid byte']])
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'),
)
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)]])
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'),
)
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'},
)
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
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.
##
'''
-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
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):
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())},
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'),
('data', 'Data', (0, 1)),
('warnings', 'Warnings', (2,)),
)
+ binary = (
+ ('address', 'Address'),
+ ('data', 'Data'),
+ )
def __init__(self):
self.reset()
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']
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).
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):
desc = 'EM4100 100-150kHz RFID protocol.'
license = 'gplv2+'
inputs = ['logic']
- outputs = ['em4100']
+ outputs = []
+ tags = ['IC', 'RFID']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
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'},
)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Jiahao Li <reg@ljh.me>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
+by the Microchip ENC28J60 Ethernet chip.
+
+Details:
+http://ww1.microchip.com/downloads/en/DeviceDoc/39662e.pdf
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Jiahao Li <reg@ljh.me>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+REGS = [
+ [
+ 'ERDPTL',
+ 'ERDPTH',
+ 'EWRPTL',
+ 'EWRPTH',
+ 'ETXSTL',
+ 'ETXSTH',
+ 'ETXNDL',
+ 'ETXNDH',
+ 'ERXSTL',
+ 'ERXSTH',
+ 'ERXNDL',
+ 'ERXNDH',
+ 'ERXRDPTL',
+ 'ERXRDPTH',
+ 'ERXWRPTL',
+ 'ERXWRPTH',
+ 'EDMASTL',
+ 'EDMASTH',
+ 'EDMANDL',
+ 'EDMANDH',
+ 'EDMADSTL',
+ 'EDMADSTH',
+ 'EDMACSL',
+ 'EDMACSH',
+ '—',
+ '—',
+ 'Reserved',
+ 'EIE',
+ 'EIR',
+ 'ESTAT',
+ 'ECON2',
+ 'ECON1',
+ ],
+ [
+ 'EHT0',
+ 'EHT1',
+ 'EHT2',
+ 'EHT3',
+ 'EHT4',
+ 'EHT5',
+ 'EHT6',
+ 'EHT7',
+ 'EPMM0',
+ 'EPMM1',
+ 'EPMM2',
+ 'EPMM3',
+ 'EPMM4',
+ 'EPMM5',
+ 'EPMM6',
+ 'EPMM7',
+ 'EPMCSL',
+ 'EPMCSH',
+ '—',
+ '—',
+ 'EPMOL',
+ 'EPMOH',
+ 'Reserved',
+ 'Reserved',
+ 'ERXFCON',
+ 'EPKTCNT',
+ 'Reserved',
+ 'EIE',
+ 'EIR',
+ 'ESTAT',
+ 'ECON2',
+ 'ECON1',
+ ],
+ [
+ 'MACON1',
+ 'Reserved',
+ 'MACON3',
+ 'MACON4',
+ 'MABBIPG',
+ '—',
+ 'MAIPGL',
+ 'MAIPGH',
+ 'MACLCON1',
+ 'MACLCON2',
+ 'MAMXFLL',
+ 'MAMXFLH',
+ 'Reserved',
+ 'Reserved',
+ 'Reserved',
+ '—',
+ 'Reserved',
+ 'Reserved',
+ 'MICMD',
+ '—',
+ 'MIREGADR',
+ 'Reserved',
+ 'MIWRL',
+ 'MIWRH',
+ 'MIRDL',
+ 'MIRDH',
+ 'Reserved',
+ 'EIE',
+ 'EIR',
+ 'ESTAT',
+ 'ECON2',
+ 'ECON1',
+ ],
+ [
+ 'MAADR5',
+ 'MAADR6',
+ 'MAADR3',
+ 'MAADR4',
+ 'MAADR1',
+ 'MAADR2',
+ 'EBSTSD',
+ 'EBSTCON',
+ 'EBSTCSL',
+ 'EBSTCSH',
+ 'MISTAT',
+ '—',
+ '—',
+ '—',
+ '—',
+ '—',
+ '—',
+ '—',
+ 'EREVID',
+ '—',
+ '—',
+ 'ECOCON',
+ 'Reserved',
+ 'EFLOCON',
+ 'EPAUSL',
+ 'EPAUSH',
+ 'Reserved',
+ 'EIE',
+ 'EIR',
+ 'ESTAT',
+ 'ECON2',
+ 'ECON1',
+ ],
+]
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Jiahao Li <reg@ljh.me>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+import sigrokdecode as srd
+from .lists import *
+
+OPCODE_MASK = 0b11100000
+REG_ADDR_MASK = 0b00011111
+
+OPCODE_HANDLERS = {
+ 0b00000000: '_process_rcr',
+ 0b00100000: '_process_rbm',
+ 0b01000000: '_process_wcr',
+ 0b01100000: '_process_wbm',
+ 0b10000000: '_process_bfs',
+ 0b10100000: '_process_bfc',
+ 0b11100000: '_process_src',
+}
+
+(ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC, ANN_DATA,
+ANN_REG_ADDR, ANN_WARNING) = range(10)
+
+REG_ADDR_ECON1 = 0x1F
+BIT_ECON1_BSEL0 = 0b00000001
+BIT_ECON1_BSEL1 = 0b00000010
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'enc28j60'
+ name = 'ENC28J60'
+ longname = 'Microchip ENC28J60'
+ desc = 'Microchip ENC28J60 10Base-T Ethernet controller protocol.'
+ license = 'mit'
+ inputs = ['spi']
+ outputs = []
+ tags = ['Embedded/industrial', 'Networking']
+ annotations = (
+ ('rcr', 'Read Control Register'),
+ ('rbm', 'Read Buffer Memory'),
+ ('wcr', 'Write Control Register'),
+ ('wbm', 'Write Buffer Memory'),
+ ('bfs', 'Bit Field Set'),
+ ('bfc', 'Bit Field Clear'),
+ ('src', 'System Reset Command'),
+ ('data', 'Data'),
+ ('reg-addr', 'Register Address'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
+ ('commands', 'Commands',
+ (ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC)),
+ ('warnings', 'Warnings', (ANN_WARNING,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.mosi = []
+ self.miso = []
+ self.ranges = []
+ self.cmd_ss = None
+ self.cmd_es = None
+ self.range_ss = None
+ self.range_es = None
+ self.active = False
+ self.bsel0 = None
+ self.bsel1 = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putc(self, data):
+ self.put(self.cmd_ss, self.cmd_es, self.out_ann, data)
+
+ def putr(self, data):
+ self.put(self.range_ss, self.range_es, self.out_ann, data)
+
+ def _process_command(self):
+ if len(self.mosi) == 0:
+ self.active = False
+ return
+
+ header = self.mosi[0]
+ opcode = header & OPCODE_MASK
+
+ if opcode not in OPCODE_HANDLERS:
+ self._put_command_warning("Unknown opcode.")
+ self.active = False
+ return
+
+ getattr(self, OPCODE_HANDLERS[opcode])()
+
+ self.active = False
+
+ def _get_register_name(self, reg_addr):
+ if (self.bsel0 is None) or (self.bsel1 is None):
+ # We don't know the bank we're in yet.
+ return None
+ else:
+ bank = (self.bsel1 << 1) + self.bsel0
+ return REGS[bank][reg_addr]
+
+ def _put_register_header(self):
+ reg_addr = self.mosi[0] & REG_ADDR_MASK
+ reg_name = self._get_register_name(reg_addr)
+
+ self.range_ss, self.range_es = self.cmd_ss, self.ranges[1][0]
+
+ if reg_name is None:
+ # We don't know the bank we're in yet.
+ self.putr([ANN_REG_ADDR, [
+ 'Reg Bank ? Addr 0x{0:02X}'.format(reg_addr),
+ '?:{0:02X}'.format(reg_addr)]])
+ self.putr([ANN_WARNING, ['Warning: Register bank not known yet.',
+ 'Warning']])
+ else:
+ self.putr([ANN_REG_ADDR, ['Reg {0}'.format(reg_name),
+ '{0}'.format(reg_name)]])
+
+ if (reg_name == '-') or (reg_name == 'Reserved'):
+ self.putr([ANN_WARNING, ['Warning: Invalid register accessed.',
+ 'Warning']])
+
+ def _put_data_byte(self, data, byte_index, binary=False):
+ self.range_ss = self.ranges[byte_index][0]
+ if byte_index == len(self.mosi) - 1:
+ self.range_es = self.cmd_es
+ else:
+ self.range_es = self.ranges[byte_index + 1][0]
+
+ if binary:
+ self.putr([ANN_DATA, ['Data 0b{0:08b}'.format(data),
+ '{0:08b}'.format(data)]])
+ else:
+ self.putr([ANN_DATA, ['Data 0x{0:02X}'.format(data),
+ '{0:02X}'.format(data)]])
+
+ def _put_command_warning(self, reason):
+ self.putc([ANN_WARNING, ['Warning: {0}'.format(reason), 'Warning']])
+
+ def _process_rcr(self):
+ self.putc([ANN_RCR, ['Read Control Register', 'RCR']])
+
+ if (len(self.mosi) != 2) and (len(self.mosi) != 3):
+ self._put_command_warning('Invalid command length.')
+ return
+
+ self._put_register_header()
+
+ reg_name = self._get_register_name(self.mosi[0] & REG_ADDR_MASK)
+ if reg_name is None:
+ # We can't tell if we're accessing MAC/MII registers or not
+ # Let's trust the user in this case.
+ pass
+ else:
+ if (reg_name[0] == 'M') and (len(self.mosi) != 3):
+ self._put_command_warning('Attempting to read a MAC/MII '
+ + 'register without using the dummy byte.')
+ return
+
+ if (reg_name[0] != 'M') and (len(self.mosi) != 2):
+ self._put_command_warning('Attempting to read a non-MAC/MII '
+ + 'register using the dummy byte.')
+ return
+
+ if len(self.mosi) == 2:
+ self._put_data_byte(self.miso[1], 1)
+ else:
+ self.range_ss, self.range_es = self.ranges[1][0], self.ranges[2][0]
+ self.putr([ANN_DATA, ['Dummy Byte', 'Dummy']])
+ self._put_data_byte(self.miso[2], 2)
+
+ def _process_rbm(self):
+ if self.mosi[0] != 0b00111010:
+ self._put_command_warning('Invalid header byte.')
+ return
+
+ self.putc([ANN_RBM, ['Read Buffer Memory: Length {0}'.format(
+ len(self.mosi) - 1), 'RBM']])
+
+ for i in range(1, len(self.miso)):
+ self._put_data_byte(self.miso[i], i)
+
+ def _process_wcr(self):
+ self.putc([ANN_WCR, ['Write Control Register', 'WCR']])
+
+ if len(self.mosi) != 2:
+ self._put_command_warning('Invalid command length.')
+ return
+
+ self._put_register_header()
+ self._put_data_byte(self.mosi[1], 1)
+
+ if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
+ self.bsel0 = (self.mosi[1] & BIT_ECON1_BSEL0) >> 0
+ self.bsel1 = (self.mosi[1] & BIT_ECON1_BSEL1) >> 1
+
+ def _process_wbm(self):
+ if self.mosi[0] != 0b01111010:
+ self._put_command_warning('Invalid header byte.')
+ return
+
+ self.putc([ANN_WBM, ['Write Buffer Memory: Length {0}'.format(
+ len(self.mosi) - 1), 'WBM']])
+
+ for i in range(1, len(self.mosi)):
+ self._put_data_byte(self.mosi[i], i)
+
+ def _process_bfc(self):
+ self.putc([ANN_BFC, ['Bit Field Clear', 'BFC']])
+
+ if len(self.mosi) != 2:
+ self._put_command_warning('Invalid command length.')
+ return
+
+ self._put_register_header()
+ self._put_data_byte(self.mosi[1], 1, True)
+
+ if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
+ if self.mosi[1] & BIT_ECON1_BSEL0:
+ self.bsel0 = 0
+ if self.mosi[1] & BIT_ECON1_BSEL1:
+ self.bsel1 = 0
+
+ def _process_bfs(self):
+ self.putc([ANN_BFS, ['Bit Field Set', 'BFS']])
+
+ if len(self.mosi) != 2:
+ self._put_command_warning('Invalid command length.')
+ return
+
+ self._put_register_header()
+ self._put_data_byte(self.mosi[1], 1, True)
+
+ if self.mosi[0] & REG_ADDR_MASK == REG_ADDR_ECON1:
+ if self.mosi[1] & BIT_ECON1_BSEL0:
+ self.bsel0 = 1
+ if self.mosi[1] & BIT_ECON1_BSEL1:
+ self.bsel1 = 1
+
+ def _process_src(self):
+ self.putc([ANN_SRC, ['System Reset Command', 'SRC']])
+
+ if len(self.mosi) != 1:
+ self._put_command_warning('Invalid command length.')
+ return
+
+ self.bsel0 = 0
+ self.bsel1 = 0
+
+ def decode(self, ss, es, data):
+ ptype, data1, data2 = data
+
+ if ptype == 'CS-CHANGE':
+ new_cs = data2
+
+ if new_cs == 0:
+ self.active = True
+ self.cmd_ss = ss
+ self.mosi = []
+ self.miso = []
+ self.ranges = []
+ elif new_cs == 1:
+ if self.active:
+ self.cmd_es = es
+ self._process_command()
+ elif ptype == 'DATA':
+ mosi, miso = data1, data2
+
+ self.mosi.append(mosi)
+ self.miso.append(miso)
+ self.ranges.append((ss, es))
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+FlexRay is a fast, deterministic and fault-tolerant fieldbus system
+which is used in cars in high security related areas like X-by-Wire.
+
+It is the result of the FlexRay consortium which consisted of BMW,
+Daimler, Motorola (today Freescale) and Philips, with the goal of
+working out a common standard automotive bus system.
+
+This decoder assumes that at least one channel of a logic level RX line
+of a transceiver is sampled (e.g. NXP TJA1080).
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+# Selection of constants as defined in FlexRay specification 3.0.1 Chapter A.1:
+class Const:
+ cChannelIdleDelimiter = 11
+ cCrcInitA = 0xFEDCBA
+ cCrcInitB = 0xABCDEF
+ cCrcPolynomial = 0x5D6DCB
+ cCrcSize = 24
+ cCycleCountMax = 63
+ cdBSS = 2
+ cdCAS = 30
+ cdFES = 2
+ cdFSS = 1
+ cHCrcInit = 0x01A
+ cHCrcPolynomial = 0x385
+ cHCrcSize = 11
+ cSamplesPerBit = 8
+ cSlotIDMax = 2047
+ cStaticSlotIDMax = 1023
+ cVotingSamples = 5
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'flexray'
+ name = 'FlexRay'
+ longname = 'FlexRay'
+ desc = 'Automotive network communications protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Automotive']
+ channels = (
+ {'id': 'channel', 'name': 'Channel', 'desc': 'FlexRay bus channel'},
+ )
+ options = (
+ {'id': 'channel_type', 'desc': 'Channel type', 'default': 'A',
+ 'values': ('A', 'B')},
+ {'id': 'bitrate', 'desc': 'Bitrate (bit/s)', 'default': 10000000,
+ 'values': (10000000, 5000000, 2500000)},
+ )
+ annotations = (
+ ('data', 'FlexRay payload data'),
+ ('tss', 'Transmission start sequence'),
+ ('fss', 'Frame start sequence'),
+ ('reserved-bit', 'Reserved bit'),
+ ('ppi', 'Payload preamble indicator'),
+ ('null-frame', 'Nullframe indicator'),
+ ('sync-frame', 'Full identifier'),
+ ('startup-frame', 'Startup frame indicator'),
+ ('id', 'Frame ID'),
+ ('length', 'Data length'),
+ ('header-crc', 'Header CRC'),
+ ('cycle', 'Cycle code'),
+ ('data-byte', 'Data byte'),
+ ('frame-crc', 'Frame CRC'),
+ ('cid-delimiter', 'Channel idle delimiter'),
+ ('bss', 'Byte start sequence'),
+ ('warnings', 'Human-readable warnings'),
+ ('bit', 'Bit'),
+ ('cid', 'Channel idle delimiter'),
+ ('dts', 'Dynamic trailing sequence'),
+ ('cas', 'Collision avoidance symbol'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (15, 17)),
+ ('fields', 'Fields', tuple(range(15)) + (18, 19, 20)),
+ ('warnings', 'Warnings', (16,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.reset_variables()
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ bitrate = float(self.options['bitrate'])
+ self.samplerate = value
+ self.bit_width = float(self.samplerate) / bitrate
+ self.sample_point = (self.bit_width / 100.0) * self.sample_point_percent
+
+ # Generic helper for FlexRay bit annotations.
+ def putg(self, ss, es, data):
+ left, right = int(self.sample_point), int(self.bit_width - self.sample_point)
+ self.put(ss - left, es + right, self.out_ann, data)
+
+ # Single-FlexRay-bit annotation using the current samplenum.
+ def putx(self, data):
+ self.putg(self.samplenum, self.samplenum, data)
+
+ # Multi-FlexRay-bit annotation from self.ss_block to current samplenum.
+ def putb(self, data):
+ self.putg(self.ss_block, self.samplenum, data)
+
+ # Generic CRC algorithm for any bit size and any data length. Used for
+ # 11-bit header and 24-bit trailer. Not very efficient but at least it
+ # works for now.
+ #
+ # TODO:
+ # - use precalculated tables to increase performance.
+ # - Add support for reverse CRC calculations.
+
+ @staticmethod
+ def crc(data, data_len_bits, polynom, crc_len_bits, iv=0, xor=0):
+ reg = iv ^ xor
+
+ for i in range(data_len_bits - 1, -1, -1):
+ bit = ((reg >> (crc_len_bits - 1)) & 0x1) ^ ((data >> i) & 0x1)
+ reg <<= 1
+ if bit:
+ reg ^= polynom
+
+ mask = (1 << crc_len_bits) - 1
+ crc = reg & mask
+
+ return crc ^ xor
+
+ def reset_variables(self):
+ self.sample_point_percent = 50 # TODO: use vote based sampling
+ self.state = 'IDLE'
+ self.tss_start = self.tss_end = self.frame_type = self.dlc = None
+ self.rawbits = [] # All bits, including byte start sequence bits
+ self.bits = [] # Only actual FlexRay frame bits (no byte start sequence bits)
+ self.curbit = 0 # Current bit of FlexRay frame (bit 0 == FSS)
+ self.last_databit = 999 # Positive value that bitnum+x will never match
+ self.last_xmit_bit = 999 # Positive value that bitnum+x will never match
+ self.ss_block = None
+ self.ss_databytebits = []
+ self.end_of_frame = False
+ self.dynamic_frame = False
+ self.ss_bit0 = None
+ self.ss_bit1 = None
+ self.ss_bit2 = None
+
+ # Poor man's clock synchronization. Use signal edges which change to
+ # dominant state in rather simple ways. This naive approach is neither
+ # aware of the SYNC phase's width nor the specific location of the edge,
+ # but improves the decoder's reliability when the input signal's bitrate
+ # does not exactly match the nominal rate.
+ def dom_edge_seen(self, force=False):
+ self.dom_edge_snum = self.samplenum
+ self.dom_edge_bcount = self.curbit
+
+ # Determine the position of the next desired bit's sample point.
+ def get_sample_point(self, bitnum):
+ samplenum = self.dom_edge_snum
+ samplenum += self.bit_width * (bitnum - self.dom_edge_bcount)
+ samplenum += self.sample_point
+ return int(samplenum)
+
+ def is_bss_sequence(self):
+ # FlexRay uses NRZ encoding and adds a binary 10 sequence before each
+ # byte. After each 8 data bits, a BSS sequence is added but not after
+ # frame CRC.
+
+ if self.end_of_frame:
+ return False
+
+ if (len(self.rawbits) - 2) % 10 == 0:
+ return True
+ elif (len(self.rawbits) - 3) % 10 == 0:
+ return True
+
+ return False
+
+ def handle_bit(self, fr_rx):
+ self.rawbits.append(fr_rx)
+ self.bits.append(fr_rx)
+
+ # Get the index of the current FlexRay frame bit.
+ bitnum = len(self.bits) - 1
+
+ # If this is a byte start sequence remove it from self.bits and ignore it.
+ if self.is_bss_sequence():
+ self.bits.pop()
+
+ if bitnum > 1:
+ self.putx([15, [str(fr_rx)]])
+ else:
+ if len(self.rawbits) == 2:
+ self.ss_bit1 = self.samplenum
+ elif len(self.rawbits) == 3:
+ self.ss_bit2 = self.samplenum
+
+ self.curbit += 1 # Increase self.curbit (bitnum is not affected).
+ return
+ else:
+ if bitnum > 1:
+ self.putx([17, [str(fr_rx)]])
+
+ # Bit 0: Frame start sequence (FSS) bit
+ if bitnum == 0:
+ self.ss_bit0 = self.samplenum
+
+ # Bit 1: Start of header
+ elif bitnum == 1:
+ if self.rawbits[:3] == [1, 1, 0]:
+ self.put(self.tss_start, self.tss_end, self.out_ann,
+ [1, ['Transmission start sequence', 'TSS']])
+
+ self.putg(self.ss_bit0, self.ss_bit0, [17, [str(self.rawbits[:3][0])]])
+ self.putg(self.ss_bit0, self.ss_bit0, [2, ['FSS', 'Frame start sequence']])
+ self.putg(self.ss_bit1, self.ss_bit1, [15, [str(self.rawbits[:3][1])]])
+ self.putg(self.ss_bit2, self.ss_bit2, [15, [str(self.rawbits[:3][2])]])
+ self.putx([17, [str(fr_rx)]])
+ self.putx([3, ['Reserved bit: %d' % fr_rx, 'RB: %d' % fr_rx, 'RB']])
+ else:
+ self.put(self.tss_start, self.tss_end, self.out_ann,
+ [20, ['Collision avoidance symbol', 'CAS']])
+ self.reset_variables()
+
+ # TODO: warning, if sequence is neither [1, 1, 0] nor [1, 1, 1]
+
+ # Bit 2: Payload preamble indicator. Must be 0 if null frame indicator is 0.
+ elif bitnum == 2:
+ self.putx([4, ['Payload preamble indicator: %d' % fr_rx,
+ 'PPI: %d' % fr_rx]])
+
+ # Bit 3: Null frame indicator (inversed)
+ elif bitnum == 3:
+ data_type = 'data frame' if fr_rx else 'null frame'
+ self.putx([5, ['Null frame indicator: %s' % data_type,
+ 'NF: %d' % fr_rx, 'NF']])
+
+ # Bit 4: Sync frame indicator
+ # Must be 1 if startup frame indicator is 1.
+ elif bitnum == 4:
+ self.putx([6, ['Sync frame indicator: %d' % fr_rx,
+ 'Sync: %d' % fr_rx, 'Sync']])
+
+ # Bit 5: Startup frame indicator
+ elif bitnum == 5:
+ self.putx([7, ['Startup frame indicator: %d' % fr_rx,
+ 'Startup: %d' % fr_rx, 'Startup']])
+
+ # Remember start of ID (see below).
+ elif bitnum == 6:
+ self.ss_block = self.samplenum
+
+ # Bits 6-16: Frame identifier (ID[10..0])
+ # ID must NOT be 0.
+ elif bitnum == 16:
+ self.id = int(''.join(str(d) for d in self.bits[6:]), 2)
+ self.putb([8, ['Frame ID: %d' % self.id, 'ID: %d' % self.id,
+ '%d' % self.id]])
+
+ # Remember start of payload length (see below).
+ elif bitnum == 17:
+ self.ss_block = self.samplenum
+
+ # Bits 17-23: Payload length (Length[7..0])
+ # Payload length in header is the half of the real payload size.
+ elif bitnum == 23:
+ self.payload_length = int(''.join(str(d) for d in self.bits[17:]), 2)
+ self.putb([9, ['Payload length: %d' % self.payload_length,
+ 'Length: %d' % self.payload_length,
+ '%d' % self.payload_length]])
+
+ # Remember start of header CRC (see below).
+ elif bitnum == 24:
+ self.ss_block = self.samplenum
+
+ # Bits 24-34: Header CRC (11-bit) (HCRC[11..0])
+ # Calculation of header CRC is equal on both channels.
+ elif bitnum == 34:
+ bits = ''.join([str(b) for b in self.bits[4:24]])
+ header_to_check = int(bits, 2)
+ expected_crc = self.crc(header_to_check, len(bits),
+ Const.cHCrcPolynomial, Const.cHCrcSize, Const.cHCrcInit)
+ self.header_crc = int(''.join(str(d) for d in self.bits[24:]), 2)
+
+ crc_ok = self.header_crc == expected_crc
+ crc_ann = "OK" if crc_ok else "bad"
+
+ self.putb([10, ['Header CRC: 0x%X (%s)' % (self.header_crc, crc_ann),
+ '0x%X (%s)' % (self.header_crc, crc_ann),
+ '0x%X' % self.header_crc]])
+
+ # Remember start of cycle code (see below).
+ elif bitnum == 35:
+ self.ss_block = self.samplenum
+
+ # Bits 35-40: Cycle code (Cyc[6..0])
+ # Cycle code. Must be between 0 and 63.
+ elif bitnum == 40:
+ self.cycle = int(''.join(str(d) for d in self.bits[35:]), 2)
+ self.putb([11, ['Cycle: %d' % self.cycle, 'Cyc: %d' % self.cycle,
+ '%d' % self.cycle]])
+ self.last_databit = 41 + 2 * self.payload_length * 8
+
+ # Remember all databyte bits, except the very last one.
+ elif bitnum in range(41, self.last_databit):
+ self.ss_databytebits.append(self.samplenum)
+
+ # Bits 41-X: Data field (0-254 bytes, depending on length)
+ # The bits within a data byte are transferred MSB-first.
+ elif bitnum == self.last_databit:
+ self.ss_databytebits.append(self.samplenum) # Last databyte bit.
+ for i in range(2 * self.payload_length):
+ x = 40 + (8 * i) + 1
+ b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+ ss = self.ss_databytebits[i * 8]
+ es = self.ss_databytebits[((i + 1) * 8) - 1]
+ self.putg(ss, es, [12, ['Data byte %d: 0x%02x' % (i, b),
+ 'DB%d: 0x%02x' % (i, b), '%02X' % b]])
+ self.ss_databytebits = []
+ self.ss_block = self.samplenum # Remember start of trailer CRC.
+
+ # Trailer CRC (24-bit) (CRC[11..0])
+ # Initialization vector of channel A and B are different, so CRCs are
+ # different for same data.
+ elif bitnum == self.last_databit + 23:
+ bits = ''.join([str(b) for b in self.bits[1:-24]])
+ frame_to_check = int(bits, 2)
+ iv = Const.cCrcInitA if self.options['channel_type'] == 'A' else Const.cCrcInitB
+ expected_crc = self.crc(frame_to_check, len(bits),
+ Const.cCrcPolynomial, Const.cCrcSize, iv=iv)
+ self.frame_crc = int(''.join(str(d) for d in self.bits[self.last_databit:]), 2)
+
+ crc_ok = self.frame_crc == expected_crc
+ crc_ann = "OK" if crc_ok else "bad"
+
+ self.putb([13, ['Frame CRC: 0x%X (%s)' % (self.frame_crc, crc_ann),
+ '0x%X (%s)' % (self.frame_crc, crc_ann),
+ '0x%X' % self.frame_crc]])
+ self.end_of_frame = True
+
+ # Remember start of frame end sequence (see below).
+ elif bitnum == self.last_databit + 24:
+ self.ss_block = self.samplenum
+
+ # Frame end sequence, must be 1 followed by 0.
+ elif bitnum == self.last_databit + 25:
+ self.putb([14, ['Frame end sequence', 'FES']])
+
+ # Check for DTS
+ elif bitnum == self.last_databit + 26:
+ if not fr_rx:
+ self.dynamic_frame = True
+ else:
+ self.last_xmit_bit = bitnum
+ self.ss_block = self.samplenum
+
+ # Remember start of channel idle delimiter (see below).
+ elif bitnum == self.last_xmit_bit:
+ self.ss_block = self.samplenum
+
+ # Channel idle limiter (CID[11..0])
+ elif bitnum == self.last_xmit_bit + Const.cChannelIdleDelimiter - 1:
+ self.putb([18, ['Channel idle delimiter', 'CID']])
+ self.reset_variables()
+
+ # DTS if dynamic frame
+ elif bitnum > self.last_databit + 27:
+ if self.dynamic_frame:
+ if fr_rx:
+ if self.last_xmit_bit == 999:
+ self.putb([19, ['Dynamic trailing sequence', 'DTS']])
+ self.last_xmit_bit = bitnum + 1
+ self.ss_block = self.samplenum
+
+ self.curbit += 1
+
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+
+ while True:
+ # State machine.
+ if self.state == 'IDLE':
+ # Wait for a dominant state (logic 0) on the bus.
+ (fr_rx,) = self.wait({0: 'l'})
+ self.tss_start = self.samplenum
+ (fr_rx,) = self.wait({0: 'h'})
+ self.tss_end = self.samplenum
+ self.dom_edge_seen(force = True)
+ self.state = 'GET BITS'
+ elif self.state == 'GET BITS':
+ # Wait until we're in the correct bit/sampling position.
+ pos = self.get_sample_point(self.curbit)
+ (fr_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
+ if self.matched[1]:
+ self.dom_edge_seen()
+ if self.matched[0]:
+ self.handle_bit(fr_rx)
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)
'''
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
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
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'},
)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Dave Craig <dcraig@brightsign.biz>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes HDCP messages.
+
+Details:
+https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Dave Craig <dcraig@brightsign.biz>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+msg_ids = {
+ 2: 'AKE_Init',
+ 3: 'AKE_Send_Cert',
+ 4: 'AKE_No_stored_km',
+ 5: 'AKE_Stored_km',
+
+ 7: 'AKE_Send_H_prime',
+ 8: 'AKE_Send_Pairing_Info',
+
+ 9: 'LC_Init',
+ 10: 'LC_Send_L_prime',
+
+ 11: 'SKE_Send_Eks',
+ 12: 'RepeaterAuth_Send_ReceiverID_List',
+
+ 15: 'RepeaterAuth_Send_Ack',
+ 16: 'RepeaterAuth_Stream_Manage',
+ 17: 'RepeaterAuth_Stream_Ready',
+}
+
+write_items = {
+ 0x00: '1.4 Bksv - Receiver KSV',
+ 0x08: '1.4 Ri\' - Link Verification',
+ 0x0a: '1.4 Pj\' - Enhanced Link Verification',
+ 0x10: '1.4 Aksv - Transmitter KSV',
+ 0x15: '1.4 Ainfo - Transmitter KSV',
+ 0x18: '1.4 An - Session random number',
+ 0x20: '1.4 V\'H0',
+ 0x24: '1.4 V\'H1',
+ 0x28: '1.4 V\'H2',
+ 0x2c: '1.4 V\'H3',
+ 0x30: '1.4 V\'H4',
+ 0x40: '1.4 Bcaps',
+ 0x41: '1.4 Bstatus',
+ 0x43: '1.4 KSV FIFO',
+ 0x50: 'HDCP2Version',
+ 0x60: 'Write_Message',
+ 0x70: 'RxStatus',
+ 0x80: 'Read_Message',
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'hdcp'
+ name = 'HDCP'
+ longname = 'HDCP over HDMI'
+ desc = 'HDCP protocol over HDMI.'
+ license = 'gplv2+'
+ inputs = ['i2c']
+ outputs = ['hdcp']
+ tags = ['PC', 'Security/crypto']
+ annotations = \
+ tuple(('message-0x%02X' % i, 'Message 0x%02X' % i) for i in range(18)) + (
+ ('summary', 'Summary'),
+ ('warnings', 'Warnings'),
+ )
+ annotation_rows = (
+ ('messages', 'Messages', tuple(range(18))),
+ ('summary', 'Summary', (18,)),
+ ('warnings', 'Warnings', (19,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.state = 'IDLE'
+ self.stack = []
+ self.msg = -1
+ self.ss = self.es = self.ss_block = self.es_block = 0
+ self.init_seq = []
+ self.valid = 0
+ self.type = ''
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putb(self, data):
+ self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+ def decode(self, ss, es, data):
+ cmd, databyte = data
+
+ # Collect the 'BITS' packet, then return. The next packet is
+ # guaranteed to belong to these bits we just stored.
+ if cmd == 'BITS':
+ self.bits = databyte
+ return
+
+ self.ss, self.es = ss, es
+
+ # State machine.
+ if self.state == 'IDLE':
+ # Wait for an I2C START condition.
+ if cmd == 'START':
+ self.reset()
+ self.ss_block = ss
+ elif cmd != 'START REPEAT':
+ return
+ self.state = 'GET SLAVE ADDR'
+ elif self.state == 'GET SLAVE ADDR':
+ if cmd == 'ADDRESS READ':
+ self.state = 'BUFFER DATA'
+ if databyte != 0x3a:
+ self.state = 'IDLE'
+ elif cmd == 'ADDRESS WRITE':
+ self.state = 'WRITE OFFSET'
+ if databyte != 0x3a:
+ self.state = 'IDLE'
+ elif self.state == 'WRITE OFFSET':
+ if cmd == 'DATA WRITE':
+ if databyte in write_items:
+ self.type = write_items[databyte]
+ if databyte in (0x10, 0x15, 0x18, 0x60):
+ self.state = 'BUFFER DATA'
+ # If we are reading, then jump back to IDLE for a start repeat.
+ # If we are writing, then just continue onwards.
+ if self.state == 'BUFFER DATA':
+ pass
+ elif self.type != '':
+ self.state = 'IDLE'
+ elif self.state == 'BUFFER DATA':
+ if cmd in ('STOP', 'NACK'):
+ self.es_block = es
+ self.state = 'IDLE'
+ if self.type == '':
+ return
+ if not self.stack:
+ self.putb([18, ['%s' % (self.type)]])
+ return
+ if self.type == 'RxStatus':
+ rxstatus = (self.stack.pop() << 8) | self.stack.pop()
+ reauth_req = (rxstatus & 0x800) != 0
+ ready = (rxstatus & 0x400) != 0
+ length = rxstatus & 0x3ff
+ text = '%s, reauth %s, ready %s, length %s' % \
+ (self.type, reauth_req, ready, length)
+ self.putb([18, [text]])
+ elif self.type == '1.4 Bstatus':
+ bstatus = (self.stack.pop() << 8) | self.stack.pop()
+ device_count = bstatus & 0x7f
+ max_devs_exceeded = (bstatus & 0x80) != 0
+ depth = ((bstatus & 0x700) >> 8)
+ max_cascase_exceeded = bstatus & 0x800
+ hdmi_mode = (bstatus & 0x1000) != 0
+ text = '%s, %s devices, depth %s, hdmi mode %s' % \
+ (self.type, device_count, depth, hdmi_mode)
+ self.putb([18, [text]])
+ elif self.type == 'Read_Message':
+ msg = self.stack.pop(0)
+ self.putb([msg, ['%s, %s' % (self.type,
+ msg_ids.get(msg, 'Invalid'))]])
+ elif self.type == 'Write_Message':
+ msg = self.stack.pop(0)
+ self.putb([msg, ['%s, %s' % (self.type,
+ msg_ids.get(msg, 'Invalid'))]])
+ elif self.type == 'HDCP2Version':
+ version = self.stack.pop(0)
+ if (version & 0x4):
+ self.putb([18, ['HDCP2']])
+ else:
+ self.putb([18, ['NOT HDCP2']])
+ else:
+ self.putb([18, ['%s' % (self.type)]])
+ elif cmd == 'DATA READ':
+ # Stack up our data bytes.
+ self.stack.append(databyte)
+ elif cmd == 'DATA WRITE':
+ # Stack up our data bytes.
+ self.stack.append(databyte)
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'},
license = 'gplv2+'
inputs = ['i2c']
outputs = [] # TODO: Only known at run-time.
+ tags = ['Util']
def __init__(self):
self.reset()
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')}
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'},
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),
'word' % (self.bitcount, self.wordlength)]])
self.wordlength = self.bitcount
+ else:
+ sck = self.wait({0: 'f'})
# Reset decoder state.
self.data = 0
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This protocol decoder can decode the GPIB (IEEE-488) protocol. Both variants
+of (up to) 16 parallel lines as well as serial communication (IEC bus) are
+supported in this implementation.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
+## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
+## Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# This file was created from earlier implementations of the 'gpib' and
+# the 'iec' protocol decoders. It combines the parallel and the serial
+# transmission variants in a single instance with optional inputs for
+# maximum code re-use.
+
+# TODO
+# - Extend annotations for improved usability.
+# - Keep talkers' data streams on separate annotation rows? Is this useful
+# here at the GPIB level, or shall stacked decoders dispatch these? May
+# depend on how often captures get inspected which involve multiple peers.
+# - Make serial bit annotations optional? Could slow down interactive
+# exploration for long captures (see USB).
+# - Move the inlined Commodore IEC peripherals support to a stacked decoder
+# when more peripherals get added.
+# - SCPI over GPIB may "represent somewhat naturally" here already when
+# text lines are a single run of data at the GPIB layer (each line between
+# the address spec and either EOI or ATN). So a stacked SCPI decoder may
+# only become necessary when the text lines' content shall get inspected.
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+
+'''
+OUTPUT_PYTHON format for stacked decoders:
+
+General packet format:
+[<ptype>, <addr>, <pdata>]
+
+This is the list of <ptype>s and their respective <pdata> values:
+
+Raw bits and bytes at the physical transport level:
+ - 'IEC_BIT': <addr> is not applicable, <pdata> is the transport's bit value.
+ - 'GPIB_RAW': <addr> is not applicable, <pdata> is the transport's
+ byte value. Data bytes are in the 0x00-0xff range, command/address
+ bytes are in the 0x100-0x1ff range.
+
+GPIB level byte fields (commands, addresses, pieces of data):
+ - 'COMMAND': <addr> is not applicable, <pdata> is the command's byte value.
+ - 'LISTEN': <addr> is the listener address (0-30), <pdata> is the raw
+ byte value (including the 0x20 offset).
+ - 'TALK': <addr> is the talker address (0-30), <pdata> is the raw byte
+ value (including the 0x40 offset).
+ - 'SECONDARY': <addr> is the secondary address (0-31), <pdata> is the
+ raw byte value (including the 0x60 offset).
+ - 'MSB_SET': <addr> as well as <pdata> are the raw byte value (including
+ the 0x80 offset). This usually does not happen for GPIB bytes with ATN
+ active, but was observed with the IEC bus and Commodore floppy drives,
+ when addressing channels within the device.
+ - 'DATA_BYTE': <addr> is the talker address (when available), <pdata>
+ is the raw data byte (transport layer, ATN inactive).
+
+Extracted payload information (peers and their communicated data):
+ - 'TALK_LISTEN': <addr> is the current talker, <pdata> is the list of
+ current listeners. These updates for the current "connected peers"
+ are sent when the set of peers changes, i.e. after talkers/listeners
+ got selected or deselected. Of course the data only covers what could
+ be gathered from the input data. Some controllers may not explicitly
+ address themselves, or captures may not include an early setup phase.
+ - 'TALKER_BYTES': <addr> is the talker address (when available), <pdata>
+ is the accumulated byte sequence between addressing a talker and EOI,
+ or the next command/address.
+ - 'TALKER_TEXT': <addr> is the talker address (when available), <pdata>
+ is the accumulated text sequence between addressing a talker and EOI,
+ or the next command/address.
+'''
+
+class ChannelError(Exception):
+ pass
+
+def _format_ann_texts(fmts, **args):
+ if not fmts:
+ return None
+ return [fmt.format(**args) for fmt in fmts]
+
+_cmd_table = {
+ # Command codes in the 0x00-0x1f range.
+ 0x01: ['Go To Local', 'GTL'],
+ 0x04: ['Selected Device Clear', 'SDC'],
+ 0x05: ['Parallel Poll Configure', 'PPC'],
+ 0x08: ['Global Execute Trigger', 'GET'],
+ 0x09: ['Take Control', 'TCT'],
+ 0x11: ['Local Lock Out', 'LLO'],
+ 0x14: ['Device Clear', 'DCL'],
+ 0x15: ['Parallel Poll Unconfigure', 'PPU'],
+ 0x18: ['Serial Poll Enable', 'SPE'],
+ 0x19: ['Serial Poll Disable', 'SPD'],
+ # Unknown type of command.
+ None: ['Unknown command 0x{cmd:02x}', 'command 0x{cmd:02x}', 'cmd {cmd:02x}', 'C{cmd_ord:c}'],
+ # Special listener/talker "addresses" (deselecting previous peers).
+ 0x3f: ['Unlisten', 'UNL'],
+ 0x5f: ['Untalk', 'UNT'],
+}
+
+def _is_command(b):
+ # Returns a tuple of booleans (or None when not applicable) whether
+ # the raw GPIB byte is: a command, an un-listen, an un-talk command.
+ if b in range(0x00, 0x20):
+ return True, None, None
+ if b in range(0x20, 0x40) and (b & 0x1f) == 31:
+ return True, True, False
+ if b in range(0x40, 0x60) and (b & 0x1f) == 31:
+ return True, False, True
+ return False, None, None
+
+def _is_listen_addr(b):
+ if b in range(0x20, 0x40):
+ return b & 0x1f
+ return None
+
+def _is_talk_addr(b):
+ if b in range(0x40, 0x60):
+ return b & 0x1f
+ return None
+
+def _is_secondary_addr(b):
+ if b in range(0x60, 0x80):
+ return b & 0x1f
+ return None
+
+def _is_msb_set(b):
+ if b & 0x80:
+ return b
+ return None
+
+def _get_raw_byte(b, atn):
+ # "Decorate" raw byte values for stacked decoders.
+ return b | 0x100 if atn else b
+
+def _get_raw_text(b, atn):
+ return ['{leader}{data:02x}'.format(leader = '/' if atn else '', data = b)]
+
+def _get_command_texts(b):
+ fmts = _cmd_table.get(b, None)
+ known = fmts is not None
+ if not fmts:
+ fmts = _cmd_table.get(None, None)
+ if not fmts:
+ return known, None
+ return known, _format_ann_texts(fmts, cmd = b, cmd_ord = ord('0') + b)
+
+def _get_address_texts(b):
+ laddr = _is_listen_addr(b)
+ taddr = _is_talk_addr(b)
+ saddr = _is_secondary_addr(b)
+ msb = _is_msb_set(b)
+ fmts = None
+ if laddr is not None:
+ fmts = ['Listen {addr:d}', 'L {addr:d}', 'L{addr_ord:c}']
+ addr = laddr
+ elif taddr is not None:
+ fmts = ['Talk {addr:d}', 'T {addr:d}', 'T{addr_ord:c}']
+ addr = taddr
+ elif saddr is not None:
+ fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
+ addr = saddr
+ elif msb is not None: # For IEC bus compat.
+ fmts = ['Secondary {addr:d}', 'S {addr:d}', 'S{addr_ord:c}']
+ addr = msb
+ return _format_ann_texts(fmts, addr = addr, addr_ord = ord('0') + addr)
+
+def _get_data_text(b):
+ # TODO Move the table of ASCII control characters to a common location?
+ # TODO Move the "printable with escapes" logic to a common helper?
+ _control_codes = {
+ 0x00: 'NUL',
+ 0x01: 'SOH',
+ 0x02: 'STX',
+ 0x03: 'ETX',
+ 0x04: 'EOT',
+ 0x05: 'ENQ',
+ 0x06: 'ACK',
+ 0x07: 'BEL',
+ 0x08: 'BS',
+ 0x09: 'TAB',
+ 0x0a: 'LF',
+ 0x0b: 'VT',
+ 0x0c: 'FF',
+ 0x0d: 'CR',
+ 0x0e: 'SO',
+ 0x0f: 'SI',
+ 0x10: 'DLE',
+ 0x11: 'DC1',
+ 0x12: 'DC2',
+ 0x13: 'DC3',
+ 0x14: 'DC4',
+ 0x15: 'NAK',
+ 0x16: 'SYN',
+ 0x17: 'ETB',
+ 0x18: 'CAN',
+ 0x19: 'EM',
+ 0x1a: 'SUB',
+ 0x1b: 'ESC',
+ 0x1c: 'FS',
+ 0x1d: 'GS',
+ 0x1e: 'RS',
+ 0x1f: 'US',
+ }
+ # Yes, exclude 0x7f (DEL) here. It's considered non-printable.
+ if b in range(0x20, 0x7f) and b not in ('[', ']'):
+ return '{:s}'.format(chr(b))
+ elif b in _control_codes:
+ return '[{:s}]'.format(_control_codes[b])
+ # Use a compact yet readable and unambigous presentation for bytes
+ # which contain non-printables. The format that is used here is
+ # compatible with 93xx EEPROM and UART decoders.
+ return '[{:02x}]'.format(b)
+
+(
+ PIN_DIO1, PIN_DIO2, PIN_DIO3, PIN_DIO4,
+ PIN_DIO5, PIN_DIO6, PIN_DIO7, PIN_DIO8,
+ PIN_EOI, PIN_DAV, PIN_NRFD, PIN_NDAC,
+ PIN_IFC, PIN_SRQ, PIN_ATN, PIN_REN,
+ PIN_CLK,
+) = range(17)
+PIN_DATA = PIN_DIO1
+
+(
+ ANN_RAW_BIT, ANN_RAW_BYTE,
+ ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,
+ ANN_EOI,
+ ANN_TEXT,
+ # TODO Want to provide one annotation class per talker address (0-30)?
+ ANN_IEC_PERIPH,
+ ANN_WARN,
+) = range(11)
+
+(
+ BIN_RAW,
+ BIN_DATA,
+ # TODO Want to provide one binary annotation class per talker address (0-30)?
+) = range(2)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ieee488'
+ name = 'IEEE-488'
+ longname = 'IEEE-488 GPIB/HPIB/IEC'
+ desc = 'IEEE-488 General Purpose Interface Bus (GPIB/HPIB or IEC).'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['ieee488']
+ tags = ['PC', 'Retro computing']
+ channels = (
+ {'id': 'dio1' , 'name': 'DIO1/DATA',
+ 'desc': 'Data I/O bit 1, or serial data'},
+ )
+ optional_channels = (
+ {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'},
+ {'id': 'dio3' , 'name': 'DIO3', 'desc': 'Data I/O bit 3'},
+ {'id': 'dio4' , 'name': 'DIO4', 'desc': 'Data I/O bit 4'},
+ {'id': 'dio5' , 'name': 'DIO5', 'desc': 'Data I/O bit 5'},
+ {'id': 'dio6' , 'name': 'DIO6', 'desc': 'Data I/O bit 6'},
+ {'id': 'dio7' , 'name': 'DIO7', 'desc': 'Data I/O bit 7'},
+ {'id': 'dio8' , 'name': 'DIO8', 'desc': 'Data I/O bit 8'},
+ {'id': 'eoi', 'name': 'EOI', 'desc': 'End or identify'},
+ {'id': 'dav', 'name': 'DAV', 'desc': 'Data valid'},
+ {'id': 'nrfd', 'name': 'NRFD', 'desc': 'Not ready for data'},
+ {'id': 'ndac', 'name': 'NDAC', 'desc': 'Not data accepted'},
+ {'id': 'ifc', 'name': 'IFC', 'desc': 'Interface clear'},
+ {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
+ {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
+ {'id': 'ren', 'name': 'REN', 'desc': 'Remote enable'},
+ {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock'},
+ )
+ options = (
+ {'id': 'iec_periph', 'desc': 'Decode Commodore IEC bus peripherals details',
+ 'default': 'no', 'values': ('no', 'yes')},
+ {'id': 'delim', 'desc': 'Payload data delimiter',
+ 'default': 'eol', 'values': ('none', 'eol')},
+ )
+ annotations = (
+ ('bit', 'IEC bit'),
+ ('raw', 'Raw byte'),
+ ('cmd', 'Command'),
+ ('laddr', 'Listener address'),
+ ('taddr', 'Talker address'),
+ ('saddr', 'Secondary address'),
+ ('data', 'Data byte'),
+ ('eoi', 'EOI'),
+ ('text', 'Talker text'),
+ ('periph', 'IEC bus peripherals'),
+ ('warn', 'Warning'),
+ )
+ annotation_rows = (
+ ('bits', 'IEC bits', (ANN_RAW_BIT,)),
+ ('raws', 'Raw bytes', (ANN_RAW_BYTE,)),
+ ('gpib', 'Commands/data', (ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,)),
+ ('eois', 'EOI', (ANN_EOI,)),
+ ('texts', 'Talker texts', (ANN_TEXT,)),
+ ('periphs', 'IEC peripherals', (ANN_IEC_PERIPH,)),
+ ('warns', 'Warnings', (ANN_WARN,)),
+ )
+ binary = (
+ ('raw', 'Raw bytes'),
+ ('data', 'Talker bytes'),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.curr_raw = None
+ self.curr_atn = None
+ self.curr_eoi = None
+ self.latch_atn = None
+ self.latch_eoi = None
+ self.accu_bytes = []
+ self.accu_text = []
+ self.ss_raw = None
+ self.es_raw = None
+ self.ss_eoi = None
+ self.es_eoi = None
+ self.ss_text = None
+ self.es_text = None
+ self.last_talker = None
+ self.last_listener = []
+ self.last_iec_addr = None
+ self.last_iec_sec = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_bin = self.register(srd.OUTPUT_BINARY)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+ def putg(self, ss, es, data):
+ self.put(ss, es, self.out_ann, data)
+
+ def putbin(self, ss, es, data):
+ self.put(ss, es, self.out_bin, data)
+
+ def putpy(self, ss, es, ptype, addr, pdata):
+ self.put(ss, es, self.out_python, [ptype, addr, pdata])
+
+ def emit_eoi_ann(self, ss, es):
+ self.putg(ss, es, [ANN_EOI, ['EOI']])
+
+ def emit_bin_ann(self, ss, es, ann_cls, data):
+ self.putbin(ss, es, [ann_cls, bytes(data)])
+
+ def emit_data_ann(self, ss, es, ann_cls, data):
+ self.putg(ss, es, [ann_cls, data])
+
+ def emit_warn_ann(self, ss, es, data):
+ self.putg(ss, es, [ANN_WARN, data])
+
+ def flush_bytes_text_accu(self):
+ if self.accu_bytes and self.ss_text is not None and self.es_text is not None:
+ self.emit_bin_ann(self.ss_text, self.es_text, BIN_DATA, bytearray(self.accu_bytes))
+ self.putpy(self.ss_text, self.es_text, 'TALKER_BYTES', self.last_talker, bytearray(self.accu_bytes))
+ self.accu_bytes = []
+ if self.accu_text and self.ss_text is not None and self.es_text is not None:
+ text = ''.join(self.accu_text)
+ self.emit_data_ann(self.ss_text, self.es_text, ANN_TEXT, [text])
+ self.putpy(self.ss_text, self.es_text, 'TALKER_TEXT', self.last_talker, text)
+ self.accu_text = []
+ self.ss_text = self.es_text = None
+
+ def check_extra_flush(self, b):
+ # Optionally flush previously accumulated runs of payload data
+ # according to user specified conditions.
+ if self.options['delim'] == 'none':
+ return
+ if not self.accu_bytes:
+ return
+
+ # This implementation exlusively handles "text lines", but adding
+ # support for more variants here is straight forward.
+ #
+ # Search for the first data byte _after_ a user specified text
+ # line termination sequence was seen. The termination sequence's
+ # alphabet may be variable, and the sequence may span multiple
+ # data bytes. We accept either CR or LF, and combine the CR+LF
+ # sequence to strive for maximum length annotations for improved
+ # readability at different zoom levels. It's acceptable that this
+ # implementation would also combine multiple line terminations
+ # like LF+LF.
+ term_chars = (10, 13)
+ is_eol = b in term_chars
+ had_eol = self.accu_bytes[-1] in term_chars
+ if had_eol and not is_eol:
+ self.flush_bytes_text_accu()
+
+ def handle_ifc_change(self, ifc):
+ # Track IFC line for parallel input.
+ # Assertion of IFC de-selects all talkers and listeners.
+ if ifc:
+ self.last_talker = None
+ self.last_listener = []
+ self.flush_bytes_text_accu()
+
+ def handle_eoi_change(self, eoi):
+ # Track EOI line for parallel and serial input.
+ if eoi:
+ self.ss_eoi = self.samplenum
+ self.curr_eoi = eoi
+ else:
+ self.es_eoi = self.samplenum
+ if self.ss_eoi and self.latch_eoi:
+ self.emit_eoi_ann(self.ss_eoi, self.es_eoi)
+ self.es_text = self.es_eoi
+ self.flush_bytes_text_accu()
+ self.ss_eoi = self.es_eoi = None
+ self.curr_eoi = None
+
+ def handle_atn_change(self, atn):
+ # Track ATN line for parallel and serial input.
+ self.curr_atn = atn
+ if atn:
+ self.flush_bytes_text_accu()
+
+ def handle_iec_periph(self, ss, es, addr, sec, data):
+ # The annotation is optional.
+ if self.options['iec_periph'] != 'yes':
+ return
+ # Void internal state.
+ if addr is None and sec is None and data is None:
+ self.last_iec_addr = None
+ self.last_iec_sec = None
+ return
+ # Grab and evaluate new input.
+ _iec_addr_names = {
+ # TODO Add more items here. See the "Device numbering" section
+ # of the https://en.wikipedia.org/wiki/Commodore_bus page.
+ 8: 'Disk 0',
+ 9: 'Disk 1',
+ }
+ _iec_disk_range = range(8, 16)
+ if addr is not None:
+ self.last_iec_addr = addr
+ name = _iec_addr_names.get(addr, None)
+ if name:
+ self.emit_data_ann(ss, es, ANN_IEC_PERIPH, [name])
+ addr = self.last_iec_addr # Simplify subsequent logic.
+ if sec is not None:
+ # BEWARE! The secondary address is a full byte and includes
+ # the 0x60 offset, to also work when the MSB was set.
+ self.last_iec_sec = sec
+ subcmd, channel = sec & 0xf0, sec & 0x0f
+ channel_ord = ord('0') + channel
+ if addr is not None and addr in _iec_disk_range:
+ subcmd_fmts = {
+ 0x60: ['Reopen {ch:d}', 'Re {ch:d}', 'R{ch_ord:c}'],
+ 0xe0: ['Close {ch:d}', 'Cl {ch:d}', 'C{ch_ord:c}'],
+ 0xf0: ['Open {ch:d}', 'Op {ch:d}', 'O{ch_ord:c}'],
+ }.get(subcmd, None)
+ if subcmd_fmts:
+ texts = _format_ann_texts(subcmd_fmts, ch = channel, ch_ord = channel_ord)
+ self.emit_data_ann(ss, es, ANN_IEC_PERIPH, texts)
+ sec = self.last_iec_sec # Simplify subsequent logic.
+ if data is not None:
+ if addr is None or sec is None:
+ return
+ # TODO Process data depending on peripheral type and channel?
+
+ def handle_data_byte(self):
+ if not self.curr_atn:
+ self.check_extra_flush(self.curr_raw)
+ b = self.curr_raw
+ texts = _get_raw_text(b, self.curr_atn)
+ self.emit_data_ann(self.ss_raw, self.es_raw, ANN_RAW_BYTE, texts)
+ self.emit_bin_ann(self.ss_raw, self.es_raw, BIN_RAW, b.to_bytes(1, byteorder='big'))
+ self.putpy(self.ss_raw, self.es_raw, 'GPIB_RAW', None, _get_raw_byte(b, self.curr_atn))
+ if self.curr_atn:
+ ann_cls = None
+ upd_iec = False,
+ py_type = None
+ py_peers = False
+ is_cmd, is_unl, is_unt = _is_command(b)
+ laddr = _is_listen_addr(b)
+ taddr = _is_talk_addr(b)
+ saddr = _is_secondary_addr(b)
+ msb = _is_msb_set(b)
+ if is_cmd:
+ known, texts = _get_command_texts(b)
+ if not known:
+ warn_texts = ['Unknown GPIB command', 'unknown', 'UNK']
+ self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
+ ann_cls = ANN_CMD
+ py_type, py_addr = 'COMMAND', None
+ if is_unl:
+ self.last_listener = []
+ py_peers = True
+ if is_unt:
+ self.last_talker = None
+ py_peers = True
+ if is_unl or is_unt:
+ upd_iec = True, None, None, None
+ elif laddr is not None:
+ addr = laddr
+ texts = _get_address_texts(b)
+ ann_cls = ANN_LADDR
+ py_type, py_addr = 'LISTEN', addr
+ if addr == self.last_talker:
+ self.last_talker = None
+ self.last_listener.append(addr)
+ upd_iec = True, addr, None, None
+ py_peers = True
+ elif taddr is not None:
+ addr = taddr
+ texts = _get_address_texts(b)
+ ann_cls = ANN_TADDR
+ py_type, py_addr = 'TALK', addr
+ if addr in self.last_listener:
+ self.last_listener.remove(addr)
+ self.last_talker = addr
+ upd_iec = True, addr, None, None
+ py_peers = True
+ elif saddr is not None:
+ addr = saddr
+ texts = _get_address_texts(b)
+ ann_cls = ANN_SADDR
+ upd_iec = True, None, b, None
+ py_type, py_addr = 'SECONDARY', addr
+ elif msb is not None:
+ # These are not really "secondary addresses", but they
+ # are used by the Commodore IEC bus (floppy channels).
+ texts = _get_address_texts(b)
+ ann_cls = ANN_SADDR
+ upd_iec = True, None, b, None
+ py_type, py_addr = 'MSB_SET', b
+ if ann_cls is not None and texts is not None:
+ self.emit_data_ann(self.ss_raw, self.es_raw, ann_cls, texts)
+ if upd_iec[0]:
+ self.handle_iec_periph(self.ss_raw, self.es_raw, upd_iec[1], upd_iec[2], upd_iec[3])
+ if py_type:
+ self.putpy(self.ss_raw, self.es_raw, py_type, py_addr, b)
+ if py_peers:
+ self.last_listener.sort()
+ self.putpy(self.ss_raw, self.es_raw, 'TALK_LISTEN', self.last_talker, self.last_listener)
+ else:
+ self.accu_bytes.append(b)
+ text = _get_data_text(b)
+ if not self.accu_text:
+ self.ss_text = self.ss_raw
+ self.accu_text.append(text)
+ self.es_text = self.es_raw
+ self.emit_data_ann(self.ss_raw, self.es_raw, ANN_DATA, [text])
+ self.handle_iec_periph(self.ss_raw, self.es_raw, None, None, b)
+ self.putpy(self.ss_raw, self.es_raw, 'DATA_BYTE', self.last_talker, b)
+
+ def handle_dav_change(self, dav, data):
+ if dav:
+ # Data availability starts when the flag goes active.
+ self.ss_raw = self.samplenum
+ self.curr_raw = bitpack(data)
+ self.latch_atn = self.curr_atn
+ self.latch_eoi = self.curr_eoi
+ return
+ # Data availability ends when the flag goes inactive. Handle the
+ # previously captured data byte according to associated flags.
+ self.es_raw = self.samplenum
+ self.handle_data_byte()
+ self.ss_raw = self.es_raw = None
+ self.curr_raw = None
+
+ def inject_dav_phase(self, ss, es, data):
+ # Inspection of serial input has resulted in one raw byte which
+ # spans a given period of time. Pretend we had seen a DAV active
+ # phase, to re-use code for the parallel transmission.
+ self.ss_raw = ss
+ self.curr_raw = bitpack(data)
+ self.latch_atn = self.curr_atn
+ self.latch_eoi = self.curr_eoi
+ self.es_raw = es
+ self.handle_data_byte()
+ self.ss_raw = self.es_raw = None
+ self.curr_raw = None
+
+ def invert_pins(self, pins):
+ # All lines (including data bits!) are low active and thus need
+ # to get inverted to receive their logical state (high active,
+ # regular data bit values). Cope with inputs being optional.
+ return [1 - p if p in (0, 1) else p for p in pins]
+
+ def decode_serial(self, has_clk, has_data_1, has_atn, has_srq):
+ if not has_clk or not has_data_1 or not has_atn:
+ raise ChannelError('IEC bus needs at least ATN and serial CLK and DATA.')
+
+ # This is a rephrased version of decoders/iec/pd.py:decode().
+ # SRQ was not used there either. Magic numbers were eliminated.
+ (
+ STEP_WAIT_READY_TO_SEND,
+ STEP_WAIT_READY_FOR_DATA,
+ STEP_PREP_DATA_TEST_EOI,
+ STEP_CLOCK_DATA_BITS,
+ ) = range(4)
+ step_wait_conds = (
+ [{PIN_ATN: 'f'}, {PIN_DATA: 'l', PIN_CLK: 'h'}],
+ [{PIN_ATN: 'f'}, {PIN_DATA: 'h', PIN_CLK: 'h'}, {PIN_CLK: 'l'}],
+ [{PIN_ATN: 'f'}, {PIN_DATA: 'f'}, {PIN_CLK: 'l'}],
+ [{PIN_ATN: 'f'}, {PIN_CLK: 'e'}],
+ )
+ step = STEP_WAIT_READY_TO_SEND
+ bits = []
+
+ while True:
+
+ # Sample input pin values. Keep DATA/CLK in verbatim form to
+ # re-use 'iec' decoder logic. Turn ATN to positive logic for
+ # easier processing. The data bits get handled during byte
+ # accumulation.
+ pins = self.wait(step_wait_conds[step])
+ data, clk = pins[PIN_DATA], pins[PIN_CLK]
+ atn, = self.invert_pins([pins[PIN_ATN]])
+
+ if self.matched[0]:
+ # Falling edge on ATN, reset step.
+ step = STEP_WAIT_READY_TO_SEND
+
+ if step == STEP_WAIT_READY_TO_SEND:
+ # Don't use self.matched[1] here since we might come from
+ # a step with different conds due to the code above.
+ if data == 0 and clk == 1:
+ # Rising edge on CLK while DATA is low: Ready to send.
+ step = STEP_WAIT_READY_FOR_DATA
+ elif step == STEP_WAIT_READY_FOR_DATA:
+ if data == 1 and clk == 1:
+ # Rising edge on DATA while CLK is high: Ready for data.
+ ss_byte = self.samplenum
+ self.handle_atn_change(atn)
+ if self.curr_eoi:
+ self.handle_eoi_change(False)
+ bits = []
+ step = STEP_PREP_DATA_TEST_EOI
+ elif clk == 0:
+ # CLK low again, transfer aborted.
+ step = STEP_WAIT_READY_TO_SEND
+ elif step == STEP_PREP_DATA_TEST_EOI:
+ if data == 0 and clk == 1:
+ # DATA goes low while CLK is still high, EOI confirmed.
+ self.handle_eoi_change(True)
+ elif clk == 0:
+ step = STEP_CLOCK_DATA_BITS
+ ss_bit = self.samplenum
+ elif step == STEP_CLOCK_DATA_BITS:
+ if self.matched[1]:
+ if clk == 1:
+ # Rising edge on CLK; latch DATA.
+ bits.append(data)
+ elif clk == 0:
+ # Falling edge on CLK; end of bit.
+ es_bit = self.samplenum
+ self.emit_data_ann(ss_bit, es_bit, ANN_RAW_BIT, ['{:d}'.format(bits[-1])])
+ self.putpy(ss_bit, es_bit, 'IEC_BIT', None, bits[-1])
+ ss_bit = self.samplenum
+ if len(bits) == 8:
+ es_byte = self.samplenum
+ self.inject_dav_phase(ss_byte, es_byte, bits)
+ if self.curr_eoi:
+ self.handle_eoi_change(False)
+ step = STEP_WAIT_READY_TO_SEND
+
+ def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
+
+ if False in has_data_n or not has_dav or not has_atn:
+ raise ChannelError('IEEE-488 needs at least ATN and DAV and eight DIO lines.')
+ has_ifc = self.has_channel(PIN_IFC)
+
+ # Capture data lines at the falling edge of DAV, process their
+ # values at rising DAV edge (when data validity ends). Also make
+ # sure to start inspection when the capture happens to start with
+ # low signal levels, i.e. won't include the initial falling edge.
+ # Scan for ATN/EOI edges as well (including the trick which works
+ # around initial pin state).
+ # Map low-active physical transport lines to positive logic here,
+ # to simplify logical inspection/decoding of communicated data,
+ # and to avoid redundancy and inconsistency in later code paths.
+ waitcond = []
+ idx_dav = len(waitcond)
+ waitcond.append({PIN_DAV: 'l'})
+ idx_atn = len(waitcond)
+ waitcond.append({PIN_ATN: 'l'})
+ idx_eoi = None
+ if has_eoi:
+ idx_eoi = len(waitcond)
+ waitcond.append({PIN_EOI: 'l'})
+ idx_ifc = None
+ if has_ifc:
+ idx_ifc = len(waitcond)
+ waitcond.append({PIN_IFC: 'l'})
+ while True:
+ pins = self.wait(waitcond)
+ pins = self.invert_pins(pins)
+
+ # BEWARE! Order of evaluation does matter. For low samplerate
+ # captures, many edges fall onto the same sample number. So
+ # we process active edges of flags early (before processing
+ # data bits), and inactive edges late (after data got processed).
+ if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 1:
+ self.handle_ifc_change(pins[PIN_IFC])
+ if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 1:
+ self.handle_eoi_change(pins[PIN_EOI])
+ if self.matched[idx_atn] and pins[PIN_ATN] == 1:
+ self.handle_atn_change(pins[PIN_ATN])
+ if self.matched[idx_dav]:
+ self.handle_dav_change(pins[PIN_DAV], pins[PIN_DIO1:PIN_DIO8 + 1])
+ if self.matched[idx_atn] and pins[PIN_ATN] == 0:
+ self.handle_atn_change(pins[PIN_ATN])
+ if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 0:
+ self.handle_eoi_change(pins[PIN_EOI])
+ if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 0:
+ self.handle_ifc_change(pins[PIN_IFC])
+
+ waitcond[idx_dav][PIN_DAV] = 'e'
+ waitcond[idx_atn][PIN_ATN] = 'e'
+ if has_eoi:
+ waitcond[idx_eoi][PIN_EOI] = 'e'
+ if has_ifc:
+ waitcond[idx_ifc][PIN_IFC] = 'e'
+
+ def decode(self):
+ # The decoder's boilerplate declares some of the input signals as
+ # optional, but only to support both serial and parallel variants.
+ # The CLK signal discriminates the two. For either variant some
+ # of the "optional" signals are not really optional for proper
+ # operation of the decoder. Check these conditions here.
+ has_clk = self.has_channel(PIN_CLK)
+ has_data_1 = self.has_channel(PIN_DIO1)
+ has_data_n = [bool(self.has_channel(pin) for pin in range(PIN_DIO1, PIN_DIO8 + 1))]
+ has_dav = self.has_channel(PIN_DAV)
+ has_atn = self.has_channel(PIN_ATN)
+ has_eoi = self.has_channel(PIN_EOI)
+ has_srq = self.has_channel(PIN_SRQ)
+ if has_clk:
+ self.decode_serial(has_clk, has_data_1, has_atn, has_srq)
+ else:
+ self.decode_parallel(has_data_n, has_dav, has_atn, has_eoi, has_srq)
desc = 'NEC infrared remote control protocol.'
license = 'gplv2+'
inputs = ['logic']
- outputs = ['ir_nec']
+ outputs = []
+ tags = ['IR']
channels = (
{'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
)
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:
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.
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+'],
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'},
)
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:
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':
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':
self.handle_bits()
self.reset_decoder_state()
- self.old_ir = self.ir
+ self.next_edge = 'l' if self.ir else 'h'
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+RC-6 is a biphase/manchester based infrared remote control protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ir_rc6'
+ name = 'IR RC-6'
+ longname = 'IR RC-6'
+ desc = 'RC-6 infrared remote control protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['IR']
+ channels = (
+ {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'},
+ )
+ options = (
+ {'id': 'polarity', 'desc': 'Polarity', 'default': 'auto',
+ 'values': ('auto', 'active-low', 'active-high')},
+ )
+ annotations = (
+ ('bit', 'Bit'),
+ ('sync', 'Sync'),
+ ('startbit', 'Startbit'),
+ ('field', 'Field'),
+ ('togglebit', 'Togglebit'),
+ ('address', 'Address'),
+ ('command', 'Command'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0,)),
+ ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.edges, self.deltas, self.bits = [], [], []
+ self.state = 'IDLE'
+ self.mode = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+ # One bit: 0.889ms (one half low, one half high).
+ self.halfbit = int((self.samplerate * 0.000889) / 2.0)
+
+ def putb(self, bit, data):
+ self.put(bit[0], bit[1], self.out_ann, data)
+
+ def putbits(self, bit1, bit2, data):
+ self.put(bit1[0], bit2[1], self.out_ann, data)
+
+ def putx(self, ss, es, data):
+ self.put(ss, es, self.out_ann, data)
+
+ def handle_bit(self):
+ if len(self.bits) != 6:
+ return
+ if self.bits[0][2] == 8 and self.bits[0][3] == 1:
+ self.putb(self.bits[0], [1, ['Synchronisation', 'Sync']])
+ else:
+ return
+ if self.bits[1][3] == 1:
+ self.putb(self.bits[1], [2, ['Startbit', 'Start']])
+ else:
+ return
+ self.mode = sum([self.bits[2 + i][3] << (2 - i) for i in range(3)])
+ self.putbits(self.bits[2], self.bits[4], [3, ['Field: %d' % self.mode]])
+ self.putb(self.bits[5], [4, ['Toggle: %d' % self.bits[5][3]]])
+
+ def handle_package(self):
+ # Sync and start bits have to be 1.
+ if self.bits[0][3] == 0 or self.bits[1][3] == 0:
+ return
+ if len(self.bits) <= 6:
+ return
+
+ if self.mode == 0 and len(self.bits) == 22: # Mode 0 standard
+ value = sum([self.bits[6 + i][3] << (7 - i) for i in range(8)])
+ self.putbits(self.bits[6], self.bits[13], [5, ['Address: %0.2X' % value]])
+
+ value = sum([self.bits[14 + i][3] << (7 - i) for i in range(8)])
+ self.putbits(self.bits[14], self.bits[21], [6, ['Data: %0.2X' % value]])
+
+ self.bits = []
+
+ if self.mode == 6 and len(self.bits) >= 15: # Mode 6
+ if self.bits[6][3] == 0: # Short addr, Mode 6A
+ value = sum([self.bits[6 + i][3] << (7 - i) for i in range(8)])
+ self.putbits(self.bits[6], self.bits[13], [5, ['Address: %0.2X' % value]])
+
+ num_data_bits = len(self.bits) - 14
+ value = sum([self.bits[14 + i][3] << (num_data_bits - 1 - i) for i in range(num_data_bits)])
+ self.putbits(self.bits[14], self.bits[-1], [6, ['Data: %X' % value]])
+
+ self.bits = []
+
+ elif len(self.bits) >= 23: # Long addr, Mode 6B
+ value = sum([self.bits[6 + i][3] << (15 - i) for i in range(16)])
+ self.putbits(self.bits[6], self.bits[21], [5, ['Address: %0.2X' % value]])
+
+ num_data_bits = len(self.bits) - 22
+ value = sum([self.bits[22 + i][3] << (num_data_bits - 1 - i) for i in range(num_data_bits)])
+ self.putbits(self.bits[22], self.bits[-1], [6, ['Data: %X' % value]])
+
+ self.bits = []
+
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ value = 0
+ num_edges = -1
+ self.invert = False
+
+ while True:
+ conditions = [{0: 'e'}]
+ if self.state == 'DATA':
+ conditions.append({'skip': self.halfbit * 6})
+ (self.ir,) = self.wait(conditions)
+
+ if len(conditions) == 2:
+ if self.matched[1]:
+ self.state = 'IDLE'
+
+ self.edges.append(self.samplenum)
+ if len(self.edges) < 2:
+ continue
+
+ delta = (self.edges[-1] - self.edges[-2]) / self.halfbit
+ delta = int(delta + 0.5)
+ self.deltas.append(delta)
+
+ if len(self.deltas) < 2:
+ continue
+
+ if self.deltas[-2:] == [6, 2]:
+ self.state = 'SYNC'
+ num_edges = 0
+ self.bits = []
+
+ if self.options['polarity'] == 'auto':
+ value = 1
+ else:
+ value = self.ir if self.options['polarity'] == 'active-high' else 1 - self.ir
+
+ self.bits.append((self.edges[-3], self.edges[-1], 8, value))
+ self.invert = self.ir == 0
+ self.putb(self.bits[-1], [0, ['%d' % value]]) # Add bit.
+
+ if (num_edges % 2) == 0: # Only count every second edge.
+ if self.deltas[-2] in [1, 2, 3] and self.deltas[-1] in [1, 2, 3, 6]:
+ self.state = 'DATA'
+ if self.deltas[-2] != self.deltas[-1]:
+ # Insert border between 2 bits.
+ self.edges.insert(-1, self.edges[-2] + self.deltas[-2] * self.halfbit)
+ total = self.deltas[-1]
+ self.deltas[-1] = self.deltas[-2]
+ self.deltas.append(total - self.deltas[-1])
+
+ self.bits.append((self.edges[-4], self.edges[-2], self.deltas[-2] * 2, value))
+
+ num_edges += 1
+ else:
+ self.bits.append((self.edges[-3], self.edges[-1], self.deltas[-1] * 2, value))
+
+ self.putb(self.bits[-1], [0, ['%d' % value]]) # Add bit.
+
+ if len(self.bits) > 0:
+ self.handle_bit()
+ if self.state == 'IDLE':
+ self.handle_package()
+
+ if self.options['polarity'] == 'auto':
+ value = self.ir if self.invert else 1 - self.ir
+ else:
+ value = self.ir if self.options['polarity'] == 'active-low' else 1 - self.ir
+
+ num_edges += 1
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.
'''
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'},
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'},
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'] + (
)
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,)),
)
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)
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()
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):
desc = 'ST STM32-specific JTAG protocol.'
license = 'gplv2+'
inputs = ['jtag']
- outputs = ['jtag_stm32']
+ outputs = []
+ tags = ['Debug/trace']
annotations = (
('item', 'Item'),
('field', 'Field'),
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'uart' PD and decodes the LIN
+(Local Interconnect Network) protocol.
+
+LIN is layered on top of the UART (async serial) protocol, with 8n1 settings.
+Bytes are sent LSB-first.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class LinFsm:
+ class State:
+ WaitForBreak = 'WAIT_FOR_BREAK'
+ Sync = 'SYNC'
+ Pid = 'PID'
+ Data = 'DATA'
+ Checksum = 'CHECKSUM'
+ Error = 'ERROR'
+
+ def transit(self, target_state):
+ if not self._transition_allowed(target_state):
+ return False
+ self.state = target_state
+ return True
+
+ def _transition_allowed(self, target_state):
+ if target_state == LinFsm.State.Error:
+ return True
+ return target_state in self.allowed_state[self.state]
+
+ def reset(self):
+ self.state = LinFsm.State.WaitForBreak
+ self.uart_idle_count = 0
+
+ def __init__(self):
+ a = dict()
+ a[LinFsm.State.WaitForBreak] = (LinFsm.State.Sync,)
+ a[LinFsm.State.Sync] = (LinFsm.State.Pid,)
+ a[LinFsm.State.Pid] = (LinFsm.State.Data,)
+ a[LinFsm.State.Data] = (LinFsm.State.Data, LinFsm.State.Checksum)
+ a[LinFsm.State.Checksum] = (LinFsm.State.WaitForBreak,)
+ a[LinFsm.State.Error] = (LinFsm.State.Sync,)
+ self.allowed_state = a
+
+ self.state = None
+ self.uart_idle_count = 0
+ self.reset()
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'lin'
+ name = 'LIN'
+ longname = 'Local Interconnect Network'
+ desc = 'Local Interconnect Network (LIN) protocol.'
+ license = 'gplv2+'
+ inputs = ['uart']
+ outputs = []
+ tags = ['Automotive']
+ options = (
+ {'id': 'version', 'desc': 'Protocol version', 'default': 2, 'values': (1, 2)},
+ )
+ annotations = (
+ ('data', 'LIN data'),
+ ('control', 'Protocol info'),
+ ('error', 'Error descriptions'),
+ ('inline_error', 'Protocol violations and errors'),
+ )
+ annotation_rows = (
+ ('data', 'Data', (0, 1, 3)),
+ ('error', 'Error', (2,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.fsm = LinFsm()
+ self.lin_header = []
+ self.lin_rsp = []
+ self.lin_version = None
+ self.ss_block = None
+ self.es_block = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.lin_version = self.options['version']
+
+ def putx(self, data):
+ self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+ def wipe_break_null_byte(self, value):
+ # Upon a break condition a null byte is received which must be ignored.
+ if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+ if len(self.lin_rsp):
+ value = self.lin_rsp.pop()[2]
+ else:
+ self.lin_header.pop()
+
+ if value != 0:
+ self.fsm.transit(LinFsm.State.Error)
+ self.handle_error(None)
+ return False
+
+ return True
+
+ def handle_uart_idle(self):
+ if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+ self.fsm.uart_idle_count += 1
+
+ if self.fsm.uart_idle_count == 2:
+ self.fsm.transit(LinFsm.State.Checksum)
+ self.handle_checksum()
+ self.fsm.reset()
+
+ def handle_wait_for_break(self, value):
+ self.wipe_break_null_byte(value)
+
+ def handle_break(self, value):
+ if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+ if self.wipe_break_null_byte(value):
+ self.fsm.transit(LinFsm.State.Checksum)
+ self.handle_checksum()
+
+ self.fsm.reset()
+ self.fsm.transit(LinFsm.State.Sync)
+
+ self.putx([1, ['Break condition', 'Break', 'Brk', 'B']])
+
+ def handle_sync(self, value):
+ self.fsm.transit(LinFsm.State.Pid)
+ self.lin_header.append((self.ss_block, self.es_block, value))
+
+ def handle_pid(self, value):
+ self.fsm.transit(LinFsm.State.Data)
+ self.lin_header.append((self.ss_block, self.es_block, value))
+
+ def handle_data(self, value):
+ self.lin_rsp.append((self.ss_block, self.es_block, value))
+
+ def handle_checksum(self):
+ sync = self.lin_header.pop(0) if len(self.lin_header) else None
+
+ self.put(sync[0], sync[1], self.out_ann, [0, ['Sync', 'S']])
+
+ if sync[2] != 0x55:
+ self.put(sync[0], sync[1], self.out_ann,
+ [2, ['Sync is not 0x55', 'Not 0x55', '!= 0x55']])
+
+ pid = self.lin_header.pop(0) if len(self.lin_header) else None
+ checksum = self.lin_rsp.pop() if len(self.lin_rsp) else None
+
+ if pid:
+ id_ = pid[2] & 0x3F
+ parity = pid[2] >> 6
+
+ expected_parity = self.calc_parity(pid[2])
+ parity_valid = parity == expected_parity
+
+ if not parity_valid:
+ self.put(pid[0], pid[1], self.out_ann, [2, ['P != %d' % expected_parity]])
+
+ ann_class = 0 if parity_valid else 3
+ self.put(pid[0], pid[1], self.out_ann, [ann_class, [
+ 'ID: %02X Parity: %d (%s)' % (id_, parity, 'ok' if parity_valid else 'bad'),
+ 'ID: 0x%02X' % id_, 'I: %d' % id_
+ ]])
+
+ if len(self.lin_rsp):
+ checksum_valid = self.checksum_is_valid(pid[2], self.lin_rsp, checksum[2])
+
+ for b in self.lin_rsp:
+ self.put(b[0], b[1], self.out_ann, [0, ['Data: 0x%02X' % b[2], 'D: 0x%02X' % b[2]]])
+
+ ann_class = 0 if checksum_valid else 3
+ self.put(checksum[0], checksum[1], self.out_ann,
+ [ann_class, ['Checksum: 0x%02X' % checksum[2], 'Checksum', 'Chk', 'C']])
+
+ if not checksum_valid:
+ self.put(checksum[0], checksum[1], self.out_ann, [2, ['Checksum invalid']])
+ else:
+ pass # No response.
+
+ self.lin_header.clear()
+ self.lin_rsp.clear()
+
+ def handle_error(self, dummy):
+ self.putx([3, ['Error', 'Err', 'E']])
+
+ def checksum_is_valid(self, pid, data, checksum):
+ if self.lin_version == 2:
+ id_ = pid & 0x3F
+
+ if id_ != 60 and id_ != 61:
+ checksum += pid
+
+ for d in data:
+ checksum += d[2]
+
+ carry_bits = int(checksum / 256)
+ checksum += carry_bits
+
+ return checksum & 0xFF == 0xFF
+
+ @staticmethod
+ def calc_parity(pid):
+ id_ = [((pid & 0x3F) >> i) & 1 for i in range(8)]
+
+ p0 = id_[0] ^ id_[1] ^ id_[2] ^ id_[4]
+ p1 = not (id_[1] ^ id_[3] ^ id_[4] ^ id_[5])
+
+ return (p0 << 0) | (p1 << 1)
+
+ def decode(self, ss, es, data):
+ ptype, rxtx, pdata = data
+
+ self.ss_block, self.es_block = ss, es
+
+ # Ignore all UART packets except the actual data packets or BREAK.
+ if ptype == 'IDLE':
+ self.handle_uart_idle()
+ if ptype == 'BREAK':
+ self.handle_break(pdata)
+ if ptype != 'DATA':
+ return
+
+ # We're only interested in the byte value (not individual bits).
+ pdata = pdata[0]
+
+ # Short LIN overview:
+ # - Message begins with a BREAK (0x00) for at least 13 bittimes.
+ # - Break is always followed by a SYNC byte (0x55).
+ # - Sync byte is followed by a PID byte (Protected Identifier).
+ # - PID byte is followed by 1 - 8 data bytes and a final checksum byte.
+
+ handler = getattr(self, 'handle_%s' % self.fsm.state.lower())
+ handler(pdata)
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',)},
##
'''
-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".
'''
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'},
def reset(self):
self.state = 'IDLE'
self.oldlclk = -1
- self.samplenum = 0
self.lad = -1
self.addr = 0
self.cur_nibble = 0
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'},
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'),
'''
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).
'''
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'},
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'},
def reset(self):
self.illegal_bus = 0
- self.samplenum = -1
self.clause45_addr = -1 # Clause 45 is context sensitive.
self.reset_decoder_state()
'''
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.
license = 'gplv2+'
inputs = ['logic']
outputs = ['microwire']
+ tags = ['Embedded/industrial']
channels = (
{'id': 'cs', 'name': 'CS', 'desc': 'Chip select'},
{'id': 'sk', 'name': 'SK', 'desc': 'Clock'},
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)'),
desc = 'Miller encoding protocol.'
license = 'gplv2+'
inputs = ['logic']
- outputs = ['miller']
+ outputs = []
+ tags = ['Encoding']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data signal'},
)
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
def metadata(self, key, value):
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'),
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
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
license = 'gplv3+'
inputs = ['uart']
outputs = ['modbus']
+ tags = ['Embedded/industrial']
annotations = (
('sc-server-id', ''),
('sc-function', ''),
('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):
# 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!
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')
desc = 'Demodulated morse code protocol.'
license = 'gplv2+'
inputs = ['logic']
- outputs = ['morse']
+ outputs = []
+ tags = ['Encoding']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
import sigrokdecode as srd
from .lists import *
+TX, RX = range(2)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'mrf24j40'
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):
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)
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]
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':
desc = 'Digital Thermal Orientation Sensor (DTOS) protocol.'
license = 'gplv2+'
inputs = ['i2c']
- outputs = ['mxc6225xu']
+ outputs = []
+ tags = ['IC', 'Sensor']
annotations = (
('text', 'Human-readable text'),
)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the button states
+of an NES gamepad.
+
+The SPI decoder needs to be configured as follows:
+
+Clock polarity = 1
+Clock phase = 0
+Bit order = msb-first
+Word size = 8
+
+Chip-select is not used and must not be assigned to any channel.
+
+Hardware setup is as follows:
+ ___
+ GND |o \
+ CUP |o o| VCC
+ OUT 0 |o o| D3
+ D1 |o o| D4
+ -----
+NES Gamepad Connector
+
+VCC - Power 5V
+GND - Ground
+CUP - Shift register clock (CLK)
+OUT 0 - Shift register latch (optional)
+D1 - Gamepad data (MOSI)
+D3 - Data (unused)
+D4 - Data (unused)
+
+Data pins D3 and D4 are not used by the standard gamepad but
+by special controllers like the Nintento Zapper light gun.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Stephan Thiele <stephan.thiele@mailbox.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'nes_gamepad'
+ name = 'NES gamepad'
+ longname = 'Nintendo Entertainment System gamepad'
+ desc = 'NES gamepad button states.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['Retro computing']
+ options = (
+ # Currently only the standard controller is supported. This might be
+ # extended by special controllers like the Nintendo Zapper light gun.
+ {'id': 'variant', 'desc': 'Gamepad variant',
+ 'default': 'Standard gamepad', 'values': ('Standard gamepad',)},
+ )
+ annotations = (
+ ('button', 'Button states'),
+ ('no-press', 'No button press'),
+ ('not-connected', 'Gamepad unconnected')
+ )
+ annotation_rows = (
+ ('buttons', 'Button states', (0,)),
+ ('no-press', 'No button press', (1,)),
+ ('not-connected', 'Gamepad unconnected', (2,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.variant = None
+ self.ss_block = None
+ self.es_block = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.variant = self.options['variant']
+
+ def putx(self, data):
+ self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+ def handle_data(self, value):
+ if value == 0xFF:
+ self.putx([1, ['No button is pressed']])
+ return
+
+ if value == 0x00:
+ self.putx([2, ['Gamepad is not connected']])
+ return
+
+ buttons = [
+ 'A',
+ 'B',
+ 'Select',
+ 'Start',
+ 'North',
+ 'South',
+ 'West',
+ 'East'
+ ]
+
+ bits = format(value, '08b')
+ button_str = ''
+
+ for b in enumerate(bits):
+ button_index = b[0]
+ button_is_pressed = b[1] == '0'
+
+ if button_is_pressed:
+ if button_str != '':
+ button_str += ' + '
+ button_str += buttons[button_index]
+
+ self.putx([0, ['%s' % button_str]])
+
+ def decode(self, ss, es, data):
+ ptype, mosi, miso = data
+ self.ss_block, self.es_block = ss, es
+
+ if ptype != 'DATA':
+ return
+
+ self.handle_data(miso)
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')},
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'),
- 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)
'''
license = 'gplv2+'
inputs = ['logic']
outputs = ['onewire_link']
+ tags = ['Embedded/industrial']
channels = (
{'id': 'owr', 'name': 'OWR', 'desc': '1-Wire signal line'},
)
# 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):
license = 'gplv2+'
inputs = ['onewire_link']
outputs = ['onewire_network']
+ tags = ['Embedded/industrial']
annotations = (
('text', 'Human-readable text'),
)
license = 'gplv2+'
inputs = ['logic']
outputs = ['ook']
+ tags = ['Encoding']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
# Filter incoming pulses to remove random noise.
if self.state == 'DECODE_TIMEOUT':
self.preamble = []
- self.edge_count == 0
+ self.edge_count = 0
self.word_first = self.samplenum
self.sample_first = self.samplenum - self.samplenumber_last
self.state = 'WAITING_FOR_PREAMBLE'
self.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:
license = 'gplv2+'
inputs = ['ook']
outputs = []
+ tags = ['Sensor']
annotations = (
('bit', 'Bit'),
('field', 'Field'),
license = 'gplv2+'
inputs = ['ook']
outputs = ['ook']
+ tags = ['Encoding']
annotations = (
('bit', 'Bit'),
('ref', 'Reference'),
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'),
license = 'gplv2+'
inputs = ['logic']
outputs = ['parallel']
+ tags = ['Util']
optional_channels = channel_list(NUM_CHANNELS)
options = (
{'id': 'clock_edge', 'desc': 'Clock edge to sample on',
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Mickael Bosch <mickael.bosch@linux.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes the NXP Semiconductors
+PCA9571 8-bit I²C output expander protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Mickael Bosch <mickael.bosch@linux.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+NUM_OUTPUT_CHANNELS = 8
+
+# TODO: Other I²C functions: general call / reset address, device ID address.
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'pca9571'
+ name = 'PCA9571'
+ longname = 'NXP PCA9571'
+ desc = 'NXP PCA9571 8-bit I²C output expander.'
+ license = 'gplv2+'
+ inputs = ['i2c']
+ outputs = []
+ tags = ['Embedded/industrial', 'IC']
+ annotations = (
+ ('register', 'Register type'),
+ ('value', 'Register value'),
+ ('warning', 'Warning messages'),
+ )
+ annotation_rows = (
+ ('regs', 'Registers', (0, 1)),
+ ('warnings', 'Warnings', (2,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.state = 'IDLE'
+ self.last_write = 0xFF # Chip port default state is high.
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putx(self, data):
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def handle_io(self, b):
+ if self.state == 'READ DATA':
+ operation = ['Outputs read', 'R']
+ if b != self.last_write:
+ self.putx([2, ['Warning: read value and last write value '
+ '(%02X) are different' % self.last_write]])
+ else:
+ operation = ['Outputs set', 'W']
+ self.last_write = b
+ self.putx([1, [operation[0] + ': %02X' % b,
+ operation[1] + ': %02X' % b]])
+
+ def check_correct_chip(self, addr):
+ if addr != 0x25:
+ self.putx([2, ['Warning: I²C slave 0x%02X not a PCA9571 '
+ 'compatible chip.' % addr]])
+ return False
+ return True
+
+ def decode(self, ss, es, data):
+ cmd, databyte = data
+ self.ss, self.es = ss, es
+
+ # State machine.
+ if cmd in ('ACK', 'BITS'): # Discard 'ACK' and 'BITS'.
+ pass
+ elif cmd in ('START', 'START REPEAT'): # Start a communication.
+ self.state = 'GET SLAVE ADDR'
+ elif cmd in ('NACK', 'STOP'): # Reset the state machine.
+ self.state = 'IDLE'
+ elif cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
+ if ((self.state == 'GET SLAVE ADDR') and
+ self.check_correct_chip(databyte)):
+ if cmd == 'ADDRESS READ':
+ self.state = 'READ DATA'
+ else:
+ self.state = 'WRITE DATA'
+ else:
+ self.state = 'IDLE'
+ elif cmd in ('DATA READ', 'DATA WRITE'):
+ if self.state in ('READ DATA', 'WRITE DATA'):
+ self.handle_io(databyte)
+ else:
+ self.state = 'IDLE'
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'},
def reset(self):
self.bits = []
- self.samplenum = 0
self.bitcount = 0
def start(self):
##
'''
-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
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'},
)
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'},
)
license = 'gplv2+'
inputs = ['logic']
outputs = []
+ tags = ['IC', 'IR']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
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'),
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'),
)
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'},
)
# 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.
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'),
##
'''
-Decoder for Siemens EEPROM SDA2506.
+Decoder for Siemens EEPROM SDA 2506-5.
'''
from .pd import 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'},
)
annotation_rows = (
('bits', 'Bits', (ann_cmdbit, ann_databit)),
- ('commands', 'Commands', (ann_cmd,)),
('data', 'Data', (ann_data,)),
+ ('commands', 'Commands', (ann_cmd,)),
('warnings', 'Warnings', (ann_warning,)),
)
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'},
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)) + ( \
('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):
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)
# 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:
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):
# 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
# 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
# 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)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder decodes the output of a 7-segment display.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Benedikt Otto <benedikt_o@web.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class ChannelError(Exception):
+ pass
+
+digits = {
+ (0, 0, 0, 0, 0, 0, 0): ' ',
+ (1, 1, 1, 1, 1, 1, 0): '0',
+ (0, 1, 1, 0, 0, 0, 0): '1',
+ (1, 1, 0, 1, 1, 0, 1): '2',
+ (1, 1, 1, 1, 0, 0, 1): '3',
+ (0, 1, 1, 0, 0, 1, 1): '4',
+ (1, 0, 1, 1, 0, 1, 1): '5',
+ (1, 0, 1, 1, 1, 1, 1): '6',
+ (1, 1, 1, 0, 0, 0, 0): '7',
+ (1, 1, 1, 1, 1, 1, 1): '8',
+ (1, 1, 1, 1, 0, 1, 1): '9',
+ (1, 1, 1, 0, 1, 1, 1): 'A',
+ (0, 0, 1, 1, 1, 1, 1): 'B',
+ (1, 0, 0, 1, 1, 1, 0): 'C',
+ (0, 1, 1, 1, 1, 0, 1): 'D',
+ (1, 0, 0, 1, 1, 1, 1): 'E',
+ (1, 0, 0, 0, 1, 1, 1): 'F',
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'seven_segment'
+ name = '7-segment'
+ longname = '7-segment display'
+ desc = '7-segment display protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Display']
+ channels = (
+ {'id': 'a', 'name': 'A', 'desc': 'Segment A'},
+ {'id': 'b', 'name': 'B', 'desc': 'Segment B'},
+ {'id': 'c', 'name': 'C', 'desc': 'Segment C'},
+ {'id': 'd', 'name': 'D', 'desc': 'Segment D'},
+ {'id': 'e', 'name': 'E', 'desc': 'Segment E'},
+ {'id': 'f', 'name': 'F', 'desc': 'Segment F'},
+ {'id': 'g', 'name': 'G', 'desc': 'Segment G'},
+ )
+ optional_channels = (
+ {'id': 'dp', 'name': 'DP', 'desc': 'Decimal point'},
+ )
+ options = (
+ {'id': 'polarity', 'desc': 'Expected polarity',
+ 'default': 'common-cathode', 'values': ('common-cathode', 'common-anode')},
+ )
+ annotations = (
+ ('decoded-digit', 'Decoded digit'),
+ )
+ annotation_rows = (
+ ('decoded-digits', 'Decoded digits', (0,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putb(self, ss_block, es_block, data):
+ self.put(ss_block, es_block, self.out_ann, data)
+
+ def pins_to_hex(self, pins):
+ return digits.get(pins, None)
+
+ def decode(self):
+ oldpins = self.wait()
+
+ # Check if at least the 7 signals are present.
+ if False in [p in (0, 1) for p in oldpins[:7]]:
+ raise ChannelError('7 or 8 pins have to be present.')
+
+ lastpos = self.samplenum
+
+ self.have_dp = self.has_channel(7)
+
+ conditions = [{0: 'e'}, {1: 'e'}, {2: 'e'}, {3: 'e'}, {4: 'e'}, {5: 'e'}, {6: 'e'}]
+
+ if self.have_dp:
+ conditions.append({7: 'e'})
+
+ while True:
+ # Wait for any change.
+ pins = self.wait(conditions)
+
+ if self.options['polarity'] == 'common-anode':
+ # Invert all data lines if a common anode display is used.
+ if self.have_dp:
+ oldpins = tuple((1 - state for state in oldpins))
+ else:
+ oldpins = tuple((1 - state for state in oldpins[:7]))
+
+ # Convert to character string.
+ digit = self.pins_to_hex(oldpins[:7])
+
+ if digit is not None:
+ dp = oldpins[7]
+
+ # Check if decimal point is present and active.
+ if self.have_dp and dp == 1:
+ digit += '.'
+
+ self.putb(lastpos, self.samplenum, [0, [digit]])
+
+ lastpos = self.samplenum
+
+ oldpins = pins
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Shirow Miura <shirowmiura@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Signature analysis function for troubleshooting logic circuits.
+This generates the same signature as Hewlett-Packard 5004A.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Shirow Miura <shirowmiura@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+symbol_map = {
+ 0b0000: '0',
+ 0b1000: '1',
+ 0b0100: '2',
+ 0b1100: '3',
+ 0b0010: '4',
+ 0b1010: '5',
+ 0b0110: '6',
+ 0b1110: '7',
+ 0b0001: '8',
+ 0b1001: '9',
+ 0b0101: 'A',
+ 0b1101: 'C',
+ 0b0011: 'F',
+ 0b1011: 'H',
+ 0b0111: 'P',
+ 0b1111: 'U',
+}
+
+START, STOP, CLOCK, DATA = range(4)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'signature'
+ name = 'Signature'
+ longname = 'Signature analysis'
+ desc = 'Annotate signature of logic patterns.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Debug/trace', 'Util', 'Encoding']
+ channels = (
+ {'id': 'start', 'name': 'START', 'desc': 'START channel'},
+ {'id': 'stop', 'name': 'STOP', 'desc': 'STOP channel'},
+ {'id': 'clk', 'name': 'CLOCK', 'desc': 'CLOCK channel'},
+ {'id': 'data', 'name': 'DATA', 'desc': 'DATA channel'},
+ )
+ options = (
+ {'id': 'start_edge', 'desc': 'START edge polarity',
+ 'default': 'rising', 'values': ('rising', 'falling')},
+ {'id': 'stop_edge', 'desc': 'STOP edge polarity',
+ 'default': 'rising', 'values': ('rising', 'falling')},
+ {'id': 'clk_edge', 'desc': 'CLOCK edge polarity',
+ 'default': 'falling', 'values': ('rising', 'falling')},
+ {'id': 'annbits', 'desc': 'Enable bit level annotations',
+ 'default': 'no', 'values': ('yes', 'no')},
+ )
+ annotations = (
+ ('bit0', 'Bit0'),
+ ('bit1', 'Bit1'),
+ ('start', 'START'),
+ ('stop', 'STOP'),
+ ('sig', 'Sig')
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0, 1, 2, 3)),
+ ('sig', 'Sig', (4,))
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putsig(self, ss, es, signature):
+ s = ''.join([symbol_map[(signature >> 0) & 0x0f],
+ symbol_map[(signature >> 4) & 0x0f],
+ symbol_map[(signature >> 8) & 0x0f],
+ symbol_map[(signature >> 12) & 0x0f]])
+ self.put(ss, es, self.out_ann, [4, [s]])
+
+ def putb(self, ss, ann):
+ self.put(ss, self.samplenum, self.out_ann, ann)
+
+ def decode(self):
+ opt = self.options
+ start_edge_mode_rising = opt['start_edge'] == 'rising'
+ stop_edge_mode_rising = opt['stop_edge'] == 'rising'
+ annbits = opt['annbits'] == 'yes'
+ gate_is_open = False
+ sample_start = None
+ started = False
+ last_samplenum = 0
+ prev_start = 0 if start_edge_mode_rising else 1
+ prev_stop = 0 if stop_edge_mode_rising else 1
+ shiftreg = 0
+
+ while True:
+ start, stop, _, data = self.wait({CLOCK: opt['clk_edge']})
+ if start != prev_start and not gate_is_open:
+ gate_is_open = (start == 1) if start_edge_mode_rising else (start == 0)
+ if gate_is_open:
+ # Start sampling.
+ sample_start = self.samplenum
+ started = True
+ elif stop != prev_stop and gate_is_open:
+ gate_is_open = not ((stop == 1) if stop_edge_mode_rising else (stop == 0))
+ if not gate_is_open:
+ # Stop sampling.
+ if annbits:
+ self.putb(last_samplenum, [3, ['STOP', 'STP', 'P']])
+ self.putsig(sample_start, self.samplenum, shiftreg)
+ shiftreg = 0
+ sample_start = None
+ if gate_is_open:
+ if annbits:
+ if started:
+ s = '<{}>'.format(data)
+ self.putb(last_samplenum, [2, ['START' + s, 'STR' + s, 'S' + s]])
+ started = False
+ else:
+ self.putb(last_samplenum, [data, [str(data)]])
+ incoming = (bin(shiftreg & 0b0000_0010_1001_0001).count('1') + data) & 1
+ shiftreg = (incoming << 15) | (shiftreg >> 1)
+ prev_start = start
+ prev_stop = stop
+ last_samplenum = self.samplenum
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'},
)
('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,)),
)
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
license = 'gplv2+'
inputs = ['logic']
outputs = ['spi']
+ tags = ['Embedded/industrial']
channels = (
{'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
)
('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 = (
self.misobytes = []
self.mosibytes = []
self.ss_block = -1
- self.samplenum = -1
self.ss_transfer = -1
self.cs_was_deasserted = False
self.have_cs = self.have_miso = self.have_mosi = None
self.ss_transfer = 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])
0x15: 'MX25L3205D',
0x16: 'MX25L6405D',
},
+ 'winbond': {
+ 0x13: 'W25Q80DV',
+ },
}
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',
'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.
+ },
}
L = len(cmds)
-# Don't forget to keep this in sync with 'cmds' is lists.py.
+# Don't forget to keep this in sync with 'cmds' in lists.py.
class Ann:
WRSR, PP, READ, WRDI, RDSR, WREN, FAST_READ, SE, RDSCUR, WRSCUR, \
RDSR2, CE, ESRY, DSRY, WRITE1, WRITE2, REMS, RDID, RDP_RES, CP, ENSO, DP, \
class 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'),
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
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:
self.putx([Ann.BIT, [decode_status_reg(miso)]])
self.putx([Ann.FIELD, ['Status register']])
self.putc([Ann.RDSR, self.cmd_ann_list()])
+ # Set write latch state.
+ self.writestate = 1 if (miso & (1 << 1)) else 0
self.cmdstate += 1
def handle_rdsr2(self, mosi, miso):
self.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()])
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)
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)
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
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
desc = 'Synchronous Serial Interface (32bit) protocol.'
license = 'gplv2+'
inputs = ['spi']
- outputs = ['ssi32']
+ outputs = []
+ tags = ['Embedded/industrial']
options = (
{'id': 'msgsize', 'desc': 'Message size', 'default': 64},
)
##
'''
-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
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'},
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'},
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
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'},
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import math
license = 'gplv2+'
inputs = ['logic']
outputs = []
+ tags = ['Debug/trace']
options = (
{'id': 'debug', 'desc': 'Debug', 'default': 'no', 'values': ('yes', 'no') },
)
desc = 'T55xx 100-150kHz RFID protocol.'
license = 'gplv2+'
inputs = ['logic']
- outputs = ['t55xx']
+ outputs = []
+ tags = ['IC', 'RFID']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
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'),
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Ben Dooks <ben.dooks@codethink.co.uk>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+TDM Audio is an audio serial bus for moving audio data between devices
+(usually on the same board) which can carry one or more channels of data.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Ben Dooks <ben.dooks@codethink.co.uk>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+MAX_CHANNELS = 8
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'tdm_audio'
+ name = 'TDM audio'
+ longname = 'Time division multiplex audio'
+ desc = 'TDM multi-channel audio protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ tags = ['Audio']
+ channels = (
+ { 'id': 'clock', 'name': 'Bitclk', 'desc': 'Data bit clock' },
+ { 'id': 'frame', 'name': 'Framesync', 'desc': 'Frame sync' },
+ { 'id': 'data', 'name': 'Data', 'desc': 'Serial data' },
+ )
+ options = (
+ {'id': 'bps', 'desc': 'Bits per sample', 'default': 16 },
+ {'id': 'channels', 'desc': 'Channels per frame', 'default': MAX_CHANNELS },
+ {'id': 'edge', 'desc': 'Clock edge to sample on', 'default': 'rising', 'values': ('rising', 'falling') }
+ )
+ annotations = tuple(('ch%d' % i, 'Ch%d' % i) for i in range(MAX_CHANNELS))
+ annotation_rows = tuple(('ch%d' % i, 'Ch%d' % i, (i,)) for i in range(MAX_CHANNELS))
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+ self.channels = MAX_CHANNELS
+ self.channel = 0
+ self.bitdepth = 16
+ self.bitcount = 0
+ self.samplecount = 0
+ self.lastsync = 0
+ self.lastframe = 0
+ self.data = 0
+ self.ss_block = None
+
+ def metdatadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.bitdepth = self.options['bps']
+ self.edge = self.options['edge']
+
+ def decode(self):
+ while True:
+ # Wait for edge of clock (sample on rising/falling edge).
+ clock, frame, data = self.wait({0: self.edge[0]})
+
+ self.data = (self.data << 1) | data
+ self.bitcount += 1
+
+ if self.ss_block is not None:
+ if self.bitcount >= self.bitdepth:
+ self.bitcount = 0
+ self.channel += 1
+
+ c1 = 'Channel %d' % self.channel
+ c2 = 'C%d' % self.channel
+ c3 = '%d' % self.channel
+ if self.bitdepth <= 8:
+ v = '%02x' % self.data
+ elif self.bitdepth <= 16:
+ v = '%04x' % self.data
+ else:
+ v = '%08x' % self.data
+
+ if self.channel < self.channels:
+ ch = self.channel
+ else:
+ ch = 0
+
+ self.put(self.ss_block, self.samplenum, self.out_ann,
+ [ch, ['%s: %s' % (c1, v), '%s: %s' % (c2, v),
+ '%s: %s' % (c3, v)]])
+ self.data = 0
+ self.ss_block = self.samplenum
+ self.samplecount += 1
+
+ # Check for new frame.
+ # Note, frame may be a single clock, or active for the first
+ # sample in the frame.
+ if frame != self.lastframe and frame == 1:
+ self.channel = 0
+ self.bitcount = 0
+ self.data = 0
+ if self.ss_block is None:
+ self.ss_block = 0
+
+ self.lastframe = frame
desc = 'Calculate time between edges.'
license = 'gplv2+'
inputs = ['logic']
- outputs = ['timing']
+ outputs = []
+ tags = ['Clock/timing', 'Util']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
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'},
- 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1).
- 'PARITY ERROR': The data is a tuple with two entries. The first one is
the expected parity value, the second is the actual parity value.
- - TODO: Frame error?
+ - 'BREAK': The data is always 0.
+ - 'FRAME': The data is always a tuple containing two items: The (integer)
+ value of the UART data, and a boolean which reflects the validity of the
+ UART frame.
+ - 'IDLE': The data is always 0.
The <rxtx> field is 0 for RX packets, 1 for TX packets.
'''
# 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':
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).
)
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'),
('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'),
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)
s, halfbit = self.samplenum, self.bit_width / 2.0
self.put(s - floor(halfbit), s + ceil(halfbit), self.out_python, data)
+ def putgse(self, ss, es, data):
+ self.put(ss, es, self.out_ann, data)
+
+ def putpse(self, ss, es, data):
+ self.put(ss, es, self.out_python, data)
+
def putbin(self, rxtx, data):
s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
def 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]
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:
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'
if self.startbit[rxtx] != 0:
self.putp(['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
+ self.frame_valid[rxtx] = False
+ es = self.samplenum + ceil(self.bit_width / 2.0)
+ self.putpse(self.frame_start[rxtx], es, ['FRAME', rxtx,
+ (self.datavalue[rxtx], self.frame_valid[rxtx])])
self.state[rxtx] = 'WAIT FOR START BIT'
return
self.state[rxtx] = '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:
# 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.
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):
# 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
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'
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):
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:
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])
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):
license = 'gplv2+'
inputs = ['usb_signalling']
outputs = ['usb_packet']
+ tags = ['PC']
options = (
{'id': 'signalling', 'desc': 'Signalling',
'default': 'full-speed', 'values': ('full-speed', 'low-speed')},
license = 'gplv2+'
inputs = ['logic']
outputs = ['usb_pd']
+ tags = ['PC']
channels = (
{'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'},
)
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)
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'),
('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 = (
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
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)]
# 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
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)
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.
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]])
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
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'},
self.samplerate = None
self.oldsym = 'J' # The "idle" state is J.
self.ss_block = None
- self.samplenum = 0
self.bitrate = None
self.bitwidth = None
self.samplepos = None
self.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))
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'},
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Petersen <spe@ciellt.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the Xicor X2444M/P
+nonvolatile static RAM protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Petersen <spe@ciellt.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import re
+import sigrokdecode as srd
+
+registers = {
+ 0x80: ['WRDS', 0, lambda _: ''],
+ 0x81: ['STO', 1, lambda _: ''],
+ 0x82: ['SLEEP', 2, lambda _: ''],
+ 0x83: ['WRITE', 3, lambda v: '0x%x' % v],
+ 0x84: ['WREN', 4, lambda _: ''],
+ 0x85: ['RCL', 5, lambda _: ''],
+ 0x86: ['READ', 6, lambda v: '0x%x' % v],
+ 0x87: ['READ', 7, lambda v: '0x%x' % v],
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'x2444m'
+ name = 'X2444M/P'
+ longname = 'Xicor X2444M/P'
+ desc = 'Xicor X2444M/P nonvolatile static RAM protocol.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = []
+ tags = ['IC', 'Memory']
+ annotations = (
+ ('wrds', 'Write disable'),
+ ('sto', 'Store RAM data in EEPROM'),
+ ('sleep', 'Enter sleep mode'),
+ ('write', 'Write data into RAM'),
+ ('wren', 'Write enable'),
+ ('rcl', 'Recall EEPROM data into RAM'),
+ ('read', 'Data read from RAM'),
+ ('read', 'Data read from RAM'),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.cs_start = 0
+ self.cs_asserted = False
+ self.cmd_digit = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putreadwrite(self, ss, es, reg, idx, addr, value):
+ self.put(ss, es, self.out_ann,
+ [idx, ['%s: %s => 0x%4.4x' % (reg, addr, value),
+ '%s: %s => 0x%4.4x' % (reg[0], addr, value), reg[0]]])
+
+ def putcmd(self, ss, es, reg, idx):
+ self.put(ss, es, self.out_ann, [idx, [reg, reg[0]]])
+
+ def decode(self, ss, es, data):
+ ptype, mosi, miso = data
+
+ if ptype == 'DATA':
+ if not self.cs_asserted:
+ return
+
+ if self.cmd_digit == 0:
+ self.addr = mosi
+ self.addr_start = ss
+ elif self.cmd_digit > 0:
+ self.read_value = (self.read_value << 8) + miso
+ self.write_value = (self.write_value << 8) + mosi
+ self.cmd_digit += 1
+ elif ptype == 'CS-CHANGE':
+ self.cs_asserted = (miso == 1)
+ # When not asserted, CS has just changed from asserted to deasserted.
+ if not self.cs_asserted:
+ # Only one digit, simple command. Else read/write.
+ if self.cmd_digit == 1:
+ name, idx, decoder = registers[self.addr & 0x87]
+ self.putcmd(self.addr_start, es, name, idx)
+ elif self.cmd_digit > 1:
+ name, idx, decoder = registers[self.addr & 0x87]
+ if name == 'READ':
+ value = self.read_value
+ elif name == 'WRITE':
+ value = self.write_value
+ else:
+ value = 0
+ self.putreadwrite(self.addr_start, es, name, idx,
+ decoder((self.addr >> 3) & 0x0f), value)
+
+ if self.cs_asserted:
+ self.cs_start = ss
+ self.cmd_digit = 0
+ self.read_value = 0
+ self.write_value = 0
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
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'),
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
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,
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 */
/**
* @{
*/
+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.
*
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;
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);
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;
PyGILState_STATE gstate;
i = 1;
- srd_dbg("Creating new %s instance.", decoder_id);
if (!sess)
return NULL;
/* 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;
}
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;
}
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();
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);
g_slist_free_full(ll, g_free);
}
+ g_slist_free(di->condition_list);
di->condition_list = NULL;
}
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;
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;
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;
}
* 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;
* 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);
*/
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);
}
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);
#include "libsigrokdecode.h"
enum {
+ SRD_TERM_ALWAYS_FALSE,
SRD_TERM_HIGH,
SRD_TERM_LOW,
SRD_TERM_RISING_EDGE,
/* 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);
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);
/** 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;
GSList *opt_channels;
/**
- * List of NULL-terminated char[], containing descriptions of the
- * supported annotation output.
+ * List of annotation classes. Each list item is a GSList itself, with
+ * two NUL-terminated strings: name and description.
*/
GSList *annotations;
GSList *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;
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;
};
/* 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;
}
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;
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;
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);
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.
*
{
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);
* @return SRD_OK upon success, a (negative) error code otherwise.
*
* @private
- *
- * @since 0.1.0
*/
SRD_PRIV int srd_decoder_searchpath_add(const char *path)
{
*/
SRD_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;
}
/** @} */
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
* 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
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)
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);
}
/*
} 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",
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) {
}
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;
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))) {
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. */
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:
*
* 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;
/* 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.");
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;
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;
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 {
}
/* 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. */
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)
/* 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);
*
* @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;
goto err;
}
- *out = PyLong_AsUnsignedLongLong(py_value);
+ *out = PyLong_AsLongLong(py_value);
PyGILState_Release(gstate);
/**
* 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.
*