]> sigrok.org Git - libsigrokdecode.git/commitdiff
All PDs: Name the files pd.py consistently.
authorUwe Hermann <redacted>
Wed, 21 Nov 2012 21:43:02 +0000 (22:43 +0100)
committerUwe Hermann <redacted>
Sat, 24 Nov 2012 19:21:19 +0000 (20:21 +0100)
The Python module name is determined by the directory name (e.g. dcf77),
the *.py file names in that directory don't matter and can be kept
consistent.

100 files changed:
decoders/dcf77/Makefile.am
decoders/dcf77/__init__.py
decoders/dcf77/dcf77.py [deleted file]
decoders/dcf77/pd.py [new file with mode: 0644]
decoders/edid/Makefile.am
decoders/edid/__init__.py
decoders/edid/edid.py [deleted file]
decoders/edid/pd.py [new file with mode: 0644]
decoders/i2c/Makefile.am
decoders/i2c/__init__.py
decoders/i2c/i2c.py [deleted file]
decoders/i2c/pd.py [new file with mode: 0644]
decoders/i2cdemux/Makefile.am
decoders/i2cdemux/__init__.py
decoders/i2cdemux/i2cdemux.py [deleted file]
decoders/i2cdemux/pd.py [new file with mode: 0644]
decoders/i2cfilter/Makefile.am
decoders/i2cfilter/__init__.py
decoders/i2cfilter/i2cfilter.py [deleted file]
decoders/i2cfilter/pd.py [new file with mode: 0644]
decoders/i2s/Makefile.am
decoders/i2s/__init__.py
decoders/i2s/i2s.py [deleted file]
decoders/i2s/pd.py [new file with mode: 0644]
decoders/jtag/Makefile.am
decoders/jtag/__init__.py
decoders/jtag/jtag.py [deleted file]
decoders/jtag/pd.py [new file with mode: 0644]
decoders/jtag_stm32/Makefile.am
decoders/jtag_stm32/__init__.py
decoders/jtag_stm32/jtag_stm32.py [deleted file]
decoders/jtag_stm32/pd.py [new file with mode: 0644]
decoders/lm75/Makefile.am
decoders/lm75/__init__.py
decoders/lm75/lm75.py [deleted file]
decoders/lm75/pd.py [new file with mode: 0644]
decoders/lpc/Makefile.am
decoders/lpc/__init__.py
decoders/lpc/lpc.py [deleted file]
decoders/lpc/pd.py [new file with mode: 0644]
decoders/maxim_ds28ea00/Makefile.am
decoders/maxim_ds28ea00/__init__.py
decoders/maxim_ds28ea00/maxim_ds28ea00.py [deleted file]
decoders/maxim_ds28ea00/pd.py [new file with mode: 0644]
decoders/mlx90614/Makefile.am
decoders/mlx90614/__init__.py
decoders/mlx90614/mlx90614.py [deleted file]
decoders/mlx90614/pd.py [new file with mode: 0644]
decoders/mx25lxx05d/Makefile.am
decoders/mx25lxx05d/__init__.py
decoders/mx25lxx05d/mx25lxx05d.py [deleted file]
decoders/mx25lxx05d/pd.py [new file with mode: 0644]
decoders/mxc6225xu/Makefile.am
decoders/mxc6225xu/__init__.py
decoders/mxc6225xu/mxc6225xu.py [deleted file]
decoders/mxc6225xu/pd.py [new file with mode: 0644]
decoders/nunchuk/Makefile.am
decoders/nunchuk/__init__.py
decoders/nunchuk/nunchuk.py [deleted file]
decoders/nunchuk/pd.py [new file with mode: 0644]
decoders/onewire_link/Makefile.am
decoders/onewire_link/__init__.py
decoders/onewire_link/onewire_link.py [deleted file]
decoders/onewire_link/pd.py [new file with mode: 0644]
decoders/onewire_network/Makefile.am
decoders/onewire_network/__init__.py
decoders/onewire_network/onewire_network.py [deleted file]
decoders/onewire_network/pd.py [new file with mode: 0644]
decoders/pan1321/Makefile.am
decoders/pan1321/__init__.py
decoders/pan1321/pan1321.py [deleted file]
decoders/pan1321/pd.py [new file with mode: 0644]
decoders/rtc8564/Makefile.am
decoders/rtc8564/__init__.py
decoders/rtc8564/pd.py [new file with mode: 0644]
decoders/rtc8564/rtc8564.py [deleted file]
decoders/spi/Makefile.am
decoders/spi/__init__.py
decoders/spi/pd.py [new file with mode: 0644]
decoders/spi/spi.py [deleted file]
decoders/transitioncounter/Makefile.am
decoders/transitioncounter/__init__.py
decoders/transitioncounter/pd.py [new file with mode: 0644]
decoders/transitioncounter/transitioncounter.py [deleted file]
decoders/uart/Makefile.am
decoders/uart/__init__.py
decoders/uart/pd.py [new file with mode: 0644]
decoders/uart/uart.py [deleted file]
decoders/uart_dump/Makefile.am
decoders/uart_dump/__init__.py
decoders/uart_dump/pd.py [new file with mode: 0644]
decoders/uart_dump/uart_dump.py [deleted file]
decoders/usb_protocol/Makefile.am
decoders/usb_protocol/__init__.py
decoders/usb_protocol/pd.py [new file with mode: 0644]
decoders/usb_protocol/usb_protocol.py [deleted file]
decoders/usb_signalling/Makefile.am
decoders/usb_signalling/__init__.py
decoders/usb_signalling/pd.py [new file with mode: 0644]
decoders/usb_signalling/usb_signalling.py [deleted file]

index c55f0b7933654a449bedb0d0de364f15ff2f2a53..33ea1c77e749360a8d8738f54646bb8cedca33a9 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/dcf77
 
 
 pkgdatadir = $(DECODERS_DIR)/dcf77
 
-dist_pkgdata_DATA = __init__.py dcf77.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index bef505b926c6ab0e896600b7ec03f1f0cd692d02..e2ed26bf19ef5814f792f6e91d8c10c909b67db7 100644 (file)
@@ -25,5 +25,5 @@ Details:
 http://en.wikipedia.org/wiki/DCF77
 '''
 
 http://en.wikipedia.org/wiki/DCF77
 '''
 
-from .dcf77 import *
+from .pd import *
 
 
diff --git a/decoders/dcf77/dcf77.py b/decoders/dcf77/dcf77.py
deleted file mode 100644 (file)
index c4132e9..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# DCF77 protocol decoder
-
-import sigrokdecode as srd
-import calendar
-
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
-    return (b & 0x0f) + ((b >> 4) * 10)
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'dcf77'
-    name = 'DCF77'
-    longname = 'DCF77 time protocol'
-    desc = 'European longwave time signal (77.5kHz carrier signal).'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['dcf77']
-    probes = [
-        {'id': 'data', 'name': 'DATA', 'desc': 'DATA line'},
-    ]
-    optional_probes = [
-        {'id': 'pon', 'name': 'PON', 'desc': 'Power on'},
-    ]
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-        ['Warnings', 'Human-readable warnings'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'WAIT FOR RISING EDGE'
-        self.oldpins = None
-        self.oldval = None
-        self.oldpon = None
-        self.samplenum = 0
-        self.bit_start = 0
-        self.bit_start_old = 0
-        self.bitcount = 0 # Counter for the DCF77 bits (0..58)
-        self.dcf77_bitnumber_is_known = 0
-
-    def start(self, metadata):
-        self.samplerate = metadata['samplerate']
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'dcf77')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'dcf77')
-
-    def report(self):
-        pass
-
-    # TODO: Which range to use? Only the 100ms/200ms or full second?
-    def handle_dcf77_bit(self, bit):
-        c = self.bitcount
-        a = self.out_ann
-        ss = es = 0 # FIXME
-
-        # Create one annotation for each DCF77 bit (containing the 0/1 value).
-        # Use 'Unknown DCF77 bit x: val' if we're not sure yet which of the
-        # 0..58 bits it is (because we haven't seen a 'new minute' marker yet).
-        # Otherwise, use 'DCF77 bit x: val'.
-        s = '' if self.dcf77_bitnumber_is_known else 'Unknown '
-        self.put(ss, es, a, [0, ['%sDCF77 bit %d: %d' % (s, c, bit)]])
-
-        # If we're not sure yet which of the 0..58 DCF77 bits we have, return.
-        # We don't want to decode bogus data.
-        if not self.dcf77_bitnumber_is_known:
-            return
-
-        # Output specific "decoded" annotations for the respective DCF77 bits.
-        if c == 0:
-            # Start of minute: DCF bit 0.
-            if bit == 0:
-                self.put(ss, es, a, [0, ['Start of minute (always 0)']])
-            else:
-                self.put(ss, es, a, [0, ['ERROR: Start of minute != 0']])
-        elif c in range(1, 14 + 1):
-            # Special bits (civil warnings, weather forecast): DCF77 bits 1-14.
-            if c == 1:
-                self.tmp = bit
-            else:
-                self.tmp |= (bit << (c - 1))
-            if c == 14:
-                self.put(ss, es, a, [0, ['Special bits: %s' % bin(self.tmp)]])
-        elif c == 15:
-            s = '' if (bit == 1) else 'not '
-            self.put(ss, es, a, [0, ['Call bit is %sset' % s]])
-            # TODO: Previously this bit indicated use of the backup antenna.
-        elif c == 16:
-            s = '' if (bit == 1) else 'not '
-            self.put(ss, es, a, [0, ['Summer time announcement %sactive' % s]])
-        elif c == 17:
-            s = '' if (bit == 1) else 'not '
-            self.put(ss, es, a, [0, ['CEST is %sin effect' % s]])
-        elif c == 18:
-            s = '' if (bit == 1) else 'not '
-            self.put(ss, es, a, [0, ['CET is %sin effect' % s]])
-        elif c == 19:
-            s = '' if (bit == 1) else 'not '
-            self.put(ss, es, a, [0, ['Leap second announcement %sactive' % s]])
-        elif c == 20:
-            # Start of encoded time: DCF bit 20.
-            if bit == 1:
-                self.put(ss, es, a, [0, ['Start of encoded time (always 1)']])
-            else:
-                self.put(ss, es, a,
-                         [0, ['ERROR: Start of encoded time != 1']])
-        elif c in range(21, 27 + 1):
-            # Minutes (0-59): DCF77 bits 21-27 (BCD format).
-            if c == 21:
-                self.tmp = bit
-            else:
-                self.tmp |= (bit << (c - 21))
-            if c == 27:
-                self.put(ss, es, a, [0, ['Minutes: %d' % bcd2int(self.tmp)]])
-        elif c == 28:
-            # Even parity over minute bits (21-28): DCF77 bit 28.
-            self.tmp |= (bit << (c - 21))
-            parity = bin(self.tmp).count('1')
-            s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
-            self.put(ss, es, a, [0, ['Minute parity: %s' % s]])
-        elif c in range(29, 34 + 1):
-            # Hours (0-23): DCF77 bits 29-34 (BCD format).
-            if c == 29:
-                self.tmp = bit
-            else:
-                self.tmp |= (bit << (c - 29))
-            if c == 34:
-                self.put(ss, es, a, [0, ['Hours: %d' % bcd2int(self.tmp)]])
-        elif c == 35:
-            # Even parity over hour bits (29-35): DCF77 bit 35.
-            self.tmp |= (bit << (c - 29))
-            parity = bin(self.tmp).count('1')
-            s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
-            self.put(ss, es, a, [0, ['Hour parity: %s' % s]])
-        elif c in range(36, 41 + 1):
-            # Day of month (1-31): DCF77 bits 36-41 (BCD format).
-            if c == 36:
-                self.tmp = bit
-            else:
-                self.tmp |= (bit << (c - 36))
-            if c == 41:
-                self.put(ss, es, a, [0, ['Day: %d' % bcd2int(self.tmp)]])
-        elif c in range(42, 44 + 1):
-            # Day of week (1-7): DCF77 bits 42-44 (BCD format).
-            # A value of 1 means Monday, 7 means Sunday.
-            if c == 42:
-                self.tmp = bit
-            else:
-                self.tmp |= (bit << (c - 42))
-            if c == 44:
-                d = bcd2int(self.tmp)
-                dn = calendar.day_name[d - 1] # day_name[0] == Monday
-                self.put(ss, es, a, [0, ['Day of week: %d (%s)' % (d, dn)]])
-        elif c in range(45, 49 + 1):
-            # Month (1-12): DCF77 bits 45-49 (BCD format).
-            if c == 45:
-                self.tmp = bit
-            else:
-                self.tmp |= (bit << (c - 45))
-            if c == 49:
-                m = bcd2int(self.tmp)
-                mn = calendar.month_name[m] # month_name[1] == January
-                self.put(ss, es, a, [0, ['Month: %d (%s)' % (m, mn)]])
-        elif c in range(50, 57 + 1):
-            # Year (0-99): DCF77 bits 50-57 (BCD format).
-            if c == 50:
-                self.tmp = bit
-            else:
-                self.tmp |= (bit << (c - 50))
-            if c == 57:
-                self.put(ss, es, a, [0, ['Year: %d' % bcd2int(self.tmp)]])
-        elif c == 58:
-            # Even parity over date bits (36-58): DCF77 bit 58.
-            self.tmp |= (bit << (c - 50))
-            parity = bin(self.tmp).count('1')
-            s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
-            self.put(ss, es, a, [0, ['Date parity: %s' % s]])
-        else:
-            raise Exception('Invalid DCF77 bit: %d' % c)
-
-    def decode(self, ss, es, data):
-        for (self.samplenum, pins) in data:
-
-            # Ignore identical samples early on (for performance reasons).
-            if self.oldpins == pins:
-                continue
-            self.oldpins, (val, pon) = pins, pins
-
-            # Always remember the old PON state.
-            if self.oldpon != pon:
-                self.oldpon = pon
-
-            # Warn if PON goes low.
-            if self.oldpon == 1 and pon == 0:
-                self.pon_ss = self.samplenum
-                self.put(self.samplenum, self.samplenum, self.out_ann,
-                         [1, ['Warning: PON goes low, DCF77 reception '
-                         'no longer possible']])
-            elif self.oldpon == 0 and pon == 1:
-                self.put(self.samplenum, self.samplenum, self.out_ann,
-                         [0, ['PON goes high, DCF77 reception now possible']])
-                self.put(self.pon_ss, self.samplenum, self.out_ann,
-                         [1, ['Warning: PON low, DCF77 reception disabled']])
-
-            # Ignore samples where PON == 0, they can't contain DCF77 signals.
-            if pon == 0:
-                continue
-
-            if self.state == 'WAIT FOR RISING EDGE':
-                # Wait until the next rising edge occurs.
-                if not (self.oldval == 0 and val == 1):
-                    self.oldval = val
-                    continue
-
-                # Save the sample number where the DCF77 bit begins.
-                self.bit_start = self.samplenum
-
-                # Calculate the length (in ms) between two rising edges.
-                len_edges = self.bit_start - self.bit_start_old
-                len_edges_ms = int((len_edges / self.samplerate) * 1000)
-
-                # The time between two rising edges is usually around 1000ms.
-                # For DCF77 bit 59, there is no rising edge at all, i.e. the
-                # time between DCF77 bit 59 and DCF77 bit 0 (of the next
-                # minute) is around 2000ms. Thus, if we see an edge with a
-                # 2000ms distance to the last one, this edge marks the
-                # beginning of a new minute (and DCF77 bit 0 of that minute).
-                if len_edges_ms in range(1600, 2400 + 1):
-                    self.put(ss, es, self.out_ann, [0, ['New minute starts']])
-                    self.bitcount = 0
-                    self.bit_start_old = self.bit_start
-                    self.dcf77_bitnumber_is_known = 1
-                    # Don't switch to 'GET BIT' state this time.
-                    continue
-
-                self.bit_start_old = self.bit_start
-                self.state = 'GET BIT'
-
-            elif self.state == 'GET BIT':
-                # Wait until the next falling edge occurs.
-                if not (self.oldval == 1 and val == 0):
-                    self.oldval = val
-                    continue
-
-                # Calculate the length (in ms) of the current high period.
-                len_high = self.samplenum - self.bit_start
-                len_high_ms = int((len_high / self.samplerate) * 1000)
-
-                # If the high signal was 100ms long, that encodes a 0 bit.
-                # If it was 200ms long, that encodes a 1 bit.
-                if len_high_ms in range(40, 160 + 1):
-                    bit = 0
-                elif len_high_ms in range(161, 260 + 1):
-                    bit = 1
-                else:
-                    bit = -1 # TODO: Error?
-
-                # There's no bit 59, make sure none is decoded.
-                if bit in (0, 1) and self.bitcount in range(0, 58 + 1):
-                    self.handle_dcf77_bit(bit)
-                    self.bitcount += 1
-
-                self.state = 'WAIT FOR RISING EDGE'
-
-            else:
-                raise Exception('Invalid state: %d' % self.state)
-
-            self.oldval = val
-
diff --git a/decoders/dcf77/pd.py b/decoders/dcf77/pd.py
new file mode 100644 (file)
index 0000000..c4132e9
--- /dev/null
@@ -0,0 +1,288 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# DCF77 protocol decoder
+
+import sigrokdecode as srd
+import calendar
+
+# Return the specified BCD number (max. 8 bits) as integer.
+def bcd2int(b):
+    return (b & 0x0f) + ((b >> 4) * 10)
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'dcf77'
+    name = 'DCF77'
+    longname = 'DCF77 time protocol'
+    desc = 'European longwave time signal (77.5kHz carrier signal).'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['dcf77']
+    probes = [
+        {'id': 'data', 'name': 'DATA', 'desc': 'DATA line'},
+    ]
+    optional_probes = [
+        {'id': 'pon', 'name': 'PON', 'desc': 'Power on'},
+    ]
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+        ['Warnings', 'Human-readable warnings'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'WAIT FOR RISING EDGE'
+        self.oldpins = None
+        self.oldval = None
+        self.oldpon = None
+        self.samplenum = 0
+        self.bit_start = 0
+        self.bit_start_old = 0
+        self.bitcount = 0 # Counter for the DCF77 bits (0..58)
+        self.dcf77_bitnumber_is_known = 0
+
+    def start(self, metadata):
+        self.samplerate = metadata['samplerate']
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'dcf77')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'dcf77')
+
+    def report(self):
+        pass
+
+    # TODO: Which range to use? Only the 100ms/200ms or full second?
+    def handle_dcf77_bit(self, bit):
+        c = self.bitcount
+        a = self.out_ann
+        ss = es = 0 # FIXME
+
+        # Create one annotation for each DCF77 bit (containing the 0/1 value).
+        # Use 'Unknown DCF77 bit x: val' if we're not sure yet which of the
+        # 0..58 bits it is (because we haven't seen a 'new minute' marker yet).
+        # Otherwise, use 'DCF77 bit x: val'.
+        s = '' if self.dcf77_bitnumber_is_known else 'Unknown '
+        self.put(ss, es, a, [0, ['%sDCF77 bit %d: %d' % (s, c, bit)]])
+
+        # If we're not sure yet which of the 0..58 DCF77 bits we have, return.
+        # We don't want to decode bogus data.
+        if not self.dcf77_bitnumber_is_known:
+            return
+
+        # Output specific "decoded" annotations for the respective DCF77 bits.
+        if c == 0:
+            # Start of minute: DCF bit 0.
+            if bit == 0:
+                self.put(ss, es, a, [0, ['Start of minute (always 0)']])
+            else:
+                self.put(ss, es, a, [0, ['ERROR: Start of minute != 0']])
+        elif c in range(1, 14 + 1):
+            # Special bits (civil warnings, weather forecast): DCF77 bits 1-14.
+            if c == 1:
+                self.tmp = bit
+            else:
+                self.tmp |= (bit << (c - 1))
+            if c == 14:
+                self.put(ss, es, a, [0, ['Special bits: %s' % bin(self.tmp)]])
+        elif c == 15:
+            s = '' if (bit == 1) else 'not '
+            self.put(ss, es, a, [0, ['Call bit is %sset' % s]])
+            # TODO: Previously this bit indicated use of the backup antenna.
+        elif c == 16:
+            s = '' if (bit == 1) else 'not '
+            self.put(ss, es, a, [0, ['Summer time announcement %sactive' % s]])
+        elif c == 17:
+            s = '' if (bit == 1) else 'not '
+            self.put(ss, es, a, [0, ['CEST is %sin effect' % s]])
+        elif c == 18:
+            s = '' if (bit == 1) else 'not '
+            self.put(ss, es, a, [0, ['CET is %sin effect' % s]])
+        elif c == 19:
+            s = '' if (bit == 1) else 'not '
+            self.put(ss, es, a, [0, ['Leap second announcement %sactive' % s]])
+        elif c == 20:
+            # Start of encoded time: DCF bit 20.
+            if bit == 1:
+                self.put(ss, es, a, [0, ['Start of encoded time (always 1)']])
+            else:
+                self.put(ss, es, a,
+                         [0, ['ERROR: Start of encoded time != 1']])
+        elif c in range(21, 27 + 1):
+            # Minutes (0-59): DCF77 bits 21-27 (BCD format).
+            if c == 21:
+                self.tmp = bit
+            else:
+                self.tmp |= (bit << (c - 21))
+            if c == 27:
+                self.put(ss, es, a, [0, ['Minutes: %d' % bcd2int(self.tmp)]])
+        elif c == 28:
+            # Even parity over minute bits (21-28): DCF77 bit 28.
+            self.tmp |= (bit << (c - 21))
+            parity = bin(self.tmp).count('1')
+            s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
+            self.put(ss, es, a, [0, ['Minute parity: %s' % s]])
+        elif c in range(29, 34 + 1):
+            # Hours (0-23): DCF77 bits 29-34 (BCD format).
+            if c == 29:
+                self.tmp = bit
+            else:
+                self.tmp |= (bit << (c - 29))
+            if c == 34:
+                self.put(ss, es, a, [0, ['Hours: %d' % bcd2int(self.tmp)]])
+        elif c == 35:
+            # Even parity over hour bits (29-35): DCF77 bit 35.
+            self.tmp |= (bit << (c - 29))
+            parity = bin(self.tmp).count('1')
+            s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
+            self.put(ss, es, a, [0, ['Hour parity: %s' % s]])
+        elif c in range(36, 41 + 1):
+            # Day of month (1-31): DCF77 bits 36-41 (BCD format).
+            if c == 36:
+                self.tmp = bit
+            else:
+                self.tmp |= (bit << (c - 36))
+            if c == 41:
+                self.put(ss, es, a, [0, ['Day: %d' % bcd2int(self.tmp)]])
+        elif c in range(42, 44 + 1):
+            # Day of week (1-7): DCF77 bits 42-44 (BCD format).
+            # A value of 1 means Monday, 7 means Sunday.
+            if c == 42:
+                self.tmp = bit
+            else:
+                self.tmp |= (bit << (c - 42))
+            if c == 44:
+                d = bcd2int(self.tmp)
+                dn = calendar.day_name[d - 1] # day_name[0] == Monday
+                self.put(ss, es, a, [0, ['Day of week: %d (%s)' % (d, dn)]])
+        elif c in range(45, 49 + 1):
+            # Month (1-12): DCF77 bits 45-49 (BCD format).
+            if c == 45:
+                self.tmp = bit
+            else:
+                self.tmp |= (bit << (c - 45))
+            if c == 49:
+                m = bcd2int(self.tmp)
+                mn = calendar.month_name[m] # month_name[1] == January
+                self.put(ss, es, a, [0, ['Month: %d (%s)' % (m, mn)]])
+        elif c in range(50, 57 + 1):
+            # Year (0-99): DCF77 bits 50-57 (BCD format).
+            if c == 50:
+                self.tmp = bit
+            else:
+                self.tmp |= (bit << (c - 50))
+            if c == 57:
+                self.put(ss, es, a, [0, ['Year: %d' % bcd2int(self.tmp)]])
+        elif c == 58:
+            # Even parity over date bits (36-58): DCF77 bit 58.
+            self.tmp |= (bit << (c - 50))
+            parity = bin(self.tmp).count('1')
+            s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
+            self.put(ss, es, a, [0, ['Date parity: %s' % s]])
+        else:
+            raise Exception('Invalid DCF77 bit: %d' % c)
+
+    def decode(self, ss, es, data):
+        for (self.samplenum, pins) in data:
+
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpins == pins:
+                continue
+            self.oldpins, (val, pon) = pins, pins
+
+            # Always remember the old PON state.
+            if self.oldpon != pon:
+                self.oldpon = pon
+
+            # Warn if PON goes low.
+            if self.oldpon == 1 and pon == 0:
+                self.pon_ss = self.samplenum
+                self.put(self.samplenum, self.samplenum, self.out_ann,
+                         [1, ['Warning: PON goes low, DCF77 reception '
+                         'no longer possible']])
+            elif self.oldpon == 0 and pon == 1:
+                self.put(self.samplenum, self.samplenum, self.out_ann,
+                         [0, ['PON goes high, DCF77 reception now possible']])
+                self.put(self.pon_ss, self.samplenum, self.out_ann,
+                         [1, ['Warning: PON low, DCF77 reception disabled']])
+
+            # Ignore samples where PON == 0, they can't contain DCF77 signals.
+            if pon == 0:
+                continue
+
+            if self.state == 'WAIT FOR RISING EDGE':
+                # Wait until the next rising edge occurs.
+                if not (self.oldval == 0 and val == 1):
+                    self.oldval = val
+                    continue
+
+                # Save the sample number where the DCF77 bit begins.
+                self.bit_start = self.samplenum
+
+                # Calculate the length (in ms) between two rising edges.
+                len_edges = self.bit_start - self.bit_start_old
+                len_edges_ms = int((len_edges / self.samplerate) * 1000)
+
+                # The time between two rising edges is usually around 1000ms.
+                # For DCF77 bit 59, there is no rising edge at all, i.e. the
+                # time between DCF77 bit 59 and DCF77 bit 0 (of the next
+                # minute) is around 2000ms. Thus, if we see an edge with a
+                # 2000ms distance to the last one, this edge marks the
+                # beginning of a new minute (and DCF77 bit 0 of that minute).
+                if len_edges_ms in range(1600, 2400 + 1):
+                    self.put(ss, es, self.out_ann, [0, ['New minute starts']])
+                    self.bitcount = 0
+                    self.bit_start_old = self.bit_start
+                    self.dcf77_bitnumber_is_known = 1
+                    # Don't switch to 'GET BIT' state this time.
+                    continue
+
+                self.bit_start_old = self.bit_start
+                self.state = 'GET BIT'
+
+            elif self.state == 'GET BIT':
+                # Wait until the next falling edge occurs.
+                if not (self.oldval == 1 and val == 0):
+                    self.oldval = val
+                    continue
+
+                # Calculate the length (in ms) of the current high period.
+                len_high = self.samplenum - self.bit_start
+                len_high_ms = int((len_high / self.samplerate) * 1000)
+
+                # If the high signal was 100ms long, that encodes a 0 bit.
+                # If it was 200ms long, that encodes a 1 bit.
+                if len_high_ms in range(40, 160 + 1):
+                    bit = 0
+                elif len_high_ms in range(161, 260 + 1):
+                    bit = 1
+                else:
+                    bit = -1 # TODO: Error?
+
+                # There's no bit 59, make sure none is decoded.
+                if bit in (0, 1) and self.bitcount in range(0, 58 + 1):
+                    self.handle_dcf77_bit(bit)
+                    self.bitcount += 1
+
+                self.state = 'WAIT FOR RISING EDGE'
+
+            else:
+                raise Exception('Invalid state: %d' % self.state)
+
+            self.oldval = val
+
index 2ec1715cfdf1d0540f27cd90e68f227ae9614673..8381dcca4778080840cd5493a8d35a76f820c5c5 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/edid
 
 
 pkgdatadir = $(DECODERS_DIR)/edid
 
-dist_pkgdata_DATA = __init__.py edid.py pnpids.txt
+dist_pkgdata_DATA = __init__.py pd.py pnpids.txt
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 0987d1780ee6f7d7b82912529538564c704e7352..baa268161b58baabd879ced7c3a2b920c2b3bf9b 100644 (file)
@@ -36,5 +36,5 @@ More information on EDID is available here:
  https://en.wikipedia.org/wiki/Extended_display_identification_data
 '''
 
  https://en.wikipedia.org/wiki/Extended_display_identification_data
 '''
 
-from .edid import *
+from .pd import *
 
 
diff --git a/decoders/edid/edid.py b/decoders/edid/edid.py
deleted file mode 100644 (file)
index a03fe67..0000000
+++ /dev/null
@@ -1,474 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 3 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-# EDID protocol decoder
-
-# TODO:
-#    - EDID < 1.3
-#    - add short annotations
-#    - Signal level standard field in basic display parameters block
-#    - Additional color point descriptors
-#    - Additional standard timing descriptors
-#    - Extensions
-
-import sigrokdecode as srd
-import os
-
-EDID_HEADER = [0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00]
-OFF_VENDOR = 8
-OFF_VERSION = 18
-OFF_BASIC = 20
-OFF_CHROM = 25
-OFF_EST_TIMING = 35
-OFF_STD_TIMING = 38
-OFF_DET_TIMING = 54
-OFF_NUM_EXT = 126
-OFF_CHECKSUM = 127
-
-# Pre-EDID established timing modes
-est_modes = [
-    '720x400@70Hz',
-    '720x400@88Hz',
-    '640x480@60Hz',
-    '640x480@67Hz',
-    '640x480@72Hz',
-    '640x480@75Hz',
-    '800x600@56Hz',
-    '800x600@60Hz',
-    '800x600@72Hz',
-    '800x600@75Hz',
-    '832x624@75Hz',
-    '1024x768@87Hz(i)',
-    '1024x768@60Hz',
-    '1024x768@70Hz',
-    '1024x768@75Hz',
-    '1280x1024@75Hz',
-    '1152x870@75Hz',
-]
-
-# X:Y display aspect ratios, as used in standard timing modes
-xy_ratio = [
-    (16, 10),
-    (4, 3),
-    (5, 4),
-    (16, 9),
-]
-
-# Annotation types
-ANN_FIELDS = 0
-ANN_SECTIONS = 1
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'edid'
-    name = 'EDID'
-    longname = 'Extended Display Identification Data'
-    desc = 'Data structure describing display device capabilities.'
-    license = 'gplv3+'
-    inputs = ['ddc2']
-    outputs = ['edid']
-    options = {}
-    annotations = [
-        ['EDID fields', 'EDID structure fields'],
-        ['EDID sections', 'EDID structure sections'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = None
-        # Received data items, used as an index into samplenum/data
-        self.cnt = 0
-        # Start/end sample numbers per data item
-        self.sn = []
-        # Received data
-        self.cache = []
-
-    def start(self, metadata):
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'edid')
-
-    def report(self):
-        pass
-
-    def decode(self, ss, es, data):
-        cmd, data = data
-
-        # We only care about actual data bytes that are read (for now).
-        if cmd != 'DATA READ':
-            return
-
-        self.cnt += 1
-        self.sn.append([ss, es])
-        self.cache.append(data)
-        # debug
-#        self.put(ss, es, self.out_ann, [0, ['%d: [%.2x]' % (self.cnt, data)]])
-
-        if self.state is None:
-            # Wait for the EDID header
-            if self.cnt >= OFF_VENDOR:
-                if self.cache[-8:] == EDID_HEADER:
-                    # Throw away any garbage before the header
-                    self.sn = self.sn[-8:]
-                    self.cache = self.cache[-8:]
-                    self.cnt = 8
-                    self.state = 'edid'
-                    self.put(ss, es, self.out_ann, [0, ['EDID header']])
-        elif self.state == 'edid':
-            if self.cnt == OFF_VERSION:
-                self.decode_vid(-10)
-                self.decode_pid(-8)
-                self.decode_serial(-6)
-                self.decode_mfrdate(-2)
-            elif self.cnt == OFF_BASIC:
-                version = 'EDID version: %d.%d' % (self.cache[-2], self.cache[-1])
-                self.put(ss, es, self.out_ann, [0, [version]])
-            elif self.cnt == OFF_CHROM:
-                self.decode_basicdisplay(-5)
-            elif self.cnt == OFF_EST_TIMING:
-                self.decode_chromaticity(-10)
-            elif self.cnt == OFF_STD_TIMING:
-                self.decode_est_timing(-3)
-            elif self.cnt == OFF_DET_TIMING:
-                self.decode_std_timing(-16)
-            elif self.cnt == OFF_NUM_EXT:
-                self.decode_descriptors(-72)
-            elif self.cnt == OFF_CHECKSUM:
-                self.put(ss, es, self.out_ann,
-                    [0, ['Extensions present: %d' % self.cache[self.cnt-1]]])
-            elif self.cnt == OFF_CHECKSUM+1:
-                checksum = 0
-                for i in range(128):
-                    checksum += self.cache[i]
-                if checksum % 256 == 0:
-                    csstr = 'OK'
-                else:
-                    csstr = 'WRONG!'
-                self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % (
-                         self.cache[self.cnt-1], csstr)]])
-                self.state = 'extensions'
-        elif self.state == 'extensions':
-            pass
-
-    def ann_field(self, start, end, annotation):
-        self.put(self.sn[start][0], self.sn[end][1],
-                 self.out_ann, [ANN_FIELDS, [annotation]])
-
-    def lookup_pnpid(self, pnpid):
-        pnpid_file = os.path.join(os.path.dirname(__file__), 'pnpids.txt')
-        if os.path.exists(pnpid_file):
-            for line in open(pnpid_file).readlines():
-                if line.find(pnpid + ';') == 0:
-                    return line[4:].strip()
-        return ''
-
-    def decode_vid(self, offset):
-        pnpid = chr(64 + ((self.cache[offset] & 0x7c) >> 2))
-        pnpid += chr(64 + (((self.cache[offset] & 0x03) << 3)
-                           | ((self.cache[offset+1] & 0xe0) >> 5)))
-        pnpid += chr(64 + (self.cache[offset+1] & 0x1f))
-        vendor = self.lookup_pnpid(pnpid)
-        if vendor:
-            pnpid += ' (%s)' % vendor
-        self.ann_field(offset, offset+1, pnpid)
-
-    def decode_pid(self, offset):
-        pidstr = 'Product 0x%.2x%.2x' % (self.cache[offset+1], self.cache[offset])
-        self.ann_field(offset, offset+1, pidstr)
-
-    def decode_serial(self, offset):
-        serialnum = (self.cache[offset+3] << 24) \
-                + (self.cache[offset+2] << 16) \
-                + (self.cache[offset+1] << 8) \
-                + self.cache[offset]
-        serialstr = ''
-        is_alnum = True
-        for i in range(4):
-            if not chr(self.cache[offset+3-i]).isalnum():
-                is_alnum = False
-                break
-            serialstr += chr(self.cache[offset+3-i])
-        serial = serialstr if is_alnum else str(serialnum)
-        self.ann_field(offset, offset+3, 'Serial ' + serial)
-
-    def decode_mfrdate(self, offset):
-        datestr = ''
-        if self.cache[offset]:
-            datestr += 'week %d, ' % self.cache[offset]
-        datestr += str(1990 + self.cache[offset+1])
-        if datestr:
-            self.ann_field(offset, offset+1, 'Manufactured ' + datestr)
-
-    def decode_basicdisplay(self, offset):
-        # Video input definition
-        vid = self.cache[offset]
-        if vid & 0x80:
-            # Digital
-            self.ann_field(offset, offset, 'Video input: VESA DFP 1.')
-        else:
-            # Analog
-            sls = (vid & 60) >> 5
-            self.ann_field(offset, offset, 'Signal level standard: %.2x' % sls)
-            if vid & 0x10:
-                self.ann_field(offset, offset, 'Blank-to-black setup expected')
-            syncs = ''
-            if vid & 0x08:
-                syncs += 'separate syncs, '
-            if vid & 0x04:
-                syncs += 'composite syncs, '
-            if vid & 0x02:
-                syncs += 'sync on green, '
-            if vid & 0x01:
-                syncs += 'Vsync serration required, '
-            if syncs:
-                self.ann_field(offset, offset, 'Supported syncs: %s' % syncs[:-2])
-        # Max horizontal/vertical image size
-        if self.cache[offset+1] != 0 and self.cache[offset+2] != 0:
-            # Projectors have this set to 0
-            sizestr = '%dx%dcm' % (self.cache[offset+1], self.cache[offset+2])
-            self.ann_field(offset+1, offset+2, 'Physical size: ' + sizestr)
-        # Display transfer characteristic (gamma)
-        if self.cache[offset+3] != 0xff:
-            gamma = (self.cache[offset+3] + 100) / 100
-            self.ann_field(offset+3, offset+3, 'Gamma: %1.2f' % gamma)
-        # Feature support
-        fs = self.cache[offset+4]
-        dpms = ''
-        if fs & 0x80:
-            dpms += 'standby, '
-        if fs & 0x40:
-            dpms += 'suspend, '
-        if fs & 0x20:
-            dpms += 'active off, '
-        if dpms:
-            self.ann_field(offset+4, offset+4, 'DPMS support: %s' % dpms[:-2])
-        dt = (fs & 0x18) >> 3
-        dtstr = ''
-        if dt == 0:
-            dtstr = 'Monochrome'
-        elif dt == 1:
-            dtstr = 'RGB color'
-        elif dt == 2:
-            dtstr = 'non-RGB multicolor'
-        if dtstr:
-            self.ann_field(offset+4, offset+4, 'Display type: %s' % dtstr)
-        if fs & 0x04:
-            self.ann_field(offset+4, offset+4, 'Color space: standard sRGB')
-        # Save this for when we decode the first detailed timing descriptor
-        self.have_preferred_timing = (fs & 0x02) == 0x02
-        if fs & 0x01:
-            gft = ''
-        else:
-            gft = 'not '
-        self.ann_field(offset+4, offset+4,
-                       'Generalized timing formula: %ssupported' % gft)
-
-    def convert_color(self, value):
-        # Convert from 10-bit packet format to float
-        outval = 0.0
-        for i in range(10):
-            if value & 0x01:
-                outval += 2 ** -(10-i)
-            value >>= 1
-        return outval
-
-    def decode_chromaticity(self, offset):
-        redx = (self.cache[offset+2] << 2) + ((self.cache[offset] & 0xc0) >> 6)
-        redy = (self.cache[offset+3] << 2) + ((self.cache[offset] & 0x30) >> 4)
-        self.ann_field(offset, offset+9, 'Chromacity red: X %1.3f, Y %1.3f' % (
-                       self.convert_color(redx), self.convert_color(redy)))
-
-        greenx = (self.cache[offset+4] << 2) + ((self.cache[offset] & 0x0c) >> 6)
-        greeny = (self.cache[offset+5] << 2) + ((self.cache[offset] & 0x03) >> 4)
-        self.ann_field(offset, offset+9, 'Chromacity green: X %1.3f, Y %1.3f' % (
-                       self.convert_color(greenx), self.convert_color(greeny)))
-
-        bluex = (self.cache[offset+6] << 2) + ((self.cache[offset+1] & 0xc0) >> 6)
-        bluey = (self.cache[offset+7] << 2) + ((self.cache[offset+1] & 0x30) >> 4)
-        self.ann_field(offset, offset+9, 'Chromacity blue: X %1.3f, Y %1.3f' % (
-                       self.convert_color(bluex), self.convert_color(bluey)))
-
-        whitex = (self.cache[offset+8] << 2) + ((self.cache[offset+1] & 0x0c) >> 6)
-        whitey = (self.cache[offset+9] << 2) + ((self.cache[offset+1] & 0x03) >> 4)
-        self.ann_field(offset, offset+9, 'Chromacity white: X %1.3f, Y %1.3f' % (
-                       self.convert_color(whitex), self.convert_color(whitey)))
-
-    def decode_est_timing(self, offset):
-        # Pre-EDID modes
-        bitmap = (self.cache[offset] << 9) \
-            + (self.cache[offset+1] << 1) \
-            + ((self.cache[offset+2] & 0x80) >> 7)
-        modestr = ''
-        for i in range(17):
-                if bitmap & (1 << (16-i)):
-                    modestr += est_modes[i] + ', '
-        if modestr:
-            self.ann_field(offset, offset+2,
-                           'Supported establised modes: %s' % modestr[:-2])
-
-    def decode_std_timing(self, offset):
-        modestr = ''
-        for i in range(0, 16, 2):
-            if self.cache[offset+i] == 0x01 and self.cache[offset+i+1] == 0x01:
-                # Unused field
-                continue
-            x = (self.cache[offset+i] + 31) * 8
-            ratio = (self.cache[offset+i+1] & 0xc0) >> 6
-            ratio_x, ratio_y = xy_ratio[ratio]
-            y = x / ratio_x * ratio_y
-            refresh = (self.cache[offset+i+1] & 0x3f) + 60
-            modestr += '%dx%d@%dHz, ' % (x, y, refresh)
-        if modestr:
-            self.ann_field(offset, offset+2,
-                           'Supported standard modes: %s' % modestr[:-2])
-
-    def decode_detailed_timing(self, offset):
-        if offset == -72 and self.have_preferred_timing:
-            # Only on first detailed timing descriptor
-            section = 'Preferred'
-        else:
-            section = 'Detailed'
-        section += ' timing descriptor'
-        self.put(self.sn[offset][0], self.sn[offset+18][1],
-             self.out_ann, [ANN_SECTIONS, [section]])
-
-        pixclock = float((self.cache[offset+1] << 8) + self.cache[offset]) / 100
-        self.ann_field(offset, offset+1, 'Pixel clock: %.2f MHz' % pixclock)
-
-        horiz_active = ((self.cache[offset+4] & 0xf0) << 4) + self.cache[offset+2]
-        self.ann_field(offset+2, offset+4, 'Horizontal active: %d' % horiz_active)
-
-        horiz_blank = ((self.cache[offset+4] & 0x0f) << 8) + self.cache[offset+3]
-        self.ann_field(offset+3, offset+4, 'Horizontal blanking: %d' % horiz_blank)
-
-        vert_active = ((self.cache[offset+7] & 0xf0) << 4) + self.cache[offset+5]
-        self.ann_field(offset+5, offset+7, 'Vertical active: %d' % vert_active)
-
-        vert_blank = ((self.cache[offset+7] & 0x0f) << 8) + self.cache[offset+6]
-        self.ann_field(offset+6, offset+7, 'Vertical blanking: %d' % vert_blank)
-
-        horiz_sync_off = ((self.cache[offset+11] & 0xc0) << 2) + self.cache[offset+8]
-        self.ann_field(offset+8, offset+11, 'Horizontal sync offset: %d' % horiz_sync_off)
-
-        horiz_sync_pw = ((self.cache[offset+11] & 0x30) << 4) + self.cache[offset+9]
-        self.ann_field(offset+9, offset+11, 'Horizontal sync pulse width: %d' % horiz_sync_pw)
-
-        vert_sync_off = ((self.cache[offset+11] & 0x0c) << 2) \
-                    + ((self.cache[offset+10] & 0xf0) >> 4)
-        self.ann_field(offset+10, offset+11, 'Vertical sync offset: %d' % vert_sync_off)
-
-        vert_sync_pw = ((self.cache[offset+11] & 0x03) << 4) \
-                    + (self.cache[offset+10] & 0x0f)
-        self.ann_field(offset+10, offset+11, 'Vertical sync pulse width: %d' % vert_sync_pw)
-
-        horiz_size = ((self.cache[offset+14] & 0xf0) << 4) + self.cache[offset+12]
-        vert_size = ((self.cache[offset+14] & 0x0f) << 8) + self.cache[offset+13]
-        self.ann_field(offset+12, offset+14, 'Physical size: %dx%dmm' % (horiz_size, vert_size))
-
-        horiz_border = self.cache[offset+15]
-        if horiz_border:
-            self.ann_field(offset+15, offset+15, 'Horizontal border: %d pixels' % horiz_border)
-        vert_border = self.cache[offset+16]
-        if vert_border:
-            self.ann_field(offset+16, offset+16, 'Vertical border: %d lines' % vert_border)
-
-        features = 'Flags: '
-        if self.cache[offset+17] & 0x80:
-            features += 'interlaced, '
-        stereo = (self.cache[offset+17] & 0x60) >> 5
-        if stereo:
-            if self.cache[offset+17] & 0x01:
-                features += '2-way interleaved stereo ('
-                features += ['right image on even lines',
-                             'left image on even lines',
-                             'side-by-side'][stereo-1]
-                features += '), '
-            else:
-                features += 'field sequential stereo ('
-                features += ['right image on sync=1', 'left image on sync=1',
-                             '4-way interleaved'][stereo-1]
-                features += '), '
-        sync = (self.cache[offset+17] & 0x18) >> 3
-        sync2 = (self.cache[offset+17] & 0x06) >> 1
-        posneg = ['negative', 'positive']
-        features += 'sync type '
-        if sync == 0x00:
-            features += 'analog composite (serrate on RGB)'
-        elif sync == 0x01:
-            features += 'bipolar analog composite (serrate on RGB)'
-        elif sync == 0x02:
-            features += 'digital composite (serrate on composite polarity ' \
-                        + (posneg[sync2 & 0x01]) + ')'
-        elif sync == 0x03:
-            features += 'digital separate ('
-            features += 'Vsync polarity ' + (posneg[(sync2 & 0x02) >> 1])
-            features += ', Hsync polarity ' + (posneg[sync2 & 0x01])
-            features += ')'
-        features += ', '
-        self.ann_field(offset+17, offset+17, features[:-2])
-
-    def decode_descriptor(self, offset):
-        tag = self.cache[offset+3]
-        if tag == 0xff:
-            # Monitor serial number
-            text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace')
-            self.ann_field(offset, offset+17, 'Serial number: %s' % text.strip())
-        elif tag == 0xfe:
-            # Text
-            text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace')
-            self.ann_field(offset, offset+17, 'Info: %s' % text.strip())
-        elif tag == 0xfc:
-            # Monitor name
-            text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace')
-            self.ann_field(offset, offset+17, 'Model name: %s' % text.strip())
-        elif tag == 0xfd:
-            # Monitor range limits
-            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
-                     [ANN_SECTIONS, ['Monitor range limits']])
-            self.ann_field(offset+5, offset+5, 'Minimum vertical rate: %dHz' %
-                           self.cache[offset+5])
-            self.ann_field(offset+6, offset+6, 'Maximum vertical rate: %dHz' %
-                           self.cache[offset+6])
-            self.ann_field(offset+7, offset+7, 'Minimum horizontal rate: %dkHz' %
-                           self.cache[offset+7])
-            self.ann_field(offset+8, offset+8, 'Maximum horizontal rate: %dkHz' %
-                           self.cache[offset+8])
-            self.ann_field(offset+9, offset+9, 'Maximum pixel clock: %dMHz' %
-                           (self.cache[offset+9] * 10))
-            if self.cache[offset+10] == 0x02:
-                # Secondary GTF curve supported
-                self.ann_field(offset+10, offset+17, 'Secondary timing formula supported')
-        elif tag == 0xfb:
-            # Additional color point data
-            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
-                     [ANN_SECTIONS, ['Additional color point data']])
-        elif tag == 0xfa:
-            # Additional standard timing definitions
-            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
-                     [ANN_SECTIONS, ['Additional standard timing definitions']])
-        else:
-            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
-                     [ANN_SECTIONS, ['Unknown descriptor']])
-
-    def decode_descriptors(self, offset):
-        # 4 consecutive 18-byte descriptor blocks
-        for i in range(offset, 0, 18):
-            if self.cache[i] != 0 and self.cache[i+1] != 0:
-                self.decode_detailed_timing(i)
-            else:
-                if self.cache[i+2] == 0 or self.cache[i+4] == 0:
-                    self.decode_descriptor(i)
-
diff --git a/decoders/edid/pd.py b/decoders/edid/pd.py
new file mode 100644 (file)
index 0000000..a03fe67
--- /dev/null
@@ -0,0 +1,474 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# EDID protocol decoder
+
+# TODO:
+#    - EDID < 1.3
+#    - add short annotations
+#    - Signal level standard field in basic display parameters block
+#    - Additional color point descriptors
+#    - Additional standard timing descriptors
+#    - Extensions
+
+import sigrokdecode as srd
+import os
+
+EDID_HEADER = [0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00]
+OFF_VENDOR = 8
+OFF_VERSION = 18
+OFF_BASIC = 20
+OFF_CHROM = 25
+OFF_EST_TIMING = 35
+OFF_STD_TIMING = 38
+OFF_DET_TIMING = 54
+OFF_NUM_EXT = 126
+OFF_CHECKSUM = 127
+
+# Pre-EDID established timing modes
+est_modes = [
+    '720x400@70Hz',
+    '720x400@88Hz',
+    '640x480@60Hz',
+    '640x480@67Hz',
+    '640x480@72Hz',
+    '640x480@75Hz',
+    '800x600@56Hz',
+    '800x600@60Hz',
+    '800x600@72Hz',
+    '800x600@75Hz',
+    '832x624@75Hz',
+    '1024x768@87Hz(i)',
+    '1024x768@60Hz',
+    '1024x768@70Hz',
+    '1024x768@75Hz',
+    '1280x1024@75Hz',
+    '1152x870@75Hz',
+]
+
+# X:Y display aspect ratios, as used in standard timing modes
+xy_ratio = [
+    (16, 10),
+    (4, 3),
+    (5, 4),
+    (16, 9),
+]
+
+# Annotation types
+ANN_FIELDS = 0
+ANN_SECTIONS = 1
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'edid'
+    name = 'EDID'
+    longname = 'Extended Display Identification Data'
+    desc = 'Data structure describing display device capabilities.'
+    license = 'gplv3+'
+    inputs = ['ddc2']
+    outputs = ['edid']
+    options = {}
+    annotations = [
+        ['EDID fields', 'EDID structure fields'],
+        ['EDID sections', 'EDID structure sections'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = None
+        # Received data items, used as an index into samplenum/data
+        self.cnt = 0
+        # Start/end sample numbers per data item
+        self.sn = []
+        # Received data
+        self.cache = []
+
+    def start(self, metadata):
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'edid')
+
+    def report(self):
+        pass
+
+    def decode(self, ss, es, data):
+        cmd, data = data
+
+        # We only care about actual data bytes that are read (for now).
+        if cmd != 'DATA READ':
+            return
+
+        self.cnt += 1
+        self.sn.append([ss, es])
+        self.cache.append(data)
+        # debug
+#        self.put(ss, es, self.out_ann, [0, ['%d: [%.2x]' % (self.cnt, data)]])
+
+        if self.state is None:
+            # Wait for the EDID header
+            if self.cnt >= OFF_VENDOR:
+                if self.cache[-8:] == EDID_HEADER:
+                    # Throw away any garbage before the header
+                    self.sn = self.sn[-8:]
+                    self.cache = self.cache[-8:]
+                    self.cnt = 8
+                    self.state = 'edid'
+                    self.put(ss, es, self.out_ann, [0, ['EDID header']])
+        elif self.state == 'edid':
+            if self.cnt == OFF_VERSION:
+                self.decode_vid(-10)
+                self.decode_pid(-8)
+                self.decode_serial(-6)
+                self.decode_mfrdate(-2)
+            elif self.cnt == OFF_BASIC:
+                version = 'EDID version: %d.%d' % (self.cache[-2], self.cache[-1])
+                self.put(ss, es, self.out_ann, [0, [version]])
+            elif self.cnt == OFF_CHROM:
+                self.decode_basicdisplay(-5)
+            elif self.cnt == OFF_EST_TIMING:
+                self.decode_chromaticity(-10)
+            elif self.cnt == OFF_STD_TIMING:
+                self.decode_est_timing(-3)
+            elif self.cnt == OFF_DET_TIMING:
+                self.decode_std_timing(-16)
+            elif self.cnt == OFF_NUM_EXT:
+                self.decode_descriptors(-72)
+            elif self.cnt == OFF_CHECKSUM:
+                self.put(ss, es, self.out_ann,
+                    [0, ['Extensions present: %d' % self.cache[self.cnt-1]]])
+            elif self.cnt == OFF_CHECKSUM+1:
+                checksum = 0
+                for i in range(128):
+                    checksum += self.cache[i]
+                if checksum % 256 == 0:
+                    csstr = 'OK'
+                else:
+                    csstr = 'WRONG!'
+                self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % (
+                         self.cache[self.cnt-1], csstr)]])
+                self.state = 'extensions'
+        elif self.state == 'extensions':
+            pass
+
+    def ann_field(self, start, end, annotation):
+        self.put(self.sn[start][0], self.sn[end][1],
+                 self.out_ann, [ANN_FIELDS, [annotation]])
+
+    def lookup_pnpid(self, pnpid):
+        pnpid_file = os.path.join(os.path.dirname(__file__), 'pnpids.txt')
+        if os.path.exists(pnpid_file):
+            for line in open(pnpid_file).readlines():
+                if line.find(pnpid + ';') == 0:
+                    return line[4:].strip()
+        return ''
+
+    def decode_vid(self, offset):
+        pnpid = chr(64 + ((self.cache[offset] & 0x7c) >> 2))
+        pnpid += chr(64 + (((self.cache[offset] & 0x03) << 3)
+                           | ((self.cache[offset+1] & 0xe0) >> 5)))
+        pnpid += chr(64 + (self.cache[offset+1] & 0x1f))
+        vendor = self.lookup_pnpid(pnpid)
+        if vendor:
+            pnpid += ' (%s)' % vendor
+        self.ann_field(offset, offset+1, pnpid)
+
+    def decode_pid(self, offset):
+        pidstr = 'Product 0x%.2x%.2x' % (self.cache[offset+1], self.cache[offset])
+        self.ann_field(offset, offset+1, pidstr)
+
+    def decode_serial(self, offset):
+        serialnum = (self.cache[offset+3] << 24) \
+                + (self.cache[offset+2] << 16) \
+                + (self.cache[offset+1] << 8) \
+                + self.cache[offset]
+        serialstr = ''
+        is_alnum = True
+        for i in range(4):
+            if not chr(self.cache[offset+3-i]).isalnum():
+                is_alnum = False
+                break
+            serialstr += chr(self.cache[offset+3-i])
+        serial = serialstr if is_alnum else str(serialnum)
+        self.ann_field(offset, offset+3, 'Serial ' + serial)
+
+    def decode_mfrdate(self, offset):
+        datestr = ''
+        if self.cache[offset]:
+            datestr += 'week %d, ' % self.cache[offset]
+        datestr += str(1990 + self.cache[offset+1])
+        if datestr:
+            self.ann_field(offset, offset+1, 'Manufactured ' + datestr)
+
+    def decode_basicdisplay(self, offset):
+        # Video input definition
+        vid = self.cache[offset]
+        if vid & 0x80:
+            # Digital
+            self.ann_field(offset, offset, 'Video input: VESA DFP 1.')
+        else:
+            # Analog
+            sls = (vid & 60) >> 5
+            self.ann_field(offset, offset, 'Signal level standard: %.2x' % sls)
+            if vid & 0x10:
+                self.ann_field(offset, offset, 'Blank-to-black setup expected')
+            syncs = ''
+            if vid & 0x08:
+                syncs += 'separate syncs, '
+            if vid & 0x04:
+                syncs += 'composite syncs, '
+            if vid & 0x02:
+                syncs += 'sync on green, '
+            if vid & 0x01:
+                syncs += 'Vsync serration required, '
+            if syncs:
+                self.ann_field(offset, offset, 'Supported syncs: %s' % syncs[:-2])
+        # Max horizontal/vertical image size
+        if self.cache[offset+1] != 0 and self.cache[offset+2] != 0:
+            # Projectors have this set to 0
+            sizestr = '%dx%dcm' % (self.cache[offset+1], self.cache[offset+2])
+            self.ann_field(offset+1, offset+2, 'Physical size: ' + sizestr)
+        # Display transfer characteristic (gamma)
+        if self.cache[offset+3] != 0xff:
+            gamma = (self.cache[offset+3] + 100) / 100
+            self.ann_field(offset+3, offset+3, 'Gamma: %1.2f' % gamma)
+        # Feature support
+        fs = self.cache[offset+4]
+        dpms = ''
+        if fs & 0x80:
+            dpms += 'standby, '
+        if fs & 0x40:
+            dpms += 'suspend, '
+        if fs & 0x20:
+            dpms += 'active off, '
+        if dpms:
+            self.ann_field(offset+4, offset+4, 'DPMS support: %s' % dpms[:-2])
+        dt = (fs & 0x18) >> 3
+        dtstr = ''
+        if dt == 0:
+            dtstr = 'Monochrome'
+        elif dt == 1:
+            dtstr = 'RGB color'
+        elif dt == 2:
+            dtstr = 'non-RGB multicolor'
+        if dtstr:
+            self.ann_field(offset+4, offset+4, 'Display type: %s' % dtstr)
+        if fs & 0x04:
+            self.ann_field(offset+4, offset+4, 'Color space: standard sRGB')
+        # Save this for when we decode the first detailed timing descriptor
+        self.have_preferred_timing = (fs & 0x02) == 0x02
+        if fs & 0x01:
+            gft = ''
+        else:
+            gft = 'not '
+        self.ann_field(offset+4, offset+4,
+                       'Generalized timing formula: %ssupported' % gft)
+
+    def convert_color(self, value):
+        # Convert from 10-bit packet format to float
+        outval = 0.0
+        for i in range(10):
+            if value & 0x01:
+                outval += 2 ** -(10-i)
+            value >>= 1
+        return outval
+
+    def decode_chromaticity(self, offset):
+        redx = (self.cache[offset+2] << 2) + ((self.cache[offset] & 0xc0) >> 6)
+        redy = (self.cache[offset+3] << 2) + ((self.cache[offset] & 0x30) >> 4)
+        self.ann_field(offset, offset+9, 'Chromacity red: X %1.3f, Y %1.3f' % (
+                       self.convert_color(redx), self.convert_color(redy)))
+
+        greenx = (self.cache[offset+4] << 2) + ((self.cache[offset] & 0x0c) >> 6)
+        greeny = (self.cache[offset+5] << 2) + ((self.cache[offset] & 0x03) >> 4)
+        self.ann_field(offset, offset+9, 'Chromacity green: X %1.3f, Y %1.3f' % (
+                       self.convert_color(greenx), self.convert_color(greeny)))
+
+        bluex = (self.cache[offset+6] << 2) + ((self.cache[offset+1] & 0xc0) >> 6)
+        bluey = (self.cache[offset+7] << 2) + ((self.cache[offset+1] & 0x30) >> 4)
+        self.ann_field(offset, offset+9, 'Chromacity blue: X %1.3f, Y %1.3f' % (
+                       self.convert_color(bluex), self.convert_color(bluey)))
+
+        whitex = (self.cache[offset+8] << 2) + ((self.cache[offset+1] & 0x0c) >> 6)
+        whitey = (self.cache[offset+9] << 2) + ((self.cache[offset+1] & 0x03) >> 4)
+        self.ann_field(offset, offset+9, 'Chromacity white: X %1.3f, Y %1.3f' % (
+                       self.convert_color(whitex), self.convert_color(whitey)))
+
+    def decode_est_timing(self, offset):
+        # Pre-EDID modes
+        bitmap = (self.cache[offset] << 9) \
+            + (self.cache[offset+1] << 1) \
+            + ((self.cache[offset+2] & 0x80) >> 7)
+        modestr = ''
+        for i in range(17):
+                if bitmap & (1 << (16-i)):
+                    modestr += est_modes[i] + ', '
+        if modestr:
+            self.ann_field(offset, offset+2,
+                           'Supported establised modes: %s' % modestr[:-2])
+
+    def decode_std_timing(self, offset):
+        modestr = ''
+        for i in range(0, 16, 2):
+            if self.cache[offset+i] == 0x01 and self.cache[offset+i+1] == 0x01:
+                # Unused field
+                continue
+            x = (self.cache[offset+i] + 31) * 8
+            ratio = (self.cache[offset+i+1] & 0xc0) >> 6
+            ratio_x, ratio_y = xy_ratio[ratio]
+            y = x / ratio_x * ratio_y
+            refresh = (self.cache[offset+i+1] & 0x3f) + 60
+            modestr += '%dx%d@%dHz, ' % (x, y, refresh)
+        if modestr:
+            self.ann_field(offset, offset+2,
+                           'Supported standard modes: %s' % modestr[:-2])
+
+    def decode_detailed_timing(self, offset):
+        if offset == -72 and self.have_preferred_timing:
+            # Only on first detailed timing descriptor
+            section = 'Preferred'
+        else:
+            section = 'Detailed'
+        section += ' timing descriptor'
+        self.put(self.sn[offset][0], self.sn[offset+18][1],
+             self.out_ann, [ANN_SECTIONS, [section]])
+
+        pixclock = float((self.cache[offset+1] << 8) + self.cache[offset]) / 100
+        self.ann_field(offset, offset+1, 'Pixel clock: %.2f MHz' % pixclock)
+
+        horiz_active = ((self.cache[offset+4] & 0xf0) << 4) + self.cache[offset+2]
+        self.ann_field(offset+2, offset+4, 'Horizontal active: %d' % horiz_active)
+
+        horiz_blank = ((self.cache[offset+4] & 0x0f) << 8) + self.cache[offset+3]
+        self.ann_field(offset+3, offset+4, 'Horizontal blanking: %d' % horiz_blank)
+
+        vert_active = ((self.cache[offset+7] & 0xf0) << 4) + self.cache[offset+5]
+        self.ann_field(offset+5, offset+7, 'Vertical active: %d' % vert_active)
+
+        vert_blank = ((self.cache[offset+7] & 0x0f) << 8) + self.cache[offset+6]
+        self.ann_field(offset+6, offset+7, 'Vertical blanking: %d' % vert_blank)
+
+        horiz_sync_off = ((self.cache[offset+11] & 0xc0) << 2) + self.cache[offset+8]
+        self.ann_field(offset+8, offset+11, 'Horizontal sync offset: %d' % horiz_sync_off)
+
+        horiz_sync_pw = ((self.cache[offset+11] & 0x30) << 4) + self.cache[offset+9]
+        self.ann_field(offset+9, offset+11, 'Horizontal sync pulse width: %d' % horiz_sync_pw)
+
+        vert_sync_off = ((self.cache[offset+11] & 0x0c) << 2) \
+                    + ((self.cache[offset+10] & 0xf0) >> 4)
+        self.ann_field(offset+10, offset+11, 'Vertical sync offset: %d' % vert_sync_off)
+
+        vert_sync_pw = ((self.cache[offset+11] & 0x03) << 4) \
+                    + (self.cache[offset+10] & 0x0f)
+        self.ann_field(offset+10, offset+11, 'Vertical sync pulse width: %d' % vert_sync_pw)
+
+        horiz_size = ((self.cache[offset+14] & 0xf0) << 4) + self.cache[offset+12]
+        vert_size = ((self.cache[offset+14] & 0x0f) << 8) + self.cache[offset+13]
+        self.ann_field(offset+12, offset+14, 'Physical size: %dx%dmm' % (horiz_size, vert_size))
+
+        horiz_border = self.cache[offset+15]
+        if horiz_border:
+            self.ann_field(offset+15, offset+15, 'Horizontal border: %d pixels' % horiz_border)
+        vert_border = self.cache[offset+16]
+        if vert_border:
+            self.ann_field(offset+16, offset+16, 'Vertical border: %d lines' % vert_border)
+
+        features = 'Flags: '
+        if self.cache[offset+17] & 0x80:
+            features += 'interlaced, '
+        stereo = (self.cache[offset+17] & 0x60) >> 5
+        if stereo:
+            if self.cache[offset+17] & 0x01:
+                features += '2-way interleaved stereo ('
+                features += ['right image on even lines',
+                             'left image on even lines',
+                             'side-by-side'][stereo-1]
+                features += '), '
+            else:
+                features += 'field sequential stereo ('
+                features += ['right image on sync=1', 'left image on sync=1',
+                             '4-way interleaved'][stereo-1]
+                features += '), '
+        sync = (self.cache[offset+17] & 0x18) >> 3
+        sync2 = (self.cache[offset+17] & 0x06) >> 1
+        posneg = ['negative', 'positive']
+        features += 'sync type '
+        if sync == 0x00:
+            features += 'analog composite (serrate on RGB)'
+        elif sync == 0x01:
+            features += 'bipolar analog composite (serrate on RGB)'
+        elif sync == 0x02:
+            features += 'digital composite (serrate on composite polarity ' \
+                        + (posneg[sync2 & 0x01]) + ')'
+        elif sync == 0x03:
+            features += 'digital separate ('
+            features += 'Vsync polarity ' + (posneg[(sync2 & 0x02) >> 1])
+            features += ', Hsync polarity ' + (posneg[sync2 & 0x01])
+            features += ')'
+        features += ', '
+        self.ann_field(offset+17, offset+17, features[:-2])
+
+    def decode_descriptor(self, offset):
+        tag = self.cache[offset+3]
+        if tag == 0xff:
+            # Monitor serial number
+            text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace')
+            self.ann_field(offset, offset+17, 'Serial number: %s' % text.strip())
+        elif tag == 0xfe:
+            # Text
+            text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace')
+            self.ann_field(offset, offset+17, 'Info: %s' % text.strip())
+        elif tag == 0xfc:
+            # Monitor name
+            text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace')
+            self.ann_field(offset, offset+17, 'Model name: %s' % text.strip())
+        elif tag == 0xfd:
+            # Monitor range limits
+            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
+                     [ANN_SECTIONS, ['Monitor range limits']])
+            self.ann_field(offset+5, offset+5, 'Minimum vertical rate: %dHz' %
+                           self.cache[offset+5])
+            self.ann_field(offset+6, offset+6, 'Maximum vertical rate: %dHz' %
+                           self.cache[offset+6])
+            self.ann_field(offset+7, offset+7, 'Minimum horizontal rate: %dkHz' %
+                           self.cache[offset+7])
+            self.ann_field(offset+8, offset+8, 'Maximum horizontal rate: %dkHz' %
+                           self.cache[offset+8])
+            self.ann_field(offset+9, offset+9, 'Maximum pixel clock: %dMHz' %
+                           (self.cache[offset+9] * 10))
+            if self.cache[offset+10] == 0x02:
+                # Secondary GTF curve supported
+                self.ann_field(offset+10, offset+17, 'Secondary timing formula supported')
+        elif tag == 0xfb:
+            # Additional color point data
+            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
+                     [ANN_SECTIONS, ['Additional color point data']])
+        elif tag == 0xfa:
+            # Additional standard timing definitions
+            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
+                     [ANN_SECTIONS, ['Additional standard timing definitions']])
+        else:
+            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
+                     [ANN_SECTIONS, ['Unknown descriptor']])
+
+    def decode_descriptors(self, offset):
+        # 4 consecutive 18-byte descriptor blocks
+        for i in range(offset, 0, 18):
+            if self.cache[i] != 0 and self.cache[i+1] != 0:
+                self.decode_detailed_timing(i)
+            else:
+                if self.cache[i+2] == 0 or self.cache[i+4] == 0:
+                    self.decode_descriptor(i)
+
index 75a53eb499d0af0f39085712439152b91e549f7f..c25393f7371267d2953c7477d167437d47c46cc1 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/i2c
 
 
 pkgdatadir = $(DECODERS_DIR)/i2c
 
-dist_pkgdata_DATA = __init__.py i2c.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 14262d18e9177a1f8860801e2b9d2df3199019c8..6c7dafe7d75e7cf20f4c62c219db307aadb81a33 100644 (file)
@@ -79,5 +79,5 @@ For 'START', 'START REPEAT', 'STOP', 'ACK', and 'NACK' <data> is None.
 
 '''
 
 
 '''
 
-from .i2c import *
+from .pd import *
 
 
diff --git a/decoders/i2c/i2c.py b/decoders/i2c/i2c.py
deleted file mode 100644 (file)
index 53321eb..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2010-2011 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# I2C protocol decoder
-
-# TODO: Look into arbitration, collision detection, clock synchronisation, etc.
-# TODO: Handle clock stretching.
-# TODO: Handle combined messages / repeated START.
-# TODO: Implement support for 7bit and 10bit slave addresses.
-# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
-# TODO: Implement support for detecting various bus errors.
-# TODO: I2C address of slaves.
-# TODO: Handle multiple different I2C devices on same bus
-#       -> we need to decode multiple protocols at the same time.
-
-import sigrokdecode as srd
-
-# Annotation feed formats
-ANN_SHIFTED = 0
-ANN_SHIFTED_SHORT = 1
-ANN_RAW = 2
-
-# Values are verbose and short annotation, respectively.
-proto = {
-    'START':           ['START',         'S'],
-    'START REPEAT':    ['START REPEAT',  'Sr'],
-    'STOP':            ['STOP',          'P'],
-    'ACK':             ['ACK',           'A'],
-    'NACK':            ['NACK',          'N'],
-    'ADDRESS READ':    ['ADDRESS READ',  'AR'],
-    'ADDRESS WRITE':   ['ADDRESS WRITE', 'AW'],
-    'DATA READ':       ['DATA READ',     'DR'],
-    'DATA WRITE':      ['DATA WRITE',    'DW'],
-}
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'i2c'
-    name = 'I2C'
-    longname = 'Inter-Integrated Circuit'
-    desc = 'Two-wire, multi-master, serial bus.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['i2c']
-    probes = [
-        {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'},
-        {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
-    ]
-    optional_probes = []
-    options = {
-        'addressing': ['Slave addressing (in bits)', 7], # 7 or 10
-    }
-    annotations = [
-        # ANN_SHIFTED
-        ['7-bit shifted hex',
-         'Read/write bit shifted out from the 8-bit I2C slave address'],
-        # ANN_SHIFTED_SHORT
-        ['7-bit shifted hex (short)',
-         'Read/write bit shifted out from the 8-bit I2C slave address'],
-        # ANN_RAW
-        ['Raw hex', 'Unaltered raw data'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.startsample = -1
-        self.samplenum = None
-        self.bitcount = 0
-        self.databyte = 0
-        self.wr = -1
-        self.is_repeat_start = 0
-        self.state = 'FIND START'
-        self.oldscl = None
-        self.oldsda = None
-        self.oldpins = None
-
-    def start(self, metadata):
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'i2c')
-
-    def report(self):
-        pass
-
-    def is_start_condition(self, scl, sda):
-        # START condition (S): SDA = falling, SCL = high
-        if (self.oldsda == 1 and sda == 0) and scl == 1:
-            return True
-        return False
-
-    def is_data_bit(self, scl, sda):
-        # Data sampling of receiver: SCL = rising
-        if self.oldscl == 0 and scl == 1:
-            return True
-        return False
-
-    def is_stop_condition(self, scl, sda):
-        # STOP condition (P): SDA = rising, SCL = high
-        if (self.oldsda == 0 and sda == 1) and scl == 1:
-            return True
-        return False
-
-    def found_start(self, scl, sda):
-        self.startsample = self.samplenum
-
-        cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
-        self.put(self.out_proto, [cmd, None])
-        self.put(self.out_ann, [ANN_SHIFTED, [proto[cmd][0]]])
-        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto[cmd][1]]])
-
-        self.state = 'FIND ADDRESS'
-        self.bitcount = self.databyte = 0
-        self.is_repeat_start = 1
-        self.wr = -1
-
-    # Gather 8 bits of data plus the ACK/NACK bit.
-    def found_address_or_data(self, scl, sda):
-        # Address and data are transmitted MSB-first.
-        self.databyte <<= 1
-        self.databyte |= sda
-
-        if self.bitcount == 0:
-            self.startsample = self.samplenum
-
-        # Return if we haven't collected all 8 + 1 bits, yet.
-        self.bitcount += 1
-        if self.bitcount != 8:
-            return
-
-        # We triggered on the ACK/NACK bit, but won't report that until later.
-        self.startsample -= 1
-
-        # Send raw output annotation before we start shifting out
-        # read/write and ACK/NACK bits.
-        self.put(self.out_ann, [ANN_RAW, ['0x%.2x' % self.databyte]])
-
-        if self.state == 'FIND ADDRESS':
-            # The READ/WRITE bit is only in address bytes, not data bytes.
-            self.wr = 0 if (self.databyte & 1) else 1
-            d = self.databyte >> 1
-        elif self.state == 'FIND DATA':
-            d = self.databyte
-
-        if self.state == 'FIND ADDRESS' and self.wr == 1:
-            cmd = 'ADDRESS WRITE'
-        elif self.state == 'FIND ADDRESS' and self.wr == 0:
-            cmd = 'ADDRESS READ'
-        elif self.state == 'FIND DATA' and self.wr == 1:
-            cmd = 'DATA WRITE'
-        elif self.state == 'FIND DATA' and self.wr == 0:
-            cmd = 'DATA READ'
-
-        self.put(self.out_proto, [cmd, d])
-        self.put(self.out_ann, [ANN_SHIFTED, [proto[cmd][0], '0x%02x' % d]])
-        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto[cmd][1], '0x%02x' % d]])
-
-        # Done with this packet.
-        self.startsample = -1
-        self.bitcount = self.databyte = 0
-        self.state = 'FIND ACK'
-
-    def get_ack(self, scl, sda):
-        self.startsample = self.samplenum
-        ack_bit = 'NACK' if (sda == 1) else 'ACK'
-        self.put(self.out_proto, [ack_bit, None])
-        self.put(self.out_ann, [ANN_SHIFTED, [proto[ack_bit][0]]])
-        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto[ack_bit][1]]])
-        # There could be multiple data bytes in a row, so either find
-        # another data byte or a STOP condition next.
-        self.state = 'FIND DATA'
-
-    def found_stop(self, scl, sda):
-        self.startsample = self.samplenum
-        self.put(self.out_proto, ['STOP', None])
-        self.put(self.out_ann, [ANN_SHIFTED, [proto['STOP'][0]]])
-        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto['STOP'][1]]])
-
-        self.state = 'FIND START'
-        self.is_repeat_start = 0
-        self.wr = -1
-
-    def put(self, output_id, data):
-        # Inject sample range into the call up to sigrok.
-        super(Decoder, self).put(self.startsample, self.samplenum, output_id, data)
-
-    def decode(self, ss, es, data):
-        for (self.samplenum, pins) in data:
-
-            # Ignore identical samples early on (for performance reasons).
-            if self.oldpins == pins:
-                continue
-            self.oldpins, (scl, sda) = pins, pins
-
-            # First sample: Save SCL/SDA value.
-            if self.oldscl == None:
-                self.oldscl = scl
-                self.oldsda = sda
-                continue
-
-            # TODO: Wait until the bus is idle (SDA = SCL = 1) first?
-
-            # State machine.
-            if self.state == 'FIND START':
-                if self.is_start_condition(scl, sda):
-                    self.found_start(scl, sda)
-            elif self.state == 'FIND ADDRESS':
-                if self.is_data_bit(scl, sda):
-                    self.found_address_or_data(scl, sda)
-            elif self.state == 'FIND DATA':
-                if self.is_data_bit(scl, sda):
-                    self.found_address_or_data(scl, sda)
-                elif self.is_start_condition(scl, sda):
-                    self.found_start(scl, sda)
-                elif self.is_stop_condition(scl, sda):
-                    self.found_stop(scl, sda)
-            elif self.state == 'FIND ACK':
-                if self.is_data_bit(scl, sda):
-                    self.get_ack(scl, sda)
-            else:
-                raise Exception('Invalid state %d' % self.STATE)
-
-            # Save current SDA/SCL values for the next round.
-            self.oldscl = scl
-            self.oldsda = sda
-
diff --git a/decoders/i2c/pd.py b/decoders/i2c/pd.py
new file mode 100644 (file)
index 0000000..53321eb
--- /dev/null
@@ -0,0 +1,240 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2010-2011 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# I2C protocol decoder
+
+# TODO: Look into arbitration, collision detection, clock synchronisation, etc.
+# TODO: Handle clock stretching.
+# TODO: Handle combined messages / repeated START.
+# TODO: Implement support for 7bit and 10bit slave addresses.
+# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
+# TODO: Implement support for detecting various bus errors.
+# TODO: I2C address of slaves.
+# TODO: Handle multiple different I2C devices on same bus
+#       -> we need to decode multiple protocols at the same time.
+
+import sigrokdecode as srd
+
+# Annotation feed formats
+ANN_SHIFTED = 0
+ANN_SHIFTED_SHORT = 1
+ANN_RAW = 2
+
+# Values are verbose and short annotation, respectively.
+proto = {
+    'START':           ['START',         'S'],
+    'START REPEAT':    ['START REPEAT',  'Sr'],
+    'STOP':            ['STOP',          'P'],
+    'ACK':             ['ACK',           'A'],
+    'NACK':            ['NACK',          'N'],
+    'ADDRESS READ':    ['ADDRESS READ',  'AR'],
+    'ADDRESS WRITE':   ['ADDRESS WRITE', 'AW'],
+    'DATA READ':       ['DATA READ',     'DR'],
+    'DATA WRITE':      ['DATA WRITE',    'DW'],
+}
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'i2c'
+    name = 'I2C'
+    longname = 'Inter-Integrated Circuit'
+    desc = 'Two-wire, multi-master, serial bus.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['i2c']
+    probes = [
+        {'id': 'scl', 'name': 'SCL', 'desc': 'Serial clock line'},
+        {'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
+    ]
+    optional_probes = []
+    options = {
+        'addressing': ['Slave addressing (in bits)', 7], # 7 or 10
+    }
+    annotations = [
+        # ANN_SHIFTED
+        ['7-bit shifted hex',
+         'Read/write bit shifted out from the 8-bit I2C slave address'],
+        # ANN_SHIFTED_SHORT
+        ['7-bit shifted hex (short)',
+         'Read/write bit shifted out from the 8-bit I2C slave address'],
+        # ANN_RAW
+        ['Raw hex', 'Unaltered raw data'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.startsample = -1
+        self.samplenum = None
+        self.bitcount = 0
+        self.databyte = 0
+        self.wr = -1
+        self.is_repeat_start = 0
+        self.state = 'FIND START'
+        self.oldscl = None
+        self.oldsda = None
+        self.oldpins = None
+
+    def start(self, metadata):
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'i2c')
+
+    def report(self):
+        pass
+
+    def is_start_condition(self, scl, sda):
+        # START condition (S): SDA = falling, SCL = high
+        if (self.oldsda == 1 and sda == 0) and scl == 1:
+            return True
+        return False
+
+    def is_data_bit(self, scl, sda):
+        # Data sampling of receiver: SCL = rising
+        if self.oldscl == 0 and scl == 1:
+            return True
+        return False
+
+    def is_stop_condition(self, scl, sda):
+        # STOP condition (P): SDA = rising, SCL = high
+        if (self.oldsda == 0 and sda == 1) and scl == 1:
+            return True
+        return False
+
+    def found_start(self, scl, sda):
+        self.startsample = self.samplenum
+
+        cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
+        self.put(self.out_proto, [cmd, None])
+        self.put(self.out_ann, [ANN_SHIFTED, [proto[cmd][0]]])
+        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto[cmd][1]]])
+
+        self.state = 'FIND ADDRESS'
+        self.bitcount = self.databyte = 0
+        self.is_repeat_start = 1
+        self.wr = -1
+
+    # Gather 8 bits of data plus the ACK/NACK bit.
+    def found_address_or_data(self, scl, sda):
+        # Address and data are transmitted MSB-first.
+        self.databyte <<= 1
+        self.databyte |= sda
+
+        if self.bitcount == 0:
+            self.startsample = self.samplenum
+
+        # Return if we haven't collected all 8 + 1 bits, yet.
+        self.bitcount += 1
+        if self.bitcount != 8:
+            return
+
+        # We triggered on the ACK/NACK bit, but won't report that until later.
+        self.startsample -= 1
+
+        # Send raw output annotation before we start shifting out
+        # read/write and ACK/NACK bits.
+        self.put(self.out_ann, [ANN_RAW, ['0x%.2x' % self.databyte]])
+
+        if self.state == 'FIND ADDRESS':
+            # The READ/WRITE bit is only in address bytes, not data bytes.
+            self.wr = 0 if (self.databyte & 1) else 1
+            d = self.databyte >> 1
+        elif self.state == 'FIND DATA':
+            d = self.databyte
+
+        if self.state == 'FIND ADDRESS' and self.wr == 1:
+            cmd = 'ADDRESS WRITE'
+        elif self.state == 'FIND ADDRESS' and self.wr == 0:
+            cmd = 'ADDRESS READ'
+        elif self.state == 'FIND DATA' and self.wr == 1:
+            cmd = 'DATA WRITE'
+        elif self.state == 'FIND DATA' and self.wr == 0:
+            cmd = 'DATA READ'
+
+        self.put(self.out_proto, [cmd, d])
+        self.put(self.out_ann, [ANN_SHIFTED, [proto[cmd][0], '0x%02x' % d]])
+        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto[cmd][1], '0x%02x' % d]])
+
+        # Done with this packet.
+        self.startsample = -1
+        self.bitcount = self.databyte = 0
+        self.state = 'FIND ACK'
+
+    def get_ack(self, scl, sda):
+        self.startsample = self.samplenum
+        ack_bit = 'NACK' if (sda == 1) else 'ACK'
+        self.put(self.out_proto, [ack_bit, None])
+        self.put(self.out_ann, [ANN_SHIFTED, [proto[ack_bit][0]]])
+        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto[ack_bit][1]]])
+        # There could be multiple data bytes in a row, so either find
+        # another data byte or a STOP condition next.
+        self.state = 'FIND DATA'
+
+    def found_stop(self, scl, sda):
+        self.startsample = self.samplenum
+        self.put(self.out_proto, ['STOP', None])
+        self.put(self.out_ann, [ANN_SHIFTED, [proto['STOP'][0]]])
+        self.put(self.out_ann, [ANN_SHIFTED_SHORT, [proto['STOP'][1]]])
+
+        self.state = 'FIND START'
+        self.is_repeat_start = 0
+        self.wr = -1
+
+    def put(self, output_id, data):
+        # Inject sample range into the call up to sigrok.
+        super(Decoder, self).put(self.startsample, self.samplenum, output_id, data)
+
+    def decode(self, ss, es, data):
+        for (self.samplenum, pins) in data:
+
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpins == pins:
+                continue
+            self.oldpins, (scl, sda) = pins, pins
+
+            # First sample: Save SCL/SDA value.
+            if self.oldscl == None:
+                self.oldscl = scl
+                self.oldsda = sda
+                continue
+
+            # TODO: Wait until the bus is idle (SDA = SCL = 1) first?
+
+            # State machine.
+            if self.state == 'FIND START':
+                if self.is_start_condition(scl, sda):
+                    self.found_start(scl, sda)
+            elif self.state == 'FIND ADDRESS':
+                if self.is_data_bit(scl, sda):
+                    self.found_address_or_data(scl, sda)
+            elif self.state == 'FIND DATA':
+                if self.is_data_bit(scl, sda):
+                    self.found_address_or_data(scl, sda)
+                elif self.is_start_condition(scl, sda):
+                    self.found_start(scl, sda)
+                elif self.is_stop_condition(scl, sda):
+                    self.found_stop(scl, sda)
+            elif self.state == 'FIND ACK':
+                if self.is_data_bit(scl, sda):
+                    self.get_ack(scl, sda)
+            else:
+                raise Exception('Invalid state %d' % self.STATE)
+
+            # Save current SDA/SCL values for the next round.
+            self.oldscl = scl
+            self.oldsda = sda
+
index d305394ce93eddf413513f0b0cdf591d7b8dcaf4..bc45d02b7fc6fd4c38825c82c2ba691ff733e257 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/i2cdemux
 
 
 pkgdatadir = $(DECODERS_DIR)/i2cdemux
 
-dist_pkgdata_DATA = __init__.py i2cdemux.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 445a88e501fa2183328934ab2703f86e6a8a8643..1e5afbd11b4c88fb7b6aa5f376267bf72b5c8051 100644 (file)
@@ -25,5 +25,5 @@ Takes an I2C stream as input and outputs multiple I2C streams, each stream
 containing only I2C packets for one specific I2C slave.
 '''
 
 containing only I2C packets for one specific I2C slave.
 '''
 
-from .i2cdemux import *
+from .pd import *
 
 
diff --git a/decoders/i2cdemux/i2cdemux.py b/decoders/i2cdemux/i2cdemux.py
deleted file mode 100644 (file)
index bb47f23..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Generic I2C demultiplexing protocol decoder
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'i2cdemux'
-    name = 'I2C demux'
-    longname = 'I2C demultiplexer'
-    desc = 'Demux I2C packets into per-slave-address streams.'
-    license = 'gplv2+'
-    inputs = ['i2c']
-    outputs = [] # TODO: Only known at run-time.
-    probes = []
-    optional_probes = []
-    options = {}
-    annotations = []
-
-    def __init__(self, **kwargs):
-        self.packets = [] # Local cache of I2C packets
-        self.slaves = [] # List of known slave addresses
-        self.stream = -1 # Current output stream
-        self.streamcount = 0 # Number of created output streams
-
-    def start(self, metadata):
-        self.out_proto = []
-
-    def report(self):
-        pass
-
-    # Grab I2C packets into a local cache, until an I2C STOP condition
-    # packet comes along. At some point before that STOP condition, there
-    # will have been an ADDRESS READ or ADDRESS WRITE which contains the
-    # I2C address of the slave that the master wants to talk to.
-    # We use this slave address to figure out which output stream should
-    # get the whole chunk of packets (from START to STOP).
-    def decode(self, ss, es, data):
-
-        cmd, databyte = data
-
-        # Add the I2C packet to our local cache.
-        self.packets.append([ss, es, data])
-
-        if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
-            if databyte in self.slaves:
-                self.stream = self.slaves.index(databyte)
-                return
-
-            # We're never seen this slave, add a new stream.
-            self.slaves.append(databyte)
-            self.out_proto.append(self.add(srd.OUTPUT_PROTO,
-                                  'i2c-%s' % hex(databyte)))
-            self.stream = self.streamcount
-            self.streamcount += 1
-        elif cmd == 'STOP':
-            if self.stream == -1:
-                raise Exception('Invalid stream!') # FIXME?
-
-            # Send the whole chunk of I2C packets to the correct stream.
-            for p in self.packets:
-                self.put(p[0], p[1], self.out_proto[self.stream], p[2])
-
-            self.packets = []
-            self.stream = -1
-        else:
-            pass # Do nothing, only add the I2C packet to our cache.
-
diff --git a/decoders/i2cdemux/pd.py b/decoders/i2cdemux/pd.py
new file mode 100644 (file)
index 0000000..bb47f23
--- /dev/null
@@ -0,0 +1,87 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Generic I2C demultiplexing protocol decoder
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'i2cdemux'
+    name = 'I2C demux'
+    longname = 'I2C demultiplexer'
+    desc = 'Demux I2C packets into per-slave-address streams.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = [] # TODO: Only known at run-time.
+    probes = []
+    optional_probes = []
+    options = {}
+    annotations = []
+
+    def __init__(self, **kwargs):
+        self.packets = [] # Local cache of I2C packets
+        self.slaves = [] # List of known slave addresses
+        self.stream = -1 # Current output stream
+        self.streamcount = 0 # Number of created output streams
+
+    def start(self, metadata):
+        self.out_proto = []
+
+    def report(self):
+        pass
+
+    # Grab I2C packets into a local cache, until an I2C STOP condition
+    # packet comes along. At some point before that STOP condition, there
+    # will have been an ADDRESS READ or ADDRESS WRITE which contains the
+    # I2C address of the slave that the master wants to talk to.
+    # We use this slave address to figure out which output stream should
+    # get the whole chunk of packets (from START to STOP).
+    def decode(self, ss, es, data):
+
+        cmd, databyte = data
+
+        # Add the I2C packet to our local cache.
+        self.packets.append([ss, es, data])
+
+        if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
+            if databyte in self.slaves:
+                self.stream = self.slaves.index(databyte)
+                return
+
+            # We're never seen this slave, add a new stream.
+            self.slaves.append(databyte)
+            self.out_proto.append(self.add(srd.OUTPUT_PROTO,
+                                  'i2c-%s' % hex(databyte)))
+            self.stream = self.streamcount
+            self.streamcount += 1
+        elif cmd == 'STOP':
+            if self.stream == -1:
+                raise Exception('Invalid stream!') # FIXME?
+
+            # Send the whole chunk of I2C packets to the correct stream.
+            for p in self.packets:
+                self.put(p[0], p[1], self.out_proto[self.stream], p[2])
+
+            self.packets = []
+            self.stream = -1
+        else:
+            pass # Do nothing, only add the I2C packet to our cache.
+
index 5b90489a08a93f7f9eb822c46e088b1d0d72b618..ff7abc4ac5cb7e832e24c9b5d1067f79f993aa79 100644 (file)
@@ -19,7 +19,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/i2cfilter
 
 
 pkgdatadir = $(DECODERS_DIR)/i2cfilter
 
-dist_pkgdata_DATA = __init__.py i2cfilter.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 6443f5b620d61093867dc80b631ca69775be51cc..9a0b256756941c91d62427cfd8a27ffcbbf59505 100644 (file)
@@ -34,5 +34,5 @@ Both of these are optional; if no options are specified the entire payload
 of the I2C session will be output.
 '''
 
 of the I2C session will be output.
 '''
 
-from .i2cfilter import *
+from .pd import *
 
 
diff --git a/decoders/i2cfilter/i2cfilter.py b/decoders/i2cfilter/i2cfilter.py
deleted file mode 100644 (file)
index 9c9c43a..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 3 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see <http://www.gnu.org/licenses/>.
-##
-
-# Generic I2C filtering protocol decoder
-
-# TODO: Support for filtering out multiple slave/direction pairs?
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'i2cfilter'
-    name = 'I2C filter'
-    longname = 'I2C filter'
-    desc = 'Filter out addresses/directions in an I2C stream.'
-    license = 'gplv3+'
-    inputs = ['i2c']
-    outputs = ['i2c']
-    probes = []
-    optional_probes = []
-    options = {
-        'address': ['Address to filter out of the I2C stream', 0],
-        'direction': ['Direction to filter (read/write/both)', 'both']
-    }
-    annotations = []
-
-    def __init__(self, **kwargs):
-        self.state = None
-        self.curslave = -1
-        self.curdirection = None
-        self.packets = [] # Local cache of I2C packets
-
-    def start(self, metadata):
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
-        if self.options['address'] not in range(0, 127 + 1):
-            raise Exception('Invalid slave (must be 0..127).')
-        if self.options['direction'] not in ('both', 'read', 'write'):
-            raise Exception('Invalid direction (valid: read/write/both).')
-
-    def report(self):
-        pass
-
-    # Grab I2C packets into a local cache, until an I2C STOP condition
-    # packet comes along. At some point before that STOP condition, there
-    # will have been an ADDRESS READ or ADDRESS WRITE which contains the
-    # I2C address of the slave that the master wants to talk to.
-    # If that slave shall be filtered, output the cache (all packets from
-    # START to STOP) as proto 'i2c', otherwise drop it.
-    def decode(self, ss, es, data):
-
-        cmd, databyte = data
-
-        # Add the I2C packet to our local cache.
-        self.packets.append([ss, es, data])
-
-        if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
-            self.curslave = databyte
-            self.curdirection = cmd[8:].lower()
-        elif cmd in ('STOP', 'START REPEAT'):
-            # If this chunk was not for the correct slave, drop it.
-            if self.options['address'] == 0:
-                pass
-            elif self.curslave != self.options['address']:
-                self.packets = []
-                return
-
-            # If this chunk was not in the right direction, drop it.
-            if self.options['direction'] == 'both':
-                pass
-            elif self.options['direction'] != self.curdirection:
-                self.packets = []
-                return
-
-            # TODO: START->STOP chunks with both read and write (Repeat START)
-            # Otherwise, send out the whole chunk of I2C packets.
-            for p in self.packets:
-                self.put(p[0], p[1], self.out_proto, p[2])
-
-            self.packets = []
-        else:
-            pass # Do nothing, only add the I2C packet to our cache.
-
diff --git a/decoders/i2cfilter/pd.py b/decoders/i2cfilter/pd.py
new file mode 100644 (file)
index 0000000..9c9c43a
--- /dev/null
@@ -0,0 +1,99 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# Generic I2C filtering protocol decoder
+
+# TODO: Support for filtering out multiple slave/direction pairs?
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'i2cfilter'
+    name = 'I2C filter'
+    longname = 'I2C filter'
+    desc = 'Filter out addresses/directions in an I2C stream.'
+    license = 'gplv3+'
+    inputs = ['i2c']
+    outputs = ['i2c']
+    probes = []
+    optional_probes = []
+    options = {
+        'address': ['Address to filter out of the I2C stream', 0],
+        'direction': ['Direction to filter (read/write/both)', 'both']
+    }
+    annotations = []
+
+    def __init__(self, **kwargs):
+        self.state = None
+        self.curslave = -1
+        self.curdirection = None
+        self.packets = [] # Local cache of I2C packets
+
+    def start(self, metadata):
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2c')
+        if self.options['address'] not in range(0, 127 + 1):
+            raise Exception('Invalid slave (must be 0..127).')
+        if self.options['direction'] not in ('both', 'read', 'write'):
+            raise Exception('Invalid direction (valid: read/write/both).')
+
+    def report(self):
+        pass
+
+    # Grab I2C packets into a local cache, until an I2C STOP condition
+    # packet comes along. At some point before that STOP condition, there
+    # will have been an ADDRESS READ or ADDRESS WRITE which contains the
+    # I2C address of the slave that the master wants to talk to.
+    # If that slave shall be filtered, output the cache (all packets from
+    # START to STOP) as proto 'i2c', otherwise drop it.
+    def decode(self, ss, es, data):
+
+        cmd, databyte = data
+
+        # Add the I2C packet to our local cache.
+        self.packets.append([ss, es, data])
+
+        if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
+            self.curslave = databyte
+            self.curdirection = cmd[8:].lower()
+        elif cmd in ('STOP', 'START REPEAT'):
+            # If this chunk was not for the correct slave, drop it.
+            if self.options['address'] == 0:
+                pass
+            elif self.curslave != self.options['address']:
+                self.packets = []
+                return
+
+            # If this chunk was not in the right direction, drop it.
+            if self.options['direction'] == 'both':
+                pass
+            elif self.options['direction'] != self.curdirection:
+                self.packets = []
+                return
+
+            # TODO: START->STOP chunks with both read and write (Repeat START)
+            # Otherwise, send out the whole chunk of I2C packets.
+            for p in self.packets:
+                self.put(p[0], p[1], self.out_proto, p[2])
+
+            self.packets = []
+        else:
+            pass # Do nothing, only add the I2C packet to our cache.
+
index ff669023fe7ce7d9c90d1093150b3bd4d4e1a0ab..1fcafcecef7d7307fdee2dbcacf3cd7a948d3341 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/i2s
 
 
 pkgdatadir = $(DECODERS_DIR)/i2s
 
-dist_pkgdata_DATA = __init__.py i2s.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 81b785baca0192f94ef4768f5af1a3144072fe6d..f48e94fce91b372b249ecfa56d88965ca2963d9f 100644 (file)
@@ -26,5 +26,5 @@ http://www.nxp.com/acrobat_download/various/I2SBUS.pdf
 http://en.wikipedia.org/wiki/I2s
 '''
 
 http://en.wikipedia.org/wiki/I2s
 '''
 
-from .i2s import *
+from .pd import *
 
 
diff --git a/decoders/i2s/i2s.py b/decoders/i2s/i2s.py
deleted file mode 100644 (file)
index b921011..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# I2S protocol decoder
-
-import sigrokdecode as srd
-
-# Annotation formats
-ANN_HEX = 0
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'i2s'
-    name = 'I2S'
-    longname = 'Integrated Interchip Sound'
-    desc = 'Serial bus for connecting digital audio devices.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['i2s']
-    probes = [
-        {'id': 'sck', 'name': 'SCK', 'desc': 'Bit clock line'},
-        {'id': 'ws', 'name': 'WS', 'desc': 'Word select line'},
-        {'id': 'sd', 'name': 'SD', 'desc': 'Serial data line'},
-    ]
-    optional_probes = []
-    options = {}
-    annotations = [
-        ['Hex', 'Annotations in hex format'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.oldsck = 1
-        self.oldws = 1
-        self.bitcount = 0
-        self.data = 0
-        self.samplesreceived = 0
-        self.first_sample = None
-        self.start_sample = None
-        self.samplenum = -1
-        self.wordlength = -1
-
-    def start(self, metadata):
-        self.samplerate = metadata['samplerate']
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2s')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'i2s')
-
-    def report(self):
-
-        # Calculate the sample rate.
-        samplerate = '?'
-        if self.start_sample != None and \
-            self.first_sample != None and \
-            self.start_sample > self.first_sample:
-            samplerate = '%d' % (self.samplesreceived *
-                self.samplerate / (self.start_sample -
-                self.first_sample))
-
-        return 'I2S: %d %d-bit samples received at %sHz' % \
-            (self.samplesreceived, self.wordlength, samplerate)
-
-    def decode(self, ss, es, data):
-        for samplenum, (sck, ws, sd) in data:
-
-            # Ignore sample if the bit clock hasn't changed.
-            if sck == self.oldsck:
-                continue
-
-            self.oldsck = sck
-            if sck == 0:   # Ignore the falling clock edge.
-                continue
-
-            self.data = (self.data << 1) | sd
-            self.bitcount += 1
-
-            # This was not the LSB unless WS has flipped.
-            if ws == self.oldws:
-                continue
-
-            # Only submit the sample, if we received the beginning of it.
-            if self.start_sample != None:
-                self.samplesreceived += 1
-                self.put(self.start_sample, self.samplenum, self.out_proto,
-                         ['data', self.data])
-                self.put(self.start_sample, self.samplenum, self.out_ann,
-                         [ANN_HEX, ['%s: 0x%08x' % ('L' if self.oldws else 'R',
-                         self.data)]])
-
-                # Check that the data word was the correct length.
-                if self.wordlength != -1 and self.wordlength != self.bitcount:
-                    self.put(self.start_sample, self.samplenum, self.out_ann,
-                        [ANN_HEX, ['WARNING: Received a %d-bit word, when a '
-                        '%d-bit word was expected' % (self.bitcount,
-                        self.wordlength)]])
-
-                self.wordlength = self.bitcount
-
-            # Reset decoder state.
-            self.data = 0
-            self.bitcount = 0
-            self.start_sample = self.samplenum
-
-            # Save the first sample position.
-            if self.first_sample == None:
-                self.first_sample = self.samplenum
-
-            self.oldws = ws
-
diff --git a/decoders/i2s/pd.py b/decoders/i2s/pd.py
new file mode 100644 (file)
index 0000000..b921011
--- /dev/null
@@ -0,0 +1,124 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# I2S protocol decoder
+
+import sigrokdecode as srd
+
+# Annotation formats
+ANN_HEX = 0
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'i2s'
+    name = 'I2S'
+    longname = 'Integrated Interchip Sound'
+    desc = 'Serial bus for connecting digital audio devices.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['i2s']
+    probes = [
+        {'id': 'sck', 'name': 'SCK', 'desc': 'Bit clock line'},
+        {'id': 'ws', 'name': 'WS', 'desc': 'Word select line'},
+        {'id': 'sd', 'name': 'SD', 'desc': 'Serial data line'},
+    ]
+    optional_probes = []
+    options = {}
+    annotations = [
+        ['Hex', 'Annotations in hex format'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.oldsck = 1
+        self.oldws = 1
+        self.bitcount = 0
+        self.data = 0
+        self.samplesreceived = 0
+        self.first_sample = None
+        self.start_sample = None
+        self.samplenum = -1
+        self.wordlength = -1
+
+    def start(self, metadata):
+        self.samplerate = metadata['samplerate']
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'i2s')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'i2s')
+
+    def report(self):
+
+        # Calculate the sample rate.
+        samplerate = '?'
+        if self.start_sample != None and \
+            self.first_sample != None and \
+            self.start_sample > self.first_sample:
+            samplerate = '%d' % (self.samplesreceived *
+                self.samplerate / (self.start_sample -
+                self.first_sample))
+
+        return 'I2S: %d %d-bit samples received at %sHz' % \
+            (self.samplesreceived, self.wordlength, samplerate)
+
+    def decode(self, ss, es, data):
+        for samplenum, (sck, ws, sd) in data:
+
+            # Ignore sample if the bit clock hasn't changed.
+            if sck == self.oldsck:
+                continue
+
+            self.oldsck = sck
+            if sck == 0:   # Ignore the falling clock edge.
+                continue
+
+            self.data = (self.data << 1) | sd
+            self.bitcount += 1
+
+            # This was not the LSB unless WS has flipped.
+            if ws == self.oldws:
+                continue
+
+            # Only submit the sample, if we received the beginning of it.
+            if self.start_sample != None:
+                self.samplesreceived += 1
+                self.put(self.start_sample, self.samplenum, self.out_proto,
+                         ['data', self.data])
+                self.put(self.start_sample, self.samplenum, self.out_ann,
+                         [ANN_HEX, ['%s: 0x%08x' % ('L' if self.oldws else 'R',
+                         self.data)]])
+
+                # Check that the data word was the correct length.
+                if self.wordlength != -1 and self.wordlength != self.bitcount:
+                    self.put(self.start_sample, self.samplenum, self.out_ann,
+                        [ANN_HEX, ['WARNING: Received a %d-bit word, when a '
+                        '%d-bit word was expected' % (self.bitcount,
+                        self.wordlength)]])
+
+                self.wordlength = self.bitcount
+
+            # Reset decoder state.
+            self.data = 0
+            self.bitcount = 0
+            self.start_sample = self.samplenum
+
+            # Save the first sample position.
+            if self.first_sample == None:
+                self.first_sample = self.samplenum
+
+            self.oldws = ws
+
index ce6aedeff2ebfc1c788fcff8b298028c972314be..6d968420950a1ec9b168083817a176943de60a7b 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/jtag
 
 
 pkgdatadir = $(DECODERS_DIR)/jtag
 
-dist_pkgdata_DATA = __init__.py jtag.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 4a53502a48b794a0d9bf4f39f1eaec7c418a085e..4538e0e0f476b953932a467866e6523a4658de64 100644 (file)
@@ -52,5 +52,5 @@ https://en.wikipedia.org/wiki/Joint_Test_Action_Group
 http://focus.ti.com/lit/an/ssya002c/ssya002c.pdf
 '''
 
 http://focus.ti.com/lit/an/ssya002c/ssya002c.pdf
 '''
 
-from .jtag import *
+from .pd import *
 
 
diff --git a/decoders/jtag/jtag.py b/decoders/jtag/jtag.py
deleted file mode 100644 (file)
index 4e27ce8..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# JTAG protocol decoder
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'jtag'
-    name = 'JTAG'
-    longname = 'Joint Test Action Group (IEEE 1149.1)'
-    desc = 'Protocol for testing, debugging, and flashing ICs.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['jtag']
-    probes = [
-        {'id': 'tdi',  'name': 'TDI',  'desc': 'Test data input'},
-        {'id': 'tdo',  'name': 'TDO',  'desc': 'Test data output'},
-        {'id': 'tck',  'name': 'TCK',  'desc': 'Test clock'},
-        {'id': 'tms',  'name': 'TMS',  'desc': 'Test mode select'},
-    ]
-    optional_probes = [
-        {'id': 'trst', 'name': 'TRST#', 'desc': 'Test reset'},
-        {'id': 'srst', 'name': 'SRST#', 'desc': 'System reset'},
-        {'id': 'rtck', 'name': 'RTCK',  'desc': 'Return clock signal'},
-    ]
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        # self.state = 'TEST-LOGIC-RESET'
-        self.state = 'RUN-TEST/IDLE'
-        self.oldstate = None
-        self.oldpins = (-1, -1, -1, -1)
-        self.oldtck = -1
-        self.bits_tdi = []
-        self.bits_tdo = []
-
-    def start(self, metadata):
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'jtag')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'jtag')
-
-    def report(self):
-        pass
-
-    def advance_state_machine(self, tms):
-        self.oldstate = self.state
-
-        # Intro "tree"
-        if self.state == 'TEST-LOGIC-RESET':
-            self.state = 'TEST-LOGIC-RESET' if (tms) else 'RUN-TEST/IDLE'
-        elif self.state == 'RUN-TEST/IDLE':
-            self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
-
-        # DR "tree"
-        elif self.state == 'SELECT-DR-SCAN':
-            self.state = 'SELECT-IR-SCAN' if (tms) else 'CAPTURE-DR'
-        elif self.state == 'CAPTURE-DR':
-            self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
-        elif self.state == 'SHIFT-DR':
-            self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
-        elif self.state == 'EXIT1-DR':
-            self.state = 'UPDATE-DR' if (tms) else 'PAUSE-DR'
-        elif self.state == 'PAUSE-DR':
-            self.state = 'EXIT2-DR' if (tms) else 'PAUSE-DR'
-        elif self.state == 'EXIT2-DR':
-            self.state = 'UPDATE-DR' if (tms) else 'SHIFT-DR'
-        elif self.state == 'UPDATE-DR':
-            self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
-
-        # IR "tree"
-        elif self.state == 'SELECT-IR-SCAN':
-            self.state = 'TEST-LOGIC-RESET' if (tms) else 'CAPTURE-IR'
-        elif self.state == 'CAPTURE-IR':
-            self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
-        elif self.state == 'SHIFT-IR':
-            self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
-        elif self.state == 'EXIT1-IR':
-            self.state = 'UPDATE-IR' if (tms) else 'PAUSE-IR'
-        elif self.state == 'PAUSE-IR':
-            self.state = 'EXIT2-IR' if (tms) else 'PAUSE-IR'
-        elif self.state == 'EXIT2-IR':
-            self.state = 'UPDATE-IR' if (tms) else 'SHIFT-IR'
-        elif self.state == 'UPDATE-IR':
-            self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
-
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
-    def handle_rising_tck_edge(self, tdi, tdo, tck, tms):
-        # Rising TCK edges always advance the state machine.
-        self.advance_state_machine(tms)
-
-        # Output the state we just switched to.
-        self.put(self.ss, self.es, self.out_ann,
-                 [0, ['New state: %s' % self.state]])
-        self.put(self.ss, self.es, self.out_proto,
-                 ['NEW STATE', self.state])
-
-        # If we went from SHIFT-IR to SHIFT-IR, or SHIFT-DR to SHIFT-DR,
-        # collect the current TDI/TDO values (upon rising TCK edge).
-        if self.state.startswith('SHIFT-') and self.oldstate == self.state:
-            self.bits_tdi.insert(0, tdi)
-            self.bits_tdo.insert(0, tdo)
-            # TODO: ANN/PROTO output.
-            # self.put(self.ss, self.es, self.out_ann,
-            #          [0, ['TDI add: ' + str(tdi)]])
-            # self.put(self.ss, self.es, self.out_ann,
-            #          [0, ['TDO add: ' + str(tdo)]])
-
-        # Output all TDI/TDO bits if we just switched from SHIFT-* to EXIT1-*.
-        if self.oldstate.startswith('SHIFT-') and \
-           self.state.startswith('EXIT1-'):
-
-            t = self.state[-2:] + ' TDI'
-            b = ''.join(map(str, self.bits_tdi))
-            h = ' (0x%x' % int('0b' + b, 2) + ')'
-            s = t + ': ' + b + h + ', ' + str(len(self.bits_tdi)) + ' bits'
-            self.put(self.ss, self.es, self.out_ann, [0, [s]])
-            self.put(self.ss, self.es, self.out_proto, [t, b])
-            self.bits_tdi = []
-
-            t = self.state[-2:] + ' TDO'
-            b = ''.join(map(str, self.bits_tdo))
-            h = ' (0x%x' % int('0b' + b, 2) + ')'
-            s = t + ': ' + b + h + ', ' + str(len(self.bits_tdo)) + ' bits'
-            self.put(self.ss, self.es, self.out_ann, [0, [s]])
-            self.put(self.ss, self.es, self.out_proto, [t, b])
-            self.bits_tdo = []
-
-    def decode(self, ss, es, data):
-        for (samplenum, pins) in data:
-
-            # If none of the pins changed, there's nothing to do.
-            if self.oldpins == pins:
-                continue
-
-            # Store current pin values for the next round.
-            self.oldpins = pins
-
-            # Get individual pin values into local variables.
-            # Unused probes will have a value of > 1.
-            (tdi, tdo, tck, tms, trst, srst, rtck) = pins
-
-            # We only care about TCK edges (either rising or falling).
-            if (self.oldtck == tck):
-                continue
-
-            # Store start/end sample for later usage.
-            self.ss, self.es = ss, es
-
-            # self.put(self.ss, self.es, self.out_ann,
-            #     [0, ['tdi:%s, tdo:%s, tck:%s, tms:%s' \
-            #          % (tdi, tdo, tck, tms)]])
-
-            if (self.oldtck == 0 and tck == 1):
-                self.handle_rising_tck_edge(tdi, tdo, tck, tms)
-
-            self.oldtck = tck
-
diff --git a/decoders/jtag/pd.py b/decoders/jtag/pd.py
new file mode 100644 (file)
index 0000000..4e27ce8
--- /dev/null
@@ -0,0 +1,180 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# JTAG protocol decoder
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'jtag'
+    name = 'JTAG'
+    longname = 'Joint Test Action Group (IEEE 1149.1)'
+    desc = 'Protocol for testing, debugging, and flashing ICs.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['jtag']
+    probes = [
+        {'id': 'tdi',  'name': 'TDI',  'desc': 'Test data input'},
+        {'id': 'tdo',  'name': 'TDO',  'desc': 'Test data output'},
+        {'id': 'tck',  'name': 'TCK',  'desc': 'Test clock'},
+        {'id': 'tms',  'name': 'TMS',  'desc': 'Test mode select'},
+    ]
+    optional_probes = [
+        {'id': 'trst', 'name': 'TRST#', 'desc': 'Test reset'},
+        {'id': 'srst', 'name': 'SRST#', 'desc': 'System reset'},
+        {'id': 'rtck', 'name': 'RTCK',  'desc': 'Return clock signal'},
+    ]
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        # self.state = 'TEST-LOGIC-RESET'
+        self.state = 'RUN-TEST/IDLE'
+        self.oldstate = None
+        self.oldpins = (-1, -1, -1, -1)
+        self.oldtck = -1
+        self.bits_tdi = []
+        self.bits_tdo = []
+
+    def start(self, metadata):
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'jtag')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'jtag')
+
+    def report(self):
+        pass
+
+    def advance_state_machine(self, tms):
+        self.oldstate = self.state
+
+        # Intro "tree"
+        if self.state == 'TEST-LOGIC-RESET':
+            self.state = 'TEST-LOGIC-RESET' if (tms) else 'RUN-TEST/IDLE'
+        elif self.state == 'RUN-TEST/IDLE':
+            self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
+
+        # DR "tree"
+        elif self.state == 'SELECT-DR-SCAN':
+            self.state = 'SELECT-IR-SCAN' if (tms) else 'CAPTURE-DR'
+        elif self.state == 'CAPTURE-DR':
+            self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
+        elif self.state == 'SHIFT-DR':
+            self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
+        elif self.state == 'EXIT1-DR':
+            self.state = 'UPDATE-DR' if (tms) else 'PAUSE-DR'
+        elif self.state == 'PAUSE-DR':
+            self.state = 'EXIT2-DR' if (tms) else 'PAUSE-DR'
+        elif self.state == 'EXIT2-DR':
+            self.state = 'UPDATE-DR' if (tms) else 'SHIFT-DR'
+        elif self.state == 'UPDATE-DR':
+            self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
+
+        # IR "tree"
+        elif self.state == 'SELECT-IR-SCAN':
+            self.state = 'TEST-LOGIC-RESET' if (tms) else 'CAPTURE-IR'
+        elif self.state == 'CAPTURE-IR':
+            self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
+        elif self.state == 'SHIFT-IR':
+            self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
+        elif self.state == 'EXIT1-IR':
+            self.state = 'UPDATE-IR' if (tms) else 'PAUSE-IR'
+        elif self.state == 'PAUSE-IR':
+            self.state = 'EXIT2-IR' if (tms) else 'PAUSE-IR'
+        elif self.state == 'EXIT2-IR':
+            self.state = 'UPDATE-IR' if (tms) else 'SHIFT-IR'
+        elif self.state == 'UPDATE-IR':
+            self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
+
+        else:
+            raise Exception('Invalid state: %s' % self.state)
+
+    def handle_rising_tck_edge(self, tdi, tdo, tck, tms):
+        # Rising TCK edges always advance the state machine.
+        self.advance_state_machine(tms)
+
+        # Output the state we just switched to.
+        self.put(self.ss, self.es, self.out_ann,
+                 [0, ['New state: %s' % self.state]])
+        self.put(self.ss, self.es, self.out_proto,
+                 ['NEW STATE', self.state])
+
+        # If we went from SHIFT-IR to SHIFT-IR, or SHIFT-DR to SHIFT-DR,
+        # collect the current TDI/TDO values (upon rising TCK edge).
+        if self.state.startswith('SHIFT-') and self.oldstate == self.state:
+            self.bits_tdi.insert(0, tdi)
+            self.bits_tdo.insert(0, tdo)
+            # TODO: ANN/PROTO output.
+            # self.put(self.ss, self.es, self.out_ann,
+            #          [0, ['TDI add: ' + str(tdi)]])
+            # self.put(self.ss, self.es, self.out_ann,
+            #          [0, ['TDO add: ' + str(tdo)]])
+
+        # Output all TDI/TDO bits if we just switched from SHIFT-* to EXIT1-*.
+        if self.oldstate.startswith('SHIFT-') and \
+           self.state.startswith('EXIT1-'):
+
+            t = self.state[-2:] + ' TDI'
+            b = ''.join(map(str, self.bits_tdi))
+            h = ' (0x%x' % int('0b' + b, 2) + ')'
+            s = t + ': ' + b + h + ', ' + str(len(self.bits_tdi)) + ' bits'
+            self.put(self.ss, self.es, self.out_ann, [0, [s]])
+            self.put(self.ss, self.es, self.out_proto, [t, b])
+            self.bits_tdi = []
+
+            t = self.state[-2:] + ' TDO'
+            b = ''.join(map(str, self.bits_tdo))
+            h = ' (0x%x' % int('0b' + b, 2) + ')'
+            s = t + ': ' + b + h + ', ' + str(len(self.bits_tdo)) + ' bits'
+            self.put(self.ss, self.es, self.out_ann, [0, [s]])
+            self.put(self.ss, self.es, self.out_proto, [t, b])
+            self.bits_tdo = []
+
+    def decode(self, ss, es, data):
+        for (samplenum, pins) in data:
+
+            # If none of the pins changed, there's nothing to do.
+            if self.oldpins == pins:
+                continue
+
+            # Store current pin values for the next round.
+            self.oldpins = pins
+
+            # Get individual pin values into local variables.
+            # Unused probes will have a value of > 1.
+            (tdi, tdo, tck, tms, trst, srst, rtck) = pins
+
+            # We only care about TCK edges (either rising or falling).
+            if (self.oldtck == tck):
+                continue
+
+            # Store start/end sample for later usage.
+            self.ss, self.es = ss, es
+
+            # self.put(self.ss, self.es, self.out_ann,
+            #     [0, ['tdi:%s, tdo:%s, tck:%s, tms:%s' \
+            #          % (tdi, tdo, tck, tms)]])
+
+            if (self.oldtck == 0 and tck == 1):
+                self.handle_rising_tck_edge(tdi, tdo, tck, tms)
+
+            self.oldtck = tck
+
index 08d74d61d3ee9035bdbf9307cc46919edc1b8341..7cd39dadac54128f3693f7781481add5f9bef340 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/jtag_stm32
 
 
 pkgdatadir = $(DECODERS_DIR)/jtag_stm32
 
-dist_pkgdata_DATA = __init__.py jtag_stm32.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index bc35c46c8522aac74e907bb359a95460d432b74a..12e667bfc8e5493eae803b8153d6a355330ba1c9 100644 (file)
@@ -28,5 +28,5 @@ https://en.wikipedia.org/wiki/STM32
 http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/CD00171190.pdf (e.g. chapter 31.7: "JTAG debug port")
 '''
 
 http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/CD00171190.pdf (e.g. chapter 31.7: "JTAG debug port")
 '''
 
-from .jtag_stm32 import *
+from .pd import *
 
 
diff --git a/decoders/jtag_stm32/jtag_stm32.py b/decoders/jtag_stm32/jtag_stm32.py
deleted file mode 100644 (file)
index e30788c..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# ST STM32 JTAG protocol decoder
-
-import sigrokdecode as srd
-
-# JTAG debug port data registers (in IR[3:0]) and their sizes (in bits)
-# Note: The ARM DAP-DP is not IEEE 1149.1 (JTAG) compliant (as per ARM docs),
-#       as it does not implement the EXTEST, SAMPLE, and PRELOAD instructions.
-#       Instead, BYPASS is decoded for any of these instructions.
-ir = {
-    '1111': ['BYPASS', 1],  # Bypass register
-    '1110': ['IDCODE', 32], # ID code register
-    '1010': ['DPACC', 35],  # Debug port access register
-    '1011': ['APACC', 35],  # Access port access register
-    '1000': ['ABORT', 35],  # Abort register # TODO: 32 bits? Datasheet typo?
-}
-
-# ARM Cortex-M3 r1p1-01rel0 ID code
-cm3_idcode = 0x3ba00477
-
-# JTAG ID code in the STM32F10xxx BSC (boundary scan) TAP
-jtag_idcode = {
-    0x06412041: 'Low-density device, rev. A',
-    0x06410041: 'Medium-density device, rev. A',
-    0x16410041: 'Medium-density device, rev. B/Z/Y',
-    0x06414041: 'High-density device, rev. A/Z/Y',
-    0x06430041: 'XL-density device, rev. A',
-    0x06418041: 'Connectivity-line device, rev. A/Z',
-}
-
-# ACK[2:0] in the DPACC/APACC registers (unlisted values are reserved)
-ack_val = {
-    '001': 'WAIT',
-    '010': 'OK/FAULT',
-}
-
-# 32bit debug port registers (addressed via A[3:2])
-dp_reg = {
-    '00': 'Reserved', # Must be kept at reset value
-    '01': 'DP CTRL/STAT',
-    '10': 'DP SELECT',
-    '11': 'DP RDBUFF',
-}
-
-# APB-AP registers (each of them 32 bits wide)
-apb_ap_reg = {
-    0x00: ['CSW', 'Control/status word'],
-    0x04: ['TAR', 'Transfer address'],
-    # 0x08: Reserved SBZ
-    0x0c: ['DRW', 'Data read/write'],
-    0x10: ['BD0', 'Banked data 0'],
-    0x14: ['BD1', 'Banked data 1'],
-    0x18: ['BD2', 'Banked data 2'],
-    0x1c: ['BD3', 'Banked data 3'],
-    # 0x20-0xf4: Reserved SBZ
-    0x800000000: ['ROM', 'Debug ROM address'],
-    0xfc: ['IDR', 'Identification register'],
-}
-
-# TODO: All start/end sample values in self.put() calls are bogus.
-# TODO: Split off generic ARM/Cortex-M3 parts into another protocol decoder?
-
-# Bits[31:28]: Version (here: 0x3)
-#              JTAG-DP: 0x3, SW-DP: 0x2
-# Bits[27:12]: Part number (here: 0xba00)
-#              JTAG-DP: 0xba00, SW-DP: 0xba10
-# Bits[11:1]:  JEDEC (JEP-106) manufacturer ID (here: 0x23b)
-#              Bits[11:8]: Continuation code ('ARM Limited': 0x04)
-#              Bits[7:1]: Identity code ('ARM Limited': 0x3b)
-# Bits[0:0]:   Reserved (here: 0x1)
-def decode_device_id_code(bits):
-    id_hex = '0x%x' % int('0b' + bits, 2)
-    ver =    '0x%x' % int('0b' + bits[-32:-28], 2)
-    part =   '0x%x' % int('0b' + bits[-28:-12], 2)
-    manuf =  '0x%x' % int('0b' + bits[-12:-1], 2)
-    res =    '0x%x' % int('0b' + bits[-1], 2)
-    return (id_hex, ver, part, manuf, res)
-
-# DPACC is used to access debug port registers (CTRL/STAT, SELECT, RDBUFF).
-# APACC is used to access all Access Port (AHB-AP) registers.
-
-# APACC/DPACC, when transferring data IN:
-# Bits[34:3] = DATA[31:0]: 32bit data to transfer (write request)
-# Bits[2:1] = A[3:2]: 2-bit address (debug/access port register)
-# Bits[0:0] = RnW: Read request (1) or write request (0)
-def data_in(instruction, bits):
-    data, a, rnw = bits[:-3], bits[-3:-1], bits[-1]
-    data_hex = '0x%x' % int('0b' + data, 2)
-    r = 'Read request' if (rnw == '1') else 'Write request'
-    # reg = dp_reg[a] if (instruction == 'DPACC') else apb_ap_reg[a]
-    reg = dp_reg[a] if (instruction == 'DPACC') else a # TODO
-    return 'New transaction: DATA: %s, A: %s, RnW: %s' % (data_hex, reg, r)
-
-# APACC/DPACC, when transferring data OUT:
-# Bits[34:3] = DATA[31:0]: 32bit data which is read (read request)
-# Bits[2:0] = ACK[2:0]: 3-bit acknowledge
-def data_out(bits):
-    data, ack = bits[:-3], bits[-3:]
-    data_hex = '0x%x' % int('0b' + data, 2)
-    ack_meaning = ack_val.get(ack, 'Reserved')
-    return 'Previous transaction result: DATA: %s, ACK: %s' \
-           % (data_hex, ack_meaning)
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'jtag_stm32'
-    name = 'JTAG / STM32'
-    longname = 'Joint Test Action Group / ST STM32'
-    desc = 'ST STM32-specific JTAG protocol.'
-    license = 'gplv2+'
-    inputs = ['jtag']
-    outputs = ['jtag_stm32']
-    probes = []
-    optional_probes = []
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'IDLE'
-        # self.state = 'BYPASS'
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'jtag_stm32')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'jtag_stm32')
-
-    def report(self):
-        pass
-
-    def handle_reg_bypass(self, cmd, bits):
-        # TODO
-        self.put(self.ss, self.es, self.out_ann, [0, ['BYPASS: ' + bits]])
-
-    def handle_reg_idcode(self, cmd, bits):
-        # TODO
-        # IDCODE is a read-only register which is always accessible.
-        # IR == IDCODE: The device ID code is shifted out via DR next.
-        self.put(self.ss, self.es, self.out_ann,
-                 [0, ['IDCODE: %s (ver=%s, part=%s, manuf=%s, res=%s)' % \
-                 decode_device_id_code(bits)]])
-
-    def handle_reg_dpacc(self, cmd, bits):
-        # self.put(self.ss, self.es, self.out_ann,
-        #          [0, ['DPACC/%s: %s' % (cmd, bits)]])
-        s = data_in('DPACC', bits) if (cmd == 'DR TDI') else data_out(bits)
-        self.put(self.ss, self.es, self.out_ann, [0, [s]])
-
-    def handle_reg_apacc(self, cmd, bits):
-        # self.put(self.ss, self.es, self.out_ann,
-        #          [0, ['APACC/%s: %s' % (cmd, bits)]])
-        s = data_in('APACC', bits) if (cmd == 'DR TDI') else data_out(bits)
-        self.put(self.ss, self.es, self.out_ann, [0, [s]])
-
-    def handle_reg_abort(self, cmd, bits):
-        # Bits[31:1]: reserved. Bit[0]: DAPABORT.
-        a = '' if (bits[0] == '1') else 'No '
-        s = 'DAPABORT = %s: %sDAP abort generated' % (bits[0], a)
-        self.put(self.ss, self.es, self.out_ann, [0, [s]])
-
-        # Warn if DAPABORT[31:1] contains non-zero bits.
-        if (bits[:-1] != ('0' * 31)):
-            self.put(self.ss, self.es, self.out_ann,
-                     [0, ['WARNING: DAPABORT[31:1] reserved!']])
-
-    def handle_reg_unknown(self, cmd, bits):
-        self.put(self.ss, self.es, self.out_ann,
-                 [0, ['Unknown instruction: ' % bits]]) # TODO
-
-    def decode(self, ss, es, data):
-        # Assumption: The right-most char in the 'val' bitstring is the LSB.
-        cmd, val = data
-
-        self.ss, self.es = ss, es
-
-        # self.put(self.ss, self.es, self.out_ann, [0, [cmd + ' / ' + val]])
-
-        # State machine
-        if self.state == 'IDLE':
-            # Wait until a new instruction is shifted into the IR register.
-            if cmd != 'IR TDI':
-                return
-            # Switch to the state named after the instruction, or 'UNKNOWN'.
-            # Ignore bits other than IR[3:0]. While the IR register is only
-            # 4 bits in size, some programs (e.g. OpenOCD) might fill in a
-            # few more (dummy) bits. OpenOCD makes IR at least 8 bits long.
-            self.state = ir.get(val[-4:], ['UNKNOWN', 0])[0]
-            self.put(self.ss, self.es, self.out_ann, [0, ['IR: ' + self.state]])
-        elif self.state == 'BYPASS':
-            # Here we're interested in incoming bits (TDI).
-            if cmd != 'DR TDI':
-                return
-            handle_reg = getattr(self, 'handle_reg_%s' % self.state.lower())
-            handle_reg(cmd, val)
-            self.state = 'IDLE'
-        elif self.state in ('IDCODE', 'ABORT', 'UNKNOWN'):
-            # Here we're interested in outgoing bits (TDO).
-            if cmd != 'DR TDO':
-                return
-            handle_reg = getattr(self, 'handle_reg_%s' % self.state.lower())
-            handle_reg(cmd, val)
-            self.state = 'IDLE'
-        elif self.state in ('DPACC', 'APACC'):
-            # Here we're interested in incoming and outgoing bits (TDI/TDO).
-            if cmd not in ('DR TDI', 'DR TDO'):
-                return
-            handle_reg = getattr(self, 'handle_reg_%s' % self.state.lower())
-            handle_reg(cmd, val)
-            if cmd == 'DR TDO': # TODO: Assumes 'DR TDI' comes before 'DR TDO'
-                self.state = 'IDLE'
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/jtag_stm32/pd.py b/decoders/jtag_stm32/pd.py
new file mode 100644 (file)
index 0000000..e30788c
--- /dev/null
@@ -0,0 +1,232 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# ST STM32 JTAG protocol decoder
+
+import sigrokdecode as srd
+
+# JTAG debug port data registers (in IR[3:0]) and their sizes (in bits)
+# Note: The ARM DAP-DP is not IEEE 1149.1 (JTAG) compliant (as per ARM docs),
+#       as it does not implement the EXTEST, SAMPLE, and PRELOAD instructions.
+#       Instead, BYPASS is decoded for any of these instructions.
+ir = {
+    '1111': ['BYPASS', 1],  # Bypass register
+    '1110': ['IDCODE', 32], # ID code register
+    '1010': ['DPACC', 35],  # Debug port access register
+    '1011': ['APACC', 35],  # Access port access register
+    '1000': ['ABORT', 35],  # Abort register # TODO: 32 bits? Datasheet typo?
+}
+
+# ARM Cortex-M3 r1p1-01rel0 ID code
+cm3_idcode = 0x3ba00477
+
+# JTAG ID code in the STM32F10xxx BSC (boundary scan) TAP
+jtag_idcode = {
+    0x06412041: 'Low-density device, rev. A',
+    0x06410041: 'Medium-density device, rev. A',
+    0x16410041: 'Medium-density device, rev. B/Z/Y',
+    0x06414041: 'High-density device, rev. A/Z/Y',
+    0x06430041: 'XL-density device, rev. A',
+    0x06418041: 'Connectivity-line device, rev. A/Z',
+}
+
+# ACK[2:0] in the DPACC/APACC registers (unlisted values are reserved)
+ack_val = {
+    '001': 'WAIT',
+    '010': 'OK/FAULT',
+}
+
+# 32bit debug port registers (addressed via A[3:2])
+dp_reg = {
+    '00': 'Reserved', # Must be kept at reset value
+    '01': 'DP CTRL/STAT',
+    '10': 'DP SELECT',
+    '11': 'DP RDBUFF',
+}
+
+# APB-AP registers (each of them 32 bits wide)
+apb_ap_reg = {
+    0x00: ['CSW', 'Control/status word'],
+    0x04: ['TAR', 'Transfer address'],
+    # 0x08: Reserved SBZ
+    0x0c: ['DRW', 'Data read/write'],
+    0x10: ['BD0', 'Banked data 0'],
+    0x14: ['BD1', 'Banked data 1'],
+    0x18: ['BD2', 'Banked data 2'],
+    0x1c: ['BD3', 'Banked data 3'],
+    # 0x20-0xf4: Reserved SBZ
+    0x800000000: ['ROM', 'Debug ROM address'],
+    0xfc: ['IDR', 'Identification register'],
+}
+
+# TODO: All start/end sample values in self.put() calls are bogus.
+# TODO: Split off generic ARM/Cortex-M3 parts into another protocol decoder?
+
+# Bits[31:28]: Version (here: 0x3)
+#              JTAG-DP: 0x3, SW-DP: 0x2
+# Bits[27:12]: Part number (here: 0xba00)
+#              JTAG-DP: 0xba00, SW-DP: 0xba10
+# Bits[11:1]:  JEDEC (JEP-106) manufacturer ID (here: 0x23b)
+#              Bits[11:8]: Continuation code ('ARM Limited': 0x04)
+#              Bits[7:1]: Identity code ('ARM Limited': 0x3b)
+# Bits[0:0]:   Reserved (here: 0x1)
+def decode_device_id_code(bits):
+    id_hex = '0x%x' % int('0b' + bits, 2)
+    ver =    '0x%x' % int('0b' + bits[-32:-28], 2)
+    part =   '0x%x' % int('0b' + bits[-28:-12], 2)
+    manuf =  '0x%x' % int('0b' + bits[-12:-1], 2)
+    res =    '0x%x' % int('0b' + bits[-1], 2)
+    return (id_hex, ver, part, manuf, res)
+
+# DPACC is used to access debug port registers (CTRL/STAT, SELECT, RDBUFF).
+# APACC is used to access all Access Port (AHB-AP) registers.
+
+# APACC/DPACC, when transferring data IN:
+# Bits[34:3] = DATA[31:0]: 32bit data to transfer (write request)
+# Bits[2:1] = A[3:2]: 2-bit address (debug/access port register)
+# Bits[0:0] = RnW: Read request (1) or write request (0)
+def data_in(instruction, bits):
+    data, a, rnw = bits[:-3], bits[-3:-1], bits[-1]
+    data_hex = '0x%x' % int('0b' + data, 2)
+    r = 'Read request' if (rnw == '1') else 'Write request'
+    # reg = dp_reg[a] if (instruction == 'DPACC') else apb_ap_reg[a]
+    reg = dp_reg[a] if (instruction == 'DPACC') else a # TODO
+    return 'New transaction: DATA: %s, A: %s, RnW: %s' % (data_hex, reg, r)
+
+# APACC/DPACC, when transferring data OUT:
+# Bits[34:3] = DATA[31:0]: 32bit data which is read (read request)
+# Bits[2:0] = ACK[2:0]: 3-bit acknowledge
+def data_out(bits):
+    data, ack = bits[:-3], bits[-3:]
+    data_hex = '0x%x' % int('0b' + data, 2)
+    ack_meaning = ack_val.get(ack, 'Reserved')
+    return 'Previous transaction result: DATA: %s, ACK: %s' \
+           % (data_hex, ack_meaning)
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'jtag_stm32'
+    name = 'JTAG / STM32'
+    longname = 'Joint Test Action Group / ST STM32'
+    desc = 'ST STM32-specific JTAG protocol.'
+    license = 'gplv2+'
+    inputs = ['jtag']
+    outputs = ['jtag_stm32']
+    probes = []
+    optional_probes = []
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'IDLE'
+        # self.state = 'BYPASS'
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'jtag_stm32')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'jtag_stm32')
+
+    def report(self):
+        pass
+
+    def handle_reg_bypass(self, cmd, bits):
+        # TODO
+        self.put(self.ss, self.es, self.out_ann, [0, ['BYPASS: ' + bits]])
+
+    def handle_reg_idcode(self, cmd, bits):
+        # TODO
+        # IDCODE is a read-only register which is always accessible.
+        # IR == IDCODE: The device ID code is shifted out via DR next.
+        self.put(self.ss, self.es, self.out_ann,
+                 [0, ['IDCODE: %s (ver=%s, part=%s, manuf=%s, res=%s)' % \
+                 decode_device_id_code(bits)]])
+
+    def handle_reg_dpacc(self, cmd, bits):
+        # self.put(self.ss, self.es, self.out_ann,
+        #          [0, ['DPACC/%s: %s' % (cmd, bits)]])
+        s = data_in('DPACC', bits) if (cmd == 'DR TDI') else data_out(bits)
+        self.put(self.ss, self.es, self.out_ann, [0, [s]])
+
+    def handle_reg_apacc(self, cmd, bits):
+        # self.put(self.ss, self.es, self.out_ann,
+        #          [0, ['APACC/%s: %s' % (cmd, bits)]])
+        s = data_in('APACC', bits) if (cmd == 'DR TDI') else data_out(bits)
+        self.put(self.ss, self.es, self.out_ann, [0, [s]])
+
+    def handle_reg_abort(self, cmd, bits):
+        # Bits[31:1]: reserved. Bit[0]: DAPABORT.
+        a = '' if (bits[0] == '1') else 'No '
+        s = 'DAPABORT = %s: %sDAP abort generated' % (bits[0], a)
+        self.put(self.ss, self.es, self.out_ann, [0, [s]])
+
+        # Warn if DAPABORT[31:1] contains non-zero bits.
+        if (bits[:-1] != ('0' * 31)):
+            self.put(self.ss, self.es, self.out_ann,
+                     [0, ['WARNING: DAPABORT[31:1] reserved!']])
+
+    def handle_reg_unknown(self, cmd, bits):
+        self.put(self.ss, self.es, self.out_ann,
+                 [0, ['Unknown instruction: ' % bits]]) # TODO
+
+    def decode(self, ss, es, data):
+        # Assumption: The right-most char in the 'val' bitstring is the LSB.
+        cmd, val = data
+
+        self.ss, self.es = ss, es
+
+        # self.put(self.ss, self.es, self.out_ann, [0, [cmd + ' / ' + val]])
+
+        # State machine
+        if self.state == 'IDLE':
+            # Wait until a new instruction is shifted into the IR register.
+            if cmd != 'IR TDI':
+                return
+            # Switch to the state named after the instruction, or 'UNKNOWN'.
+            # Ignore bits other than IR[3:0]. While the IR register is only
+            # 4 bits in size, some programs (e.g. OpenOCD) might fill in a
+            # few more (dummy) bits. OpenOCD makes IR at least 8 bits long.
+            self.state = ir.get(val[-4:], ['UNKNOWN', 0])[0]
+            self.put(self.ss, self.es, self.out_ann, [0, ['IR: ' + self.state]])
+        elif self.state == 'BYPASS':
+            # Here we're interested in incoming bits (TDI).
+            if cmd != 'DR TDI':
+                return
+            handle_reg = getattr(self, 'handle_reg_%s' % self.state.lower())
+            handle_reg(cmd, val)
+            self.state = 'IDLE'
+        elif self.state in ('IDCODE', 'ABORT', 'UNKNOWN'):
+            # Here we're interested in outgoing bits (TDO).
+            if cmd != 'DR TDO':
+                return
+            handle_reg = getattr(self, 'handle_reg_%s' % self.state.lower())
+            handle_reg(cmd, val)
+            self.state = 'IDLE'
+        elif self.state in ('DPACC', 'APACC'):
+            # Here we're interested in incoming and outgoing bits (TDI/TDO).
+            if cmd not in ('DR TDI', 'DR TDO'):
+                return
+            handle_reg = getattr(self, 'handle_reg_%s' % self.state.lower())
+            handle_reg(cmd, val)
+            if cmd == 'DR TDO': # TODO: Assumes 'DR TDI' comes before 'DR TDO'
+                self.state = 'IDLE'
+        else:
+            raise Exception('Invalid state: %s' % self.state)
+
index 6ab18181632e619ca2f6e7b1285c039526b6157a..82a219066674bc32873229a7abfbb455cc2e2ee2 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/lm75
 
 
 pkgdatadir = $(DECODERS_DIR)/lm75
 
-dist_pkgdata_DATA = __init__.py lm75.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index fd72612bacbfe569342033660090591220f5e167..548cf6521bf01087dab5a5f1b1cbc3e823aa42a9 100644 (file)
@@ -30,5 +30,5 @@ Details:
 TODO.
 '''
 
 TODO.
 '''
 
-from .lm75 import *
+from .pd import *
 
 
diff --git a/decoders/lm75/lm75.py b/decoders/lm75/lm75.py
deleted file mode 100644 (file)
index 9d2f0ab..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# National LM75 (and compatibles) temperature sensor protocol decoder
-
-# TODO: Better support for various LM75 compatible devices.
-
-import sigrokdecode as srd
-
-# LM75 only supports 9 bit resolution, compatible devices usually 9-12 bits.
-resolution = {
-    # CONFIG[6:5]: <resolution>
-    0x00: 9,
-    0x01: 10,
-    0x02: 11,
-    0x03: 12,
-}
-
-ft = {
-    # CONFIG[4:3]: <fault tolerance setting>
-    0x00: 1,
-    0x01: 2,
-    0x02: 4,
-    0x03: 6,
-}
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'lm75'
-    name = 'LM75'
-    longname = 'National LM75'
-    desc = 'National LM75 (and compatibles) temperature sensor protocol.'
-    license = 'gplv2+'
-    inputs = ['i2c']
-    outputs = ['lm75']
-    probes = []
-    optional_probes = [
-        {'id': 'os', 'name': 'OS', 'desc': 'Overtemperature shutdown'},
-        {'id': 'a0', 'name': 'A0', 'desc': 'I2C slave address input 0'},
-        {'id': 'a1', 'name': 'A1', 'desc': 'I2C slave address input 1'},
-        {'id': 'a2', 'name': 'A2', 'desc': 'I2C slave address input 2'},
-    ]
-    options = {
-        'sensor': ['Sensor type', 'lm75'],
-        'resolution': ['Resolution', 9], # 9-12 bit, sensor/config dependent
-    }
-    annotations = [
-        ['Celsius', 'Temperature in degrees Celsius'],
-        ['Kelvin', 'Temperature in Kelvin'],
-        ['Text (verbose)', 'Human-readable text (verbose)'],
-        ['Text', 'Human-readable text'],
-        ['Warnings', 'Human-readable warnings'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'IDLE'
-        self.reg = 0x00 # Currently selected register
-        self.databytes = []
-        self.mintemp = 0
-        self.maxtemp = 0
-        self.avgvalues = []
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'lm75')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'lm75')
-
-    def report(self):
-        # TODO: print() or self.put() or return xyz, or... ?
-        avg = sum(self.avgvalues) / len(self.avgvalues)
-        temperatures = (self.mintemp, self.maxtemp, avg)
-        # TODO: Configurable report() output, e.g. for Kelvin.
-        return 'Min/max/avg temperature: %f/%f/%f Â°C' % temperatures
-
-    def putx(self, data):
-        # Helper for annotations which span exactly one I2C packet.
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def putb(self, data):
-        # Helper for annotations which span a block of I2C packets.
-        self.put(self.block_start, self.block_end, self.out_ann, data)
-
-    def warn_upon_invalid_slave(self, addr):
-        # LM75 and compatible devices have a 7-bit I2C slave address where
-        # the 4 MSBs are fixed to 1001, and the 3 LSBs are configurable.
-        # Thus, valid slave addresses (1001xxx) range from 0x48 to 0x4f.
-        if addr not in range(0x48, 0x4f + 1):
-            s = 'Warning: I2C slave 0x%02x not an LM75 compatible sensor.'
-            self.putx([4, [s % addr]])
-
-    def output_temperature(self, s, rw):
-        # TODO: Support for resolutions other than 9 bit.
-        before, after = self.databytes[0], (self.databytes[1] >> 7) * 5
-        celsius = float('%d.%d' % (before, after))
-        kelvin = celsius + 273.15
-        self.putb([0, ['%s: %.1f Â°C' % (s, celsius)]])
-        self.putb([1, ['%s: %.1f K' % (s, kelvin)]])
-
-        # Warn about the temperature register (0x00) being read-only.
-        if s == 'Temperature' and rw == 'WRITE':
-            s = 'Warning: The temperature register is read-only!'
-            self.putb([4, [s]])
-
-        # Keep some statistics. Can be output in report(), for example.
-        if celsius < self.mintemp:
-            self.mintemp = celsius
-        if celsius > self.maxtemp:
-            self.maxtemp = celsius
-        self.avgvalues.append(celsius)
-
-    def handle_temperature_reg(self, b, s, rw):
-        # Common helper for the temperature/T_HYST/T_OS registers.
-        if len(self.databytes) == 0:
-            self.block_start = self.ss
-            self.databytes.append(b)
-            return
-        self.databytes.append(b)
-        self.block_end = self.es
-        self.output_temperature(s, rw)
-        self.databytes = []
-
-    def handle_reg_0x00(self, b, rw):
-        # Temperature register (16bits, read-only).
-        self.handle_temperature_reg(b, 'Temperature', rw)
-
-    def handle_reg_0x01(self, b, rw):
-        # Configuration register (8 bits, read/write).
-        # TODO: Bit-exact annotation ranges.
-
-        sd = b & (1 << 0)
-        tmp = 'normal operation' if (sd == 0) else 'shutdown mode'
-        s = 'SD = %d: %s\n' % (sd, tmp)
-        s2 = 'SD = %s, ' % tmp
-
-        cmp_int = b & (1 << 1)
-        tmp = 'comparator' if (cmp_int == 0) else 'interrupt'
-        s += 'CMP/INT = %d: %s mode\n' % (cmp_int, tmp)
-        s2 += 'CMP/INT = %s, ' % tmp
-
-        pol = b & (1 << 2)
-        tmp = 'low' if (pol == 0) else 'high'
-        s += 'POL = %d: OS polarity is active-%s\n' % (pol, tmp)
-        s2 += 'POL = active-%s, ' % tmp
-
-        bits = (b & ((1 << 4) | (1 << 3))) >> 3
-        s += 'Fault tolerance setting: %d bit(s)\n' % ft[bits]
-        s2 += 'FT = %d' % ft[bits]
-
-        # Not supported by LM75, but by various compatible devices.
-        if self.options['sensor'] != 'lm75': # TODO
-            bits = (b & ((1 << 6) | (1 << 5))) >> 5
-            s += 'Resolution: %d bits\n' % resolution[bits]
-            s2 += ', resolution = %d' % resolution[bits]
-
-        self.putx([2, [s]])
-        self.putx([3, [s2]])
-
-    def handle_reg_0x02(self, b, rw):
-        # T_HYST register (16 bits, read/write).
-        self.handle_temperature_reg(b, 'T_HYST trip temperature', rw)
-
-    def handle_reg_0x03(self, b, rw):
-        # T_OS register (16 bits, read/write).
-        self.handle_temperature_reg(b, 'T_OS trip temperature', rw)
-
-    def decode(self, ss, es, data):
-        cmd, databyte = data
-
-        # Store the start/end samples of this I2C packet.
-        self.ss, self.es = ss, es
-
-        # State machine.
-        if self.state == 'IDLE':
-            # Wait for an I2C START condition.
-            if cmd != 'START':
-                return
-            self.state = 'GET SLAVE ADDR'
-        elif self.state == 'GET SLAVE ADDR':
-            # Wait for an address read/write operation.
-            if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
-                self.warn_upon_invalid_slave(databyte)
-                self.state = cmd[8:] + ' REGS' # READ REGS / WRITE REGS
-        elif self.state in ('READ REGS', 'WRITE REGS'):
-            if cmd in ('DATA READ', 'DATA WRITE'):
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte, cmd[5:]) # READ / WRITE
-            elif cmd == 'STOP':
-                # TODO: Any output?
-                self.state = 'IDLE'
-            else:
-                # self.putx([0, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
-                pass
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/lm75/pd.py b/decoders/lm75/pd.py
new file mode 100644 (file)
index 0000000..9d2f0ab
--- /dev/null
@@ -0,0 +1,211 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# National LM75 (and compatibles) temperature sensor protocol decoder
+
+# TODO: Better support for various LM75 compatible devices.
+
+import sigrokdecode as srd
+
+# LM75 only supports 9 bit resolution, compatible devices usually 9-12 bits.
+resolution = {
+    # CONFIG[6:5]: <resolution>
+    0x00: 9,
+    0x01: 10,
+    0x02: 11,
+    0x03: 12,
+}
+
+ft = {
+    # CONFIG[4:3]: <fault tolerance setting>
+    0x00: 1,
+    0x01: 2,
+    0x02: 4,
+    0x03: 6,
+}
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'lm75'
+    name = 'LM75'
+    longname = 'National LM75'
+    desc = 'National LM75 (and compatibles) temperature sensor protocol.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['lm75']
+    probes = []
+    optional_probes = [
+        {'id': 'os', 'name': 'OS', 'desc': 'Overtemperature shutdown'},
+        {'id': 'a0', 'name': 'A0', 'desc': 'I2C slave address input 0'},
+        {'id': 'a1', 'name': 'A1', 'desc': 'I2C slave address input 1'},
+        {'id': 'a2', 'name': 'A2', 'desc': 'I2C slave address input 2'},
+    ]
+    options = {
+        'sensor': ['Sensor type', 'lm75'],
+        'resolution': ['Resolution', 9], # 9-12 bit, sensor/config dependent
+    }
+    annotations = [
+        ['Celsius', 'Temperature in degrees Celsius'],
+        ['Kelvin', 'Temperature in Kelvin'],
+        ['Text (verbose)', 'Human-readable text (verbose)'],
+        ['Text', 'Human-readable text'],
+        ['Warnings', 'Human-readable warnings'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'IDLE'
+        self.reg = 0x00 # Currently selected register
+        self.databytes = []
+        self.mintemp = 0
+        self.maxtemp = 0
+        self.avgvalues = []
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'lm75')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'lm75')
+
+    def report(self):
+        # TODO: print() or self.put() or return xyz, or... ?
+        avg = sum(self.avgvalues) / len(self.avgvalues)
+        temperatures = (self.mintemp, self.maxtemp, avg)
+        # TODO: Configurable report() output, e.g. for Kelvin.
+        return 'Min/max/avg temperature: %f/%f/%f Â°C' % temperatures
+
+    def putx(self, data):
+        # Helper for annotations which span exactly one I2C packet.
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def putb(self, data):
+        # Helper for annotations which span a block of I2C packets.
+        self.put(self.block_start, self.block_end, self.out_ann, data)
+
+    def warn_upon_invalid_slave(self, addr):
+        # LM75 and compatible devices have a 7-bit I2C slave address where
+        # the 4 MSBs are fixed to 1001, and the 3 LSBs are configurable.
+        # Thus, valid slave addresses (1001xxx) range from 0x48 to 0x4f.
+        if addr not in range(0x48, 0x4f + 1):
+            s = 'Warning: I2C slave 0x%02x not an LM75 compatible sensor.'
+            self.putx([4, [s % addr]])
+
+    def output_temperature(self, s, rw):
+        # TODO: Support for resolutions other than 9 bit.
+        before, after = self.databytes[0], (self.databytes[1] >> 7) * 5
+        celsius = float('%d.%d' % (before, after))
+        kelvin = celsius + 273.15
+        self.putb([0, ['%s: %.1f Â°C' % (s, celsius)]])
+        self.putb([1, ['%s: %.1f K' % (s, kelvin)]])
+
+        # Warn about the temperature register (0x00) being read-only.
+        if s == 'Temperature' and rw == 'WRITE':
+            s = 'Warning: The temperature register is read-only!'
+            self.putb([4, [s]])
+
+        # Keep some statistics. Can be output in report(), for example.
+        if celsius < self.mintemp:
+            self.mintemp = celsius
+        if celsius > self.maxtemp:
+            self.maxtemp = celsius
+        self.avgvalues.append(celsius)
+
+    def handle_temperature_reg(self, b, s, rw):
+        # Common helper for the temperature/T_HYST/T_OS registers.
+        if len(self.databytes) == 0:
+            self.block_start = self.ss
+            self.databytes.append(b)
+            return
+        self.databytes.append(b)
+        self.block_end = self.es
+        self.output_temperature(s, rw)
+        self.databytes = []
+
+    def handle_reg_0x00(self, b, rw):
+        # Temperature register (16bits, read-only).
+        self.handle_temperature_reg(b, 'Temperature', rw)
+
+    def handle_reg_0x01(self, b, rw):
+        # Configuration register (8 bits, read/write).
+        # TODO: Bit-exact annotation ranges.
+
+        sd = b & (1 << 0)
+        tmp = 'normal operation' if (sd == 0) else 'shutdown mode'
+        s = 'SD = %d: %s\n' % (sd, tmp)
+        s2 = 'SD = %s, ' % tmp
+
+        cmp_int = b & (1 << 1)
+        tmp = 'comparator' if (cmp_int == 0) else 'interrupt'
+        s += 'CMP/INT = %d: %s mode\n' % (cmp_int, tmp)
+        s2 += 'CMP/INT = %s, ' % tmp
+
+        pol = b & (1 << 2)
+        tmp = 'low' if (pol == 0) else 'high'
+        s += 'POL = %d: OS polarity is active-%s\n' % (pol, tmp)
+        s2 += 'POL = active-%s, ' % tmp
+
+        bits = (b & ((1 << 4) | (1 << 3))) >> 3
+        s += 'Fault tolerance setting: %d bit(s)\n' % ft[bits]
+        s2 += 'FT = %d' % ft[bits]
+
+        # Not supported by LM75, but by various compatible devices.
+        if self.options['sensor'] != 'lm75': # TODO
+            bits = (b & ((1 << 6) | (1 << 5))) >> 5
+            s += 'Resolution: %d bits\n' % resolution[bits]
+            s2 += ', resolution = %d' % resolution[bits]
+
+        self.putx([2, [s]])
+        self.putx([3, [s2]])
+
+    def handle_reg_0x02(self, b, rw):
+        # T_HYST register (16 bits, read/write).
+        self.handle_temperature_reg(b, 'T_HYST trip temperature', rw)
+
+    def handle_reg_0x03(self, b, rw):
+        # T_OS register (16 bits, read/write).
+        self.handle_temperature_reg(b, 'T_OS trip temperature', rw)
+
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+
+        # Store the start/end samples of this I2C packet.
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if self.state == 'IDLE':
+            # Wait for an I2C START condition.
+            if cmd != 'START':
+                return
+            self.state = 'GET SLAVE ADDR'
+        elif self.state == 'GET SLAVE ADDR':
+            # Wait for an address read/write operation.
+            if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
+                self.warn_upon_invalid_slave(databyte)
+                self.state = cmd[8:] + ' REGS' # READ REGS / WRITE REGS
+        elif self.state in ('READ REGS', 'WRITE REGS'):
+            if cmd in ('DATA READ', 'DATA WRITE'):
+                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
+                handle_reg(databyte, cmd[5:]) # READ / WRITE
+            elif cmd == 'STOP':
+                # TODO: Any output?
+                self.state = 'IDLE'
+            else:
+                # self.putx([0, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
+                pass
+        else:
+            raise Exception('Invalid state: %s' % self.state)
+
index 28de382de4ae880496c78473773fef8328059d56..d9cf564d69c1ac07f7e5ce799a2e0a6fc20983f4 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/lpc
 
 
 pkgdatadir = $(DECODERS_DIR)/lpc
 
-dist_pkgdata_DATA = __init__.py lpc.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index ce2a1406fd0ba66527a8f6ff684c7cd87b47a37b..1c79b7334cc6a30aae82da26757ef54f57cb82a6 100644 (file)
@@ -25,5 +25,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .lpc import *
+from .pd import *
 
 
diff --git a/decoders/lpc/lpc.py b/decoders/lpc/lpc.py
deleted file mode 100644 (file)
index cd56a9b..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# LPC protocol decoder
-
-import sigrokdecode as srd
-
-# ...
-fields = {
-    # START field (indicates start or stop of a transaction)
-    'START': {
-        0b0000: 'Start of cycle for a target',
-        0b0001: 'Reserved',
-        0b0010: 'Grant for bus master 0',
-        0b0011: 'Grant for bus master 1',
-        0b0100: 'Reserved',
-        0b0101: 'Reserved',
-        0b0110: 'Reserved',
-        0b0111: 'Reserved',
-        0b1000: 'Reserved',
-        0b1001: 'Reserved',
-        0b1010: 'Reserved',
-        0b1011: 'Reserved',
-        0b1100: 'Reserved',
-        0b1101: 'Start of cycle for a Firmware Memory Read cycle',
-        0b1110: 'Start of cycle for a Firmware Memory Write cycle',
-        0b1111: 'Stop/abort (end of a cycle for a target)',
-    },
-    # Cycle type / direction field
-    # Bit 0 (LAD[0]) is unused, should always be 0.
-    # Neither host nor peripheral are allowed to drive 0b11x0.
-    'CT_DR': {
-        0b0000: 'I/O read',
-        0b0010: 'I/O write',
-        0b0100: 'Memory read',
-        0b0110: 'Memory write',
-        0b1000: 'DMA read',
-        0b1010: 'DMA write',
-        0b1100: 'Reserved / not allowed',
-        0b1110: 'Reserved / not allowed',
-    },
-    # SIZE field (determines how many bytes are to be transferred)
-    # Bits[3:2] are reserved, must be driven to 0b00.
-    # Neither host nor peripheral are allowed to drive 0b0010.
-    'SIZE': {
-        0b0000: '8 bits (1 byte)',
-        0b0001: '16 bits (2 bytes)',
-        0b0010: 'Reserved / not allowed',
-        0b0011: '32 bits (4 bytes)',
-    },
-    # CHANNEL field (bits[2:0] contain the DMA channel number)
-    'CHANNEL': {
-        0b0000: '0',
-        0b0001: '1',
-        0b0010: '2',
-        0b0011: '3',
-        0b0100: '4',
-        0b0101: '5',
-        0b0110: '6',
-        0b0111: '7',
-    },
-    # SYNC field (used to add wait states)
-    'SYNC': {
-        0b0000: 'Ready',
-        0b0001: 'Reserved',
-        0b0010: 'Reserved',
-        0b0011: 'Reserved',
-        0b0100: 'Reserved',
-        0b0101: 'Short wait',
-        0b0110: 'Long wait',
-        0b0111: 'Reserved',
-        0b1000: 'Reserved',
-        0b1001: 'Ready more (DMA only)',
-        0b1010: 'Error',
-        0b1011: 'Reserved',
-        0b1100: 'Reserved',
-        0b1101: 'Reserved',
-        0b1110: 'Reserved',
-        0b1111: 'Reserved',
-    },
-}
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'lpc'
-    name = 'LPC'
-    longname = 'Low-Pin-Count'
-    desc = 'Protocol for low-bandwidth devices on PC mainboards.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['lpc']
-    probes = [
-        {'id': 'lframe', 'name': 'LFRAME#', 'desc': 'TODO'},
-        {'id': 'lreset', 'name': 'LRESET#', 'desc': 'TODO'},
-        {'id': 'lclk',   'name': 'LCLK',    'desc': 'TODO'},
-        {'id': 'lad0',   'name': 'LAD[0]',  'desc': 'TODO'},
-        {'id': 'lad1',   'name': 'LAD[1]',  'desc': 'TODO'},
-        {'id': 'lad2',   'name': 'LAD[2]',  'desc': 'TODO'},
-        {'id': 'lad3',   'name': 'LAD[3]',  'desc': 'TODO'},
-    ]
-    optional_probes = [
-        {'id': 'ldrq',   'name': 'LDRQ#',   'desc': 'TODO'},
-        {'id': 'serirq', 'name': 'SERIRQ',  'desc': 'TODO'},
-        {'id': 'clkrun', 'name': 'CLKRUN#', 'desc': 'TODO'},
-        {'id': 'lpme',   'name': 'LPME#',   'desc': 'TODO'},
-        {'id': 'lpcpd',  'name': 'LPCPD#',  'desc': 'TODO'},
-        {'id': 'lsmi',   'name': 'LSMI#',   'desc': 'TODO'},
-    ]
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'IDLE'
-        self.oldlclk = -1
-        self.samplenum = 0
-        self.clocknum = 0
-        self.lad = -1
-        self.addr = 0
-        self.cur_nibble = 0
-        self.cycle_type = -1
-        self.oldpins = (-1, -1, -1, -1, -1, -1, -1)
-
-    def start(self, metadata):
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'lpc')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'lpc')
-
-    def report(self):
-        pass
-
-    def handle_get_start(self, lad, lframe):
-        # LAD[3:0]: START field (1 clock cycle).
-
-        # The last value of LAD[3:0] before LFRAME# gets de-asserted is what
-        # the peripherals must use. However, the host can keep LFRAME# asserted
-        # multiple clocks, and we output all START fields that occur, even
-        # though the peripherals are supposed to ignore all but the last one.
-        s = fields['START'][lad]
-        self.put(0, 0, self.out_ann, [0, [s]])
-
-        # Output a warning if LAD[3:0] changes while LFRAME# is low.
-        # TODO
-        if (self.lad != -1 and self.lad != lad):
-            self.put(0, 0, self.out_ann,
-                     [0, ['Warning: LAD[3:0] changed while '
-                     'LFRAME# was asserted']])
-
-        # LFRAME# is asserted (low). Wait until it gets de-asserted again
-        # (the host is allowed to keep it asserted multiple clocks).
-        if lframe != 1:
-            return
-
-        self.start_field = self.lad
-        self.state = 'GET CT/DR'
-
-    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]
-
-        # TODO: Warning/error on invalid cycle types.
-        if self.cycle_type == 'Reserved':
-            self.put(0, 0, self.out_ann,
-                     [0, ['Warning: Invalid cycle type (%s)' % lad_bits]])
-
-        # ...
-        self.put(0, 0, self.out_ann, [0, ['Cycle type: %s' % self.cycle_type]])
-
-        self.state = 'GET ADDR'
-        self.addr = 0
-        self.cur_nibble = 0
-
-    def handle_get_addr(self, lad, lad_bits):
-        # LAD[3:0]: ADDR field (4/8/0 clock cycles).
-
-        # I/O cycles: 4 ADDR clocks. Memory cycles: 8 ADDR clocks.
-        # DMA cycles: no ADDR clocks at all.
-        if self.cycle_type in ('I/O read', 'I/O write'):
-            addr_nibbles = 4 # Address is 16bits.
-        elif self.cycle_type in ('Memory read', 'Memory write'):
-            addr_nibbles = 8 # Address is 32bits.
-        else:
-            addr_nibbles = 0 # TODO: How to handle later on?
-
-        # Data is driven MSN-first.
-        offset = ((addr_nibbles - 1) - self.cur_nibble) * 4
-        self.addr |= (lad << offset)
-
-        # Continue if we haven't seen all ADDR cycles, yet.
-        # TODO: Off-by-one?
-        if (self.cur_nibble < addr_nibbles - 1):
-            self.cur_nibble += 1
-            return
-
-        self.put(0, 0, self.out_ann, [0, ['Address: %s' % hex(self.addr)]])
-
-        self.state = 'GET TAR'
-        self.tar_count = 0
-
-    def handle_get_tar(self, lad, lad_bits):
-        # LAD[3:0]: First TAR (turn-around) field (2 clock cycles).
-
-        self.put(0, 0, self.out_ann, [0, ['TAR, cycle %d: %s'
-                 % (self.tarcount, lad_bits)]])
-
-        # On the first TAR clock cycle LAD[3:0] is driven to 1111 by
-        # either the host or peripheral. On the second clock cycle,
-        # the host or peripheral tri-states LAD[3:0], but its value
-        # should still be 1111, due to pull-ups on the LAD lines.
-        if lad_bits != '1111':
-            self.put(0, 0, self.out_ann,
-                     [0, ['Warning: TAR, cycle %d: %s (expected 1111)'
-                     % (self.tarcount, lad_bits)]])
-
-        if (self.tarcount != 2):
-            self.tarcount += 1
-            return
-
-        self.state = 'GET SYNC'
-
-    def handle_get_sync(self, lad, lad_bits):
-        # LAD[3:0]: SYNC field (1-n clock cycles).
-
-        self.sync_val = lad_bits
-        self.cycle_type = fields['SYNC'][lad]
-
-        # TODO: Warnings if reserved value are seen?
-        if self.cycle_type == 'Reserved':
-            self.put(0, 0, self.out_ann, [0, ['Warning: SYNC, cycle %d: %s '
-                     '(reserved value)' % (self.synccount, self.sync_val)]])
-
-        self.put(0, 0, self.out_ann, [0, ['SYNC, cycle %d: %s'
-                 % (self.synccount, self.sync_val)]])
-
-        # TODO
-
-        self.state = 'GET DATA'
-        self.cycle_count = 0
-
-    def handle_get_data(self, lad, lad_bits):
-        # LAD[3:0]: DATA field (2 clock cycles).
-
-        if (self.cycle_count == 0):
-            self.databyte = lad
-        elif (self.cycle_count == 1):
-            self.databyte |= (lad << 4)
-        else:
-            pass # TODO: Error?
-
-        if (self.cycle_count != 2):
-            self.cycle_count += 1
-            return
-
-        self.put(0, 0, self.out_ann, [0, ['DATA: %s' % hex(self.databyte)]])
-        
-        self.state = 'GET TAR2'
-
-    def handle_get_tar2(self, lad, lad_bits):
-        # LAD[3:0]: Second TAR field (2 clock cycles).
-
-        self.put(0, 0, self.out_ann, [0, ['TAR, cycle %d: %s'
-                 % (self.tarcount, lad_bits)]])
-
-        # On the first TAR clock cycle LAD[3:0] is driven to 1111 by
-        # either the host or peripheral. On the second clock cycle,
-        # the host or peripheral tri-states LAD[3:0], but its value
-        # should still be 1111, due to pull-ups on the LAD lines.
-        if lad_bits != '1111':
-            self.put(0, 0, self.out_ann,
-                     [0, ['Warning: TAR, cycle %d: %s (expected 1111)'
-                     % (self.tarcount, lad_bits)]])
-
-        if (self.tarcount != 2):
-            self.tarcount += 1
-            return
-
-        self.state = 'GET SYNC'
-
-    # TODO: At which edge of the clock is data latched? Falling?
-    def decode(self, ss, es, data):
-        for (samplenum, pins) in data:
-
-            # If none of the pins changed, there's nothing to do.
-            if self.oldpins == pins:
-                continue
-
-            # Store current pin values for the next round.
-            self.oldpins = pins
-
-            # Get individual pin values into local variables.
-            # TODO: Handle optional pins.
-            (lframe, lreset, lclk, lad0, lad1, lad2, lad3) = pins
-
-            # Only look at the signals upon falling LCLK edges.
-            # TODO: Rising?
-            ## if not (self.oldlclk == 1 and lclk == 0)
-            ##     self.oldlclk = lclk
-            ##     continue
-
-            # Store LAD[3:0] bit values (one nibble) in local variables.
-            # Most (but not all) states need this.
-            if self.state != 'IDLE':
-                lad = (lad3 << 3) | (lad2 << 2) | (lad1 << 1) | lad0
-                lad_bits = bin(lad)[2:]
-
-            # State machine
-            if self.state == 'IDLE':
-                # A valid LPC cycle starts with LFRAME# being asserted (low).
-                # TODO?
-                if lframe != 0:
-                   continue
-                self.state = 'GET START'
-                self.lad = -1
-                # self.clocknum = 0
-            elif self.state == 'GET START':
-                handle_get_start(lad, lad_bits, lframe)
-            elif self.state == 'GET CT/DR':
-                handle_get_ct_dr(lad, lad_bits)
-            elif self.state == 'GET ADDR':
-                handle_get_addr(lad, lad_bits)
-            elif self.state == 'GET TAR':
-                handle_get_tar(lad, lad_bits)
-            elif self.state == 'GET SYNC':
-                handle_get_sync(lad, lad_bits)
-            elif self.state == 'GET DATA':
-                handle_get_data(lad, lad_bits)
-            elif self.state == 'GET TAR2':
-                handle_get_tar2(lad, lad_bits)
-            else:
-                raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/lpc/pd.py b/decoders/lpc/pd.py
new file mode 100644 (file)
index 0000000..cd56a9b
--- /dev/null
@@ -0,0 +1,349 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# LPC protocol decoder
+
+import sigrokdecode as srd
+
+# ...
+fields = {
+    # START field (indicates start or stop of a transaction)
+    'START': {
+        0b0000: 'Start of cycle for a target',
+        0b0001: 'Reserved',
+        0b0010: 'Grant for bus master 0',
+        0b0011: 'Grant for bus master 1',
+        0b0100: 'Reserved',
+        0b0101: 'Reserved',
+        0b0110: 'Reserved',
+        0b0111: 'Reserved',
+        0b1000: 'Reserved',
+        0b1001: 'Reserved',
+        0b1010: 'Reserved',
+        0b1011: 'Reserved',
+        0b1100: 'Reserved',
+        0b1101: 'Start of cycle for a Firmware Memory Read cycle',
+        0b1110: 'Start of cycle for a Firmware Memory Write cycle',
+        0b1111: 'Stop/abort (end of a cycle for a target)',
+    },
+    # Cycle type / direction field
+    # Bit 0 (LAD[0]) is unused, should always be 0.
+    # Neither host nor peripheral are allowed to drive 0b11x0.
+    'CT_DR': {
+        0b0000: 'I/O read',
+        0b0010: 'I/O write',
+        0b0100: 'Memory read',
+        0b0110: 'Memory write',
+        0b1000: 'DMA read',
+        0b1010: 'DMA write',
+        0b1100: 'Reserved / not allowed',
+        0b1110: 'Reserved / not allowed',
+    },
+    # SIZE field (determines how many bytes are to be transferred)
+    # Bits[3:2] are reserved, must be driven to 0b00.
+    # Neither host nor peripheral are allowed to drive 0b0010.
+    'SIZE': {
+        0b0000: '8 bits (1 byte)',
+        0b0001: '16 bits (2 bytes)',
+        0b0010: 'Reserved / not allowed',
+        0b0011: '32 bits (4 bytes)',
+    },
+    # CHANNEL field (bits[2:0] contain the DMA channel number)
+    'CHANNEL': {
+        0b0000: '0',
+        0b0001: '1',
+        0b0010: '2',
+        0b0011: '3',
+        0b0100: '4',
+        0b0101: '5',
+        0b0110: '6',
+        0b0111: '7',
+    },
+    # SYNC field (used to add wait states)
+    'SYNC': {
+        0b0000: 'Ready',
+        0b0001: 'Reserved',
+        0b0010: 'Reserved',
+        0b0011: 'Reserved',
+        0b0100: 'Reserved',
+        0b0101: 'Short wait',
+        0b0110: 'Long wait',
+        0b0111: 'Reserved',
+        0b1000: 'Reserved',
+        0b1001: 'Ready more (DMA only)',
+        0b1010: 'Error',
+        0b1011: 'Reserved',
+        0b1100: 'Reserved',
+        0b1101: 'Reserved',
+        0b1110: 'Reserved',
+        0b1111: 'Reserved',
+    },
+}
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'lpc'
+    name = 'LPC'
+    longname = 'Low-Pin-Count'
+    desc = 'Protocol for low-bandwidth devices on PC mainboards.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['lpc']
+    probes = [
+        {'id': 'lframe', 'name': 'LFRAME#', 'desc': 'TODO'},
+        {'id': 'lreset', 'name': 'LRESET#', 'desc': 'TODO'},
+        {'id': 'lclk',   'name': 'LCLK',    'desc': 'TODO'},
+        {'id': 'lad0',   'name': 'LAD[0]',  'desc': 'TODO'},
+        {'id': 'lad1',   'name': 'LAD[1]',  'desc': 'TODO'},
+        {'id': 'lad2',   'name': 'LAD[2]',  'desc': 'TODO'},
+        {'id': 'lad3',   'name': 'LAD[3]',  'desc': 'TODO'},
+    ]
+    optional_probes = [
+        {'id': 'ldrq',   'name': 'LDRQ#',   'desc': 'TODO'},
+        {'id': 'serirq', 'name': 'SERIRQ',  'desc': 'TODO'},
+        {'id': 'clkrun', 'name': 'CLKRUN#', 'desc': 'TODO'},
+        {'id': 'lpme',   'name': 'LPME#',   'desc': 'TODO'},
+        {'id': 'lpcpd',  'name': 'LPCPD#',  'desc': 'TODO'},
+        {'id': 'lsmi',   'name': 'LSMI#',   'desc': 'TODO'},
+    ]
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'IDLE'
+        self.oldlclk = -1
+        self.samplenum = 0
+        self.clocknum = 0
+        self.lad = -1
+        self.addr = 0
+        self.cur_nibble = 0
+        self.cycle_type = -1
+        self.oldpins = (-1, -1, -1, -1, -1, -1, -1)
+
+    def start(self, metadata):
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'lpc')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'lpc')
+
+    def report(self):
+        pass
+
+    def handle_get_start(self, lad, lframe):
+        # LAD[3:0]: START field (1 clock cycle).
+
+        # The last value of LAD[3:0] before LFRAME# gets de-asserted is what
+        # the peripherals must use. However, the host can keep LFRAME# asserted
+        # multiple clocks, and we output all START fields that occur, even
+        # though the peripherals are supposed to ignore all but the last one.
+        s = fields['START'][lad]
+        self.put(0, 0, self.out_ann, [0, [s]])
+
+        # Output a warning if LAD[3:0] changes while LFRAME# is low.
+        # TODO
+        if (self.lad != -1 and self.lad != lad):
+            self.put(0, 0, self.out_ann,
+                     [0, ['Warning: LAD[3:0] changed while '
+                     'LFRAME# was asserted']])
+
+        # LFRAME# is asserted (low). Wait until it gets de-asserted again
+        # (the host is allowed to keep it asserted multiple clocks).
+        if lframe != 1:
+            return
+
+        self.start_field = self.lad
+        self.state = 'GET CT/DR'
+
+    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]
+
+        # TODO: Warning/error on invalid cycle types.
+        if self.cycle_type == 'Reserved':
+            self.put(0, 0, self.out_ann,
+                     [0, ['Warning: Invalid cycle type (%s)' % lad_bits]])
+
+        # ...
+        self.put(0, 0, self.out_ann, [0, ['Cycle type: %s' % self.cycle_type]])
+
+        self.state = 'GET ADDR'
+        self.addr = 0
+        self.cur_nibble = 0
+
+    def handle_get_addr(self, lad, lad_bits):
+        # LAD[3:0]: ADDR field (4/8/0 clock cycles).
+
+        # I/O cycles: 4 ADDR clocks. Memory cycles: 8 ADDR clocks.
+        # DMA cycles: no ADDR clocks at all.
+        if self.cycle_type in ('I/O read', 'I/O write'):
+            addr_nibbles = 4 # Address is 16bits.
+        elif self.cycle_type in ('Memory read', 'Memory write'):
+            addr_nibbles = 8 # Address is 32bits.
+        else:
+            addr_nibbles = 0 # TODO: How to handle later on?
+
+        # Data is driven MSN-first.
+        offset = ((addr_nibbles - 1) - self.cur_nibble) * 4
+        self.addr |= (lad << offset)
+
+        # Continue if we haven't seen all ADDR cycles, yet.
+        # TODO: Off-by-one?
+        if (self.cur_nibble < addr_nibbles - 1):
+            self.cur_nibble += 1
+            return
+
+        self.put(0, 0, self.out_ann, [0, ['Address: %s' % hex(self.addr)]])
+
+        self.state = 'GET TAR'
+        self.tar_count = 0
+
+    def handle_get_tar(self, lad, lad_bits):
+        # LAD[3:0]: First TAR (turn-around) field (2 clock cycles).
+
+        self.put(0, 0, self.out_ann, [0, ['TAR, cycle %d: %s'
+                 % (self.tarcount, lad_bits)]])
+
+        # On the first TAR clock cycle LAD[3:0] is driven to 1111 by
+        # either the host or peripheral. On the second clock cycle,
+        # the host or peripheral tri-states LAD[3:0], but its value
+        # should still be 1111, due to pull-ups on the LAD lines.
+        if lad_bits != '1111':
+            self.put(0, 0, self.out_ann,
+                     [0, ['Warning: TAR, cycle %d: %s (expected 1111)'
+                     % (self.tarcount, lad_bits)]])
+
+        if (self.tarcount != 2):
+            self.tarcount += 1
+            return
+
+        self.state = 'GET SYNC'
+
+    def handle_get_sync(self, lad, lad_bits):
+        # LAD[3:0]: SYNC field (1-n clock cycles).
+
+        self.sync_val = lad_bits
+        self.cycle_type = fields['SYNC'][lad]
+
+        # TODO: Warnings if reserved value are seen?
+        if self.cycle_type == 'Reserved':
+            self.put(0, 0, self.out_ann, [0, ['Warning: SYNC, cycle %d: %s '
+                     '(reserved value)' % (self.synccount, self.sync_val)]])
+
+        self.put(0, 0, self.out_ann, [0, ['SYNC, cycle %d: %s'
+                 % (self.synccount, self.sync_val)]])
+
+        # TODO
+
+        self.state = 'GET DATA'
+        self.cycle_count = 0
+
+    def handle_get_data(self, lad, lad_bits):
+        # LAD[3:0]: DATA field (2 clock cycles).
+
+        if (self.cycle_count == 0):
+            self.databyte = lad
+        elif (self.cycle_count == 1):
+            self.databyte |= (lad << 4)
+        else:
+            pass # TODO: Error?
+
+        if (self.cycle_count != 2):
+            self.cycle_count += 1
+            return
+
+        self.put(0, 0, self.out_ann, [0, ['DATA: %s' % hex(self.databyte)]])
+        
+        self.state = 'GET TAR2'
+
+    def handle_get_tar2(self, lad, lad_bits):
+        # LAD[3:0]: Second TAR field (2 clock cycles).
+
+        self.put(0, 0, self.out_ann, [0, ['TAR, cycle %d: %s'
+                 % (self.tarcount, lad_bits)]])
+
+        # On the first TAR clock cycle LAD[3:0] is driven to 1111 by
+        # either the host or peripheral. On the second clock cycle,
+        # the host or peripheral tri-states LAD[3:0], but its value
+        # should still be 1111, due to pull-ups on the LAD lines.
+        if lad_bits != '1111':
+            self.put(0, 0, self.out_ann,
+                     [0, ['Warning: TAR, cycle %d: %s (expected 1111)'
+                     % (self.tarcount, lad_bits)]])
+
+        if (self.tarcount != 2):
+            self.tarcount += 1
+            return
+
+        self.state = 'GET SYNC'
+
+    # TODO: At which edge of the clock is data latched? Falling?
+    def decode(self, ss, es, data):
+        for (samplenum, pins) in data:
+
+            # If none of the pins changed, there's nothing to do.
+            if self.oldpins == pins:
+                continue
+
+            # Store current pin values for the next round.
+            self.oldpins = pins
+
+            # Get individual pin values into local variables.
+            # TODO: Handle optional pins.
+            (lframe, lreset, lclk, lad0, lad1, lad2, lad3) = pins
+
+            # Only look at the signals upon falling LCLK edges.
+            # TODO: Rising?
+            ## if not (self.oldlclk == 1 and lclk == 0)
+            ##     self.oldlclk = lclk
+            ##     continue
+
+            # Store LAD[3:0] bit values (one nibble) in local variables.
+            # Most (but not all) states need this.
+            if self.state != 'IDLE':
+                lad = (lad3 << 3) | (lad2 << 2) | (lad1 << 1) | lad0
+                lad_bits = bin(lad)[2:]
+
+            # State machine
+            if self.state == 'IDLE':
+                # A valid LPC cycle starts with LFRAME# being asserted (low).
+                # TODO?
+                if lframe != 0:
+                   continue
+                self.state = 'GET START'
+                self.lad = -1
+                # self.clocknum = 0
+            elif self.state == 'GET START':
+                handle_get_start(lad, lad_bits, lframe)
+            elif self.state == 'GET CT/DR':
+                handle_get_ct_dr(lad, lad_bits)
+            elif self.state == 'GET ADDR':
+                handle_get_addr(lad, lad_bits)
+            elif self.state == 'GET TAR':
+                handle_get_tar(lad, lad_bits)
+            elif self.state == 'GET SYNC':
+                handle_get_sync(lad, lad_bits)
+            elif self.state == 'GET DATA':
+                handle_get_data(lad, lad_bits)
+            elif self.state == 'GET TAR2':
+                handle_get_tar2(lad, lad_bits)
+            else:
+                raise Exception('Invalid state: %s' % self.state)
+
index ee53993e12105308a27577e17be05d32dc3f997e..24413596d5a7e5cbe0556490c9f59b00bd1a22e2 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/maxim_ds28ea00
 
 
 pkgdatadir = $(DECODERS_DIR)/maxim_ds28ea00
 
-dist_pkgdata_DATA = __init__.py maxim_ds28ea00.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 508ae5b960077fd21eee6fa02c7a25c3b9b531da..cb1c77870bae562a5cda9d71ec5b29010d3771f0 100644 (file)
@@ -28,5 +28,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .maxim_ds28ea00 import *
+from .pd import *
 
 
diff --git a/decoders/maxim_ds28ea00/maxim_ds28ea00.py b/decoders/maxim_ds28ea00/maxim_ds28ea00.py
deleted file mode 100644 (file)
index 026457e..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Maxim DS28EA00 protocol decoder
-
-import sigrokdecode as srd
-
-# Dictionary of FUNCTION commands and their names.
-command = {
-    # Scratchpad
-    0x4e: 'Write scratchpad',
-    0xbe: 'Read scratchpad',
-    0x48: 'Copy scratchpad',
-    # Thermometer
-    0x44: 'Convert temperature',
-    0xb4: 'Read power mode',
-    0xb8: 'Recall EEPROM',
-    0xf5: 'PIO access read',
-    0xA5: 'PIO access write',
-    0x99: 'Chain',
-}
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'maxim_ds28ea00'
-    name = 'Maxim DS28EA00'
-    longname = 'Maxim DS28EA00 1-Wire digital thermometer'
-    desc = '1-Wire digital thermometer with Sequence Detect and PIO'
-    license = 'gplv2+'
-    inputs = ['onewire_network']
-    outputs = ['maxim_ds28ea00']
-    probes = []
-    optional_probes = [
-        {'id': 'pioa', 'name': 'PIOA/DONE#',
-         'desc': 'PIOA channel and chain output'},
-        {'id': 'piob', 'name': 'PIOB/EN#',
-         'desc': 'PIOB channel and chain output'},
-    ]
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.trn_beg = 0
-        self.trn_end = 0
-        self.state = 'ROM'
-        self.rom = 0x0000000000000000
-
-    def start(self, metadata):
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'maxim_ds28ea00')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def decode(self, ss, es, data):
-        code, val = data
-
-        self.ss, self.es = ss, es
-
-        # State machine.
-        if code == 'RESET/PRESENCE':
-            self.putx([0, ['Reset/presence: %s'
-                           % ('true' if val else 'false')]])
-            self.state = 'ROM'
-        elif code == 'ROM':
-            self.rom = val
-            self.putx([0, ['ROM: 0x%016x' % (val)]])
-            self.state = 'COMMAND'
-        elif code == 'DATA':
-            if self.state == 'COMMAND':
-                if val not in command:
-                    self.putx([0, ['Unrecognized command: 0x%02x' % val]])
-                    return
-                self.putx([0, ['Function command: 0x%02x \'%s\''
-                          % (val, command[val])]])
-                self.state = command[val].upper()
-            elif self.state == 'READ SCRATCHPAD':
-                self.putx([0, ['Scratchpad data: 0x%02x' % val]])
-            elif self.state == 'CONVERT TEMPERATURE':
-                self.putx([0, ['Temperature conversion status: 0x%02x' % val]])
-            elif self.state in [s.upper() for s in command.values()]:
-                self.putx([0, ['TODO \'%s\': 0x%02x' % (self.state, val)]])
-            else:
-                raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/maxim_ds28ea00/pd.py b/decoders/maxim_ds28ea00/pd.py
new file mode 100644 (file)
index 0000000..026457e
--- /dev/null
@@ -0,0 +1,106 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Maxim DS28EA00 protocol decoder
+
+import sigrokdecode as srd
+
+# Dictionary of FUNCTION commands and their names.
+command = {
+    # Scratchpad
+    0x4e: 'Write scratchpad',
+    0xbe: 'Read scratchpad',
+    0x48: 'Copy scratchpad',
+    # Thermometer
+    0x44: 'Convert temperature',
+    0xb4: 'Read power mode',
+    0xb8: 'Recall EEPROM',
+    0xf5: 'PIO access read',
+    0xA5: 'PIO access write',
+    0x99: 'Chain',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'maxim_ds28ea00'
+    name = 'Maxim DS28EA00'
+    longname = 'Maxim DS28EA00 1-Wire digital thermometer'
+    desc = '1-Wire digital thermometer with Sequence Detect and PIO'
+    license = 'gplv2+'
+    inputs = ['onewire_network']
+    outputs = ['maxim_ds28ea00']
+    probes = []
+    optional_probes = [
+        {'id': 'pioa', 'name': 'PIOA/DONE#',
+         'desc': 'PIOA channel and chain output'},
+        {'id': 'piob', 'name': 'PIOB/EN#',
+         'desc': 'PIOB channel and chain output'},
+    ]
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.trn_beg = 0
+        self.trn_end = 0
+        self.state = 'ROM'
+        self.rom = 0x0000000000000000
+
+    def start(self, metadata):
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'maxim_ds28ea00')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def decode(self, ss, es, data):
+        code, val = data
+
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if code == 'RESET/PRESENCE':
+            self.putx([0, ['Reset/presence: %s'
+                           % ('true' if val else 'false')]])
+            self.state = 'ROM'
+        elif code == 'ROM':
+            self.rom = val
+            self.putx([0, ['ROM: 0x%016x' % (val)]])
+            self.state = 'COMMAND'
+        elif code == 'DATA':
+            if self.state == 'COMMAND':
+                if val not in command:
+                    self.putx([0, ['Unrecognized command: 0x%02x' % val]])
+                    return
+                self.putx([0, ['Function command: 0x%02x \'%s\''
+                          % (val, command[val])]])
+                self.state = command[val].upper()
+            elif self.state == 'READ SCRATCHPAD':
+                self.putx([0, ['Scratchpad data: 0x%02x' % val]])
+            elif self.state == 'CONVERT TEMPERATURE':
+                self.putx([0, ['Temperature conversion status: 0x%02x' % val]])
+            elif self.state in [s.upper() for s in command.values()]:
+                self.putx([0, ['TODO \'%s\': 0x%02x' % (self.state, val)]])
+            else:
+                raise Exception('Invalid state: %s' % self.state)
+
index f14874fa5bc610f83e79a58eab2819f920ac8a74..c9d0d4b502740a8da9893d4a57703a0c64179ef1 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/mlx90614
 
 
 pkgdatadir = $(DECODERS_DIR)/mlx90614
 
-dist_pkgdata_DATA = __init__.py mlx90614.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 0ceabc20d986606587413b494333a512a5b758b6..c14e67f40f3c7a02adf627c709c95b48dc42904a 100644 (file)
@@ -25,5 +25,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .mlx90614 import *
+from .pd import *
 
 
diff --git a/decoders/mlx90614/mlx90614.py b/decoders/mlx90614/mlx90614.py
deleted file mode 100644 (file)
index 1ee88d7..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Melexis MLX90614 Infrared Thermometer protocol decoder
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'mlx90614'
-    name = 'MLX90614'
-    longname = 'Melexis MLX90614'
-    desc = 'Infrared Thermometer protocol.'
-    license = 'gplv2+'
-    inputs = ['i2c']
-    outputs = ['mlx90614']
-    probes = []
-    optional_probes = []
-    options = {}
-    annotations = [
-        ['Celsius', 'Temperature in degrees Celsius'],
-        ['Kelvin', 'Temperature in degrees Kelvin'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'IGNORE START REPEAT'
-        self.data = []
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mlx90614')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'mlx90614')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    # Quick hack implementation! This needs to be improved a lot!
-    def decode(self, ss, es, data):
-        cmd, databyte = data
-
-        # State machine.
-        if self.state == 'IGNORE START REPEAT':
-            if cmd != 'START REPEAT':
-                return
-            self.state = 'IGNORE ADDRESS WRITE'
-        elif self.state == 'IGNORE ADDRESS WRITE':
-            if cmd != 'ADDRESS WRITE':
-                return
-            self.state = 'GET TEMPERATURE'
-        elif self.state == 'GET TEMPERATURE':
-            if cmd != 'DATA WRITE':
-                return
-            if len(self.data) == 0:
-                self.data.append(databyte)
-                self.ss = ss
-            elif len(self.data) == 1:
-                self.data.append(databyte)
-                self.es = es
-            else:
-                kelvin = (self.data[0] | (self.data[1] << 8)) * 0.02
-                celsius = kelvin - 273.15
-                self.putx([0, ['Temperature: %3.2f Â°C' % celsius]])
-                self.putx([1, ['Temperature: %3.2f Â°K' % kelvin]])
-                self.state = 'IGNORE START REPEAT'
-                self.data = []
-        else:
-            raise Exception('Invalid state: %d' % self.state)
-
diff --git a/decoders/mlx90614/pd.py b/decoders/mlx90614/pd.py
new file mode 100644 (file)
index 0000000..1ee88d7
--- /dev/null
@@ -0,0 +1,87 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Melexis MLX90614 Infrared Thermometer protocol decoder
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'mlx90614'
+    name = 'MLX90614'
+    longname = 'Melexis MLX90614'
+    desc = 'Infrared Thermometer protocol.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['mlx90614']
+    probes = []
+    optional_probes = []
+    options = {}
+    annotations = [
+        ['Celsius', 'Temperature in degrees Celsius'],
+        ['Kelvin', 'Temperature in degrees Kelvin'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'IGNORE START REPEAT'
+        self.data = []
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mlx90614')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'mlx90614')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    # Quick hack implementation! This needs to be improved a lot!
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+
+        # State machine.
+        if self.state == 'IGNORE START REPEAT':
+            if cmd != 'START REPEAT':
+                return
+            self.state = 'IGNORE ADDRESS WRITE'
+        elif self.state == 'IGNORE ADDRESS WRITE':
+            if cmd != 'ADDRESS WRITE':
+                return
+            self.state = 'GET TEMPERATURE'
+        elif self.state == 'GET TEMPERATURE':
+            if cmd != 'DATA WRITE':
+                return
+            if len(self.data) == 0:
+                self.data.append(databyte)
+                self.ss = ss
+            elif len(self.data) == 1:
+                self.data.append(databyte)
+                self.es = es
+            else:
+                kelvin = (self.data[0] | (self.data[1] << 8)) * 0.02
+                celsius = kelvin - 273.15
+                self.putx([0, ['Temperature: %3.2f Â°C' % celsius]])
+                self.putx([1, ['Temperature: %3.2f Â°K' % kelvin]])
+                self.state = 'IGNORE START REPEAT'
+                self.data = []
+        else:
+            raise Exception('Invalid state: %d' % self.state)
+
index 2d571f3bd8a3d2d751f4fef6faef5b297e7545e6..69497966bd18e38884fae7cd2749d3eaafb34866 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/mx25lxx05d
 
 
 pkgdatadir = $(DECODERS_DIR)/mx25lxx05d
 
-dist_pkgdata_DATA = __init__.py mx25lxx05d.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 3f3c1c662d13d707cbfc6ff138619869446ce0f9..74b70e8184f569bd876c12797415718a220dc6e3 100644 (file)
@@ -29,5 +29,5 @@ Details:
 http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/h_Index/3F21BAC2E121E17848257639003A3146/$File/MX25L1605D-3205D-6405D-1.5.pdf
 '''
 
 http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/h_Index/3F21BAC2E121E17848257639003A3146/$File/MX25L1605D-3205D-6405D-1.5.pdf
 '''
 
-from .mx25lxx05d import *
+from .pd import *
 
 
diff --git a/decoders/mx25lxx05d/mx25lxx05d.py b/decoders/mx25lxx05d/mx25lxx05d.py
deleted file mode 100644 (file)
index 182ae26..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Macronix MX25Lxx05D SPI (NOR) flash chip protocol decoder
-
-# Note: Works for MX25L1605D/MX25L3205D/MX25L6405D.
-
-import sigrokdecode as srd
-
-# Dict which maps command IDs to their names and descriptions.
-cmds = {
-    0x06: ('WREN', 'Write enable'),
-    0x04: ('WRDI', 'Write disable'),
-    0x9f: ('RDID', 'Read identification'),
-    0x05: ('RDSR', 'Read status register'),
-    0x01: ('WRSR', 'Write status register'),
-    0x03: ('READ', 'Read data'),
-    0x0b: ('FAST/READ', 'Fast read data'),
-    0xbb: ('2READ', '2x I/O read'),
-    0x20: ('SE', 'Sector erase'),
-    0xd8: ('BE', 'Block erase'),
-    0x60: ('CE', 'Chip erase'),
-    0xc7: ('CE2', 'Chip erase'), # Alternative command ID
-    0x02: ('PP', 'Page program'),
-    0xad: ('CP', 'Continuously program mode'),
-    0xb9: ('DP', 'Deep power down'),
-    0xab: ('RDP/RES', 'Release from deep powerdown / Read electronic ID'),
-    0x90: ('REMS', 'Read electronic manufacturer & device ID'),
-    0xef: ('REMS2', 'Read ID for 2x I/O mode'),
-    0xb1: ('ENSO', 'Enter secured OTP'),
-    0xc1: ('EXSO', 'Exit secured OTP'),
-    0x2b: ('RDSCUR', 'Read security register'),
-    0x2f: ('WRSCUR', 'Write security register'),
-    0x70: ('ESRY', 'Enable SO to output RY/BY#'),
-    0x80: ('DSRY', 'Disable SO to output RY/BY#'),
-}
-
-device_name = {
-    0x14: 'MX25L1605D',
-    0x15: 'MX25L3205D',
-    0x16: 'MX25L6405D',
-}
-
-def decode_status_reg(data):
-    # TODO: Additional per-bit(s) self.put() calls with correct start/end.
-
-    # Bits[0:0]: WIP (write in progress)
-    s = 'W' if (data & (1 << 0)) else 'No w'
-    ret = '%srite operation in progress.\n' % s
-
-    # Bits[1:1]: WEL (write enable latch)
-    s = '' if (data & (1 << 1)) else 'not '
-    ret += 'Internal write enable latch is %sset.\n' % s
-
-    # Bits[5:2]: Block protect bits
-    # TODO: More detailed decoding (chip-dependent).
-    ret += 'Block protection bits (BP3-BP0): 0x%x.\n' % ((data & 0x3c) >> 2)
-
-    # Bits[6:6]: Continuously program mode (CP mode)
-    s = '' if (data & (1 << 6)) else 'not '
-    ret += 'Device is %sin continuously program mode (CP mode).\n' % s
-
-    # Bits[7:7]: SRWD (status register write disable)
-    s = 'not ' if (data & (1 << 7)) else ''
-    ret += 'Status register writes are %sallowed.\n' % s
-
-    return ret
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'mx25lxx05d'
-    name = 'MX25Lxx05D'
-    longname = 'Macronix MX25Lxx05D'
-    desc = 'SPI (NOR) flash chip protocol.'
-    license = 'gplv2+'
-    inputs = ['spi', 'logic']
-    outputs = ['mx25lxx05d']
-    probes = []
-    optional_probes = [
-        {'id': 'hold', 'name': 'HOLD#', 'desc': 'TODO.'},
-        {'id': 'wp_acc', 'name': 'WP#/ACC', 'desc': 'TODO.'},
-    ]
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-        ['Verbose decode', 'Decoded register bits, read/write data'],
-        ['Warnings', 'Human-readable warnings'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = None
-        self.cmdstate = 1
-        self.addr = 0
-        self.data = []
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mx25lxx05d')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'mx25lxx05d')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        # Simplification, most annotations span exactly one SPI byte/packet.
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def handle_wren(self, mosi, miso):
-        self.putx([0, ['Command: %s' % cmds[self.state][1]]])
-        self.state = None
-
-    def handle_wrdi(self, mosi, miso):
-        pass # TODO
-
-    # TODO: Check/display device ID / name
-    def handle_rdid(self, mosi, miso):
-        if self.cmdstate == 1:
-            # Byte 1: Master sends command ID.
-            self.start_sample = self.ss
-            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
-        elif self.cmdstate == 2:
-            # Byte 2: Slave sends the JEDEC manufacturer ID.
-            self.putx([0, ['Manufacturer ID: 0x%02x' % miso]])
-        elif self.cmdstate == 3:
-            # Byte 3: Slave sends the memory type (0x20 for this chip).
-            self.putx([0, ['Memory type: 0x%02x' % miso]])
-        elif self.cmdstate == 4:
-            # Byte 4: Slave sends the device ID.
-            self.device_id = miso
-            self.putx([0, ['Device ID: 0x%02x' % miso]])
-
-        if self.cmdstate == 4:
-            # TODO: Check self.device_id is valid & exists in device_names.
-            # TODO: Same device ID? Check!
-            d = 'Device: Macronix %s' % device_name[self.device_id]
-            self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
-            self.state = None
-        else:
-            self.cmdstate += 1
-
-    def handle_rdsr(self, mosi, miso):
-        # Read status register: Master asserts CS#, sends RDSR command,
-        # reads status register byte. If CS# is kept asserted, the status
-        # register can be read continuously / multiple times in a row.
-        # When done, the master de-asserts CS# again.
-        if self.cmdstate == 1:
-            # Byte 1: Master sends command ID.
-            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
-        elif self.cmdstate >= 2:
-            # Bytes 2-x: Slave sends status register as long as master clocks.
-            if self.cmdstate <= 3: # TODO: While CS# asserted.
-                self.putx([0, ['Status register: 0x%02x' % miso]])
-                self.putx([1, [decode_status_reg(miso)]])
-
-            if self.cmdstate == 3: # TODO: If CS# got de-asserted.
-                self.state = None
-                return
-
-        self.cmdstate += 1
-
-    def handle_wrsr(self, mosi, miso):
-        pass # TODO
-
-    def handle_read(self, mosi, miso):
-        # Read data bytes: Master asserts CS#, sends READ command, sends
-        # 3-byte address, reads >= 1 data bytes, de-asserts CS#.
-        if self.cmdstate == 1:
-            # Byte 1: Master sends command ID.
-            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
-        elif self.cmdstate in (2, 3, 4):
-            # Bytes 2/3/4: Master sends read address (24bits, MSB-first).
-            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
-            # self.putx([0, ['Read address, byte %d: 0x%02x' % \
-            #                (4 - self.cmdstate, mosi)]])
-            if self.cmdstate == 4:
-                self.putx([0, ['Read address: 0x%06x' % self.addr]])
-                self.addr = 0
-        elif self.cmdstate >= 5:
-            # Bytes 5-x: Master reads data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 256 bytes per READ command.
-            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
-                self.data.append(miso)
-                # self.putx([0, ['New read byte: 0x%02x' % miso]])
-
-            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
-                # s = ', '.join(map(hex, self.data))
-                s = ''.join(map(chr, self.data))
-                self.putx([0, ['Read data']])
-                self.putx([1, ['Read data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
-
-        self.cmdstate += 1
-
-    def handle_fast_read(self, mosi, miso):
-        pass # TODO
-
-    def handle_2read(self, mosi, miso):
-        pass # TODO
-
-    # TODO: Warn/abort if we don't see the necessary amount of bytes.
-    # TODO: Warn if WREN was not seen before.
-    def handle_se(self, mosi, miso):
-        if self.cmdstate == 1:
-            # Byte 1: Master sends command ID.
-            self.addr = 0
-            self.start_sample = self.ss
-            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
-        elif self.cmdstate in (2, 3, 4):
-            # Bytes 2/3/4: Master sends sectror address (24bits, MSB-first).
-            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
-            # self.putx([0, ['Sector address, byte %d: 0x%02x' % \
-            #                (4 - self.cmdstate, mosi)]])
-
-        if self.cmdstate == 4:
-            d = 'Erase sector %d (0x%06x)' % (self.addr, self.addr)
-            self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
-            # TODO: Max. size depends on chip, check that too if possible.
-            if self.addr % 4096 != 0:
-                # Sector addresses must be 4K-aligned (same for all 3 chips).
-                d = 'Warning: Invalid sector address!'
-                self.put(self.start_sample, self.es, self.out_ann, [2, [d]])
-            self.state = None
-        else:
-            self.cmdstate += 1
-
-    def handle_be(self, mosi, miso):
-        pass # TODO
-
-    def handle_ce(self, mosi, miso):
-        pass # TODO
-
-    def handle_ce2(self, mosi, miso):
-        pass # TODO
-
-    def handle_pp(self, mosi, miso):
-        # Page program: Master asserts CS#, sends PP command, sends 3-byte
-        # page address, sends >= 1 data bytes, de-asserts CS#.
-        if self.cmdstate == 1:
-            # Byte 1: Master sends command ID.
-            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
-        elif self.cmdstate in (2, 3, 4):
-            # Bytes 2/3/4: Master sends page address (24bits, MSB-first).
-            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
-            # self.putx([0, ['Page address, byte %d: 0x%02x' % \
-            #                (4 - self.cmdstate, mosi)]])
-            if self.cmdstate == 4:
-                self.putx([0, ['Page address: 0x%06x' % self.addr]])
-                self.addr = 0
-        elif self.cmdstate >= 5:
-            # Bytes 5-x: Master sends data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 256 bytes per page / PP command.
-            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
-                self.data.append(mosi)
-                # self.putx([0, ['New data byte: 0x%02x' % mosi]])
-
-            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
-                # s = ', '.join(map(hex, self.data))
-                s = ''.join(map(chr, self.data))
-                self.putx([0, ['Page data']])
-                self.putx([1, ['Page data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
-
-        self.cmdstate += 1
-
-    def handle_cp(self, mosi, miso):
-        pass # TODO
-
-    def handle_dp(self, mosi, miso):
-        pass # TODO
-
-    def handle_rdp_res(self, mosi, miso):
-        pass # TODO
-
-    def handle_rems(self, mosi, miso):
-        if self.cmdstate == 1:
-            # Byte 1: Master sends command ID.
-            self.start_sample = self.ss
-            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
-        elif self.cmdstate in (2, 3):
-            # Bytes 2/3: Master sends two dummy bytes.
-            # TODO: Check dummy bytes? Check reply from device?
-            self.putx([0, ['Dummy byte: %s' % mosi]])
-        elif self.cmdstate == 4:
-            # Byte 4: Master sends 0x00 or 0x01.
-            # 0x00: Master wants manufacturer ID as first reply byte.
-            # 0x01: Master wants device ID as first reply byte.
-            self.manufacturer_id_first = True if (mosi == 0x00) else False
-            d = 'manufacturer' if (mosi == 0x00) else 'device'
-            self.putx([0, ['Master wants %s ID first' % d]])
-        elif self.cmdstate == 5:
-            # Byte 5: Slave sends manufacturer ID (or device ID).
-            self.ids = [miso]
-            d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
-            self.putx([0, ['%s ID' % d]])
-        elif self.cmdstate == 6:
-            # Byte 6: Slave sends device ID (or manufacturer ID).
-            self.ids.append(miso)
-            d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
-            self.putx([0, ['%s ID' % d]])
-
-        if self.cmdstate == 6:
-            self.end_sample = self.es
-            id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
-            self.putx([0, ['Device: Macronix %s' % device_name[id]]])
-            self.state = None
-        else:
-            self.cmdstate += 1
-
-    def handle_rems2(self, mosi, miso):
-        pass # TODO
-
-    def handle_enso(self, mosi, miso):
-        pass # TODO
-
-    def handle_exso(self, mosi, miso):
-        pass # TODO
-
-    def handle_rdscur(self, mosi, miso):
-        pass # TODO
-
-    def handle_wrscur(self, mosi, miso):
-        pass # TODO
-
-    def handle_esry(self, mosi, miso):
-        pass # TODO
-
-    def handle_dsry(self, mosi, miso):
-        pass # TODO
-
-    def decode(self, ss, es, data):
-
-        ptype, mosi, miso = data
-
-        # if ptype == 'DATA':
-        #     self.putx([0, ['MOSI: 0x%02x, MISO: 0x%02x' % (mosi, miso)]])
-
-        # if ptype == 'CS-CHANGE':
-        #     if mosi == 1 and miso == 0:
-        #         self.putx([0, ['Asserting CS#']])
-        #     elif mosi == 0 and miso == 1:
-        #         self.putx([0, ['De-asserting CS#']])
-
-        if ptype != 'DATA':
-            return
-
-        self.ss, self.es = ss, es
-
-        # If we encountered a known chip command, enter the resp. state.
-        if self.state == None:
-            self.state = mosi
-            self.cmdstate = 1
-
-        # Handle commands.
-        if self.state in cmds:
-            s = 'handle_%s' % cmds[self.state][0].lower().replace('/', '_')
-            handle_reg = getattr(self, s)
-            handle_reg(mosi, miso)
-        else:
-            self.putx([0, ['Unknown command: 0x%02x' % mosi]])
-            self.state = None
-
diff --git a/decoders/mx25lxx05d/pd.py b/decoders/mx25lxx05d/pd.py
new file mode 100644 (file)
index 0000000..182ae26
--- /dev/null
@@ -0,0 +1,381 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Macronix MX25Lxx05D SPI (NOR) flash chip protocol decoder
+
+# Note: Works for MX25L1605D/MX25L3205D/MX25L6405D.
+
+import sigrokdecode as srd
+
+# Dict which maps command IDs to their names and descriptions.
+cmds = {
+    0x06: ('WREN', 'Write enable'),
+    0x04: ('WRDI', 'Write disable'),
+    0x9f: ('RDID', 'Read identification'),
+    0x05: ('RDSR', 'Read status register'),
+    0x01: ('WRSR', 'Write status register'),
+    0x03: ('READ', 'Read data'),
+    0x0b: ('FAST/READ', 'Fast read data'),
+    0xbb: ('2READ', '2x I/O read'),
+    0x20: ('SE', 'Sector erase'),
+    0xd8: ('BE', 'Block erase'),
+    0x60: ('CE', 'Chip erase'),
+    0xc7: ('CE2', 'Chip erase'), # Alternative command ID
+    0x02: ('PP', 'Page program'),
+    0xad: ('CP', 'Continuously program mode'),
+    0xb9: ('DP', 'Deep power down'),
+    0xab: ('RDP/RES', 'Release from deep powerdown / Read electronic ID'),
+    0x90: ('REMS', 'Read electronic manufacturer & device ID'),
+    0xef: ('REMS2', 'Read ID for 2x I/O mode'),
+    0xb1: ('ENSO', 'Enter secured OTP'),
+    0xc1: ('EXSO', 'Exit secured OTP'),
+    0x2b: ('RDSCUR', 'Read security register'),
+    0x2f: ('WRSCUR', 'Write security register'),
+    0x70: ('ESRY', 'Enable SO to output RY/BY#'),
+    0x80: ('DSRY', 'Disable SO to output RY/BY#'),
+}
+
+device_name = {
+    0x14: 'MX25L1605D',
+    0x15: 'MX25L3205D',
+    0x16: 'MX25L6405D',
+}
+
+def decode_status_reg(data):
+    # TODO: Additional per-bit(s) self.put() calls with correct start/end.
+
+    # Bits[0:0]: WIP (write in progress)
+    s = 'W' if (data & (1 << 0)) else 'No w'
+    ret = '%srite operation in progress.\n' % s
+
+    # Bits[1:1]: WEL (write enable latch)
+    s = '' if (data & (1 << 1)) else 'not '
+    ret += 'Internal write enable latch is %sset.\n' % s
+
+    # Bits[5:2]: Block protect bits
+    # TODO: More detailed decoding (chip-dependent).
+    ret += 'Block protection bits (BP3-BP0): 0x%x.\n' % ((data & 0x3c) >> 2)
+
+    # Bits[6:6]: Continuously program mode (CP mode)
+    s = '' if (data & (1 << 6)) else 'not '
+    ret += 'Device is %sin continuously program mode (CP mode).\n' % s
+
+    # Bits[7:7]: SRWD (status register write disable)
+    s = 'not ' if (data & (1 << 7)) else ''
+    ret += 'Status register writes are %sallowed.\n' % s
+
+    return ret
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'mx25lxx05d'
+    name = 'MX25Lxx05D'
+    longname = 'Macronix MX25Lxx05D'
+    desc = 'SPI (NOR) flash chip protocol.'
+    license = 'gplv2+'
+    inputs = ['spi', 'logic']
+    outputs = ['mx25lxx05d']
+    probes = []
+    optional_probes = [
+        {'id': 'hold', 'name': 'HOLD#', 'desc': 'TODO.'},
+        {'id': 'wp_acc', 'name': 'WP#/ACC', 'desc': 'TODO.'},
+    ]
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+        ['Verbose decode', 'Decoded register bits, read/write data'],
+        ['Warnings', 'Human-readable warnings'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = None
+        self.cmdstate = 1
+        self.addr = 0
+        self.data = []
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mx25lxx05d')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'mx25lxx05d')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        # Simplification, most annotations span exactly one SPI byte/packet.
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def handle_wren(self, mosi, miso):
+        self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        self.state = None
+
+    def handle_wrdi(self, mosi, miso):
+        pass # TODO
+
+    # TODO: Check/display device ID / name
+    def handle_rdid(self, mosi, miso):
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.start_sample = self.ss
+            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate == 2:
+            # Byte 2: Slave sends the JEDEC manufacturer ID.
+            self.putx([0, ['Manufacturer ID: 0x%02x' % miso]])
+        elif self.cmdstate == 3:
+            # Byte 3: Slave sends the memory type (0x20 for this chip).
+            self.putx([0, ['Memory type: 0x%02x' % miso]])
+        elif self.cmdstate == 4:
+            # Byte 4: Slave sends the device ID.
+            self.device_id = miso
+            self.putx([0, ['Device ID: 0x%02x' % miso]])
+
+        if self.cmdstate == 4:
+            # TODO: Check self.device_id is valid & exists in device_names.
+            # TODO: Same device ID? Check!
+            d = 'Device: Macronix %s' % device_name[self.device_id]
+            self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
+            self.state = None
+        else:
+            self.cmdstate += 1
+
+    def handle_rdsr(self, mosi, miso):
+        # Read status register: Master asserts CS#, sends RDSR command,
+        # reads status register byte. If CS# is kept asserted, the status
+        # register can be read continuously / multiple times in a row.
+        # When done, the master de-asserts CS# again.
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate >= 2:
+            # Bytes 2-x: Slave sends status register as long as master clocks.
+            if self.cmdstate <= 3: # TODO: While CS# asserted.
+                self.putx([0, ['Status register: 0x%02x' % miso]])
+                self.putx([1, [decode_status_reg(miso)]])
+
+            if self.cmdstate == 3: # TODO: If CS# got de-asserted.
+                self.state = None
+                return
+
+        self.cmdstate += 1
+
+    def handle_wrsr(self, mosi, miso):
+        pass # TODO
+
+    def handle_read(self, mosi, miso):
+        # Read data bytes: Master asserts CS#, sends READ command, sends
+        # 3-byte address, reads >= 1 data bytes, de-asserts CS#.
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate in (2, 3, 4):
+            # Bytes 2/3/4: Master sends read address (24bits, MSB-first).
+            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+            # self.putx([0, ['Read address, byte %d: 0x%02x' % \
+            #                (4 - self.cmdstate, mosi)]])
+            if self.cmdstate == 4:
+                self.putx([0, ['Read address: 0x%06x' % self.addr]])
+                self.addr = 0
+        elif self.cmdstate >= 5:
+            # Bytes 5-x: Master reads data bytes (until CS# de-asserted).
+            # TODO: For now we hardcode 256 bytes per READ command.
+            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
+                self.data.append(miso)
+                # self.putx([0, ['New read byte: 0x%02x' % miso]])
+
+            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
+                # s = ', '.join(map(hex, self.data))
+                s = ''.join(map(chr, self.data))
+                self.putx([0, ['Read data']])
+                self.putx([1, ['Read data: %s' % s]])
+                self.data = []
+                self.state = None
+                return
+
+        self.cmdstate += 1
+
+    def handle_fast_read(self, mosi, miso):
+        pass # TODO
+
+    def handle_2read(self, mosi, miso):
+        pass # TODO
+
+    # TODO: Warn/abort if we don't see the necessary amount of bytes.
+    # TODO: Warn if WREN was not seen before.
+    def handle_se(self, mosi, miso):
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.addr = 0
+            self.start_sample = self.ss
+            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate in (2, 3, 4):
+            # Bytes 2/3/4: Master sends sectror address (24bits, MSB-first).
+            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+            # self.putx([0, ['Sector address, byte %d: 0x%02x' % \
+            #                (4 - self.cmdstate, mosi)]])
+
+        if self.cmdstate == 4:
+            d = 'Erase sector %d (0x%06x)' % (self.addr, self.addr)
+            self.put(self.start_sample, self.es, self.out_ann, [0, [d]])
+            # TODO: Max. size depends on chip, check that too if possible.
+            if self.addr % 4096 != 0:
+                # Sector addresses must be 4K-aligned (same for all 3 chips).
+                d = 'Warning: Invalid sector address!'
+                self.put(self.start_sample, self.es, self.out_ann, [2, [d]])
+            self.state = None
+        else:
+            self.cmdstate += 1
+
+    def handle_be(self, mosi, miso):
+        pass # TODO
+
+    def handle_ce(self, mosi, miso):
+        pass # TODO
+
+    def handle_ce2(self, mosi, miso):
+        pass # TODO
+
+    def handle_pp(self, mosi, miso):
+        # Page program: Master asserts CS#, sends PP command, sends 3-byte
+        # page address, sends >= 1 data bytes, de-asserts CS#.
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate in (2, 3, 4):
+            # Bytes 2/3/4: Master sends page address (24bits, MSB-first).
+            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+            # self.putx([0, ['Page address, byte %d: 0x%02x' % \
+            #                (4 - self.cmdstate, mosi)]])
+            if self.cmdstate == 4:
+                self.putx([0, ['Page address: 0x%06x' % self.addr]])
+                self.addr = 0
+        elif self.cmdstate >= 5:
+            # Bytes 5-x: Master sends data bytes (until CS# de-asserted).
+            # TODO: For now we hardcode 256 bytes per page / PP command.
+            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
+                self.data.append(mosi)
+                # self.putx([0, ['New data byte: 0x%02x' % mosi]])
+
+            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
+                # s = ', '.join(map(hex, self.data))
+                s = ''.join(map(chr, self.data))
+                self.putx([0, ['Page data']])
+                self.putx([1, ['Page data: %s' % s]])
+                self.data = []
+                self.state = None
+                return
+
+        self.cmdstate += 1
+
+    def handle_cp(self, mosi, miso):
+        pass # TODO
+
+    def handle_dp(self, mosi, miso):
+        pass # TODO
+
+    def handle_rdp_res(self, mosi, miso):
+        pass # TODO
+
+    def handle_rems(self, mosi, miso):
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.start_sample = self.ss
+            self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate in (2, 3):
+            # Bytes 2/3: Master sends two dummy bytes.
+            # TODO: Check dummy bytes? Check reply from device?
+            self.putx([0, ['Dummy byte: %s' % mosi]])
+        elif self.cmdstate == 4:
+            # Byte 4: Master sends 0x00 or 0x01.
+            # 0x00: Master wants manufacturer ID as first reply byte.
+            # 0x01: Master wants device ID as first reply byte.
+            self.manufacturer_id_first = True if (mosi == 0x00) else False
+            d = 'manufacturer' if (mosi == 0x00) else 'device'
+            self.putx([0, ['Master wants %s ID first' % d]])
+        elif self.cmdstate == 5:
+            # Byte 5: Slave sends manufacturer ID (or device ID).
+            self.ids = [miso]
+            d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
+            self.putx([0, ['%s ID' % d]])
+        elif self.cmdstate == 6:
+            # Byte 6: Slave sends device ID (or manufacturer ID).
+            self.ids.append(miso)
+            d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
+            self.putx([0, ['%s ID' % d]])
+
+        if self.cmdstate == 6:
+            self.end_sample = self.es
+            id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
+            self.putx([0, ['Device: Macronix %s' % device_name[id]]])
+            self.state = None
+        else:
+            self.cmdstate += 1
+
+    def handle_rems2(self, mosi, miso):
+        pass # TODO
+
+    def handle_enso(self, mosi, miso):
+        pass # TODO
+
+    def handle_exso(self, mosi, miso):
+        pass # TODO
+
+    def handle_rdscur(self, mosi, miso):
+        pass # TODO
+
+    def handle_wrscur(self, mosi, miso):
+        pass # TODO
+
+    def handle_esry(self, mosi, miso):
+        pass # TODO
+
+    def handle_dsry(self, mosi, miso):
+        pass # TODO
+
+    def decode(self, ss, es, data):
+
+        ptype, mosi, miso = data
+
+        # if ptype == 'DATA':
+        #     self.putx([0, ['MOSI: 0x%02x, MISO: 0x%02x' % (mosi, miso)]])
+
+        # if ptype == 'CS-CHANGE':
+        #     if mosi == 1 and miso == 0:
+        #         self.putx([0, ['Asserting CS#']])
+        #     elif mosi == 0 and miso == 1:
+        #         self.putx([0, ['De-asserting CS#']])
+
+        if ptype != 'DATA':
+            return
+
+        self.ss, self.es = ss, es
+
+        # If we encountered a known chip command, enter the resp. state.
+        if self.state == None:
+            self.state = mosi
+            self.cmdstate = 1
+
+        # Handle commands.
+        if self.state in cmds:
+            s = 'handle_%s' % cmds[self.state][0].lower().replace('/', '_')
+            handle_reg = getattr(self, s)
+            handle_reg(mosi, miso)
+        else:
+            self.putx([0, ['Unknown command: 0x%02x' % mosi]])
+            self.state = None
+
index 891dbc10c10cbb9c9105f959aa03de782811adf9..45cd50ca0f38d732b8da74119ac5ca3a5df2abd8 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/mxc6225xu
 
 
 pkgdatadir = $(DECODERS_DIR)/mxc6225xu
 
-dist_pkgdata_DATA = __init__.py mxc6225xu.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 02b63b22583ef6227acd589bd13b50ff940b1722..3bca9e4f0d21eef641999fbb597915787e561c88 100644 (file)
@@ -30,5 +30,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .mxc6225xu import *
+from .pd import *
 
 
diff --git a/decoders/mxc6225xu/mxc6225xu.py b/decoders/mxc6225xu/mxc6225xu.py
deleted file mode 100644 (file)
index 39564a9..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# MEMSIC MXC6225XU protocol decoder
-
-import sigrokdecode as srd
-
-# Definitions of various bits in MXC6225XU registers.
-status = {
-    # SH[1:0]
-    'sh': {
-        0b00: 'none',
-        0b01: 'shake left',
-        0b10: 'shake right',
-        0b11: 'undefined',
-    },
-    # ORI[1:0] and OR[1:0] (same format)
-    'ori': {
-        0b00: 'vertical in upright orientation',
-        0b01: 'rotated 90 degrees clockwise',
-        0b10: 'vertical in inverted orientation',
-        0b11: 'rotated 90 degrees counterclockwise',
-    },
-    # SHTH[1:0]
-    'shth': {
-        0b00: '0.5g',
-        0b01: '1.0g',
-        0b10: '1.5g',
-        0b11: '2.0g',
-    },
-    # SHC[1:0]
-    'shc': {
-        0b00: '16',
-        0b01: '32',
-        0b10: '64',
-        0b11: '128',
-    },
-    # ORC[1:0]
-    'orc': {
-        0b00: '16',
-        0b01: '32',
-        0b10: '64',
-        0b11: '128',
-    },
-}
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'mxc6225xu'
-    name = 'MXC6225XU'
-    longname = 'MEMSIC MXC6225XU'
-    desc = 'Digital Thermal Orientation Sensor (DTOS) protocol.'
-    license = 'gplv2+'
-    inputs = ['i2c']
-    outputs = ['mxc6225xu']
-    probes = []
-    optional_probes = [
-        {'id': 'int', 'name': 'INT', 'desc': 'DTOS interrupt output pin'},
-    ]
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'IDLE'
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mxc6225xu')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'mxc6225xu')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def handle_reg_0x00(self, b):
-        # XOUT: 8-bit x-axis acceleration output.
-        # Data is in 2's complement, values range from -128 to 127.
-        self.putx([0, ['XOUT: %d' % b]])
-
-    def handle_reg_0x01(self, b):
-        # YOUT: 8-bit y-axis acceleration output.
-        # Data is in 2's complement, values range from -128 to 127.
-        self.putx([0, ['YOUT: %d' % b]])
-
-    def handle_reg_0x02(self, b):
-        # STATUS: Orientation and shake status.
-
-        # Bits[7:7]: INT
-        int_val = (b >> 7) & 1
-        s = 'unchanged and no' if (int_val == 0) else 'changed or'
-        ann = 'INT = %d: Orientation %s shake event occured\n' % (int_val, s)
-
-        # Bits[6:5]: SH[1:0]
-        sh = (((b >> 6) & 1) << 1) | ((b >> 5) & 1)
-        ann += 'SH[1:0] = %s: Shake event: %s\n' % \
-               (bin(sh)[2:], status['sh'][sh])
-
-        # Bits[4:4]: TILT
-        tilt = (b >> 4) & 1
-        s = '' if (tilt == 0) else 'not '
-        ann += 'TILT = %d: Orientation measurement is %svalid\n' % (tilt, s)
-
-        # Bits[3:2]: ORI[1:0]
-        ori = (((b >> 3) & 1) << 1) | ((b >> 2) & 1)
-        ann += 'ORI[1:0] = %s: %s\n' % (bin(ori)[2:], status['ori'][ori])
-
-        # Bits[1:0]: OR[1:0]
-        or_val = (((b >> 1) & 1) << 1) | ((b >> 0) & 1)
-        ann += 'OR[1:0] = %s: %s\n' % (bin(or_val)[2:], status['ori'][or_val])
-
-        # ann += 'b = %s\n' % (bin(b))
-
-        self.putx([0, [ann]])
-
-    def handle_reg_0x03(self, b):
-        # DETECTION: Powerdown, orientation and shake detection parameters.
-        # Note: This is a write-only register.
-
-        # Bits[7:7]: PD
-        pd = (b >> 7) & 1
-        s = 'Do not power down' if (pd == 0) else 'Power down'
-        ann = 'PD = %d: %s the device (into a low-power state)\n' % (pd, s)
-
-        # Bits[6:6]: SHM
-        shm = (b >> 6) & 1
-        ann = 'SHM = %d: Set shake mode to %d\n' % (shm, shm)
-
-        # Bits[5:4]: SHTH[1:0]
-        shth = (((b >> 5) & 1) << 1) | ((b >> 4) & 1)
-        ann += 'SHTH[1:0] = %s: Set shake threshold to %s\n' \
-               % (bin(shth)[2:], status['shth'][shth])
-
-        # Bits[3:2]: SHC[1:0]
-        shc = (((b >> 3) & 1) << 1) | ((b >> 2) & 1)
-        ann += 'SHC[1:0] = %s: Set shake count to %s readings\n' \
-               % (bin(shc)[2:], status['shc'][shc])
-
-        # Bits[1:0]: ORC[1:0]
-        orc = (((b >> 1) & 1) << 1) | ((b >> 0) & 1)
-        ann += 'ORC[1:0] = %s: Set orientation count to %s readings\n' \
-               % (bin(orc)[2:], status['orc'][orc])
-
-        self.putx([0, [ann]])
-
-    # TODO: Fixup, this is copy-pasted from another PD.
-    # TODO: Handle/check the ACKs/NACKs.
-    def decode(self, ss, es, data):
-        cmd, databyte = data
-
-        # Store the start/end samples of this I2C packet.
-        self.ss, self.es = ss, es
-
-        # State machine.
-        if self.state == 'IDLE':
-            # Wait for an I2C START condition.
-            if cmd != 'START':
-                return
-            self.state = 'GET SLAVE ADDR'
-            self.block_start_sample = ss
-        elif self.state == 'GET SLAVE ADDR':
-            # Wait for an address write operation.
-            # TODO: We should only handle packets to the slave(?)
-            if cmd != 'ADDRESS WRITE':
-                return
-            self.state = 'GET REG ADDR'
-        elif self.state == 'GET REG ADDR':
-            # Wait for a data write (master selects the slave register).
-            if cmd != 'DATA WRITE':
-                return
-            self.reg = databyte
-            self.state = 'WRITE REGS'
-        elif self.state == 'WRITE REGS':
-            # If we see a Repeated Start here, it's a multi-byte read.
-            if cmd == 'START REPEAT':
-                self.state = 'READ REGS'
-                return
-            # Otherwise: Get data bytes until a STOP condition occurs.
-            if cmd == 'DATA WRITE':
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte)
-                self.reg += 1
-                # TODO: Check for NACK!
-            elif cmd == 'STOP':
-                # TODO
-                self.state = 'IDLE'
-            else:
-                pass # TODO
-        elif self.state == 'READ REGS':
-            # Wait for an address read operation.
-            # TODO: We should only handle packets to the slave(?)
-            if cmd == 'ADDRESS READ':
-                self.state = 'READ REGS2'
-                return
-            else:
-                pass # TODO
-        elif self.state == 'READ REGS2':
-            if cmd == 'DATA READ':
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte)
-                self.reg += 1
-                # TODO: Check for NACK!
-            elif cmd == 'STOP':
-                # TODO
-                self.state = 'IDLE'
-            else:
-                pass # TODO?
-        else:
-            raise Exception('Invalid state: %d' % self.state)
-
diff --git a/decoders/mxc6225xu/pd.py b/decoders/mxc6225xu/pd.py
new file mode 100644 (file)
index 0000000..39564a9
--- /dev/null
@@ -0,0 +1,229 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# MEMSIC MXC6225XU protocol decoder
+
+import sigrokdecode as srd
+
+# Definitions of various bits in MXC6225XU registers.
+status = {
+    # SH[1:0]
+    'sh': {
+        0b00: 'none',
+        0b01: 'shake left',
+        0b10: 'shake right',
+        0b11: 'undefined',
+    },
+    # ORI[1:0] and OR[1:0] (same format)
+    'ori': {
+        0b00: 'vertical in upright orientation',
+        0b01: 'rotated 90 degrees clockwise',
+        0b10: 'vertical in inverted orientation',
+        0b11: 'rotated 90 degrees counterclockwise',
+    },
+    # SHTH[1:0]
+    'shth': {
+        0b00: '0.5g',
+        0b01: '1.0g',
+        0b10: '1.5g',
+        0b11: '2.0g',
+    },
+    # SHC[1:0]
+    'shc': {
+        0b00: '16',
+        0b01: '32',
+        0b10: '64',
+        0b11: '128',
+    },
+    # ORC[1:0]
+    'orc': {
+        0b00: '16',
+        0b01: '32',
+        0b10: '64',
+        0b11: '128',
+    },
+}
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'mxc6225xu'
+    name = 'MXC6225XU'
+    longname = 'MEMSIC MXC6225XU'
+    desc = 'Digital Thermal Orientation Sensor (DTOS) protocol.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['mxc6225xu']
+    probes = []
+    optional_probes = [
+        {'id': 'int', 'name': 'INT', 'desc': 'DTOS interrupt output pin'},
+    ]
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'IDLE'
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'mxc6225xu')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'mxc6225xu')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def handle_reg_0x00(self, b):
+        # XOUT: 8-bit x-axis acceleration output.
+        # Data is in 2's complement, values range from -128 to 127.
+        self.putx([0, ['XOUT: %d' % b]])
+
+    def handle_reg_0x01(self, b):
+        # YOUT: 8-bit y-axis acceleration output.
+        # Data is in 2's complement, values range from -128 to 127.
+        self.putx([0, ['YOUT: %d' % b]])
+
+    def handle_reg_0x02(self, b):
+        # STATUS: Orientation and shake status.
+
+        # Bits[7:7]: INT
+        int_val = (b >> 7) & 1
+        s = 'unchanged and no' if (int_val == 0) else 'changed or'
+        ann = 'INT = %d: Orientation %s shake event occured\n' % (int_val, s)
+
+        # Bits[6:5]: SH[1:0]
+        sh = (((b >> 6) & 1) << 1) | ((b >> 5) & 1)
+        ann += 'SH[1:0] = %s: Shake event: %s\n' % \
+               (bin(sh)[2:], status['sh'][sh])
+
+        # Bits[4:4]: TILT
+        tilt = (b >> 4) & 1
+        s = '' if (tilt == 0) else 'not '
+        ann += 'TILT = %d: Orientation measurement is %svalid\n' % (tilt, s)
+
+        # Bits[3:2]: ORI[1:0]
+        ori = (((b >> 3) & 1) << 1) | ((b >> 2) & 1)
+        ann += 'ORI[1:0] = %s: %s\n' % (bin(ori)[2:], status['ori'][ori])
+
+        # Bits[1:0]: OR[1:0]
+        or_val = (((b >> 1) & 1) << 1) | ((b >> 0) & 1)
+        ann += 'OR[1:0] = %s: %s\n' % (bin(or_val)[2:], status['ori'][or_val])
+
+        # ann += 'b = %s\n' % (bin(b))
+
+        self.putx([0, [ann]])
+
+    def handle_reg_0x03(self, b):
+        # DETECTION: Powerdown, orientation and shake detection parameters.
+        # Note: This is a write-only register.
+
+        # Bits[7:7]: PD
+        pd = (b >> 7) & 1
+        s = 'Do not power down' if (pd == 0) else 'Power down'
+        ann = 'PD = %d: %s the device (into a low-power state)\n' % (pd, s)
+
+        # Bits[6:6]: SHM
+        shm = (b >> 6) & 1
+        ann = 'SHM = %d: Set shake mode to %d\n' % (shm, shm)
+
+        # Bits[5:4]: SHTH[1:0]
+        shth = (((b >> 5) & 1) << 1) | ((b >> 4) & 1)
+        ann += 'SHTH[1:0] = %s: Set shake threshold to %s\n' \
+               % (bin(shth)[2:], status['shth'][shth])
+
+        # Bits[3:2]: SHC[1:0]
+        shc = (((b >> 3) & 1) << 1) | ((b >> 2) & 1)
+        ann += 'SHC[1:0] = %s: Set shake count to %s readings\n' \
+               % (bin(shc)[2:], status['shc'][shc])
+
+        # Bits[1:0]: ORC[1:0]
+        orc = (((b >> 1) & 1) << 1) | ((b >> 0) & 1)
+        ann += 'ORC[1:0] = %s: Set orientation count to %s readings\n' \
+               % (bin(orc)[2:], status['orc'][orc])
+
+        self.putx([0, [ann]])
+
+    # TODO: Fixup, this is copy-pasted from another PD.
+    # TODO: Handle/check the ACKs/NACKs.
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+
+        # Store the start/end samples of this I2C packet.
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if self.state == 'IDLE':
+            # Wait for an I2C START condition.
+            if cmd != 'START':
+                return
+            self.state = 'GET SLAVE ADDR'
+            self.block_start_sample = ss
+        elif self.state == 'GET SLAVE ADDR':
+            # Wait for an address write operation.
+            # TODO: We should only handle packets to the slave(?)
+            if cmd != 'ADDRESS WRITE':
+                return
+            self.state = 'GET REG ADDR'
+        elif self.state == 'GET REG ADDR':
+            # Wait for a data write (master selects the slave register).
+            if cmd != 'DATA WRITE':
+                return
+            self.reg = databyte
+            self.state = 'WRITE REGS'
+        elif self.state == 'WRITE REGS':
+            # If we see a Repeated Start here, it's a multi-byte read.
+            if cmd == 'START REPEAT':
+                self.state = 'READ REGS'
+                return
+            # Otherwise: Get data bytes until a STOP condition occurs.
+            if cmd == 'DATA WRITE':
+                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
+                handle_reg(databyte)
+                self.reg += 1
+                # TODO: Check for NACK!
+            elif cmd == 'STOP':
+                # TODO
+                self.state = 'IDLE'
+            else:
+                pass # TODO
+        elif self.state == 'READ REGS':
+            # Wait for an address read operation.
+            # TODO: We should only handle packets to the slave(?)
+            if cmd == 'ADDRESS READ':
+                self.state = 'READ REGS2'
+                return
+            else:
+                pass # TODO
+        elif self.state == 'READ REGS2':
+            if cmd == 'DATA READ':
+                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
+                handle_reg(databyte)
+                self.reg += 1
+                # TODO: Check for NACK!
+            elif cmd == 'STOP':
+                # TODO
+                self.state = 'IDLE'
+            else:
+                pass # TODO?
+        else:
+            raise Exception('Invalid state: %d' % self.state)
+
index 5a4917b786dc533280cec7dfce5f7b5960ea0ed2..7e3752226bc59bc62448e2ffa439807a2ebb30f7 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/nunchuk
 
 
 pkgdatadir = $(DECODERS_DIR)/nunchuk
 
-dist_pkgdata_DATA = __init__.py nunchuk.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 9b85374729718dd84e0b78ca19d21616c0697224..ac8e64b787447fa5db7f953414adaeef599ff4c1 100644 (file)
@@ -29,5 +29,5 @@ http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/
 https://www.sparkfun.com/products/9281
 '''
 
 https://www.sparkfun.com/products/9281
 '''
 
-from .nunchuk import *
+from .pd import *
 
 
diff --git a/decoders/nunchuk/nunchuk.py b/decoders/nunchuk/nunchuk.py
deleted file mode 100644 (file)
index 0dae39c..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Nintendo Wii Nunchuk protocol decoder
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'nunchuk'
-    name = 'Nunchuk'
-    longname = 'Nintendo Wii Nunchuk'
-    desc = 'Nintendo Wii Nunchuk controller protocol.'
-    license = 'gplv2+'
-    inputs = ['i2c']
-    outputs = ['nunchuck']
-    probes = []
-    optional_probes = []
-    options = {}
-    annotations = [
-        ['Text (verbose)', 'Human-readable text (verbose)'],
-        ['Text', 'Human-readable text'],
-        ['Warnings', 'Human-readable warnings'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'IDLE'
-        self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1
-        self.databytecount = 0
-        self.reg = 0x00
-        self.init_seq = []
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'nunchuk')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'nunchuk')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        # Helper for annotations which span exactly one I2C packet.
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def putb(self, data):
-        # Helper for annotations which span a block of I2C packets.
-        self.put(self.block_start_sample, self.block_end_sample,
-                 self.out_ann, data)
-
-    def handle_reg_0x00(self, databyte):
-        self.sx = databyte
-        self.putx([0, ['Analog stick X position: 0x%02x' % self.sx]])
-        self.putx([1, ['SX: 0x%02x' % self.sx]])
-
-    def handle_reg_0x01(self, databyte):
-        self.sy = databyte
-        self.putx([0, ['Analog stick Y position: 0x%02x' % self.sy]])
-        self.putx([1, ['SY: 0x%02x' % self.sy]])
-
-    def handle_reg_0x02(self, databyte):
-        self.ax = databyte << 2
-        self.putx([0, ['Accelerometer X value bits[9:2]: 0x%03x' % self.ax]])
-        self.putx([1, ['AX[9:2]: 0x%03x' % self.ax]])
-
-    def handle_reg_0x03(self, databyte):
-        self.ay = databyte << 2
-        self.putx([0, ['Accelerometer Y value bits[9:2]: 0x%03x' % self.ay]])
-        self.putx([1, ['AY[9:2]: 0x%x' % self.ay]])
-
-    def handle_reg_0x04(self, databyte):
-        self.az = databyte << 2
-        self.putx([0, ['Accelerometer Z value bits[9:2]: 0x%03x' % self.az]])
-        self.putx([1, ['AZ[9:2]: 0x%x' % self.az]])
-
-    # TODO: Bit-exact annotations.
-    def handle_reg_0x05(self, databyte):
-        self.bz = (databyte & (1 << 0)) >> 0 # Bits[0:0]
-        self.bc = (databyte & (1 << 1)) >> 1 # Bits[1:1]
-        ax_rest = (databyte & (3 << 2)) >> 2 # Bits[3:2]
-        ay_rest = (databyte & (3 << 4)) >> 4 # Bits[5:4]
-        az_rest = (databyte & (3 << 6)) >> 6 # Bits[7:6]
-        self.ax |= ax_rest
-        self.ay |= ay_rest
-        self.az |= az_rest
-
-        s = '' if (self.bz == 0) else 'not '
-        self.putx([0, ['Z button: %spressed' % s]])
-        self.putx([1, ['BZ: %d' % self.bz]])
-
-        s = '' if (self.bc == 0) else 'not '
-        self.putx([0, ['C button: %spressed' % s]])
-        self.putx([1, ['BC: %d' % self.bc]])
-
-        self.putx([0, ['Accelerometer X value bits[1:0]: 0x%x' % ax_rest]])
-        self.putx([1, ['AX[1:0]: 0x%x' % ax_rest]])
-
-        self.putx([0, ['Accelerometer Y value bits[1:0]: 0x%x' % ay_rest]])
-        self.putx([1, ['AY[1:0]: 0x%x' % ay_rest]])
-
-        self.putx([0, ['Accelerometer Z value bits[1:0]: 0x%x' % az_rest]])
-        self.putx([1, ['AZ[1:0]: 0x%x' % az_rest]])
-
-    def output_full_block_if_possible(self):
-        # For now, only output summary annotation if all values are available.
-        t = (self.sx, self.sy, self.ax, self.ay, self.az, self.bz, self.bc)
-        if -1 in t:
-            return
-
-        s = 'Analog stick X position: 0x%02x\n' % self.sx
-        s += 'Analog stick Y position: 0x%02x\n' % self.sy
-        s += 'Z button: %spressed\n' % ('' if (self.bz == 0) else 'not ')
-        s += 'C button: %spressed\n' % ('' if (self.bc == 0) else 'not ')
-        s += 'Accelerometer X value: 0x%03x\n' % self.ax
-        s += 'Accelerometer Y value: 0x%03x\n' % self.ay
-        s += 'Accelerometer Z value: 0x%03x\n' % self.az
-        self.put(self.block_start_sample, self.block_end_sample,
-                 self.out_ann, [0, [s]])
-
-        s = 'SX = 0x%02x, SY = 0x%02x, AX = 0x%02x, AY = 0x%02x, ' \
-            'AZ = 0x%02x, BZ = 0x%02x, BC = 0x%02x' % (self.sx, \
-            self.sy, self.ax, self.ay, self.az, self.bz, self.bc)
-        self.put(self.block_start_sample, self.block_end_sample,
-                 self.out_ann, [1, [s]])
-
-    def handle_reg_write(self, databyte):
-        self.putx([0, ['Nunchuk write: 0x%02x' % databyte]])
-        if len(self.init_seq) < 2:
-            self.init_seq.append(databyte)
-
-    def output_init_seq(self):
-        if len(self.init_seq) != 2:
-            self.putb([2, ['Init sequence was %d bytes long (2 expected)' % \
-                      len(self.init_seq)]])
-
-        if self.init_seq != (0x40, 0x00):
-            self.putb([2, ['Unknown init sequence (expected: 0x40 0x00)']])
-
-        # TODO: Detect Nunchuk clones (they have different init sequences).
-        s = 'Initialized Nintendo Wii Nunchuk'
-
-        self.putb([0, [s]])
-        self.putb([1, ['INIT']])
-
-    def decode(self, ss, es, data):
-        cmd, databyte = data
-
-        # Store the start/end samples of this I2C packet.
-        self.ss, self.es = ss, es
-
-        # State machine.
-        if self.state == 'IDLE':
-            # Wait for an I2C START condition.
-            if cmd != 'START':
-                return
-            self.state = 'GET SLAVE ADDR'
-            self.block_start_sample = ss
-        elif self.state == 'GET SLAVE ADDR':
-            # Wait for an address read/write operation.
-            if cmd == 'ADDRESS READ':
-                self.state = 'READ REGS'
-            elif cmd == 'ADDRESS WRITE':
-                self.state = 'WRITE REGS'
-        elif self.state == 'READ REGS':
-            if cmd == 'DATA READ':
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte)
-                self.reg += 1
-            elif cmd == 'STOP':
-                self.block_end_sample = es
-                self.output_full_block_if_possible()
-                self.sx = self.sy = self.ax = self.ay = self.az = -1
-                self.bz = self.bc = -1
-                self.state = 'IDLE'
-            else:
-                # self.putx([0, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
-                pass
-        elif self.state == 'WRITE REGS':
-            if cmd == 'DATA WRITE':
-                self.handle_reg_write(databyte)
-            elif cmd == 'STOP':
-                self.block_end_sample = es
-                self.output_init_seq()
-                self.init_seq = []
-                self.state = 'IDLE'
-            else:
-                # self.putx([0, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
-                pass
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/nunchuk/pd.py b/decoders/nunchuk/pd.py
new file mode 100644 (file)
index 0000000..0dae39c
--- /dev/null
@@ -0,0 +1,206 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2010-2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Nintendo Wii Nunchuk protocol decoder
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'nunchuk'
+    name = 'Nunchuk'
+    longname = 'Nintendo Wii Nunchuk'
+    desc = 'Nintendo Wii Nunchuk controller protocol.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['nunchuck']
+    probes = []
+    optional_probes = []
+    options = {}
+    annotations = [
+        ['Text (verbose)', 'Human-readable text (verbose)'],
+        ['Text', 'Human-readable text'],
+        ['Warnings', 'Human-readable warnings'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'IDLE'
+        self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1
+        self.databytecount = 0
+        self.reg = 0x00
+        self.init_seq = []
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'nunchuk')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'nunchuk')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        # Helper for annotations which span exactly one I2C packet.
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def putb(self, data):
+        # Helper for annotations which span a block of I2C packets.
+        self.put(self.block_start_sample, self.block_end_sample,
+                 self.out_ann, data)
+
+    def handle_reg_0x00(self, databyte):
+        self.sx = databyte
+        self.putx([0, ['Analog stick X position: 0x%02x' % self.sx]])
+        self.putx([1, ['SX: 0x%02x' % self.sx]])
+
+    def handle_reg_0x01(self, databyte):
+        self.sy = databyte
+        self.putx([0, ['Analog stick Y position: 0x%02x' % self.sy]])
+        self.putx([1, ['SY: 0x%02x' % self.sy]])
+
+    def handle_reg_0x02(self, databyte):
+        self.ax = databyte << 2
+        self.putx([0, ['Accelerometer X value bits[9:2]: 0x%03x' % self.ax]])
+        self.putx([1, ['AX[9:2]: 0x%03x' % self.ax]])
+
+    def handle_reg_0x03(self, databyte):
+        self.ay = databyte << 2
+        self.putx([0, ['Accelerometer Y value bits[9:2]: 0x%03x' % self.ay]])
+        self.putx([1, ['AY[9:2]: 0x%x' % self.ay]])
+
+    def handle_reg_0x04(self, databyte):
+        self.az = databyte << 2
+        self.putx([0, ['Accelerometer Z value bits[9:2]: 0x%03x' % self.az]])
+        self.putx([1, ['AZ[9:2]: 0x%x' % self.az]])
+
+    # TODO: Bit-exact annotations.
+    def handle_reg_0x05(self, databyte):
+        self.bz = (databyte & (1 << 0)) >> 0 # Bits[0:0]
+        self.bc = (databyte & (1 << 1)) >> 1 # Bits[1:1]
+        ax_rest = (databyte & (3 << 2)) >> 2 # Bits[3:2]
+        ay_rest = (databyte & (3 << 4)) >> 4 # Bits[5:4]
+        az_rest = (databyte & (3 << 6)) >> 6 # Bits[7:6]
+        self.ax |= ax_rest
+        self.ay |= ay_rest
+        self.az |= az_rest
+
+        s = '' if (self.bz == 0) else 'not '
+        self.putx([0, ['Z button: %spressed' % s]])
+        self.putx([1, ['BZ: %d' % self.bz]])
+
+        s = '' if (self.bc == 0) else 'not '
+        self.putx([0, ['C button: %spressed' % s]])
+        self.putx([1, ['BC: %d' % self.bc]])
+
+        self.putx([0, ['Accelerometer X value bits[1:0]: 0x%x' % ax_rest]])
+        self.putx([1, ['AX[1:0]: 0x%x' % ax_rest]])
+
+        self.putx([0, ['Accelerometer Y value bits[1:0]: 0x%x' % ay_rest]])
+        self.putx([1, ['AY[1:0]: 0x%x' % ay_rest]])
+
+        self.putx([0, ['Accelerometer Z value bits[1:0]: 0x%x' % az_rest]])
+        self.putx([1, ['AZ[1:0]: 0x%x' % az_rest]])
+
+    def output_full_block_if_possible(self):
+        # For now, only output summary annotation if all values are available.
+        t = (self.sx, self.sy, self.ax, self.ay, self.az, self.bz, self.bc)
+        if -1 in t:
+            return
+
+        s = 'Analog stick X position: 0x%02x\n' % self.sx
+        s += 'Analog stick Y position: 0x%02x\n' % self.sy
+        s += 'Z button: %spressed\n' % ('' if (self.bz == 0) else 'not ')
+        s += 'C button: %spressed\n' % ('' if (self.bc == 0) else 'not ')
+        s += 'Accelerometer X value: 0x%03x\n' % self.ax
+        s += 'Accelerometer Y value: 0x%03x\n' % self.ay
+        s += 'Accelerometer Z value: 0x%03x\n' % self.az
+        self.put(self.block_start_sample, self.block_end_sample,
+                 self.out_ann, [0, [s]])
+
+        s = 'SX = 0x%02x, SY = 0x%02x, AX = 0x%02x, AY = 0x%02x, ' \
+            'AZ = 0x%02x, BZ = 0x%02x, BC = 0x%02x' % (self.sx, \
+            self.sy, self.ax, self.ay, self.az, self.bz, self.bc)
+        self.put(self.block_start_sample, self.block_end_sample,
+                 self.out_ann, [1, [s]])
+
+    def handle_reg_write(self, databyte):
+        self.putx([0, ['Nunchuk write: 0x%02x' % databyte]])
+        if len(self.init_seq) < 2:
+            self.init_seq.append(databyte)
+
+    def output_init_seq(self):
+        if len(self.init_seq) != 2:
+            self.putb([2, ['Init sequence was %d bytes long (2 expected)' % \
+                      len(self.init_seq)]])
+
+        if self.init_seq != (0x40, 0x00):
+            self.putb([2, ['Unknown init sequence (expected: 0x40 0x00)']])
+
+        # TODO: Detect Nunchuk clones (they have different init sequences).
+        s = 'Initialized Nintendo Wii Nunchuk'
+
+        self.putb([0, [s]])
+        self.putb([1, ['INIT']])
+
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+
+        # Store the start/end samples of this I2C packet.
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if self.state == 'IDLE':
+            # Wait for an I2C START condition.
+            if cmd != 'START':
+                return
+            self.state = 'GET SLAVE ADDR'
+            self.block_start_sample = ss
+        elif self.state == 'GET SLAVE ADDR':
+            # Wait for an address read/write operation.
+            if cmd == 'ADDRESS READ':
+                self.state = 'READ REGS'
+            elif cmd == 'ADDRESS WRITE':
+                self.state = 'WRITE REGS'
+        elif self.state == 'READ REGS':
+            if cmd == 'DATA READ':
+                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
+                handle_reg(databyte)
+                self.reg += 1
+            elif cmd == 'STOP':
+                self.block_end_sample = es
+                self.output_full_block_if_possible()
+                self.sx = self.sy = self.ax = self.ay = self.az = -1
+                self.bz = self.bc = -1
+                self.state = 'IDLE'
+            else:
+                # self.putx([0, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
+                pass
+        elif self.state == 'WRITE REGS':
+            if cmd == 'DATA WRITE':
+                self.handle_reg_write(databyte)
+            elif cmd == 'STOP':
+                self.block_end_sample = es
+                self.output_init_seq()
+                self.init_seq = []
+                self.state = 'IDLE'
+            else:
+                # self.putx([0, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
+                pass
+        else:
+            raise Exception('Invalid state: %s' % self.state)
+
index b043eca77fde0b5c6518b273f2cb2a6849b85bbf..f949f4c14a815c5093af84324ca8bd05bbc48cf1 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/onewire_link
 
 
 pkgdatadir = $(DECODERS_DIR)/onewire_link
 
-dist_pkgdata_DATA = __init__.py onewire_link.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index cb0347170958b385212fa5960d13bda3f844d234..c1331c6322d1d6988170816f67c8896c3d9f47a9 100644 (file)
@@ -87,5 +87,5 @@ TODO:
 - Maybe add support for interrupts, check if this feature is deprecated.
 '''
 
 - Maybe add support for interrupts, check if this feature is deprecated.
 '''
 
-from .onewire_link import *
+from .pd import *
 
 
diff --git a/decoders/onewire_link/onewire_link.py b/decoders/onewire_link/onewire_link.py
deleted file mode 100644 (file)
index c8bda56..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# 1-Wire protocol decoder (link layer)
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'onewire_link'
-    name = '1-Wire link layer'
-    longname = '1-Wire serial communication bus (link layer)'
-    desc = 'Bidirectional, half-duplex, asynchronous serial bus.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['onewire_link']
-    probes = [
-        {'id': 'owr', 'name': 'OWR', 'desc': '1-Wire signal line'},
-    ]
-    optional_probes = [
-        {'id': 'pwr', 'name': 'PWR', 'desc': '1-Wire power supply pin'},
-    ]
-    options = {
-        'overdrive': ['Overdrive', 1],
-        # Time options (specified in number of samplerate periods):
-        'cnt_normal_bit': ['Normal mode sample bit time', 0],
-        'cnt_normal_slot': ['Normal mode data slot time', 0],
-        'cnt_normal_presence': ['Normal mode sample presence time', 0],
-        'cnt_normal_reset': ['Normal mode reset time', 0],
-        'cnt_overdrive_bit': ['Overdrive mode sample bit time', 0],
-        'cnt_overdrive_slot': ['Overdrive mode data slot time', 0],
-        'cnt_overdrive_presence': ['Overdrive mode sample presence time', 0],
-        'cnt_overdrive_reset': ['Overdrive mode reset time', 0],
-    }
-    annotations = [
-        ['Text', 'Human-readable text'],
-        ['Warnings', 'Human-readable warnings'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.samplenum = 0
-        self.state = 'WAIT FOR FALLING EDGE'
-        self.present = 0
-        self.bit = 0
-        self.bit_cnt = 0
-        self.command = 0
-        self.overdrive = 0
-        self.fall = 0
-        self.rise = 0
-
-    def start(self, metadata):
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'onewire_link')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'onewire_link')
-
-        self.samplerate = metadata['samplerate']
-
-        # Check if samplerate is appropriate.
-        if self.options['overdrive']:
-            if self.samplerate < 2000000:
-                self.put(0, 0, self.out_ann, [1,
-                    ['ERROR: Sampling rate is too low. Must be above 2MHz ' +
-                     'for proper overdrive mode decoding.']])
-            elif self.samplerate < 5000000:
-                self.put(0, 0, self.out_ann, [1,
-                  ['WARNING: Sampling rate is suggested to be above 5MHz ' +
-                   'for proper overdrive mode decoding.']])
-        else:
-            if self.samplerate < 400000:
-                self.put(0, 0, self.out_ann, [1,
-                    ['ERROR: Sampling rate is too low. Must be above ' +
-                     '400kHz for proper normal mode decoding.']])
-            elif (self.samplerate < 1000000):
-                self.put(0, 0, self.out_ann, [1,
-                    ['WARNING: Sampling rate is suggested to be above ' +
-                     '1MHz for proper normal mode decoding.']])
-
-        # The default 1-Wire time base is 30us. This is used to calculate
-        # sampling times.
-        samplerate = float(self.samplerate)
-        if self.options['cnt_normal_bit']:
-            self.cnt_normal_bit = self.options['cnt_normal_bit']
-        else:
-            self.cnt_normal_bit = int(samplerate * 0.000015) - 1 # 15ns
-        if self.options['cnt_normal_slot']:
-            self.cnt_normal_slot = self.options['cnt_normal_slot']
-        else:
-            self.cnt_normal_slot = int(samplerate * 0.000060) - 1 # 60ns
-        if self.options['cnt_normal_presence']:
-            self.cnt_normal_presence = self.options['cnt_normal_presence']
-        else:
-            self.cnt_normal_presence = int(samplerate * 0.000075) - 1 # 75ns
-        if self.options['cnt_normal_reset']:
-            self.cnt_normal_reset = self.options['cnt_normal_reset']
-        else:
-            self.cnt_normal_reset = int(samplerate * 0.000480) - 1 # 480ns
-        if self.options['cnt_overdrive_bit']:
-            self.cnt_overdrive_bit = self.options['cnt_overdrive_bit']
-        else:
-            self.cnt_overdrive_bit = int(samplerate * 0.000002) - 1 # 2ns
-        if self.options['cnt_overdrive_slot']:
-            self.cnt_overdrive_slot = self.options['cnt_overdrive_slot']
-        else:
-            self.cnt_overdrive_slot = int(samplerate * 0.0000073) - 1 # 6ns+1.3ns
-        if self.options['cnt_overdrive_presence']:
-            self.cnt_overdrive_presence = self.options['cnt_overdrive_presence']
-        else:
-            self.cnt_overdrive_presence = int(samplerate * 0.000010) - 1 # 10ns
-        if self.options['cnt_overdrive_reset']:
-            self.cnt_overdrive_reset = self.options['cnt_overdrive_reset']
-        else:
-            self.cnt_overdrive_reset = int(samplerate * 0.000048) - 1 # 48ns
-
-        # Organize values into lists.
-        self.cnt_bit = [self.cnt_normal_bit, self.cnt_overdrive_bit]
-        self.cnt_presence = [self.cnt_normal_presence, self.cnt_overdrive_presence]
-        self.cnt_reset = [self.cnt_normal_reset, self.cnt_overdrive_reset]
-        self.cnt_slot = [self.cnt_normal_slot, self.cnt_overdrive_slot]
-
-        # Check if sample times are in the allowed range.
-
-        time_min = float(self.cnt_normal_bit) / self.samplerate
-        time_max = float(self.cnt_normal_bit + 1) / self.samplerate
-        if (time_min < 0.000005) or (time_max > 0.000015):
-            self.put(0, 0, self.out_ann, [1,
-                ['WARNING: The normal mode data sample time interval ' +
-                 '(%2.1fus-%2.1fus) should be inside (5.0us, 15.0us).'
-                 % (time_min * 1000000, time_max * 1000000)]])
-
-        time_min = float(self.cnt_normal_presence) / self.samplerate
-        time_max = float(self.cnt_normal_presence + 1) / self.samplerate
-        if (time_min < 0.0000681) or (time_max > 0.000075):
-            self.put(0, 0, self.out_ann, [1,
-                ['WARNING: The normal mode presence sample time interval ' +
-                 '(%2.1fus-%2.1fus) should be inside (68.1us, 75.0us).'
-                 % (time_min * 1000000, time_max * 1000000)]])
-
-        time_min = float(self.cnt_overdrive_bit) / self.samplerate
-        time_max = float(self.cnt_overdrive_bit + 1) / self.samplerate
-        if (time_min < 0.000001) or (time_max > 0.000002):
-            self.put(0, 0, self.out_ann, [1,
-                ['WARNING: The overdrive mode data sample time interval ' +
-                 '(%2.1fus-%2.1fus) should be inside (1.0us, 2.0us).'
-                 % (time_min * 1000000, time_max * 1000000)]])
-
-        time_min = float(self.cnt_overdrive_presence) / self.samplerate
-        time_max = float(self.cnt_overdrive_presence + 1) / self.samplerate
-        if (time_min < 0.0000073) or (time_max > 0.000010):
-            self.put(0, 0, self.out_ann, [1,
-                ['WARNING: The overdrive mode presence sample time interval ' +
-                 '(%2.1fus-%2.1fus) should be inside (7.3us, 10.0us).'
-                 % (time_min*1000000, time_max*1000000)]])
-
-    def report(self):
-        pass
-
-    def decode(self, ss, es, data):
-        for (self.samplenum, (owr, pwr)) in data:
-            # State machine.
-            if self.state == 'WAIT FOR FALLING EDGE':
-                # The start of a cycle is a falling edge.
-                if owr != 0:
-                    continue
-                # Save the sample number for the falling edge.
-                self.fall = self.samplenum
-                # Go to waiting for sample time.
-                self.state = 'WAIT FOR DATA SAMPLE'
-            elif self.state == 'WAIT FOR DATA SAMPLE':
-                # Sample data bit.
-                t = self.samplenum - self.fall
-                if t == self.cnt_bit[self.overdrive]:
-                    self.bit = owr
-                    self.state = 'WAIT FOR DATA SLOT END'
-            elif self.state == 'WAIT FOR DATA SLOT END':
-                # A data slot ends in a recovery period, otherwise, this is
-                # probably a reset.
-                t = self.samplenum - self.fall
-                if t != self.cnt_slot[self.overdrive]:
-                    continue
-
-                if owr == 0:
-                    # This seems to be a reset slot, wait for its end.
-                    self.state = 'WAIT FOR RISING EDGE'
-                    continue
-
-                self.put(self.fall, self.samplenum, self.out_ann,
-                         [0, ['Bit: %d' % self.bit]])
-                self.put(self.fall, self.samplenum, self.out_proto,
-                         ['BIT', self.bit])
-
-                # Checking the first command to see if overdrive mode
-                # should be entered.
-                if self.bit_cnt <= 8:
-                    self.command |= (self.bit << self.bit_cnt)
-                elif self.bit_cnt == 8 and self.command in [0x3c, 0x69]:
-                    self.put(self.fall, self.cnt_bit[self.overdrive],
-                             self.out_ann, [0, ['Entering overdrive mode']])
-                # Increment the bit counter.
-                self.bit_cnt += 1
-                # Wait for next slot.
-                self.state = 'WAIT FOR FALLING EDGE'
-            elif self.state == 'WAIT FOR RISING EDGE':
-                # The end of a cycle is a rising edge.
-                if owr != 1:
-                    continue
-
-                # Check if this was a reset cycle.
-                t = self.samplenum - self.fall
-                if t > self.cnt_normal_reset:
-                    # Save the sample number for the falling edge.
-                    self.rise = self.samplenum
-                    self.state = 'WAIT FOR PRESENCE DETECT'
-                    # Exit overdrive mode.
-                    if self.overdrive:
-                        self.put(self.fall, self.cnt_bit[self.overdrive],
-                                 self.out_ann, [0, ['Exiting overdrive mode']])
-                        self.overdrive = 0
-                    # Clear command bit counter and data register.
-                    self.bit_cnt = 0
-                    self.command = 0
-                elif (t > self.cnt_overdrive_reset) and self.overdrive:
-                    # Save the sample number for the falling edge.
-                    self.rise = self.samplenum
-                    self.state = "WAIT FOR PRESENCE DETECT"
-                # Otherwise this is assumed to be a data bit.
-                else:
-                    self.state = "WAIT FOR FALLING EDGE"
-            elif self.state == 'WAIT FOR PRESENCE DETECT':
-                # Sample presence status.
-                t = self.samplenum - self.rise
-                if t == self.cnt_presence[self.overdrive]:
-                    self.present = owr
-                    self.state = 'WAIT FOR RESET SLOT END'
-            elif self.state == 'WAIT FOR RESET SLOT END':
-                # A reset slot ends in a long recovery period.
-                t = self.samplenum - self.rise
-                if t != self.cnt_reset[self.overdrive]:
-                    continue
-
-                if owr == 0:
-                    # This seems to be a reset slot, wait for its end.
-                    self.state = 'WAIT FOR RISING EDGE'
-                    continue
-
-                self.put(self.fall, self.samplenum, self.out_ann,
-                         [0, ['Reset/presence: %s'
-                         % ('false' if self.present else 'true')]])
-                self.put(self.fall, self.samplenum, self.out_proto,
-                         ['RESET/PRESENCE', not self.present])
-                # Wait for next slot.
-                self.state = 'WAIT FOR FALLING EDGE'
-            else:
-                raise Exception('Invalid state: %s' % self.state)
diff --git a/decoders/onewire_link/pd.py b/decoders/onewire_link/pd.py
new file mode 100644 (file)
index 0000000..c8bda56
--- /dev/null
@@ -0,0 +1,269 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# 1-Wire protocol decoder (link layer)
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'onewire_link'
+    name = '1-Wire link layer'
+    longname = '1-Wire serial communication bus (link layer)'
+    desc = 'Bidirectional, half-duplex, asynchronous serial bus.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['onewire_link']
+    probes = [
+        {'id': 'owr', 'name': 'OWR', 'desc': '1-Wire signal line'},
+    ]
+    optional_probes = [
+        {'id': 'pwr', 'name': 'PWR', 'desc': '1-Wire power supply pin'},
+    ]
+    options = {
+        'overdrive': ['Overdrive', 1],
+        # Time options (specified in number of samplerate periods):
+        'cnt_normal_bit': ['Normal mode sample bit time', 0],
+        'cnt_normal_slot': ['Normal mode data slot time', 0],
+        'cnt_normal_presence': ['Normal mode sample presence time', 0],
+        'cnt_normal_reset': ['Normal mode reset time', 0],
+        'cnt_overdrive_bit': ['Overdrive mode sample bit time', 0],
+        'cnt_overdrive_slot': ['Overdrive mode data slot time', 0],
+        'cnt_overdrive_presence': ['Overdrive mode sample presence time', 0],
+        'cnt_overdrive_reset': ['Overdrive mode reset time', 0],
+    }
+    annotations = [
+        ['Text', 'Human-readable text'],
+        ['Warnings', 'Human-readable warnings'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.samplenum = 0
+        self.state = 'WAIT FOR FALLING EDGE'
+        self.present = 0
+        self.bit = 0
+        self.bit_cnt = 0
+        self.command = 0
+        self.overdrive = 0
+        self.fall = 0
+        self.rise = 0
+
+    def start(self, metadata):
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'onewire_link')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'onewire_link')
+
+        self.samplerate = metadata['samplerate']
+
+        # Check if samplerate is appropriate.
+        if self.options['overdrive']:
+            if self.samplerate < 2000000:
+                self.put(0, 0, self.out_ann, [1,
+                    ['ERROR: Sampling rate is too low. Must be above 2MHz ' +
+                     'for proper overdrive mode decoding.']])
+            elif self.samplerate < 5000000:
+                self.put(0, 0, self.out_ann, [1,
+                  ['WARNING: Sampling rate is suggested to be above 5MHz ' +
+                   'for proper overdrive mode decoding.']])
+        else:
+            if self.samplerate < 400000:
+                self.put(0, 0, self.out_ann, [1,
+                    ['ERROR: Sampling rate is too low. Must be above ' +
+                     '400kHz for proper normal mode decoding.']])
+            elif (self.samplerate < 1000000):
+                self.put(0, 0, self.out_ann, [1,
+                    ['WARNING: Sampling rate is suggested to be above ' +
+                     '1MHz for proper normal mode decoding.']])
+
+        # The default 1-Wire time base is 30us. This is used to calculate
+        # sampling times.
+        samplerate = float(self.samplerate)
+        if self.options['cnt_normal_bit']:
+            self.cnt_normal_bit = self.options['cnt_normal_bit']
+        else:
+            self.cnt_normal_bit = int(samplerate * 0.000015) - 1 # 15ns
+        if self.options['cnt_normal_slot']:
+            self.cnt_normal_slot = self.options['cnt_normal_slot']
+        else:
+            self.cnt_normal_slot = int(samplerate * 0.000060) - 1 # 60ns
+        if self.options['cnt_normal_presence']:
+            self.cnt_normal_presence = self.options['cnt_normal_presence']
+        else:
+            self.cnt_normal_presence = int(samplerate * 0.000075) - 1 # 75ns
+        if self.options['cnt_normal_reset']:
+            self.cnt_normal_reset = self.options['cnt_normal_reset']
+        else:
+            self.cnt_normal_reset = int(samplerate * 0.000480) - 1 # 480ns
+        if self.options['cnt_overdrive_bit']:
+            self.cnt_overdrive_bit = self.options['cnt_overdrive_bit']
+        else:
+            self.cnt_overdrive_bit = int(samplerate * 0.000002) - 1 # 2ns
+        if self.options['cnt_overdrive_slot']:
+            self.cnt_overdrive_slot = self.options['cnt_overdrive_slot']
+        else:
+            self.cnt_overdrive_slot = int(samplerate * 0.0000073) - 1 # 6ns+1.3ns
+        if self.options['cnt_overdrive_presence']:
+            self.cnt_overdrive_presence = self.options['cnt_overdrive_presence']
+        else:
+            self.cnt_overdrive_presence = int(samplerate * 0.000010) - 1 # 10ns
+        if self.options['cnt_overdrive_reset']:
+            self.cnt_overdrive_reset = self.options['cnt_overdrive_reset']
+        else:
+            self.cnt_overdrive_reset = int(samplerate * 0.000048) - 1 # 48ns
+
+        # Organize values into lists.
+        self.cnt_bit = [self.cnt_normal_bit, self.cnt_overdrive_bit]
+        self.cnt_presence = [self.cnt_normal_presence, self.cnt_overdrive_presence]
+        self.cnt_reset = [self.cnt_normal_reset, self.cnt_overdrive_reset]
+        self.cnt_slot = [self.cnt_normal_slot, self.cnt_overdrive_slot]
+
+        # Check if sample times are in the allowed range.
+
+        time_min = float(self.cnt_normal_bit) / self.samplerate
+        time_max = float(self.cnt_normal_bit + 1) / self.samplerate
+        if (time_min < 0.000005) or (time_max > 0.000015):
+            self.put(0, 0, self.out_ann, [1,
+                ['WARNING: The normal mode data sample time interval ' +
+                 '(%2.1fus-%2.1fus) should be inside (5.0us, 15.0us).'
+                 % (time_min * 1000000, time_max * 1000000)]])
+
+        time_min = float(self.cnt_normal_presence) / self.samplerate
+        time_max = float(self.cnt_normal_presence + 1) / self.samplerate
+        if (time_min < 0.0000681) or (time_max > 0.000075):
+            self.put(0, 0, self.out_ann, [1,
+                ['WARNING: The normal mode presence sample time interval ' +
+                 '(%2.1fus-%2.1fus) should be inside (68.1us, 75.0us).'
+                 % (time_min * 1000000, time_max * 1000000)]])
+
+        time_min = float(self.cnt_overdrive_bit) / self.samplerate
+        time_max = float(self.cnt_overdrive_bit + 1) / self.samplerate
+        if (time_min < 0.000001) or (time_max > 0.000002):
+            self.put(0, 0, self.out_ann, [1,
+                ['WARNING: The overdrive mode data sample time interval ' +
+                 '(%2.1fus-%2.1fus) should be inside (1.0us, 2.0us).'
+                 % (time_min * 1000000, time_max * 1000000)]])
+
+        time_min = float(self.cnt_overdrive_presence) / self.samplerate
+        time_max = float(self.cnt_overdrive_presence + 1) / self.samplerate
+        if (time_min < 0.0000073) or (time_max > 0.000010):
+            self.put(0, 0, self.out_ann, [1,
+                ['WARNING: The overdrive mode presence sample time interval ' +
+                 '(%2.1fus-%2.1fus) should be inside (7.3us, 10.0us).'
+                 % (time_min*1000000, time_max*1000000)]])
+
+    def report(self):
+        pass
+
+    def decode(self, ss, es, data):
+        for (self.samplenum, (owr, pwr)) in data:
+            # State machine.
+            if self.state == 'WAIT FOR FALLING EDGE':
+                # The start of a cycle is a falling edge.
+                if owr != 0:
+                    continue
+                # Save the sample number for the falling edge.
+                self.fall = self.samplenum
+                # Go to waiting for sample time.
+                self.state = 'WAIT FOR DATA SAMPLE'
+            elif self.state == 'WAIT FOR DATA SAMPLE':
+                # Sample data bit.
+                t = self.samplenum - self.fall
+                if t == self.cnt_bit[self.overdrive]:
+                    self.bit = owr
+                    self.state = 'WAIT FOR DATA SLOT END'
+            elif self.state == 'WAIT FOR DATA SLOT END':
+                # A data slot ends in a recovery period, otherwise, this is
+                # probably a reset.
+                t = self.samplenum - self.fall
+                if t != self.cnt_slot[self.overdrive]:
+                    continue
+
+                if owr == 0:
+                    # This seems to be a reset slot, wait for its end.
+                    self.state = 'WAIT FOR RISING EDGE'
+                    continue
+
+                self.put(self.fall, self.samplenum, self.out_ann,
+                         [0, ['Bit: %d' % self.bit]])
+                self.put(self.fall, self.samplenum, self.out_proto,
+                         ['BIT', self.bit])
+
+                # Checking the first command to see if overdrive mode
+                # should be entered.
+                if self.bit_cnt <= 8:
+                    self.command |= (self.bit << self.bit_cnt)
+                elif self.bit_cnt == 8 and self.command in [0x3c, 0x69]:
+                    self.put(self.fall, self.cnt_bit[self.overdrive],
+                             self.out_ann, [0, ['Entering overdrive mode']])
+                # Increment the bit counter.
+                self.bit_cnt += 1
+                # Wait for next slot.
+                self.state = 'WAIT FOR FALLING EDGE'
+            elif self.state == 'WAIT FOR RISING EDGE':
+                # The end of a cycle is a rising edge.
+                if owr != 1:
+                    continue
+
+                # Check if this was a reset cycle.
+                t = self.samplenum - self.fall
+                if t > self.cnt_normal_reset:
+                    # Save the sample number for the falling edge.
+                    self.rise = self.samplenum
+                    self.state = 'WAIT FOR PRESENCE DETECT'
+                    # Exit overdrive mode.
+                    if self.overdrive:
+                        self.put(self.fall, self.cnt_bit[self.overdrive],
+                                 self.out_ann, [0, ['Exiting overdrive mode']])
+                        self.overdrive = 0
+                    # Clear command bit counter and data register.
+                    self.bit_cnt = 0
+                    self.command = 0
+                elif (t > self.cnt_overdrive_reset) and self.overdrive:
+                    # Save the sample number for the falling edge.
+                    self.rise = self.samplenum
+                    self.state = "WAIT FOR PRESENCE DETECT"
+                # Otherwise this is assumed to be a data bit.
+                else:
+                    self.state = "WAIT FOR FALLING EDGE"
+            elif self.state == 'WAIT FOR PRESENCE DETECT':
+                # Sample presence status.
+                t = self.samplenum - self.rise
+                if t == self.cnt_presence[self.overdrive]:
+                    self.present = owr
+                    self.state = 'WAIT FOR RESET SLOT END'
+            elif self.state == 'WAIT FOR RESET SLOT END':
+                # A reset slot ends in a long recovery period.
+                t = self.samplenum - self.rise
+                if t != self.cnt_reset[self.overdrive]:
+                    continue
+
+                if owr == 0:
+                    # This seems to be a reset slot, wait for its end.
+                    self.state = 'WAIT FOR RISING EDGE'
+                    continue
+
+                self.put(self.fall, self.samplenum, self.out_ann,
+                         [0, ['Reset/presence: %s'
+                         % ('false' if self.present else 'true')]])
+                self.put(self.fall, self.samplenum, self.out_proto,
+                         ['RESET/PRESENCE', not self.present])
+                # Wait for next slot.
+                self.state = 'WAIT FOR FALLING EDGE'
+            else:
+                raise Exception('Invalid state: %s' % self.state)
index 2484987ee6721be94228d2f04be9dd8db18a3a8a..d0a84a80440d452c1c39152b94986aa39d8866fb 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/onewire_network
 
 
 pkgdatadir = $(DECODERS_DIR)/onewire_network
 
-dist_pkgdata_DATA = __init__.py onewire_network.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 25b3f5d4cce3d7b32bb793830b36efee0b140931..74b9f4bf658cd43971050ed2706b7a8b4d6cb87e 100644 (file)
@@ -58,5 +58,5 @@ TODO:
  - Add reporting original/complement address values from the search algorithm.
 '''
 
  - Add reporting original/complement address values from the search algorithm.
 '''
 
-from .onewire_network import *
+from .pd import *
 
 
diff --git a/decoders/onewire_network/onewire_network.py b/decoders/onewire_network/onewire_network.py
deleted file mode 100644 (file)
index ab11ea4..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# 1-Wire protocol decoder (network layer)
-
-import sigrokdecode as srd
-
-# Dictionary of ROM commands and their names, next state.
-command = {
-    0x33: ['Read ROM'              , 'GET ROM'   ],
-    0x0f: ['Conditional read ROM'  , 'GET ROM'   ],
-    0xcc: ['Skip ROM'              , 'TRANSPORT' ],
-    0x55: ['Match ROM'             , 'GET ROM'   ],
-    0xf0: ['Search ROM'            , 'SEARCH ROM'],
-    0xec: ['Conditional search ROM', 'SEARCH ROM'],
-    0x3c: ['Overdrive skip ROM'    , 'TRANSPORT' ],
-    0x69: ['Overdrive match ROM'   , 'GET ROM'   ],
-}
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'onewire_network'
-    name = '1-Wire network layer'
-    longname = '1-Wire serial communication bus (network layer)'
-    desc = 'Bidirectional, half-duplex, asynchronous serial bus.'
-    license = 'gplv2+'
-    inputs = ['onewire_link']
-    outputs = ['onewire_network']
-    probes = []
-    optional_probes = []
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.beg = 0
-        self.end = 0
-        self.state = 'COMMAND'
-        self.bit_cnt = 0
-        self.search = 'P'
-        self.data_p = 0x0
-        self.data_n = 0x0
-        self.data = 0x0
-        self.rom = 0x0000000000000000
-
-    def start(self, metadata):
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'onewire_network')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'onewire_network')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        # Helper function for most annotations.
-        self.put(self.beg, self.end, self.out_ann, data)
-
-    def puty(self, data):
-        # Helper function for most protocol packets.
-        self.put(self.beg, self.end, self.out_proto, data)
-
-    def decode(self, ss, es, data):
-        code, val = data
-
-        # State machine.
-        if code == 'RESET/PRESENCE':
-            self.search = 'P'
-            self.bit_cnt = 0
-            self.put(ss, es, self.out_ann,
-                     [0, ['Reset/presence: %s' % ('true' if val else 'false')]])
-            self.put(ss, es, self.out_proto, ['RESET/PRESENCE', val])
-            self.state = 'COMMAND'
-            return
-
-        # For now we're only interested in 'RESET/PRESENCE' and 'BIT' packets.
-        if code != 'BIT':
-            return
-
-        if self.state == 'COMMAND':
-            # Receiving and decoding a ROM command.
-            if self.onewire_collect(8, val, ss, es) == 0:
-                return
-            if self.data in command:
-                self.putx([0, ['ROM command: 0x%02x \'%s\''
-                          % (self.data, command[self.data][0])]])
-                self.state = command[self.data][1]
-            else:
-                self.putx([0, ['ROM command: 0x%02x \'%s\''
-                          % (self.data, 'unrecognized')]])
-                self.state = 'COMMAND ERROR'
-        elif self.state == 'GET ROM':
-            # A 64 bit device address is selected.
-            # Family code (1 byte) + serial number (6 bytes) + CRC (1 byte)
-            if self.onewire_collect(64, val, ss, es) == 0:
-                return
-            self.rom = self.data & 0xffffffffffffffff
-            self.putx([0, ['ROM: 0x%016x' % self.rom]])
-            self.puty(['ROM', self.rom])
-            self.state = 'TRANSPORT'
-        elif self.state == 'SEARCH ROM':
-            # A 64 bit device address is searched for.
-            # Family code (1 byte) + serial number (6 bytes) + CRC (1 byte)
-            if self.onewire_search(64, val, ss, es) == 0:
-                return
-            self.rom = self.data & 0xffffffffffffffff
-            self.putx([0, ['ROM: 0x%016x' % self.rom]])
-            self.puty(['ROM', self.rom])
-            self.state = 'TRANSPORT'
-        elif self.state == 'TRANSPORT':
-            # The transport layer is handled in byte sized units.
-            if self.onewire_collect(8, val, ss, es) == 0:
-                return
-            self.putx([0, ['Data: 0x%02x' % self.data]])
-            self.puty(['DATA', self.data])
-        elif self.state == 'COMMAND ERROR':
-            # Since the command is not recognized, print raw data.
-            if self.onewire_collect(8, val, ss, es) == 0:
-                return
-            self.putx([0, ['ROM error data: 0x%02x' % self.data]])
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
-    # Data collector.
-    def onewire_collect(self, length, val, ss, es):
-        # Storing the sample this sequence begins with.
-        if self.bit_cnt == 1:
-            self.beg = ss
-        self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt)
-        self.bit_cnt += 1
-        # Storing the sample this sequence ends with.
-        # In case the full length of the sequence is received, return 1.
-        if self.bit_cnt == length:
-            self.end = es
-            self.data = self.data & ((1 << length) - 1)
-            self.bit_cnt = 0
-            return 1
-        else:
-            return 0
-
-    # Search collector.
-    def onewire_search(self, length, val, ss, es):
-        # Storing the sample this sequence begins with.
-        if (self.bit_cnt == 0) and (self.search == 'P'):
-            self.beg = ss
-
-        if self.search == 'P':
-            # Master receives an original address bit.
-            self.data_p = self.data_p & ~(1 << self.bit_cnt) | \
-                          (val << self.bit_cnt)
-            self.search = 'N'
-        elif self.search == 'N':
-            # Master receives a complemented address bit.
-            self.data_n = self.data_n & ~(1 << self.bit_cnt) | \
-                          (val << self.bit_cnt)
-            self.search = 'D'
-        elif self.search == 'D':
-            # Master transmits an address bit.
-            self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt)
-            self.search = 'P'
-            self.bit_cnt += 1
-
-        # Storing the sample this sequence ends with.
-        # In case the full length of the sequence is received, return 1.
-        if self.bit_cnt == length:
-            self.end = es
-            self.data_p = self.data_p & ((1 << length) - 1)
-            self.data_n = self.data_n & ((1 << length) - 1)
-            self.data = self.data & ((1 << length) - 1)
-            self.search = 'P'
-            self.bit_cnt = 0
-            return 1
-        else:
-            return 0
diff --git a/decoders/onewire_network/pd.py b/decoders/onewire_network/pd.py
new file mode 100644 (file)
index 0000000..ab11ea4
--- /dev/null
@@ -0,0 +1,190 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# 1-Wire protocol decoder (network layer)
+
+import sigrokdecode as srd
+
+# Dictionary of ROM commands and their names, next state.
+command = {
+    0x33: ['Read ROM'              , 'GET ROM'   ],
+    0x0f: ['Conditional read ROM'  , 'GET ROM'   ],
+    0xcc: ['Skip ROM'              , 'TRANSPORT' ],
+    0x55: ['Match ROM'             , 'GET ROM'   ],
+    0xf0: ['Search ROM'            , 'SEARCH ROM'],
+    0xec: ['Conditional search ROM', 'SEARCH ROM'],
+    0x3c: ['Overdrive skip ROM'    , 'TRANSPORT' ],
+    0x69: ['Overdrive match ROM'   , 'GET ROM'   ],
+}
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'onewire_network'
+    name = '1-Wire network layer'
+    longname = '1-Wire serial communication bus (network layer)'
+    desc = 'Bidirectional, half-duplex, asynchronous serial bus.'
+    license = 'gplv2+'
+    inputs = ['onewire_link']
+    outputs = ['onewire_network']
+    probes = []
+    optional_probes = []
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.beg = 0
+        self.end = 0
+        self.state = 'COMMAND'
+        self.bit_cnt = 0
+        self.search = 'P'
+        self.data_p = 0x0
+        self.data_n = 0x0
+        self.data = 0x0
+        self.rom = 0x0000000000000000
+
+    def start(self, metadata):
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'onewire_network')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'onewire_network')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        # Helper function for most annotations.
+        self.put(self.beg, self.end, self.out_ann, data)
+
+    def puty(self, data):
+        # Helper function for most protocol packets.
+        self.put(self.beg, self.end, self.out_proto, data)
+
+    def decode(self, ss, es, data):
+        code, val = data
+
+        # State machine.
+        if code == 'RESET/PRESENCE':
+            self.search = 'P'
+            self.bit_cnt = 0
+            self.put(ss, es, self.out_ann,
+                     [0, ['Reset/presence: %s' % ('true' if val else 'false')]])
+            self.put(ss, es, self.out_proto, ['RESET/PRESENCE', val])
+            self.state = 'COMMAND'
+            return
+
+        # For now we're only interested in 'RESET/PRESENCE' and 'BIT' packets.
+        if code != 'BIT':
+            return
+
+        if self.state == 'COMMAND':
+            # Receiving and decoding a ROM command.
+            if self.onewire_collect(8, val, ss, es) == 0:
+                return
+            if self.data in command:
+                self.putx([0, ['ROM command: 0x%02x \'%s\''
+                          % (self.data, command[self.data][0])]])
+                self.state = command[self.data][1]
+            else:
+                self.putx([0, ['ROM command: 0x%02x \'%s\''
+                          % (self.data, 'unrecognized')]])
+                self.state = 'COMMAND ERROR'
+        elif self.state == 'GET ROM':
+            # A 64 bit device address is selected.
+            # Family code (1 byte) + serial number (6 bytes) + CRC (1 byte)
+            if self.onewire_collect(64, val, ss, es) == 0:
+                return
+            self.rom = self.data & 0xffffffffffffffff
+            self.putx([0, ['ROM: 0x%016x' % self.rom]])
+            self.puty(['ROM', self.rom])
+            self.state = 'TRANSPORT'
+        elif self.state == 'SEARCH ROM':
+            # A 64 bit device address is searched for.
+            # Family code (1 byte) + serial number (6 bytes) + CRC (1 byte)
+            if self.onewire_search(64, val, ss, es) == 0:
+                return
+            self.rom = self.data & 0xffffffffffffffff
+            self.putx([0, ['ROM: 0x%016x' % self.rom]])
+            self.puty(['ROM', self.rom])
+            self.state = 'TRANSPORT'
+        elif self.state == 'TRANSPORT':
+            # The transport layer is handled in byte sized units.
+            if self.onewire_collect(8, val, ss, es) == 0:
+                return
+            self.putx([0, ['Data: 0x%02x' % self.data]])
+            self.puty(['DATA', self.data])
+        elif self.state == 'COMMAND ERROR':
+            # Since the command is not recognized, print raw data.
+            if self.onewire_collect(8, val, ss, es) == 0:
+                return
+            self.putx([0, ['ROM error data: 0x%02x' % self.data]])
+        else:
+            raise Exception('Invalid state: %s' % self.state)
+
+    # Data collector.
+    def onewire_collect(self, length, val, ss, es):
+        # Storing the sample this sequence begins with.
+        if self.bit_cnt == 1:
+            self.beg = ss
+        self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt)
+        self.bit_cnt += 1
+        # Storing the sample this sequence ends with.
+        # In case the full length of the sequence is received, return 1.
+        if self.bit_cnt == length:
+            self.end = es
+            self.data = self.data & ((1 << length) - 1)
+            self.bit_cnt = 0
+            return 1
+        else:
+            return 0
+
+    # Search collector.
+    def onewire_search(self, length, val, ss, es):
+        # Storing the sample this sequence begins with.
+        if (self.bit_cnt == 0) and (self.search == 'P'):
+            self.beg = ss
+
+        if self.search == 'P':
+            # Master receives an original address bit.
+            self.data_p = self.data_p & ~(1 << self.bit_cnt) | \
+                          (val << self.bit_cnt)
+            self.search = 'N'
+        elif self.search == 'N':
+            # Master receives a complemented address bit.
+            self.data_n = self.data_n & ~(1 << self.bit_cnt) | \
+                          (val << self.bit_cnt)
+            self.search = 'D'
+        elif self.search == 'D':
+            # Master transmits an address bit.
+            self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt)
+            self.search = 'P'
+            self.bit_cnt += 1
+
+        # Storing the sample this sequence ends with.
+        # In case the full length of the sequence is received, return 1.
+        if self.bit_cnt == length:
+            self.end = es
+            self.data_p = self.data_p & ((1 << length) - 1)
+            self.data_n = self.data_n & ((1 << length) - 1)
+            self.data = self.data & ((1 << length) - 1)
+            self.search = 'P'
+            self.bit_cnt = 0
+            return 1
+        else:
+            return 0
index 6997ed51bd062dda6f22a0e04b9a442ea53ac4ed..5c5913e7e69a8b3497164ad7e8dd880dd71ac3f8 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/pan1321
 
 
 pkgdatadir = $(DECODERS_DIR)/pan1321
 
-dist_pkgdata_DATA = __init__.py pan1321.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 155d96141a51d216cfefa026a0e0db51b0bf8967..df48767f070776015ae5b6fc983f3386fbc5d76c 100644 (file)
@@ -25,5 +25,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .pan1321 import *
+from .pd import *
 
 
diff --git a/decoders/pan1321/pan1321.py b/decoders/pan1321/pan1321.py
deleted file mode 100644 (file)
index 827269d..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Panasonic PAN1321 Bluetooth module protocol decoder
-
-import sigrokdecode as srd
-
-# ...
-RX = 0
-TX = 1
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'pan1321'
-    name = 'PAN1321'
-    longname = 'Panasonic PAN1321'
-    desc = 'Bluetooth RF module with Serial Port Profile (SPP).'
-    license = 'gplv2+'
-    inputs = ['uart']
-    outputs = ['pan1321']
-    probes = []
-    optional_probes = []
-    options = {}
-    annotations = [
-        ['Text (verbose)', 'Human-readable text (verbose)'],
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.cmd = ['', '']
-        self.ss_block = None
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'pan1321')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'pan1321')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        self.put(self.ss_block, self.es_block, self.out_ann, data)
-
-    def handle_host_command(self, rxtx, s):
-        if s.startswith('AT+JSEC'):
-            pin = s[-4:]
-            self.putx([0, ['Host set the Bluetooth PIN to ' + pin]])
-            self.putx([1, ['PIN = ' + pin]])
-        elif s.startswith('AT+JSLN'):
-            name = s[s.find(',') + 1:]
-            self.putx([0, ['Host set the Bluetooth name to ' + name]])
-            self.putx([1, ['BT name = ' + name]])
-        else:
-            self.putx([0, ['Host sent unsupported command: %s' % s]])
-            self.putx([1, ['Unsupported command: %s' % s]])
-        self.cmd[rxtx] = ''
-
-    def handle_device_reply(self, rxtx, s):
-        if s == 'ROK':
-            self.putx([0, ['Device initialized correctly']])
-            self.putx([1, ['Init']])
-        elif s == 'OK':
-            self.putx([0, ['Device acknowledged last command']])
-            self.putx([1, ['ACK']])
-        elif s.startswith('ERR'):
-            error = s[s.find('=') + 1:]
-            self.putx([0, ['Device sent error code ' + error]])
-            self.putx([1, ['ERR = ' + error]])
-        else:
-            self.putx([0, ['Device sent an unknown reply: %s' % s]])
-            self.putx([1, ['Unknown reply: %s' % s]])
-        self.cmd[rxtx] = ''
-
-    def decode(self, ss, es, data):
-        ptype, rxtx, pdata = data
-
-        # For now, ignore all UART packets except the actual data packets.
-        if ptype != 'DATA':
-            return
-
-        # If this is the start of a command/reply, remember the start sample.
-        if self.cmd[rxtx] == '':
-            self.ss_block = ss
-
-        # Append a new (ASCII) byte to the currently built/parsed command.
-        self.cmd[rxtx] += chr(pdata)
-
-        # Get packets/bytes until an \r\n sequence is found (end of command).
-        if self.cmd[rxtx][-1:] != '\n':
-            return
-
-        # Handle host commands and device replies.
-        # We remove trailing \r\n from the strings before handling them.
-        if rxtx == RX:
-            self.es_block = es
-            self.handle_device_reply(rxtx, self.cmd[rxtx][:-2])
-        elif rxtx == TX:
-            self.es_block = es
-            self.handle_host_command(rxtx, self.cmd[rxtx][:-2])
-        else:
-            raise Exception('Invalid rxtx value: %d' % rxtx)
-
diff --git a/decoders/pan1321/pd.py b/decoders/pan1321/pd.py
new file mode 100644 (file)
index 0000000..827269d
--- /dev/null
@@ -0,0 +1,118 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Panasonic PAN1321 Bluetooth module protocol decoder
+
+import sigrokdecode as srd
+
+# ...
+RX = 0
+TX = 1
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'pan1321'
+    name = 'PAN1321'
+    longname = 'Panasonic PAN1321'
+    desc = 'Bluetooth RF module with Serial Port Profile (SPP).'
+    license = 'gplv2+'
+    inputs = ['uart']
+    outputs = ['pan1321']
+    probes = []
+    optional_probes = []
+    options = {}
+    annotations = [
+        ['Text (verbose)', 'Human-readable text (verbose)'],
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.cmd = ['', '']
+        self.ss_block = None
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'pan1321')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'pan1321')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+    def handle_host_command(self, rxtx, s):
+        if s.startswith('AT+JSEC'):
+            pin = s[-4:]
+            self.putx([0, ['Host set the Bluetooth PIN to ' + pin]])
+            self.putx([1, ['PIN = ' + pin]])
+        elif s.startswith('AT+JSLN'):
+            name = s[s.find(',') + 1:]
+            self.putx([0, ['Host set the Bluetooth name to ' + name]])
+            self.putx([1, ['BT name = ' + name]])
+        else:
+            self.putx([0, ['Host sent unsupported command: %s' % s]])
+            self.putx([1, ['Unsupported command: %s' % s]])
+        self.cmd[rxtx] = ''
+
+    def handle_device_reply(self, rxtx, s):
+        if s == 'ROK':
+            self.putx([0, ['Device initialized correctly']])
+            self.putx([1, ['Init']])
+        elif s == 'OK':
+            self.putx([0, ['Device acknowledged last command']])
+            self.putx([1, ['ACK']])
+        elif s.startswith('ERR'):
+            error = s[s.find('=') + 1:]
+            self.putx([0, ['Device sent error code ' + error]])
+            self.putx([1, ['ERR = ' + error]])
+        else:
+            self.putx([0, ['Device sent an unknown reply: %s' % s]])
+            self.putx([1, ['Unknown reply: %s' % s]])
+        self.cmd[rxtx] = ''
+
+    def decode(self, ss, es, data):
+        ptype, rxtx, pdata = data
+
+        # For now, ignore all UART packets except the actual data packets.
+        if ptype != 'DATA':
+            return
+
+        # If this is the start of a command/reply, remember the start sample.
+        if self.cmd[rxtx] == '':
+            self.ss_block = ss
+
+        # Append a new (ASCII) byte to the currently built/parsed command.
+        self.cmd[rxtx] += chr(pdata)
+
+        # Get packets/bytes until an \r\n sequence is found (end of command).
+        if self.cmd[rxtx][-1:] != '\n':
+            return
+
+        # Handle host commands and device replies.
+        # We remove trailing \r\n from the strings before handling them.
+        if rxtx == RX:
+            self.es_block = es
+            self.handle_device_reply(rxtx, self.cmd[rxtx][:-2])
+        elif rxtx == TX:
+            self.es_block = es
+            self.handle_host_command(rxtx, self.cmd[rxtx][:-2])
+        else:
+            raise Exception('Invalid rxtx value: %d' % rxtx)
+
index 581eb5f2bbe36685dcec3f8c5502c222ca5b9bcf..8757b9cf8214fc4ad4fc29a0fbae230dafe2161b 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/rtc8564
 
 
 pkgdatadir = $(DECODERS_DIR)/rtc8564
 
-dist_pkgdata_DATA = __init__.py rtc8564.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 56534532b2920dc36463da65627df2438133999e..94fdaeaee07510d4e946da282ebdace8a332c758 100644 (file)
@@ -25,5 +25,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .rtc8564 import *
+from .pd import *
 
 
diff --git a/decoders/rtc8564/pd.py b/decoders/rtc8564/pd.py
new file mode 100644 (file)
index 0000000..bf5d4b6
--- /dev/null
@@ -0,0 +1,216 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Epson RTC-8564 JE/NB protocol decoder
+
+import sigrokdecode as srd
+
+# Return the specified BCD number (max. 8 bits) as integer.
+def bcd2int(b):
+    return (b & 0x0f) + ((b >> 4) * 10)
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'rtc8564'
+    name = 'RTC-8564'
+    longname = 'Epson RTC-8564 JE/NB'
+    desc = 'Realtime clock module protocol.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['rtc8564']
+    probes = []
+    optional_probes = [
+        {'id': 'clkout', 'name': 'CLKOUT', 'desc': 'TODO.'},
+        {'id': 'clkoe', 'name': 'CLKOE', 'desc': 'TODO.'},
+        {'id': 'int', 'name': 'INT#', 'desc': 'TODO.'},
+    ]
+    options = {}
+    annotations = [
+        ['Text', 'Human-readable text'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.state = 'IDLE'
+        self.hours = -1
+        self.minutes = -1
+        self.seconds = -1
+        self.days = -1
+        self.months = -1
+        self.years = -1
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'rtc8564')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'rtc8564')
+
+    def report(self):
+        pass
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def handle_reg_0x00(self, b): # Control register 1
+        pass
+
+    def handle_reg_0x01(self, b): # Control register 2
+        ti_tp = 1 if (b & (1 << 4)) else 0
+        af = 1 if (b & (1 << 3)) else 0
+        tf = 1 if (b & (1 << 2)) else 0
+        aie = 1 if (b & (1 << 1)) else 0
+        tie = 1 if (b & (1 << 0)) else 0
+
+        ann = ''
+
+        s = 'repeated' if ti_tp else 'single-shot'
+        ann += 'TI/TP = %d: %s operation upon fixed-cycle timer interrupt '\
+               'events\n' % (ti_tp, s)
+        s = '' if af else 'no '
+        ann += 'AF = %d: %salarm interrupt detected\n' % (af, s)
+        s = '' if tf else 'no '
+        ann += 'TF = %d: %sfixed-cycle timer interrupt detected\n' % (tf, s)
+        s = 'enabled' if aie else 'prohibited'
+        ann += 'AIE = %d: INT# pin output %s when an alarm interrupt '\
+               'occurs\n' % (aie, s)
+        s = 'enabled' if tie else 'prohibited'
+        ann += 'TIE = %d: INT# pin output %s when a fixed-cycle interrupt '\
+               'event occurs\n' % (tie, s)
+
+        self.putx([0, [ann]])
+
+    def handle_reg_0x02(self, b): # Seconds / Voltage-low flag
+        self.seconds = bcd2int(b & 0x7f)
+        self.putx([0, ['Seconds: %d' % self.seconds]])
+        vl = 1 if (b & (1 << 7)) else 0
+        self.putx([0, ['Voltage low (VL) bit: %d' % vl]])
+
+    def handle_reg_0x03(self, b): # Minutes
+        self.minutes = bcd2int(b & 0x7f)
+        self.putx([0, ['Minutes: %d' % self.minutes]])
+
+    def handle_reg_0x04(self, b): # Hours
+        self.hours = bcd2int(b & 0x3f)
+        self.putx([0, ['Hours: %d' % self.hours]])
+
+    def handle_reg_0x05(self, b): # Days
+        self.days = bcd2int(b & 0x3f)
+        self.putx([0, ['Days: %d' % self.days]])
+
+    def handle_reg_0x06(self, b): # Day counter
+        pass
+
+    def handle_reg_0x07(self, b): # Months / century
+        # TODO: Handle century bit.
+        self.months = bcd2int(b & 0x1f)
+        self.putx([0, ['Months: %d' % self.months]])
+
+    def handle_reg_0x08(self, b): # Years
+        self.years = bcd2int(b & 0xff)
+        self.putx([0, ['Years: %d' % self.years]])
+
+    def handle_reg_0x09(self, b): # Alarm, minute
+        pass
+
+    def handle_reg_0x0a(self, b): # Alarm, hour
+        pass
+
+    def handle_reg_0x0b(self, b): # Alarm, day
+        pass
+
+    def handle_reg_0x0c(self, b): # Alarm, weekday
+        pass
+
+    def handle_reg_0x0d(self, b): # CLKOUT output
+        pass
+
+    def handle_reg_0x0e(self, b): # Timer setting
+        pass
+
+    def handle_reg_0x0f(self, b): # Down counter for fixed-cycle timer
+        pass
+
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+
+        # Store the start/end samples of this I2C packet.
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if self.state == 'IDLE':
+            # Wait for an I2C START condition.
+            if cmd != 'START':
+                return
+            self.state = 'GET SLAVE ADDR'
+            self.block_start_sample = ss
+        elif self.state == 'GET SLAVE ADDR':
+            # Wait for an address write operation.
+            # TODO: We should only handle packets to the RTC slave (0xa2/0xa3).
+            if cmd != 'ADDRESS WRITE':
+                return
+            self.state = 'GET REG ADDR'
+        elif self.state == 'GET REG ADDR':
+            # Wait for a data write (master selects the slave register).
+            if cmd != 'DATA WRITE':
+                return
+            self.reg = databyte
+            self.state = 'WRITE RTC REGS'
+        elif self.state == 'WRITE RTC REGS':
+            # If we see a Repeated Start here, it's probably an RTC read.
+            if cmd == 'START REPEAT':
+                self.state = 'READ RTC REGS'
+                return
+            # Otherwise: Get data bytes until a STOP condition occurs.
+            if cmd == 'DATA WRITE':
+                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
+                handle_reg(databyte)
+                self.reg += 1
+                # TODO: Check for NACK!
+            elif cmd == 'STOP':
+                # TODO: Handle read/write of only parts of these items.
+                d = '%02d.%02d.%02d %02d:%02d:%02d' % (self.days, self.months,
+                    self.years, self.hours, self.minutes, self.seconds)
+                self.put(self.block_start_sample, es, self.out_ann,
+                         [0, ['Written date/time: %s' % d]])
+                self.state = 'IDLE'
+            else:
+                pass # TODO
+        elif self.state == 'READ RTC REGS':
+            # Wait for an address read operation.
+            # TODO: We should only handle packets to the RTC slave (0xa2/0xa3).
+            if cmd == 'ADDRESS READ':
+                self.state = 'READ RTC REGS2'
+                return
+            else:
+                pass # TODO
+        elif self.state == 'READ RTC REGS2':
+            if cmd == 'DATA READ':
+                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
+                handle_reg(databyte)
+                self.reg += 1
+                # TODO: Check for NACK!
+            elif cmd == 'STOP':
+                d = '%02d.%02d.%02d %02d:%02d:%02d' % (self.days, self.months,
+                    self.years, self.hours, self.minutes, self.seconds)
+                self.put(self.block_start_sample, es, self.out_ann,
+                         [0, ['Read date/time: %s' % d]])
+                self.state = 'IDLE'
+            else:
+                pass # TODO?
+        else:
+            raise Exception('Invalid state: %d' % self.state)
+
diff --git a/decoders/rtc8564/rtc8564.py b/decoders/rtc8564/rtc8564.py
deleted file mode 100644 (file)
index bf5d4b6..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Epson RTC-8564 JE/NB protocol decoder
-
-import sigrokdecode as srd
-
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
-    return (b & 0x0f) + ((b >> 4) * 10)
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'rtc8564'
-    name = 'RTC-8564'
-    longname = 'Epson RTC-8564 JE/NB'
-    desc = 'Realtime clock module protocol.'
-    license = 'gplv2+'
-    inputs = ['i2c']
-    outputs = ['rtc8564']
-    probes = []
-    optional_probes = [
-        {'id': 'clkout', 'name': 'CLKOUT', 'desc': 'TODO.'},
-        {'id': 'clkoe', 'name': 'CLKOE', 'desc': 'TODO.'},
-        {'id': 'int', 'name': 'INT#', 'desc': 'TODO.'},
-    ]
-    options = {}
-    annotations = [
-        ['Text', 'Human-readable text'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.state = 'IDLE'
-        self.hours = -1
-        self.minutes = -1
-        self.seconds = -1
-        self.days = -1
-        self.months = -1
-        self.years = -1
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'rtc8564')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'rtc8564')
-
-    def report(self):
-        pass
-
-    def putx(self, data):
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def handle_reg_0x00(self, b): # Control register 1
-        pass
-
-    def handle_reg_0x01(self, b): # Control register 2
-        ti_tp = 1 if (b & (1 << 4)) else 0
-        af = 1 if (b & (1 << 3)) else 0
-        tf = 1 if (b & (1 << 2)) else 0
-        aie = 1 if (b & (1 << 1)) else 0
-        tie = 1 if (b & (1 << 0)) else 0
-
-        ann = ''
-
-        s = 'repeated' if ti_tp else 'single-shot'
-        ann += 'TI/TP = %d: %s operation upon fixed-cycle timer interrupt '\
-               'events\n' % (ti_tp, s)
-        s = '' if af else 'no '
-        ann += 'AF = %d: %salarm interrupt detected\n' % (af, s)
-        s = '' if tf else 'no '
-        ann += 'TF = %d: %sfixed-cycle timer interrupt detected\n' % (tf, s)
-        s = 'enabled' if aie else 'prohibited'
-        ann += 'AIE = %d: INT# pin output %s when an alarm interrupt '\
-               'occurs\n' % (aie, s)
-        s = 'enabled' if tie else 'prohibited'
-        ann += 'TIE = %d: INT# pin output %s when a fixed-cycle interrupt '\
-               'event occurs\n' % (tie, s)
-
-        self.putx([0, [ann]])
-
-    def handle_reg_0x02(self, b): # Seconds / Voltage-low flag
-        self.seconds = bcd2int(b & 0x7f)
-        self.putx([0, ['Seconds: %d' % self.seconds]])
-        vl = 1 if (b & (1 << 7)) else 0
-        self.putx([0, ['Voltage low (VL) bit: %d' % vl]])
-
-    def handle_reg_0x03(self, b): # Minutes
-        self.minutes = bcd2int(b & 0x7f)
-        self.putx([0, ['Minutes: %d' % self.minutes]])
-
-    def handle_reg_0x04(self, b): # Hours
-        self.hours = bcd2int(b & 0x3f)
-        self.putx([0, ['Hours: %d' % self.hours]])
-
-    def handle_reg_0x05(self, b): # Days
-        self.days = bcd2int(b & 0x3f)
-        self.putx([0, ['Days: %d' % self.days]])
-
-    def handle_reg_0x06(self, b): # Day counter
-        pass
-
-    def handle_reg_0x07(self, b): # Months / century
-        # TODO: Handle century bit.
-        self.months = bcd2int(b & 0x1f)
-        self.putx([0, ['Months: %d' % self.months]])
-
-    def handle_reg_0x08(self, b): # Years
-        self.years = bcd2int(b & 0xff)
-        self.putx([0, ['Years: %d' % self.years]])
-
-    def handle_reg_0x09(self, b): # Alarm, minute
-        pass
-
-    def handle_reg_0x0a(self, b): # Alarm, hour
-        pass
-
-    def handle_reg_0x0b(self, b): # Alarm, day
-        pass
-
-    def handle_reg_0x0c(self, b): # Alarm, weekday
-        pass
-
-    def handle_reg_0x0d(self, b): # CLKOUT output
-        pass
-
-    def handle_reg_0x0e(self, b): # Timer setting
-        pass
-
-    def handle_reg_0x0f(self, b): # Down counter for fixed-cycle timer
-        pass
-
-    def decode(self, ss, es, data):
-        cmd, databyte = data
-
-        # Store the start/end samples of this I2C packet.
-        self.ss, self.es = ss, es
-
-        # State machine.
-        if self.state == 'IDLE':
-            # Wait for an I2C START condition.
-            if cmd != 'START':
-                return
-            self.state = 'GET SLAVE ADDR'
-            self.block_start_sample = ss
-        elif self.state == 'GET SLAVE ADDR':
-            # Wait for an address write operation.
-            # TODO: We should only handle packets to the RTC slave (0xa2/0xa3).
-            if cmd != 'ADDRESS WRITE':
-                return
-            self.state = 'GET REG ADDR'
-        elif self.state == 'GET REG ADDR':
-            # Wait for a data write (master selects the slave register).
-            if cmd != 'DATA WRITE':
-                return
-            self.reg = databyte
-            self.state = 'WRITE RTC REGS'
-        elif self.state == 'WRITE RTC REGS':
-            # If we see a Repeated Start here, it's probably an RTC read.
-            if cmd == 'START REPEAT':
-                self.state = 'READ RTC REGS'
-                return
-            # Otherwise: Get data bytes until a STOP condition occurs.
-            if cmd == 'DATA WRITE':
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte)
-                self.reg += 1
-                # TODO: Check for NACK!
-            elif cmd == 'STOP':
-                # TODO: Handle read/write of only parts of these items.
-                d = '%02d.%02d.%02d %02d:%02d:%02d' % (self.days, self.months,
-                    self.years, self.hours, self.minutes, self.seconds)
-                self.put(self.block_start_sample, es, self.out_ann,
-                         [0, ['Written date/time: %s' % d]])
-                self.state = 'IDLE'
-            else:
-                pass # TODO
-        elif self.state == 'READ RTC REGS':
-            # Wait for an address read operation.
-            # TODO: We should only handle packets to the RTC slave (0xa2/0xa3).
-            if cmd == 'ADDRESS READ':
-                self.state = 'READ RTC REGS2'
-                return
-            else:
-                pass # TODO
-        elif self.state == 'READ RTC REGS2':
-            if cmd == 'DATA READ':
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte)
-                self.reg += 1
-                # TODO: Check for NACK!
-            elif cmd == 'STOP':
-                d = '%02d.%02d.%02d %02d:%02d:%02d' % (self.days, self.months,
-                    self.years, self.hours, self.minutes, self.seconds)
-                self.put(self.block_start_sample, es, self.out_ann,
-                         [0, ['Read date/time: %s' % d]])
-                self.state = 'IDLE'
-            else:
-                pass # TODO?
-        else:
-            raise Exception('Invalid state: %d' % self.state)
-
index 89bf640db5f09cf7922653b3653d6166f99d7775..0ab762e074d8ec4019630a6283bb1a7a2611b582 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/spi
 
 
 pkgdatadir = $(DECODERS_DIR)/spi
 
-dist_pkgdata_DATA = __init__.py spi.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index fedcc5fbbbd4af3bf33216411a54d8d349d90bb9..1fccbe74dee06292db4ffcd58662b3cce184e134 100644 (file)
@@ -44,5 +44,5 @@ Examples:
 
 '''
 
 
 '''
 
-from .spi import *
+from .pd import *
 
 
diff --git a/decoders/spi/pd.py b/decoders/spi/pd.py
new file mode 100644 (file)
index 0000000..1085786
--- /dev/null
@@ -0,0 +1,166 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# SPI protocol decoder
+
+import sigrokdecode as srd
+
+# Key: (CPOL, CPHA). Value: SPI mode.
+# Clock polarity (CPOL) = 0/1: Clock is low/high when inactive.
+# Clock phase (CPHA) = 0/1: Data is valid on the leading/trailing clock edge.
+spi_mode = {
+    (0, 0): 0, # Mode 0
+    (0, 1): 1, # Mode 1
+    (1, 0): 2, # Mode 2
+    (1, 1): 3, # Mode 3
+}
+
+# Annotation formats
+ANN_HEX = 0
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'spi'
+    name = 'SPI'
+    longname = 'Serial Peripheral Interface'
+    desc = 'Full-duplex, synchronous, serial bus.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['spi']
+    probes = [
+        {'id': 'miso', 'name': 'MISO',
+         'desc': 'SPI MISO line (Master in, slave out)'},
+        {'id': 'mosi', 'name': 'MOSI',
+         'desc': 'SPI MOSI line (Master out, slave in)'},
+        {'id': 'sck', 'name': 'CLK', 'desc': 'SPI clock line'},
+        {'id': 'cs', 'name': 'CS#', 'desc': 'SPI CS (chip select) line'},
+    ]
+    optional_probes = [] # TODO
+    options = {
+        'cs_polarity': ['CS# polarity', 'active-low'],
+        'cpol': ['Clock polarity', 0],
+        'cpha': ['Clock phase', 0],
+        'bitorder': ['Bit order within the SPI data', 'msb-first'],
+        'wordsize': ['Word size of SPI data', 8], # 1-64?
+    }
+    annotations = [
+        ['Hex', 'SPI data bytes in hex format'],
+    ]
+
+    def __init__(self):
+        self.oldsck = 1
+        self.bitcount = 0
+        self.mosidata = 0
+        self.misodata = 0
+        self.bytesreceived = 0
+        self.samplenum = -1
+        self.cs_was_deasserted_during_data_word = 0
+        self.oldcs = -1
+        self.oldpins = None
+
+    def start(self, metadata):
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'spi')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'spi')
+
+    def report(self):
+        return 'SPI: %d bytes received' % self.bytesreceived
+
+    def decode(self, ss, es, data):
+        # TODO: Either MISO or MOSI could be optional. CS# is optional.
+        for (self.samplenum, pins) in data:
+
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpins == pins:
+                continue
+            self.oldpins, (miso, mosi, sck, cs) = pins, pins
+
+            if self.oldcs != cs:
+                # Send all CS# pin value changes.
+                self.put(self.samplenum, self.samplenum, self.out_proto,
+                         ['CS-CHANGE', self.oldcs, cs])
+                self.put(self.samplenum, self.samplenum, self.out_ann,
+                         [0, ['CS-CHANGE: %d->%d' % (self.oldcs, cs)]])
+                self.oldcs = cs
+
+            # Ignore sample if the clock pin hasn't changed.
+            if sck == self.oldsck:
+                continue
+
+            self.oldsck = sck
+
+            # Sample data on rising/falling clock edge (depends on mode).
+            mode = spi_mode[self.options['cpol'], self.options['cpha']]
+            if mode == 0 and sck == 0:   # Sample on rising clock edge
+                    continue
+            elif mode == 1 and sck == 1: # Sample on falling clock edge
+                    continue
+            elif mode == 2 and sck == 1: # Sample on falling clock edge
+                    continue
+            elif mode == 3 and sck == 0: # Sample on rising clock edge
+                    continue
+
+            # If this is the first bit, save its sample number.
+            if self.bitcount == 0:
+                self.start_sample = self.samplenum
+                active_low = (self.options['cs_polarity'] == 'active-low')
+                deasserted = cs if active_low else not cs
+                if deasserted:
+                    self.cs_was_deasserted_during_data_word = 1
+
+            ws = self.options['wordsize']
+
+            # Receive MOSI bit into our shift register.
+            if self.options['bitorder'] == 'msb-first':
+                self.mosidata |= mosi << (ws - 1 - self.bitcount)
+            else:
+                self.mosidata |= mosi << self.bitcount
+
+            # Receive MISO bit into our shift register.
+            if self.options['bitorder'] == 'msb-first':
+                self.misodata |= miso << (ws - 1 - self.bitcount)
+            else:
+                self.misodata |= miso << self.bitcount
+
+            self.bitcount += 1
+
+            # Continue to receive if not enough bits were received, yet.
+            if self.bitcount != ws:
+                continue
+
+            self.put(self.start_sample, self.samplenum, self.out_proto,
+                     ['DATA', self.mosidata, self.misodata])
+            self.put(self.start_sample, self.samplenum, self.out_ann,
+                     [ANN_HEX, ['MOSI: 0x%02x, MISO: 0x%02x' % (self.mosidata,
+                     self.misodata)]])
+
+            if self.cs_was_deasserted_during_data_word:
+                self.put(self.start_sample, self.samplenum, self.out_ann,
+                         [ANN_HEX, ['WARNING: CS# was deasserted during this '
+                         'SPI data byte!']])
+
+            # Reset decoder state.
+            self.mosidata = 0
+            self.misodata = 0
+            self.bitcount = 0
+
+            # Keep stats for summary.
+            self.bytesreceived += 1
+
diff --git a/decoders/spi/spi.py b/decoders/spi/spi.py
deleted file mode 100644 (file)
index 1085786..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# SPI protocol decoder
-
-import sigrokdecode as srd
-
-# Key: (CPOL, CPHA). Value: SPI mode.
-# Clock polarity (CPOL) = 0/1: Clock is low/high when inactive.
-# Clock phase (CPHA) = 0/1: Data is valid on the leading/trailing clock edge.
-spi_mode = {
-    (0, 0): 0, # Mode 0
-    (0, 1): 1, # Mode 1
-    (1, 0): 2, # Mode 2
-    (1, 1): 3, # Mode 3
-}
-
-# Annotation formats
-ANN_HEX = 0
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'spi'
-    name = 'SPI'
-    longname = 'Serial Peripheral Interface'
-    desc = 'Full-duplex, synchronous, serial bus.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['spi']
-    probes = [
-        {'id': 'miso', 'name': 'MISO',
-         'desc': 'SPI MISO line (Master in, slave out)'},
-        {'id': 'mosi', 'name': 'MOSI',
-         'desc': 'SPI MOSI line (Master out, slave in)'},
-        {'id': 'sck', 'name': 'CLK', 'desc': 'SPI clock line'},
-        {'id': 'cs', 'name': 'CS#', 'desc': 'SPI CS (chip select) line'},
-    ]
-    optional_probes = [] # TODO
-    options = {
-        'cs_polarity': ['CS# polarity', 'active-low'],
-        'cpol': ['Clock polarity', 0],
-        'cpha': ['Clock phase', 0],
-        'bitorder': ['Bit order within the SPI data', 'msb-first'],
-        'wordsize': ['Word size of SPI data', 8], # 1-64?
-    }
-    annotations = [
-        ['Hex', 'SPI data bytes in hex format'],
-    ]
-
-    def __init__(self):
-        self.oldsck = 1
-        self.bitcount = 0
-        self.mosidata = 0
-        self.misodata = 0
-        self.bytesreceived = 0
-        self.samplenum = -1
-        self.cs_was_deasserted_during_data_word = 0
-        self.oldcs = -1
-        self.oldpins = None
-
-    def start(self, metadata):
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'spi')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'spi')
-
-    def report(self):
-        return 'SPI: %d bytes received' % self.bytesreceived
-
-    def decode(self, ss, es, data):
-        # TODO: Either MISO or MOSI could be optional. CS# is optional.
-        for (self.samplenum, pins) in data:
-
-            # Ignore identical samples early on (for performance reasons).
-            if self.oldpins == pins:
-                continue
-            self.oldpins, (miso, mosi, sck, cs) = pins, pins
-
-            if self.oldcs != cs:
-                # Send all CS# pin value changes.
-                self.put(self.samplenum, self.samplenum, self.out_proto,
-                         ['CS-CHANGE', self.oldcs, cs])
-                self.put(self.samplenum, self.samplenum, self.out_ann,
-                         [0, ['CS-CHANGE: %d->%d' % (self.oldcs, cs)]])
-                self.oldcs = cs
-
-            # Ignore sample if the clock pin hasn't changed.
-            if sck == self.oldsck:
-                continue
-
-            self.oldsck = sck
-
-            # Sample data on rising/falling clock edge (depends on mode).
-            mode = spi_mode[self.options['cpol'], self.options['cpha']]
-            if mode == 0 and sck == 0:   # Sample on rising clock edge
-                    continue
-            elif mode == 1 and sck == 1: # Sample on falling clock edge
-                    continue
-            elif mode == 2 and sck == 1: # Sample on falling clock edge
-                    continue
-            elif mode == 3 and sck == 0: # Sample on rising clock edge
-                    continue
-
-            # If this is the first bit, save its sample number.
-            if self.bitcount == 0:
-                self.start_sample = self.samplenum
-                active_low = (self.options['cs_polarity'] == 'active-low')
-                deasserted = cs if active_low else not cs
-                if deasserted:
-                    self.cs_was_deasserted_during_data_word = 1
-
-            ws = self.options['wordsize']
-
-            # Receive MOSI bit into our shift register.
-            if self.options['bitorder'] == 'msb-first':
-                self.mosidata |= mosi << (ws - 1 - self.bitcount)
-            else:
-                self.mosidata |= mosi << self.bitcount
-
-            # Receive MISO bit into our shift register.
-            if self.options['bitorder'] == 'msb-first':
-                self.misodata |= miso << (ws - 1 - self.bitcount)
-            else:
-                self.misodata |= miso << self.bitcount
-
-            self.bitcount += 1
-
-            # Continue to receive if not enough bits were received, yet.
-            if self.bitcount != ws:
-                continue
-
-            self.put(self.start_sample, self.samplenum, self.out_proto,
-                     ['DATA', self.mosidata, self.misodata])
-            self.put(self.start_sample, self.samplenum, self.out_ann,
-                     [ANN_HEX, ['MOSI: 0x%02x, MISO: 0x%02x' % (self.mosidata,
-                     self.misodata)]])
-
-            if self.cs_was_deasserted_during_data_word:
-                self.put(self.start_sample, self.samplenum, self.out_ann,
-                         [ANN_HEX, ['WARNING: CS# was deasserted during this '
-                         'SPI data byte!']])
-
-            # Reset decoder state.
-            self.mosidata = 0
-            self.misodata = 0
-            self.bitcount = 0
-
-            # Keep stats for summary.
-            self.bytesreceived += 1
-
index 74688a3e9c22b1f542ec85534edf5dba6dc9e238..b7ee1f2cc2278318fc9e56d79e2f6fe57a327882 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/transitioncounter
 
 
 pkgdatadir = $(DECODERS_DIR)/transitioncounter
 
-dist_pkgdata_DATA = __init__.py transitioncounter.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index f2c3b800cd014d918bb380c7c8f394f261eb2e32..3345da6fbc5a9d708d95369f6c3217d8f30713ff 100644 (file)
@@ -25,5 +25,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .transitioncounter import *
+from .pd import *
 
 
diff --git a/decoders/transitioncounter/pd.py b/decoders/transitioncounter/pd.py
new file mode 100644 (file)
index 0000000..9506cee
--- /dev/null
@@ -0,0 +1,104 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Transition counter protocol decoder
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'transitioncounter'
+    name = 'Transition counter'
+    longname = 'Pin transition counter'
+    desc = 'Counts rising/falling edges in the signal.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['transitioncounts']
+    probes = []
+    optional_probes = []
+    options = {}
+    annotations = [
+        ['TODO', 'TODO'],
+    ]
+
+    def __init__(self, **kwargs):
+        self.channels = -1
+        self.lastsample = None
+
+    def start(self, metadata):
+        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'transitioncounter')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'transitioncounter')
+
+    def report(self):
+        pass
+
+    def decode(self, ss, es, data):
+
+        for (samplenum, s) in data:
+
+            # ...
+            if self.channels == -1:
+                self.channels = len(s)
+                self.oldbit = [0] * self.channels
+                self.transitions = [0] * self.channels
+                self.rising = [0] * self.channels
+                self.falling = [0] * self.channels
+
+            # Optimization: Skip identical samples (no transitions).
+            if self.lastsample == s:
+                continue
+
+            # Upon the first sample, store the initial values.
+            if self.lastsample == None:
+                self.lastsample = s
+                for i in range(self.channels):
+                    self.oldbit[i] = self.lastsample[i]
+
+            # Iterate over all channels/probes in this sample.
+            # Count rising and falling edges for each channel.
+            for i in range(self.channels):
+                curbit = s[i]
+                # Optimization: Skip identical bits (no transitions).
+                if self.oldbit[i] == curbit:
+                    continue
+                elif (self.oldbit[i] == 0 and curbit == 1):
+                    self.rising[i] += 1
+                elif (self.oldbit[i] == 1 and curbit == 0):
+                    self.falling[i] += 1
+                self.oldbit[i] = curbit
+
+            # Save the current sample as 'lastsample' for the next round.
+            self.lastsample = s
+
+        # Total number of transitions = rising + falling edges.
+        for i in range(self.channels):
+            self.transitions[i] = self.rising[i] + self.falling[i]
+
+        # TODO: Which output format?
+        # TODO: How to only output something after the last chunk of data?
+        outdata = []
+        for i in range(self.channels):
+            outdata.append([self.transitions[i], self.rising[i],
+                            self.falling[i]])
+
+        if outdata != []:
+            # self.put(0, 0, self.out_proto, out_proto)
+            self.put(0, 0, self.out_ann, [0, [str(outdata)]])
+
diff --git a/decoders/transitioncounter/transitioncounter.py b/decoders/transitioncounter/transitioncounter.py
deleted file mode 100644 (file)
index 9506cee..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# Transition counter protocol decoder
-
-import sigrokdecode as srd
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'transitioncounter'
-    name = 'Transition counter'
-    longname = 'Pin transition counter'
-    desc = 'Counts rising/falling edges in the signal.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['transitioncounts']
-    probes = []
-    optional_probes = []
-    options = {}
-    annotations = [
-        ['TODO', 'TODO'],
-    ]
-
-    def __init__(self, **kwargs):
-        self.channels = -1
-        self.lastsample = None
-
-    def start(self, metadata):
-        # self.out_proto = self.add(srd.OUTPUT_PROTO, 'transitioncounter')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'transitioncounter')
-
-    def report(self):
-        pass
-
-    def decode(self, ss, es, data):
-
-        for (samplenum, s) in data:
-
-            # ...
-            if self.channels == -1:
-                self.channels = len(s)
-                self.oldbit = [0] * self.channels
-                self.transitions = [0] * self.channels
-                self.rising = [0] * self.channels
-                self.falling = [0] * self.channels
-
-            # Optimization: Skip identical samples (no transitions).
-            if self.lastsample == s:
-                continue
-
-            # Upon the first sample, store the initial values.
-            if self.lastsample == None:
-                self.lastsample = s
-                for i in range(self.channels):
-                    self.oldbit[i] = self.lastsample[i]
-
-            # Iterate over all channels/probes in this sample.
-            # Count rising and falling edges for each channel.
-            for i in range(self.channels):
-                curbit = s[i]
-                # Optimization: Skip identical bits (no transitions).
-                if self.oldbit[i] == curbit:
-                    continue
-                elif (self.oldbit[i] == 0 and curbit == 1):
-                    self.rising[i] += 1
-                elif (self.oldbit[i] == 1 and curbit == 0):
-                    self.falling[i] += 1
-                self.oldbit[i] = curbit
-
-            # Save the current sample as 'lastsample' for the next round.
-            self.lastsample = s
-
-        # Total number of transitions = rising + falling edges.
-        for i in range(self.channels):
-            self.transitions[i] = self.rising[i] + self.falling[i]
-
-        # TODO: Which output format?
-        # TODO: How to only output something after the last chunk of data?
-        outdata = []
-        for i in range(self.channels):
-            outdata.append([self.transitions[i], self.rising[i],
-                            self.falling[i]])
-
-        if outdata != []:
-            # self.put(0, 0, self.out_proto, out_proto)
-            self.put(0, 0, self.out_ann, [0, [str(outdata)]])
-
index 155bfc989db9bf3c0b3887bae7ab8c49546374f5..61efbd0c8aa9bd60db2c1e14c9c5e64a7363f941 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/uart
 
 
 pkgdatadir = $(DECODERS_DIR)/uart
 
-dist_pkgdata_DATA = __init__.py uart.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index ecba2e1ac04a03893354ee5c2292015488ddd050..23e2dd0398afedb9a61c9eabd35c04b114308f12 100644 (file)
@@ -111,5 +111,5 @@ This is the list of <packet-type>s and their respective <packet-data>:
 The <rxtx> field is 0 for RX packets, 1 for TX packets.
 '''
 
 The <rxtx> field is 0 for RX packets, 1 for TX packets.
 '''
 
-from .uart import *
+from .pd import *
 
 
diff --git a/decoders/uart/pd.py b/decoders/uart/pd.py
new file mode 100644 (file)
index 0000000..24551da
--- /dev/null
@@ -0,0 +1,304 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# UART protocol decoder
+
+import sigrokdecode as srd
+
+# Used for differentiating between the two data directions.
+RX = 0
+TX = 1
+
+# Annotation feed formats
+ANN_ASCII = 0
+ANN_DEC = 1
+ANN_HEX = 2
+ANN_OCT = 3
+ANN_BITS = 4
+
+# Given a parity type to check (odd, even, zero, one), the value of the
+# parity bit, the value of the data, and the length of the data (5-9 bits,
+# usually 8 bits) return True if the parity is correct, False otherwise.
+# 'none' is _not_ allowed as value for 'parity_type'.
+def parity_ok(parity_type, parity_bit, data, num_data_bits):
+
+    # Handle easy cases first (parity bit is always 1 or 0).
+    if parity_type == 'zero':
+        return parity_bit == 0
+    elif parity_type == 'one':
+        return parity_bit == 1
+
+    # Count number of 1 (high) bits in the data (and the parity bit itself!).
+    ones = bin(data).count('1') + parity_bit
+
+    # Check for odd/even parity.
+    if parity_type == 'odd':
+        return (ones % 2) == 1
+    elif parity_type == 'even':
+        return (ones % 2) == 0
+    else:
+        raise Exception('Invalid parity type: %d' % parity_type)
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'uart'
+    name = 'UART'
+    longname = 'Universal Asynchronous Receiver/Transmitter'
+    desc = 'Asynchronous, serial bus.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['uart']
+    probes = [
+        # Allow specifying only one of the signals, e.g. if only one data
+        # direction exists (or is relevant).
+        {'id': 'rx', 'name': 'RX', 'desc': 'UART receive line'},
+        {'id': 'tx', 'name': 'TX', 'desc': 'UART transmit line'},
+    ]
+    optional_probes = []
+    options = {
+        'baudrate': ['Baud rate', 115200],
+        'num_data_bits': ['Data bits', 8], # Valid: 5-9.
+        'parity_type': ['Parity type', 'none'],
+        'parity_check': ['Check parity?', 'yes'], # TODO: Bool supported?
+        'num_stop_bits': ['Stop bit(s)', '1'], # String! 0, 0.5, 1, 1.5.
+        'bit_order': ['Bit order', 'lsb-first'],
+        # TODO: Options to invert the signal(s).
+    }
+    annotations = [
+        ['ASCII', 'Data bytes as ASCII characters'],
+        ['Decimal', 'Databytes as decimal, integer values'],
+        ['Hex', 'Data bytes in hex format'],
+        ['Octal', 'Data bytes as octal numbers'],
+        ['Bits', 'Data bytes in bit notation (sequence of 0/1 digits)'],
+    ]
+
+    def putx(self, rxtx, data):
+        self.put(self.startsample[rxtx], self.samplenum - 1, self.out_ann, data)
+
+    def __init__(self, **kwargs):
+        self.samplenum = 0
+        self.frame_start = [-1, -1]
+        self.startbit = [-1, -1]
+        self.cur_data_bit = [0, 0]
+        self.databyte = [0, 0]
+        self.paritybit = [-1, -1]
+        self.stopbit1 = [-1, -1]
+        self.startsample = [-1, -1]
+        self.state = ['WAIT FOR START BIT', 'WAIT FOR START BIT']
+        self.oldbit = [None, None]
+        self.oldpins = None
+
+    def start(self, metadata):
+        self.samplerate = metadata['samplerate']
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'uart')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'uart')
+
+        # The width of one UART bit in number of samples.
+        self.bit_width = \
+            float(self.samplerate) / float(self.options['baudrate'])
+
+    def report(self):
+        pass
+
+    # Return true if we reached the middle of the desired bit, false otherwise.
+    def reached_bit(self, rxtx, bitnum):
+        # bitpos is the samplenumber which is in the middle of the
+        # specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit
+        # (if used) or the first stop bit, and so on).
+        bitpos = self.frame_start[rxtx] + (self.bit_width / 2.0)
+        bitpos += bitnum * self.bit_width
+        if self.samplenum >= bitpos:
+            return True
+        return False
+
+    def reached_bit_last(self, rxtx, bitnum):
+        bitpos = self.frame_start[rxtx] + ((bitnum + 1) * self.bit_width)
+        if self.samplenum >= bitpos:
+            return True
+        return False
+
+    def wait_for_start_bit(self, rxtx, old_signal, signal):
+        # The start bit is always 0 (low). As the idle UART (and the stop bit)
+        # level is 1 (high), the beginning of a start bit is a falling edge.
+        if not (old_signal == 1 and signal == 0):
+            return
+
+        # Save the sample number where the start bit begins.
+        self.frame_start[rxtx] = self.samplenum
+
+        self.state[rxtx] = 'GET START BIT'
+
+    def get_start_bit(self, rxtx, signal):
+        # Skip samples until we're in the middle of the start bit.
+        if not self.reached_bit(rxtx, 0):
+            return
+
+        self.startbit[rxtx] = signal
+
+        # The startbit must be 0. If not, we report an error.
+        if self.startbit[rxtx] != 0:
+            self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
+                     ['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
+            # TODO: Abort? Ignore rest of the frame?
+
+        self.cur_data_bit[rxtx] = 0
+        self.databyte[rxtx] = 0
+        self.startsample[rxtx] = -1
+
+        self.state[rxtx] = 'GET DATA BITS'
+
+        self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
+                 ['STARTBIT', rxtx, self.startbit[rxtx]])
+        self.put(self.frame_start[rxtx], self.samplenum, self.out_ann,
+                 [ANN_ASCII, ['Start bit', 'Start', 'S']])
+
+    def get_data_bits(self, rxtx, signal):
+        # Skip samples until we're in the middle of the desired data bit.
+        if not self.reached_bit(rxtx, self.cur_data_bit[rxtx] + 1):
+            return
+
+        # Save the sample number where the data byte starts.
+        if self.startsample[rxtx] == -1:
+            self.startsample[rxtx] = self.samplenum
+
+        # Get the next data bit in LSB-first or MSB-first fashion.
+        if self.options['bit_order'] == 'lsb-first':
+            self.databyte[rxtx] >>= 1
+            self.databyte[rxtx] |= \
+                (signal << (self.options['num_data_bits'] - 1))
+        elif self.options['bit_order'] == 'msb-first':
+            self.databyte[rxtx] <<= 1
+            self.databyte[rxtx] |= (signal << 0)
+        else:
+            raise Exception('Invalid bit order value: %s',
+                            self.options['bit_order'])
+
+        # Return here, unless we already received all data bits.
+        # TODO? Off-by-one?
+        if self.cur_data_bit[rxtx] < self.options['num_data_bits'] - 1:
+            self.cur_data_bit[rxtx] += 1
+            return
+
+        self.state[rxtx] = 'GET PARITY BIT'
+
+        self.put(self.startsample[rxtx], self.samplenum - 1, self.out_proto,
+                 ['DATA', rxtx, self.databyte[rxtx]])
+
+        s = 'RX: ' if (rxtx == RX) else 'TX: '
+        self.putx(rxtx, [ANN_ASCII, [s + chr(self.databyte[rxtx])]])
+        self.putx(rxtx, [ANN_DEC,   [s + str(self.databyte[rxtx])]])
+        self.putx(rxtx, [ANN_HEX,   [s + hex(self.databyte[rxtx]),
+                                     s + hex(self.databyte[rxtx])[2:]]])
+        self.putx(rxtx, [ANN_OCT,   [s + oct(self.databyte[rxtx]),
+                                     s + oct(self.databyte[rxtx])[2:]]])
+        self.putx(rxtx, [ANN_BITS,  [s + bin(self.databyte[rxtx]),
+                                     s + bin(self.databyte[rxtx])[2:]]])
+
+    def get_parity_bit(self, rxtx, signal):
+        # If no parity is used/configured, skip to the next state immediately.
+        if self.options['parity_type'] == 'none':
+            self.state[rxtx] = 'GET STOP BITS'
+            return
+
+        # Skip samples until we're in the middle of the parity bit.
+        if not self.reached_bit(rxtx, self.options['num_data_bits'] + 1):
+            return
+
+        self.paritybit[rxtx] = signal
+
+        self.state[rxtx] = 'GET STOP BITS'
+
+        if parity_ok(self.options['parity_type'], self.paritybit[rxtx],
+                     self.databyte[rxtx], self.options['num_data_bits']):
+            # TODO: Fix range.
+            self.put(self.samplenum, self.samplenum, self.out_proto,
+                     ['PARITYBIT', rxtx, self.paritybit[rxtx]])
+            self.put(self.samplenum, self.samplenum, self.out_ann,
+                     [ANN_ASCII, ['Parity bit', 'Parity', 'P']])
+        else:
+            # TODO: Fix range.
+            # TODO: Return expected/actual parity values.
+            self.put(self.samplenum, self.samplenum, self.out_proto,
+                     ['PARITY ERROR', rxtx, (0, 1)]) # FIXME: Dummy tuple...
+            self.put(self.samplenum, self.samplenum, self.out_ann,
+                     [ANN_ASCII, ['Parity error', 'Parity err', 'PE']])
+
+    # TODO: Currently only supports 1 stop bit.
+    def get_stop_bits(self, rxtx, signal):
+        # Skip samples until we're in the middle of the stop bit(s).
+        skip_parity = 0 if self.options['parity_type'] == 'none' else 1
+        b = self.options['num_data_bits'] + 1 + skip_parity
+        if not self.reached_bit(rxtx, b):
+            return
+
+        self.stopbit1[rxtx] = signal
+
+        # Stop bits must be 1. If not, we report an error.
+        if self.stopbit1[rxtx] != 1:
+            self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
+                     ['INVALID STOPBIT', rxtx, self.stopbit1[rxtx]])
+            # TODO: Abort? Ignore the frame? Other?
+
+        self.state[rxtx] = 'WAIT FOR START BIT'
+
+        # TODO: Fix range.
+        self.put(self.samplenum, self.samplenum, self.out_proto,
+                 ['STOPBIT', rxtx, self.stopbit1[rxtx]])
+        self.put(self.samplenum, self.samplenum, self.out_ann,
+                 [ANN_ASCII, ['Stop bit', 'Stop', 'P']])
+
+    def decode(self, ss, es, data):
+        # TODO: Either RX or TX could be omitted (optional probe).
+        for (self.samplenum, pins) in data:
+
+            # Note: Ignoring identical samples here for performance reasons
+            # is not possible for this PD, at least not in the current state.
+            # if self.oldpins == pins:
+            #     continue
+            self.oldpins, (rx, tx) = pins, pins
+
+            # First sample: Save RX/TX value.
+            if self.oldbit[RX] == None:
+                self.oldbit[RX] = rx
+                continue
+            if self.oldbit[TX] == None:
+                self.oldbit[TX] = tx
+                continue
+
+            # State machine.
+            for rxtx in (RX, TX):
+                signal = rx if (rxtx == RX) else tx
+
+                if self.state[rxtx] == 'WAIT FOR START BIT':
+                    self.wait_for_start_bit(rxtx, self.oldbit[rxtx], signal)
+                elif self.state[rxtx] == 'GET START BIT':
+                    self.get_start_bit(rxtx, signal)
+                elif self.state[rxtx] == 'GET DATA BITS':
+                    self.get_data_bits(rxtx, signal)
+                elif self.state[rxtx] == 'GET PARITY BIT':
+                    self.get_parity_bit(rxtx, signal)
+                elif self.state[rxtx] == 'GET STOP BITS':
+                    self.get_stop_bits(rxtx, signal)
+                else:
+                    raise Exception('Invalid state: %d' % self.state[rxtx])
+
+                # Save current RX/TX values for the next round.
+                self.oldbit[rxtx] = signal
+
diff --git a/decoders/uart/uart.py b/decoders/uart/uart.py
deleted file mode 100644 (file)
index 24551da..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011-2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# UART protocol decoder
-
-import sigrokdecode as srd
-
-# Used for differentiating between the two data directions.
-RX = 0
-TX = 1
-
-# Annotation feed formats
-ANN_ASCII = 0
-ANN_DEC = 1
-ANN_HEX = 2
-ANN_OCT = 3
-ANN_BITS = 4
-
-# Given a parity type to check (odd, even, zero, one), the value of the
-# parity bit, the value of the data, and the length of the data (5-9 bits,
-# usually 8 bits) return True if the parity is correct, False otherwise.
-# 'none' is _not_ allowed as value for 'parity_type'.
-def parity_ok(parity_type, parity_bit, data, num_data_bits):
-
-    # Handle easy cases first (parity bit is always 1 or 0).
-    if parity_type == 'zero':
-        return parity_bit == 0
-    elif parity_type == 'one':
-        return parity_bit == 1
-
-    # Count number of 1 (high) bits in the data (and the parity bit itself!).
-    ones = bin(data).count('1') + parity_bit
-
-    # Check for odd/even parity.
-    if parity_type == 'odd':
-        return (ones % 2) == 1
-    elif parity_type == 'even':
-        return (ones % 2) == 0
-    else:
-        raise Exception('Invalid parity type: %d' % parity_type)
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'uart'
-    name = 'UART'
-    longname = 'Universal Asynchronous Receiver/Transmitter'
-    desc = 'Asynchronous, serial bus.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['uart']
-    probes = [
-        # Allow specifying only one of the signals, e.g. if only one data
-        # direction exists (or is relevant).
-        {'id': 'rx', 'name': 'RX', 'desc': 'UART receive line'},
-        {'id': 'tx', 'name': 'TX', 'desc': 'UART transmit line'},
-    ]
-    optional_probes = []
-    options = {
-        'baudrate': ['Baud rate', 115200],
-        'num_data_bits': ['Data bits', 8], # Valid: 5-9.
-        'parity_type': ['Parity type', 'none'],
-        'parity_check': ['Check parity?', 'yes'], # TODO: Bool supported?
-        'num_stop_bits': ['Stop bit(s)', '1'], # String! 0, 0.5, 1, 1.5.
-        'bit_order': ['Bit order', 'lsb-first'],
-        # TODO: Options to invert the signal(s).
-    }
-    annotations = [
-        ['ASCII', 'Data bytes as ASCII characters'],
-        ['Decimal', 'Databytes as decimal, integer values'],
-        ['Hex', 'Data bytes in hex format'],
-        ['Octal', 'Data bytes as octal numbers'],
-        ['Bits', 'Data bytes in bit notation (sequence of 0/1 digits)'],
-    ]
-
-    def putx(self, rxtx, data):
-        self.put(self.startsample[rxtx], self.samplenum - 1, self.out_ann, data)
-
-    def __init__(self, **kwargs):
-        self.samplenum = 0
-        self.frame_start = [-1, -1]
-        self.startbit = [-1, -1]
-        self.cur_data_bit = [0, 0]
-        self.databyte = [0, 0]
-        self.paritybit = [-1, -1]
-        self.stopbit1 = [-1, -1]
-        self.startsample = [-1, -1]
-        self.state = ['WAIT FOR START BIT', 'WAIT FOR START BIT']
-        self.oldbit = [None, None]
-        self.oldpins = None
-
-    def start(self, metadata):
-        self.samplerate = metadata['samplerate']
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'uart')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'uart')
-
-        # The width of one UART bit in number of samples.
-        self.bit_width = \
-            float(self.samplerate) / float(self.options['baudrate'])
-
-    def report(self):
-        pass
-
-    # Return true if we reached the middle of the desired bit, false otherwise.
-    def reached_bit(self, rxtx, bitnum):
-        # bitpos is the samplenumber which is in the middle of the
-        # specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit
-        # (if used) or the first stop bit, and so on).
-        bitpos = self.frame_start[rxtx] + (self.bit_width / 2.0)
-        bitpos += bitnum * self.bit_width
-        if self.samplenum >= bitpos:
-            return True
-        return False
-
-    def reached_bit_last(self, rxtx, bitnum):
-        bitpos = self.frame_start[rxtx] + ((bitnum + 1) * self.bit_width)
-        if self.samplenum >= bitpos:
-            return True
-        return False
-
-    def wait_for_start_bit(self, rxtx, old_signal, signal):
-        # The start bit is always 0 (low). As the idle UART (and the stop bit)
-        # level is 1 (high), the beginning of a start bit is a falling edge.
-        if not (old_signal == 1 and signal == 0):
-            return
-
-        # Save the sample number where the start bit begins.
-        self.frame_start[rxtx] = self.samplenum
-
-        self.state[rxtx] = 'GET START BIT'
-
-    def get_start_bit(self, rxtx, signal):
-        # Skip samples until we're in the middle of the start bit.
-        if not self.reached_bit(rxtx, 0):
-            return
-
-        self.startbit[rxtx] = signal
-
-        # The startbit must be 0. If not, we report an error.
-        if self.startbit[rxtx] != 0:
-            self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
-                     ['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
-            # TODO: Abort? Ignore rest of the frame?
-
-        self.cur_data_bit[rxtx] = 0
-        self.databyte[rxtx] = 0
-        self.startsample[rxtx] = -1
-
-        self.state[rxtx] = 'GET DATA BITS'
-
-        self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
-                 ['STARTBIT', rxtx, self.startbit[rxtx]])
-        self.put(self.frame_start[rxtx], self.samplenum, self.out_ann,
-                 [ANN_ASCII, ['Start bit', 'Start', 'S']])
-
-    def get_data_bits(self, rxtx, signal):
-        # Skip samples until we're in the middle of the desired data bit.
-        if not self.reached_bit(rxtx, self.cur_data_bit[rxtx] + 1):
-            return
-
-        # Save the sample number where the data byte starts.
-        if self.startsample[rxtx] == -1:
-            self.startsample[rxtx] = self.samplenum
-
-        # Get the next data bit in LSB-first or MSB-first fashion.
-        if self.options['bit_order'] == 'lsb-first':
-            self.databyte[rxtx] >>= 1
-            self.databyte[rxtx] |= \
-                (signal << (self.options['num_data_bits'] - 1))
-        elif self.options['bit_order'] == 'msb-first':
-            self.databyte[rxtx] <<= 1
-            self.databyte[rxtx] |= (signal << 0)
-        else:
-            raise Exception('Invalid bit order value: %s',
-                            self.options['bit_order'])
-
-        # Return here, unless we already received all data bits.
-        # TODO? Off-by-one?
-        if self.cur_data_bit[rxtx] < self.options['num_data_bits'] - 1:
-            self.cur_data_bit[rxtx] += 1
-            return
-
-        self.state[rxtx] = 'GET PARITY BIT'
-
-        self.put(self.startsample[rxtx], self.samplenum - 1, self.out_proto,
-                 ['DATA', rxtx, self.databyte[rxtx]])
-
-        s = 'RX: ' if (rxtx == RX) else 'TX: '
-        self.putx(rxtx, [ANN_ASCII, [s + chr(self.databyte[rxtx])]])
-        self.putx(rxtx, [ANN_DEC,   [s + str(self.databyte[rxtx])]])
-        self.putx(rxtx, [ANN_HEX,   [s + hex(self.databyte[rxtx]),
-                                     s + hex(self.databyte[rxtx])[2:]]])
-        self.putx(rxtx, [ANN_OCT,   [s + oct(self.databyte[rxtx]),
-                                     s + oct(self.databyte[rxtx])[2:]]])
-        self.putx(rxtx, [ANN_BITS,  [s + bin(self.databyte[rxtx]),
-                                     s + bin(self.databyte[rxtx])[2:]]])
-
-    def get_parity_bit(self, rxtx, signal):
-        # If no parity is used/configured, skip to the next state immediately.
-        if self.options['parity_type'] == 'none':
-            self.state[rxtx] = 'GET STOP BITS'
-            return
-
-        # Skip samples until we're in the middle of the parity bit.
-        if not self.reached_bit(rxtx, self.options['num_data_bits'] + 1):
-            return
-
-        self.paritybit[rxtx] = signal
-
-        self.state[rxtx] = 'GET STOP BITS'
-
-        if parity_ok(self.options['parity_type'], self.paritybit[rxtx],
-                     self.databyte[rxtx], self.options['num_data_bits']):
-            # TODO: Fix range.
-            self.put(self.samplenum, self.samplenum, self.out_proto,
-                     ['PARITYBIT', rxtx, self.paritybit[rxtx]])
-            self.put(self.samplenum, self.samplenum, self.out_ann,
-                     [ANN_ASCII, ['Parity bit', 'Parity', 'P']])
-        else:
-            # TODO: Fix range.
-            # TODO: Return expected/actual parity values.
-            self.put(self.samplenum, self.samplenum, self.out_proto,
-                     ['PARITY ERROR', rxtx, (0, 1)]) # FIXME: Dummy tuple...
-            self.put(self.samplenum, self.samplenum, self.out_ann,
-                     [ANN_ASCII, ['Parity error', 'Parity err', 'PE']])
-
-    # TODO: Currently only supports 1 stop bit.
-    def get_stop_bits(self, rxtx, signal):
-        # Skip samples until we're in the middle of the stop bit(s).
-        skip_parity = 0 if self.options['parity_type'] == 'none' else 1
-        b = self.options['num_data_bits'] + 1 + skip_parity
-        if not self.reached_bit(rxtx, b):
-            return
-
-        self.stopbit1[rxtx] = signal
-
-        # Stop bits must be 1. If not, we report an error.
-        if self.stopbit1[rxtx] != 1:
-            self.put(self.frame_start[rxtx], self.samplenum, self.out_proto,
-                     ['INVALID STOPBIT', rxtx, self.stopbit1[rxtx]])
-            # TODO: Abort? Ignore the frame? Other?
-
-        self.state[rxtx] = 'WAIT FOR START BIT'
-
-        # TODO: Fix range.
-        self.put(self.samplenum, self.samplenum, self.out_proto,
-                 ['STOPBIT', rxtx, self.stopbit1[rxtx]])
-        self.put(self.samplenum, self.samplenum, self.out_ann,
-                 [ANN_ASCII, ['Stop bit', 'Stop', 'P']])
-
-    def decode(self, ss, es, data):
-        # TODO: Either RX or TX could be omitted (optional probe).
-        for (self.samplenum, pins) in data:
-
-            # Note: Ignoring identical samples here for performance reasons
-            # is not possible for this PD, at least not in the current state.
-            # if self.oldpins == pins:
-            #     continue
-            self.oldpins, (rx, tx) = pins, pins
-
-            # First sample: Save RX/TX value.
-            if self.oldbit[RX] == None:
-                self.oldbit[RX] = rx
-                continue
-            if self.oldbit[TX] == None:
-                self.oldbit[TX] = tx
-                continue
-
-            # State machine.
-            for rxtx in (RX, TX):
-                signal = rx if (rxtx == RX) else tx
-
-                if self.state[rxtx] == 'WAIT FOR START BIT':
-                    self.wait_for_start_bit(rxtx, self.oldbit[rxtx], signal)
-                elif self.state[rxtx] == 'GET START BIT':
-                    self.get_start_bit(rxtx, signal)
-                elif self.state[rxtx] == 'GET DATA BITS':
-                    self.get_data_bits(rxtx, signal)
-                elif self.state[rxtx] == 'GET PARITY BIT':
-                    self.get_parity_bit(rxtx, signal)
-                elif self.state[rxtx] == 'GET STOP BITS':
-                    self.get_stop_bits(rxtx, signal)
-                else:
-                    raise Exception('Invalid state: %d' % self.state[rxtx])
-
-                # Save current RX/TX values for the next round.
-                self.oldbit[rxtx] = signal
-
index d95618c8a492789b0481801a002263717d22812f..ca9f532dadbeb64d8851cbccc60f6711e0daf072 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/uart_dump
 
 
 pkgdatadir = $(DECODERS_DIR)/uart_dump
 
-dist_pkgdata_DATA = __init__.py uart_dump.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 06a6d95d0dc8bc3498daf029d17f59282eb0d954..ea351ab6af6e2a43f2b73fca71d6265c33a0291b 100644 (file)
@@ -25,5 +25,5 @@ Details:
 TODO
 '''
 
 TODO
 '''
 
-from .uart_dump import *
+from .pd import *
 
 
diff --git a/decoders/uart_dump/pd.py b/decoders/uart_dump/pd.py
new file mode 100644 (file)
index 0000000..1174832
--- /dev/null
@@ -0,0 +1,110 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+import os
+import sys
+
+RX = 0
+TX = 1
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'uart_dump'
+    name = 'UART dump'
+    longname = 'UART dump'
+    desc = 'Output decoded UART data to a file.'
+    license = 'gplv2+'
+    inputs = ['uart']
+    outputs = [] # TODO?
+    probes = []
+    optional_probes = []
+    options = {
+        'rx': ['Output RX data?', 'yes'],
+        'tx': ['Output TX data?', 'yes'],
+        'filename': ['File name for RX and TX data', '-'],
+        'filename_rx': ['File name for RX data', 'none'],
+        'filename_tx': ['File name for TX data', 'none'],
+    }
+    annotations = []
+
+    def __init__(self, **kwargs):
+        self.f = None
+        self.f_rx = None
+        self.f_tx = None
+
+    def file_open(self, filename):
+        if filename == 'none':
+            return None
+        elif filename == '-':
+            return sys.stdout
+        else:
+            return open(filename, 'w')
+
+    def start(self, metadata):
+        # The user can specify 'filename' (gets both RX and TX data), and/or
+        # 'filename_rx' (for RX data only), and/or 'filename_tx', respectively.
+
+        # Default is to output RX and TX to 'filename', with 'filename_rx' and
+        # 'filename_tx' being unused.
+
+        # If multiple 'filename*' options are specified, the user must NOT
+        # use the same file for any of them.
+
+        # A filename of 'none' is not allowed (has special meaning). A filename
+        # of '-' means 'stdout'.
+
+        self.f = self.file_open(self.options['filename'])
+        self.f_rx = self.file_open(self.options['filename_rx'])
+        self.f_tx = self.file_open(self.options['filename_tx'])
+
+    def report(self):
+        pass
+
+    def decode(self, ss, es, data):
+        ptype, rxtx, pdata = data
+
+        # Ignore all UART packets except the actual data packets (i.e., we
+        # do not print start bits, parity bits, stop bits, errors, and so on).
+        if ptype != 'DATA':
+            return
+
+        # TODO: Configurable format.
+        c = chr(pdata)
+
+        # TODO: Error handling.
+
+        # Output RX and/or TX to 'filename'.
+        if self.f != None:
+            self.f.write(c)
+            self.f.flush()
+
+        # Output RX data to 'filename_rx'.
+        if self.f_rx != None:
+            if self.options['rx'] == 'yes' and rxtx == RX:
+                self.f_rx.write(c)
+                self.f_rx.flush()
+
+        # Output TX data to 'filename_tx'.
+        if self.f_tx != None:
+            if self.options['tx'] == 'yes' and rxtx == TX:
+                self.f_tx.write(c)
+                self.f_tx.flush()
+
diff --git a/decoders/uart_dump/uart_dump.py b/decoders/uart_dump/uart_dump.py
deleted file mode 100644 (file)
index 1174832..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-import sigrokdecode as srd
-import os
-import sys
-
-RX = 0
-TX = 1
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'uart_dump'
-    name = 'UART dump'
-    longname = 'UART dump'
-    desc = 'Output decoded UART data to a file.'
-    license = 'gplv2+'
-    inputs = ['uart']
-    outputs = [] # TODO?
-    probes = []
-    optional_probes = []
-    options = {
-        'rx': ['Output RX data?', 'yes'],
-        'tx': ['Output TX data?', 'yes'],
-        'filename': ['File name for RX and TX data', '-'],
-        'filename_rx': ['File name for RX data', 'none'],
-        'filename_tx': ['File name for TX data', 'none'],
-    }
-    annotations = []
-
-    def __init__(self, **kwargs):
-        self.f = None
-        self.f_rx = None
-        self.f_tx = None
-
-    def file_open(self, filename):
-        if filename == 'none':
-            return None
-        elif filename == '-':
-            return sys.stdout
-        else:
-            return open(filename, 'w')
-
-    def start(self, metadata):
-        # The user can specify 'filename' (gets both RX and TX data), and/or
-        # 'filename_rx' (for RX data only), and/or 'filename_tx', respectively.
-
-        # Default is to output RX and TX to 'filename', with 'filename_rx' and
-        # 'filename_tx' being unused.
-
-        # If multiple 'filename*' options are specified, the user must NOT
-        # use the same file for any of them.
-
-        # A filename of 'none' is not allowed (has special meaning). A filename
-        # of '-' means 'stdout'.
-
-        self.f = self.file_open(self.options['filename'])
-        self.f_rx = self.file_open(self.options['filename_rx'])
-        self.f_tx = self.file_open(self.options['filename_tx'])
-
-    def report(self):
-        pass
-
-    def decode(self, ss, es, data):
-        ptype, rxtx, pdata = data
-
-        # Ignore all UART packets except the actual data packets (i.e., we
-        # do not print start bits, parity bits, stop bits, errors, and so on).
-        if ptype != 'DATA':
-            return
-
-        # TODO: Configurable format.
-        c = chr(pdata)
-
-        # TODO: Error handling.
-
-        # Output RX and/or TX to 'filename'.
-        if self.f != None:
-            self.f.write(c)
-            self.f.flush()
-
-        # Output RX data to 'filename_rx'.
-        if self.f_rx != None:
-            if self.options['rx'] == 'yes' and rxtx == RX:
-                self.f_rx.write(c)
-                self.f_rx.flush()
-
-        # Output TX data to 'filename_tx'.
-        if self.f_tx != None:
-            if self.options['tx'] == 'yes' and rxtx == TX:
-                self.f_tx.write(c)
-                self.f_tx.flush()
-
index 599092bc5c51b5aa82e576960e035be575e4563c..d76c1a455dfabf19c77e735ed382a72dc6f4639a 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/usb_protocol
 
 
 pkgdatadir = $(DECODERS_DIR)/usb_protocol
 
-dist_pkgdata_DATA = __init__.py usb_protocol.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index a19304e4efdba842dfbfcbd377fee333f83bd5a3..735e3f8a8f55b30aa3c1e91a108dbb2b656eaab6 100644 (file)
@@ -43,5 +43,5 @@ https://en.wikipedia.org/wiki/USB
 http://www.usb.org/developers/docs/
 '''
 
 http://www.usb.org/developers/docs/
 '''
 
-from .usb_protocol import *
+from .pd import *
 
 
diff --git a/decoders/usb_protocol/pd.py b/decoders/usb_protocol/pd.py
new file mode 100644 (file)
index 0000000..b1cd33b
--- /dev/null
@@ -0,0 +1,137 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# USB (low-speed and full-speed) protocol decoder
+
+import sigrokdecode as srd
+
+# Packet IDs (PIDs).
+# The first 4 bits are the 'packet type' field, the last 4 bits are the
+# 'check field' (each bit in the check field must be the inverse of the resp.
+# bit in the 'packet type' field; if not, that's a 'PID error').
+# For the 4-bit strings, the left-most '1' or '0' is the LSB, i.e. it's sent
+# to the bus first.
+pids = {
+    # Tokens
+    '10000111': ['OUT', 'Address & EP number in host-to-function transaction'],
+    '10010110': ['IN', 'Address & EP number in function-to-host transaction'],
+    '10100101': ['SOF', 'Start-Of-Frame marker & frame number'],
+    '10110100': ['SETUP', 'Address & EP number in host-to-function transaction for SETUP to a control pipe'],
+
+    # Data
+    # Note: DATA2 and MDATA are HS-only.
+    '11000011': ['DATA0', 'Data packet PID even'],
+    '11010010': ['DATA1', 'Data packet PID odd'],
+    '11100001': ['DATA2', 'Data packet PID HS, high bandwidth isosynchronous transaction in a microframe'],
+    '11110000': ['MDATA', 'Data packet PID HS for split and high-bandwidth isosynchronous transactions'],
+
+    # Handshake
+    '01001011': ['ACK', 'Receiver accepts error-free packet'],
+    '01011010': ['NAK', 'Receiver cannot accept or transmitter cannot send'],
+    '01111000': ['STALL', 'EP halted or control pipe request unsupported'],
+    '01101001': ['NYET', 'No response yet from receiver'],
+
+    # Special
+    '00111100': ['PRE', 'Host-issued preamble; enables downstream bus traffic to low-speed devices'],
+    '00111100': ['ERR', 'Split transaction error handshake'],
+    '00011110': ['SPLIT', 'HS split transaction token'],
+    '00101101': ['PING', 'HS flow control probe for a bulk/control EP'],
+    '00001111': ['Reserved', 'Reserved PID'],
+}
+
+def bitstr_to_num(bitstr):
+    if not bitstr:
+        return 0
+    l = list(bitstr)
+    l.reverse()
+    return int(''.join(l), 2)
+
+def packet_decode(packet):
+    sync = packet[:8]
+    pid = packet[8:16]
+    pid = pids.get(pid, (pid, ''))[0]
+
+    # Remove CRC.
+    if pid in ('OUT', 'IN', 'SOF', 'SETUP'):
+        data = packet[16:-5]
+        if pid == 'SOF':
+            data = str(bitstr_to_num(data))
+        else:
+            dev = bitstr_to_num(data[:7])
+            ep = bitstr_to_num(data[7:])
+            data = 'DEV %d EP %d' % (dev, ep)
+    elif pid in ('DATA0', 'DATA1'):
+        data = packet[16:-16]
+        tmp = ''
+        while data:
+            tmp += '%02x ' % bitstr_to_num(data[:8])
+            data = data[8:]
+        data = tmp
+    else:
+        data = packet[16:]
+
+    # The SYNC pattern for low-speed/full-speed is KJKJKJKK (0001).
+    if sync != '00000001':
+        return 'SYNC INVALID: %s' % sync
+
+    return pid + ' ' + data
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'usb_protocol'
+    name = 'USB protocol'
+    longname = 'Universal Serial Bus (LS/FS) protocol'
+    desc = 'USB 1.x (low-speed and full-speed) serial protocol.'
+    license = 'gplv2+'
+    inputs = ['usb_signalling']
+    outputs = ['usb_protocol']
+    probes = []
+    optional_probes = []
+    options = {
+        'signalling': ['Signalling', 'full-speed'],
+    }
+    annotations = [
+        ['Text', 'Human-readable text']
+    ]
+
+    def __init__(self):
+        self.sym = 'J'
+        self.samplenum = 0
+        self.scount = 0
+        self.packet = ''
+        self.state = 'IDLE'
+
+    def start(self, metadata):
+        self.samplerate = metadata['samplerate']
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb_protocol')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'usb_protocol')
+
+    def report(self):
+        pass
+
+    def decode(self, ss, es, data):
+        (ptype, pdata) = data
+
+        if ptype == 'PACKET':
+            self.put(0, 0, self.out_ann, [0, [packet_decode(pdata)]])
+
+        # TODO.
+
diff --git a/decoders/usb_protocol/usb_protocol.py b/decoders/usb_protocol/usb_protocol.py
deleted file mode 100644 (file)
index b1cd33b..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# USB (low-speed and full-speed) protocol decoder
-
-import sigrokdecode as srd
-
-# Packet IDs (PIDs).
-# The first 4 bits are the 'packet type' field, the last 4 bits are the
-# 'check field' (each bit in the check field must be the inverse of the resp.
-# bit in the 'packet type' field; if not, that's a 'PID error').
-# For the 4-bit strings, the left-most '1' or '0' is the LSB, i.e. it's sent
-# to the bus first.
-pids = {
-    # Tokens
-    '10000111': ['OUT', 'Address & EP number in host-to-function transaction'],
-    '10010110': ['IN', 'Address & EP number in function-to-host transaction'],
-    '10100101': ['SOF', 'Start-Of-Frame marker & frame number'],
-    '10110100': ['SETUP', 'Address & EP number in host-to-function transaction for SETUP to a control pipe'],
-
-    # Data
-    # Note: DATA2 and MDATA are HS-only.
-    '11000011': ['DATA0', 'Data packet PID even'],
-    '11010010': ['DATA1', 'Data packet PID odd'],
-    '11100001': ['DATA2', 'Data packet PID HS, high bandwidth isosynchronous transaction in a microframe'],
-    '11110000': ['MDATA', 'Data packet PID HS for split and high-bandwidth isosynchronous transactions'],
-
-    # Handshake
-    '01001011': ['ACK', 'Receiver accepts error-free packet'],
-    '01011010': ['NAK', 'Receiver cannot accept or transmitter cannot send'],
-    '01111000': ['STALL', 'EP halted or control pipe request unsupported'],
-    '01101001': ['NYET', 'No response yet from receiver'],
-
-    # Special
-    '00111100': ['PRE', 'Host-issued preamble; enables downstream bus traffic to low-speed devices'],
-    '00111100': ['ERR', 'Split transaction error handshake'],
-    '00011110': ['SPLIT', 'HS split transaction token'],
-    '00101101': ['PING', 'HS flow control probe for a bulk/control EP'],
-    '00001111': ['Reserved', 'Reserved PID'],
-}
-
-def bitstr_to_num(bitstr):
-    if not bitstr:
-        return 0
-    l = list(bitstr)
-    l.reverse()
-    return int(''.join(l), 2)
-
-def packet_decode(packet):
-    sync = packet[:8]
-    pid = packet[8:16]
-    pid = pids.get(pid, (pid, ''))[0]
-
-    # Remove CRC.
-    if pid in ('OUT', 'IN', 'SOF', 'SETUP'):
-        data = packet[16:-5]
-        if pid == 'SOF':
-            data = str(bitstr_to_num(data))
-        else:
-            dev = bitstr_to_num(data[:7])
-            ep = bitstr_to_num(data[7:])
-            data = 'DEV %d EP %d' % (dev, ep)
-    elif pid in ('DATA0', 'DATA1'):
-        data = packet[16:-16]
-        tmp = ''
-        while data:
-            tmp += '%02x ' % bitstr_to_num(data[:8])
-            data = data[8:]
-        data = tmp
-    else:
-        data = packet[16:]
-
-    # The SYNC pattern for low-speed/full-speed is KJKJKJKK (0001).
-    if sync != '00000001':
-        return 'SYNC INVALID: %s' % sync
-
-    return pid + ' ' + data
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'usb_protocol'
-    name = 'USB protocol'
-    longname = 'Universal Serial Bus (LS/FS) protocol'
-    desc = 'USB 1.x (low-speed and full-speed) serial protocol.'
-    license = 'gplv2+'
-    inputs = ['usb_signalling']
-    outputs = ['usb_protocol']
-    probes = []
-    optional_probes = []
-    options = {
-        'signalling': ['Signalling', 'full-speed'],
-    }
-    annotations = [
-        ['Text', 'Human-readable text']
-    ]
-
-    def __init__(self):
-        self.sym = 'J'
-        self.samplenum = 0
-        self.scount = 0
-        self.packet = ''
-        self.state = 'IDLE'
-
-    def start(self, metadata):
-        self.samplerate = metadata['samplerate']
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb_protocol')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'usb_protocol')
-
-    def report(self):
-        pass
-
-    def decode(self, ss, es, data):
-        (ptype, pdata) = data
-
-        if ptype == 'PACKET':
-            self.put(0, 0, self.out_ann, [0, [packet_decode(pdata)]])
-
-        # TODO.
-
index 316c62cf1a55d2dd5ef093ab91c23e10d935b3dc..6a54eb326885efb59fb63323ca100ec69d47d5e0 100644 (file)
@@ -20,7 +20,7 @@
 
 pkgdatadir = $(DECODERS_DIR)/usb_signalling
 
 
 pkgdatadir = $(DECODERS_DIR)/usb_signalling
 
-dist_pkgdata_DATA = __init__.py usb_signalling.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
 
 CLEANFILES = *.pyc
 
index 026875c538d210e0244ae241e8f4df5315ad1aa1..98ae8723e90a3451466e36d9b9499e1474186d48 100644 (file)
@@ -51,5 +51,5 @@ https://en.wikipedia.org/wiki/USB
 http://www.usb.org/developers/docs/
 '''
 
 http://www.usb.org/developers/docs/
 '''
 
-from .usb_signalling import *
+from .pd import *
 
 
diff --git a/decoders/usb_signalling/pd.py b/decoders/usb_signalling/pd.py
new file mode 100644 (file)
index 0000000..3272cbf
--- /dev/null
@@ -0,0 +1,152 @@
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# USB signalling (low-speed and full-speed) protocol decoder
+
+import sigrokdecode as srd
+
+# Low-/full-speed symbols (used as states of our state machine, too).
+# Note: Low-speed J and K are inverted compared to the full-speed J and K!
+symbols_ls = {
+        # (<dp>, <dm>): <symbol/state>
+        (0, 0): 'SE0',
+        (1, 0): 'K',
+        (0, 1): 'J',
+        (1, 1): 'SE1',
+}
+symbols_fs = {
+        # (<dp>, <dm>): <symbol/state>
+        (0, 0): 'SE0',
+        (1, 0): 'J',
+        (0, 1): 'K',
+        (1, 1): 'SE1',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 1
+    id = 'usb_signalling'
+    name = 'USB signalling'
+    longname = 'Universal Serial Bus (LS/FS) signalling'
+    desc = 'USB 1.x (low-speed and full-speed) signalling protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['usb_signalling']
+    probes = [
+        {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'},
+        {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
+    ]
+    optional_probes = []
+    options = {
+        'signalling': ['Signalling', 'full-speed'],
+    }
+    annotations = [
+        ['Text', 'Human-readable text']
+    ]
+
+    def __init__(self):
+        self.sym = 'J' # The "idle" state is J.
+        self.samplenum = 0
+        self.scount = 0
+        self.packet = ''
+        self.syms = []
+        self.oldpins = None
+
+    def start(self, metadata):
+        self.samplerate = metadata['samplerate']
+        self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb_signalling')
+        self.out_ann = self.add(srd.OUTPUT_ANN, 'usb_signalling')
+
+    def report(self):
+        pass
+
+    def decode(self, ss, es, data):
+        for (self.samplenum, pins) in data:
+
+            # Note: self.samplenum is the absolute sample number, whereas
+            # self.scount only counts the number of samples since the
+            # last change in the D+/D- lines.
+            self.scount += 1
+
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpins == pins:
+                continue
+            self.oldpins, (dp, dm) = pins, pins
+
+            if self.options['signalling'] == 'low-speed':
+                sym = symbols_ls[dp, dm]
+            elif self.options['signalling'] == 'full-speed':
+                sym = symbols_fs[dp, dm]
+
+            self.put(0, 0, self.out_ann, [0, [sym]])
+            self.put(0, 0, self.out_proto, ['SYM', sym])
+
+            # Wait for a symbol change (i.e., change in D+/D- lines).
+            if sym == self.sym:
+                continue
+
+            ## # Debug code:
+            ## self.syms.append(sym + ' ')
+            ## if len(self.syms) == 16:
+            ##     self.put(0, 0, self.out_ann, [0, [''.join(self.syms)]])
+            ##     self.syms = []
+            # continue
+
+            # How many bits since the last transition?
+            if self.packet != '' or self.sym != 'J':
+                if self.options['signalling'] == 'low-speed':
+                    bitrate = 1500000 # 1.5Mb/s (+/- 1.5%)
+                elif self.options['signalling'] == 'full-speed':
+                    bitrate = 12000000 # 12Mb/s (+/- 0.25%)
+                bitcount = int((self.scount - 1) * bitrate / self.samplerate)
+            else:
+                bitcount = 0
+
+            if self.sym == 'SE0':
+                if bitcount == 1:
+                    # End-Of-Packet (EOP)
+                    # self.put(0, 0, self.out_ann,
+                    #          [0, [packet_decode(self.packet), self.packet]])
+                    if self.packet != '': # FIXME?
+                        self.put(0, 0, self.out_ann, [0, ['PACKET: %s' % self.packet]])
+                        self.put(0, 0, self.out_proto, ['PACKET', self.packet])
+                else:
+                    # Longer than EOP, assume reset.
+                    self.put(0, 0, self.out_ann, [0, ['RESET']])
+                    self.put(0, 0, self.out_proto, ['RESET', None])
+                # self.put(0, 0, self.out_ann, [0, [self.packet]])
+                self.scount = 0
+                self.sym = sym
+                self.packet = ''
+                continue
+
+            # Add bits to the packet string.
+            self.packet += '1' * bitcount
+
+            # Handle bit stuffing.
+            if bitcount < 6 and sym != 'SE0':
+                self.packet += '0'
+            elif bitcount > 6:
+                self.put(0, 0, self.out_ann, [0, ['BIT STUFF ERROR']])
+                self.put(0, 0, self.out_proto, ['BIT STUFF ERROR', None])
+
+            self.scount = 0
+            self.sym = sym
+
diff --git a/decoders/usb_signalling/usb_signalling.py b/decoders/usb_signalling/usb_signalling.py
deleted file mode 100644 (file)
index 3272cbf..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-##
-## This file is part of the sigrok project.
-##
-## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-# USB signalling (low-speed and full-speed) protocol decoder
-
-import sigrokdecode as srd
-
-# Low-/full-speed symbols (used as states of our state machine, too).
-# Note: Low-speed J and K are inverted compared to the full-speed J and K!
-symbols_ls = {
-        # (<dp>, <dm>): <symbol/state>
-        (0, 0): 'SE0',
-        (1, 0): 'K',
-        (0, 1): 'J',
-        (1, 1): 'SE1',
-}
-symbols_fs = {
-        # (<dp>, <dm>): <symbol/state>
-        (0, 0): 'SE0',
-        (1, 0): 'J',
-        (0, 1): 'K',
-        (1, 1): 'SE1',
-}
-
-class Decoder(srd.Decoder):
-    api_version = 1
-    id = 'usb_signalling'
-    name = 'USB signalling'
-    longname = 'Universal Serial Bus (LS/FS) signalling'
-    desc = 'USB 1.x (low-speed and full-speed) signalling protocol.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['usb_signalling']
-    probes = [
-        {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'},
-        {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
-    ]
-    optional_probes = []
-    options = {
-        'signalling': ['Signalling', 'full-speed'],
-    }
-    annotations = [
-        ['Text', 'Human-readable text']
-    ]
-
-    def __init__(self):
-        self.sym = 'J' # The "idle" state is J.
-        self.samplenum = 0
-        self.scount = 0
-        self.packet = ''
-        self.syms = []
-        self.oldpins = None
-
-    def start(self, metadata):
-        self.samplerate = metadata['samplerate']
-        self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb_signalling')
-        self.out_ann = self.add(srd.OUTPUT_ANN, 'usb_signalling')
-
-    def report(self):
-        pass
-
-    def decode(self, ss, es, data):
-        for (self.samplenum, pins) in data:
-
-            # Note: self.samplenum is the absolute sample number, whereas
-            # self.scount only counts the number of samples since the
-            # last change in the D+/D- lines.
-            self.scount += 1
-
-            # Ignore identical samples early on (for performance reasons).
-            if self.oldpins == pins:
-                continue
-            self.oldpins, (dp, dm) = pins, pins
-
-            if self.options['signalling'] == 'low-speed':
-                sym = symbols_ls[dp, dm]
-            elif self.options['signalling'] == 'full-speed':
-                sym = symbols_fs[dp, dm]
-
-            self.put(0, 0, self.out_ann, [0, [sym]])
-            self.put(0, 0, self.out_proto, ['SYM', sym])
-
-            # Wait for a symbol change (i.e., change in D+/D- lines).
-            if sym == self.sym:
-                continue
-
-            ## # Debug code:
-            ## self.syms.append(sym + ' ')
-            ## if len(self.syms) == 16:
-            ##     self.put(0, 0, self.out_ann, [0, [''.join(self.syms)]])
-            ##     self.syms = []
-            # continue
-
-            # How many bits since the last transition?
-            if self.packet != '' or self.sym != 'J':
-                if self.options['signalling'] == 'low-speed':
-                    bitrate = 1500000 # 1.5Mb/s (+/- 1.5%)
-                elif self.options['signalling'] == 'full-speed':
-                    bitrate = 12000000 # 12Mb/s (+/- 0.25%)
-                bitcount = int((self.scount - 1) * bitrate / self.samplerate)
-            else:
-                bitcount = 0
-
-            if self.sym == 'SE0':
-                if bitcount == 1:
-                    # End-Of-Packet (EOP)
-                    # self.put(0, 0, self.out_ann,
-                    #          [0, [packet_decode(self.packet), self.packet]])
-                    if self.packet != '': # FIXME?
-                        self.put(0, 0, self.out_ann, [0, ['PACKET: %s' % self.packet]])
-                        self.put(0, 0, self.out_proto, ['PACKET', self.packet])
-                else:
-                    # Longer than EOP, assume reset.
-                    self.put(0, 0, self.out_ann, [0, ['RESET']])
-                    self.put(0, 0, self.out_proto, ['RESET', None])
-                # self.put(0, 0, self.out_ann, [0, [self.packet]])
-                self.scount = 0
-                self.sym = sym
-                self.packet = ''
-                continue
-
-            # Add bits to the packet string.
-            self.packet += '1' * bitcount
-
-            # Handle bit stuffing.
-            if bitcount < 6 and sym != 'SE0':
-                self.packet += '0'
-            elif bitcount > 6:
-                self.put(0, 0, self.out_ann, [0, ['BIT STUFF ERROR']])
-                self.put(0, 0, self.out_proto, ['BIT STUFF ERROR', None])
-
-            self.scount = 0
-            self.sym = sym
-