From: Uwe Hermann Date: Mon, 12 Jun 2017 01:17:28 +0000 (+0200) Subject: Backport recent changes from mainline. X-Git-Tag: libsigrokdecode-0.5.1~5 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=2ba06442543625adec845a38b7b0fbb776fcce7b;p=libsigrokdecode.git Backport recent changes from mainline. This includes all changes from 23e806c21e6e01999163c892635c6dea9d788daa "session.c: Fix a compiler warning on Mac OS X." up to cffb6592f4cff804745b8456e2c9f8abc6571603 "can: Fix incorrect stuff bit handling." This is possible since none of the changes above change or remove public API calls of the library. --- diff --git a/HACKING b/HACKING index a6fe7d3..30cd1fe 100644 --- a/HACKING +++ b/HACKING @@ -21,15 +21,16 @@ the Python PEP-8, which includes the convention of 4 spaces for indentation: 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 ------------ diff --git a/Makefile.am b/Makefile.am index 675b460..97e5228 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,7 +44,6 @@ libsigrokdecode_la_SOURCES = \ exception.c \ module_sigrokdecode.c \ type_decoder.c \ - type_logic.c \ error.c \ version.c diff --git a/README b/README index 686efef..784be27 100644 --- a/README +++ b/README @@ -34,7 +34,7 @@ Requirements - 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) diff --git a/configure.ac b/configure.ac index 16aab58..894ce2a 100644 --- a/configure.ac +++ b/configure.ac @@ -67,6 +67,10 @@ SR_LIB_VERSION_SET([SRD_LIB_VERSION], [4:0:0]) 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 ## ############################ @@ -123,6 +127,8 @@ SR_SEARCH_LIBS([SRD_EXTRA_LIBS], [pow], [m]) AC_SYS_LARGEFILE +AC_C_BIGENDIAN + ############################## ## Finalize configuration ## ############################## @@ -131,7 +137,7 @@ AC_SUBST([SRD_PKGLIBS]) # 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` @@ -162,7 +168,7 @@ Compile configuration: - 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 diff --git a/decoder.c b/decoder.c index 64292bc..6689bd4 100644 --- a/decoder.c +++ b/decoder.c @@ -152,11 +152,15 @@ static void decoder_option_free(void *data) 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); @@ -183,10 +187,15 @@ static int get_channels(const struct srd_decoder *d, const char *attr, 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; @@ -227,6 +236,8 @@ static int get_channels(const struct srd_decoder *d, const char *attr, Py_DECREF(py_channellist); *out_pdchl = pdchl; + PyGILState_Release(gstate); + return SRD_OK; except_out: @@ -235,6 +246,7 @@ except_out: err_out: g_slist_free_full(pdchl, &channel_free); Py_XDECREF(py_channellist); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -246,10 +258,15 @@ static int get_options(struct srd_decoder *d) 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; @@ -281,7 +298,7 @@ static int get_options(struct srd_decoder *d) 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; } @@ -342,6 +359,7 @@ static int get_options(struct srd_decoder *d) } d->options = options; Py_DECREF(py_opts); + PyGILState_Release(gstate); return SRD_OK; @@ -350,6 +368,7 @@ except_out: err_out: g_slist_free_full(options, &decoder_option_free); Py_XDECREF(py_opts); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -362,9 +381,14 @@ static int get_annotations(struct srd_decoder *dec) 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; @@ -396,6 +420,7 @@ static int get_annotations(struct srd_decoder *dec) } dec->annotations = annotations; Py_DECREF(py_annlist); + PyGILState_Release(gstate); return SRD_OK; @@ -404,6 +429,7 @@ except_out: err_out: g_slist_free_full(annotations, (GDestroyNotify)&g_strfreev); Py_XDECREF(py_annlist); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -417,9 +443,14 @@ static int get_annotation_rows(struct srd_decoder *dec) 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; @@ -492,6 +523,7 @@ static int get_annotation_rows(struct srd_decoder *dec) } dec->annotation_rows = annotation_rows; Py_DECREF(py_ann_rows); + PyGILState_Release(gstate); return SRD_OK; @@ -501,6 +533,7 @@ except_out: err_out: g_slist_free_full(annotation_rows, &annotation_row_free); Py_XDECREF(py_ann_rows); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -513,9 +546,14 @@ static int get_binary_classes(struct srd_decoder *dec) 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; @@ -548,6 +586,7 @@ static int get_binary_classes(struct srd_decoder *dec) } dec->binary = bin_classes; Py_DECREF(py_bin_classes); + PyGILState_Release(gstate); return SRD_OK; @@ -557,6 +596,7 @@ except_out: err_out: g_slist_free_full(bin_classes, (GDestroyNotify)&g_strfreev); Py_XDECREF(py_bin_classes); + PyGILState_Release(gstate); return SRD_ERR_PYTHON; } @@ -568,17 +608,23 @@ static int check_method(PyObject *py_dec, const char *mod_name, { 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); @@ -601,15 +647,20 @@ SRD_PRIV long srd_decoder_apiver(const struct srd_decoder *d) { 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; } @@ -629,6 +680,7 @@ SRD_API int srd_decoder_load(const char *module_name) long apiver; int is_subclass; const char *fail_txt; + PyGILState_STATE gstate; if (!srd_check_init()) return SRD_ERR; @@ -636,13 +688,14 @@ SRD_API int srd_decoder_load(const char *module_name) 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; @@ -686,8 +739,8 @@ SRD_API int srd_decoder_load(const char *module_name) * 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; @@ -775,6 +828,8 @@ SRD_API int srd_decoder_load(const char *module_name) goto err_out; } + PyGILState_Release(gstate); + /* Append it to the list of loaded decoders. */ pd_list = g_slist_append(pd_list, d); @@ -791,6 +846,7 @@ err_out: 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; } @@ -809,6 +865,7 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec) { PyObject *py_str; char *doc; + PyGILState_STATE gstate; if (!srd_check_init()) return NULL; @@ -816,12 +873,14 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec) 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; @@ -829,7 +888,14 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec) py_str_as_str(py_str, &doc); Py_DECREF(py_str); + PyGILState_Release(gstate); + return doc; + +err: + PyGILState_Release(gstate); + + return NULL; } /** @@ -852,8 +918,6 @@ SRD_API int srd_decoder_unload(struct srd_decoder *dec) 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 @@ -880,9 +944,12 @@ static void srd_decoder_load_all_zip_path(char *path) 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; @@ -951,6 +1018,7 @@ err_out: Py_XDECREF(zipimporter_class); Py_XDECREF(zipimport_mod); PyErr_Clear(); + PyGILState_Release(gstate); } static void srd_decoder_load_all_path(char *path) diff --git a/decoders/ac97/__init__.py b/decoders/ac97/__init__.py new file mode 100644 index 0000000..8b96e8a --- /dev/null +++ b/decoders/ac97/__init__.py @@ -0,0 +1,36 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Gerhard Sittig +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +''' +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 diff --git a/decoders/ac97/pd.py b/decoders/ac97/pd.py new file mode 100644 index 0000000..6cb7e93 --- /dev/null +++ b/decoders/ac97/pd.py @@ -0,0 +1,503 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Gerhard Sittig +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +# This 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 diff --git a/decoders/ade77xx/pd.py b/decoders/ade77xx/pd.py index 053575e..0dfd7c8 100644 --- a/decoders/ade77xx/pd.py +++ b/decoders/ade77xx/pd.py @@ -26,7 +26,7 @@ import sigrokdecode as srd from .lists import * class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ade77xx' name = 'ADE77xx' longname = 'Analog Devices ADE77xx' @@ -45,14 +45,17 @@ class Decoder(srd.Decoder): ('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) @@ -77,7 +80,7 @@ class Decoder(srd.Decoder): 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. @@ -124,4 +127,4 @@ class Decoder(srd.Decoder): else: self.putx([0, ['%s: %#x' % (rblob[0], vali)]]) - self.reset() + self.reset_data() diff --git a/decoders/adf435x/pd.py b/decoders/adf435x/pd.py index 8f51ee2..dcc08de 100644 --- a/decoders/adf435x/pd.py +++ b/decoders/adf435x/pd.py @@ -88,7 +88,7 @@ regs = { ANN_REG = 0 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'adf435x' name = 'ADF435x' longname = 'Analog Devices ADF4350/1' @@ -105,6 +105,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.bits = [] def start(self): diff --git a/decoders/adns5020/pd.py b/decoders/adns5020/pd.py index 5857314..cd72eca 100644 --- a/decoders/adns5020/pd.py +++ b/decoders/adns5020/pd.py @@ -20,26 +20,26 @@ 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' @@ -59,6 +59,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] diff --git a/decoders/am230x/pd.py b/decoders/am230x/pd.py index 3c5003d..81c1f28 100644 --- a/decoders/am230x/pd.py +++ b/decoders/am230x/pd.py @@ -74,7 +74,7 @@ class Decoder(srd.Decoder): 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 @@ -122,9 +122,12 @@ class Decoder(srd.Decoder): 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) @@ -177,7 +180,7 @@ class Decoder(srd.Decoder): 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'): @@ -185,14 +188,14 @@ class Decoder(srd.Decoder): 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'): @@ -201,14 +204,14 @@ class Decoder(srd.Decoder): 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'): @@ -216,10 +219,10 @@ class Decoder(srd.Decoder): 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() diff --git a/decoders/arm_etmv3/pd.py b/decoders/arm_etmv3/pd.py index 8f353a2..8de3ce2 100644 --- a/decoders/arm_etmv3/pd.py +++ b/decoders/arm_etmv3/pd.py @@ -127,7 +127,7 @@ def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc): 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' @@ -170,6 +170,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.buf = [] self.syncbuf = [] self.prevsample = 0 diff --git a/decoders/arm_itm/pd.py b/decoders/arm_itm/pd.py index 33649a0..5970f27 100644 --- a/decoders/arm_itm/pd.py +++ b/decoders/arm_itm/pd.py @@ -37,7 +37,7 @@ ARM_EXCEPTIONS = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'arm_itm' name = 'ARM ITM' longname = 'ARM Instrumentation Trace Macroblock' @@ -80,6 +80,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.buf = [] self.syncbuf = [] self.swpackets = {} diff --git a/decoders/arm_tpiu/pd.py b/decoders/arm_tpiu/pd.py index 0a32571..f50af65 100644 --- a/decoders/arm_tpiu/pd.py +++ b/decoders/arm_tpiu/pd.py @@ -20,7 +20,7 @@ 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' @@ -42,6 +42,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.buf = [] self.syncbuf = [] self.prevsample = 0 diff --git a/decoders/aud/pd.py b/decoders/aud/pd.py index baa920e..30c32f5 100644 --- a/decoders/aud/pd.py +++ b/decoders/aud/pd.py @@ -46,6 +46,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ncnt = 0 self.nmax = 0 self.addr = 0 diff --git a/decoders/avr_isp/pd.py b/decoders/avr_isp/pd.py index 6a3a241..2530e8c 100644 --- a/decoders/avr_isp/pd.py +++ b/decoders/avr_isp/pd.py @@ -23,7 +23,7 @@ from .parts import * 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' @@ -51,6 +51,9 @@ class Decoder(srd.Decoder): ) 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 diff --git a/decoders/avr_pdi/pd.py b/decoders/avr_pdi/pd.py index fcb73af..7fedbbd 100644 --- a/decoders/avr_pdi/pd.py +++ b/decoders/avr_pdi/pd.py @@ -154,6 +154,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.clear_state() diff --git a/decoders/can/pd.py b/decoders/can/pd.py index 375069f..d76d649 100644 --- a/decoders/can/pd.py +++ b/decoders/can/pd.py @@ -65,6 +65,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.reset_variables() @@ -75,11 +78,11 @@ class Decoder(srd.Decoder): 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. @@ -105,16 +108,31 @@ class Decoder(srd.Decoder): 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]): @@ -382,9 +400,14 @@ class Decoder(srd.Decoder): # 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() diff --git a/decoders/common/srdhelper/mod.py b/decoders/common/srdhelper/mod.py index 4871205..b559c95 100644 --- a/decoders/common/srdhelper/mod.py +++ b/decoders/common/srdhelper/mod.py @@ -20,3 +20,17 @@ # 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) diff --git a/decoders/counter/__init__.py b/decoders/counter/__init__.py new file mode 100644 index 0000000..e731311 --- /dev/null +++ b/decoders/counter/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stefan Brüns +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +''' +This 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 diff --git a/decoders/counter/pd.py b/decoders/counter/pd.py new file mode 100644 index 0000000..cbb6a5f --- /dev/null +++ b/decoders/counter/pd.py @@ -0,0 +1,99 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Stefan Brüns +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +class Decoder(srd.Decoder): + api_version = 3 + id = '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)]) diff --git a/decoders/dali/lists.py b/decoders/dali/lists.py index 24e6bc4..e9d3a4b 100644 --- a/decoders/dali/lists.py +++ b/decoders/dali/lists.py @@ -17,8 +17,8 @@ ## along with this program. If not, see . ## -# DALI Extended commands -extendedCommands = { +# DALI extended commands +extended_commands = { 0xA1: ['Terminate special processes', 'Terminate'], 0xA3: ['DTR = DATA', 'DTR'], 0xA5: ['INITIALISE', 'INIT'], @@ -39,7 +39,7 @@ extendedCommands = { } # List of commands -DALICommands = { +dali_commands = { 0x00: ['Immediate Off', 'IOFF'], 0x01: ['Up 200ms', 'Up'], 0x02: ['Down 200ms', 'Down'], @@ -89,7 +89,7 @@ DALICommands = { } # 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'], diff --git a/decoders/dali/pd.py b/decoders/dali/pd.py index 5b801a1..bf842ad 100644 --- a/decoders/dali/pd.py +++ b/decoders/dali/pd.py @@ -24,7 +24,7 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'dali' name = 'DALI' longname = 'Digital Addressable Lighting Interface' @@ -52,17 +52,18 @@ class Decoder(srd.Decoder): 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) @@ -132,8 +133,8 @@ class Decoder(srd.Decoder): 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'] @@ -151,19 +152,18 @@ class Decoder(srd.Decoder): 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: @@ -182,12 +182,12 @@ class Decoder(srd.Decoder): 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: @@ -198,46 +198,30 @@ class Decoder(srd.Decoder): 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 @@ -247,9 +231,9 @@ class Decoder(srd.Decoder): 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 @@ -257,6 +241,4 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/dcf77/pd.py b/decoders/dcf77/pd.py index a4e87f6..7b180ce 100644 --- a/decoders/dcf77/pd.py +++ b/decoders/dcf77/pd.py @@ -65,6 +65,9 @@ class Decoder(srd.Decoder): ) 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 diff --git a/decoders/dmx512/pd.py b/decoders/dmx512/pd.py index e83d943..1bcca20 100644 --- a/decoders/dmx512/pd.py +++ b/decoders/dmx512/pd.py @@ -20,7 +20,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'dmx512' name = 'DMX512' longname = 'Digital MultipleX 512' @@ -52,9 +52,11 @@ class Decoder(srd.Decoder): ) 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' @@ -71,15 +73,14 @@ class Decoder(srd.Decoder): 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']]) @@ -89,23 +90,23 @@ class Decoder(srd.Decoder): 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 @@ -126,7 +127,7 @@ class Decoder(srd.Decoder): 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. @@ -148,19 +149,18 @@ class Decoder(srd.Decoder): # 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']]) diff --git a/decoders/ds1307/pd.py b/decoders/ds1307/pd.py index 9b7a21f..414da65 100644 --- a/decoders/ds1307/pd.py +++ b/decoders/ds1307/pd.py @@ -52,7 +52,7 @@ def regs_and_bits(): return tuple(l) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ds1307' name = 'DS1307' longname = 'Dallas DS1307' @@ -75,6 +75,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.hours = -1 self.minutes = -1 diff --git a/decoders/ds243x/__init__.py b/decoders/ds243x/__init__.py new file mode 100644 index 0000000..c460e04 --- /dev/null +++ b/decoders/ds243x/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Kevin Redon +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +''' +This decoder stacks on top of the 'onewire_network' PD and decodes the +Maxim DS243x (1-Wire EEPROM) protocol. +''' + +from .pd import Decoder diff --git a/decoders/ds243x/pd.py b/decoders/ds243x/pd.py new file mode 100644 index 0000000..c7869a8 --- /dev/null +++ b/decoders/ds243x/pd.py @@ -0,0 +1,269 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Kevin Redon +## Copyright (C) 2017 Soeren Apel +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +# Dictionary of FUNCTION commands and their names. +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]) diff --git a/decoders/ds28ea00/pd.py b/decoders/ds28ea00/pd.py index 255f7c8..a792d95 100644 --- a/decoders/ds28ea00/pd.py +++ b/decoders/ds28ea00/pd.py @@ -35,7 +35,7 @@ command = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ds28ea00' name = 'DS28EA00' longname = 'Maxim DS28EA00 1-Wire digital thermometer' @@ -48,6 +48,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.trn_beg = 0 self.trn_end = 0 self.state = 'ROM' diff --git a/decoders/dsi/pd.py b/decoders/dsi/pd.py index f2ca4be..c5d9bf9 100644 --- a/decoders/dsi/pd.py +++ b/decoders/dsi/pd.py @@ -23,7 +23,7 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'dsi' name = 'DSI' longname = 'Digital Serial Interface' @@ -40,23 +40,24 @@ class Decoder(srd.Decoder): ) 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) @@ -105,13 +106,12 @@ class Decoder(srd.Decoder): 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. @@ -128,15 +128,9 @@ class Decoder(srd.Decoder): 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)): @@ -149,9 +143,9 @@ class Decoder(srd.Decoder): 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 @@ -159,6 +153,4 @@ class Decoder(srd.Decoder): self.bits.append([self.edges[-3], bit]) self.state = 'PHASE0' - # self.nextSamplePoint = self.edges[-1] + int(self.halfbit / 2) - self.old_dsi = self.dsi diff --git a/decoders/edid/pd.py b/decoders/edid/pd.py index 389fbda..e73884e 100644 --- a/decoders/edid/pd.py +++ b/decoders/edid/pd.py @@ -73,7 +73,7 @@ ANN_FIELDS = 0 ANN_SECTIONS = 1 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'edid' name = 'EDID' longname = 'Extended Display Identification Data' @@ -91,6 +91,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = None # Received data items, used as an index into samplenum/data self.cnt = 0 diff --git a/decoders/eeprom24xx/pd.py b/decoders/eeprom24xx/pd.py index c259dc1..49c586d 100644 --- a/decoders/eeprom24xx/pd.py +++ b/decoders/eeprom24xx/pd.py @@ -21,7 +21,7 @@ import sigrokdecode as srd from .lists import * class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'eeprom24xx' name = '24xx EEPROM' longname = '24xx I²C EEPROM' @@ -75,6 +75,9 @@ class Decoder(srd.Decoder): 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) @@ -90,7 +93,7 @@ class Decoder(srd.Decoder): 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 = [] @@ -179,7 +182,7 @@ class Decoder(srd.Decoder): 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 @@ -237,7 +240,7 @@ class Decoder(srd.Decoder): 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) @@ -249,18 +252,18 @@ class Decoder(srd.Decoder): 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' @@ -272,20 +275,20 @@ class Decoder(srd.Decoder): 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': @@ -294,7 +297,7 @@ class Decoder(srd.Decoder): # 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': @@ -302,18 +305,18 @@ class Decoder(srd.Decoder): 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' @@ -322,7 +325,7 @@ class Decoder(srd.Decoder): 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': @@ -332,7 +335,7 @@ class Decoder(srd.Decoder): 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': @@ -340,7 +343,7 @@ class Decoder(srd.Decoder): 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: @@ -348,31 +351,31 @@ class Decoder(srd.Decoder): 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': @@ -383,9 +386,9 @@ class Decoder(srd.Decoder): 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': @@ -394,22 +397,22 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/eeprom93xx/pd.py b/decoders/eeprom93xx/pd.py index 5ae789f..d76b869 100644 --- a/decoders/eeprom93xx/pd.py +++ b/decoders/eeprom93xx/pd.py @@ -20,7 +20,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'eeprom93xx' name = '93xx EEPROM' longname = '93xx Microwire EEPROM' @@ -43,6 +43,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.frame = [] def start(self): diff --git a/decoders/em4100/pd.py b/decoders/em4100/pd.py index 8c8a72b..778cfd1 100644 --- a/decoders/em4100/pd.py +++ b/decoders/em4100/pd.py @@ -62,6 +62,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.oldpin = None self.last_samplenum = None @@ -212,7 +215,7 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/em4305/pd.py b/decoders/em4305/pd.py index 895ee15..9fac9c6 100644 --- a/decoders/em4305/pd.py +++ b/decoders/em4305/pd.py @@ -65,6 +65,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.last_samplenum = None self.state = 'FFS_SEARCH' diff --git a/decoders/gpib/pd.py b/decoders/gpib/pd.py index 01801bc..d131cb9 100644 --- a/decoders/gpib/pd.py +++ b/decoders/gpib/pd.py @@ -61,6 +61,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.items = [] self.itemcount = 0 self.saved_item = None diff --git a/decoders/graycode/__init__.py b/decoders/graycode/__init__.py new file mode 100644 index 0000000..90ef824 --- /dev/null +++ b/decoders/graycode/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## 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 . +## + +''' +Gray code and rotary encoder protocol. +''' + +from .pd import Decoder diff --git a/decoders/graycode/pd.py b/decoders/graycode/pd.py new file mode 100644 index 0000000..ef5d513 --- /dev/null +++ b/decoders/graycode/pd.py @@ -0,0 +1,196 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import 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))]]) diff --git a/decoders/guess_bitrate/pd.py b/decoders/guess_bitrate/pd.py index 1bccc54..7a3121a 100644 --- a/decoders/guess_bitrate/pd.py +++ b/decoders/guess_bitrate/pd.py @@ -42,9 +42,10 @@ class Decoder(srd.Decoder): 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) @@ -57,19 +58,21 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/i2c/pd.py b/decoders/i2c/pd.py index 0e7f769..e70c27d 100644 --- a/decoders/i2c/pd.py +++ b/decoders/i2c/pd.py @@ -18,7 +18,6 @@ ## # 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. @@ -62,9 +61,6 @@ proto = { 'DATA WRITE': [9, 'Data write', 'DW'], } -class SamplerateError(Exception): - pass - class Decoder(srd.Decoder): api_version = 3 id = 'i2c' @@ -108,6 +104,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.ss = self.es = self.ss_byte = -1 self.bitcount = 0 @@ -236,9 +235,10 @@ class Decoder(srd.Decoder): 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 @@ -250,11 +250,6 @@ class Decoder(srd.Decoder): 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': diff --git a/decoders/i2cdemux/pd.py b/decoders/i2cdemux/pd.py index d28763b..2495e84 100644 --- a/decoders/i2cdemux/pd.py +++ b/decoders/i2cdemux/pd.py @@ -20,7 +20,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'i2cdemux' name = 'I²C demux' longname = 'I²C demultiplexer' @@ -30,6 +30,9 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/i2cfilter/pd.py b/decoders/i2cfilter/pd.py index c3f148f..1dc7fd1 100644 --- a/decoders/i2cfilter/pd.py +++ b/decoders/i2cfilter/pd.py @@ -23,7 +23,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'i2cfilter' name = 'I²C filter' longname = 'I²C filter' @@ -39,6 +39,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.curslave = -1 self.curdirection = None self.packets = [] # Local cache of I²C packets diff --git a/decoders/i2s/pd.py b/decoders/i2s/pd.py index b0b177f..bfb2c9e 100644 --- a/decoders/i2s/pd.py +++ b/decoders/i2s/pd.py @@ -18,6 +18,7 @@ ## import sigrokdecode as srd +import struct ''' OUTPUT_PYTHON format: @@ -32,9 +33,6 @@ Packet: : integer ''' -class SamplerateError(Exception): - pass - class Decoder(srd.Decoder): api_version = 3 id = 'i2s' @@ -59,6 +57,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.oldws = 1 self.bitcount = 0 @@ -88,12 +89,12 @@ class Decoder(srd.Decoder): 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)) @@ -112,25 +113,18 @@ class Decoder(srd.Decoder): 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('= 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]]) @@ -151,9 +159,36 @@ class Decoder(srd.Decoder): 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. @@ -164,13 +199,13 @@ class Decoder(srd.Decoder): # 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 diff --git a/decoders/ir_rc5/pd.py b/decoders/ir_rc5/pd.py index adf4e06..60a9416 100644 --- a/decoders/ir_rc5/pd.py +++ b/decoders/ir_rc5/pd.py @@ -24,7 +24,7 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ir_rc5' name = 'IR RC-5' longname = 'IR RC-5' @@ -56,6 +56,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.samplenum = None self.edges, self.bits, self.ss_es_bits = [], [], [] @@ -134,12 +137,12 @@ class Decoder(srd.Decoder): 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: @@ -147,8 +150,9 @@ class Decoder(srd.Decoder): # 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 diff --git a/decoders/jitter/pd.py b/decoders/jitter/pd.py index 04a2577..f492a9f 100644 --- a/decoders/jitter/pd.py +++ b/decoders/jitter/pd.py @@ -63,6 +63,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'CLK' self.samplerate = None self.oldclk, self.oldsig = 0, 0 diff --git a/decoders/jtag/pd.py b/decoders/jtag/pd.py index 4ad11c4..7f784eb 100644 --- a/decoders/jtag/pd.py +++ b/decoders/jtag/pd.py @@ -88,6 +88,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): # self.state = 'TEST-LOGIC-RESET' self.state = 'RUN-TEST/IDLE' self.oldstate = None diff --git a/decoders/jtag_stm32/pd.py b/decoders/jtag_stm32/pd.py index d8e10e7..d27a557 100644 --- a/decoders/jtag_stm32/pd.py +++ b/decoders/jtag_stm32/pd.py @@ -134,7 +134,7 @@ def data_out(bits): % (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' @@ -156,6 +156,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.samplenums = None diff --git a/decoders/lm75/pd.py b/decoders/lm75/pd.py index c8f4a1a..29237d7 100644 --- a/decoders/lm75/pd.py +++ b/decoders/lm75/pd.py @@ -39,7 +39,7 @@ ft = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'lm75' name = 'LM75' longname = 'National LM75' @@ -62,6 +62,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.reg = 0x00 # Currently selected register self.databytes = [] diff --git a/decoders/lpc/pd.py b/decoders/lpc/pd.py index d0dadaa..7242208 100644 --- a/decoders/lpc/pd.py +++ b/decoders/lpc/pd.py @@ -95,7 +95,7 @@ fields = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'lpc' name = 'LPC' longname = 'Low-Pin-Count' @@ -136,10 +136,12 @@ class Decoder(srd.Decoder): ) 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 @@ -183,10 +185,10 @@ class Decoder(srd.Decoder): 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 @@ -252,10 +254,10 @@ class Decoder(srd.Decoder): # 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)]]) @@ -312,8 +314,10 @@ class Decoder(srd.Decoder): 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: @@ -349,7 +353,6 @@ class Decoder(srd.Decoder): 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': diff --git a/decoders/maple_bus/__init__.py b/decoders/maple_bus/__init__.py new file mode 100644 index 0000000..33b90a5 --- /dev/null +++ b/decoders/maple_bus/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Marcus Comstedt +## +## 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 . +## + +''' +Maple bus is serial communication protocol used by peripherals for the +SEGA Dreamcast game console. +''' + +from .pd import Decoder diff --git a/decoders/maple_bus/pd.py b/decoders/maple_bus/pd.py new file mode 100644 index 0000000..8306061 --- /dev/null +++ b/decoders/maple_bus/pd.py @@ -0,0 +1,217 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Marcus Comstedt +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +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 diff --git a/decoders/max7219/pd.py b/decoders/max7219/pd.py index 9589c8f..c8e99e1 100644 --- a/decoders/max7219/pd.py +++ b/decoders/max7219/pd.py @@ -41,7 +41,7 @@ registers = { 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' @@ -59,6 +59,12 @@ class Decoder(srd.Decoder): ('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 diff --git a/decoders/mdio/pd.py b/decoders/mdio/pd.py index 7c2fc5f..3580acc 100644 --- a/decoders/mdio/pd.py +++ b/decoders/mdio/pd.py @@ -62,6 +62,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.illegal_bus = 0 self.samplenum = -1 self.clause45_addr = -1 # Clause 45 is context sensitive. diff --git a/decoders/microwire/pd.py b/decoders/microwire/pd.py index 4ec6eb0..4180ba2 100644 --- a/decoders/microwire/pd.py +++ b/decoders/microwire/pd.py @@ -71,6 +71,12 @@ class Decoder(srd.Decoder): ('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) diff --git a/decoders/midi/pd.py b/decoders/midi/pd.py index 21c4024..feb581c 100644 --- a/decoders/midi/pd.py +++ b/decoders/midi/pd.py @@ -25,7 +25,7 @@ RX = 0 TX = 1 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'midi' name = 'MIDI' longname = 'Musical Instrument Digital Interface' @@ -44,6 +44,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.status_byte = 0 self.explicit_status_byte = False diff --git a/decoders/mlx90614/pd.py b/decoders/mlx90614/pd.py index c2b37c7..60927f7 100644 --- a/decoders/mlx90614/pd.py +++ b/decoders/mlx90614/pd.py @@ -20,7 +20,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'mlx90614' name = 'MLX90614' longname = 'Melexis MLX90614' @@ -34,6 +34,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IGNORE START REPEAT' self.data = [] diff --git a/decoders/modbus/pd.py b/decoders/modbus/pd.py index 3420f99..d2307b9 100644 --- a/decoders/modbus/pd.py +++ b/decoders/modbus/pd.py @@ -811,7 +811,7 @@ class Modbus_ADU_CS(Modbus_ADU): 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' @@ -847,6 +847,9 @@ class Decoder(srd.Decoder): ) 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. diff --git a/decoders/morse/__init__.py b/decoders/morse/__init__.py new file mode 100644 index 0000000..5d91624 --- /dev/null +++ b/decoders/morse/__init__.py @@ -0,0 +1,28 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## 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 . +## + +''' +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 diff --git a/decoders/morse/pd.py b/decoders/morse/pd.py new file mode 100644 index 0000000..3048332 --- /dev/null +++ b/decoders/morse/pd.py @@ -0,0 +1,247 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 Christoph Rackwitz +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +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 diff --git a/decoders/mrf24j40/pd.py b/decoders/mrf24j40/pd.py index 83fc254..8ccf473 100644 --- a/decoders/mrf24j40/pd.py +++ b/decoders/mrf24j40/pd.py @@ -21,7 +21,7 @@ import sigrokdecode as srd from .lists import * class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'mrf24j40' name = 'MRF24J40' longname = 'Microchip MRF24J40' @@ -43,6 +43,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] self.miso_bytes = [] @@ -56,7 +59,7 @@ class Decoder(srd.Decoder): 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 = [] @@ -103,7 +106,7 @@ class Decoder(srd.Decoder): 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. @@ -126,8 +129,8 @@ class Decoder(srd.Decoder): 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() diff --git a/decoders/mxc6225xu/pd.py b/decoders/mxc6225xu/pd.py index 39e73f7..1e90455 100644 --- a/decoders/mxc6225xu/pd.py +++ b/decoders/mxc6225xu/pd.py @@ -59,7 +59,7 @@ status = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'mxc6225xu' name = 'MXC6225XU' longname = 'MEMSIC MXC6225XU' @@ -72,6 +72,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' def start(self): diff --git a/decoders/nrf24l01/pd.py b/decoders/nrf24l01/pd.py index ae1cb35..221b968 100644 --- a/decoders/nrf24l01/pd.py +++ b/decoders/nrf24l01/pd.py @@ -59,7 +59,7 @@ xn297_regs = { } class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'nrf24l01' name = 'nRF24L01(+)' longname = 'Nordic Semiconductor nRF24L01/nRF24L01+' @@ -94,6 +94,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.next() self.requirements_met = True self.cs_was_released = False diff --git a/decoders/nunchuk/pd.py b/decoders/nunchuk/pd.py index 6a6b4e9..4f006f2 100644 --- a/decoders/nunchuk/pd.py +++ b/decoders/nunchuk/pd.py @@ -20,7 +20,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'nunchuk' name = 'Nunchuk' longname = 'Nintendo Wii Nunchuk' @@ -47,6 +47,9 @@ class Decoder(srd.Decoder): ) 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 diff --git a/decoders/onewire_link/pd.py b/decoders/onewire_link/pd.py index ca32415..72aea83 100644 --- a/decoders/onewire_link/pd.py +++ b/decoders/onewire_link/pd.py @@ -118,6 +118,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.state = 'INITIAL' self.present = 0 diff --git a/decoders/onewire_network/pd.py b/decoders/onewire_network/pd.py index fca16da..5d850b5 100644 --- a/decoders/onewire_network/pd.py +++ b/decoders/onewire_network/pd.py @@ -32,7 +32,7 @@ command = { } 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)' @@ -45,6 +45,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_block = 0 self.es_block = 0 self.state = 'COMMAND' @@ -129,7 +132,7 @@ class Decoder(srd.Decoder): # 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 diff --git a/decoders/pan1321/pd.py b/decoders/pan1321/pd.py index d054e89..ecaa680 100644 --- a/decoders/pan1321/pd.py +++ b/decoders/pan1321/pd.py @@ -24,7 +24,7 @@ RX = 0 TX = 1 class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'pan1321' name = 'PAN1321' longname = 'Panasonic PAN1321' @@ -39,6 +39,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.cmd = ['', ''] self.ss_block = None diff --git a/decoders/parallel/pd.py b/decoders/parallel/pd.py index c8ac2b0..0e81344 100644 --- a/decoders/parallel/pd.py +++ b/decoders/parallel/pd.py @@ -18,6 +18,7 @@ ## import sigrokdecode as srd +from common.srdhelper import bitpack ''' OUTPUT_PYTHON format: @@ -78,7 +79,8 @@ class Decoder(srd.Decoder): 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')}, ) @@ -86,14 +88,21 @@ class Decoder(srd.Decoder): ('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) @@ -111,19 +120,20 @@ class Decoder(srd.Decoder): 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 @@ -133,50 +143,69 @@ class Decoder(srd.Decoder): # 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) diff --git a/decoders/ps2/pd.py b/decoders/ps2/pd.py index 1a7f0be..6ed04c8 100644 --- a/decoders/ps2/pd.py +++ b/decoders/ps2/pd.py @@ -53,6 +53,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.bits = [] self.samplenum = 0 self.bitcount = 0 diff --git a/decoders/pwm/pd.py b/decoders/pwm/pd.py index 0b7be45..9770445 100644 --- a/decoders/pwm/pd.py +++ b/decoders/pwm/pd.py @@ -20,6 +20,9 @@ import sigrokdecode as srd +class SamplerateError(Exception): + pass + class Decoder(srd.Decoder): api_version = 3 id = 'pwm' @@ -49,19 +52,17 @@ class Decoder(srd.Decoder): ) 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 = \ @@ -89,51 +90,51 @@ class Decoder(srd.Decoder): 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)) diff --git a/decoders/qi/pd.py b/decoders/qi/pd.py index 0c880dd..345efd8 100644 --- a/decoders/qi/pd.py +++ b/decoders/qi/pd.py @@ -49,7 +49,7 @@ class Decoder(srd.Decoder): 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'] @@ -73,6 +73,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.reset_variables() @@ -232,7 +235,7 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/rc_encode/__init__.py b/decoders/rc_encode/__init__.py new file mode 100644 index 0000000..db78dc1 --- /dev/null +++ b/decoders/rc_encode/__init__.py @@ -0,0 +1,36 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +''' +This 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 diff --git a/decoders/rc_encode/pd.py b/decoders/rc_encode/pd.py new file mode 100644 index 0000000..56b60ca --- /dev/null +++ b/decoders/rc_encode/pd.py @@ -0,0 +1,166 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Steve R +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +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 diff --git a/decoders/rfm12/pd.py b/decoders/rfm12/pd.py index ab20823..e709696 100644 --- a/decoders/rfm12/pd.py +++ b/decoders/rfm12/pd.py @@ -20,7 +20,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'rfm12' name = 'RFM12' longname = 'RFM12 control protocol' @@ -43,6 +43,9 @@ class Decoder(srd.Decoder): ) 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] diff --git a/decoders/rgb_led_spi/pd.py b/decoders/rgb_led_spi/pd.py index c64bd29..d087556 100644 --- a/decoders/rgb_led_spi/pd.py +++ b/decoders/rgb_led_spi/pd.py @@ -20,7 +20,7 @@ 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)' @@ -33,6 +33,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] diff --git a/decoders/rgb_led_ws281x/pd.py b/decoders/rgb_led_ws281x/pd.py index 0cb6603..b4f7c58 100644 --- a/decoders/rgb_led_ws281x/pd.py +++ b/decoders/rgb_led_ws281x/pd.py @@ -24,7 +24,7 @@ class SamplerateError(Exception): 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)' @@ -46,6 +46,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.oldpin = None self.ss_packet = None @@ -70,11 +73,14 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/rtc8564/pd.py b/decoders/rtc8564/pd.py index 2e90573..3a9dca0 100644 --- a/decoders/rtc8564/pd.py +++ b/decoders/rtc8564/pd.py @@ -28,7 +28,7 @@ def reg_list(): return tuple(l) class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'rtc8564' name = 'RTC-8564' longname = 'Epson RTC-8564 JE/NB' @@ -52,6 +52,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.hours = -1 self.minutes = -1 diff --git a/decoders/sda2506/__init__.py b/decoders/sda2506/__init__.py new file mode 100644 index 0000000..4e820ef --- /dev/null +++ b/decoders/sda2506/__init__.py @@ -0,0 +1,24 @@ +## +## 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 . +## + +''' +Decoder for Siemens EEPROM SDA2506. +''' + +from .pd import Decoder diff --git a/decoders/sda2506/pd.py b/decoders/sda2506/pd.py new file mode 100644 index 0000000..8b63cf0 --- /dev/null +++ b/decoders/sda2506/pd.py @@ -0,0 +1,142 @@ +## +## 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 . +## + +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() diff --git a/decoders/sdcard_sd/pd.py b/decoders/sdcard_sd/pd.py index 4f10f7c..9402f7e 100644 --- a/decoders/sdcard_sd/pd.py +++ b/decoders/sdcard_sd/pd.py @@ -61,6 +61,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'GET COMMAND TOKEN' self.token = [] self.is_acmd = False # Indicates CMD vs. ACMD @@ -92,10 +95,6 @@ class Decoder(srd.Decoder): 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') diff --git a/decoders/sdcard_spi/pd.py b/decoders/sdcard_spi/pd.py index 545cee5..7c59634 100644 --- a/decoders/sdcard_spi/pd.py +++ b/decoders/sdcard_spi/pd.py @@ -21,7 +21,7 @@ import sigrokdecode as srd 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)' @@ -46,6 +46,9 @@ class Decoder(srd.Decoder): ) 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 diff --git a/decoders/spdif/pd.py b/decoders/spdif/pd.py index 0c535e4..add2834 100644 --- a/decoders/spdif/pd.py +++ b/decoders/spdif/pd.py @@ -58,6 +58,9 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/spi/pd.py b/decoders/spi/pd.py index b08b8a8..9beb4e6 100644 --- a/decoders/spi/pd.py +++ b/decoders/spi/pd.py @@ -121,6 +121,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.bitcount = 0 self.misodata = self.mosidata = 0 @@ -142,7 +145,7 @@ class Decoder(srd.Decoder): 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 @@ -207,17 +210,18 @@ class Decoder(srd.Decoder): 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 @@ -249,10 +253,10 @@ class Decoder(srd.Decoder): 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: @@ -326,12 +330,9 @@ class Decoder(srd.Decoder): # 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) diff --git a/decoders/spiflash/pd.py b/decoders/spiflash/pd.py index a9eff9e..07934c5 100644 --- a/decoders/spiflash/pd.py +++ b/decoders/spiflash/pd.py @@ -71,7 +71,7 @@ def decode_status_reg(data): return ret class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'spiflash' name = 'SPI flash' longname = 'SPI flash chips' @@ -98,6 +98,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.device_id = -1 self.on_end_transaction = None self.end_current_transaction() diff --git a/decoders/ssi32/pd.py b/decoders/ssi32/pd.py index f2e79b8..f2685e2 100644 --- a/decoders/ssi32/pd.py +++ b/decoders/ssi32/pd.py @@ -23,7 +23,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'ssi32' name = 'SSI32' longname = 'Synchronous Serial Interface (32bit)' @@ -46,6 +46,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.ss_cmd, self.es_cmd = 0, 0 self.mosi_bytes = [] self.miso_bytes = [] @@ -59,7 +62,7 @@ class Decoder(srd.Decoder): 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 = [] @@ -93,7 +96,7 @@ class Decoder(srd.Decoder): 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. @@ -114,10 +117,10 @@ class Decoder(srd.Decoder): 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() diff --git a/decoders/stepper_motor/pd.py b/decoders/stepper_motor/pd.py index 52e3e63..0546395 100644 --- a/decoders/stepper_motor/pd.py +++ b/decoders/stepper_motor/pd.py @@ -19,9 +19,6 @@ import sigrokdecode as srd -class SamplerateError(Exception): - pass - class Decoder(srd.Decoder): api_version = 3 id = 'stepper_motor' @@ -50,6 +47,10 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None self.oldstep = None self.ss_prev_step = None self.pos = 0 @@ -70,12 +71,13 @@ class Decoder(srd.Decoder): 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]]) @@ -87,8 +89,6 @@ class Decoder(srd.Decoder): 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) diff --git a/decoders/swd/pd.py b/decoders/swd/pd.py index 22aad45..0f7bc22 100644 --- a/decoders/swd/pd.py +++ b/decoders/swd/pd.py @@ -92,6 +92,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): # SWD data/clock state self.state = 'UNKNOWN' self.sample_edge = RISING diff --git a/decoders/t55xx/pd.py b/decoders/t55xx/pd.py index 5a7611a..07df9c2 100644 --- a/decoders/t55xx/pd.py +++ b/decoders/t55xx/pd.py @@ -66,6 +66,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.last_samplenum = None self.lastlast_samplenum = None diff --git a/decoders/tca6408a/pd.py b/decoders/tca6408a/pd.py index 9fa9d23..0e60767 100644 --- a/decoders/tca6408a/pd.py +++ b/decoders/tca6408a/pd.py @@ -22,7 +22,7 @@ import sigrokdecode as srd class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'tca6408a' name = 'TI TCA6408A' longname = 'Texas Instruments TCA6408A' @@ -41,6 +41,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.state = 'IDLE' self.chip = -1 diff --git a/decoders/timing/pd.py b/decoders/timing/pd.py index ab963bb..61eab1e 100644 --- a/decoders/timing/pd.py +++ b/decoders/timing/pd.py @@ -74,6 +74,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.last_samplenum = None self.last_n = deque() diff --git a/decoders/tlc5620/pd.py b/decoders/tlc5620/pd.py index 3ca1415..6fe8ae1 100644 --- a/decoders/tlc5620/pd.py +++ b/decoders/tlc5620/pd.py @@ -71,6 +71,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.bits = [] self.ss_dac_first = None self.ss_dac = self.es_dac = 0 diff --git a/decoders/uart/pd.py b/decoders/uart/pd.py index 16729cc..8993271 100644 --- a/decoders/uart/pd.py +++ b/decoders/uart/pd.py @@ -160,6 +160,9 @@ class Decoder(srd.Decoder): 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] diff --git a/decoders/usb_packet/pd.py b/decoders/usb_packet/pd.py index aab0c95..489e58e 100644 --- a/decoders/usb_packet/pd.py +++ b/decoders/usb_packet/pd.py @@ -173,7 +173,7 @@ def calc_crc16(bitstr): 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' @@ -222,6 +222,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.bits = [] self.packet = [] self.packet_summary = '' diff --git a/decoders/usb_power_delivery/pd.py b/decoders/usb_power_delivery/pd.py index 3258ac3..c182498 100644 --- a/decoders/usb_power_delivery/pd.py +++ b/decoders/usb_power_delivery/pd.py @@ -101,14 +101,23 @@ EOP = 0x16 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 = [ @@ -228,7 +237,7 @@ class Decoder(srd.Decoder): 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) @@ -252,7 +261,7 @@ class Decoder(srd.Decoder): 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) @@ -276,7 +285,7 @@ class Decoder(srd.Decoder): 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) @@ -403,7 +412,7 @@ class Decoder(srd.Decoder): 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 @@ -412,7 +421,7 @@ class Decoder(srd.Decoder): 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 @@ -439,6 +448,9 @@ class Decoder(srd.Decoder): return -1 # No Start Of Packet def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.idx = 0 self.packet_seq = 0 diff --git a/decoders/usb_request/pd.py b/decoders/usb_request/pd.py index c1f00cd..e77debc 100644 --- a/decoders/usb_request/pd.py +++ b/decoders/usb_request/pd.py @@ -112,7 +112,7 @@ class pcap_usb_pkt(): 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' @@ -136,6 +136,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.request = {} self.request_id = 0 @@ -168,7 +171,8 @@ class Decoder(srd.Decoder): 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) @@ -179,6 +183,14 @@ class Decoder(srd.Decoder): 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, @@ -187,16 +199,18 @@ class Decoder(srd.Decoder): 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 @@ -222,11 +236,9 @@ class Decoder(srd.Decoder): # 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: @@ -251,7 +263,7 @@ class Decoder(srd.Decoder): 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): diff --git a/decoders/usb_signalling/pd.py b/decoders/usb_signalling/pd.py index 0ad8fdc..424117d 100644 --- a/decoders/usb_signalling/pd.py +++ b/decoders/usb_signalling/pd.py @@ -100,7 +100,7 @@ class SamplerateError(Exception): pass class Decoder(srd.Decoder): - api_version = 2 + api_version = 3 id = 'usb_signalling' name = 'USB signalling' longname = 'Universal Serial Bus (LS/FS) signalling' @@ -135,6 +135,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.samplerate = None self.oldsym = 'J' # The "idle" state is J. self.ss_block = None @@ -145,11 +148,10 @@ class Decoder(srd.Decoder): 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) @@ -298,17 +300,21 @@ class Decoder(srd.Decoder): 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' @@ -317,28 +323,23 @@ class Decoder(srd.Decoder): 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 diff --git a/decoders/wiegand/pd.py b/decoders/wiegand/pd.py index 7a24fc8..c494789 100644 --- a/decoders/wiegand/pd.py +++ b/decoders/wiegand/pd.py @@ -19,8 +19,11 @@ 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' @@ -48,6 +51,10 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): + self.samplerate = None self._samples_per_bit = 10 self._d0_prev = None @@ -64,15 +71,17 @@ class Decoder(srd.Decoder): 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.' @@ -102,8 +111,13 @@ class Decoder(srd.Decoder): 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): diff --git a/decoders/xfp/pd.py b/decoders/xfp/pd.py index 27135e5..0bc6724 100644 --- a/decoders/xfp/pd.py +++ b/decoders/xfp/pd.py @@ -23,7 +23,7 @@ from common.plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS, 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)' @@ -37,6 +37,9 @@ class Decoder(srd.Decoder): ) 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 diff --git a/decoders/z80/pd.py b/decoders/z80/pd.py index bd2ec57..b7ff9a5 100644 --- a/decoders/z80/pd.py +++ b/decoders/z80/pd.py @@ -111,6 +111,9 @@ class Decoder(srd.Decoder): ) def __init__(self): + self.reset() + + def reset(self): self.prev_cycle = Cycle.NONE self.op_state = self.state_IDLE @@ -132,7 +135,7 @@ class Decoder(srd.Decoder): 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: diff --git a/exception.c b/exception.c index 7027df5..b440758 100644 --- a/exception.c +++ b/exception.c @@ -28,6 +28,8 @@ static char *py_stringify(PyObject *py_obj) PyObject *py_str, *py_bytes; char *str = NULL; + /* Note: Caller already ran PyGILState_Ensure(). */ + if (!py_obj) return NULL; @@ -56,6 +58,8 @@ static char *py_get_string_attr(PyObject *py_obj, const char *attr) PyObject *py_str, *py_bytes; char *str = NULL; + /* Note: Caller already ran PyGILState_Ensure(). */ + if (!py_obj) return NULL; @@ -87,6 +91,7 @@ SRD_PRIV void srd_exception_catch(const char *format, ...) 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; @@ -94,6 +99,8 @@ SRD_PRIV void srd_exception_catch(const char *format, ...) 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. */ @@ -151,5 +158,7 @@ cleanup: /* Just in case. */ PyErr_Clear(); + PyGILState_Release(gstate); + g_free(msg); } diff --git a/instance.c b/instance.c index b716f2f..6fbdc01 100644 --- a/instance.c +++ b/instance.c @@ -30,11 +30,9 @@ 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 */ @@ -76,6 +74,7 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di, gint64 val_int; int ret; const char *val_str; + PyGILState_STATE gstate; if (!di) { srd_err("Invalid decoder instance."); @@ -87,8 +86,11 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di, 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; @@ -170,6 +172,7 @@ err_out: srd_exception_catch("Stray exception in srd_inst_option_set()"); ret = SRD_ERR_PYTHON; } + PyGILState_Release(gstate); return ret; } @@ -253,14 +256,20 @@ SRD_API int srd_inst_channel_set_all(struct srd_decoder_inst *di, } 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"); } @@ -300,6 +309,7 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess, 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); @@ -354,22 +364,23 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess, } /* 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); @@ -480,7 +491,6 @@ SRD_API int srd_inst_stack(struct srd_session *sess, 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; @@ -665,6 +675,7 @@ SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, GArray *i } 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]); @@ -676,6 +687,25 @@ SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, GArray *i return SRD_OK; } +/** @private */ +SRD_PRIV void oldpins_array_seed(struct srd_decoder_inst *di) +{ + size_t count; + GArray *arr; + + if (!di) + return; + if (di->old_pins_array) + return; + + srd_dbg("%s: Seeding old pins, %s().", di->inst_id, __func__); + count = di->dec_num_channels; + arr = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), count); + g_array_set_size(arr, count); + memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count); + di->old_pins_array = arr; +} + /** @private */ SRD_PRIV void oldpins_array_free(struct srd_decoder_inst *di) { @@ -697,14 +727,18 @@ SRD_PRIV int srd_inst_start(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); @@ -715,6 +749,8 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) /* 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; @@ -743,8 +779,7 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) */ 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: @@ -836,6 +871,7 @@ static void update_old_pins_array(struct srd_decoder_inst *di, 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; @@ -855,6 +891,7 @@ static void update_old_pins_array_initial_pins(struct srd_decoder_inst *di) 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; @@ -871,19 +908,16 @@ static gboolean term_matches(const struct srd_decoder_inst *di, 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); } @@ -894,8 +928,7 @@ static gboolean all_terms_match(const struct srd_decoder_inst *di, 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; @@ -911,8 +944,7 @@ static gboolean at_least_one_condition_matched( { unsigned int i; - if (!di) - return FALSE; + /* Caller ensures di != NULL. */ for (i = 0; i < num_conditions; i++) { if (di->match_array->data[i]) @@ -924,12 +956,13 @@ static gboolean at_least_one_condition_matched( 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."); @@ -953,7 +986,7 @@ static gboolean find_match(struct srd_decoder_inst *di) 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); @@ -1040,6 +1073,7 @@ static gpointer di_thread(gpointer data) PyObject *py_res; struct srd_decoder_inst *di; int wanted_term; + PyGILState_STATE gstate; if (!data) return NULL; @@ -1048,6 +1082,8 @@ static gpointer di_thread(gpointer data) 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. @@ -1083,6 +1119,7 @@ static gpointer di_thread(gpointer data) */ srd_dbg("%s: Thread done (!res, want_term).", di->inst_id); PyErr_Clear(); + PyGILState_Release(gstate); return NULL; } if (!py_res) { @@ -1094,6 +1131,7 @@ static gpointer di_thread(gpointer data) 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; } @@ -1106,6 +1144,8 @@ static gpointer di_thread(gpointer data) Py_DecRef(py_res); PyErr_Clear(); + PyGILState_Release(gstate); + srd_dbg("%s: Thread done (with res).", di->inst_id); return NULL; @@ -1165,10 +1205,6 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, 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"); @@ -1203,60 +1239,96 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, 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; @@ -1267,13 +1339,18 @@ SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di) { 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); diff --git a/libsigrokdecode-internal.h b/libsigrokdecode-internal.h index eb79287..6fb590c 100644 --- a/libsigrokdecode-internal.h +++ b/libsigrokdecode-internal.h @@ -84,6 +84,7 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, 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); diff --git a/libsigrokdecode.h b/libsigrokdecode.h index d37c432..ee610c9 100644 --- a/libsigrokdecode.h +++ b/libsigrokdecode.h @@ -320,6 +320,7 @@ struct srd_pd_callback { /* 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); @@ -329,6 +330,7 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key, 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); diff --git a/module_sigrokdecode.c b/module_sigrokdecode.c index 17563c1..ab5df19 100644 --- a/module_sigrokdecode.c +++ b/module_sigrokdecode.c @@ -23,8 +23,6 @@ /** @cond PRIVATE */ -SRD_PRIV PyObject *srd_logic_type = NULL; - /* * When initialized, a reference to this module inside the Python interpreter * lives here. @@ -43,7 +41,10 @@ static struct PyModuleDef sigrokdecode_module = { /** @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) @@ -55,12 +56,6 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void) 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; @@ -74,13 +69,15 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void) 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; } diff --git a/session.c b/session.c index 128d224..4d794df 100644 --- a/session.c +++ b/session.c @@ -131,11 +131,14 @@ static int srd_inst_send_meta(struct srd_decoder_inst *di, int key, 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, @@ -143,6 +146,8 @@ static int srd_inst_send_meta(struct srd_decoder_inst *di, int key, 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; @@ -199,7 +204,7 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key, 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; @@ -292,6 +297,45 @@ SRD_API int srd_session_send(struct srd_session *sess, 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. * diff --git a/srd.c b/srd.c index e8fd751..bed8031 100644 --- a/srd.c +++ b/srd.c @@ -144,6 +144,55 @@ static void print_versions(void) 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. * @@ -232,8 +281,16 @@ SRD_API int srd_init(const char *path) } } + /* 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; } @@ -261,9 +318,18 @@ SRD_API int srd_exit(void) 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; @@ -291,28 +357,38 @@ SRD_API int srd_exit(void) 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; } /** @} */ diff --git a/tests/session.c b/tests/session.c index f9e42df..e79496e 100644 --- a/tests/session.c +++ b/tests/session.c @@ -224,6 +224,36 @@ START_TEST(test_session_metadata_set_bogus) } 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; @@ -246,5 +276,9 @@ Suite *suite_session(void) 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; } diff --git a/type_decoder.c b/type_decoder.c index 9ed3339..06842f9 100644 --- a/type_decoder.c +++ b/type_decoder.c @@ -48,12 +48,15 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, 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. */ @@ -61,7 +64,7 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, 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; } /* @@ -72,13 +75,13 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, 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. */ @@ -86,12 +89,12 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, 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)); @@ -99,7 +102,14 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, 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, @@ -110,12 +120,15 @@ 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. */ @@ -123,7 +136,7 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, 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. */ @@ -131,13 +144,13 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, 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. */ @@ -145,19 +158,22 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, 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))) @@ -166,36 +182,51 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, 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) @@ -204,15 +235,18 @@ 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, @@ -222,13 +256,13 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) * 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; @@ -236,21 +270,23 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) 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: @@ -269,28 +305,32 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) 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: @@ -299,9 +339,14 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) break; } - g_free(pdata); + PyGILState_Release(gstate); Py_RETURN_NONE; + +err: + PyGILState_Release(gstate); + + return NULL; } static PyObject *Decoder_register(PyObject *self, PyObject *args, @@ -315,6 +360,12 @@ 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; @@ -322,7 +373,7 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, 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. */ @@ -331,21 +382,46 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, &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); @@ -366,7 +442,14 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, 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) @@ -405,9 +488,13 @@ static PyObject *get_current_pinvalues(const struct srd_decoder_inst *di) 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; } @@ -427,6 +514,8 @@ static PyObject *get_current_pinvalues(const struct srd_decoder_inst *di) } } + PyGILState_Release(gstate); + return py_pinvalues; } @@ -448,6 +537,7 @@ static int create_term_list(PyObject *py_dict, GSList **term_list) 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; @@ -455,6 +545,8 @@ static int create_term_list(PyObject *py_dict, GSList **term_list) /* "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. */ @@ -464,7 +556,7 @@ static int create_term_list(PyObject *py_dict, GSList **term_list) /* 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); @@ -475,7 +567,7 @@ static int create_term_list(PyObject *py_dict, GSList **term_list) /* 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; @@ -483,14 +575,21 @@ static int create_term_list(PyObject *py_dict, GSList **term_list) 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; } /** @@ -510,14 +609,17 @@ static int set_new_condition_list(PyObject *self, PyObject *args) 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; } /* @@ -526,27 +628,35 @@ static int set_new_condition_list(PyObject *self, PyObject *args) */ 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); @@ -554,7 +664,7 @@ static int set_new_condition_list(PyObject *self, PyObject *args) 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. */ @@ -582,38 +692,108 @@ static int set_new_condition_list(PyObject *self, PyObject *args) 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) @@ -629,6 +809,8 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args) 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. */ @@ -644,11 +826,13 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args) } 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; } @@ -671,13 +855,20 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args) 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; } /** @@ -692,40 +883,45 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args) */ 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[] = { @@ -756,11 +952,20 @@ SRD_PRIV PyObject *srd_Decoder_type_new(void) { 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; } diff --git a/type_logic.c b/type_logic.c deleted file mode 100644 index ba356c0..0000000 --- a/type_logic.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of the libsigrokdecode project. - * - * Copyright (C) 2012 Bert Vermeulen - * - * 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 . - */ - -#include -#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ -#include "libsigrokdecode.h" -#include -#include - -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); -} diff --git a/util.c b/util.c index 9a573cc..430a7fb 100644 --- a/util.c +++ b/util.c @@ -36,14 +36,21 @@ 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; } @@ -64,21 +71,31 @@ SRD_PRIV int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr) { 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; } /** @@ -101,20 +118,23 @@ SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **out 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; @@ -123,14 +143,21 @@ SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **out 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; } /** @@ -150,18 +177,28 @@ SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, 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; } /** @@ -181,18 +218,28 @@ SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx, 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; } /** @@ -212,26 +259,34 @@ SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, 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; } /** @@ -249,28 +304,38 @@ SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, 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; } /** @@ -289,9 +354,13 @@ SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr) { 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; } @@ -301,11 +370,14 @@ SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr) 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; } @@ -327,22 +399,27 @@ SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv) 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++) { @@ -374,7 +451,10 @@ err_out: g_strfreev(strv); srd_exception_catch("Failed to obtain string item"); - return SRD_ERR_PYTHON; +err: + PyGILState_Release(gstate); + + return ret; } /** @@ -389,6 +469,9 @@ err_out: 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; @@ -426,5 +509,7 @@ SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj) srd_err("Failed to extract value of unsupported type."); } + PyGILState_Release(gstate); + return var; }