Contributions
-------------
- - Patches should be sent to the development mailinglist at
+ - In order to contribute you should ideally clone the git repository and
+ let us know (preferably via IRC, or via the mailing list) from where to
+ pull/review your changes. You can use github.com, or any other public git
+ hosting site.
+
+ - Alternatively, patches can be sent to the development mailinglist at
sigrok-devel@lists.sourceforge.net (please subscribe to the list first).
https://lists.sourceforge.net/lists/listinfo/sigrok-devel
- - Alternatively, you can also clone the git repository and let us know
- from where to pull/review your changes. You can use gitorious.org,
- github.com, or any other public git hosting site.
-
Random notes
------------
exception.c \
module_sigrokdecode.c \
type_decoder.c \
- type_logic.c \
error.c \
version.c
- automake >= 1.11 (only needed when building from git)
- libtool (only needed when building from git)
- pkg-config >= 0.22
- - libglib >= 2.28.0
+ - libglib >= 2.34
- Python >= 3.2
- check >= 0.9.4 (optional, only needed to run unit tests)
- doxygen (optional, only needed for the C API docs)
AM_CONDITIONAL([WIN32], [test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"])
+# Initialize pkg-config.
+# We require at least 0.22, as "Requires.private" behaviour changed there.
+PKG_PROG_PKG_CONFIG
+
############################
## Package dependencies ##
############################
AC_SYS_LARGEFILE
+AC_C_BIGENDIAN
+
##############################
## Finalize configuration ##
##############################
# Retrieve the compile and link flags for all modules combined.
# Also, bail out at this point if any module dependency is not met.
-PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.28.0 $SRD_PKGLIBS])
+PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.34 $SRD_PKGLIBS])
PKG_CHECK_MODULES([TESTS], [$SRD_PKGLIBS_TESTS glib-2.0 $SRD_PKGLIBS])
srd_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&AS_MESSAGE_LOG_FD`
- Linker flags.................... $LDFLAGS
Detected libraries (required):
- - glib-2.0 >= 2.28.0.............. $srd_glib_version
+ - glib-2.0 >= 2.34................ $srd_glib_version
$srd_pkglibs_summary
Detected libraries (optional):
$srd_pkglibs_opt_summary
static void decoder_free(struct srd_decoder *dec)
{
+ PyGILState_STATE gstate;
+
if (!dec)
return;
+ gstate = PyGILState_Ensure();
Py_XDECREF(dec->py_dec);
Py_XDECREF(dec->py_mod);
+ PyGILState_Release(gstate);
g_slist_free_full(dec->options, &decoder_option_free);
g_slist_free_full(dec->binary, (GDestroyNotify)&g_strfreev);
struct srd_channel *pdch;
GSList *pdchl;
ssize_t i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
- if (!PyObject_HasAttrString(d->py_dec, attr))
+ if (!PyObject_HasAttrString(d->py_dec, attr)) {
/* No channels of this type specified. */
+ PyGILState_Release(gstate);
return SRD_OK;
+ }
pdchl = NULL;
Py_DECREF(py_channellist);
*out_pdchl = pdchl;
+ PyGILState_Release(gstate);
+
return SRD_OK;
except_out:
err_out:
g_slist_free_full(pdchl, &channel_free);
Py_XDECREF(py_channellist);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
struct srd_decoder_option *o;
GVariant *gvar;
ssize_t opt, i;
+ PyGILState_STATE gstate;
- if (!PyObject_HasAttrString(d->py_dec, "options"))
+ gstate = PyGILState_Ensure();
+
+ if (!PyObject_HasAttrString(d->py_dec, "options")) {
/* No options, that's fine. */
+ PyGILState_Release(gstate);
return SRD_OK;
+ }
options = NULL;
py_str = PyDict_GetItemString(py_opt, "id");
if (!py_str) {
- srd_err("Protocol decoder %s option %zd has no id.",
+ srd_err("Protocol decoder %s option %zd has no ID.",
d->name, opt);
goto err_out;
}
}
d->options = options;
Py_DECREF(py_opts);
+ PyGILState_Release(gstate);
return SRD_OK;
err_out:
g_slist_free_full(options, &decoder_option_free);
Py_XDECREF(py_opts);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
GSList *annotations;
char **annpair;
ssize_t i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
- if (!PyObject_HasAttrString(dec->py_dec, "annotations"))
+ if (!PyObject_HasAttrString(dec->py_dec, "annotations")) {
+ PyGILState_Release(gstate);
return SRD_OK;
+ }
annotations = NULL;
}
dec->annotations = annotations;
Py_DECREF(py_annlist);
+ PyGILState_Release(gstate);
return SRD_OK;
err_out:
g_slist_free_full(annotations, (GDestroyNotify)&g_strfreev);
Py_XDECREF(py_annlist);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
struct srd_decoder_annotation_row *ann_row;
ssize_t i, k;
size_t class_idx;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
- if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows"))
+ if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows")) {
+ PyGILState_Release(gstate);
return SRD_OK;
+ }
annotation_rows = NULL;
}
dec->annotation_rows = annotation_rows;
Py_DECREF(py_ann_rows);
+ PyGILState_Release(gstate);
return SRD_OK;
err_out:
g_slist_free_full(annotation_rows, &annotation_row_free);
Py_XDECREF(py_ann_rows);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
GSList *bin_classes;
char **bin;
ssize_t i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
- if (!PyObject_HasAttrString(dec->py_dec, "binary"))
+ if (!PyObject_HasAttrString(dec->py_dec, "binary")) {
+ PyGILState_Release(gstate);
return SRD_OK;
+ }
bin_classes = NULL;
}
dec->binary = bin_classes;
Py_DECREF(py_bin_classes);
+ PyGILState_Release(gstate);
return SRD_OK;
err_out:
g_slist_free_full(bin_classes, (GDestroyNotify)&g_strfreev);
Py_XDECREF(py_bin_classes);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
{
PyObject *py_method;
int is_callable;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
py_method = PyObject_GetAttrString(py_dec, method_name);
if (!py_method) {
srd_exception_catch("Protocol decoder %s Decoder class "
"has no %s() method", mod_name, method_name);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
is_callable = PyCallable_Check(py_method);
Py_DECREF(py_method);
+ PyGILState_Release(gstate);
+
if (!is_callable) {
srd_err("Protocol decoder %s Decoder class attribute '%s' "
"is not a method.", mod_name, method_name);
{
PyObject *py_apiver;
long apiver;
+ PyGILState_STATE gstate;
if (!d)
return 0;
+ gstate = PyGILState_Ensure();
+
py_apiver = PyObject_GetAttrString(d->py_dec, "api_version");
apiver = (py_apiver && PyLong_Check(py_apiver))
? PyLong_AsLong(py_apiver) : 0;
Py_XDECREF(py_apiver);
+ PyGILState_Release(gstate);
+
return apiver;
}
long apiver;
int is_subclass;
const char *fail_txt;
+ PyGILState_STATE gstate;
if (!srd_check_init())
return SRD_ERR;
if (!module_name)
return SRD_ERR_ARG;
+ gstate = PyGILState_Ensure();
+
if (PyDict_GetItemString(PyImport_GetModuleDict(), module_name)) {
/* Module was already imported. */
+ PyGILState_Release(gstate);
return SRD_OK;
}
- srd_dbg("Loading protocol decoder '%s'.", module_name);
-
d = g_malloc0(sizeof(struct srd_decoder));
fail_txt = NULL;
* PDs of different API versions are incompatible and cannot work.
*/
apiver = srd_decoder_apiver(d);
- if (apiver != 2 && apiver != 3) {
- srd_exception_catch("Only PD API version 2/3 is supported, "
+ if (apiver != 3) {
+ srd_exception_catch("Only PD API version 3 is supported, "
"decoder %s has version %ld", module_name, apiver);
fail_txt = "API version mismatch";
goto err_out;
goto err_out;
}
+ PyGILState_Release(gstate);
+
/* Append it to the list of loaded decoders. */
pd_list = g_slist_append(pd_list, d);
if (fail_txt)
srd_err("Failed to load decoder %s: %s", module_name, fail_txt);
decoder_free(d);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
{
PyObject *py_str;
char *doc;
+ PyGILState_STATE gstate;
if (!srd_check_init())
return NULL;
if (!dec)
return NULL;
+ gstate = PyGILState_Ensure();
+
if (!PyObject_HasAttrString(dec->py_mod, "__doc__"))
- return NULL;
+ goto err;
if (!(py_str = PyObject_GetAttrString(dec->py_mod, "__doc__"))) {
srd_exception_catch("Failed to get docstring");
- return NULL;
+ goto err;
}
doc = NULL;
py_str_as_str(py_str, &doc);
Py_DECREF(py_str);
+ PyGILState_Release(gstate);
+
return doc;
+
+err:
+ PyGILState_Release(gstate);
+
+ return NULL;
}
/**
if (!dec)
return SRD_ERR_ARG;
- srd_dbg("Unloading protocol decoder '%s'.", dec->name);
-
/*
* Since any instances of this decoder need to be released as well,
* but they could be anywhere in the stack, just free the entire
Py_ssize_t pos = 0;
char *prefix;
size_t prefix_len;
+ PyGILState_STATE gstate;
set = files = prefix_obj = zipimporter = zipimporter_class = NULL;
+ gstate = PyGILState_Ensure();
+
zipimport_mod = py_import_by_name("zipimport");
if (zipimport_mod == NULL)
goto err_out;
Py_XDECREF(zipimporter_class);
Py_XDECREF(zipimport_mod);
PyErr_Clear();
+ PyGILState_Release(gstate);
}
static void srd_decoder_load_all_path(char *path)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 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/>.
+##
+
+'''
+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 communicates full duplex data (SDATA_IN, SDATA_OUT), where bits
+are clocked by the BIT_CLK and frames are signalled by the SYNC signals.
+A low active RESET# line completes the set of signals.
+
+Frames repeat at a nominal frequency of 48kHz, and consist of 256 bits
+each. One 16bit slot contains management information, twelve 20bit slots
+follow which carry data for three management and nine audio/modem channels.
+Optionally two slots of one frame can get combined for higher resolution
+on fewer channels, or double data rate.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 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 implementation is incomplete. TODO items:
+# - Support the optional RESET# pin, detect cold and warm reset.
+# - Split slot values into audio samples of their respective width and
+# frequency (either on user provided parameters, or from inspection of
+# decoded register access).
+
+import sigrokdecode as srd
+
+class ChannelError(Exception):
+ pass
+
+class Pins:
+ (SYNC, BIT_CLK, SDATA_OUT, SDATA_IN, RESET) = range(5)
+
+class Ann:
+ (
+ BITS_OUT, BITS_IN,
+ SLOT_OUT_RAW, SLOT_OUT_TAG, SLOT_OUT_ADDR, SLOT_OUT_DATA,
+ SLOT_OUT_03, SLOT_OUT_04, SLOT_OUT_05, SLOT_OUT_06,
+ SLOT_OUT_07, SLOT_OUT_08, SLOT_OUT_09, SLOT_OUT_10,
+ SLOT_OUT_11, SLOT_OUT_IO,
+ SLOT_IN_RAW, SLOT_IN_TAG, SLOT_IN_ADDR, SLOT_IN_DATA,
+ SLOT_IN_03, SLOT_IN_04, SLOT_IN_05, SLOT_IN_06,
+ SLOT_IN_07, SLOT_IN_08, SLOT_IN_09, SLOT_IN_10,
+ SLOT_IN_11, SLOT_IN_IO,
+ WARN, ERROR,
+ ) = range(32)
+ (
+ BIN_FRAME_OUT,
+ BIN_FRAME_IN,
+ BIN_SLOT_RAW_OUT,
+ BIN_SLOT_RAW_IN,
+ ) = range(4)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ac97'
+ name = "AC '97"
+ longname = "Audio Codec '97"
+ desc = 'Audio and modem control for PC systems.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['ac97']
+ channels = (
+ {'id': 'sync', 'name': 'SYNC', 'desc': 'Frame synchronization'},
+ {'id': 'clk', 'name': 'BIT_CLK', 'desc': 'Data bits clock'},
+ )
+ optional_channels = (
+ {'id': 'out', 'name': 'SDATA_OUT', 'desc': 'Data output'},
+ {'id': 'in', 'name': 'SDATA_IN', 'desc': 'Data input'},
+ {'id': 'rst', 'name': 'RESET#', 'desc': 'Reset line'},
+ )
+ annotations = (
+ ('bit-out', 'Output bits'),
+ ('bit-in', 'Input bits'),
+ ('slot-out-raw', 'Output raw value'),
+ ('slot-out-tag', 'Output TAG'),
+ ('slot-out-cmd-addr', 'Output command address'),
+ ('slot-out-cmd-data', 'Output command data'),
+ ('slot-out-03', 'Output slot 3'),
+ ('slot-out-04', 'Output slot 4'),
+ ('slot-out-05', 'Output slot 5'),
+ ('slot-out-06', 'Output slot 6'),
+ ('slot-out-07', 'Output slot 7'),
+ ('slot-out-08', 'Output slot 8'),
+ ('slot-out-09', 'Output slot 9'),
+ ('slot-out-10', 'Output slot 10'),
+ ('slot-out-11', 'Output slot 11'),
+ ('slot-out-io-ctrl', 'Output I/O control'),
+ ('slot-in-raw', 'Input raw value'),
+ ('slot-in-tag', 'Input TAG'),
+ ('slot-in-sts-addr', 'Input status address'),
+ ('slot-in-sts-data', 'Input status data'),
+ ('slot-in-03', 'Input slot 3'),
+ ('slot-in-04', 'Input slot 4'),
+ ('slot-in-05', 'Input slot 5'),
+ ('slot-in-06', 'Input slot 6'),
+ ('slot-in-07', 'Input slot 7'),
+ ('slot-in-08', 'Input slot 8'),
+ ('slot-in-09', 'Input slot 9'),
+ ('slot-in-10', 'Input slot 10'),
+ ('slot-in-11', 'Input slot 11'),
+ ('slot-in-io-sts', 'Input I/O status'),
+ # TODO: Add more annotation classes:
+ # TAG: 'ready', 'valid', 'id', 'rsv'
+ # CMD ADDR: 'r/w', 'addr', 'unused'
+ # CMD DATA: 'data', 'unused'
+ # 3-11: 'data', 'unused', 'double data'
+ ('warning', 'Warning'),
+ ('error', 'Error'),
+ )
+ annotation_rows = (
+ ('bits-out', 'Output bits', (Ann.BITS_OUT,)),
+ ('slots-out-raw', 'Output numbers', (Ann.SLOT_OUT_RAW,)),
+ ('slots-out', 'Output slots', (
+ Ann.SLOT_OUT_TAG, Ann.SLOT_OUT_ADDR, Ann.SLOT_OUT_DATA,
+ Ann.SLOT_OUT_03, Ann.SLOT_OUT_04, Ann.SLOT_OUT_05, Ann.SLOT_OUT_06,
+ Ann.SLOT_OUT_07, Ann.SLOT_OUT_08, Ann.SLOT_OUT_09, Ann.SLOT_OUT_10,
+ Ann.SLOT_OUT_11, Ann.SLOT_OUT_IO,)),
+ ('bits-in', 'Input bits', (Ann.BITS_IN,)),
+ ('slots-in-raw', 'Input numbers', (Ann.SLOT_IN_RAW,)),
+ ('slots-in', 'Input slots', (
+ Ann.SLOT_IN_TAG, Ann.SLOT_IN_ADDR, Ann.SLOT_IN_DATA,
+ Ann.SLOT_IN_03, Ann.SLOT_IN_04, Ann.SLOT_IN_05, Ann.SLOT_IN_06,
+ Ann.SLOT_IN_07, Ann.SLOT_IN_08, Ann.SLOT_IN_09, Ann.SLOT_IN_10,
+ Ann.SLOT_IN_11, Ann.SLOT_IN_IO,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ ('errors', 'Errors', (Ann.ERROR,)),
+ )
+ binary = (
+ ('frame-out', 'Frame bits, output data'),
+ ('frame-in', 'Frame bits, input data'),
+ ('slot-raw-out', 'Raw slot bits, output data'),
+ ('slot-raw-in', 'Raw slot bits, input data'),
+ # TODO: Which (other) binary classes to implement?
+ # - Are binary annotations per audio slot useful?
+ # - Assume 20bit per slot, in 24bit units? Or assume 16bit
+ # audio samples? Observe register access and derive width
+ # of the audio data? Dump channels 3-11 or 1-12?
+ )
+
+ def putx(self, ss, es, cls, data):
+ self.put(ss, es, self.out_ann, [cls, data])
+
+ def putf(self, frombit, bitcount, cls, data):
+ ss = self.frame_ss_list[frombit]
+ es = self.frame_ss_list[frombit + bitcount]
+ self.putx(ss, es, cls, data)
+
+ def putb(self, frombit, bitcount, cls, data):
+ ss = self.frame_ss_list[frombit]
+ es = self.frame_ss_list[frombit + bitcount]
+ self.put(ss, es, self.out_binary, [cls, data])
+
+ def __init__(self):
+ self.out_binary = None
+ self.out_ann = None
+ self.reset()
+
+ def reset(self):
+ self.frame_ss_list = None
+ self.frame_slot_lens = [0, 16] + [16 + 20 * i for i in range(1, 13)]
+ self.frame_total_bits = self.frame_slot_lens[-1]
+ self.handle_slots = {
+ 0: self.handle_slot_00,
+ 1: self.handle_slot_01,
+ 2: self.handle_slot_02,
+ }
+
+ 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)
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def bits_to_int(self, bits):
+ # Convert MSB-first bit sequence to integer value.
+ if not bits:
+ return 0
+ count = len(bits)
+ value = sum([2 ** (count - 1 - i) for i in range(count) if bits[i]])
+ return value
+
+ def bits_to_bin_ann(self, bits):
+ # Convert MSB-first bit sequence to binary annotation data.
+ # It's assumed that the number of bits does not (in useful ways)
+ # fit into an integer, and we need to create an array of bytes
+ # from the data afterwards, anyway. Hence the separate routine
+ # and the conversion of eight bits each.
+ out = []
+ count = len(bits)
+ while count > 0:
+ count -= 8
+ by, bits = bits[:8], bits[8:]
+ by = self.bits_to_int(by)
+ out.append(by)
+ out = bytes(out)
+ return out
+
+ def int_to_nibble_text(self, value, bitcount):
+ # Convert number to hex digits for given bit count.
+ digits = (bitcount + 3) // 4
+ text = '{{:0{:d}x}}'.format(digits).format(value)
+ return text
+
+ def get_bit_field(self, data, size, off, count):
+ shift = size - off - count
+ data >>= shift
+ mask = (1 << count) - 1
+ data &= mask
+ return data
+
+ def flush_frame_bits(self):
+ # Flush raw frame bits to binary annotation.
+ anncls = Ann.BIN_FRAME_OUT
+ data = self.frame_bits_out[:]
+ count = len(data)
+ data = self.bits_to_bin_ann(data)
+ self.putb(0, count, anncls, data)
+
+ anncls = Ann.BIN_FRAME_IN
+ data = self.frame_bits_in[:]
+ count = len(data)
+ data = self.bits_to_bin_ann(data)
+ self.putb(0, count, anncls, data)
+
+ def start_frame(self, ss):
+ # Mark the start of a frame.
+ if self.frame_ss_list:
+ # Flush bits if we had a frame before the frame which is
+ # starting here.
+ self.flush_frame_bits()
+ self.frame_ss_list = [ss]
+ self.frame_bits_out = []
+ self.frame_bits_in = []
+ self.frame_slot_data_out = []
+ self.frame_slot_data_in = []
+ self.have_slots = {True: None, False: None}
+
+ def handle_slot_dummy(self, slotidx, bitidx, bitcount, is_out, data):
+ # Handle slot x, default/fallback handler.
+ # Only process data of slots 1-12 when slot 0 says "valid".
+ if not self.have_slots[is_out]:
+ return
+ if not self.have_slots[is_out][slotidx]:
+ return
+
+ # Emit a naive annotation with just the data bits that we saw
+ # for the slot (hex nibbles for density). For audio data this
+ # can be good enough. Slots with special meaning should not end
+ # up calling the dummy handler.
+ text = self.int_to_nibble_text(data, bitcount)
+ anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+ self.putf(bitidx, bitcount, anncls + slotidx, [text])
+
+ # Emit binary output for the data that is contained in slots
+ # which end up calling the default handler. This transparently
+ # should translate to "the slots with audio data", as other
+ # slots which contain management data should have their specific
+ # handler routines. In the present form, this approach might be
+ # good enough to get a (header-less) audio stream for typical
+ # setups where only line-in or line-out are in use.
+ #
+ # TODO: Improve this early prototype implementation. For now the
+ # decoder just exports the upper 16 bits of each audio channel
+ # that happens to be valid. For an improved implementation, it
+ # either takes user provided specs or more smarts like observing
+ # register access (if the capture includes it).
+ anncls = Ann.BIN_SLOT_RAW_OUT if is_out else Ann.BIN_SLOT_RAW_IN
+ data_bin = data >> 4
+ data_bin &= 0xffff
+ data_bin = data_bin.to_bytes(2, byteorder = 'big')
+ self.putb(bitidx, bitcount, anncls, data_bin)
+
+ def handle_slot_00(self, slotidx, bitidx, bitcount, is_out, data):
+ # Handle slot 0, TAG.
+ slotpos = self.frame_slot_lens[slotidx]
+ fieldoff = 0
+ anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+
+ fieldlen = 1
+ ready = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ text = ['READY: 1', 'READY', 'RDY', 'R'] if ready else ['ready: 0', 'rdy', '-']
+ self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+ fieldoff += fieldlen
+
+ fieldlen = 12
+ valid = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ text = ['VALID: {:3x}'.format(valid), '{:3x}'.format(valid)]
+ self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+ have_slots = [True] + [False] * 12
+ for idx in range(12):
+ have_slots[idx + 1] = bool(valid & (1 << (11 - idx)))
+ self.have_slots[is_out] = have_slots
+ fieldoff += fieldlen
+
+ fieldlen = 1
+ rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ if rsv != 0:
+ text = ['reserved bit error', 'rsv error', 'rsv']
+ self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+ fieldoff += fieldlen
+
+ # TODO: Will input slot 0 have a Codec ID, or 3 reserved bits?
+ fieldlen = 2
+ codec = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ text = ['CODEC: {:1x}'.format(codec), '{:1x}'.format(codec)]
+ self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+ fieldoff += fieldlen
+
+ def handle_slot_01(self, slotidx, bitidx, bitcount, is_out, data):
+ # Handle slot 1, command/status address.
+ slotpos = self.frame_slot_lens[slotidx]
+ if not self.have_slots[is_out]:
+ return
+ if not self.have_slots[is_out][slotidx]:
+ return
+ fieldoff = 0
+ anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+ anncls += slotidx
+
+ fieldlen = 1
+ if is_out:
+ is_read = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ text = ['READ', 'RD', 'R'] if is_read else ['WRITE', 'WR', 'W']
+ self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+ # TODO: Check for the "atomic" constraint? Some operations
+ # involve address _and_ data, which cannot be spread across
+ # several frames. Slot 0 and 1 _must_ be provided within the
+ # same frame (the test should occur in the handler for slot
+ # 2 of course, in slot 1 we don't know what will follow).
+ else:
+ rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ if rsv != 0:
+ text = ['reserved bit error', 'rsv error', 'rsv']
+ self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+ fieldoff += fieldlen
+
+ fieldlen = 7
+ regaddr = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ # TODO: Present 0-63 or 0-126 as the address of the 16bit register?
+ text = ['ADDR: {:2x}'.format(regaddr), '{:2x}'.format(regaddr)]
+ self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+ if regaddr & 0x01:
+ text = ['odd register address', 'odd reg addr', 'odd addr', 'odd']
+ self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+ fieldoff += fieldlen
+
+ # Strictly speaking there are 10 data request bits and 2 reserved
+ # bits for input slots, and 12 reserved bits for output slots. We
+ # test for 10 and 2 bits, to simplify the logic. Only in case of
+ # non-zero reserved bits for outputs this will result in "a little
+ # strange" an annotation. This is a cosmetic issue, we don't mind.
+ fieldlen = 10
+ reqdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ if is_out and reqdata != 0:
+ text = ['reserved bit error', 'rsv error', 'rsv']
+ self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+ if not is_out:
+ text = ['REQ: {:3x}'.format(reqdata), '{:3x}'.format(reqdata)]
+ self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+ fieldoff += fieldlen
+
+ fieldlen = 2
+ rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ if rsv != 0:
+ text = ['reserved bit error', 'rsv error', 'rsv']
+ self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+ fieldoff += fieldlen
+
+ def handle_slot_02(self, slotidx, bitidx, bitcount, is_out, data):
+ # Handle slot 2, command/status data.
+ slotpos = self.frame_slot_lens[slotidx]
+ if not self.have_slots[is_out]:
+ return
+ if not self.have_slots[is_out][slotidx]:
+ return
+ fieldoff = 0
+ anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+ anncls += slotidx
+
+ fieldlen = 16
+ rwdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ # TODO: Check for zero output data when the operation is a read.
+ # TODO: Check for the "atomic" constraint.
+ text = ['DATA: {:4x}'.format(rwdata), '{:4x}'.format(rwdata)]
+ self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+ fieldoff += fieldlen
+
+ fieldlen = 4
+ rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+ if rsv != 0:
+ text = ['reserved bits error', 'rsv error', 'rsv']
+ self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+ fieldoff += fieldlen
+
+ # TODO: Implement other slots.
+ # - 1: cmd/status addr (check status vs command)
+ # - 2: cmd/status data (check status vs command)
+ # - 3-11: audio out/in
+ # - 12: io control/status (modem GPIO(?))
+
+ def handle_slot(self, slotidx, data_out, data_in):
+ # Process a received slot of a frame.
+ func = self.handle_slots.get(slotidx, self.handle_slot_dummy)
+ bitidx = self.frame_slot_lens[slotidx]
+ bitcount = self.frame_slot_lens[slotidx + 1] - bitidx
+ if data_out is not None:
+ func(slotidx, bitidx, bitcount, True, data_out)
+ if data_in is not None:
+ func(slotidx, bitidx, bitcount, False, data_in)
+
+ def handle_bits(self, ss, es, bit_out, bit_in):
+ # Process a received pair of bits.
+ # Emit the bits' annotations. Only interpret the data when we
+ # are in a frame (have seen the start of the frame, and don't
+ # exceed the expected number of bits in a frame).
+ if bit_out is not None:
+ self.putx(ss, es, Ann.BITS_OUT, ['{:d}'.format(bit_out)])
+ if bit_in is not None:
+ self.putx(ss, es, Ann.BITS_IN, ['{:d}'.format(bit_in)])
+ if self.frame_ss_list is None:
+ return
+ self.frame_ss_list.append(es)
+ have_len = len(self.frame_ss_list) - 1
+ if have_len > self.frame_total_bits:
+ return
+
+ # Accumulate the bits within the frame, until one slot of the
+ # frame has become available.
+ slot_idx = 0
+ if bit_out is not None:
+ self.frame_bits_out.append(bit_out)
+ slot_idx = len(self.frame_slot_data_out)
+ if bit_in is not None:
+ self.frame_bits_in.append(bit_in)
+ slot_idx = len(self.frame_slot_data_in)
+ want_len = self.frame_slot_lens[slot_idx + 1]
+ if have_len != want_len:
+ return
+ prev_len = self.frame_slot_lens[slot_idx]
+
+ # Convert bits to integer values. This shall simplify extraction
+ # of bit fields in multiple other locations.
+ slot_data_out = None
+ if bit_out is not None:
+ slot_bits = self.frame_bits_out[prev_len:]
+ slot_data = self.bits_to_int(slot_bits)
+ self.frame_slot_data_out.append(slot_data)
+ slot_data_out = slot_data
+ slot_data_in = None
+ if bit_in is not None:
+ slot_bits = self.frame_bits_in[prev_len:]
+ slot_data = self.bits_to_int(slot_bits)
+ self.frame_slot_data_in.append(slot_data)
+ slot_data_in = slot_data
+
+ # Emit simple annotations for the integer values, until upper
+ # layer decode stages will be implemented.
+ slot_len = have_len - prev_len
+ slot_ss = self.frame_ss_list[prev_len]
+ slot_es = self.frame_ss_list[have_len]
+ if slot_data_out is not None:
+ slot_text = self.int_to_nibble_text(slot_data_out, slot_len)
+ self.putx(slot_ss, slot_es, Ann.SLOT_OUT_RAW, [slot_text])
+ if slot_data_in is not None:
+ slot_text = self.int_to_nibble_text(slot_data_in, slot_len)
+ self.putx(slot_ss, slot_es, Ann.SLOT_IN_RAW, [slot_text])
+
+ self.handle_slot(slot_idx, slot_data_out, slot_data_in)
+
+ def decode(self):
+ have_sdo = self.has_channel(Pins.SDATA_OUT)
+ have_sdi = self.has_channel(Pins.SDATA_IN)
+ if not have_sdo and not have_sdi:
+ raise ChannelError('Either SDATA_OUT or SDATA_IN (or both) are required.')
+ have_reset = self.has_channel(Pins.RESET)
+
+ # Data is sampled at falling CLK edges. Annotations need to span
+ # the period between rising edges. SYNC rises one cycle _before_
+ # the start of a frame. Grab the earliest SYNC sample we can get
+ # and advance to the start of a bit time. Then keep getting the
+ # samples and the end of all subsequent bit times.
+ prev_sync = [None, None, None]
+ pins = self.wait({Pins.BIT_CLK: 'e'})
+ if pins[Pins.BIT_CLK] == 0:
+ prev_sync[-1] = pins[Pins.SYNC]
+ pins = self.wait({Pins.BIT_CLK: 'r'})
+ bit_ss = self.samplenum
+ while True:
+ pins = self.wait({Pins.BIT_CLK: 'f'})
+ prev_sync.pop(0)
+ prev_sync.append(pins[Pins.SYNC])
+ self.wait({Pins.BIT_CLK: 'r'})
+ if prev_sync[0] == 0 and prev_sync[1] == 1:
+ self.start_frame(bit_ss)
+ self.handle_bits(bit_ss, self.samplenum,
+ pins[Pins.SDATA_OUT] if have_sdo else None,
+ pins[Pins.SDATA_IN] if have_sdi else None)
+ bit_ss = self.samplenum
from .lists import *
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'ade77xx'
name = 'ADE77xx'
longname = 'Analog Devices ADE77xx'
('warnings', 'Warnings', (2,)),
)
- def reset(self):
+ def reset_data(self):
self.expected = 0
self.mosi_bytes, self.miso_bytes = [], []
def __init__(self):
- self.ss_cmd, self.es_cmd = 0, 0
self.reset()
+ def reset(self):
+ self.ss_cmd, self.es_cmd = 0, 0
+ self.reset_data()
+
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
idx = 1 if write else 0
self.putx([idx, ['%s: %s' % (rblob[0], "SHORT")]])
self.put_warn([self.ss_cmd, es], "Short transfer!")
- self.reset()
+ self.reset_data()
return
# Don't care about anything else.
else:
self.putx([0, ['%s: %#x' % (rblob[0], vali)]])
- self.reset()
+ self.reset_data()
ANN_REG = 0
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'adf435x'
name = 'ADF435x'
longname = 'Analog Devices ADF4350/1'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.bits = []
def start(self):
import sigrokdecode as srd
regs = {
- 0: 'Product_ID',
- 1: 'Revision_ID',
- 2: 'Motion',
- 3: 'Delta_X',
- 4: 'Delta_Y',
- 5: 'SQUAL',
- 6: 'Shutter_Upper',
- 7: 'Shutter_Lower',
- 8: 'Maximum_Pixel',
- 9: 'Pixel_Sum',
- 0xa: 'Minimum_Pixel',
- 0xb: 'Pixel_Grab',
- 0xd: 'Mouse_Control',
- 0x3a: 'Chip_Reset',
- 0x3f: 'Inv_Rev_ID',
- 0x63: 'Motion_Burst',
+ 0: 'Product_ID',
+ 1: 'Revision_ID',
+ 2: 'Motion',
+ 3: 'Delta_X',
+ 4: 'Delta_Y',
+ 5: 'SQUAL',
+ 6: 'Shutter_Upper',
+ 7: 'Shutter_Lower',
+ 8: 'Maximum_Pixel',
+ 9: 'Pixel_Sum',
+ 0xa: 'Minimum_Pixel',
+ 0xb: 'Pixel_Grab',
+ 0xd: 'Mouse_Control',
+ 0x3a: 'Chip_Reset',
+ 0x3f: 'Inv_Rev_ID',
+ 0x63: 'Motion_Burst',
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'adns5020'
name = 'ADNS-5020'
longname = 'Avago ADNS-5020 optical mouse sensor'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
def putv(self, data):
self.put(self.bytepos[-2], self.samplenum, self.out_ann, data)
- def reset(self):
+ def reset_variables(self):
self.state = 'WAIT FOR START LOW'
self.fall = 0
self.rise = 0
return checksum % 256
def __init__(self):
- self.samplerate = None
self.reset()
+ def reset(self):
+ self.samplerate = None
+ self.reset_variables()
+
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.rise = self.samplenum
self.state = 'WAIT FOR RESPONSE LOW'
else:
- self.reset()
+ self.reset_variables()
elif self.state == 'WAIT FOR RESPONSE LOW':
self.wait({0: 'f'})
if self.is_valid('START HIGH'):
self.fall = self.samplenum
self.state = 'WAIT FOR RESPONSE HIGH'
else:
- self.reset()
+ self.reset_variables()
elif self.state == 'WAIT FOR RESPONSE HIGH':
self.wait({0: 'r'})
if self.is_valid('RESPONSE LOW'):
self.rise = self.samplenum
self.state = 'WAIT FOR FIRST BIT'
else:
- self.reset()
+ self.reset_variables()
elif self.state == 'WAIT FOR FIRST BIT':
self.wait({0: 'f'})
if self.is_valid('RESPONSE HIGH'):
self.bytepos.append(self.samplenum)
self.state = 'WAIT FOR BIT HIGH'
else:
- self.reset()
+ self.reset_variables()
elif self.state == 'WAIT FOR BIT HIGH':
self.wait({0: 'r'})
if self.is_valid('BIT LOW'):
self.rise = self.samplenum
self.state = 'WAIT FOR BIT LOW'
else:
- self.reset()
+ self.reset_variables()
elif self.state == 'WAIT FOR BIT LOW':
self.wait({0: 'f'})
if self.is_valid('BIT 0 HIGH'):
elif self.is_valid('BIT 1 HIGH'):
bit = 1
else:
- self.reset()
+ self.reset_variables()
continue
self.handle_byte(bit)
elif self.state == 'WAIT FOR END':
self.wait({0: 'r'})
self.putfs([3, ['End', 'E']])
- self.reset()
+ self.reset_variables()
return addr, addrlen, cpu_state, exc_info
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'arm_etmv3'
name = 'ARM ETMv3'
longname = 'ARM Embedded Trace Macroblock'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.buf = []
self.syncbuf = []
self.prevsample = 0
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'arm_itm'
name = 'ARM ITM'
longname = 'ARM Instrumentation Trace Macroblock'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.buf = []
self.syncbuf = []
self.swpackets = {}
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'arm_tpiu'
name = 'ARM TPIU'
longname = 'ARM Trace Port Interface Unit'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.buf = []
self.syncbuf = []
self.prevsample = 0
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ncnt = 0
self.nmax = 0
self.addr = 0
VENDOR_CODE_ATMEL = 0x1e
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'avr_isp'
name = 'AVR ISP'
longname = 'AVR In-System Programming'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.mosi_bytes, self.miso_bytes = [], []
self.ss_cmd, self.es_cmd = 0, 0
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.clear_state()
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.reset_variables()
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
self.bit_width = float(self.samplerate) / float(self.options['bitrate'])
- self.bitpos = (self.bit_width / 100.0) * self.options['sample_point']
+ self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
# Generic helper for CAN bit annotations.
def putg(self, ss, es, data):
- left, right = int(self.bitpos), int(self.bit_width - self.bitpos)
+ left, right = int(self.sample_point), int(self.bit_width - self.sample_point)
self.put(ss - left, es + right, self.out_ann, data)
# Single-CAN-bit annotation using the current samplenum.
self.ss_bit12 = None
self.ss_databytebits = []
+ # 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
+
+ def bit_sampled(self):
+ # EMPTY
+ pass
+
# Determine the position of the next desired bit's sample point.
def get_sample_point(self, bitnum):
- bitpos = int(self.sof + (self.bit_width * bitnum) + self.bitpos)
- return bitpos
+ samplenum = self.dom_edge_snum
+ samplenum += int(self.bit_width * (bitnum - self.dom_edge_bcount))
+ samplenum += int(self.sample_point)
+ return samplenum
def is_stuff_bit(self):
# CAN uses NRZ encoding and bit stuffing.
# After 5 identical bits, a stuff bit of opposite value is added.
# But not in the CRC delimiter, ACK, and end of frame fields.
- if len(self.bits) > self.last_databit + 16:
+ if len(self.bits) > self.last_databit + 17:
return False
last_6_bits = self.rawbits[-6:]
if last_6_bits not in ([0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 0]):
# Wait for a dominant state (logic 0) on the bus.
(can_rx,) = self.wait({0: 'l'})
self.sof = 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)
- (can_rx,) = self.wait({'skip': pos - self.samplenum})
- self.handle_bit(can_rx)
+ (can_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
+ if self.matched[1]:
+ self.dom_edge_seen()
+ if self.matched[0]:
+ self.handle_bit(can_rx)
+ self.bit_sampled()
# Return the specified BCD number (max. 8 bits) as integer.
def bcd2int(b):
return (b & 0x0f) + ((b >> 4) * 10)
+
+def bitpack(bits):
+ res = 0
+ for i, b in enumerate(bits):
+ res |= b << i
+ return res
+
+def bitunpack(num, minbits=0):
+ res = []
+ while num or minbits > 0:
+ res.append(num & 1)
+ num >>= 1
+ minbits -= 1
+ return tuple(res)
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.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 PD is a simple 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).
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.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 Decoder(srd.Decoder):
+ api_version = 3
+ id = 'counter'
+ name = 'Counter'
+ longname = 'Edge counter'
+ desc = 'Count number of edges.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ channels = (
+ {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+ )
+ optional_channels = (
+ {'id': 'reset', 'name': 'Reset', 'desc': 'Reset line'},
+ )
+ annotations = (
+ ('edge_count', 'Edge count'),
+ ('word_count', 'Word count'),
+ ('word_reset', 'Word reset'),
+ )
+ annotation_rows = (
+ ('edge_counts', 'Edges', (0,)),
+ ('word_counts', 'Words', (1,)),
+ ('word_resets', 'Word resets', (2,)),
+ )
+ options = (
+ {'id': 'data_edge', 'desc': 'Edges to count (data)', 'default': 'any',
+ 'values': ('any', 'rising', 'falling')},
+ {'id': 'divider', 'desc': 'Count divider (word width)', 'default': 0},
+ {'id': 'reset_edge', 'desc': 'Edge which clears counters (reset)',
+ 'default': 'falling', 'values': ('rising', 'falling')},
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.edge_count = 0
+ self.word_count = 0
+ self.have_reset = None
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.edge = self.options['data_edge']
+ self.divider = self.options['divider']
+ if self.divider < 0:
+ self.divider = 0
+
+ def putc(self, cls, annlist):
+ self.put(self.samplenum, self.samplenum, self.out_ann, [cls, annlist])
+
+ def decode(self):
+ condition = [{'rising': {0: 'r'},
+ 'falling': {0: 'f'},
+ 'any': {0: 'e'},}[self.edge]]
+
+ if self.has_channel(1):
+ self.have_reset = True
+ condition.append({1: self.options['reset_edge'][0]})
+
+ while True:
+ self.wait(condition)
+ if self.have_reset and self.matched[1]:
+ self.edge_count = 0
+ self.word_count = 0
+ self.putc(2, ['Word reset', 'Reset', 'Rst', 'R'])
+ continue
+
+ self.edge_count += 1
+
+ self.putc(0, [str(self.edge_count)])
+ if self.divider > 0 and (self.edge_count % self.divider) == 0:
+ self.word_count += 1
+ self.putc(1, [str(self.word_count)])
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
-# DALI Extended commands
-extendedCommands = {
+# DALI extended commands
+extended_commands = {
0xA1: ['Terminate special processes', 'Terminate'],
0xA3: ['DTR = DATA', 'DTR'],
0xA5: ['INITIALISE', 'INIT'],
}
# List of commands
-DALICommands = {
+dali_commands = {
0x00: ['Immediate Off', 'IOFF'],
0x01: ['Up 200ms', 'Up'],
0x02: ['Down 200ms', 'Down'],
}
# DALI device type 8
-DALIDeviceType8 = {
+dali_device_type8 = {
0xE0: ['Set Temp X-Y Coordinate', 'Set X-Y'],
0xE2: ['Activate Colour Set point', 'Activate SetPoint'],
0xE7: ['Set Colour Temperature Tc', 'DTRs->ColTemp'],
pass
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'dali'
name = 'DALI'
longname = 'Digital Addressable Lighting Interface'
annotation_rows = (
('bits', 'Bits', (0,)),
('raw', 'Raw data', (7,)),
- ('fields', 'Fields', (1, 2, 3, 4, 5, 6,)),
+ ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
- self.nextSamplePoint = None
- self.nextSample = None
- self.devType = None
+ self.dev_type = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.putb(1, 7, [5, s])
elif f >= 160: # Extended command 0b10100000
if f == 0xC1: # DALI_ENABLE_DEVICE_TYPE_X
- self.devType = -1
- x = extendedCommands.get(f, ['Unknown', 'Unk'])
+ self.dev_type = -1
+ x = extended_commands.get(f, ['Unknown', 'Unk'])
s = ['Extended Command: %02X (%s)' % (f, x[0]),
'XC: %02X (%s)' % (f, x[1]),
'XC: %02X' % f, 'X: %02X' % f, 'X']
s = ['YBit: %d' % b[1][1], 'YB: %d' % b[1][1], 'YB', 'Y', 'Y']
self.putb(1, 1, [3, s])
a = f >> 1
- # x = system.get(a, ['Unknown', 'Unk'])
s = ['Short address: %d' % a, 'Addr: %d' % a,
'Addr: %d' % a, 'A: %d' % a, 'A']
self.putb(2, 7, [4, s])
# Bits[9:16]: Command/data (MSB-first)
if f >= 160 and f < 254:
- if self.devType == -1:
- self.devType = c
+ if self.dev_type == -1:
+ self.dev_type = c
s = ['Type: %d' % c, 'Typ: %d' % c,
'Typ: %d' % c, 'T: %d' % c, 'D']
else:
- self.devType = None
+ self.dev_type = None
s = ['Data: %d' % c, 'Dat: %d' % c,
'Dat: %d' % c, 'D: %d' % c, 'D']
elif b[8][1] == 1:
elif un == 0xB0:
x = ['Query Scene %d Level' % ln, 'Sc %d Level' % ln]
elif c >= 224: # Application specific commands
- if self.devType == 8:
- x = DALIDeviceType8.get(c, ['Unknown App', 'Unk'])
+ if self.dev_type == 8:
+ x = dali_device_type8.get(c, ['Unknown App', 'Unk'])
else:
x = ['Application Specific Command %d' % c, 'App Cmd %d' % c]
else:
- x = DALICommands.get(c, ['Unknown', 'Unk'])
+ x = dali_commands.get(c, ['Unknown', 'Unk'])
s = ['Command: %d (%s)' % (c, x[0]), 'Com: %d (%s)' % (c, x[1]),
'Com: %d' % c, 'C: %d' % c, 'C']
else:
def reset_decoder_state(self):
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
- # self.devType = None
- def decode(self, ss, es, data):
+ def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- bit = 0;
- for (self.samplenum, pins) in data:
- self.dali = pins[0]
- # data.itercnt += 1
- # data.logic_mask = 1
- # data.cur_pos = self.samplenum
- # data.edge_index = -1
+ bit = 0
+ while True:
+ # TODO: Come up with more appropriate self.wait() conditions.
+ (dali,) = self.wait()
if self.options['polarity'] == 'active-high':
- self.dali ^= 1 # Invert.
+ dali ^= 1 # Invert.
# State machine.
if self.state == 'IDLE':
# Wait for any edge (rising or falling).
- if self.old_dali == self.dali:
- # data.exp_logic = self.exp_logic
- # data.logic_mask = 1
- # logic.cur_pos = self.samplenum
+ if self.old_dali == dali:
continue
self.edges.append(self.samplenum)
self.state = 'PHASE0'
- self.old_dali = self.dali
- # Get the next sample point.
- # self.nextSamplePoint = self.samplenum + int(self.halfbit / 2)
- self.old_dali = self.dali
- # bit = self.dali
- # data.itercnt += int((self.halfbit - 1) * 0.5)
+ self.old_dali = dali
continue
- # if(self.samplenum == self.nextSamplePoint):
- # bit = self.dali
- # continue
-
- if (self.old_dali != self.dali):
+ if self.old_dali != dali:
self.edges.append(self.samplenum)
- elif (self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5))):
+ elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)):
self.edges.append(self.samplenum - int(self.halfbit * 0.5))
else:
continue
self.phase0 = bit
self.state = 'PHASE1'
elif self.state == 'PHASE1':
- if (bit == 1) and (self.phase0 == 1): # Stop bit
+ if (bit == 1) and (self.phase0 == 1): # Stop bit.
if len(self.bits) == 17 or len(self.bits) == 9:
- # Forward or Backward
+ # Forward or Backward.
self.handle_bits(len(self.bits))
self.reset_decoder_state() # Reset upon errors.
continue
self.bits.append([self.edges[-3], bit])
self.state = 'PHASE0'
- # self.nextSamplePoint = self.edges[-1] + int(self.halfbit / 2)
-
- self.old_dali = self.dali
+ self.old_dali = dali
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.state = 'WAIT FOR RISING EDGE'
self.ss_bit = self.ss_bit_old = self.es_bit = self.ss_block = 0
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'dmx512'
name = 'DMX512'
longname = 'Digital MultipleX 512'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.sample_usec = None
- self.samplenum = -1
self.run_start = -1
self.run_bit = 0
self.state = 'FIND BREAK'
def putr(self, data):
self.put(self.run_start, self.samplenum, self.out_ann, data)
- def decode(self, ss, es, data):
+ def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- for (self.samplenum, pins) in data:
+ while True:
# Seek for an interval with no state change with a length between
# 88 and 1000000 us (BREAK).
if self.state == 'FIND BREAK':
- if self.run_bit == pins[0]:
- continue
+ (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
runlen = (self.samplenum - self.run_start) * self.sample_usec
if runlen > 88 and runlen < 1000000:
self.putr([1, ['Break']])
elif runlen >= 1000000:
# Error condition.
self.putr([10, ['Invalid break length']])
- self.run_bit = pins[0]
+ self.run_bit = dmx
self.run_start = self.samplenum
# Directly following the BREAK is the MARK AFTER BREAK.
elif self.state == 'MARK MAB':
- if self.run_bit == pins[0]:
- continue
+ (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
self.putr([2, ['MAB']])
self.state = 'READ BYTE'
self.channel = 0
self.bit = 0
- self.aggreg = pins[0]
+ self.aggreg = dmx
self.run_start = self.samplenum
# Mark and read a single transmitted byte
# (start bit, 8 data bits, 2 stop bits).
elif self.state == 'READ BYTE':
+ (dmx,) = self.wait()
self.next_sample = self.run_start + (self.bit + 1) * self.skip_per_bit
- self.aggreg += pins[0]
+ self.aggreg += dmx
if self.samplenum != self.next_sample:
continue
bit_value = 0 if round(self.aggreg/self.skip_per_bit) == self.bit_break else 1
self.out_ann, [10, ['Invalid stop bit']])
if self.bit == 10:
# On invalid 2nd stop bit, search for new break.
- self.run_bit = pins[0]
+ self.run_bit = dmx
self.state = 'FIND BREAK'
else:
# Label and process one bit.
# Continue by scanning the IFT.
self.channel += 1
self.run_start = self.samplenum
- self.run_bit = pins[0]
+ self.run_bit = dmx
self.state = 'MARK IFT'
- self.aggreg = pins[0]
+ self.aggreg = dmx
self.bit += 1
# Mark the INTERFRAME-TIME between bytes / INTERPACKET-TIME between packets.
elif self.state == 'MARK IFT':
- if self.run_bit == pins[0]:
- continue
+ (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
if self.channel > 512:
self.putr([8, ['Interpacket']])
self.state = 'FIND BREAK'
- self.run_bit = pins[0]
+ self.run_bit = dmx
self.run_start = self.samplenum
else:
self.putr([7, ['Interframe']])
return tuple(l)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'ds1307'
name = 'DS1307'
longname = 'Dallas DS1307'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.hours = -1
self.minutes = -1
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
+##
+## 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 DS243x (1-Wire EEPROM) protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
+## Copyright (C) 2017 Soeren Apel <soeren@apelpie.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.
+commands_2432 = {
+ 0x0f: 'Write scratchpad',
+ 0xaa: 'Read scratchpad',
+ 0x55: 'Copy scratchpad',
+ 0xf0: 'Read memory',
+ 0x5a: 'Load first secret',
+ 0x33: 'Compute next secret',
+ 0xa5: 'Read authenticated page',
+}
+
+commands_2433 = {
+ 0x0f: 'Write scratchpad',
+ 0xaa: 'Read scratchpad',
+ 0x55: 'Copy scratchpad',
+ 0xf0: 'Read memory',
+}
+
+# Maxim DS243x family code, present at the end of the ROM code.
+family_codes = {
+ 0x33: ('DS2432', commands_2432),
+ 0x23: ('DS2433', commands_2433),
+}
+
+# Calculate the CRC-16 checksum.
+# Initial value: 0x0000, xor-in: 0x0000, polynom 0x8005, xor-out: 0xffff.
+def crc16(byte_array):
+ reverse = 0xa001 # Use the reverse polynom to make algo simpler.
+ crc = 0x0000 # Initial value.
+ # Reverse CRC calculation.
+ for byte in byte_array:
+ for bit in range(8):
+ if (byte ^ crc) & 1:
+ crc = (crc >> 1) ^ reverse
+ else:
+ crc >>= 1
+ byte >>= 1
+ crc ^= 0xffff # Invert CRC.
+ return crc
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'ds243x'
+ name = 'DS243x'
+ longname = 'Maxim DS2432/2433'
+ desc = 'Maxim DS243x series 1-Wire EEPROM protocol.'
+ license = 'gplv2+'
+ inputs = ['onewire_network']
+ outputs = ['ds243x']
+ annotations = (
+ ('text', 'Human-readable text'),
+ )
+ binary = (
+ ('mem_read', 'Data read from memory'),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ # Bytes for function command.
+ self.bytes = []
+ self.family_code = None
+ self.family = ''
+ self.commands = commands_2432 # Use max command set until we know better.
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+ 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
+ self.family_code = val & 0xff
+
+ s = None
+ if self.family_code in family_codes:
+ 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)
+
+ self.putx([0, ['ROM: 0x%016x (%s)' % (val, 'family code ' + s),
+ 'ROM: 0x%016x (%s)' % (val, self.family)]])
+ self.bytes = []
+ elif code == 'DATA':
+ self.bytes.append(val)
+ if 1 == len(self.bytes):
+ self.ss, self.es = ss, es
+ if val not in self.commands:
+ self.putx([0, ['Unrecognized command: 0x%02x' % val]])
+ else:
+ self.putx([0, ['Function command: %s (0x%02x)'
+ % (self.commands[val], val)]])
+ elif 0x0f == self.bytes[0]: # Write scratchpad
+ 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 4 == len(self.bytes):
+ self.ss = ss
+ elif 11 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
+ for n in self.bytes[3:11]))]])
+ elif 12 == len(self.bytes):
+ self.ss = ss
+ elif 13 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['CRC: '
+ + ('ok' if crc16(self.bytes[0:11]) == (self.bytes[11]
+ + (self.bytes[12] << 8)) else 'error')]])
+ elif 0xaa == self.bytes[0]: # Read scratchpad
+ 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 4 == len(self.bytes):
+ self.ss, self.es = ss, es
+ self.putx([0, ['Data status (E/S): 0x%02x'
+ % (self.bytes[3])]])
+ elif 5 == len(self.bytes):
+ self.ss = ss
+ elif 12 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
+ for n in self.bytes[4:12]))]])
+ elif 13 == len(self.bytes):
+ self.ss = ss
+ elif 14 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['CRC: '
+ + ('ok' if crc16(self.bytes[0:12]) == (self.bytes[12]
+ + (self.bytes[13] << 8)) else 'error')]])
+ elif 0x5a == self.bytes[0]: # Load first secret
+ if 2 == len(self.bytes):
+ self.ss = ss
+ elif 4 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['Authorization pattern (TA1, TA2, E/S): '
+ + (','.join(format(n, '#04x')
+ for n in self.bytes[1:4]))]])
+ elif 4 < len(self.bytes):
+ self.ss, self.es = ss, es
+ if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+ self.putx([0, ['End of operation']])
+ elif 0x33 == self.bytes[0]: # Compute next secret
+ 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
+ if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+ self.putx([0, ['End of operation']])
+ elif 0x55 == self.bytes[0]: # Copy scratchpad
+ if 2 == len(self.bytes):
+ self.ss = ss
+ elif 4 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['Authorization pattern (TA1, TA2, E/S): '
+ + (','.join(format(n, '#04x')
+ for n in self.bytes[1:4]))]])
+ elif 5 == len(self.bytes):
+ self.ss = ss
+ elif 24 == len(self.bytes):
+ self.es = es
+ mac = ','.join(format(n, '#04x') for n in self.bytes[4:24])
+ self.putx([0, ['Message authentication code: ' + mac,
+ 'MAC: ' + mac]])
+ elif 24 < len(self.bytes):
+ self.ss, self.es = ss, es
+ if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+ self.putx([0, ['Operation succeeded']])
+ elif (0 == self.bytes[-1]):
+ self.putx([0, ['Operation failed']])
+ elif 0xa5 == self.bytes[0]: # Read authenticated page
+ 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 4 == len(self.bytes):
+ self.ss = ss
+ elif 35 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
+ for n in self.bytes[3:35]))]])
+ elif 36 == len(self.bytes):
+ self.ss, self.es = ss, es
+ self.putx([0, ['Padding: '
+ + ('ok' if 0xff == self.bytes[-1] else 'error')]])
+ elif 37 == len(self.bytes):
+ self.ss = ss
+ elif 38 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['CRC: '
+ + ('ok' if crc16(self.bytes[0:36]) == (self.bytes[36]
+ + (self.bytes[37] << 8)) else 'error')]])
+ elif 39 == len(self.bytes):
+ self.ss = ss
+ elif 58 == len(self.bytes):
+ self.es = es
+ mac = ','.join(format(n, '#04x') for n in self.bytes[38:58])
+ self.putx([0, ['Message authentication code: ' + mac,
+ 'MAC: ' + mac]])
+ elif 59 == len(self.bytes):
+ self.ss = ss
+ elif 60 == len(self.bytes):
+ self.es = es
+ self.putx([0, ['MAC CRC: '
+ + ('ok' if crc16(self.bytes[38:58]) == (self.bytes[58]
+ + (self.bytes[59] << 8)) else 'error')]])
+ elif 60 < len(self.bytes):
+ self.ss, self.es = ss, es
+ if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+ self.putx([0, ['Operation completed']])
+ elif 0xf0 == self.bytes[0]: # Read memory
+ 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])]])
+
+ bdata = self.bytes[-1].to_bytes(1, byteorder='big')
+ self.put(ss, es, self.out_binary, [0, bdata])
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'ds28ea00'
name = 'DS28EA00'
longname = 'Maxim DS28EA00 1-Wire digital thermometer'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.trn_beg = 0
self.trn_end = 0
self.state = 'ROM'
pass
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'dsi'
name = 'DSI'
longname = 'Digital Serial Interface'
)
annotations = (
('bit', 'Bit'),
- ('startbit', 'Startbit'),
- ('Level', 'Dimmer level'),
+ ('startbit', 'Start bit'),
+ ('level', 'Dimmer level'),
('raw', 'Raw data'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
- ('raw', 'Raw Data',(3,)),
- ('fields', 'Fields', (1, 2,)),
+ ('raw', 'Raw data', (3,)),
+ ('fields', 'Fields', (1, 2)),
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
- self.nextSamplePoint = None
- self.nextSample = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
- def decode(self, ss, es, data):
+ def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- bit = 0;
- for (self.samplenum, pins) in data:
- self.dsi = pins[0]
- # data.itercnt += 1
+ bit = 0
+ while True:
+ (self.dsi,) = self.wait()
if self.options['polarity'] == 'active-high':
self.dsi ^= 1 # Invert.
self.state = 'PHASE1'
self.old_dsi = self.dsi
# Get the next sample point.
- # self.nextSamplePoint = self.samplenum + int(self.halfbit / 2)
self.old_dsi = self.dsi
- # bit = self.dsi
continue
- # if(self.samplenum == self.nextSamplePoint):
- # bit = self.dsi
- # continue
-
if self.old_dsi != self.dsi:
self.edges.append(self.samplenum)
elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)):
self.phase0 = bit
self.state = 'PHASE1'
elif self.state == 'PHASE1':
- if (bit == 1) and (self.phase0 == 1): # Stop bit
+ if (bit == 1) and (self.phase0 == 1): # Stop bit.
if len(self.bits) == 17 or len(self.bits) == 9:
- # Forward or Backward
+ # Forward or Backward.
self.handle_bits(len(self.bits))
self.reset_decoder_state() # Reset upon errors.
continue
self.bits.append([self.edges[-3], bit])
self.state = 'PHASE0'
- # self.nextSamplePoint = self.edges[-1] + int(self.halfbit / 2)
-
self.old_dsi = self.dsi
ANN_SECTIONS = 1
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'edid'
name = 'EDID'
longname = 'Extended Display Identification Data'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = None
# Received data items, used as an index into samplenum/data
self.cnt = 0
from .lists import *
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'eeprom24xx'
name = '24xx EEPROM'
longname = '24xx I²C EEPROM'
def __init__(self):
self.reset()
+ def reset(self):
+ self.reset_variables()
+
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
def putbits(self, bit1, bit2, bits, data):
self.put(bits[bit1][1], bits[bit2][2], self.out_ann, data)
- def reset(self):
+ def reset_variables(self):
self.state = 'WAIT FOR START'
self.packets = []
self.bytebuf = []
def decide_on_seq_or_rnd_read(self):
if len(self.bytebuf) < 2:
- self.reset()
+ self.reset_variables()
return
if len(self.bytebuf) == 2:
self.is_random_access_read = True
def handle_get_control_word(self):
# The packet after START must be an ADDRESS READ or ADDRESS WRITE.
if self.cmd not in ('ADDRESS READ', 'ADDRESS WRITE'):
- self.reset()
+ self.reset_variables()
return
self.packet_append()
self.put_control_word(self.bits)
elif self.cmd == 'NACK':
self.es_block = self.es
self.putb([0, ['Warning: No reply from slave!']])
- self.reset()
+ self.reset_variables()
else:
- self.reset()
+ self.reset_variables()
def handle_r_get_word_addr_or_byte(self):
if self.cmd == 'STOP':
self.es_block = self.es
self.putb([0, ['Warning: Slave replied, but master aborted!']])
- self.reset()
+ self.reset_variables()
return
elif self.cmd != 'DATA READ':
- self.reset()
+ self.reset_variables()
return
self.packet_append()
self.state = 'R GET ACK NACK AFTER WORD ADDR OR BYTE'
self.is_cur_addr_read = True
self.state = 'GET STOP AFTER LAST BYTE'
else:
- self.reset()
+ self.reset_variables()
def handle_r_get_restart(self):
if self.cmd == 'RESTART':
self.state = 'R READ BYTE'
else:
- self.reset()
+ self.reset_variables()
def handle_r_read_byte(self):
if self.cmd == 'DATA READ':
self.packet_append()
self.state = 'R GET ACK NACK AFTER BYTE WAS READ'
else:
- self.reset()
+ self.reset_variables()
def handle_r_get_ack_nack_after_byte_was_read(self):
if self.cmd == 'ACK':
# It's either a RANDOM READ or a SEQUENTIAL READ.
self.state = 'GET STOP AFTER LAST BYTE'
else:
- self.reset()
+ self.reset_variables()
def handle_w_get_ack_nack_after_control_word(self):
if self.cmd == 'ACK':
elif self.cmd == 'NACK':
self.es_block = self.es
self.putb([0, ['Warning: No reply from slave!']])
- self.reset()
+ self.reset_variables()
else:
- self.reset()
+ self.reset_variables()
def handle_w_get_word_addr(self):
if self.cmd == 'STOP':
self.es_block = self.es
self.putb([0, ['Warning: Slave replied, but master aborted!']])
- self.reset()
+ self.reset_variables()
return
elif self.cmd != 'DATA WRITE':
- self.reset()
+ self.reset_variables()
return
self.packet_append()
self.state = 'W GET ACK AFTER WORD ADDR'
if self.cmd == 'ACK':
self.state = 'W DETERMINE EEPROM READ OR WRITE'
else:
- self.reset()
+ self.reset_variables()
def handle_w_determine_eeprom_read_or_write(self):
if self.cmd == 'START REPEAT':
self.packet_append()
self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
else:
- self.reset()
+ self.reset_variables()
def handle_w_write_byte(self):
if self.cmd == 'DATA WRITE':
self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
elif self.cmd == 'STOP':
if len(self.bytebuf) < 2:
- self.reset()
+ self.reset_variables()
return
self.es_block = self.es
if len(self.bytebuf) == 2:
else:
self.is_page_write = True
self.put_operation()
- self.reset()
+ self.reset_variables()
elif self.cmd == 'START REPEAT':
# It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ.
self.state = 'R2 GET CONTROL WORD'
else:
- self.reset()
+ self.reset_variables()
def handle_w_get_ack_nack_after_byte_was_written(self):
if self.cmd == 'ACK':
self.state = 'W WRITE BYTE'
else:
- self.reset()
+ self.reset_variables()
def handle_r2_get_control_word(self):
if self.cmd == 'ADDRESS READ':
self.packet_append()
self.state = 'R2 GET ACK AFTER ADDR READ'
else:
- self.reset()
+ self.reset_variables()
def handle_r2_get_ack_after_addr_read(self):
if self.cmd == 'ACK':
self.state = 'R2 READ BYTE'
else:
- self.reset()
+ self.reset_variables()
def handle_r2_read_byte(self):
if self.cmd == 'DATA READ':
self.es_block = self.es
self.putb([0, ['Warning: STOP expected after a NACK (not ACK)']])
self.put_operation()
- self.reset()
+ self.reset_variables()
else:
- self.reset()
+ self.reset_variables()
def handle_r2_get_ack_nack_after_byte_was_read(self):
if self.cmd == 'ACK':
self.decide_on_seq_or_rnd_read()
self.state = 'GET STOP AFTER LAST BYTE'
else:
- self.reset()
+ self.reset_variables()
def handle_get_stop_after_last_byte(self):
if self.cmd == 'STOP':
self.es_block = self.es
self.put_operation()
- self.reset()
+ self.reset_variables()
elif self.cmd == 'START REPEAT':
self.es_block = self.es
self.putb([0, ['Warning: STOP expected (not RESTART)']])
self.put_operation()
- self.reset()
+ self.reset_variables()
self.ss_block = self.ss
self.state = 'GET CONTROL WORD'
else:
- self.reset()
+ self.reset_variables()
def decode(self, ss, es, data):
self.cmd, self.databyte = data
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'eeprom93xx'
name = '93xx EEPROM'
longname = '93xx Microwire EEPROM'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.frame = []
def start(self):
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.oldpin = None
self.last_samplenum = None
raise SamplerateError('Cannot decode without samplerate.')
# Initialize internal state from the very first sample.
- (pin,) = self.wait({'skip': 1})
+ (pin,) = self.wait()
self.oldpin = pin
self.last_samplenum = self.samplenum
self.lastlast_samplenum = self.samplenum
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.last_samplenum = None
self.state = 'FFS_SEARCH'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.items = []
self.itemcount = 0
self.saved_item = None
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.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/>.
+##
+
+'''
+Gray code and rotary encoder protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.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 math
+import sigrokdecode as srd
+from collections import deque
+from common.srdhelper import bitpack, bitunpack
+
+def gray_encode(plain):
+ return plain & (plain >> 1)
+
+def gray_decode(gray):
+ temp = gray
+ temp ^= (temp >> 8)
+ temp ^= (temp >> 4)
+ temp ^= (temp >> 2)
+ temp ^= (temp >> 1)
+ return temp
+
+def prefix_fmt(value, emin=None):
+ sgn = (value > 0) - (value < 0)
+ value = abs(value)
+ p = math.log10(value) if value else 0
+ value = sgn * math.floor(value * 10**int(3 - p)) * 10**-int(3 - p)
+ e = p // 3 * 3
+ if emin is not None and e < emin:
+ e = emin
+ value *= 10**-e
+ p -= e
+ decimals = 2 - int(p)
+ prefixes = {-9: 'n', -6: 'µ', -3: 'm', 0: '', 3: 'k', 6: 'M', 9: 'G'}
+ return '{0:.{1}f} {2}'.format(value, decimals, prefixes[e])
+
+class ChannelMapError(Exception):
+ pass
+
+class Value:
+ def __init__(self, onchange):
+ self.onchange = onchange
+ self.timestamp = None
+ self.value = None
+
+ def get(self):
+ return self.value
+
+ def set(self, timestamp, newval):
+ if newval != self.value:
+ if self.value is not None:
+ self.onchange(self.timestamp, self.value, timestamp, newval)
+
+ self.value = newval
+ self.timestamp = timestamp
+ elif False:
+ if self.value is not None:
+ self.onchange(self.timestamp, self.value, timestamp, newval)
+
+MAX_CHANNELS = 8 # 10 channels causes some weird problems...
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'graycode'
+ name = 'Gray code'
+ longname = 'Gray code and rotary encoder'
+ desc = 'Accumulate rotary encoder increments, provide timing statistics.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['graycode']
+ optional_channels = tuple(
+ {'id': 'd{}'.format(i), 'name': 'D{}'.format(i), 'desc': 'Data line {}'.format(i)}
+ for i in range(MAX_CHANNELS)
+ )
+ options = (
+ {'id': 'edges', 'desc': 'Edges per rotation', 'default': 0},
+ {'id': 'avg_period', 'desc': 'Averaging period', 'default': 10},
+ )
+ annotations = (
+ ('phase', 'Phase'),
+ ('increment', 'Increment'),
+ ('count', 'Count'),
+ ('turns', 'Turns'),
+ ('interval', 'Interval'),
+ ('average', 'Average'),
+ ('rpm', 'Rate'),
+ )
+ annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.num_channels = 0
+ self.samplerate = None
+ self.last_n = deque()
+
+ self.phase = Value(self.on_phase)
+ self.increment = Value(self.on_increment)
+ self.count = Value(self.on_count)
+ self.turns = Value(self.on_turns)
+
+ def on_phase(self, told, vold, tnew, vnew):
+ self.put(told, tnew, self.out_ann, [0, ['{}'.format(vold)]])
+
+ def on_increment(self, told, vold, tnew, vnew):
+ if vold == 0:
+ message = '0'
+ elif abs(vold) == self.ENCODER_STEPS // 2:
+ message = '±π'
+ else:
+ message = '{:+d}'.format(vold)
+ self.put(told, tnew, self.out_ann, [1, [message]])
+
+ def on_count(self, told, vold, tnew, vnew):
+ self.put(told, tnew, self.out_ann, [2, ['{}'.format(vold)]])
+
+ def on_turns(self, told, vold, tnew, vnew):
+ self.put(told, tnew, self.out_ann, [3, ['{:+d}'.format(vold)]])
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def decode(self):
+ chmask = [self.has_channel(i) for i in range(MAX_CHANNELS)]
+ self.num_channels = sum(chmask)
+ if chmask != [i < self.num_channels for i in range(MAX_CHANNELS)]:
+ raise ChannelMapError('Assigned channels need to be contiguous')
+
+ self.ENCODER_STEPS = 1 << self.num_channels
+
+ startbits = self.wait()
+ curtime = self.samplenum
+
+ self.turns.set(self.samplenum, 0)
+ self.count.set(self.samplenum, 0)
+ self.phase.set(self.samplenum, gray_decode(bitpack(startbits[:self.num_channels])))
+
+ while True:
+ prevtime = curtime
+ bits = self.wait([{i: 'e'} for i in range(self.num_channels)])
+ curtime = self.samplenum
+
+ oldcount = self.count.get()
+ oldphase = self.phase.get()
+
+ newphase = gray_decode(bitpack(bits[:self.num_channels]))
+ self.phase.set(self.samplenum, newphase)
+
+ phasedelta_raw = (newphase - oldphase + (self.ENCODER_STEPS // 2 - 1)) % self.ENCODER_STEPS - (self.ENCODER_STEPS // 2 - 1)
+ phasedelta = phasedelta_raw
+ self.increment.set(self.samplenum, phasedelta)
+ if abs(phasedelta) == self.ENCODER_STEPS // 2:
+ phasedelta = 0
+
+ self.count.set(self.samplenum, self.count.get() + phasedelta)
+
+ if self.options['edges']:
+ self.turns.set(self.samplenum, self.count.get() // self.options['edges'])
+
+ if self.samplerate:
+ period = (curtime - prevtime) / self.samplerate
+ freq = abs(phasedelta_raw) / period
+
+ self.put(prevtime, curtime, self.out_ann, [4, [
+ '{}s, {}Hz'.format(prefix_fmt(period), prefix_fmt(freq))]])
+
+ if self.options['avg_period']:
+ self.last_n.append((abs(phasedelta_raw), period))
+ if len(self.last_n) > self.options['avg_period']:
+ self.last_n.popleft()
+
+ avg_period = sum(v for u, v in self.last_n) / (sum(u for u, v in self.last_n) or 1)
+ self.put(prevtime, curtime, self.out_ann, [5, [
+ '{}s, {}Hz'.format(prefix_fmt(avg_period),
+ prefix_fmt(1 / avg_period))]])
+
+ if self.options['edges']:
+ self.put(prevtime, curtime, self.out_ann, [6, ['{}rpm'.format(prefix_fmt(60 * freq / self.options['edges'], emin=0))]])
self.put(self.ss_edge, self.samplenum, self.out_ann, data)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ss_edge = None
- self.first_transition = True
- self.bitwidth = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
+ # Get the first edge on the data line.
+ self.wait({0: 'e'})
+ self.ss_edge = self.samplenum
+
+ # Get any subsequent edge on the data line. Get the smallest
+ # distance between any two transitions, assuming it corresponds
+ # to one bit time of the respective bitrate of the input stream.
+ # This heuristics keeps getting better for longer captures.
+ bitwidth = None
while True:
- # Wait for any transition/edge on the data line.
self.wait({0: 'e'})
- # Get the smallest distance between two transitions
- # and use that to calculate the bitrate/baudrate.
- if self.first_transition:
- self.ss_edge = self.samplenum
- self.first_transition = False
- else:
- b = self.samplenum - self.ss_edge
- if self.bitwidth is None or b < self.bitwidth:
- self.bitwidth = b
- bitrate = int(float(self.samplerate) / float(b))
- self.putx([0, ['%d' % bitrate]])
- self.ss_edge = self.samplenum
+ b = self.samplenum - self.ss_edge
+ if bitwidth is None or b < bitwidth:
+ bitwidth = b
+ bitrate = int(float(self.samplerate) / float(b))
+ self.putx([0, ['%d' % bitrate]])
+ self.ss_edge = self.samplenum
##
# TODO: Look into arbitration, collision detection, clock synchronisation, etc.
-# TODO: Implement support for 10bit slave addresses.
# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
# TODO: Implement support for detecting various bus errors.
'DATA WRITE': [9, 'Data write', 'DW'],
}
-class SamplerateError(Exception):
- pass
-
class Decoder(srd.Decoder):
api_version = 3
id = 'i2c'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.ss = self.es = self.ss_byte = -1
self.bitcount = 0
def handle_stop(self, pins):
# Meta bitrate
- elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
- bitrate = int(1 / elapsed * self.pdu_bits)
- self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
+ if self.samplerate:
+ elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
+ bitrate = int(1 / elapsed * self.pdu_bits)
+ self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
cmd = 'STOP'
self.ss, self.es = self.samplenum, self.samplenum
self.bits = []
def decode(self):
- if not self.samplerate:
- raise SamplerateError('Cannot decode without samplerate.')
-
- self.wait({})
-
while True:
# State machine.
if self.state == 'FIND START':
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'i2cdemux'
name = 'I²C demux'
longname = 'I²C demultiplexer'
outputs = [] # TODO: Only known at run-time.
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.packets = [] # Local cache of I²C packets
self.slaves = [] # List of known slave addresses
self.stream = -1 # Current output stream
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'i2cfilter'
name = 'I²C filter'
longname = 'I²C filter'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.curslave = -1
self.curdirection = None
self.packets = [] # Local cache of I²C packets
##
import sigrokdecode as srd
+import struct
'''
OUTPUT_PYTHON format:
<value>: integer
'''
-class SamplerateError(Exception):
- pass
-
class Decoder(srd.Decoder):
api_version = 3
id = 'i2s'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.oldws = 1
self.bitcount = 0
self.put(self.ss_block, self.samplenum, self.out_ann, data)
def report(self):
-
# Calculate the sample rate.
samplerate = '?'
if self.ss_block is not None and \
self.first_sample is not None and \
- self.ss_block > self.first_sample:
+ self.ss_block > self.first_sample and \
+ self.samplerate:
samplerate = '%d' % (self.samplesreceived *
self.samplerate / (self.ss_block -
self.first_sample))
h += b'\x01\x00' # Audio format (0x0001 == PCM)
h += b'\x02\x00' # Number of channels (2)
h += b'\x80\x3e\x00\x00' # Samplerate (16000)
- h += b'\x00\x7d\x00\x00' # Byterate (32000)
+ h += b'\x00\xfa\x00\x00' # Byterate (64000)
h += b'\x04\x00' # Blockalign (4)
- h += b'\x10\x00' # Bits per sample (16)
+ h += b'\x20\x00' # Bits per sample (32)
# Data subchunk
h += b'data'
- h += b'\xff\xff\x00\x00' # Subchunk size (65535 bytes) TODO
+ h += b'\xff\xff\xff\xff' # Subchunk size (4G bytes) TODO
return h
def wav_sample(self, sample):
- # TODO: This currently assumes U32 samples, and converts to S16.
- s = sample >> 16
- if s >= 0x8000:
- s -= 0x10000
- lo, hi = s & 0xff, (s >> 8) & 0xff
- return bytes([lo, hi])
+ return struct.pack('<I', self.data)
def decode(self):
- if not self.samplerate:
- raise SamplerateError('Cannot decode without samplerate.')
while True:
# Wait for a rising edge on the SCK pin.
sck, ws, sd = self.wait({0: 'r'})
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.saved_ATN = False
self.saved_EOI = False
self.ss_item = self.es_item = None
options = (
{'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
'values': ('active-low', 'active-high')},
+ {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
)
annotations = (
('bit', 'Bit'),
'%s' % btn[1]]])
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
self.data = self.count = self.active = None
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- self.margin = int(self.samplerate * 0.0001) - 1 # 0.1ms
+ self.tolerance = 0.05 # +/-5%
self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
+ def compare_with_tolerance(self, measured, base):
+ return (measured >= base * (1 - self.tolerance)
+ and measured <= base * (1 + self.tolerance))
+
def handle_bit(self, tick):
ret = None
- if tick in range(self.dazero - self.margin, self.dazero + self.margin):
+ if self.compare_with_tolerance(tick, self.dazero):
ret = 0
- elif tick in range(self.daone - self.margin, self.daone + self.margin):
+ elif self.compare_with_tolerance(tick, self.daone):
ret = 1
if ret in (0, 1):
self.putb([0, ['%d' % ret]])
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
+
+ cd_count = None
+ if self.options['cd_freq']:
+ cd_count = int(self.samplerate / self.options['cd_freq']) + 1
+ prev_ir = None
+
while True:
- # Wait for any edge (rising or falling).
- (self.ir,) = self.wait({0: 'e'})
+ # Detect changes in the presence of an active input signal.
+ # The decoder can either be fed an already filtered RX signal
+ # or optionally can detect the presence of a carrier. Periods
+ # of inactivity (signal changes slower than the carrier freq,
+ # if specified) pass on the most recently sampled level. This
+ # approach works for filtered and unfiltered input alike, and
+ # only slightly extends the active phase of input signals with
+ # carriers included by one period of the carrier frequency.
+ # IR based communication protocols can cope with this slight
+ # inaccuracy just fine by design. Enabling carrier detection
+ # on already filtered signals will keep the length of their
+ # active period, but will shift their signal changes by one
+ # carrier period before they get passed to decoding logic.
+ if cd_count:
+ (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
+ if self.matched[0]:
+ cur_ir = self.active
+ if cur_ir == prev_ir:
+ continue
+ prev_ir = cur_ir
+ self.ir = cur_ir
+ else:
+ (self.ir,) = self.wait({0: 'e'})
if self.ir != self.active:
# Save the non-active edge, then wait for the next edge.
# State machine.
if self.state == 'IDLE':
- if b in range(self.lc - self.margin, self.lc + self.margin):
+ if self.compare_with_tolerance(b, self.lc):
self.putpause('Long')
self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
self.ss_remote = self.ss_start
self.data = self.count = 0
self.state = 'ADDRESS'
- elif b in range(self.rc - self.margin, self.rc + self.margin):
+ elif self.compare_with_tolerance(b, self.rc):
self.putpause('Short')
self.putstop(self.samplenum)
self.samplenum += self.stop
pass
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'ir_rc5'
name = 'IR RC-5'
longname = 'IR RC-5'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
- def decode(self, ss, es, data):
+ def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- for (self.samplenum, pins) in data:
+ while True:
- self.ir = pins[0]
+ (self.ir,) = self.wait()
# Wait for any edge (rising or falling).
if self.old_ir == self.ir:
# State machine.
if self.state == 'IDLE':
+ bit = 1
self.edges.append(self.samplenum)
- self.bits.append([self.samplenum, 1])
+ self.bits.append([self.samplenum, bit])
self.state = 'MID1'
self.old_ir = self.ir
continue
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'CLK'
self.samplerate = None
self.oldclk, self.oldsig = 0, 0
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
# self.state = 'TEST-LOGIC-RESET'
self.state = 'RUN-TEST/IDLE'
self.oldstate = None
% (data_hex, ack_meaning)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'jtag_stm32'
name = 'JTAG / STM32'
longname = 'Joint Test Action Group / ST STM32'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.samplenums = None
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'lm75'
name = 'LM75'
longname = 'National LM75'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.reg = 0x00 # Currently selected register
self.databytes = []
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'lpc'
name = 'LPC'
longname = 'Low-Pin-Count'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.oldlclk = -1
self.samplenum = 0
- self.clocknum = 0
self.lad = -1
self.addr = 0
self.cur_nibble = 0
def handle_get_ct_dr(self, lad, lad_bits):
# LAD[3:0]: Cycle type / direction field (1 clock cycle).
- self.cycle_type = fields['CT_DR'][lad]
+ self.cycle_type = fields['CT_DR'].get(lad, 'Reserved / unknown')
# TODO: Warning/error on invalid cycle types.
- if self.cycle_type == 'Reserved':
+ if 'Reserved' in self.cycle_type:
self.putb([0, ['Invalid cycle type (%s)' % lad_bits]])
self.es_block = self.samplenum
# LAD[3:0]: SYNC field (1-n clock cycles).
self.sync_val = lad_bits
- self.cycle_type = fields['SYNC'][lad]
+ self.cycle_type = fields['SYNC'].get(lad, 'Reserved / unknown')
# TODO: Warnings if reserved value are seen?
- if self.cycle_type == 'Reserved':
+ if 'Reserved' in self.cycle_type:
self.putb([0, ['SYNC, cycle %d: %s (reserved value)' % \
(self.synccount, self.sync_val)]])
self.tarcount = 0
self.state = 'IDLE'
- def decode(self, ss, es, data):
- for (self.samplenum, pins) in data:
+ def decode(self):
+ while True:
+ # TODO: Come up with more appropriate self.wait() conditions.
+ pins = self.wait()
# If none of the pins changed, there's nothing to do.
if self.oldpins == pins:
self.ss_block = self.samplenum
self.state = 'GET START'
self.lad = -1
- # self.clocknum = 0
elif self.state == 'GET START':
self.handle_get_start(lad, lad_bits, lframe)
elif self.state == 'GET CT/DR':
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.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/>.
+##
+
+'''
+Maple bus is serial communication protocol used by peripherals for the
+SEGA Dreamcast game console.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.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 sigrokdecode as srd
+
+ann = [
+ ['Size', 'L'],
+ ['SrcAP', 'S'],
+ ['DstAP', 'D'],
+ ['Cmd', 'C'],
+ ['Data'],
+ ['Cksum', 'K'],
+]
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'maple_bus'
+ name = 'Maple bus'
+ longname = 'SEGA Maple bus'
+ desc = 'Maple bus peripheral protocol for SEGA Dreamcast.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['maple_bus']
+ channels = (
+ {'id': 'sdcka', 'name': 'SDCKA', 'desc': 'Data/clock line A'},
+ {'id': 'sdckb', 'name': 'SDCKB', 'desc': 'Data/clock line B'},
+ )
+ annotations = (
+ ('start', 'Start pattern'),
+ ('end', 'End pattern'),
+ ('start-with-crc', 'Start pattern with CRC'),
+ ('occupancy', 'SDCKB occupancy pattern'),
+ ('reset', 'RESET pattern'),
+ ('bit', 'Bit'),
+ ('size', 'Data size'),
+ ('source', 'Source AP'),
+ ('dest', 'Destination AP'),
+ ('command', 'Command'),
+ ('data', 'Data'),
+ ('checksum', 'Checksum'),
+ ('frame-error', 'Frame error'),
+ ('checksum-error', 'Checksum error'),
+ ('size-error', 'Size error'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0, 1, 2, 3, 4, 5)),
+ ('fields', 'Fields', (6, 7, 8, 9, 10, 11)),
+ ('warnings', 'Warnings', (12, 13, 14)),
+ )
+ binary = (
+ ('size', 'Data size'),
+ ('source', 'Source AP'),
+ ('dest', 'Destination AP'),
+ ('command', 'Command code'),
+ ('data', 'Data'),
+ ('checksum', 'Checksum'),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
+ self.pending_bit_pos = None
+
+ def putx(self, data):
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def putb(self, data):
+ self.put(self.ss, self.es, self.out_binary, data)
+
+ def byte_annotation(self, bintype, d):
+ return [bintype + 6,
+ ['%s: %02X' % (name, d) for name in ann[bintype]] + ['%02X' % d]]
+
+ def got_start(self):
+ self.putx([0, ['Start pattern', 'Start', 'S']])
+
+ def got_end(self):
+ self.putx([1, ['End pattern', 'End', 'E']])
+ if self.length != self.expected_length + 1:
+ self.putx([14, ['Size error', 'L error', 'LE']])
+
+ def got_start_with_crc(self):
+ self.putx([2, ['Start pattern with CRC', 'Start CRC', 'SC']])
+
+ def got_occupancy(self):
+ self.putx([3, ['SDCKB occupancy pattern', 'Occupancy', 'O']])
+
+ def got_reset(self):
+ self.putx([4, ['RESET pattern', 'RESET', 'R']])
+
+ def output_pending_bit(self):
+ if self.pending_bit_pos:
+ self.put(self.pending_bit_pos, self.pending_bit_pos, self.out_ann, [5, ['Bit: %d' % self.pending_bit, '%d' % self.pending_bit]])
+
+ def got_bit(self, n):
+ self.output_pending_bit()
+ self.data = self.data * 2 + n
+ self.pending_bit = n
+ self.pending_bit_pos = self.samplenum
+
+ def got_byte(self):
+ self.output_pending_bit()
+ bintype = 4
+ if self.length < 4:
+ if self.length == 0:
+ self.expected_length = 4 * (self.data + 1)
+ bintype = self.length
+ elif self.length == self.expected_length:
+ bintype = 5
+ if self.data != self.checksum:
+ self.putx([13, ['Cksum error', 'K error', 'KE']])
+ self.length = self.length + 1
+ self.checksum = self.checksum ^ self.data
+ self.putx(self.byte_annotation(bintype, self.data))
+ self.putb([bintype, bytes([self.data])])
+ self.pending_bit_pos = None
+
+ def frame_error(self):
+ self.putx([7, ['Frame error', 'F error', 'FE']])
+
+ def handle_start(self):
+ self.wait({0: 'l', 1: 'h'})
+ self.ss = self.samplenum
+ count = 0
+ while True:
+ sdcka, sdckb = self.wait([{1: 'f'}, {0: 'r'}])
+ if self.matched[0]:
+ count = count + 1
+ if self.matched[1]:
+ self.es = self.samplenum
+ if sdckb == 1:
+ if count == 4:
+ self.got_start()
+ return True
+ elif count == 6:
+ self.got_start_with_crc()
+ return True
+ elif count == 8:
+ self.got_occupancy()
+ return False
+ elif count >= 14:
+ self.got_reset()
+ return False
+ self.frame_error()
+ return False
+
+ def handle_byte_or_stop(self):
+ self.ss = self.samplenum
+ self.pending_bit_pos = None
+ initial = True
+ counta = 0
+ countb = 0
+ self.data = 0
+ while countb < 4:
+ sdcka, sdckb = self.wait([{0: 'f'}, {1: 'f'}])
+ self.es = self.samplenum
+ if self.matched[0]:
+ if counta == countb:
+ self.got_bit(sdckb)
+ counta = counta + 1
+ elif counta == 1 and countb == 0 and self.data == 0 and sdckb == 0:
+ self.wait([{0: 'h', 1: 'h'}, {0: 'f'}, {1: 'f'}])
+ self.es = self.samplenum
+ if self.matched[0]:
+ self.got_end()
+ else:
+ self.frame_error()
+ return False
+ else:
+ self.frame_error()
+ return False
+ elif self.matched[1]:
+ if counta == countb + 1:
+ self.got_bit(sdcka)
+ countb = countb + 1
+ elif counta == 0 and countb == 0 and sdcka == 1 and initial:
+ self.ss = self.samplenum
+ initial = False
+ else:
+ self.frame_error()
+ return False
+ self.wait({0: 'h'})
+ self.es = self.samplenum
+ self.got_byte()
+ return True
+
+ def decode(self):
+ while True:
+ while not self.handle_start():
+ pass
+ self.length = 0
+ self.expected_length = 4
+ self.checksum = 0
+ while self.handle_byte_or_stop():
+ pass
ann_reg, ann_digit, ann_warning = range(3)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'max7219'
name = 'MAX7219'
longname = 'Maxim MAX7219/MAX7221'
('warnings', 'Warnings', (ann_warning,)),
)
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.pos = 0
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.illegal_bus = 0
self.samplenum = -1
self.clause45_addr = -1 # Clause 45 is context sensitive.
('warnings', 'Warnings', (5,)),
)
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ pass
+
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_ann = self.register(srd.OUTPUT_ANN)
TX = 1
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'midi'
name = 'MIDI'
longname = 'Musical Instrument Digital Interface'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.status_byte = 0
self.explicit_status_byte = False
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'mlx90614'
name = 'MLX90614'
longname = 'Melexis MLX90614'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IGNORE START REPEAT'
self.data = []
self.check_crc(bytecount + 12)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'modbus'
name = 'Modbus'
longname = 'Modbus RTU over RS232/RS485'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ADUSc = None # Start off with empty slave -> client ADU.
self.ADUCs = None # Start off with empty client -> slave ADU.
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.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/>.
+##
+
+'''
+Morse code is a method of transmitting text information as a series of
+on-off tones.
+
+Details:
+https://en.wikipedia.org/wiki/Morse_code
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.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
+
+def decode_ditdah(s):
+ return tuple({'-': 3, '.': 1}[c] for c in s)
+
+def encode_ditdah(tpl):
+ return ''.join({1: '.', 3: '-'}[c] for c in tpl)
+
+# https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf
+# Recommendation ITU-R M.1677-1
+# (10/2009)
+# International Morse code
+alphabet = {
+ # 1.1.1 Letters
+ '.-': 'a',
+ '-...': 'b',
+ '-.-.': 'c',
+ '-..': 'd',
+ '.': 'e',
+ '..-..': 'é', # "accented"
+ '..-.': 'f',
+ '--.': 'g',
+ '....': 'h',
+ '..': 'i',
+ '.---': 'j',
+ '-.-': 'k',
+ '.-..': 'l',
+ '--': 'm',
+ '-.': 'n',
+ '---': 'o',
+ '.--.': 'p',
+ '--.-': 'q',
+ '.-.': 'r',
+ '...': 's',
+ '-': 't',
+ '..-': 'u',
+ '...-': 'v',
+ '.--': 'w',
+ '-..-': 'x',
+ '-.--': 'y',
+ '--..': 'z',
+
+ # 1.1.2 Figures
+ '.----': '1',
+ '..---': '2',
+ '...--': '3',
+ '....-': '4',
+ '.....': '5',
+ '-....': '6',
+ '--...': '7',
+ '---..': '8',
+ '----.': '9',
+ '-----': '0',
+
+ # 1.1.3 Punctuation marks and miscellaneous signs
+ '.-.-.-': '.', # Full stop (period)
+ '--..--': ',', # Comma
+ '---...': ':', # Colon or division sign
+ '..--..': '?', # Question mark (note of interrogation or request for repetition of a transmission not understood)
+ '.----.': '’', # Apostrophe
+ '-....-': '-', # Hyphen or dash or subtraction sign
+ '-..-.': '/', # Fraction bar or division sign
+ '-.--.': '(', # Left-hand bracket (parenthesis)
+ '-.--.-': ')', # Right-hand bracket (parenthesis)
+ '.-..-.': '“ ”', # Inverted commas (quotation marks) (before and after the words)
+ '-...-': '=', # Double hyphen
+ '...-.': 'UNDERSTOOD', # Understood
+ '........': 'ERROR', # Error (eight dots)
+ '.-.-.': '+', # Cross or addition sign
+ '.-...': 'WAIT', # Wait
+ '...-.-': 'EOW', # End of work
+ '-.-.-': 'START', # Starting signal (to precede every transmission)
+ '.--.-.': '@', # Commercial at
+
+ #'-.-': 'ITT', # K: Invitation to transmit
+
+ # 3.2.1 For the multiplication sign, the signal corresponding to the letter X shall be transmitted.
+ #'-..-': '×', # Multiplication sign
+}
+
+alphabet = {decode_ditdah(k): v for k, v in alphabet.items()}
+
+# 2 Spacing and length of the signals (right side is just for printing).
+symbols = { # level, time units
+ # 2.1 A dash is equal to three dots.
+ (1, 1): '*',
+ (1, 3): '===',
+ # 2.2 The space between the signals forming the same letter is equal to one dot.
+ (0, 1): '_',
+ # 2.3 The space between two letters is equal to three dots.
+ (0, 3): '__',
+ # 2.4 The space between two words is equal to seven dots.
+ (0, 7): '___',
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'morse'
+ name = 'Morse'
+ longname = 'Morse code'
+ desc = 'Demodulated morse code protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['morse']
+ channels = (
+ {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+ )
+ options = (
+ {'id': 'timeunit', 'desc': 'Time unit (guess)', 'default': 0.1},
+ )
+ annotations = (
+ ('time', 'Time'),
+ ('units', 'Units'),
+ ('symbol', 'Symbol'),
+ ('letter', 'Letter'),
+ ('word', 'Word'),
+ )
+ annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+ def decode_symbols(self):
+ # Annotate symbols, emit symbols, handle timeout via token.
+
+ timeunit = self.options['timeunit']
+
+ self.wait({0: 'r'})
+ prevtime = self.samplenum # Time of an actual edge.
+
+ while True:
+ (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}])
+
+ pval = 1 - val
+ curtime = self.samplenum
+ dt = (curtime - prevtime) / self.samplerate
+ units = dt / timeunit
+ iunits = round(units)
+ error = abs(units - iunits)
+
+ symbol = (pval, iunits)
+
+ if self.matched[1]:
+ yield None # Flush word.
+ continue
+
+ self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]])
+ self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]])
+
+ if symbol in symbols:
+ yield (prevtime, curtime, symbol)
+
+ prevtime = curtime
+
+ thisunit = dt / iunits
+ timeunit += (thisunit - timeunit) * 0.02 * iunits # Adapt.
+
+ def decode_morse(self):
+ # Group symbols into letters.
+ sequence = ()
+ s0 = s1 = None
+
+ for item in self.decode_symbols():
+ do_yield = False
+ if item is not None: # Level + width.
+ (t0, t1, symbol) = item
+ (sval, sunits) = symbol
+ if sval == 1:
+ if s0 is None:
+ s0 = t0
+ s1 = t1
+ sequence += (sunits,)
+ else:
+ # Generate "flush" for end of letter, end of word.
+ if sunits >= 3:
+ do_yield = True
+ else:
+ do_yield = True
+ if do_yield:
+ if sequence:
+ yield (s0, s1, alphabet.get(sequence, encode_ditdah(sequence)))
+ sequence = ()
+ s0 = s1 = None
+ if item is None:
+ yield None # Pass through flush of 5+ space.
+
+ def decode(self):
+
+ # Strictly speaking there is no point in running this decoder
+ # when the sample rate is unknown or zero. But the previous
+ # implementation already fell back to a rate of 1 in that case.
+ # We stick with this approach, to not introduce new constraints
+ # for existing use scenarios.
+ if not self.samplerate:
+ self.samplerate = 1.0
+
+ # Annotate letters, group into words.
+ s0 = s1 = None
+ word = ''
+ for item in self.decode_morse():
+ do_yield = False
+
+ if item is not None: # Append letter.
+ (t0, t1, letter) = item
+ self.put(t0, t1, self.out_ann, [3, [letter]])
+ if s0 is None:
+ s0 = t0
+ s1 = t1
+ word += letter
+ else:
+ do_yield = True
+
+ if do_yield: # Flush of word.
+ if word:
+ self.put(s0, s1, self.out_ann, [4, [word]])
+ word = ''
+ s0 = s1 = None
from .lists import *
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'mrf24j40'
name = 'MRF24J40'
longname = 'Microchip MRF24J40'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
self.miso_bytes = []
def putw(self, pos, msg):
self.put(pos[0], pos[1], self.out_ann, [4, [msg]])
- def reset(self):
+ def reset_data(self):
self.mosi_bytes = []
self.miso_bytes = []
if cs_old is not None and cs_old == 0 and cs_new == 1:
if len(self.mosi_bytes) not in (0, 2, 3):
self.putw([self.ss_cmd, es], 'Misplaced CS!')
- self.reset()
+ self.reset_data()
return
# Don't care about anything else.
if len(self.mosi_bytes) == 3:
self.es_cmd = es
self.handle_long()
- self.reset()
+ self.reset_data()
else:
self.es_cmd = es
self.handle_short()
- self.reset()
+ self.reset_data()
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'mxc6225xu'
name = 'MXC6225XU'
longname = 'MEMSIC MXC6225XU'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
def start(self):
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'nrf24l01'
name = 'nRF24L01(+)'
longname = 'Nordic Semiconductor nRF24L01/nRF24L01+'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.next()
self.requirements_met = True
self.cs_was_released = False
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'nunchuk'
name = 'Nunchuk'
longname = 'Nintendo Wii Nunchuk'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1
self.databytecount = 0
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.state = 'INITIAL'
self.present = 0
}
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'onewire_network'
name = '1-Wire network layer'
longname = '1-Wire serial communication bus (network layer)'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ss_block = 0
self.es_block = 0
self.state = 'COMMAND'
# Data collector.
def onewire_collect(self, length, val, ss, es):
# Storing the sample this sequence begins with.
- if self.bit_cnt == 1:
+ if self.bit_cnt == 0:
self.ss_block = ss
self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt)
self.bit_cnt += 1
TX = 1
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'pan1321'
name = 'PAN1321'
longname = 'Panasonic PAN1321'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.cmd = ['', '']
self.ss_block = None
##
import sigrokdecode as srd
+from common.srdhelper import bitpack
'''
OUTPUT_PYTHON format:
options = (
{'id': 'clock_edge', 'desc': 'Clock edge to sample on',
'default': 'rising', 'values': ('rising', 'falling')},
- {'id': 'wordsize', 'desc': 'Data wordsize', 'default': 1},
+ {'id': 'wordsize', 'desc': 'Data wordsize (# bus cycles)',
+ 'default': 0},
{'id': 'endianness', 'desc': 'Data endianness',
'default': 'little', 'values': ('little', 'big')},
)
('items', 'Items'),
('words', 'Words'),
)
+ annotation_rows = (
+ ('items', 'Items', (0,)),
+ ('words', 'Words', (1,)),
+ )
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.items = []
- self.itemcount = 0
self.saved_item = None
self.ss_item = self.es_item = None
+ self.saved_word = None
+ self.ss_word = self.es_word = None
self.first = True
- self.num_channels = 0
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
def putw(self, data):
self.put(self.ss_word, self.es_word, self.out_ann, data)
- def handle_bits(self, datapins):
- # If this is the first item in a word, save its sample number.
- if self.itemcount == 0:
- self.ss_word = self.samplenum
+ def handle_bits(self, item, used_pins):
- # Get the bits for this item.
- item, used_pins = 0, datapins.count(1) + datapins.count(0)
- for i in range(used_pins):
- item |= datapins[i] << i
-
- self.items.append(item)
- self.itemcount += 1
+ # If a word was previously accumulated, then emit its annotation
+ # now after its end samplenumber became available.
+ if self.saved_word is not None:
+ if self.options['wordsize'] > 0:
+ self.es_word = self.samplenum
+ self.putw([1, [self.fmt_word.format(self.saved_word)]])
+ self.putpw(['WORD', self.saved_word])
+ self.saved_word = None
+ # Defer annotations for individual items until the next sample
+ # is taken, and the previous sample's end samplenumber has
+ # become available.
if self.first:
# Save the start sample and item for later (no output yet).
self.ss_item = self.samplenum
# Output the saved item (from the last CLK edge to the current).
self.es_item = self.samplenum
self.putpb(['ITEM', self.saved_item])
- self.putb([0, ['%X' % self.saved_item]])
+ self.putb([0, [self.fmt_item.format(self.saved_item)]])
self.ss_item = self.samplenum
self.saved_item = item
- endian, ws = self.options['endianness'], self.options['wordsize']
-
- # Get as many items as the configured wordsize says.
- if self.itemcount < ws:
+ # Get as many items as the configured wordsize specifies.
+ if not self.items:
+ self.ss_word = self.samplenum
+ self.items.append(item)
+ ws = self.options['wordsize']
+ if len(self.items) < ws:
return
- # Output annotations/python for a word (a collection of items).
- word = 0
- for i in range(ws):
- if endian == 'little':
- word |= self.items[i] << ((ws - 1 - i) * used_pins)
- elif endian == 'big':
- word |= self.items[i] << (i * used_pins)
-
- self.es_word = self.samplenum
- # self.putpw(['WORD', word])
- # self.putw([1, ['%X' % word]])
- self.ss_word = self.samplenum
-
- self.itemcount, self.items = 0, []
+ # Collect words and prepare annotation details, but defer emission
+ # until the end samplenumber becomes available.
+ endian = self.options['endianness']
+ if endian == 'big':
+ self.items.reverse()
+ word = sum([self.items[i] << (i * used_pins) for i in range(ws)])
+ self.saved_word = word
+ self.items = []
def decode(self):
- for i in range(len(self.optional_channels)):
- if self.has_channel(i):
- self.num_channels += 1
-
- if self.num_channels == 0:
+ # Determine which (optional) channels have input data. Insist in
+ # a non-empty input data set. Cope with sparse connection maps.
+ # Store enough state to later "compress" sampled input data.
+ max_possible = len(self.optional_channels)
+ idx_channels = [
+ idx if self.has_channel(idx) else None
+ for idx in range(max_possible)
+ ]
+ has_channels = [idx for idx in idx_channels if idx is not None]
+ if not has_channels:
raise ChannelError('At least one channel has to be supplied.')
-
- if not self.has_channel(0):
- # CLK was not supplied, sample on ANY edge of ANY of the pins
- # (but only of those pins that were actually supplied).
- conds = []
- for i in range(1, len(self.optional_channels)):
- if self.has_channel(i):
- conds.append({i: 'e'})
- while True:
- self.handle_bits(self.wait(conds)[1:])
+ max_connected = max(has_channels)
+
+ # Determine .wait() conditions, depending on the presence of a
+ # clock signal. Either inspect samples on the configured edge of
+ # the clock, or inspect samples upon ANY edge of ANY of the pins
+ # which provide input data.
+ if self.has_channel(0):
+ edge = self.options['clock_edge'][0]
+ conds = {0: edge}
else:
- # Sample on the rising or falling CLK edge (depends on config).
- while True:
- pins = self.wait({0: self.options['clock_edge'][0]})
- self.handle_bits(pins[1:])
+ conds = [{idx: 'e'} for idx in has_channels]
+
+ # Pre-determine which input data to strip off, the width of
+ # individual items and multiplexed words, as well as format
+ # strings here. This simplifies call sites which run in tight
+ # loops later.
+ idx_strip = max_connected + 1
+ num_item_bits = idx_strip - 1
+ num_word_items = self.options['wordsize']
+ num_word_bits = num_item_bits * num_word_items
+ num_digits = (num_item_bits + 3) // 4
+ self.fmt_item = "{{:0{}x}}".format(num_digits)
+ num_digits = (num_word_bits + 3) // 4
+ self.fmt_word = "{{:0{}x}}".format(num_digits)
+
+ # Keep processing the input stream. Assume "always zero" for
+ # not-connected input lines. Pass data bits (all inputs except
+ # clock) to the handle_bits() method.
+ while True:
+ pins = self.wait(conds)
+ bits = [0 if idx is None else pins[idx] for idx in idx_channels]
+ item = bitpack(bits[1:idx_strip])
+ self.handle_bits(item, num_item_bits)
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.bits = []
self.samplenum = 0
self.bitcount = 0
import sigrokdecode as srd
+class SamplerateError(Exception):
+ pass
+
class Decoder(srd.Decoder):
api_version = 3
id = 'pwm'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
self.ss_block = self.es_block = None
- self.first_samplenum = None
- self.start_samplenum = None
- self.end_samplenum = None
- self.num_cycles = 0
- self.average = 0
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
- self.startedge = 0 if self.options['polarity'] == 'active-low' else 1
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
self.out_average = \
self.put(self.ss_block, self.es_block, self.out_ann, [1, [period_s]])
def putb(self, data):
- self.put(self.num_cycles, self.num_cycles, self.out_binary, data)
+ self.put(self.ss_block, self.es_block, self.out_binary, data)
def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
- # Get the first rising edge.
- pin, = self.wait({0: 'e'})
- if pin != self.startedge:
- pin, = self.wait({0: 'e'})
+ num_cycles = 0
+ average = 0
+
+ # Wait for an "active" edge (depends on config). This starts
+ # the first full period of the inspected signal waveform.
+ self.wait({0: 'f' if self.options['polarity'] == 'active-low' else 'r'})
self.first_samplenum = self.samplenum
- self.start_samplenum = self.samplenum
- # Handle all next edges.
+ # Keep getting samples for the period's middle and terminal edges.
+ # At the same time that last sample starts the next period.
while True:
- pin, = self.wait({0: 'e'})
-
- if pin == self.startedge:
- # Rising edge
- # We are on a full cycle we can calculate
- # the period, the duty cycle and its ratio.
- period = self.samplenum - self.start_samplenum
- duty = self.end_samplenum - self.start_samplenum
- ratio = float(duty / period)
-
- # This interval starts at this edge.
- self.ss_block = self.start_samplenum
- # Store the new rising edge position and the ending
- # edge interval.
- self.start_samplenum = self.es_block = self.samplenum
-
- # Report the duty cycle in percent.
- percent = float(ratio * 100)
- self.putx([0, ['%f%%' % percent]])
-
- # Report the duty cycle in the binary output.
- self.putb([0, bytes([int(ratio * 256)])])
-
- # Report the period in units of time.
- period_t = float(period / self.samplerate)
- self.putp(period_t)
-
- # Update and report the new duty cycle average.
- self.num_cycles += 1
- self.average += percent
- self.put(self.first_samplenum, self.es_block, self.out_average,
- float(self.average / self.num_cycles))
- else:
- # Falling edge
- self.end_samplenum = self.ss_block = self.samplenum
+
+ # Get the next two edges. Setup some variables that get
+ # referenced in the calculation and in put() routines.
+ start_samplenum = self.samplenum
+ self.wait({0: 'e'})
+ end_samplenum = self.samplenum
+ self.wait({0: 'e'})
+ self.ss_block = start_samplenum
+ self.es_block = self.samplenum
+
+ # Calculate the period, the duty cycle, and its ratio.
+ period = self.samplenum - start_samplenum
+ duty = end_samplenum - start_samplenum
+ ratio = float(duty / period)
+
+ # Report the duty cycle in percent.
+ percent = float(ratio * 100)
+ self.putx([0, ['%f%%' % percent]])
+
+ # Report the duty cycle in the binary output.
+ self.putb([0, bytes([int(ratio * 256)])])
+
+ # Report the period in units of time.
+ period_t = float(period / self.samplerate)
+ self.putp(period_t)
+
+ # Update and report the new duty cycle average.
+ num_cycles += 1
+ average += percent
+ self.put(self.first_samplenum, self.es_block, self.out_average,
+ float(average / num_cycles))
id = 'qi'
name = 'Qi'
longname = 'Qi charger protocol'
- desc = 'Protocol used by Qi receiver'
+ desc = 'Protocol used by Qi receiver.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['qi']
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.reset_variables()
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- (qi,) = self.wait({'skip': 1})
+ (qi,) = self.wait()
self.handle_transition(self.samplenum, qi == 0)
while True:
prev = self.samplenum
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Steve R <steversig@virginmedia.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 PD decodes the remote control protocol which is frequently used
+within key fobs and power socket remotes.
+
+They contain encoding chips like the PT2262 which converts the button
+pressed and address settings into a series of pulses which is then
+transmitted over whatever frequency and modulation that the designer
+chooses. These devices operate at a number of frequencies including 433MHz.
+
+This PD should also decode the HX2262 and SC5262 which are equivalents.
+
+The decoder also contains some additional decoding for a Maplin L95AR
+remote control and will turn the received signal into which button was
+pressed and what the address code DIP switches are set to.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Steve R <steversig@virginmedia.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
+
+bitvals = ('0', '1', 'f', 'U')
+
+def decode_bit(edges):
+ # Datasheet says long pulse is 3 times short pulse.
+ lmin = 2 # long min multiplier
+ lmax = 5 # long max multiplier
+ eqmin = 0.5 # equal min multiplier
+ eqmax = 1.5 # equal max multiplier
+ if ( # 0 -___-___
+ (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+ (edges[2] >= edges[0] * eqmin and edges[2] <= edges[0] * eqmax) and
+ (edges[3] >= edges[0] * lmin and edges[3] <= edges[0] * lmax)):
+ return '0'
+ elif ( # 1 ---_---_
+ (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax) and
+ (edges[0] >= edges[2] * eqmin and edges[0] <= edges[2] * eqmax) and
+ (edges[0] >= edges[3] * lmin and edges[0] <= edges[3] * lmax)):
+ return '1'
+ elif ( # float ---_-___
+ (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+ (edges[2] >= edges[0] * lmin and edges[2] <= edges[0]* lmax) and
+ (edges[3] >= edges[0] * eqmin and edges[3] <= edges[0] * eqmax)):
+ return 'f'
+ else:
+ return 'U'
+
+def pinlabels(bit_count):
+ if bit_count <= 6:
+ return 'A%i' % (bit_count - 1)
+ else:
+ return 'A%i/D%i' % (bit_count - 1, 12 - bit_count)
+
+def decode_model(model, bits):
+ if model == 'maplin_l95ar':
+ address = 'Addr' # Address pins A0 to A5
+ for i in range(0, 6):
+ address += ' %i:' % (i + 1) + ('on' if bits[i][0] == '0' else 'off')
+ button = 'Button'
+ # Button pins A6/D5 to A11/D0
+ if bits[6][0] == '0' and bits[11][0] == '0':
+ button += ' A ON/OFF'
+ elif bits[7][0] == '0' and bits[11][0] == '0':
+ button += ' B ON/OFF'
+ elif bits[9][0] == '0' and bits[11][0] == '0':
+ button += ' C ON/OFF'
+ elif bits[8][0] == '0' and bits[11][0] == '0':
+ button += ' D ON/OFF'
+ else:
+ button += ' Unknown'
+ return ['%s' % address, bits[0][1], bits[5][2], \
+ '%s' % button, bits[6][1], bits[11][2]]
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'rc_encode'
+ name = 'RC encode'
+ longname = 'Remote control encoder'
+ desc = 'PT2262/HX2262/SC5262 remote control encoder protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = []
+ channels = (
+ {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+ )
+ annotations = (
+ ('bit-0', 'Bit 0'),
+ ('bit-1', 'Bit 1'),
+ ('bit-f', 'Bit f'),
+ ('bit-U', 'Bit U'),
+ ('bit-sync', 'Bit sync'),
+ ('pin', 'Pin'),
+ ('code-word-addr', 'Code word address'),
+ ('code-word-data', 'Code word data'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0, 1, 2, 3, 4)),
+ ('pins', 'Pins', (5,)),
+ ('code-words', 'Code words', (6, 7)),
+ )
+ options = (
+ {'id': 'remote', 'desc': 'Remote', 'default': 'none',
+ 'values': ('none', 'maplin_l95ar')},
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplenumber_last = None
+ self.pulses = []
+ self.bits = []
+ self.labels = []
+ self.bit_count = 0
+ self.ss = None
+ self.es = None
+ self.state = 'IDLE'
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.model = self.options['remote']
+
+ def putx(self, data):
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def decode(self):
+ while True:
+ pin = self.wait({0: 'e'})
+ self.state = 'DECODING'
+
+ if not self.samplenumber_last: # Set counters to start of signal.
+ self.samplenumber_last = self.samplenum
+ self.ss = self.samplenum
+ continue
+
+ if self.bit_count < 12: # Decode A0 to A11.
+ self.bit_count += 1
+ for i in range(0, 4): # Get four pulses for each bit.
+ if i > 0:
+ pin = self.wait({0: 'e'}) # Get next 3 edges.
+ samples = self.samplenum - self.samplenumber_last
+ self.pulses.append(samples) # Save the pulse width.
+ self.samplenumber_last = self.samplenum
+ self.es = self.samplenum
+ self.bits.append([decode_bit(self.pulses), self.ss,
+ self.es]) # Save states and times.
+ idx = bitvals.index(decode_bit(self.pulses))
+ self.putx([idx, [decode_bit(self.pulses)]]) # Write decoded bit.
+ self.putx([5, [pinlabels(self.bit_count)]]) # Write pin labels.
+ self.pulses = []
+ self.ss = self.samplenum
+ else:
+ if self.model != 'none':
+ self.labels = decode_model(self.model, self.bits)
+ self.put(self.labels[1], self.labels[2], self.out_ann,
+ [6, [self.labels[0]]]) # Write model decode.
+ self.put(self.labels[4], self.labels[5], self.out_ann,
+ [7, [self.labels[3]]]) # Write model decode.
+ samples = self.samplenum - self.samplenumber_last
+ pin = self.wait({'skip': 8 * samples}) # Wait for end of sync bit.
+ self.es = self.samplenum
+ self.putx([4, ['Sync']]) # Write sync label.
+ self.reset() # Reset and wait for next set of pulses.
+ self.state = 'DECODE_TIMEOUT'
+ if not self.state == 'DECODE_TIMEOUT':
+ self.samplenumber_last = self.samplenum
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'rfm12'
name = 'RFM12'
longname = 'RFM12 control protocol'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.mosi_bytes, self.miso_bytes = [], []
self.mosi_bits, self.miso_bits = [], []
self.row_pos = [0, 0, 0]
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'rgb_led_spi'
name = 'RGB LED (SPI)'
longname = 'RGB LED string decoder (SPI)'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
pass
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'rgb_led_ws281x'
name = 'RGB LED (WS281x)'
longname = 'RGB LED string decoder (WS281x)'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.oldpin = None
self.ss_packet = None
self.bits = []
self.ss_packet = None
- def decode(self, ss, es, data):
+ def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- for (self.samplenum, (pin, )) in data:
+ while True:
+ # TODO: Come up with more appropriate self.wait() conditions.
+ (pin,) = self.wait()
+
if self.oldpin is None:
self.oldpin = pin
continue
return tuple(l)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'rtc8564'
name = 'RTC-8564'
longname = 'Epson RTC-8564 JE/NB'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.hours = -1
self.minutes = -1
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Max Weller
+##
+## 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/>.
+##
+
+'''
+Decoder for Siemens EEPROM SDA2506.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Max Weller
+##
+## 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
+
+ann_cmdbit, ann_databit, ann_cmd, ann_data, ann_warning = range(5)
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'sda2506'
+ name = 'SDA2506'
+ longname = 'Siemens SDA 2506-5'
+ desc = 'Serial nonvolatile 1-Kbit EEPROM.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['sda2506']
+ channels = (
+ {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
+ {'id': 'd', 'name': 'DATA', 'desc': 'Data'},
+ {'id': 'ce', 'name': 'CE#', 'desc': 'Chip-enable'},
+ )
+ annotations = (
+ ('cmdbit', 'Command bit'),
+ ('databit', 'Data bit'),
+ ('cmd', 'Command'),
+ ('data', 'Data byte'),
+ ('warnings', 'Human-readable warnings'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (ann_cmdbit, ann_databit)),
+ ('commands', 'Commands', (ann_cmd,)),
+ ('data', 'Data', (ann_data,)),
+ ('warnings', 'Warnings', (ann_warning,)),
+ )
+
+ def __init__(self):
+ self.samplerate = None
+ self.reset()
+
+ def reset(self):
+ self.cmdbits = []
+ self.databits = []
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putbit(self, ss, es, typ, value):
+ self.put(ss, es, self.out_ann, [typ, ['%s' % (value)]])
+
+ def putdata(self, ss, es):
+ value = 0
+ for i in range(8):
+ value = (value << 1) | self.databits[i]
+ self.put(ss, es, self.out_ann, [ann_data, ['%02X' % (value)]])
+
+ def decode_bits(self, offset, width):
+ out = 0
+ for i in range(width):
+ out = (out << 1) | self.cmdbits[offset + i][0]
+ return (out, self.cmdbits[offset + width - 1][1], self.cmdbits[offset][2])
+
+ def decode_field(self, name, offset, width):
+ val, ss, es = self.decode_bits(offset, width)
+ self.put(ss, es, self.out_ann, [ann_data, ['%s: %02X' % (name, val)]])
+ return val
+
+ def decode(self):
+ while True:
+ # Wait for CLK edge or CE edge.
+ clk, d, ce = self.wait([{0: 'e'}, {2: 'e'}])
+
+ if self.matched[0] and ce == 1 and clk == 1:
+ # Rising clk edge and command mode.
+ bitstart = self.samplenum
+ self.wait({0: 'f'})
+ self.cmdbits = [(d, bitstart, self.samplenum)] + self.cmdbits
+ if len(self.cmdbits) > 24:
+ self.cmdbits = self.cmdbits[0:24]
+ self.putbit(bitstart, self.samplenum, ann_cmdbit, d)
+ elif self.matched[0] and ce == 0 and clk == 0:
+ # Falling clk edge and data mode.
+ bitstart = self.samplenum
+ clk, d, ce = self.wait([{'skip': int(2.5 * (1e6 / self.samplerate))}, {0: 'r'}, {2: 'e'}]) # Wait 25 us for data ready.
+ if self.matched == (True, False, False):
+ self.wait([{0: 'r'}, {2: 'e'}])
+ if len(self.databits) == 0:
+ self.datastart = bitstart
+ self.databits = [d] + self.databits
+ self.putbit(bitstart, self.samplenum, ann_databit, d)
+ if len(self.databits) == 8:
+ self.putdata(self.datastart, self.samplenum)
+ self.databits = []
+ elif self.matched[1] and ce == 0:
+ # Chip enable edge.
+ try:
+ self.decode_field('addr', 1, 7)
+ self.decode_field('CB', 0, 1)
+ if self.cmdbits[0][0] == 0:
+ # Beginning read command.
+ self.decode_field('read', 1, 7)
+ self.put(self.cmdbits[7][1], self.samplenum,
+ self.out_ann, [ann_cmd, ['read' ]])
+ elif d == 0:
+ # Beginning write command.
+ self.decode_field('data', 8, 8)
+ addr, ss, es = self.decode_bits(1, 7)
+ data, ss, es = self.decode_bits(8, 8)
+ cmdstart = self.samplenum
+ self.wait({2: 'r'})
+ self.put(cmdstart, self.samplenum, self.out_ann,
+ [ann_cmd, ['Write to %02X: %02X' % (addr, data)]])
+ else:
+ # Beginning erase command.
+ val, ss, es = self.decode_bits(1, 7)
+ cmdstart = self.samplenum
+ self.wait({2: 'r'})
+ self.put(cmdstart, self.samplenum, self.out_ann,
+ [ann_cmd, ['Erase: %02X' % (val)]])
+ self.databits = []
+ except Exception as ex:
+ self.reset()
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'GET COMMAND TOKEN'
self.token = []
self.is_acmd = False # Indicates CMD vs. ACMD
def putr(self, desc):
self.putt([self.last_cmd, ['Reply: %s' % desc]])
- def reset(self):
- self.cmd, self.arg = None, None
- self.token, self.state = [], 'GET COMMAND TOKEN'
-
def cmd_name(self, cmd):
c = acmd_names if self.is_acmd else cmd_names
return c.get(cmd, 'Unknown')
from common.sdcard import (cmd_names, acmd_names)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'sdcard_spi'
name = 'SD card (SPI mode)'
longname = 'Secure Digital card (SPI mode)'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.ss, self.es = 0, 0
self.ss_bit, self.es_bit = 0, 0
self.put(self.ss_edge, self.samplenum, self.out_ann, data)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'GET FIRST PULSE WIDTH'
self.ss_edge = None
self.first_edge = True
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.bitcount = 0
self.misodata = self.mosidata = 0
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
- if self.samplerate is not None:
+ if self.samplerate:
self.out_bitrate = self.register(srd.OUTPUT_META,
meta=(int, 'Bitrate', 'Bitrate during transfers'))
self.bw = (self.options['wordsize'] + 7) // 8
not self.cs_asserted(cs) if self.have_cs else False
ws = self.options['wordsize']
+ bo = self.options['bitorder']
# Receive MISO bit into our shift register.
if self.have_miso:
- if self.options['bitorder'] == 'msb-first':
+ if bo == 'msb-first':
self.misodata |= miso << (ws - 1 - self.bitcount)
else:
self.misodata |= miso << self.bitcount
# Receive MOSI bit into our shift register.
if self.have_mosi:
- if self.options['bitorder'] == 'msb-first':
+ if bo == 'msb-first':
self.mosidata |= mosi << (ws - 1 - self.bitcount)
else:
self.mosidata |= mosi << self.bitcount
self.putdata()
# Meta bitrate.
- if self.samplerate is not None:
+ if self.samplerate:
elapsed = 1 / float(self.samplerate)
elapsed *= (self.samplenum - self.ss_block + 1)
- bitrate = int(1 / elapsed * self.options['wordsize'])
+ bitrate = int(1 / elapsed * ws)
self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
if self.have_cs and self.cs_was_deasserted:
# process the very first sample before checking for edges. The
# previous implementation did this by seeding old values with
# None, which led to an immediate "change" in comparison.
- pins = self.wait({})
- (clk, miso, mosi, cs) = pins
+ (clk, miso, mosi, cs) = self.wait({})
self.find_clk_edge(miso, mosi, clk, cs, True)
while True:
- # Ignore identical samples early on (for performance reasons).
- pins = self.wait(wait_cond)
- (clk, miso, mosi, cs) = pins
+ (clk, miso, mosi, cs) = self.wait(wait_cond)
self.find_clk_edge(miso, mosi, clk, cs, False)
return ret
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'spiflash'
name = 'SPI flash'
longname = 'SPI flash chips'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.device_id = -1
self.on_end_transaction = None
self.end_current_transaction()
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'ssi32'
name = 'SSI32'
longname = 'Synchronous Serial Interface (32bit)'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
self.miso_bytes = []
def putx(self, data):
self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
- def reset(self):
+ def reset_data(self):
self.mosi_bytes = []
self.miso_bytes = []
self.es_array = []
def decode(self, ss, es, data):
ptype = data[0]
if ptype == 'CS-CHANGE':
- self.reset()
+ self.reset_data()
return
# Don't care about anything else.
return
self.handle_ack()
- self.reset()
+ self.reset_data()
else:
if len(self.mosi_bytes) < self.options['msgsize']:
return
self.handle_ctrl()
- self.reset()
+ self.reset_data()
import sigrokdecode as srd
-class SamplerateError(Exception):
- pass
-
class Decoder(srd.Decoder):
api_version = 3
id = 'stepper_motor'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
self.oldstep = None
self.ss_prev_step = None
self.pos = 0
def step(self, ss, direction):
if self.ss_prev_step is not None:
- delta = ss - self.ss_prev_step
- speed = self.samplerate / delta / self.scale
- speed_txt = self.format % speed
+ if self.samplerate:
+ delta = ss - self.ss_prev_step
+ speed = self.samplerate / delta / self.scale
+ speed_txt = self.format % speed
+ self.put(self.ss_prev_step, ss, self.out_ann,
+ [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]])
pos_txt = self.format % (self.pos / self.scale)
- self.put(self.ss_prev_step, ss, self.out_ann,
- [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]])
self.put(self.ss_prev_step, ss, self.out_ann,
[1, [pos_txt + ' ' + self.unit, pos_txt]])
self.samplerate = value
def decode(self):
- if not self.samplerate:
- raise SamplerateError('Cannot decode without samplerate.')
while True:
step, direction = self.wait({0: 'r'})
self.step(self.samplenum, direction)
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
# SWD data/clock state
self.state = 'UNKNOWN'
self.sample_edge = RISING
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.last_samplenum = None
self.lastlast_samplenum = None
import sigrokdecode as srd
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'tca6408a'
name = 'TI TCA6408A'
longname = 'Texas Instruments TCA6408A'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.state = 'IDLE'
self.chip = -1
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.last_samplenum = None
self.last_n = deque()
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.bits = []
self.ss_dac_first = None
self.ss_dac = self.es_dac = 0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.samplenum = 0
self.frame_start = [-1, -1]
return reverse_number(crc16, 16)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'usb_packet'
name = 'USB packet'
longname = 'Universal Serial Bus (LS/FS) packet'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.bits = []
self.packet = []
self.packet_summary = ''
SYNC_CODES = [SYNC1, SYNC2, SYNC3]
HRST_CODES = [RST1, RST1, RST1, RST2]
+SOP_SEQUENCES = [
+ (SYNC1, SYNC1, SYNC1, SYNC2),
+ (SYNC1, SYNC1, SYNC3, SYNC3),
+ (SYNC1, SYNC3, SYNC1, SYNC3),
+ (SYNC1, RST2, RST2, SYNC3),
+ (SYNC1, RST2, SYNC3, SYNC2),
+ (RST1, SYNC1, RST1, SYNC3),
+ (RST1, RST1, RST1, RST2),
+]
START_OF_PACKETS = {
- (SYNC1, SYNC1, SYNC1, SYNC2): 'SOP',
- (SYNC1, SYNC1, SYNC3, SYNC3): "SOP'",
- (SYNC1, SYNC3, SYNC1, SYNC3): 'SOP"',
- (SYNC1, RST2, RST2, SYNC3): "SOP' Debug",
- (SYNC1, RST2, SYNC3, SYNC2): 'SOP" Debug',
- (RST1, SYNC1, RST1, SYNC3): 'Cable Reset',
- (RST1, RST1, RST1, RST2): 'Hard Reset',
+ SOP_SEQUENCES[0]: 'SOP',
+ SOP_SEQUENCES[1]: "SOP'",
+ SOP_SEQUENCES[2]: 'SOP"',
+ SOP_SEQUENCES[3]: "SOP' Debug",
+ SOP_SEQUENCES[4]: 'SOP" Debug',
+ SOP_SEQUENCES[5]: 'Cable Reset',
+ SOP_SEQUENCES[6]: 'Hard Reset',
}
SYM_NAME = [
op_ma = ((rdo >> 10) & 0x3ff) * 10
max_ma = (rdo & 0x3ff) * 10
flags = ''
- for f in RDO_FLAGS.keys():
+ for f in sorted(RDO_FLAGS.keys(), reverse = True):
if rdo & f:
flags += ' ' + RDO_FLAGS[f]
return '[%d]%d/%d mA%s' % (pos, op_ma, max_ma, flags)
else:
p = ''
flags = ''
- for f in PDO_FLAGS.keys():
+ for f in sorted(PDO_FLAGS.keys(), reverse = True):
if pdo & f:
flags += ' ' + PDO_FLAGS[f]
return '%s%s%s' % (PDO_TYPE[t], p, flags)
else:
p = ''
flags = ''
- for f in PDO_FLAGS.keys():
+ for f in sorted(PDO_FLAGS.keys(), reverse = True):
if pdo & f:
flags += ' ' + PDO_FLAGS[f]
return '%s%s%s' % (PDO_TYPE[t], p, flags)
def find_corrupted_sop(self, k):
# Start of packet are valid even if they have only 3 correct symbols
# out of 4.
- for seq in START_OF_PACKETS.keys():
+ for seq in SOP_SEQUENCES:
if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
return START_OF_PACKETS[seq]
return None
for i in range(len(self.bits) - 19):
k = (self.get_sym(i, rec=False), self.get_sym(i+5, rec=False),
self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False))
- sym = START_OF_PACKETS[k] if k in START_OF_PACKETS else None
+ sym = START_OF_PACKETS.get(k, None)
if not sym:
sym = self.find_corrupted_sop(k)
# We have an interesting symbol sequence
return -1 # No Start Of Packet
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.idx = 0
self.packet_seq = 0
return 64 + len(self.data)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'usb_request'
name = 'USB request'
longname = 'Universal Serial Bus (LS/FS) transaction/request'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.request = {}
self.request_id = 0
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- self.secs_per_sample = float(1) / float(self.samplerate)
+ if self.samplerate:
+ self.secs_per_sample = float(1) / float(self.samplerate)
def start(self):
self.out_binary = self.register(srd.OUTPUT_BINARY)
request_end = self.handshake in ('ACK', 'STALL', 'timeout')
ep = self.transaction_ep
addr = self.transaction_addr
+
+ # Handle protocol STALLs, condition lasts until next SETUP transfer (8.5.3.4)
+ if self.transaction_type == 'SETUP' and (addr, ep) in self.request:
+ request = self.request[(addr,ep)]
+ if request['type'] in ('SETUP IN', 'SETUP OUT'):
+ request['es'] = self.ss_transaction
+ self.handle_request(0, 1)
+
if not (addr, ep) in self.request:
self.request[(addr, ep)] = {'setup_data': [], 'data': [],
'type': None, 'ss': self.ss_transaction, 'es': None,
request_started = 1
request = self.request[(addr,ep)]
+ if request_end:
+ request['es'] = self.es_transaction
+ request['handshake'] = self.handshake
+
# BULK or INTERRUPT transfer
if request['type'] in (None, 'BULK IN') and self.transaction_type == 'IN':
request['type'] = 'BULK IN'
request['data'] += self.transaction_data
- request['es'] = self.es_transaction
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
- request['es'] = self.es_transaction
self.handle_request(request_started, request_end)
# CONTROL, SETUP stage
# CONTROL, STATUS stage
elif request['type'] == 'SETUP IN' and self.transaction_type == 'OUT':
- request['es'] = self.es_transaction
self.handle_request(0, request_end)
elif request['type'] == 'SETUP OUT' and self.transaction_type == 'IN':
- request['es'] = self.es_transaction
self.handle_request(0, request_end)
else:
s += ' ]['
for b in request['data']:
s += ' %02X' % b
- s += ' ] : %s' % self.handshake
+ s += ' ] : %s' % request['handshake']
return s
def handle_request(self, request_start, request_end):
pass
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'usb_signalling'
name = 'USB signalling'
longname = 'Universal Serial Bus (LS/FS) signalling'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.samplerate = None
self.oldsym = 'J' # The "idle" state is J.
self.ss_block = None
self.samplenum_target = None
self.samplenum_edge = None
self.samplenum_lastedge = 0
- self.oldpins = None
self.edgepins = None
self.consecutive_ones = 0
self.bits = None
- self.state = 'INIT'
+ self.state = 'IDLE'
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.oldsym = 'J'
self.state = 'IDLE'
- def decode(self, ss, es, data):
+ def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- for (self.samplenum, pins) in data:
+
+ # Seed internal state from the very first sample.
+ pins = self.wait()
+ sym = symbols[self.options['signalling']][pins]
+ self.handle_idle(sym)
+
+ while True:
# State machine.
if self.state == 'IDLE':
- # Ignore identical samples early on (for performance reasons).
- if self.oldpins == pins:
- continue
- self.oldpins = pins
- sym = symbols[self.signalling][tuple(pins)]
+ # Wait for any edge on either DP and/or DM.
+ pins = self.wait([{0: 'e'}, {1: 'e'}])
+ sym = symbols[self.signalling][pins]
if sym == 'SE0':
self.samplenum_lastedge = self.samplenum
self.state = 'WAIT IDLE'
self.edgepins = pins
elif self.state in ('GET BIT', 'GET EOP'):
# Wait until we're in the middle of the desired bit.
- if self.samplenum == self.samplenum_edge:
- self.edgepins = pins
- if self.samplenum < self.samplenum_target:
- continue
- sym = symbols[self.signalling][tuple(pins)]
+ self.edgepins = self.wait([{'skip': self.samplenum_edge - self.samplenum}])
+ pins = self.wait([{'skip': self.samplenum_target - self.samplenum}])
+
+ sym = symbols[self.signalling][pins]
if self.state == 'GET BIT':
self.get_bit(sym)
elif self.state == 'GET EOP':
self.get_eop(sym)
- self.oldpins = pins
elif self.state == 'WAIT IDLE':
- if tuple(pins) == (0, 0):
- continue
+ # Skip "all-low" input. Wait for high level on either DP or DM.
+ pins = self.wait()
+ while not pins[0] and not pins[1]:
+ pins = self.wait([{0: 'h'}, {1: 'h'}])
if self.samplenum - self.samplenum_lastedge > 1:
- sym = symbols[self.options['signalling']][tuple(pins)]
+ sym = symbols[self.options['signalling']][pins]
self.handle_idle(sym)
else:
- sym = symbols[self.signalling][tuple(pins)]
+ sym = symbols[self.signalling][pins]
self.wait_for_sop(sym)
- self.oldpins = pins
self.edgepins = pins
- elif self.state == 'INIT':
- sym = symbols[self.options['signalling']][tuple(pins)]
- self.handle_idle(sym)
- self.oldpins = pins
import sigrokdecode as srd
+class SamplerateError(Exception):
+ pass
+
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'wiegand'
name = 'Wiegand'
longname = 'Wiegand interface'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.samplerate = None
self._samples_per_bit = 10
self._d0_prev = None
def start(self):
'Register output types and verify user supplied decoder values.'
self.out_ann = self.register(srd.OUTPUT_ANN)
- self._active = self.options['active'] == 'high' and 1 or 0
+ self._active = 1 if self.options['active'] == 'high' else 0
self._inactive = 1 - self._active
def metadata(self, key, value):
'Receive decoder metadata about the data stream.'
if key == srd.SRD_CONF_SAMPLERATE:
- ms_per_sample = 1000 * (1.0 / value)
- ms_per_bit = float(self.options['bitwidth_ms'])
- self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample)))
+ self.samplerate = value
+ if self.samplerate:
+ ms_per_sample = 1000 * (1.0 / self.samplerate)
+ ms_per_bit = float(self.options['bitwidth_ms'])
+ self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample)))
def _update_state(self, state, bit=None):
'Update state and bit values when they change.'
self._state = state
self._bits = []
- def decode(self, ss, es, data):
- for self.samplenum, (d0, d1) in data:
+ def decode(self):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ while True:
+ # TODO: Come up with more appropriate self.wait() conditions.
+ (d0, d1) = self.wait()
+
if d0 == self._d0_prev and d1 == self._d1_prev:
if self.es_bit and self.samplenum >= self.es_bit:
if (d0, d1) == (self._inactive, self._inactive):
ENHANCED_OPTS, AUX_TYPES)
class Decoder(srd.Decoder):
- api_version = 2
+ api_version = 3
id = 'xfp'
name = 'XFP'
longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
# Received data items, used as an index into samplenum/data
self.cnt = -1
# Start/end sample numbers per data item
)
def __init__(self):
+ self.reset()
+
+ def reset(self):
self.prev_cycle = Cycle.NONE
self.op_state = self.state_IDLE
def decode(self):
while True:
# TODO: Come up with more appropriate self.wait() conditions.
- pins = self.wait({'skip': 1})
+ pins = self.wait()
cycle = Cycle.NONE
if pins[Pin.MREQ] != 1: # default to asserted
if pins[Pin.RD] == 0:
PyObject *py_str, *py_bytes;
char *str = NULL;
+ /* Note: Caller already ran PyGILState_Ensure(). */
+
if (!py_obj)
return NULL;
PyObject *py_str, *py_bytes;
char *str = NULL;
+ /* Note: Caller already ran PyGILState_Ensure(). */
+
if (!py_obj)
return NULL;
PyObject *py_mod, *py_func, *py_tracefmt;
char *msg, *etype_name, *evalue_str, *tracefmt_str;
const char *etype_name_fallback;
+ PyGILState_STATE gstate;
py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL;
msg = g_strdup_vprintf(format, args);
va_end(args);
+ gstate = PyGILState_Ensure();
+
PyErr_Fetch(&py_etype, &py_evalue, &py_etraceback);
if (!py_etype) {
/* No current exception, so just print the message. */
/* Just in case. */
PyErr_Clear();
+ PyGILState_Release(gstate);
+
g_free(msg);
}
extern SRD_PRIV GSList *sessions;
-/* module_sigrokdecode.c */
-extern SRD_PRIV PyObject *srd_logic_type;
-
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 */
gint64 val_int;
int ret;
const char *val_str;
+ PyGILState_STATE gstate;
if (!di) {
srd_err("Invalid decoder instance.");
return SRD_ERR_ARG;
}
+ gstate = PyGILState_Ensure();
+
if (!PyObject_HasAttrString(di->decoder->py_dec, "options")) {
/* Decoder has no options. */
+ PyGILState_Release(gstate);
if (g_hash_table_size(options) == 0) {
/* No options provided. */
return SRD_OK;
srd_exception_catch("Stray exception in srd_inst_option_set()");
ret = SRD_ERR_PYTHON;
}
+ PyGILState_Release(gstate);
return ret;
}
}
pdch = sl->data;
new_channelmap[pdch->order] = new_channelnum;
- srd_dbg("Setting channel mapping: %s (index %d) = channel %d.",
+ srd_dbg("Setting channel mapping: %s (PD ch idx %d) = input data ch idx %d.",
pdch->id, pdch->order, new_channelnum);
}
srd_dbg("Final channel map:");
num_required_channels = g_slist_length(di->decoder->channels);
for (i = 0; i < di->dec_num_channels; i++) {
- srd_dbg(" - index %d = channel %d (%s)", i, new_channelmap[i],
+ GSList *l = g_slist_nth(di->decoder->channels, i);
+ if (!l)
+ l = g_slist_nth(di->decoder->opt_channels,
+ i - num_required_channels);
+ pdch = l->data;
+ srd_dbg(" - PD ch idx %d (%s) = input data ch idx %d (%s)", i,
+ pdch->id, new_channelmap[i],
(i < num_required_channels) ? "required" : "optional");
}
struct srd_decoder *dec;
struct srd_decoder_inst *di;
char *inst_id;
+ PyGILState_STATE gstate;
i = 1;
srd_dbg("Creating new %s instance.", decoder_id);
}
/* Default to the initial pins being the same as in sample 0. */
- di->old_pins_array = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t),
- di->dec_num_channels);
- g_array_set_size(di->old_pins_array, di->dec_num_channels);
- memset(di->old_pins_array->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0,
- di->dec_num_channels);
+ oldpins_array_seed(di);
+
+ gstate = PyGILState_Ensure();
/* Create a new instance of this decoder class. */
if (!(di->py_inst = PyObject_CallObject(dec->py_dec, NULL))) {
if (PyErr_Occurred())
srd_exception_catch("Failed to create %s instance",
decoder_id);
+ PyGILState_Release(gstate);
g_free(di->dec_channelmap);
g_free(di);
return NULL;
}
+ PyGILState_Release(gstate);
+
if (options && srd_inst_option_set(di, options) != SRD_OK) {
g_free(di->dec_channelmap);
g_free(di);
struct srd_decoder_inst *di_bottom,
struct srd_decoder_inst *di_top)
{
-
if (session_is_valid(sess) != SRD_OK) {
srd_err("Invalid session.");
return SRD_ERR_ARG;
}
s = g_string_sized_new(100);
+ oldpins_array_seed(di);
for (i = 0; i < di->dec_num_channels; i++) {
di->old_pins_array->data[i] = initial_pins->data[i];
g_string_append_printf(s, "%d, ", di->old_pins_array->data[i]);
return SRD_OK;
}
+/** @private */
+SRD_PRIV void oldpins_array_seed(struct srd_decoder_inst *di)
+{
+ size_t count;
+ GArray *arr;
+
+ if (!di)
+ return;
+ if (di->old_pins_array)
+ return;
+
+ srd_dbg("%s: Seeding old pins, %s().", di->inst_id, __func__);
+ count = di->dec_num_channels;
+ arr = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), count);
+ g_array_set_size(arr, count);
+ memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count);
+ di->old_pins_array = arr;
+}
+
/** @private */
SRD_PRIV void oldpins_array_free(struct srd_decoder_inst *di)
{
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);
+ gstate = PyGILState_Ensure();
+
/* Run self.start(). */
if (!(py_res = PyObject_CallMethod(di->py_inst, "start", NULL))) {
srd_exception_catch("Protocol decoder instance %s",
di->inst_id);
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
Py_DecRef(py_res);
/* Set self.matched to None. */
PyObject_SetAttrString(di->py_inst, "matched", Py_None);
+ PyGILState_Release(gstate);
+
/* Start all the PDs stacked on top of this one. */
for (l = di->next_di; l; l = l->next) {
next_di = l->data;
*/
static gboolean sample_matches(uint8_t old_sample, uint8_t sample, struct srd_term *term)
{
- if (!term)
- return FALSE;
+ /* Caller ensures term != NULL. */
switch (term->type) {
case SRD_TERM_HIGH:
if (!di || !di->dec_channelmap || !sample_pos)
return;
+ oldpins_array_seed(di);
for (i = 0; i < di->dec_num_channels; i++) {
byte_offset = di->dec_channelmap[i] / 8;
bit_offset = di->dec_channelmap[i] % 8;
sample_pos = di->inbuf + ((di->abs_cur_samplenum - di->abs_start_samplenum) * di->data_unitsize);
+ oldpins_array_seed(di);
for (i = 0; i < di->dec_num_channels; i++) {
if (di->old_pins_array->data[i] != SRD_INITIAL_PIN_SAME_AS_SAMPLE0)
continue;
uint8_t old_sample, sample;
int byte_offset, bit_offset, ch;
- if (!di || !di->dec_channelmap || !term || !sample_pos)
- return FALSE;
+ /* Caller ensures di, di->dec_channelmap, term, sample_pos != NULL. */
- /* Overwritten below (or ignored for SRD_TERM_SKIP). */
- old_sample = sample = 0;
+ if (term->type == SRD_TERM_SKIP)
+ return sample_matches(0, 0, term);
- if (term->type != SRD_TERM_SKIP) {
- ch = term->channel;
- byte_offset = di->dec_channelmap[ch] / 8;
- bit_offset = di->dec_channelmap[ch] % 8;
- sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
- old_sample = di->old_pins_array->data[ch];
- }
+ ch = term->channel;
+ byte_offset = di->dec_channelmap[ch] / 8;
+ bit_offset = di->dec_channelmap[ch] % 8;
+ sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
+ old_sample = di->old_pins_array->data[ch];
return sample_matches(old_sample, sample, term);
}
const GSList *l;
struct srd_term *term;
- if (!di || !cond || !sample_pos)
- return FALSE;
+ /* Caller ensures di, cond, sample_pos != NULL. */
for (l = cond; l; l = l->next) {
term = l->data;
{
unsigned int i;
- if (!di)
- return FALSE;
+ /* Caller ensures di != NULL. */
for (i = 0; i < num_conditions; i++) {
if (di->match_array->data[i])
static gboolean find_match(struct srd_decoder_inst *di)
{
- static uint64_t s = 0;
uint64_t i, j, num_samples_to_process;
GSList *l, *cond;
const uint8_t *sample_pos;
unsigned int num_conditions;
+ /* Caller ensures di != NULL. */
+
/* Check whether the condition list is NULL/empty. */
if (!di->condition_list) {
srd_dbg("NULL/empty condition list, automatic match.");
if (di->abs_cur_samplenum == 0)
update_old_pins_array_initial_pins(di);
- for (i = 0, s = 0; i < num_samples_to_process; i++, s++, (di->abs_cur_samplenum)++) {
+ for (i = 0; i < num_samples_to_process; i++, (di->abs_cur_samplenum)++) {
sample_pos = di->inbuf + ((di->abs_cur_samplenum - di->abs_start_samplenum) * di->data_unitsize);
PyObject *py_res;
struct srd_decoder_inst *di;
int wanted_term;
+ PyGILState_STATE gstate;
if (!data)
return NULL;
srd_dbg("%s: Starting thread routine for decoder.", di->inst_id);
+ gstate = PyGILState_Ensure();
+
/*
* Call self.decode(). Only returns if the PD throws an exception.
* "Regular" termination of the decode() method is not expected.
*/
srd_dbg("%s: Thread done (!res, want_term).", di->inst_id);
PyErr_Clear();
+ PyGILState_Release(gstate);
return NULL;
}
if (!py_res) {
srd_dbg("%s: decode() terminated unrequested.", di->inst_id);
srd_exception_catch("Protocol decoder instance %s: ", di->inst_id);
srd_dbg("%s: Thread done (!res, !want_term).", di->inst_id);
+ PyGILState_Release(gstate);
return NULL;
}
Py_DecRef(py_res);
PyErr_Clear();
+ PyGILState_Release(gstate);
+
srd_dbg("%s: Thread done (with res).", di->inst_id);
return NULL;
uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize)
{
- PyObject *py_res;
- srd_logic *logic;
- long apiver;
-
/* Return an error upon unusable input. */
if (!di) {
srd_dbg("empty decoder instance");
abs_end_samplenum - abs_start_samplenum, inbuflen, di->data_unitsize,
di->inst_id);
- apiver = srd_decoder_apiver(di->decoder);
+ /* If this is the first call, start the worker thread. */
+ if (!di->thread_handle) {
+ srd_dbg("No worker thread for this decoder stack "
+ "exists yet, creating one: %s.", di->inst_id);
+ di->thread_handle = g_thread_new(di->inst_id,
+ di_thread, di);
+ }
- if (apiver == 2) {
- /*
- * Create new srd_logic object. Each iteration around the PD's
- * loop will fill one sample into this object.
- */
- logic = PyObject_New(srd_logic, (PyTypeObject *)srd_logic_type);
- Py_INCREF(logic);
- logic->di = (struct srd_decoder_inst *)di;
- logic->abs_start_samplenum = abs_start_samplenum;
- logic->itercnt = 0;
- logic->inbuf = (uint8_t *)inbuf;
- logic->inbuflen = inbuflen;
- logic->sample = PyList_New(2);
- Py_INCREF(logic->sample);
-
- Py_IncRef(di->py_inst);
- if (!(py_res = PyObject_CallMethod(di->py_inst, "decode",
- "KKO", abs_start_samplenum, abs_end_samplenum, logic))) {
- srd_exception_catch("Protocol decoder instance %s",
- di->inst_id);
- return SRD_ERR_PYTHON;
- }
- di->abs_cur_samplenum = abs_end_samplenum;
- Py_DecRef(py_res);
- } else {
- /* If this is the first call, start the worker thread. */
- if (!di->thread_handle) {
- srd_dbg("No worker thread for this decoder stack "
- "exists yet, creating one: %s.", di->inst_id);
- di->thread_handle = g_thread_new(di->inst_id,
- di_thread, di);
- }
+ /* Push the new sample chunk to the worker thread. */
+ g_mutex_lock(&di->data_mutex);
+ di->abs_start_samplenum = abs_start_samplenum;
+ di->abs_end_samplenum = abs_end_samplenum;
+ di->inbuf = inbuf;
+ di->inbuflen = inbuflen;
+ di->got_new_samples = TRUE;
+ di->handled_all_samples = FALSE;
+ di->want_wait_terminate = FALSE;
- /* Push the new sample chunk to the worker thread. */
- g_mutex_lock(&di->data_mutex);
- di->abs_start_samplenum = abs_start_samplenum;
- di->abs_end_samplenum = abs_end_samplenum;
- di->inbuf = inbuf;
- di->inbuflen = inbuflen;
- di->got_new_samples = TRUE;
- di->handled_all_samples = FALSE;
- di->want_wait_terminate = FALSE;
-
- /* Signal the thread that we have new data. */
- g_cond_signal(&di->got_new_samples_cond);
- g_mutex_unlock(&di->data_mutex);
-
- /* When all samples in this chunk were handled, return. */
- g_mutex_lock(&di->data_mutex);
- while (!di->handled_all_samples && !di->want_wait_terminate)
- g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
- g_mutex_unlock(&di->data_mutex);
+ /* Signal the thread that we have new data. */
+ g_cond_signal(&di->got_new_samples_cond);
+ g_mutex_unlock(&di->data_mutex);
+
+ /* When all samples in this chunk were handled, return. */
+ g_mutex_lock(&di->data_mutex);
+ while (!di->handled_all_samples && !di->want_wait_terminate)
+ g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
+ g_mutex_unlock(&di->data_mutex);
+
+ return SRD_OK;
+}
+
+/**
+ * Terminate current decoder work, prepare for re-use on new input data.
+ *
+ * Terminates all decoder operations in the specified decoder instance
+ * and the instances stacked on top of it. Resets internal state such
+ * that the previously constructed stack can process new input data that
+ * is not related to previously processed input data. This avoids the
+ * expensive and complex re-construction of decoder stacks.
+ *
+ * Callers are expected to follow up with start, metadata, and decode
+ * calls like they would for newly constructed decoder stacks.
+ *
+ * @param di The decoder instance to call. Must not be NULL.
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ * @private
+ */
+SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di)
+{
+ PyGILState_STATE gstate;
+ PyObject *py_ret;
+ GSList *l;
+ int ret;
+
+ if (!di)
+ return SRD_ERR_ARG;
+
+ /*
+ * Request termination and wait for previously initiated
+ * background operation to finish. Reset internal state, but
+ * do not start releasing resources yet. This shall result in
+ * decoders' state just like after creation. This block handles
+ * the C language library side.
+ */
+ srd_dbg("Terminating instance %s", di->inst_id);
+ srd_inst_join_decode_thread(di);
+ srd_inst_reset_state(di);
+
+ /*
+ * Have the Python side's .reset() method executed (if the PD
+ * implements it). It's assumed that .reset() assigns variables
+ * very much like __init__() used to do in the past. Thus memory
+ * that was allocated in previous calls gets released by Python
+ * as it's not referenced any longer.
+ */
+ gstate = PyGILState_Ensure();
+ if (PyObject_HasAttrString(di->py_inst, "reset")) {
+ srd_dbg("Calling .reset() of instance %s", di->inst_id);
+ py_ret = PyObject_CallMethod(di->py_inst, "reset", NULL);
+ Py_XDECREF(py_ret);
+ }
+ PyGILState_Release(gstate);
+
+ /*
+ * Pass the "restart" request to all stacked decoders.
+ */
+ for (l = di->next_di; l; l = l->next) {
+ ret = srd_inst_terminate_reset(l->data);
+ if (ret != SRD_OK)
+ return ret;
}
return SRD_OK;
{
GSList *l;
struct srd_pd_output *pdo;
+ PyGILState_STATE gstate;
srd_dbg("Freeing instance %s", di->inst_id);
srd_inst_join_decode_thread(di);
+
srd_inst_reset_state(di);
+ gstate = PyGILState_Ensure();
Py_DecRef(di->py_inst);
+ PyGILState_Release(gstate);
+
g_free(di->inst_id);
g_free(di->dec_channelmap);
g_free(di->channel_samples);
uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match);
+SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di);
SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di);
SRD_PRIV void srd_inst_free_all(struct srd_session *sess);
/* srd.c */
SRD_API int srd_init(const char *path);
SRD_API int srd_exit(void);
+SRD_API GSList *srd_searchpaths_get(void);
/* session.c */
SRD_API int srd_session_new(struct srd_session **sess);
SRD_API int srd_session_send(struct srd_session *sess,
uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
+SRD_API int srd_session_terminate_reset(struct srd_session *sess);
SRD_API int srd_session_destroy(struct srd_session *sess);
SRD_API int srd_pd_output_callback_add(struct srd_session *sess,
int output_type, srd_pd_output_callback cb, void *cb_data);
/** @cond PRIVATE */
-SRD_PRIV PyObject *srd_logic_type = NULL;
-
/*
* When initialized, a reference to this module inside the Python interpreter
* lives here.
/** @cond PRIVATE */
PyMODINIT_FUNC PyInit_sigrokdecode(void)
{
- PyObject *mod, *Decoder_type, *logic_type;
+ PyObject *mod, *Decoder_type;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
mod = PyModule_Create(&sigrokdecode_module);
if (!mod)
if (PyModule_AddObject(mod, "Decoder", Decoder_type) < 0)
goto err_out;
- logic_type = srd_logic_type_new();
- if (!logic_type)
- goto err_out;
- if (PyModule_AddObject(mod, "srd_logic", logic_type) < 0)
- goto err_out;
-
/* Expose output types as symbols in the sigrokdecode module */
if (PyModule_AddIntConstant(mod, "OUTPUT_ANN", SRD_OUTPUT_ANN) < 0)
goto err_out;
if (PyModule_AddIntConstant(mod, "SRD_CONF_SAMPLERATE", SRD_CONF_SAMPLERATE) < 0)
goto err_out;
- srd_logic_type = logic_type;
mod_sigrokdecode = mod;
+ PyGILState_Release(gstate);
+
return mod;
err_out:
Py_XDECREF(mod);
srd_exception_catch("Failed to initialize module");
+ PyGILState_Release(gstate);
return NULL;
}
GSList *l;
struct srd_decoder_inst *next_di;
int ret;
+ PyGILState_STATE gstate;
if (key != SRD_CONF_SAMPLERATE)
/* This is the only key we pass on to the decoder for now. */
return SRD_OK;
+ gstate = PyGILState_Ensure();
+
if (PyObject_HasAttrString(di->py_inst, "metadata")) {
py_ret = PyObject_CallMethod(di->py_inst, "metadata", "lK",
(long)SRD_CONF_SAMPLERATE,
Py_XDECREF(py_ret);
}
+ PyGILState_Release(gstate);
+
/* Push metadata to all the PDs stacked on top of this one. */
for (l = di->next_di; l; l = l->next) {
next_di = l->data;
return SRD_ERR_ARG;
}
- srd_dbg("Setting session %d samplerate to %"PRIu64".",
+ srd_dbg("Setting session %d samplerate to %"G_GUINT64_FORMAT".",
sess->session_id, g_variant_get_uint64(data));
ret = SRD_OK;
return SRD_OK;
}
+/**
+ * Terminate currently executing decoders in a session, reset internal state.
+ *
+ * All decoder instances have their .wait() method terminated, which
+ * shall terminate .decode() as well. Afterwards the decoders' optional
+ * .reset() method gets executed.
+ *
+ * This routine allows callers to abort pending expensive operations,
+ * when they are no longer interested in the decoders' results. Note
+ * that the decoder state is lost and aborted work cannot resume.
+ *
+ * This routine also allows callers to re-use previously created decoder
+ * stacks to process new input data which is not related to previously
+ * processed input data. This avoids the necessity to re-construct the
+ * decoder stack.
+ *
+ * @param sess The session in which to terminate decoders.
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ *
+ * @since 0.6.0
+ */
+SRD_API int srd_session_terminate_reset(struct srd_session *sess)
+{
+ GSList *d;
+ int ret;
+
+ if (session_is_valid(sess) != SRD_OK) {
+ srd_err("Invalid session.");
+ return SRD_ERR_ARG;
+ }
+
+ for (d = sess->di_list; d; d = d->next) {
+ ret = srd_inst_terminate_reset(d->data);
+ if (ret != SRD_OK)
+ return ret;
+ }
+ return SRD_OK;
+}
+
/**
* Destroy a decoding session.
*
g_free(str);
}
+static int print_searchpaths(void)
+{
+ PyObject *py_paths, *py_path, *py_bytes;
+ PyGILState_STATE gstate;
+ GString *s;
+ GSList *l;
+ int i;
+
+ s = g_string_sized_new(500);
+ g_string_append(s, "Protocol decoder search paths:\n");
+ for (l = searchpaths; l; l = l->next)
+ g_string_append_printf(s, " - %s\n", (const char *)l->data);
+ s->str[s->len - 1] = '\0';
+ srd_dbg("%s", s->str);
+ g_string_free(s, TRUE);
+
+ gstate = PyGILState_Ensure();
+
+ py_paths = PySys_GetObject("path");
+ if (!py_paths)
+ goto err;
+
+ s = g_string_sized_new(500);
+ g_string_append(s, "Python system search paths:\n");
+ for (i = 0; i < PyList_Size(py_paths); i++) {
+ py_path = PyList_GetItem(py_paths, i);
+ py_bytes = PyUnicode_AsUTF8String(py_path);
+ g_string_append_printf(s, " - %s\n", PyBytes_AsString(py_bytes));
+ }
+ s->str[s->len - 1] = '\0';
+ srd_dbg("%s", s->str);
+ g_string_free(s, TRUE);
+
+ PyGILState_Release(gstate);
+
+ return SRD_OK;
+
+err:
+ srd_err("Unable to query Python system search paths.");
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
+SRD_API GSList *srd_searchpaths_get(void)
+{
+ return g_slist_copy_deep(searchpaths, (GCopyFunc)g_strdup, NULL);
+}
+
/**
* Initialize libsigrokdecode.
*
}
}
+ /* Initialize the Python GIL (this also happens to acquire it). */
+ PyEval_InitThreads();
+
+ /* Release the GIL (ignore return value, we don't need it here). */
+ (void)PyEval_SaveThread();
+
max_session_id = 0;
+ print_searchpaths();
+
return SRD_OK;
}
g_slist_free_full(searchpaths, g_free);
searchpaths = NULL;
+ /*
+ * Acquire the GIL, otherwise Py_Finalize() might have issues.
+ * Ignore the return value, we don't need it here.
+ */
+ if (Py_IsInitialized())
+ (void)PyGILState_Ensure();
+
/* Py_Finalize() returns void, any finalization errors are ignored. */
Py_Finalize();
+ /* Note: No need to release the GIL since Python is shut down now. */
+
max_session_id = -1;
return SRD_OK;
SRD_PRIV int srd_decoder_searchpath_add(const char *path)
{
PyObject *py_cur_path, *py_item;
+ PyGILState_STATE gstate;
srd_dbg("Adding '%s' to module path.", path);
+ gstate = PyGILState_Ensure();
+
py_cur_path = PySys_GetObject("path");
if (!py_cur_path)
- return SRD_ERR_PYTHON;
+ goto err;
py_item = PyUnicode_FromString(path);
if (!py_item) {
srd_exception_catch("Failed to create Unicode object");
- return SRD_ERR_PYTHON;
+ goto err;
}
if (PyList_Insert(py_cur_path, 0, py_item) < 0) {
srd_exception_catch("Failed to insert path element");
Py_DECREF(py_item);
- return SRD_ERR_PYTHON;
+ goto err;
}
Py_DECREF(py_item);
+ PyGILState_Release(gstate);
+
searchpaths = g_slist_prepend(searchpaths, g_strdup(path));
return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
/** @} */
}
END_TEST
+/*
+ * Check whether srd_session_terminate_reset() succeeds on newly created
+ * sessions, as well as after calling start() and meta(). No data is fed
+ * to decoders here.
+ */
+START_TEST(test_session_reset_nodata)
+{
+ struct srd_session *sess;
+ int ret;
+ GVariant *data;
+
+ srd_init(NULL);
+ srd_session_new(&sess);
+ ret = srd_session_terminate_reset(sess);
+ fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret);
+ ret = srd_session_start(sess);
+ fail_unless(ret == SRD_OK, "srd_session_start() failed: %d.", ret);
+ ret = srd_session_terminate_reset(sess);
+ fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret);
+ data = g_variant_new_uint64(1000000);
+ ret = srd_session_metadata_set(sess, SRD_CONF_SAMPLERATE, data);
+ fail_unless(ret == SRD_OK, "srd_session_metadata_set() failed: %d.", ret);
+ ret = srd_session_terminate_reset(sess);
+ fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret);
+ ret = srd_session_destroy(sess);
+ fail_unless(ret == SRD_OK, "srd_session_destroy() failed: %d.", ret);
+ srd_exit();
+}
+END_TEST
+
Suite *suite_session(void)
{
Suite *s;
tcase_add_test(tc, test_session_metadata_set_bogus);
suite_add_tcase(s, tc);
+ tc = tcase_create("reset");
+ tcase_add_test(tc, test_session_reset_nodata);
+ suite_add_tcase(s, tc);
+
return s;
}
struct srd_proto_data_annotation *pda;
int ann_class;
char **ann_text;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
/* Should be a list of [annotation class, [string, ...]]. */
if (!PyList_Check(obj)) {
srd_err("Protocol decoder %s submitted an annotation that"
" is not a list", di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
/* Should have 2 elements. */
srd_err("Protocol decoder %s submitted annotation list with "
"%zd elements instead of 2", di->decoder->name,
PyList_Size(obj));
- return SRD_ERR_PYTHON;
+ goto err;
}
/*
if (!PyLong_Check(py_tmp)) {
srd_err("Protocol decoder %s submitted annotation list, but "
"first element was not an integer.", di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
ann_class = PyLong_AsLong(py_tmp);
if (!(pdo = g_slist_nth_data(di->decoder->annotations, ann_class))) {
srd_err("Protocol decoder %s submitted data to unregistered "
"annotation class %d.", di->decoder->name, ann_class);
- return SRD_ERR_PYTHON;
+ goto err;
}
/* Second element must be a list. */
if (!PyList_Check(py_tmp)) {
srd_err("Protocol decoder %s submitted annotation list, but "
"second element was not a list.", di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
if (py_strseq_to_char(py_tmp, &ann_text) != SRD_OK) {
srd_err("Protocol decoder %s submitted annotation list, but "
"second element was malformed.", di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
pda = g_malloc(sizeof(struct srd_proto_data_annotation));
pda->ann_text = ann_text;
pdata->data = pda;
+ PyGILState_Release(gstate);
+
return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
Py_ssize_t size;
int bin_class;
char *class_name, *buf;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
/* Should be a list of [binary class, bytes]. */
if (!PyList_Check(obj)) {
srd_err("Protocol decoder %s submitted non-list for SRD_OUTPUT_BINARY.",
di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
/* Should have 2 elements. */
srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list "
"with %zd elements instead of 2", di->decoder->name,
PyList_Size(obj));
- return SRD_ERR_PYTHON;
+ goto err;
}
/* The first element should be an integer. */
if (!PyLong_Check(py_tmp)) {
srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
"but first element was not an integer.", di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
bin_class = PyLong_AsLong(py_tmp);
if (!(class_name = g_slist_nth_data(di->decoder->binary, bin_class))) {
srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with "
"unregistered binary class %d.", di->decoder->name, bin_class);
- return SRD_ERR_PYTHON;
+ goto err;
}
/* Second element should be bytes. */
if (!PyBytes_Check(py_tmp)) {
srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
"but second element was not bytes.", di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
/* Consider an empty set of bytes a bug. */
if (PyBytes_Size(py_tmp) == 0) {
srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY "
"with empty data set.", di->decoder->name);
- return SRD_ERR_PYTHON;
+ goto err;
}
pdb = g_malloc(sizeof(struct srd_proto_data_binary));
if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1)
- return SRD_ERR_PYTHON;
+ goto err;
+
+ PyGILState_Release(gstate);
+
pdb->bin_class = bin_class;
pdb->size = size;
if (!(pdb->data = g_try_malloc(pdb->size)))
pdata->data = pdb;
return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
static int convert_meta(struct srd_proto_data *pdata, PyObject *obj)
{
long long intvalue;
double dvalue;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (pdata->pdo->meta_type == G_VARIANT_TYPE_INT64) {
if (!PyLong_Check(obj)) {
PyErr_Format(PyExc_TypeError, "This output was registered "
"as 'int', but something else was passed.");
- return SRD_ERR_PYTHON;
+ goto err;
}
intvalue = PyLong_AsLongLong(obj);
if (PyErr_Occurred())
- return SRD_ERR_PYTHON;
+ goto err;
pdata->data = g_variant_new_int64(intvalue);
} else if (pdata->pdo->meta_type == G_VARIANT_TYPE_DOUBLE) {
if (!PyFloat_Check(obj)) {
PyErr_Format(PyExc_TypeError, "This output was registered "
"as 'float', but something else was passed.");
- return SRD_ERR_PYTHON;
+ goto err;
}
dvalue = PyFloat_AsDouble(obj);
if (PyErr_Occurred())
- return SRD_ERR_PYTHON;
+ goto err;
pdata->data = g_variant_new_double(dvalue);
}
+ PyGILState_Release(gstate);
+
return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
static PyObject *Decoder_put(PyObject *self, PyObject *args)
PyObject *py_data, *py_res;
struct srd_decoder_inst *di, *next_di;
struct srd_pd_output *pdo;
- struct srd_proto_data *pdata;
+ struct srd_proto_data pdata;
uint64_t start_sample, end_sample;
int output_id;
struct srd_pd_callback *cb;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (!(di = srd_inst_find_by_obj(NULL, self))) {
/* Shouldn't happen. */
srd_dbg("put(): self instance not found.");
- return NULL;
+ goto err;
}
if (!PyArg_ParseTuple(args, "KKiO", &start_sample, &end_sample,
* Python raise it. This results in a much better trace in
* controller.c on the decode() method call.
*/
- return NULL;
+ goto err;
}
if (!(l = g_slist_nth(di->pd_output, output_id))) {
srd_err("Protocol decoder %s submitted invalid output ID %d.",
di->decoder->name, output_id);
- return NULL;
+ goto err;
}
pdo = l->data;
di->inst_id, start_sample, end_sample,
output_type_name(pdo->output_type), output_id);
- pdata = g_malloc0(sizeof(struct srd_proto_data));
- pdata->start_sample = start_sample;
- pdata->end_sample = end_sample;
- pdata->pdo = pdo;
+ pdata.start_sample = start_sample;
+ pdata.end_sample = end_sample;
+ pdata.pdo = pdo;
+ pdata.data = NULL;
switch (pdo->output_type) {
case SRD_OUTPUT_ANN:
/* Annotations are only fed to callbacks. */
if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
/* Convert from PyDict to srd_proto_data_annotation. */
- if (convert_annotation(di, py_data, pdata) != SRD_OK) {
+ if (convert_annotation(di, py_data, &pdata) != SRD_OK) {
/* An error was already logged. */
break;
}
- cb->cb(pdata, cb->cb_data);
+ Py_BEGIN_ALLOW_THREADS
+ cb->cb(&pdata, cb->cb_data);
+ Py_END_ALLOW_THREADS
}
break;
case SRD_OUTPUT_PYTHON:
if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
/* Frontends aren't really supposed to get Python
* callbacks, but it's useful for testing. */
- pdata->data = py_data;
- cb->cb(pdata, cb->cb_data);
+ pdata.data = py_data;
+ cb->cb(&pdata, cb->cb_data);
}
break;
case SRD_OUTPUT_BINARY:
if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
/* Convert from PyDict to srd_proto_data_binary. */
- if (convert_binary(di, py_data, pdata) != SRD_OK) {
+ if (convert_binary(di, py_data, &pdata) != SRD_OK) {
/* An error was already logged. */
break;
}
- cb->cb(pdata, cb->cb_data);
+ Py_BEGIN_ALLOW_THREADS
+ cb->cb(&pdata, cb->cb_data);
+ Py_END_ALLOW_THREADS
}
break;
case SRD_OUTPUT_META:
if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
/* Annotations need converting from PyObject. */
- if (convert_meta(pdata, py_data) != SRD_OK) {
+ if (convert_meta(&pdata, py_data) != SRD_OK) {
/* An exception was already set up. */
break;
}
- cb->cb(pdata, cb->cb_data);
+ Py_BEGIN_ALLOW_THREADS
+ cb->cb(&pdata, cb->cb_data);
+ Py_END_ALLOW_THREADS
}
break;
default:
break;
}
- g_free(pdata);
+ PyGILState_Release(gstate);
Py_RETURN_NONE;
+
+err:
+ PyGILState_Release(gstate);
+
+ return NULL;
}
static PyObject *Decoder_register(PyObject *self, PyObject *args,
int output_type;
char *proto_id, *meta_name, *meta_descr;
char *keywords[] = {"output_type", "proto_id", "meta", NULL};
+ PyGILState_STATE gstate;
+ gboolean is_meta;
+ GSList *l;
+ struct srd_pd_output *cmp;
+
+ gstate = PyGILState_Ensure();
meta_type_py = NULL;
meta_type_gv = NULL;
if (!(di = srd_inst_find_by_obj(NULL, self))) {
PyErr_SetString(PyExc_Exception, "decoder instance not found");
- return NULL;
+ goto err;
}
/* Default to instance id, which defaults to class id. */
&output_type, &proto_id,
&meta_type_py, &meta_name, &meta_descr)) {
/* Let Python raise this exception. */
- return NULL;
+ goto err;
}
/* Check if the meta value's type is supported. */
- if (output_type == SRD_OUTPUT_META) {
+ is_meta = output_type == SRD_OUTPUT_META;
+ if (is_meta) {
if (meta_type_py == &PyLong_Type)
meta_type_gv = G_VARIANT_TYPE_INT64;
else if (meta_type_py == &PyFloat_Type)
meta_type_gv = G_VARIANT_TYPE_DOUBLE;
else {
PyErr_Format(PyExc_TypeError, "Unsupported type.");
- return NULL;
+ goto err;
}
}
+ srd_dbg("Instance %s checking registration type %d for %s.",
+ di->inst_id, output_type, proto_id);
+ pdo = NULL;
+ for (l = di->pd_output; l; l = l->next) {
+ cmp = l->data;
+ if (cmp->output_type != output_type)
+ continue;
+ if (strcmp(cmp->proto_id, proto_id) != 0)
+ continue;
+ if (is_meta && cmp->meta_type != meta_type_gv)
+ continue;
+ if (is_meta && strcmp(cmp->meta_name, meta_name) != 0)
+ continue;
+ if (is_meta && strcmp(cmp->meta_descr, meta_descr) != 0)
+ continue;
+ pdo = cmp;
+ break;
+ }
+ if (pdo) {
+ py_new_output_id = Py_BuildValue("i", pdo->pdo_id);
+ PyGILState_Release(gstate);
+ return py_new_output_id;
+ }
+
srd_dbg("Instance %s creating new output type %d for %s.",
di->inst_id, output_type, proto_id);
di->pd_output = g_slist_append(di->pd_output, pdo);
py_new_output_id = Py_BuildValue("i", pdo->pdo_id);
+ PyGILState_Release(gstate);
+
return py_new_output_id;
+
+err:
+ PyGILState_Release(gstate);
+
+ return NULL;
}
static int get_term_type(const char *v)
const uint8_t *sample_pos;
int byte_offset, bit_offset;
PyObject *py_pinvalues;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (!di) {
srd_err("Invalid decoder instance.");
+ PyGILState_Release(gstate);
return NULL;
}
}
}
+ PyGILState_Release(gstate);
+
return py_pinvalues;
}
struct srd_term *term;
uint64_t num_samples_to_skip;
char *term_str;
+ PyGILState_STATE gstate;
if (!py_dict || !term_list)
return SRD_ERR_ARG;
/* "Create" an empty GSList of terms. */
*term_list = NULL;
+ gstate = PyGILState_Ensure();
+
/* Iterate over all items in the current dict. */
while (PyDict_Next(py_dict, &pos, &py_key, &py_value)) {
/* Check whether the current key is a string or a number. */
/* Get the value string. */
if ((py_pydictitem_as_str(py_dict, py_key, &term_str)) != SRD_OK) {
srd_err("Failed to get the value.");
- return SRD_ERR;
+ goto err;
}
term = g_malloc0(sizeof(struct srd_term));
term->type = get_term_type(term_str);
/* TODO: Check if it's "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.");
- return SRD_ERR;
+ goto err;
}
term = g_malloc0(sizeof(struct srd_term));
term->type = SRD_TERM_SKIP;
term->num_samples_already_skipped = 0;
} else {
srd_err("Term key is neither a string nor a number.");
- return SRD_ERR;
+ goto err;
}
/* Add the term to the list of terms. */
*term_list = g_slist_append(*term_list, term);
}
+ PyGILState_Release(gstate);
+
return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR;
}
/**
GSList *term_list;
PyObject *py_conditionlist, *py_conds, *py_dict;
int i, num_conditions, ret;
+ PyGILState_STATE gstate;
if (!self || !args)
return SRD_ERR_ARG;
+ gstate = PyGILState_Ensure();
+
/* Get the decoder instance. */
if (!(di = srd_inst_find_by_obj(NULL, self))) {
PyErr_SetString(PyExc_Exception, "decoder instance not found");
- return SRD_ERR;
+ goto err;
}
/*
*/
if (di->want_wait_terminate) {
srd_dbg("%s: %s: Skip (want_term).", di->inst_id, __func__);
- return SRD_ERR;
+ goto err;
}
- /* Parse the argument of self.wait() into 'py_conds'. */
- if (!PyArg_ParseTuple(args, "O", &py_conds)) {
+ /*
+ * Parse the argument of self.wait() into 'py_conds', and check
+ * the data type. The argument is optional, None is assumed in
+ * its absence. None or an empty dict or an empty list mean that
+ * there is no condition, and the next available sample shall
+ * get returned to the caller.
+ */
+ py_conds = Py_None;
+ if (!PyArg_ParseTuple(args, "|O", &py_conds)) {
/* Let Python raise this exception. */
- return SRD_ERR;
+ goto err;
}
-
- /* Check whether 'py_conds' is a dict or a list. */
- if (PyList_Check(py_conds)) {
+ if (py_conds == Py_None) {
+ /* 'py_conds' is None. */
+ goto ret_9999;
+ } else if (PyList_Check(py_conds)) {
/* 'py_conds' is a list. */
py_conditionlist = py_conds;
num_conditions = PyList_Size(py_conditionlist);
if (num_conditions == 0)
- return 9999; /* The PD invoked self.wait([]). */
+ goto ret_9999; /* The PD invoked self.wait([]). */
Py_IncRef(py_conditionlist);
} else if (PyDict_Check(py_conds)) {
/* 'py_conds' is a dict. */
if (PyDict_Size(py_conds) == 0)
- return 9999; /* The PD invoked self.wait({}). */
+ 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);
num_conditions = 1;
} else {
srd_err("Condition list is neither a list nor a dict.");
- return SRD_ERR;
+ goto err;
}
/* Free the old condition list. */
Py_DecRef(py_conditionlist);
+ PyGILState_Release(gstate);
+
return ret;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR;
+
+ret_9999:
+ PyGILState_Release(gstate);
+
+ return 9999;
+}
+
+/**
+ * Create a SKIP condition list for condition-less .wait() calls.
+ *
+ * @param di Decoder instance.
+ * @param count Number of samples to skip.
+ *
+ * @retval SRD_OK The new condition list was set successfully.
+ * @retval SRD_ERR There was an error setting the new condition list.
+ * The contents of di->condition_list are undefined.
+ *
+ * This routine is a reduced and specialized version of the @ref
+ * set_new_condition_list() and @ref create_term_list() routines which
+ * gets invoked when .wait() was called without specifications for
+ * conditions. This minor duplication of the SKIP term list creation
+ * simplifies the logic and avoids the creation of expensive Python
+ * objects with "constant" values which the caller did not pass in the
+ * first place. It results in maximum sharing of match handling code
+ * paths.
+ */
+static int set_skip_condition(struct srd_decoder_inst *di, uint64_t count)
+{
+ struct srd_term *term;
+ GSList *term_list;
+
+ condition_list_free(di);
+ term = g_malloc0(sizeof(*term));
+ term->type = SRD_TERM_SKIP;
+ term->num_samples_to_skip = count;
+ term->num_samples_already_skipped = 0;
+ term_list = g_slist_append(NULL, term);
+ di->condition_list = g_slist_append(di->condition_list, term_list);
+
+ return SRD_OK;
}
static PyObject *Decoder_wait(PyObject *self, PyObject *args)
{
int ret;
+ uint64_t skip_count;
unsigned int i;
gboolean found_match;
struct srd_decoder_inst *di;
PyObject *py_pinvalues, *py_matched;
+ PyGILState_STATE gstate;
if (!self || !args)
return NULL;
+ gstate = PyGILState_Ensure();
+
if (!(di = srd_inst_find_by_obj(NULL, self))) {
PyErr_SetString(PyExc_Exception, "decoder instance not found");
+ PyGILState_Release(gstate);
Py_RETURN_NONE;
}
ret = set_new_condition_list(self, args);
if (ret < 0) {
srd_dbg("%s: %s: Aborting wait().", di->inst_id, __func__);
- return NULL;
+ goto err;
}
if (ret == 9999) {
- /* Empty condition list, automatic match. */
- PyObject_SetAttrString(di->py_inst, "matched", Py_None);
- /* Leave self.samplenum unchanged (== di->abs_cur_samplenum). */
- return get_current_pinvalues(di);
+ /*
+ * Empty condition list, automatic match. Arrange for the
+ * execution of regular match handling code paths such that
+ * the next available sample is returned to the caller.
+ * Make sure to skip one sample when "anywhere within the
+ * stream", yet make sure to not skip sample number 0.
+ */
+ if (di->abs_cur_samplenum)
+ skip_count = 1;
+ else if (!di->condition_list)
+ skip_count = 0;
+ else
+ skip_count = 1;
+ ret = set_skip_condition(di, skip_count);
+ if (ret < 0) {
+ srd_dbg("%s: %s: Cannot setup condition-less wait().",
+ di->inst_id, __func__);
+ goto err;
+ }
}
while (1) {
+
+ Py_BEGIN_ALLOW_THREADS
+
/* Wait for new samples to process, or termination request. */
g_mutex_lock(&di->data_mutex);
while (!di->got_new_samples && !di->want_wait_terminate)
found_match = FALSE;
ret = process_samples_until_condition_match(di, &found_match);
+ Py_END_ALLOW_THREADS
+
/* If there's a match, set self.samplenum etc. and return. */
if (found_match) {
/* Set self.samplenum to the (absolute) sample number that matched. */
} else {
PyObject_SetAttrString(di->py_inst, "matched", Py_None);
}
-
+
py_pinvalues = get_current_pinvalues(di);
g_mutex_unlock(&di->data_mutex);
+ PyGILState_Release(gstate);
+
return py_pinvalues;
}
srd_dbg("%s: %s: Will return from wait().",
di->inst_id, __func__);
g_mutex_unlock(&di->data_mutex);
- return NULL;
+ goto err;
}
g_mutex_unlock(&di->data_mutex);
}
+ PyGILState_Release(gstate);
+
Py_RETURN_NONE;
+
+err:
+ PyGILState_Release(gstate);
+
+ return NULL;
}
/**
*/
static PyObject *Decoder_has_channel(PyObject *self, PyObject *args)
{
- int idx, max_idx;
+ int idx, count;
struct srd_decoder_inst *di;
- PyObject *py_channel;
+ PyGILState_STATE gstate;
if (!self || !args)
return NULL;
+ gstate = PyGILState_Ensure();
+
if (!(di = srd_inst_find_by_obj(NULL, self))) {
PyErr_SetString(PyExc_Exception, "decoder instance not found");
- return NULL;
+ goto err;
}
- /* Parse the argument of self.has_channel() into 'py_channel'. */
- if (!PyArg_ParseTuple(args, "O", &py_channel)) {
+ /*
+ * Get the integer argument of self.has_channel(). Check for
+ * the range of supported PD input channel numbers.
+ */
+ if (!PyArg_ParseTuple(args, "i", &idx)) {
/* Let Python raise this exception. */
- return NULL;
+ goto err;
}
- if (!PyLong_Check(py_channel)) {
- PyErr_SetString(PyExc_Exception, "channel index not a number");
- return NULL;
+ count = g_slist_length(di->decoder->channels) +
+ g_slist_length(di->decoder->opt_channels);
+ if (idx < 0 || idx >= count) {
+ srd_err("Invalid index %d, PD channel count %d.", idx, count);
+ PyErr_SetString(PyExc_IndexError, "invalid channel index");
+ goto err;
}
- idx = PyLong_AsLong(py_channel);
- max_idx = g_slist_length(di->decoder->channels)
- + g_slist_length(di->decoder->opt_channels) - 1;
-
- if (idx < 0 || idx > max_idx) {
- srd_err("Invalid channel index %d/%d.", idx, max_idx);
- PyErr_SetString(PyExc_Exception, "invalid channel");
- return NULL;
- }
+ PyGILState_Release(gstate);
return (di->dec_channelmap[idx] == -1) ? Py_False : Py_True;
+
+err:
+ PyGILState_Release(gstate);
+
+ return NULL;
}
static PyMethodDef Decoder_methods[] = {
{ Py_tp_new, (void *)&PyType_GenericNew },
{ 0, NULL }
};
+ PyObject *py_obj;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
spec.name = "sigrokdecode.Decoder";
spec.basicsize = sizeof(srd_Decoder);
spec.itemsize = 0;
spec.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
spec.slots = slots;
- return PyType_FromSpec(&spec);
+ py_obj = PyType_FromSpec(&spec);
+
+ PyGILState_Release(gstate);
+
+ return py_obj;
}
+++ /dev/null
-/*
- * This file is part of the libsigrokdecode project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.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 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/>.
- */
-
-#include <config.h>
-#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
-#include "libsigrokdecode.h"
-#include <inttypes.h>
-#include <string.h>
-
-static PyObject *srd_logic_iter(PyObject *self)
-{
- return self;
-}
-
-static PyObject *srd_logic_iternext(PyObject *self)
-{
- srd_logic *logic;
- PyObject *py_samplenum, *py_samples;
- uint8_t *sample_pos, sample;
- int byte_offset, bit_offset, i;
-
- logic = (srd_logic *)self;
- if (logic->itercnt >= logic->inbuflen / logic->di->data_unitsize) {
- /* End iteration loop. */
- return NULL;
- }
-
- /*
- * Convert the bit-packed sample to an array of bytes, with only 0x01
- * and 0x00 values, so the PD doesn't need to do any bitshifting.
- */
- sample_pos = logic->inbuf + logic->itercnt * logic->di->data_unitsize;
- for (i = 0; i < logic->di->dec_num_channels; i++) {
- /* A channelmap value of -1 means "unused optional channel". */
- if (logic->di->dec_channelmap[i] == -1) {
- /* Value of unused channel is 0xff, instead of 0 or 1. */
- logic->di->channel_samples[i] = 0xff;
- } else {
- byte_offset = logic->di->dec_channelmap[i] / 8;
- bit_offset = logic->di->dec_channelmap[i] % 8;
- sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
- logic->di->channel_samples[i] = sample;
- }
- }
-
- /* Prepare the next samplenum/sample list in this iteration. */
- py_samplenum =
- PyLong_FromUnsignedLongLong(logic->abs_start_samplenum +
- logic->itercnt);
- PyList_SetItem(logic->sample, 0, py_samplenum);
- py_samples = PyBytes_FromStringAndSize((const char *)logic->di->channel_samples,
- logic->di->dec_num_channels);
- PyList_SetItem(logic->sample, 1, py_samples);
- Py_INCREF(logic->sample);
- logic->itercnt++;
-
- return logic->sample;
-}
-
-/** Create the srd_logic type.
- * @return The new type object.
- * @private
- */
-SRD_PRIV PyObject *srd_logic_type_new(void)
-{
- PyType_Spec spec;
- PyType_Slot slots[] = {
- { Py_tp_doc, "sigrokdecode logic sample object" },
- { Py_tp_iter, (void *)&srd_logic_iter },
- { Py_tp_iternext, (void *)&srd_logic_iternext },
- { Py_tp_new, (void *)&PyType_GenericNew },
- { 0, NULL }
- };
- spec.name = "srd_logic";
- spec.basicsize = sizeof(srd_logic);
- spec.itemsize = 0;
- spec.flags = Py_TPFLAGS_DEFAULT;
- spec.slots = slots;
-
- return PyType_FromSpec(&spec);
-}
SRD_PRIV PyObject *py_import_by_name(const char *name)
{
PyObject *py_mod, *py_modname;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
py_modname = PyUnicode_FromString(name);
- if (!py_modname)
+ if (!py_modname) {
+ PyGILState_Release(gstate);
return NULL;
+ }
py_mod = PyImport_Import(py_modname);
Py_DECREF(py_modname);
+ PyGILState_Release(gstate);
+
return py_mod;
}
{
PyObject *py_str;
int ret;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (!PyObject_HasAttrString(py_obj, attr)) {
srd_dbg("Object has no attribute '%s'.", attr);
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!(py_str = PyObject_GetAttrString(py_obj, attr))) {
srd_exception_catch("Failed to get attribute '%s'", attr);
- return SRD_ERR_PYTHON;
+ goto err;
}
ret = py_str_as_str(py_str, outstr);
Py_DECREF(py_str);
+ PyGILState_Release(gstate);
+
return ret;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
/**
Py_ssize_t i;
int ret;
char *outstr;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (!PyObject_HasAttrString(py_obj, attr)) {
srd_dbg("Object has no attribute '%s'.", attr);
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!(py_list = PyObject_GetAttrString(py_obj, attr))) {
srd_exception_catch("Failed to get attribute '%s'", attr);
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!PyList_Check(py_list)) {
srd_dbg("Object is not a list.");
- return SRD_ERR_PYTHON;
+ goto err;
}
*outstrlist = NULL;
ret = py_listitem_as_str(py_list, i, &outstr);
if (ret < 0) {
srd_dbg("Couldn't get item %" PY_FORMAT_SIZE_T "d.", i);
- return SRD_ERR_PYTHON;
+ goto err;
}
*outstrlist = g_slist_append(*outstrlist, outstr);
}
Py_DECREF(py_list);
+ PyGILState_Release(gstate);
+
return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
/**
char **outstr)
{
PyObject *py_value;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (!PyDict_Check(py_obj)) {
srd_dbg("Object is not a dictionary.");
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!(py_value = PyDict_GetItemString(py_obj, key))) {
srd_dbg("Dictionary has no attribute '%s'.", key);
- return SRD_ERR_PYTHON;
+ goto err;
}
+ PyGILState_Release(gstate);
+
return py_str_as_str(py_value, outstr);
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
/**
char **outstr)
{
PyObject *py_value;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (!PyList_Check(py_obj)) {
srd_dbg("Object is not a list.");
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!(py_value = PyList_GetItem(py_obj, idx))) {
srd_dbg("Couldn't get list item %" PY_FORMAT_SIZE_T "d.", idx);
- return SRD_ERR_PYTHON;
+ goto err;
}
+ PyGILState_Release(gstate);
+
return py_str_as_str(py_value, outstr);
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
/**
char **outstr)
{
PyObject *py_value;
+ PyGILState_STATE gstate;
if (!py_obj || !py_key || !outstr)
return SRD_ERR_ARG;
+ gstate = PyGILState_Ensure();
+
if (!PyDict_Check(py_obj)) {
srd_dbg("Object is not a dictionary.");
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!(py_value = PyDict_GetItem(py_obj, py_key))) {
srd_dbg("Dictionary has no such key.");
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!PyUnicode_Check(py_value)) {
srd_dbg("Dictionary value should be a string.");
- return SRD_ERR_PYTHON;
+ goto err;
}
return py_str_as_str(py_value, outstr);
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
/**
SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out)
{
PyObject *py_value;
+ PyGILState_STATE gstate;
if (!py_obj || !py_key || !out)
return SRD_ERR_ARG;
+ gstate = PyGILState_Ensure();
+
if (!PyDict_Check(py_obj)) {
srd_dbg("Object is not a dictionary.");
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!(py_value = PyDict_GetItem(py_obj, py_key))) {
srd_dbg("Dictionary has no such key.");
- return SRD_ERR_PYTHON;
+ goto err;
}
if (!PyLong_Check(py_value)) {
srd_dbg("Dictionary value should be a long.");
- return SRD_ERR_PYTHON;
+ goto err;
}
*out = PyLong_AsUnsignedLongLong(py_value);
+ PyGILState_Release(gstate);
+
return SRD_OK;
+
+err:
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
}
/**
{
PyObject *py_bytes;
char *str;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (!PyUnicode_Check(py_str)) {
srd_dbg("Object is not a string object.");
+ PyGILState_Release(gstate);
return SRD_ERR_PYTHON;
}
Py_DECREF(py_bytes);
if (str) {
*outstr = str;
+ PyGILState_Release(gstate);
return SRD_OK;
}
}
srd_exception_catch("Failed to extract string");
+ PyGILState_Release(gstate);
+
return SRD_ERR_PYTHON;
}
PyObject *py_item, *py_bytes;
char **strv, *str;
ssize_t seq_len, i;
+ PyGILState_STATE gstate;
+ int ret = SRD_ERR_PYTHON;
+
+ gstate = PyGILState_Ensure();
if (!PySequence_Check(py_strseq)) {
srd_err("Object does not provide sequence protocol.");
- return SRD_ERR_PYTHON;
+ goto err;
}
seq_len = PySequence_Size(py_strseq);
if (seq_len < 0) {
srd_exception_catch("Failed to obtain sequence size");
- return SRD_ERR_PYTHON;
+ goto err;
}
strv = g_try_new0(char *, seq_len + 1);
if (!strv) {
srd_err("Failed to allocate result string vector.");
- return SRD_ERR_MALLOC;
+ ret = SRD_ERR_MALLOC;
+ goto err;
}
for (i = 0; i < seq_len; i++) {
g_strfreev(strv);
srd_exception_catch("Failed to obtain string item");
- return SRD_ERR_PYTHON;
+err:
+ PyGILState_Release(gstate);
+
+ return ret;
}
/**
SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj)
{
GVariant *var = NULL;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
if (PyUnicode_Check(py_obj)) { /* string */
PyObject *py_bytes;
srd_err("Failed to extract value of unsupported type.");
}
+ PyGILState_Release(gstate);
+
return var;
}