All PDs: Name the files pd.py consistently.
authorUwe Hermann <uwe@hermann-uwe.de>
Wed, 21 Nov 2012 21:43:02 +0000 (22:43 +0100)
committerUwe Hermann <uwe@hermann-uwe.de>
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
 
-dist_pkgdata_DATA = __init__.py dcf77.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
index bef505b926c6ab0e896600b7ec03f1f0cd692d02..e2ed26bf19ef5814f792f6e91d8c10c909b67db7 100644 (file)
@@ -25,5 +25,5 @@ Details:
 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
 
-dist_pkgdata_DATA = __init__.py edid.py pnpids.txt
+dist_pkgdata_DATA = __init__.py pd.py pnpids.txt
 
 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
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py i2c.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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
 
-dist_pkgdata_DATA = __init__.py i2cdemux.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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.
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py i2cfilter.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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.
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py i2s.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py jtag.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py jtag_stm32.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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")
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py lm75.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
index fd72612bacbfe569342033660090591220f5e167..548cf6521bf01087dab5a5f1b1cbc3e823aa42a9 100644 (file)
@@ -30,5 +30,5 @@ Details:
 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
 
-dist_pkgdata_DATA = __init__.py lpc.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
index ce2a1406fd0ba66527a8f6ff684c7cd87b47a37b..1c79b7334cc6a30aae82da26757ef54f57cb82a6 100644 (file)
@@ -25,5 +25,5 @@ Details:
 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
 
-dist_pkgdata_DATA = __init__.py maxim_ds28ea00.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
index 508ae5b960077fd21eee6fa02c7a25c3b9b531da..cb1c77870bae562a5cda9d71ec5b29010d3771f0 100644 (file)
@@ -28,5 +28,5 @@ Details:
 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
 
-dist_pkgdata_DATA = __init__.py mlx90614.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
index 0ceabc20d986606587413b494333a512a5b758b6..c14e67f40f3c7a02adf627c709c95b48dc42904a 100644 (file)
@@ -25,5 +25,5 @@ Details:
 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
 
-dist_pkgdata_DATA = __init__.py mx25lxx05d.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py mxc6225xu.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
index 02b63b22583ef6227acd589bd13b50ff940b1722..3bca9e4f0d21eef641999fbb597915787e561c88 100644 (file)
@@ -30,5 +30,5 @@ Details:
 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
 
-dist_pkgdata_DATA = __init__.py nunchuk.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 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
 '''
 
-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
 
-dist_pkgdata_DATA = __init__.py onewire_link.py
+dist_pkgdata_DATA = __init__.py pd.py
 
 CLEANFILES = *.pyc
 
index cb0347170958b385212fa5960d13bda3f844d234..c1331c6322d1d6988170816f67c8896c3d9f47a9 100644 (file)
@@ -87,5 +87,5 @@ TODO:
 - 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: