]> sigrok.org Git - libsigrokdecode.git/commitdiff
Backport current PDs from git mainline.
authorUwe Hermann <redacted>
Sun, 12 Jul 2015 17:14:49 +0000 (19:14 +0200)
committerUwe Hermann <redacted>
Sun, 12 Jul 2015 17:26:02 +0000 (19:26 +0200)
115 files changed:
decoders/adns5020/__init__.py [new file with mode: 0644]
decoders/adns5020/pd.py [new file with mode: 0644]
decoders/am230x/__init__.py [new file with mode: 0644]
decoders/am230x/pd.py [new file with mode: 0644]
decoders/arm_etmv3/__init__.py [new file with mode: 0644]
decoders/arm_etmv3/pd.py [new file with mode: 0644]
decoders/arm_itm/__init__.py [new file with mode: 0644]
decoders/arm_itm/pd.py [new file with mode: 0644]
decoders/arm_tpiu/__init__.py [new file with mode: 0644]
decoders/arm_tpiu/pd.py [new file with mode: 0644]
decoders/avr_isp/__init__.py
decoders/avr_isp/parts.py
decoders/avr_isp/pd.py
decoders/can/__init__.py
decoders/can/pd.py
decoders/dcf77/__init__.py
decoders/dcf77/pd.py
decoders/ds1307/__init__.py
decoders/ds1307/pd.py
decoders/edid/__init__.py
decoders/edid/pd.py
decoders/eeprom24xx/__init__.py [new file with mode: 0644]
decoders/eeprom24xx/lists.py [new file with mode: 0644]
decoders/eeprom24xx/pd.py [new file with mode: 0644]
decoders/guess_bitrate/__init__.py
decoders/guess_bitrate/pd.py
decoders/i2c/__init__.py
decoders/i2c/pd.py
decoders/i2cdemux/__init__.py
decoders/i2cdemux/pd.py
decoders/i2cfilter/__init__.py
decoders/i2cfilter/pd.py
decoders/i2s/__init__.py
decoders/i2s/pd.py
decoders/ir_nec/__init__.py
decoders/ir_nec/lists.py
decoders/ir_nec/pd.py
decoders/ir_rc5/__init__.py
decoders/ir_rc5/lists.py
decoders/ir_rc5/pd.py
decoders/jitter/__init__.py [new file with mode: 0644]
decoders/jitter/pd.py [new file with mode: 0644]
decoders/jtag/__init__.py
decoders/jtag/pd.py
decoders/jtag_stm32/__init__.py
decoders/jtag_stm32/pd.py
decoders/lm75/__init__.py
decoders/lm75/pd.py
decoders/lpc/__init__.py
decoders/lpc/pd.py
decoders/maxim_ds28ea00/__init__.py
decoders/maxim_ds28ea00/pd.py
decoders/mdio/__init__.py [new file with mode: 0644]
decoders/mdio/pd.py [new file with mode: 0644]
decoders/midi/__init__.py
decoders/midi/lists.py
decoders/midi/pd.py
decoders/mlx90614/__init__.py
decoders/mlx90614/pd.py
decoders/mrf24j40/__init__.py [new file with mode: 0644]
decoders/mrf24j40/lists.py [new file with mode: 0644]
decoders/mrf24j40/pd.py [new file with mode: 0644]
decoders/mx25lxx05d/__init__.py [deleted file]
decoders/mx25lxx05d/pd.py [deleted file]
decoders/mxc6225xu/__init__.py
decoders/mxc6225xu/pd.py
decoders/nrf24l01/__init__.py [new file with mode: 0644]
decoders/nrf24l01/pd.py [new file with mode: 0644]
decoders/nunchuk/__init__.py
decoders/nunchuk/pd.py
decoders/onewire_link/__init__.py
decoders/onewire_link/pd.py
decoders/onewire_network/__init__.py
decoders/onewire_network/pd.py
decoders/pan1321/__init__.py
decoders/pan1321/pd.py
decoders/parallel/__init__.py
decoders/parallel/pd.py
decoders/pwm/__init__.py [new file with mode: 0644]
decoders/pwm/pd.py [new file with mode: 0644]
decoders/qi/__init__.py [new file with mode: 0644]
decoders/qi/pd.py [new file with mode: 0644]
decoders/rfm12/__init__.py [new file with mode: 0644]
decoders/rfm12/pd.py [new file with mode: 0644]
decoders/rgb_led_spi/__init__.py
decoders/rgb_led_spi/pd.py
decoders/rtc8564/__init__.py
decoders/rtc8564/pd.py
decoders/sdcard_spi/__init__.py
decoders/sdcard_spi/pd.py
decoders/spdif/__init__.py [new file with mode: 0644]
decoders/spdif/pd.py [new file with mode: 0644]
decoders/spi/__init__.py
decoders/spi/pd.py
decoders/spiflash/__init__.py [new file with mode: 0644]
decoders/spiflash/lists.py [new file with mode: 0644]
decoders/spiflash/pd.py [new file with mode: 0644]
decoders/stepper_motor/__init__.py [new file with mode: 0644]
decoders/stepper_motor/pd.py [new file with mode: 0644]
decoders/swd/__init__.py [new file with mode: 0644]
decoders/swd/pd.py [new file with mode: 0644]
decoders/tca6408a/__init__.py [new file with mode: 0644]
decoders/tca6408a/pd.py [new file with mode: 0644]
decoders/timing/__init__.py [new file with mode: 0644]
decoders/timing/pd.py [new file with mode: 0644]
decoders/tlc5620/__init__.py
decoders/tlc5620/pd.py
decoders/uart/__init__.py
decoders/uart/pd.py
decoders/usb_packet/__init__.py
decoders/usb_packet/pd.py
decoders/usb_signalling/__init__.py
decoders/usb_signalling/pd.py
decoders/xfp/__init__.py
decoders/xfp/pd.py

diff --git a/decoders/adns5020/__init__.py b/decoders/adns5020/__init__.py
new file mode 100644 (file)
index 0000000..a0cc63c
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Karl Palsson <karlp@tweak.net.au>
+##
+## 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
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes ADNS-5020 optical mouse
+sensor commands and data. Use MOSI for the SDIO shared line.
+'''
+
+from .pd import Decoder
diff --git a/decoders/adns5020/pd.py b/decoders/adns5020/pd.py
new file mode 100644 (file)
index 0000000..bcdb52a
--- /dev/null
@@ -0,0 +1,113 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Karl Palsson <karlp@tweak.net.au>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+regs = {
+       0: 'Product_ID',
+       1: 'Revision_ID',
+       2: 'Motion',
+       3: 'Delta_X',
+       4: 'Delta_Y',
+       5: 'SQUAL',
+       6: 'Shutter_Upper',
+       7: 'Shutter_Lower',
+       8: 'Maximum_Pixel',
+       9: 'Pixel_Sum',
+       0xa: 'Minimum_Pixel',
+       0xb: 'Pixel_Grab',
+       0xd: 'Mouse_Control',
+       0x3a: 'Chip_Reset',
+       0x3f: 'Inv_Rev_ID',
+       0x63: 'Motion_Burst',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'adns5020'
+    name = 'ADNS-5020'
+    longname = 'Avago ADNS-5020 optical mouse sensor'
+    desc = 'Bidirectional command and data over an SPI-like protocol.'
+    license = 'gplv2'
+    inputs = ['spi']
+    outputs = ['adns5020']
+    annotations = (
+        ('read', 'Register read commands'),
+        ('write', 'Register write commands'),
+        ('warning', 'Warnings'),
+    )
+    annotation_rows = (
+        ('read', 'Read', (0,)),
+        ('write', 'Write', (1,)),
+        ('warnings', 'Warnings', (2,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.ss_cmd, self.es_cmd = 0, 0
+        self.mosi_bytes = []
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+
+    def put_warn(self, pos, msg):
+        self.put(pos[0], pos[1], self.out_ann, [2, [msg]])
+
+    def decode(self, ss, es, data):
+        ptype = data[0]
+        if ptype == 'CS-CHANGE':
+            # If we transition high mid-stream, toss out our data and restart.
+            cs_old, cs_new = data[1:]
+            if cs_old is not None and cs_old == 0 and cs_new == 1:
+                if len(self.mosi_bytes) not in [0, 2]:
+                    self.put_warn([self.ss_cmd, es], 'Misplaced CS#!')
+                    self.mosi_bytes = []
+            return
+
+        # Don't care about anything else.
+        if ptype != 'DATA':
+            return
+        mosi, miso = data[1:]
+
+        self.ss, self.es = ss, es
+
+        if len(self.mosi_bytes) == 0:
+            self.ss_cmd = ss
+        self.mosi_bytes.append(mosi)
+
+        # Writes/reads are mostly two transfers (burst mode is different).
+        if len(self.mosi_bytes) != 2:
+            return
+
+        self.es_cmd = es
+        cmd, arg = self.mosi_bytes
+        write = cmd & 0x80
+        reg = cmd & 0x7f
+        reg_desc = regs.get(reg, 'Reserved %#x' % reg)
+        if reg > 0x63:
+            reg_desc = 'Unknown'
+        if write:
+            self.putx([1, ['%s: %#x' % (reg_desc, arg)]])
+        else:
+            self.putx([0, ['%s: %d' % (reg_desc, arg)]])
+
+        self.mosi_bytes = []
diff --git a/decoders/am230x/__init__.py b/decoders/am230x/__init__.py
new file mode 100644 (file)
index 0000000..51b8488
--- /dev/null
@@ -0,0 +1,37 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Johannes Roemer <jroemer@physik.uni-wuerzburg.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
+##
+
+'''
+This decoder handles the proprietary single wire communication protocol used
+by the Aosong AM230x/DHTxx/RHTxx series of digital humidity and temperature
+sensors.
+
+Sample rate:
+A sample rate of at least 200kHz is recommended to properly detect all the
+elements of the protocol.
+
+Options:
+The AM230x and DHTxx/RHTxx digital humidity and temperature sensors use the
+same single-wire protocol with different encoding of the measured values.
+Therefore the option 'device' must be used to properly decode the
+communication of the respective sensor.
+'''
+
+from .pd import Decoder
diff --git a/decoders/am230x/pd.py b/decoders/am230x/pd.py
new file mode 100644 (file)
index 0000000..2b54cde
--- /dev/null
@@ -0,0 +1,235 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Johannes Roemer <jroemer@physik.uni-wuerzburg.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+# Define valid timing values (in microseconds).
+timing = {
+    'START LOW'     : {'min': 750, 'max': 25000},
+    'START HIGH'    : {'min': 10, 'max': 10000},
+    'RESPONSE LOW'  : {'min': 50, 'max': 90},
+    'RESPONSE HIGH' : {'min': 50, 'max': 90},
+    'BIT LOW'       : {'min': 45, 'max': 90},
+    'BIT 0 HIGH'    : {'min': 20, 'max': 35},
+    'BIT 1 HIGH'    : {'min': 65, 'max': 80},
+}
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'am230x'
+    name = 'AM230x/DHTxx/RHTxx'
+    longname = 'Aosong AM230x/DHTxx/RHTxx'
+    desc = 'Aosong AM230x/DHTxx/RHTxx humidity/temperature sensor protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['am230x']
+    channels = (
+        {'id': 'sda', 'name': 'SDA', 'desc': 'Single wire serial data line'},
+    )
+    options = (
+        {'id': 'device', 'desc': 'Device type',
+            'default': 'am230x', 'values': ('am230x/rht', 'dht11')},
+    )
+    annotations = (
+        ('start', 'Start'),
+        ('response', 'Response'),
+        ('bit', 'Bit'),
+        ('end', 'End'),
+        ('byte', 'Byte'),
+        ('humidity', 'Relative humidity in percent'),
+        ('temperature', 'Temperature in degrees Celsius'),
+        ('checksum', 'Checksum'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0, 1, 2, 3)),
+        ('bytes', 'Bytes', (4,)),
+        ('results', 'Results', (5, 6, 7)),
+    )
+
+    def putfs(self, data):
+        self.put(self.fall, self.samplenum, self.out_ann, data)
+
+    def putb(self, data):
+        self.put(self.bytepos[-1], self.samplenum, self.out_ann, data)
+
+    def putv(self, data):
+        self.put(self.bytepos[-2], self.samplenum, self.out_ann, data)
+
+    def reset(self):
+        self.state = 'WAIT FOR START LOW'
+        self.samplenum = 0
+        self.fall = 0
+        self.rise = 0
+        self.bits = []
+        self.bytepos = []
+
+    def is_valid(self, name):
+        dt = 0
+        if name.endswith('LOW'):
+            dt = self.samplenum - self.fall
+        elif name.endswith('HIGH'):
+            dt = self.samplenum - self.rise
+        if dt >= self.cnt[name]['min'] and dt <= self.cnt[name]['max']:
+            return True
+        return False
+
+    def bits2num(self, bitlist):
+        number = 0
+        for i in range(len(bitlist)):
+            number += bitlist[-1 - i] * 2**i
+        return number
+
+    def calculate_humidity(self, bitlist):
+        h = 0
+        if self.options['device'] == 'dht11':
+            h = self.bits2num(bitlist[0:8])
+        else:
+            h = self.bits2num(bitlist) / 10
+        return h
+
+    def calculate_temperature(self, bitlist):
+        t = 0
+        if self.options['device'] == 'dht11':
+            t = self.bits2num(bitlist[0:8])
+        else:
+            t = self.bits2num(bitlist[1:]) / 10
+            if bitlist[0] == 1:
+                t = -t
+        return t
+
+    def calculate_checksum(self, bitlist):
+        checksum = 0
+        for i in range(8, len(bitlist) + 1, 8):
+            checksum += self.bits2num(bitlist[i-8:i])
+        return checksum % 256
+
+    def __init__(self, **kwargs):
+        self.samplerate = None
+        self.reset()
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def metadata(self, key, value):
+        if key != srd.SRD_CONF_SAMPLERATE:
+            return
+        self.samplerate = value
+        # Convert microseconds to sample counts.
+        self.cnt = {}
+        for e in timing:
+            self.cnt[e] = {}
+            for t in timing[e]:
+                self.cnt[e][t] = timing[e][t] * self.samplerate / 1000000
+
+    def handle_byte(self, bit):
+        self.bits.append(bit)
+        self.putfs([2, ['Bit: %d' % bit, '%d' % bit]])
+        self.fall = self.samplenum
+        self.state = 'WAIT FOR BIT HIGH'
+        if len(self.bits) % 8 == 0:
+            byte = self.bits2num(self.bits[-8:])
+            self.putb([4, ['Byte: %#04x' % byte, '%#04x' % byte]])
+            if len(self.bits) == 16:
+                h = self.calculate_humidity(self.bits[-16:])
+                self.putv([5, ['Humidity: %.1f %%' % h, 'RH = %.1f %%' % h]])
+            elif len(self.bits) == 32:
+                t = self.calculate_temperature(self.bits[-16:])
+                self.putv([6, ['Temperature: %.1f °C' % t, 'T = %.1f °C' % t]])
+            elif len(self.bits) == 40:
+                parity = self.bits2num(self.bits[-8:])
+                if parity == self.calculate_checksum(self.bits[0:32]):
+                    self.putb([7, ['Checksum: OK', 'OK']])
+                else:
+                    self.putb([7, ['Checksum: not OK', 'NOK']])
+                self.state = 'WAIT FOR END'
+            self.bytepos.append(self.samplenum)
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        for (self.samplenum, (sda,)) in data:
+            # State machine.
+            if self.state == 'WAIT FOR START LOW':
+                if sda != 0:
+                    continue
+                self.fall = self.samplenum
+                self.state = 'WAIT FOR START HIGH'
+            elif self.state == 'WAIT FOR START HIGH':
+                if sda != 1:
+                    continue
+                if self.is_valid('START LOW'):
+                    self.rise = self.samplenum
+                    self.state = 'WAIT FOR RESPONSE LOW'
+                else:
+                    self.reset()
+            elif self.state == 'WAIT FOR RESPONSE LOW':
+                if sda != 0:
+                    continue
+                if self.is_valid('START HIGH'):
+                    self.putfs([0, ['Start', 'S']])
+                    self.fall = self.samplenum
+                    self.state = 'WAIT FOR RESPONSE HIGH'
+                else:
+                    self.reset()
+            elif self.state == 'WAIT FOR RESPONSE HIGH':
+                if sda != 1:
+                    continue
+                if self.is_valid('RESPONSE LOW'):
+                    self.rise = self.samplenum
+                    self.state = 'WAIT FOR FIRST BIT'
+                else:
+                    self.reset()
+            elif self.state == 'WAIT FOR FIRST BIT':
+                if sda != 0:
+                    continue
+                if self.is_valid('RESPONSE HIGH'):
+                    self.putfs([1, ['Response', 'R']])
+                    self.fall = self.samplenum
+                    self.bytepos.append(self.samplenum)
+                    self.state = 'WAIT FOR BIT HIGH'
+                else:
+                    self.reset()
+            elif self.state == 'WAIT FOR BIT HIGH':
+                if sda != 1:
+                    continue
+                if self.is_valid('BIT LOW'):
+                    self.rise = self.samplenum
+                    self.state = 'WAIT FOR BIT LOW'
+                else:
+                    self.reset()
+            elif self.state == 'WAIT FOR BIT LOW':
+                if sda != 0:
+                    continue
+                if self.is_valid('BIT 0 HIGH'):
+                    bit = 0
+                elif self.is_valid('BIT 1 HIGH'):
+                    bit = 1
+                else:
+                    self.reset()
+                    continue
+                self.handle_byte(bit)
+            elif self.state == 'WAIT FOR END':
+                if sda != 1:
+                    continue
+                self.putfs([3, ['End', 'E']])
+                self.reset()
diff --git a/decoders/arm_etmv3/__init__.py b/decoders/arm_etmv3/__init__.py
new file mode 100644 (file)
index 0000000..7955139
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'uart' decoder and decodes packets of
+the ARMv7m Embedded Trace Macroblock v3.x.
+'''
+
+from .pd import Decoder
diff --git a/decoders/arm_etmv3/pd.py b/decoders/arm_etmv3/pd.py
new file mode 100644 (file)
index 0000000..0083fdb
--- /dev/null
@@ -0,0 +1,564 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+import subprocess
+import re
+
+# See ETMv3 Signal Protocol table 7-11: 'Encoding of Exception[8:0]'.
+exc_names = [
+    'No exception', 'IRQ1', 'IRQ2', 'IRQ3', 'IRQ4', 'IRQ5', 'IRQ6', 'IRQ7',
+    'IRQ0', 'UsageFault', 'NMI', 'SVC', 'DebugMon', 'MemManage', 'PendSV',
+    'SysTick', 'Reserved', 'Reset', 'BusFault', 'Reserved', 'Reserved'
+]
+
+for i in range(8, 496):
+    exc_names.append('IRQ%d' % i)
+
+def parse_varint(bytes):
+    '''Parse an integer where the top bit is the continuation bit.
+    Returns value and number of parsed bytes.'''
+    v = 0
+    for i, b in enumerate(bytes):
+        v |= (b & 0x7F) << (i * 7)
+        if b & 0x80 == 0:
+            return v, i+1
+    return v, len(bytes)
+
+def parse_uint(bytes):
+    '''Parse little-endian integer.'''
+    v = 0
+    for i, b in enumerate(bytes):
+        v |= b << (i * 8)
+    return v
+
+def parse_exc_info(bytes):
+    '''Parse exception information bytes from a branch packet.'''
+    if len(bytes) < 1:
+        return None
+
+    excv, exclen = parse_varint(bytes)
+    if bytes[exclen - 1] & 0x80 != 0x00:
+        return None # Exception info not complete.
+
+    if exclen == 2 and excv & (1 << 13):
+        # Exception byte 1 was skipped, fix up the decoding.
+        excv = (excv & 0x7F) | ((excv & 0x3F80) << 7)
+
+    ns = excv & 1
+    exc = ((excv >> 1) & 0x0F) | ((excv >> 7) & 0x1F0)
+    cancel = (excv >> 5) & 1
+    altisa = (excv >> 6) & 1
+    hyp = (excv >> 12) & 1
+    resume = (excv >> 14) & 0x0F
+    return (ns, exc, cancel, altisa, hyp, resume)
+
+def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc):
+    '''Parse encoded branch address.
+       Returns addr, addrlen, cpu_state, exc_info.
+       Returns None if packet is not yet complete'''
+
+    addr, addrlen = parse_varint(bytes)
+
+    if bytes[addrlen-1] & 0x80 != 0x00:
+        return None # Branch address not complete.
+
+    addr_bits = 7 * addrlen
+
+    have_exc_info = False
+    if branch_enc == 'original':
+        if addrlen == 5 and bytes[4] & 0x40:
+            have_exc_info = True
+    elif branch_enc == 'alternative':
+        addr_bits -= 1 # Top bit of address indicates exc_info.
+        if addrlen >= 2 and addr & (1 << addr_bits):
+            have_exc_info = True
+            addr &= ~(1 << addr_bits)
+
+    exc_info = None
+    if have_exc_info:
+        exc_info = parse_exc_info(bytes[addrlen:])
+        if exc_info is None:
+            return None # Exception info not complete.
+
+    if addrlen == 5:
+        # Possible change in CPU state.
+        if bytes[4] & 0xB8 == 0x08:
+            cpu_state = 'arm'
+        elif bytes[4] & 0xB0 == 0x10:
+            cpu_state = 'thumb'
+        elif bytes[4] & 0xA0 == 0x20:
+            cpu_state = 'jazelle'
+        else:
+            raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes[4])
+
+    # Shift the address according to current CPU state.
+    if cpu_state == 'arm':
+        addr = (addr & 0xFFFFFFFE) << 1
+        addr_bits += 1
+    elif cpu_state == 'thumb':
+        addr = addr & 0xFFFFFFFE
+    elif cpu_state == 'jazelle':
+        addr = (addr & 0xFFFFFFFFE) >> 1
+        addr_bits -= 1
+    else:
+        raise NotImplementedError('Unhandled state: ' + cpu_state)
+
+    # If the address wasn't full, fill in with the previous address.
+    if addrlen < 5:
+        addr |= ref_addr & (0xFFFFFFFF << addr_bits)
+
+    return addr, addrlen, cpu_state, exc_info
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'arm_etmv3'
+    name = 'ARM ETMv3'
+    longname = 'ARM Embedded Trace Macroblock'
+    desc = 'Decode ETM instruction trace packets.'
+    license = 'gplv2+'
+    inputs = ['uart']
+    outputs = ['arm_etmv3']
+    annotations = (
+        ('trace', 'Trace info'),
+        ('branch', 'Branches'),
+        ('exception', 'Exceptions'),
+        ('execution', 'Instruction execution'),
+        ('data', 'Data access'),
+        ('pc', 'Program counter'),
+        ('instr_e', 'Executed instructions'),
+        ('instr_n', 'Not executed instructions'),
+        ('source', 'Source code'),
+        ('location', 'Current location'),
+        ('function', 'Current function'),
+    )
+    annotation_rows = (
+        ('trace', 'Trace info', (0,)),
+        ('flow', 'Code flow', (1, 2, 3,)),
+        ('data', 'Data access', (4,)),
+        ('pc', 'Program counter', (5,)),
+        ('instruction', 'Instructions', (6, 7,)),
+        ('source', 'Source code', (8,)),
+        ('location', 'Current location', (9,)),
+        ('function', 'Current function', (10,)),
+    )
+    options = (
+        {'id': 'objdump', 'desc': 'objdump path',
+            'default': 'arm-none-eabi-objdump'},
+        {'id': 'objdump_opts', 'desc': 'objdump options',
+            'default': '-lSC'},
+        {'id': 'elffile', 'desc': '.elf path',
+            'default': ''},
+        {'id': 'branch_enc', 'desc': 'Branch encoding',
+            'default': 'alternative', 'values': ('alternative', 'original')},
+    )
+
+    def __init__(self, **kwargs):
+        self.buf = []
+        self.syncbuf = []
+        self.prevsample = 0
+        self.last_branch = 0
+        self.cpu_state = 'arm'
+        self.current_pc = 0
+        self.current_loc = None
+        self.current_func = None
+        self.next_instr_lookup = {}
+        self.file_lookup = {}
+        self.func_lookup = {}
+        self.disasm_lookup = {}
+        self.source_lookup = {}
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.load_objdump()
+
+    def load_objdump(self):
+        '''Parse disassembly obtained from objdump into two tables:
+        next_instr_lookup: Find the next PC addr from current PC.
+        disasm_lookup: Find the instruction text from current PC.
+        source_lookup: Find the source code line from current PC.
+        '''
+        if not (self.options['objdump'] and self.options['elffile']):
+            return
+
+        opts = [self.options['objdump']]
+        opts += self.options['objdump_opts'].split()
+        opts += [self.options['elffile']]
+
+        try:
+            disasm = subprocess.check_output(opts)
+        except subprocess.CalledProcessError:
+            return
+
+        disasm = disasm.decode('utf-8', 'replace')
+
+        instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
+        branchpat = re.compile('(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)')
+        filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
+        funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
+
+        prev_src = ''
+        prev_file = ''
+        prev_func = ''
+
+        for line in disasm.split('\n'):
+            m = instpat.match(line)
+            if m:
+                addr = int(m.group(1), 16)
+                raw = m.group(2)
+                disas = m.group(3).strip().replace('\t', ' ')
+                self.disasm_lookup[addr] = disas
+                self.source_lookup[addr] = prev_src
+                self.file_lookup[addr] = prev_file
+                self.func_lookup[addr] = prev_func
+
+                # Next address in direct sequence.
+                ilen = len(raw.replace(' ', '')) // 2
+                next_n = addr + ilen
+
+                # Next address if branch is taken.
+                bm = branchpat.match(disas)
+                if bm:
+                    next_e = int(bm.group(2), 16)
+                else:
+                    next_e = next_n
+
+                self.next_instr_lookup[addr] = (next_n, next_e)
+            else:
+                m = funcpat.match(line)
+                if m:
+                    prev_func = m.group(1)
+                    prev_src = None
+                else:
+                    m = filepat.match(line)
+                    if m:
+                        prev_file = m.group(1)
+                        prev_src = None
+                    else:
+                        prev_src = line.strip()
+
+    def flush_current_loc(self):
+        if self.current_loc is not None:
+            ss, es, loc, src = self.current_loc
+            if loc:
+                self.put(ss, es, self.out_ann, [9, [loc]])
+            if src:
+                self.put(ss, es, self.out_ann, [8, [src]])
+            self.current_loc = None
+
+    def flush_current_func(self):
+        if self.current_func is not None:
+            ss, es, func = self.current_func
+            if func:
+                self.put(ss, es, self.out_ann, [10, [func]])
+            self.current_func = None
+
+    def instructions_executed(self, exec_status):
+        '''Advance program counter based on executed instructions.
+        Argument is a list of False for not executed and True for executed
+        instructions.
+        '''
+
+        if len(exec_status) == 0:
+            return
+
+        tdelta = max(1, (self.prevsample - self.startsample) / len(exec_status))
+
+        for i, exec_status in enumerate(exec_status):
+            pc = self.current_pc
+            default_next = pc + 2 if self.cpu_state == 'thumb' else pc + 4
+            target_n, target_e = self.next_instr_lookup.get(pc, (default_next, default_next))
+            ss = self.startsample + round(tdelta * i)
+            es = self.startsample + round(tdelta * (i+1))
+
+            self.put(ss, es, self.out_ann,
+                     [5, ['PC 0x%08x' % pc, '0x%08x' % pc, '%08x' % pc]])
+
+            new_loc = self.file_lookup.get(pc)
+            new_src = self.source_lookup.get(pc)
+            new_dis = self.disasm_lookup.get(pc)
+            new_func = self.func_lookup.get(pc)
+
+            # Report source line only when it changes.
+            if self.current_loc is not None:
+                if new_loc != self.current_loc[2] or new_src != self.current_loc[3]:
+                    self.flush_current_loc()
+
+            if self.current_loc is None:
+                self.current_loc = [ss, es, new_loc, new_src]
+            else:
+                self.current_loc[1] = es
+
+            # Report function name only when it changes.
+            if self.current_func is not None:
+                if new_func != self.current_func[2]:
+                    self.flush_current_func()
+
+            if self.current_func is None:
+                self.current_func = [ss, es, new_func]
+            else:
+                self.current_func[1] = es
+
+            # Report instruction every time.
+            if new_dis:
+                if exec_status:
+                    a = [6, ['Executed: ' + new_dis, new_dis, new_dis.split()[0]]]
+                else:
+                    a = [7, ['Not executed: ' + new_dis, new_dis, new_dis.split()[0]]]
+                self.put(ss, es, self.out_ann, a)
+
+            if exec_status:
+                self.current_pc = target_e
+            else:
+                self.current_pc = target_n
+
+    def get_packet_type(self, byte):
+        '''Identify packet type based on its first byte.
+           See ARM IHI0014Q section "ETMv3 Signal Protocol" "Packet Types"
+        '''
+        if byte & 0x01 == 0x01:
+            return 'branch'
+        elif byte == 0x00:
+            return 'a_sync'
+        elif byte == 0x04:
+            return 'cyclecount'
+        elif byte == 0x08:
+            return 'i_sync'
+        elif byte == 0x0C:
+            return 'trigger'
+        elif byte & 0xF3 in (0x20, 0x40, 0x60):
+            return 'ooo_data'
+        elif byte == 0x50:
+            return 'store_failed'
+        elif byte == 0x70:
+            return 'i_sync'
+        elif byte & 0xDF in (0x54, 0x58, 0x5C):
+            return 'ooo_place'
+        elif byte == 0x3C:
+            return 'vmid'
+        elif byte & 0xD3 == 0x02:
+            return 'data'
+        elif byte & 0xFB == 0x42:
+            return 'timestamp'
+        elif byte == 0x62:
+            return 'data_suppressed'
+        elif byte == 0x66:
+            return 'ignore'
+        elif byte & 0xEF == 0x6A:
+            return 'value_not_traced'
+        elif byte == 0x6E:
+            return 'context_id'
+        elif byte == 0x76:
+            return 'exception_exit'
+        elif byte == 0x7E:
+            return 'exception_entry'
+        elif byte & 0x81 == 0x80:
+            return 'p_header'
+        else:
+            return 'unknown'
+
+    def fallback(self, buf):
+        ptype = self.get_packet_type(buf[0])
+        return [0, ['Unhandled ' + ptype + ': ' + ' '.join(['%02x' % b for b in buf])]]
+
+    def handle_a_sync(self, buf):
+        if buf[-1] == 0x80:
+            return [0, ['Synchronization']]
+
+    def handle_exception_exit(self, buf):
+        return [2, ['Exception exit']]
+
+    def handle_exception_entry(self, buf):
+        return [2, ['Exception entry']]
+
+    def handle_i_sync(self, buf):
+        contextid_bytes = 0 # This is the default ETM config.
+
+        if len(buf) < 6:
+            return None # Packet definitely not full yet.
+
+        if buf[0] == 0x08: # No cycle count.
+            cyclecount = None
+            idx = 1 + contextid_bytes # Index to info byte.
+        elif buf[0] == 0x70: # With cycle count.
+            cyclecount, cyclen = parse_varint(buf[1:6])
+            idx = 1 + cyclen + contextid_bytes
+
+        if len(buf) <= idx + 4:
+            return None
+        infobyte = buf[idx]
+        addr = parse_uint(buf[idx+1:idx+5])
+
+        reasoncode = (infobyte >> 5) & 3
+        reason = ('Periodic', 'Tracing enabled', 'After overflow', 'Exit from debug')[reasoncode]
+        jazelle = (infobyte >> 4) & 1
+        nonsec = (infobyte >> 3) & 1
+        altisa = (infobyte >> 2) & 1
+        hypervisor = (infobyte >> 1) & 1
+        thumb = addr & 1
+        addr &= 0xFFFFFFFE
+
+        if reasoncode == 0 and self.current_pc != addr:
+            self.put(self.startsample, self.prevsample, self.out_ann,
+                     [0, ['WARN: Unexpected PC change 0x%08x -> 0x%08x' % \
+                     (self.current_pc, addr)]])
+        elif reasoncode != 0:
+            # Reset location when the trace has been interrupted.
+            self.flush_current_loc()
+            self.flush_current_func()
+
+        self.last_branch = addr
+        self.current_pc = addr
+
+        if jazelle:
+            self.cpu_state = 'jazelle'
+        elif thumb:
+            self.cpu_state = 'thumb'
+        else:
+            self.cpu_state = 'arm'
+
+        cycstr = ''
+        if cyclecount is not None:
+            cycstr = ', cyclecount %d' % cyclecount
+
+        if infobyte & 0x80: # LSIP packet
+            self.put(self.startsample, self.prevsample, self.out_ann,
+                     [0, ['WARN: LSIP I-Sync packet not implemented']])
+
+        return [0, ['I-Sync: %s, PC 0x%08x, %s state%s' % \
+                    (reason, addr, self.cpu_state, cycstr), \
+                    'I-Sync: %s 0x%08x' % (reason, addr)]]
+
+    def handle_trigger(self, buf):
+        return [0, ['Trigger event', 'Trigger']]
+
+    def handle_p_header(self, buf):
+        # Only non cycle-accurate mode supported.
+        if buf[0] & 0x83 == 0x80:
+            n = (buf[0] >> 6) & 1
+            e = (buf[0] >> 2) & 15
+
+            self.instructions_executed([1] * e + [0] * n)
+
+            if n:
+                return [3, ['%d instructions executed, %d skipped due to ' \
+                            'condition codes' % (e, n),
+                            '%d ins exec, %d skipped' % (e, n),
+                            '%dE,%dN' % (e, n)]]
+            else:
+                return [3, ['%d instructions executed' % e,
+                            '%d ins exec' % e, '%dE' % e]]
+        elif buf[0] & 0xF3 == 0x82:
+            i1 = (buf[0] >> 3) & 1
+            i2 = (buf[0] >> 2) & 1
+            self.instructions_executed([not i1, not i2])
+            txt1 = ('executed', 'skipped')
+            txt2 = ('E', 'S')
+            return [3, ['Instruction 1 %s, instruction 2 %s' % (txt1[i1], txt1[i2]),
+                        'I1 %s, I2 %s' % (txt2[i1], txt2[i2]),
+                        '%s,%s' % (txt2[i1], txt2[i2])]]
+        else:
+            return self.fallback(buf)
+
+    def handle_branch(self, buf):
+        if buf[-1] & 0x80 != 0x00:
+            return None # Not complete yet.
+
+        brinfo = parse_branch_addr(buf, self.last_branch, self.cpu_state,
+                                   self.options['branch_enc'])
+
+        if brinfo is None:
+            return None # Not complete yet.
+
+        addr, addrlen, cpu_state, exc_info = brinfo
+        self.last_branch = addr
+        self.current_pc = addr
+
+        txt = ''
+
+        if cpu_state != self.cpu_state:
+            txt += ', to %s state' % cpu_state
+            self.cpu_state = cpu_state
+
+        annidx = 1
+
+        if exc_info:
+            annidx = 2
+            ns, exc, cancel, altisa, hyp, resume = exc_info
+            if ns:
+                txt += ', to non-secure state'
+            if exc:
+                if exc < len(exc_names):
+                    txt += ', exception %s' % exc_names[exc]
+                else:
+                    txt += ', exception 0x%02x' % exc
+            if cancel:
+                txt += ', instr cancelled'
+            if altisa:
+                txt += ', to AltISA'
+            if hyp:
+                txt += ', to hypervisor'
+            if resume:
+                txt += ', instr resume 0x%02x' % resume
+
+        return [annidx, ['Branch to 0x%08x%s' % (addr, txt),
+                         'B 0x%08x%s' % (addr, txt)]]
+
+    def decode(self, ss, es, data):
+        ptype, rxtx, pdata = data
+
+        if ptype != 'DATA':
+            return
+
+        # Reset packet if there is a long pause between bytes.
+        # This helps getting the initial synchronization.
+        self.byte_len = es - ss
+        if ss - self.prevsample > 16 * self.byte_len:
+            self.flush_current_loc()
+            self.flush_current_func()
+            self.buf = []
+        self.prevsample = es
+
+        self.buf.append(pdata[0])
+
+        # Store the start time of the packet.
+        if len(self.buf) == 1:
+            self.startsample = ss
+
+        # Keep separate buffer for detection of sync packets.
+        # Sync packets override everything else, so that we can regain sync
+        # even if some packets are corrupted.
+        self.syncbuf = self.syncbuf[-4:] + [pdata[0]]
+        if self.syncbuf == [0x00, 0x00, 0x00, 0x00, 0x80]:
+            self.buf = self.syncbuf
+            self.syncbuf = []
+
+        # See if it is ready to be decoded.
+        ptype = self.get_packet_type(self.buf[0])
+        if hasattr(self, 'handle_' + ptype):
+            func = getattr(self, 'handle_' + ptype)
+            data = func(self.buf)
+        else:
+            data = self.fallback(self.buf)
+
+        if data is not None:
+            if data:
+                self.put(self.startsample, es, self.out_ann, data)
+            self.buf = []
diff --git a/decoders/arm_itm/__init__.py b/decoders/arm_itm/__init__.py
new file mode 100644 (file)
index 0000000..d9789a4
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'uart' or 'arm_tpiu' PD and decodes the 
+ARM Cortex-M processor trace data from Instrumentation Trace Macroblock.
+'''
+
+from .pd import Decoder
diff --git a/decoders/arm_itm/pd.py b/decoders/arm_itm/pd.py
new file mode 100644 (file)
index 0000000..e32cce3
--- /dev/null
@@ -0,0 +1,335 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+import string
+import subprocess
+
+ARM_EXCEPTIONS = {
+    0: 'Thread',
+    1: 'Reset',
+    2: 'NMI',
+    3: 'HardFault',
+    4: 'MemManage',
+    5: 'BusFault',
+    6: 'UsageFault',
+    11: 'SVCall',
+    12: 'Debug Monitor',
+    14: 'PendSV',
+    15: 'SysTick',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'arm_itm'
+    name = 'ARM ITM'
+    longname = 'ARM Instrumentation Trace Macroblock'
+    desc = 'Trace data from Cortex-M / ARMv7m ITM module.'
+    license = 'gplv2+'
+    inputs = ['uart']
+    outputs = ['arm_itm']
+    options = (
+        {'id': 'addr2line', 'desc': 'addr2line path',
+            'default': 'arm-none-eabi-addr2line'},
+        {'id': 'addr2line_opts', 'desc': 'addr2line options',
+            'default': '-f -C -s -p'},
+        {'id': 'elffile', 'desc': '.elf path', 'default': ''},
+    )
+    annotations = (
+        ('trace', 'Trace information'),
+        ('timestamp', 'Timestamp'),
+        ('software', 'Software message'),
+        ('dwt_event', 'DWT event'),
+        ('dwt_watchpoint', 'DWT watchpoint'),
+        ('dwt_exc', 'Exception trace'),
+        ('dwt_pc', 'Program counter'),
+        ('mode_thread', 'Current mode: thread'),
+        ('mode_irq', 'Current mode: IRQ'),
+        ('mode_exc', 'Current mode: Exception'),
+        ('location', 'Current location')
+    )
+    annotation_rows = (
+        ('trace', 'Trace information', (0, 1)),
+        ('software', 'Software trace', (2,)),
+        ('dwt_event', 'DWT event', (3,)),
+        ('dwt_watchpoint', 'DWT watchpoint', (4,)),
+        ('dwt_exc', 'Exception trace', (5,)),
+        ('dwt_pc', 'Program counter', (6,)),
+        ('mode', 'Current mode', (7, 8, 9,)),
+        ('location', 'Current location', (10,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.buf = []
+        self.syncbuf = []
+        self.swpackets = {}
+        self.prevsample = 0
+        self.dwt_timestamp = 0
+        self.current_mode = None
+        self.current_loc = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def get_packet_type(self, byte):
+        '''Identify packet type based on its first byte.
+           See ARMv7-M_ARM.pdf section "Debug ITM and DWT" "Packet Types"
+        '''
+        if byte & 0x7F == 0:
+            return 'sync'
+        elif byte == 0x70:
+            return 'overflow'
+        elif byte & 0x0F == 0 and byte & 0xF0 != 0:
+            return 'timestamp'
+        elif byte & 0x0F == 0x08:
+            return 'sw_extension'
+        elif byte & 0x0F == 0x0C:
+            return 'hw_extension'
+        elif byte & 0x0F == 0x04:
+            return 'reserved'
+        elif byte & 0x04 == 0x00:
+            return 'software'
+        else:
+            return 'hardware'
+
+    def mode_change(self, new_mode):
+        if self.current_mode is not None:
+            start, mode = self.current_mode
+            if mode.startswith('Thread'):
+                ann_idx = 7
+            elif mode.startswith('IRQ'):
+                ann_idx = 8
+            else:
+                ann_idx = 9
+            self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
+
+        if new_mode is None:
+            self.current_mode = None
+        else:
+            self.current_mode = (self.startsample, new_mode)
+
+    def location_change(self, new_pc):
+        if self.options['addr2line'] and self.options['elffile']:
+            opts = [self.options['addr2line'], '-e', self.options['elffile']]
+            opts += self.options['addr2line_opts'].split()
+            opts += ['0x%08x' % new_pc]
+
+            try:
+                new_loc = subprocess.check_output(opts)
+            except subprocess.CalledProcessError:
+                return
+
+            new_loc = new_loc.decode('utf-8', 'replace').strip()
+
+            if self.current_loc is not None:
+                start, loc = self.current_loc
+                if loc == new_loc:
+                    return # Still on same line.
+                self.put(start, self.startsample, self.out_ann, [10, [loc]])
+
+            self.current_loc = (self.startsample, new_loc)
+
+    def fallback(self, buf):
+        ptype = self.get_packet_type(buf[0])
+        return [0, [('Unhandled %s: ' % ptype) + ' '.join(['%02x' % b for b in buf])]]
+
+    def handle_overflow(self, buf):
+        return [0, ['Overflow']]
+
+    def handle_hardware(self, buf):
+        '''Handle packets from hardware source, i.e. DWT block.'''
+        plen = (0, 1, 2, 4)[buf[0] & 0x03]
+        pid = buf[0] >> 3
+        if len(buf) != plen + 1:
+            return None # Not complete yet.
+
+        if pid == 0:
+            text = 'DWT events:'
+            if buf[1] & 0x20:
+                text += ' Cyc'
+            if buf[1] & 0x10:
+                text += ' Fold'
+            if buf[1] & 0x08:
+                text += ' LSU'
+            if buf[1] & 0x04:
+                text += ' Sleep'
+            if buf[1] & 0x02:
+                text += ' Exc'
+            if buf[1] & 0x01:
+                text += ' CPI'
+            return [3, [text]]
+        elif pid == 1:
+            excnum = ((buf[2] & 1) << 8) | buf[1]
+            event = (buf[2] >> 4)
+            excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
+            if event == 1:
+                self.mode_change(excstr)
+                return [5, ['Enter: ' + excstr, 'E ' + excstr]]
+            elif event == 2:
+                self.mode_change(None)
+                return [5, ['Exit: ' + excstr, 'X ' + excstr]]
+            elif event == 3:
+                self.mode_change(excstr)
+                return [5, ['Resume: ' + excstr, 'R ' + excstr]]
+        elif pid == 2:
+            pc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
+            self.location_change(pc)
+            return [6, ['PC: 0x%08x' % pc]]
+        elif (buf[0] & 0xC4) == 0x84:
+            comp = (buf[0] & 0x30) >> 4
+            what = 'Read' if (buf[0] & 0x08) == 0 else 'Write'
+            if plen == 1:
+                data = '0x%02x' % (buf[1])
+            elif plen == 2:
+                data = '0x%04x' % (buf[1] | (buf[2] << 8))
+            else:
+                data = '0x%08x' % (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24))
+            return [4, ['Watchpoint %d: %s data %s' % (comp, what, data),
+                        'WP%d: %s %s' % (comp, what[0], data)]]
+        elif (buf[0] & 0xCF) == 0x47:
+            comp = (buf[0] & 0x30) >> 4
+            addr = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
+            self.location_change(addr)
+            return [4, ['Watchpoint %d: PC 0x%08x' % (comp, addr),
+                        'WP%d: PC 0x%08x' % (comp, addr)]]
+        elif (buf[0] & 0xCF) == 0x4E:
+            comp = (buf[0] & 0x30) >> 4
+            offset = buf[1] | (buf[2] << 8)
+            return [4, ['Watchpoint %d: address 0x????%04x' % (comp, offset),
+                        'WP%d: A 0x%04x' % (comp, offset)]]
+
+        return self.fallback(buf)
+
+    def handle_software(self, buf):
+        '''Handle packets generated by software running on the CPU.'''
+        plen = (0, 1, 2, 4)[buf[0] & 0x03]
+        pid = buf[0] >> 3
+        if len(buf) != plen + 1:
+            return None # Not complete yet.
+
+        if plen == 1 and chr(buf[1]) in string.printable:
+            self.add_delayed_sw(pid, chr(buf[1]))
+            return [] # Handled but no data to output.
+
+        self.push_delayed_sw()
+
+        if plen == 1:
+            return [2, ['%d: 0x%02x' % (pid, buf[1])]]
+        elif plen == 2:
+            return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
+        elif plen == 4:
+            return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
+
+    def handle_timestamp(self, buf):
+        '''Handle timestamp packets, which indicate the time of some DWT event packet.'''
+        if buf[-1] & 0x80 != 0:
+            return None # Not complete yet.
+
+        if buf[0] & 0x80 == 0:
+            tc = 0
+            ts = buf[0] >> 4
+        else:
+            tc = (buf[0] & 0x30) >> 4
+            ts = buf[1] & 0x7F
+            if len(buf) > 2:
+                ts |= (buf[2] & 0x7F) << 7
+            if len(buf) > 3:
+                ts |= (buf[3] & 0x7F) << 14
+            if len(buf) > 4:
+                ts |= (buf[4] & 0x7F) << 21
+
+        self.dwt_timestamp += ts
+
+        if tc == 0:
+            msg = '(exact)'
+        elif tc == 1:
+            msg = '(timestamp delayed)'
+        elif tc == 2:
+            msg = '(event delayed)'
+        elif tc == 3:
+            msg = '(event and timestamp delayed)'
+
+        return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
+
+    def add_delayed_sw(self, pid, c):
+        '''We join printable characters from software source so that printed
+        strings are easy to read. Joining is done by PID so that different
+        sources do not get confused with each other.'''
+        if self.swpackets.get(pid) is not None:
+            self.swpackets[pid][1] = self.prevsample
+            self.swpackets[pid][2] += c
+        else:
+            self.swpackets[pid] = [self.startsample, self.prevsample, c]
+
+    def push_delayed_sw(self):
+        for pid, packet in self.swpackets.items():
+            if packet is None:
+                continue
+            ss, prevtime, text = packet
+            # Heuristic criterion: Text has ended if at least 16 byte
+            # durations after previous received byte. Actual delay depends
+            # on printf implementation on target.
+            if self.prevsample - prevtime > 16 * self.byte_len:
+                self.put(ss, prevtime, self.out_ann, [2, ['%d: "%s"' % (pid, text)]])
+                self.swpackets[pid] = None
+
+    def decode(self, ss, es, data):
+        ptype, rxtx, pdata = data
+
+        # For now, ignore all UART packets except the actual data packets.
+        if ptype != 'DATA':
+            return
+
+        self.byte_len = es - ss
+
+        # Reset packet if there is a long pause between bytes.
+        # TPIU framing can introduce small pauses, but more than 1 frame
+        # should reset packet.
+        if ss - self.prevsample > 16 * self.byte_len:
+            self.push_delayed_sw()
+            self.buf = []
+        self.prevsample = es
+
+        # Build up the current packet byte by byte.
+        self.buf.append(pdata[0])
+
+        # Store the start time of the packet.
+        if len(self.buf) == 1:
+            self.startsample = ss
+
+        # Keep separate buffer for detection of sync packets.
+        # Sync packets override everything else, so that we can regain sync
+        # even if some packets are corrupted.
+        self.syncbuf = self.syncbuf[-5:] + [pdata[0]]
+        if self.syncbuf == [0, 0, 0, 0, 0, 0x80]:
+            self.buf = self.syncbuf
+
+        # See if it is ready to be decoded.
+        ptype = self.get_packet_type(self.buf[0])
+        if hasattr(self, 'handle_' + ptype):
+            func = getattr(self, 'handle_' + ptype)
+            data = func(self.buf)
+        else:
+            data = self.fallback(self.buf)
+
+        if data is not None:
+            if data:
+                self.put(self.startsample, es, self.out_ann, data)
+            self.buf = []
diff --git a/decoders/arm_tpiu/__init__.py b/decoders/arm_tpiu/__init__.py
new file mode 100644 (file)
index 0000000..4958df8
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'uart' decoder and decodes the frame format
+of ARMv7m Trace Port Interface Unit. It filters the data coming from various
+trace sources (such as ARMv7m ITM and ETM blocks) into separate streams that
+can be further decoded by other PDs.
+'''
+
+from .pd import Decoder
diff --git a/decoders/arm_tpiu/pd.py b/decoders/arm_tpiu/pd.py
new file mode 100644 (file)
index 0000000..ba01aa0
--- /dev/null
@@ -0,0 +1,128 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'arm_tpiu'
+    name = 'ARM TPIU'
+    longname = 'ARM Trace Port Interface Unit'
+    desc = 'Filter TPIU formatted trace data into separate streams.'
+    license = 'gplv2+'
+    inputs = ['uart']
+    outputs = ['uart'] # Emulate uart output so that arm_itm/arm_etm can stack.
+    options = (
+        {'id': 'stream', 'desc': 'Stream index', 'default': 1},
+        {'id': 'sync_offset', 'desc': 'Initial sync offset', 'default': 0},
+    )
+    annotations = (
+        ('stream', 'Current stream'),
+        ('data', 'Stream data'),
+    )
+    annotation_rows = (
+        ('stream', 'Current stream', (0,)),
+        ('data', 'Stream data', (1,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.buf = []
+        self.syncbuf = []
+        self.prevsample = 0
+        self.stream = 0
+        self.stream_ss = None
+        self.bytenum = 0
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+    def stream_changed(self, ss, stream):
+        if self.stream != stream:
+            if self.stream != 0:
+                self.put(self.stream_ss, ss, self.out_ann,
+                         [0, ["Stream %d" % self.stream, "S%d" % self.stream]])
+            self.stream = stream
+            self.stream_ss = ss
+
+    def emit_byte(self, ss, es, byte):
+        if self.stream == self.options['stream']:
+            self.put(ss, es, self.out_ann, [1, ["0x%02x" % byte]])
+            self.put(ss, es, self.out_python, ['DATA', 0, (byte, [])])
+
+    def process_frame(self, buf):
+        # Byte 15 contains the lowest bits of bytes 0, 2, ... 14.
+        lowbits = buf[15][2]
+
+        for i in range(0, 15, 2):
+            # Odd bytes can be stream ID or data.
+            delayed_stream_change = None
+            lowbit = (lowbits >> (i // 2)) & 0x01
+            if buf[i][2] & 0x01 != 0:
+                if lowbit:
+                    delayed_stream_change = buf[i][2] >> 1
+                else:
+                    self.stream_changed(buf[i][0], buf[i][2] >> 1)
+            else:
+                byte = buf[i][2] | lowbit
+                self.emit_byte(buf[i][0], buf[i][1], byte)
+
+            # Even bytes are always data.
+            if i < 14:
+                self.emit_byte(buf[i+1][0], buf[i+1][1], buf[i+1][2])
+
+            # The stream change can be delayed to occur after the data byte.
+            if delayed_stream_change is not None:
+                self.stream_changed(buf[i+1][1], delayed_stream_change)
+
+    def decode(self, ss, es, data):
+        ptype, rxtx, pdata = data
+
+        if ptype != 'DATA':
+            return
+
+        # Reset packet if there is a long pause between bytes.
+        self.byte_len = es - ss
+        if ss - self.prevsample > self.byte_len:
+            self.buf = []
+        self.prevsample = es
+
+        self.buf.append((ss, es, pdata[0]))
+        self.bytenum += 1
+
+        # Allow skipping N first bytes of the data. By adjusting the sync
+        # value, one can get initial synchronization as soon as the trace
+        # starts.
+        if self.bytenum < self.options['sync_offset']:
+            self.buf = []
+            return
+
+        # Keep separate buffer for detection of sync packets.
+        # Sync packets override everything else, so that we can regain sync
+        # even if some packets are corrupted.
+        self.syncbuf = self.syncbuf[-3:] + [pdata[0]]
+        if self.syncbuf == [0xFF, 0xFF, 0xFF, 0x7F]:
+            self.buf = []
+            self.syncbuf = []
+            return
+
+        if len(self.buf) == 16:
+            self.process_frame(self.buf)
+            self.buf = []
index 4c4a900eebdd4c04042b41e8a92043e441f9ae67..75a9fe2977b25aff43c674b283a13a82e6c4f419 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'spi' PD and decodes the In-System
 Programming (ISP) protocol of some Atmel AVR 8-bit microcontrollers.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index b9747f87f916538869e3e927d9a91f41062e9418..8e437b91eb2f1a2c02c673d0e3bd231a1d2ebbf7 100644 (file)
@@ -40,4 +40,3 @@ part = {
     (0x01, 0x02): 'Device locked',
     # TODO: Lots more entries.
 }
-
index a6b9c80edc9cb5173a934b5fef384999073aadbc..0914b5b347101b1b16f34b7ee75fcf985f580e7e 100644 (file)
@@ -54,15 +54,15 @@ class Decoder(srd.Decoder):
     def __init__(self, **kwargs):
         self.state = 'IDLE'
         self.mosi_bytes, self.miso_bytes = [], []
-        self.cmd_ss, self.cmd_es = 0, 0
+        self.ss_cmd, self.es_cmd = 0, 0
         self.xx, self.yy, self.zz, self.mm = 0, 0, 0, 0
-        self.device_ss = None
+        self.ss_device = None
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def putx(self, data):
-        self.put(self.cmd_ss, self.cmd_es, self.out_ann, data)
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
 
     def handle_cmd_programming_enable(self, cmd, ret):
         # Programming enable.
@@ -99,7 +99,7 @@ class Decoder(srd.Decoder):
 
         # Store for later.
         self.mm = cmd[3]
-        self.device_ss = self.cmd_ss
+        self.ss_device = self.ss_cmd
 
         # Sanity check on reply.
         if ret[1] != 0x30 or ret[2] != cmd[1] or ret[0] != self.yy:
@@ -112,7 +112,7 @@ class Decoder(srd.Decoder):
 
         p = part[(self.part_fam_flash_size, self.part_number)]
         data = [9, ['Device: Atmel %s' % p]]
-        self.put(self.device_ss, self.cmd_es, self.out_ann, data)
+        self.put(self.ss_device, self.es_cmd, self.out_ann, data)
 
         # Sanity check on reply.
         if ret[1] != 0x30 or ret[2] != self.xx or ret[0] != self.mm:
@@ -192,7 +192,7 @@ class Decoder(srd.Decoder):
         self.ss, self.es = ss, es
 
         if len(self.mosi_bytes) == 0:
-            self.cmd_ss = ss
+            self.ss_cmd = ss
 
         # Append new bytes.
         self.mosi_bytes.append(mosi)
@@ -202,10 +202,9 @@ class Decoder(srd.Decoder):
         if len(self.mosi_bytes) < 4:
             return
 
-        self.cmd_es = es
+        self.es_cmd = es
 
         self.handle_command(self.mosi_bytes, self.miso_bytes)
 
         self.mosi_bytes = []
         self.miso_bytes = []
-
index 38731c269348012161b5840b1a045ebe4cc82e85..60dc85f884b27a8e92f7df965687f848076b6e33 100644 (file)
@@ -27,5 +27,4 @@ the digital output side of a CAN transceiver IC such as the Microchip
 MCP-2515DM-BM).
 '''
 
-from .pd import *
-
+from .pd import Decoder
index bf38b7fb9d0e1d23f9a9b83dd76ba82349ed7a84..df2cbee7818c7d3f4938f76a44af5e44c61e92b1 100644 (file)
@@ -20,6 +20,9 @@
 
 import sigrokdecode as srd
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'can'
@@ -358,8 +361,8 @@ class Decoder(srd.Decoder):
         self.curbit += 1
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
 
             (can_rx,) = pins
@@ -376,6 +379,3 @@ class Decoder(srd.Decoder):
                 if not self.reached_bit(self.curbit):
                     continue
                 self.handle_bit(can_rx)
-            else:
-                raise Exception("Invalid state: %s" % self.state)
-
index 04805c21d07c993da177831e6bacb78e5cf7d716..8423028c3100785a6d183e53c2ff17f26336341c 100644 (file)
@@ -26,5 +26,4 @@ Details:
 http://en.wikipedia.org/wiki/DCF77
 '''
 
-from .pd import *
-
+from .pd import Decoder
index fc97a1ddd1911e5fac5d3a0c07d13dd69eda1a1c..adee4037323df77428d4ed8c8e409d8c3348287f 100644 (file)
@@ -25,6 +25,9 @@ import calendar
 def bcd2int(b):
     return (b & 0x0f) + ((b >> 4) * 10)
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'dcf77'
@@ -239,14 +242,14 @@ class Decoder(srd.Decoder):
             # Even parity over date bits (36-58): DCF77 bit 58.
             parity = self.datebits.count(1)
             s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
-            self.putx([16, ['Date parity: %s' % s, 'DP: %s' %s]])
+            self.putx([16, ['Date parity: %s' % s, 'DP: %s' % s]])
             self.datebits = []
         else:
             raise Exception('Invalid DCF77 bit: %d' % c)
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
 
             # Ignore identical samples early on (for performance reasons).
@@ -310,8 +313,4 @@ class Decoder(srd.Decoder):
 
                 self.state = 'WAIT FOR RISING EDGE'
 
-            else:
-                raise Exception('Invalid state: %s' % self.state)
-
             self.oldval = val
-
index 5bc28c3782e3f8e2148bf789eef2daf685db7c93..71662298eefbef4e7353dd9ead1a98b8fb6a158d 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'i2c' PD and decodes the Dallas DS1307
 real-time clock (RTC) specific registers and commands.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 68943d475e006c4f38f363006416b433f6ee591e..b784b6dd225bfcb6e51a4607cd0ad6d70bfa10c3 100644 (file)
@@ -1,7 +1,7 @@
 ##
 ## This file is part of the libsigrokdecode project.
-## 
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
 ## Copyright (C) 2013 Matt Ranostay <mranostay@gmail.com>
 ##
 ## This program is free software; you can redistribute it and/or modify
 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 ##
 
+import re
 import sigrokdecode as srd
 
-days_of_week = [
-    'Sunday',
-    'Monday',
-    'Tuesday',
-    'Wednesday',
-    'Thursday',
-    'Friday',
-    'Saturday',
-]
+days_of_week = (
+    'Sunday', 'Monday', 'Tuesday', 'Wednesday',
+    'Thursday', 'Friday', 'Saturday',
+)
+
+regs = (
+    'Seconds', 'Minutes', 'Hours', 'Day', 'Date', 'Month', 'Year',
+    'Control', 'RAM',
+)
+
+bits = (
+    'Clock halt', 'Seconds', 'Reserved', 'Minutes', '12/24 hours', 'AM/PM',
+    'Hours', 'Day', 'Date', 'Month', 'Year', 'OUT', 'SQWE', 'RS', 'RAM',
+)
+
+rates = {
+    0b00: '1Hz',
+    0b01: '4096kHz',
+    0b10: '8192kHz',
+    0b11: '32768kHz',
+}
+
+DS1307_I2C_ADDRESS = 0x68
+
+def regs_and_bits():
+    l = [('reg-' + r.lower(), r + ' register') for r in regs]
+    l += [('bit-' + re.sub('\/| ', '-', b).lower(), b + ' bit') for b in bits]
+    return tuple(l)
 
 # Return the specified BCD number (max. 8 bits) as integer.
 def bcd2int(b):
@@ -44,8 +64,18 @@ class Decoder(srd.Decoder):
     license = 'gplv2+'
     inputs = ['i2c']
     outputs = ['ds1307']
-    annotations = (
-        ('text', 'Human-readable text'),
+    annotations =  regs_and_bits() + (
+        ('read-datetime', 'Read date/time'),
+        ('write-datetime', 'Write date/time'),
+        ('reg-read', 'Register read'),
+        ('reg-write', 'Register write'),
+        ('warnings', 'Warnings'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', tuple(range(9, 24))),
+        ('regs', 'Registers', tuple(range(9))),
+        ('date-time', 'Date/time', (24, 25, 26, 27)),
+        ('warnings', 'Warnings', (28,)),
     )
 
     def __init__(self, **kwargs):
@@ -57,6 +87,7 @@ class Decoder(srd.Decoder):
         self.date = -1
         self.months = -1
         self.years = -1
+        self.bits = []
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -64,40 +95,124 @@ class Decoder(srd.Decoder):
     def putx(self, data):
         self.put(self.ss, self.es, self.out_ann, data)
 
-    def handle_reg_0x00(self, b): # Seconds
-        self.seconds = bcd2int(b & 0x7f)
-        self.putx([0, ['Seconds: %d' % self.seconds]])
+    def putd(self, bit1, bit2, data):
+        self.put(self.bits[bit1][1], self.bits[bit2][2], self.out_ann, data)
+
+    def putr(self, bit):
+        self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann,
+                 [11, ['Reserved bit', 'Reserved', 'Rsvd', 'R']])
 
-    def handle_reg_0x01(self, b): # Minutes
-        self.minutes = bcd2int(b & 0x7f)
-        self.putx([0, ['Minutes: %d' % self.minutes]])
+    def handle_reg_0x00(self, b): # Seconds (0-59) / Clock halt bit
+        self.putd(7, 0, [0, ['Seconds', 'Sec', 'S']])
+        ch = 1 if (b & (1 << 7)) else 0
+        self.putd(7, 7, [9, ['Clock halt: %d' % ch, 'Clk hlt: %d' % ch,
+                        'CH: %d' % ch, 'CH']])
+        s = self.seconds = bcd2int(b & 0x7f)
+        self.putd(6, 0, [10, ['Second: %d' % s, 'Sec: %d' % s, 'S: %d' % s, 'S']])
 
-    def handle_reg_0x02(self, b): # Hours
-        self.hours = bcd2int(b & 0x3f)
-        self.putx([0, ['Hours: %d' % self.hours]])
+    def handle_reg_0x01(self, b): # Minutes (0-59)
+        self.putd(7, 0, [1, ['Minutes', 'Min', 'M']])
+        self.putr(7)
+        m = self.minutes = bcd2int(b & 0x7f)
+        self.putd(6, 0, [12, ['Minute: %d' % m, 'Min: %d' % m, 'M: %d' % m, 'M']])
 
-    def handle_reg_0x03(self, b): # Day of week
-        self.days = bcd2int(b & 0x7)
-        self.putx([0, ['Day of Week: %s' % days_of_week[self.days - 1]]])
+    def handle_reg_0x02(self, b): # Hours (1-12+AM/PM or 0-23)
+        self.putd(7, 0, [2, ['Hours', 'H']])
+        self.putr(7)
+        ampm_mode = True if (b & (1 << 6)) else False
+        if ampm_mode:
+            self.putd(6, 6, [13, ['12-hour mode', '12h mode', '12h']])
+            a = 'AM' if (b & (1 << 6)) else 'PM'
+            self.putd(5, 5, [14, [a, a[0]]])
+            h = self.hours = bcd2int(b & 0x1f)
+            self.putd(4, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
+        else:
+            self.putd(6, 6, [13, ['24-hour mode', '24h mode', '24h']])
+            h = self.hours = bcd2int(b & 0x3f)
+            self.putd(5, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
 
-    def handle_reg_0x04(self, b): # Date
-        self.date =  bcd2int(b & 0x3f)
-        self.putx([0, ['Days: %d' % self.date]])
+    def handle_reg_0x03(self, b): # Day / day of week (1-7)
+        self.putd(7, 0, [3, ['Day of week', 'Day', 'D']])
+        for i in (7, 6, 5, 4, 3):
+            self.putr(i)
+        w = self.days = bcd2int(b & 0x07)
+        ws = days_of_week[self.days - 1]
+        self.putd(2, 0, [16, ['Weekday: %s' % ws, 'WD: %s' % ws, 'WD', 'W']])
 
-    def handle_reg_0x05(self, b): # Month
-        self.months = bcd2int(b & 0x1f)
-        self.putx([0, ['Months: %d' % self.months]])
+    def handle_reg_0x04(self, b): # Date (1-31)
+        self.putd(7, 0, [4, ['Date', 'D']])
+        for i in (7, 6):
+            self.putr(i)
+        d = self.date = bcd2int(b & 0x3f)
+        self.putd(5, 0, [17, ['Date: %d' % d, 'D: %d' % d, 'D']])
 
-    def handle_reg_0x06(self, b): # Year
-        self.years = bcd2int(b & 0xff) + 2000;
-        self.putx([0, ['Years: %d' % self.years]])
+    def handle_reg_0x05(self, b): # Month (1-12)
+        self.putd(7, 0, [5, ['Month', 'Mon', 'M']])
+        for i in (7, 6, 5):
+            self.putr(i)
+        m = self.months = bcd2int(b & 0x1f)
+        self.putd(4, 0, [18, ['Month: %d' % m, 'Mon: %d' % m, 'M: %d' % m, 'M']])
+
+    def handle_reg_0x06(self, b): # Year (0-99)
+        self.putd(7, 0, [6, ['Year', 'Y']])
+        y = self.years = bcd2int(b & 0xff)
+        self.years += 2000
+        self.putd(7, 0, [19, ['Year: %d' % y, 'Y: %d' % y, 'Y']])
 
     def handle_reg_0x07(self, b): # Control Register
-        pass
+        self.putd(7, 0, [7, ['Control', 'Ctrl', 'C']])
+        for i in (6, 5, 3, 2):
+            self.putr(i)
+        o = 1 if (b & (1 << 7)) else 0
+        s = 1 if (b & (1 << 4)) else 0
+        s2 = 'en' if (b & (1 << 4)) else 'dis'
+        r = rates[b & 0x03]
+        self.putd(7, 7, [20, ['Output control: %d' % o,
+            'OUT: %d' % o, 'O: %d' % o, 'O']])
+        self.putd(4, 4, [21, ['Square wave output: %sabled' % s2,
+            'SQWE: %sabled' % s2, 'SQWE: %d' % s, 'S: %d' % s, 'S']])
+        self.putd(1, 0, [22, ['Square wave output rate: %s' % r,
+            'Square wave rate: %s' % r, 'SQW rate: %s' % r, 'Rate: %s' % r,
+            'RS: %s' % s, 'RS', 'R']])
+
+    def handle_reg_0x3f(self, b): # RAM (bytes 0x08-0x3f)
+        self.putd(7, 0, [8, ['RAM', 'R']])
+        self.putd(7, 0, [23, ['SRAM: 0x%02X' % b, '0x%02X' % b]])
+
+    def output_datetime(self, cls, rw):
+        # TODO: Handle read/write of only parts of these items.
+        d = '%s, %02d.%02d.%4d %02d:%02d:%02d' % (
+            days_of_week[self.days - 1], self.date, self.months,
+            self.years, self.hours, self.minutes, self.seconds)
+        self.put(self.ss_block, self.es, self.out_ann,
+                 [cls, ['%s date/time: %s' % (rw, d)]])
+
+    def handle_reg(self, b):
+        r = self.reg if self.reg < 8 else 0x3f
+        fn = getattr(self, 'handle_reg_0x%02x' % r)
+        fn(b)
+        # Honor address auto-increment feature of the DS1307. When the
+        # address reaches 0x3f, it will wrap around to address 0.
+        self.reg += 1
+        if self.reg > 0x3f:
+            self.reg = 0
+
+    def is_correct_chip(self, addr):
+        if addr == DS1307_I2C_ADDRESS:
+            return True
+        self.put(self.ss_block, self.es, self.out_ann,
+                 [28, ['Ignoring non-DS1307 data (slave 0x%02X)' % addr]])
+        return False
 
     def decode(self, ss, es, data):
         cmd, databyte = data
 
+        # Collect the 'BITS' packet, then return. The next packet is
+        # guaranteed to belong to these bits we just stored.
+        if cmd == 'BITS':
+            self.bits = databyte
+            return
+
         # Store the start/end samples of this I²C packet.
         self.ss, self.es = ss, es
 
@@ -107,12 +222,14 @@ class Decoder(srd.Decoder):
             if cmd != 'START':
                 return
             self.state = 'GET SLAVE ADDR'
-            self.block_start_sample = ss
+            self.ss_block = ss
         elif self.state == 'GET SLAVE ADDR':
             # Wait for an address write operation.
-            # TODO: We should only handle packets to the RTC slave (0x68).
             if cmd != 'ADDRESS WRITE':
                 return
+            if not self.is_correct_chip(databyte):
+                self.state = 'IDLE'
+                return
             self.state = 'GET REG ADDR'
         elif self.state == 'GET REG ADDR':
             # Wait for a data write (master selects the slave register).
@@ -121,49 +238,27 @@ class Decoder(srd.Decoder):
             self.reg = databyte
             self.state = 'WRITE RTC REGS'
         elif self.state == 'WRITE RTC REGS':
-            # If we see a Repeated Start here, it's probably an RTC read.
+            # If we see a Repeated Start here, it's an RTC read.
             if cmd == 'START REPEAT':
                 self.state = 'READ RTC REGS'
                 return
             # Otherwise: Get data bytes until a STOP condition occurs.
             if cmd == 'DATA WRITE':
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte)
-                self.reg += 1
-                # TODO: Check for NACK!
+                self.handle_reg(databyte)
             elif cmd == 'STOP':
-                # TODO: Handle read/write of only parts of these items.
-                d = '%s, %02d.%02d.%02d %02d:%02d:%02d' % (
-                    days_of_week[self.days - 1], self.date, self.months,
-                    self.years, self.hours, self.minutes, self.seconds)
-                self.put(self.block_start_sample, es, self.out_ann,
-                         [0, ['Written date/time: %s' % d]])
+                self.output_datetime(25, 'Written')
                 self.state = 'IDLE'
-            else:
-                pass # TODO
         elif self.state == 'READ RTC REGS':
             # Wait for an address read operation.
-            # TODO: We should only handle packets to the RTC slave (0x68).
-            if cmd == 'ADDRESS READ':
-                self.state = 'READ RTC REGS2'
+            if cmd != 'ADDRESS READ':
+                return
+            if not self.is_correct_chip(databyte):
+                self.state = 'IDLE'
                 return
-            else:
-                pass # TODO
+            self.state = 'READ RTC REGS2'
         elif self.state == 'READ RTC REGS2':
             if cmd == 'DATA READ':
-                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
-                handle_reg(databyte)
-                self.reg += 1
-                # TODO: Check for NACK!
+                self.handle_reg(databyte)
             elif cmd == 'STOP':
-                d = '%s, %02d.%02d.%02d %02d:%02d:%02d' % (
-                    days_of_week[self.days - 1], self.date, self.months,
-                    self.years, self.hours, self.minutes, self.seconds)
-                self.put(self.block_start_sample, es, self.out_ann,
-                         [0, ['Read date/time: %s' % d]])
+                self.output_datetime(24, 'Read')
                 self.state = 'IDLE'
-            else:
-                pass # TODO?
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
index 8683006db4fd5c8579de22bd2398eebfa8e2e5d9..de544d3c716eb8c48dacb9f080029dba07032a80 100644 (file)
@@ -36,5 +36,4 @@ More information on EDID is available here:
  https://en.wikipedia.org/wiki/Extended_display_identification_data
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 8af10aee7e65dba082d0c8753f92176a52cabe4a..b154de7e4e0ad82204afa32b4618646df69ec290 100644 (file)
@@ -85,6 +85,10 @@ class Decoder(srd.Decoder):
         ('fields', 'EDID structure fields'),
         ('sections', 'EDID structure sections'),
     )
+    annotation_rows = (
+        ('sections', 'Sections', (1,)),
+        ('fields', 'Fields', (0,)),
+    )
 
     def __init__(self, **kwargs):
         self.state = None
@@ -109,7 +113,6 @@ class Decoder(srd.Decoder):
         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
@@ -120,24 +123,43 @@ class Decoder(srd.Decoder):
                     self.cache = self.cache[-8:]
                     self.cnt = 8
                     self.state = 'edid'
-                    self.put(ss, es, self.out_ann, [0, ['EDID header']])
+                    self.put(self.sn[0][0], es, self.out_ann,
+                            [ANN_SECTIONS, ['Header']])
+                    self.put(self.sn[0][0], es, self.out_ann,
+                            [ANN_FIELDS, ['Header pattern']])
         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)
+                self.put(self.sn[OFF_VENDOR][0], es, self.out_ann,
+                        [ANN_SECTIONS, ['Vendor/product']])
             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]])
+                self.put(self.sn[OFF_VERSION][0], es, self.out_ann,
+                        [ANN_SECTIONS, ['EDID Version']])
+                self.put(self.sn[OFF_VERSION][0], self.sn[OFF_VERSION][1],
+                        self.out_ann, [ANN_FIELDS,
+                            ["Version %d" % self.cache[-2]]])
+                self.put(self.sn[OFF_VERSION+1][0], self.sn[OFF_VERSION+1][1],
+                        self.out_ann, [ANN_FIELDS,
+                            [ "Revision %d" % self.cache[-1]]])
             elif self.cnt == OFF_CHROM:
+                self.put(self.sn[OFF_BASIC][0], es, self.out_ann,
+                        [ANN_SECTIONS, ['Basic display']])
                 self.decode_basicdisplay(-5)
             elif self.cnt == OFF_EST_TIMING:
+                self.put(self.sn[OFF_CHROM][0], es, self.out_ann,
+                        [ANN_SECTIONS, ['Color characteristics']])
                 self.decode_chromaticity(-10)
             elif self.cnt == OFF_STD_TIMING:
+                self.put(self.sn[OFF_EST_TIMING][0], es, self.out_ann,
+                        [ANN_SECTIONS, ['Established timings']])
                 self.decode_est_timing(-3)
             elif self.cnt == OFF_DET_TIMING:
-                self.decode_std_timing(-16)
+                self.put(self.sn[OFF_STD_TIMING][0], es, self.out_ann,
+                        [ANN_SECTIONS, ['Standard timings']])
+                self.decode_std_timing(self.cnt - 16)
             elif self.cnt == OFF_NUM_EXT:
                 self.decode_descriptors(-72)
             elif self.cnt == OFF_CHECKSUM:
@@ -311,7 +333,7 @@ class Decoder(srd.Decoder):
                     modestr += est_modes[i] + ', '
         if modestr:
             self.ann_field(offset, offset+2,
-                           'Supported establised modes: %s' % modestr[:-2])
+                           'Supported established modes: %s' % modestr[:-2])
 
     def decode_std_timing(self, offset):
         modestr = ''
@@ -326,8 +348,8 @@ class Decoder(srd.Decoder):
             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])
+            self.ann_field(offset, offset + 15,
+                    'Supported standard modes: %s' % modestr[:-2])
 
     def decode_detailed_timing(self, offset):
         if offset == -72 and self.have_preferred_timing:
@@ -336,7 +358,7 @@ class Decoder(srd.Decoder):
         else:
             section = 'Detailed'
         section += ' timing descriptor'
-        self.put(self.sn[offset][0], self.sn[offset+18][1],
+        self.put(self.sn[offset][0], self.sn[offset+17][1],
              self.out_ann, [ANN_SECTIONS, [section]])
 
         pixclock = float((self.cache[offset+1] << 8) + self.cache[offset]) / 100
@@ -346,38 +368,36 @@ class Decoder(srd.Decoder):
         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)
+        self.ann_field(offset+2, 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)
+        self.ann_field(offset+5, 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)
+        self.ann_field(offset+8, 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)
+        self.ann_field(offset+8, 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)
+        self.ann_field(offset+8, 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)
+        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)
+        self.ann_field(offset+16, offset+16, 'Vertical border: %d lines' % vert_border)
 
         features = 'Flags: '
         if self.cache[offset+17] & 0x80:
@@ -418,16 +438,22 @@ class Decoder(srd.Decoder):
         tag = self.cache[offset+3]
         if tag == 0xff:
             # Monitor serial number
+            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
+                     [ANN_SECTIONS, ['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())
+            self.ann_field(offset, offset+17, text.strip())
         elif tag == 0xfe:
             # Text
+            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
+                     [ANN_SECTIONS, ['Text']])
             text = bytes(self.cache[offset+5:][:13]).decode(encoding='cp437', errors='replace')
-            self.ann_field(offset, offset+17, 'Info: %s' % text.strip())
+            self.ann_field(offset, offset+17, text.strip())
         elif tag == 0xfc:
             # Monitor name
+            self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
+                     [ANN_SECTIONS, ['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())
+            self.ann_field(offset, offset+17, text.strip())
         elif tag == 0xfd:
             # Monitor range limits
             self.put(self.sn[offset][0], self.sn[offset+17][1], self.out_ann,
@@ -465,4 +491,3 @@ class Decoder(srd.Decoder):
             else:
                 if self.cache[i+2] == 0 or self.cache[i+4] == 0:
                     self.decode_descriptor(i)
-
diff --git a/decoders/eeprom24xx/__init__.py b/decoders/eeprom24xx/__init__.py
new file mode 100644 (file)
index 0000000..de19da4
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 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
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes the
+industry standard 24xx series serial EEPROM protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/eeprom24xx/lists.py b/decoders/eeprom24xx/lists.py
new file mode 100644 (file)
index 0000000..ff4b3ed
--- /dev/null
@@ -0,0 +1,171 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 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
+##
+
+#
+# Chip specific properties:
+#
+# - vendor: chip manufacturer
+# - model: chip model
+# - size: total EEPROM size (in number of bytes)
+# - page_size: page size (in number of bytes)
+# - page_wraparound: Whether writes wrap-around at page boundaries
+# - addr_bytes: number of EEPROM address bytes used
+# - addr_pins: number of address pins (A0/A1/A2) on this chip
+# - max_speed: max. supported I²C speed (in kHz)
+#
+chips = {
+    # Generic chip (128 bytes, 8 bytes page size)
+    'generic': {
+        'vendor': '',
+        'model': 'Generic',
+        'size': 128,
+        'page_size': 8,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 3,
+        'max_speed': 400,
+    },
+
+    # Microchip
+    'microchip_24aa65': {
+        'vendor': 'Microchip',
+        'model': '24AA65',
+        'size': 8 * 1024,
+        'page_size': 64, # Actually 8, but there are 8 pages of "input cache"
+        'page_wraparound': True,
+        'addr_bytes': 2,
+        'addr_pins': 3,
+        'max_speed': 400,
+    },
+    'microchip_24lc65': {
+        'vendor': 'Microchip',
+        'model': '24LC65',
+        'size': 8 * 1024,
+        'page_size': 64, # Actually 8, but there are 8 pages of "input cache"
+        'page_wraparound': True,
+        'addr_bytes': 2,
+        'addr_pins': 3,
+        'max_speed': 400,
+    },
+    'microchip_24c65': {
+        'vendor': 'Microchip',
+        'model': '24C65',
+        'size': 8 * 1024,
+        'page_size': 64, # Actually 8, but there are 8 pages of "input cache"
+        'page_wraparound': True,
+        'addr_bytes': 2,
+        'addr_pins': 3,
+        'max_speed': 400,
+    },
+    'microchip_24aa64': {
+        'vendor': 'Microchip',
+        'model': '24AA64',
+        'size': 8 * 1024,
+        'page_size': 32,
+        'page_wraparound': True,
+        'addr_bytes': 2,
+        'addr_pins': 3,
+        'max_speed': 400, # 100 for VCC < 2.5V
+    },
+    'microchip_24lc64': {
+        'vendor': 'Microchip',
+        'model': '24LC64',
+        'size': 8 * 1024,
+        'page_size': 32,
+        'page_wraparound': True,
+        'addr_bytes': 2,
+        'addr_pins': 3,
+        'max_speed': 400,
+    },
+    'microchip_24aa02uid': {
+        'vendor': 'Microchip',
+        'model': '24AA02UID',
+        'size': 256,
+        'page_size': 8,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 0, # Pins A0, A1, A2 not used
+        'max_speed': 400,
+    },
+    'microchip_24aa025uid': {
+        'vendor': 'Microchip',
+        'model': '24AA025UID',
+        'size': 256,
+        'page_size': 16,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 3,
+        'max_speed': 400,
+    },
+    'microchip_24aa025uid_sot23': {
+        'vendor': 'Microchip',
+        'model': '24AA025UID (SOT-23)',
+        'size': 256,
+        'page_size': 16,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 2, # SOT-23 package: A2 not available
+        'max_speed': 400,
+    },
+
+    # Siemens
+    'siemens_slx_24c01': {
+        'vendor': 'Siemens',
+        'model': 'SLx 24C01',
+        'size': 128,
+        'page_size': 8,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 0, # Pins A0, A1, A2 are not connected (NC)
+        'max_speed': 400,
+    },
+    'siemens_slx_24c02': {
+        'vendor': 'Siemens',
+        'model': 'SLx 24C02',
+        'size': 256,
+        'page_size': 8,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 0, # Pins A0, A1, A2 are not connected (NC)
+        'max_speed': 400,
+    },
+
+    # ST
+    'st_m24c01': {
+        'vendor': 'ST',
+        'model': 'M24C01',
+        'size': 128,
+        'page_size': 16,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 3, # Called E0, E1, E2 on this chip.
+        'max_speed': 400,
+    },
+    'st_m24c02': {
+        'vendor': 'ST',
+        'model': 'M24C02',
+        'size': 256,
+        'page_size': 16,
+        'page_wraparound': True,
+        'addr_bytes': 1,
+        'addr_pins': 3, # Called E0, E1, E2 on this chip.
+        'max_speed': 400,
+    },
+}
diff --git a/decoders/eeprom24xx/pd.py b/decoders/eeprom24xx/pd.py
new file mode 100644 (file)
index 0000000..0738c06
--- /dev/null
@@ -0,0 +1,430 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+from .lists import *
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'eeprom24xx'
+    name = '24xx EEPROM'
+    longname = '24xx I²C EEPROM'
+    desc = '24xx series I²C EEPROM protocol.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['eeprom24xx']
+    options = (
+        {'id': 'chip', 'desc': 'Chip', 'default': 'generic',
+            'values': tuple(chips.keys())},
+        {'id': 'addr_counter', 'desc': 'Initial address counter value',
+            'default': 0},
+    )
+    annotations = (
+        # Warnings
+        ('warnings', 'Warnings'),
+        # Bits/bytes
+        ('control-code', 'Control code'),
+        ('address-pin', 'Address pin (A0/A1/A2)'),
+        ('rw-bit', 'Read/write bit'),
+        ('word-addr-byte', 'Word address byte'),
+        ('data-byte', 'Data byte'),
+        # Fields
+        ('control-word', 'Control word'),
+        ('word-addr', 'Word address'),
+        ('data', 'Data'),
+        # Operations
+        ('byte-write', 'Byte write'),
+        ('page-write', 'Page write'),
+        ('cur-addr-read', 'Current address read'),
+        ('random-read', 'Random read'),
+        ('seq-random-read', 'Sequential random read'),
+        ('seq-cur-addr-read', 'Sequential current address read'),
+        ('ack-polling', 'Acknowledge polling'),
+        ('set-bank-addr', 'Set bank address'), # SBA. Only 34AA04.
+        ('read-bank-addr', 'Read bank address'), # RBA. Only 34AA04.
+        ('set-wp', 'Set write protection'), # SWP
+        ('clear-all-wp', 'Clear all write protection'), # CWP
+        ('read-wp', 'Read write protection status'), # RPS
+    )
+    annotation_rows = (
+        ('bits-bytes', 'Bits/bytes', (1, 2, 3, 4, 5)),
+        ('fields', 'Fields', (6, 7, 8)),
+        ('ops', 'Operations', tuple(range(9, 21))),
+        ('warnings', 'Warnings', (0,)),
+    )
+    binary = (
+        ('binary', 'Binary'),
+    )
+
+    def __init__(self, **kwargs):
+        self.reset()
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_bin = self.register(srd.OUTPUT_BINARY)
+        self.chip = chips[self.options['chip']]
+        self.addr_counter = self.options['addr_counter']
+
+    def putb(self, data):
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+    def putbin(self, data):
+        self.put(self.ss_block, self.es_block, self.out_bin, data)
+
+    def putbits(self, bit1, bit2, bits, data):
+        self.put(bits[bit1][1], bits[bit2][2], self.out_ann, data)
+
+    def reset(self):
+        self.state = 'WAIT FOR START'
+        self.packets = []
+        self.bytebuf = []
+        self.is_cur_addr_read = False
+        self.is_random_access_read = False
+        self.is_seq_random_read = False
+        self.is_byte_write = False
+        self.is_page_write = False
+
+    def packet_append(self):
+        self.packets.append([self.ss, self.es, self.cmd, self.databyte, self.bits])
+        if self.cmd in ('DATA READ', 'DATA WRITE'):
+            self.bytebuf.append(self.databyte)
+
+    def hexbytes(self, idx):
+        return ' '.join(['%02X' % b for b in self.bytebuf[idx:]])
+
+    def put_control_word(self, bits):
+        s = ''.join(['%d' % b[0] for b in reversed(bits[4:])])
+        self.putbits(7, 4, bits, [1, ['Control code bits: ' + s,
+            'Control code: ' + s, 'Ctrl code: ' + s, 'Ctrl code', 'Ctrl', 'C']])
+        for i in reversed(range(self.chip['addr_pins'])):
+            self.putbits(i + 1, i + 1, bits,
+                [2, ['Address bit %d: %d' % (i, bits[i + 1][0]),
+                     'Addr bit %d' % i, 'A%d' % i, 'A']])
+        s1 = 'read' if bits[0][0] == 1 else 'write'
+        s2 = 'R' if bits[0][0] == 1 else 'W'
+        self.putbits(0, 0, bits, [3, ['R/W bit: ' + s1, 'R/W', 'RW', s2]])
+        self.putbits(7, 0, bits, [6, ['Control word', 'Control', 'CW', 'C']])
+
+    def put_word_addr(self, p):
+        if self.chip['addr_bytes'] == 1:
+            a = p[1][3]
+            self.put(p[1][0], p[1][1], self.out_ann,
+                [4, ['Word address byte: %02X' % a, 'Word addr byte: %02X' % a,
+                     'Addr: %02X' % a, 'A: %02X' % a, '%02X' % a]])
+            self.put(p[1][0], p[1][1], self.out_ann, [7, ['Word address',
+                     'Word addr', 'Addr', 'A']])
+            self.addr_counter = a
+        else:
+            a = p[1][3]
+            self.put(p[1][0], p[1][1], self.out_ann,
+                [4, ['Word address high byte: %02X' % a,
+                     'Word addr high byte: %02X' % a,
+                     'Addr high: %02X' % a, 'AH: %02X' % a, '%02X' % a]])
+            a = p[2][3]
+            self.put(p[2][0], p[2][1], self.out_ann,
+                [4, ['Word address low byte: %02X' % a,
+                     'Word addr low byte: %02X' % a,
+                     'Addr low: %02X' % a, 'AL: %02X' % a, '%02X' % a]])
+            self.put(p[1][0], p[2][1], self.out_ann, [7, ['Word address',
+                     'Word addr', 'Addr', 'A']])
+            self.addr_counter = (p[1][3] << 8) | p[2][3]
+
+    def put_data_byte(self, p):
+        if self.chip['addr_bytes'] == 1:
+            s = '%02X' % self.addr_counter
+        else:
+            s = '%04X' % self.addr_counter
+        self.put(p[0], p[1], self.out_ann, [5, ['Data byte %s: %02X' % \
+            (s, p[3]), 'Data byte: %02X' % p[3], \
+            'Byte: %02X' % p[3], 'DB: %02X' % p[3], '%02X' % p[3]]])
+
+    def put_data_bytes(self, idx, cls, s):
+        for p in self.packets[idx:]:
+            self.put_data_byte(p)
+            self.addr_counter += 1
+        self.put(self.packets[idx][0], self.packets[-1][1], self.out_ann,
+            [8, ['Data', 'D']])
+        a = ''.join(['%s' % c[0] for c in s.split()]).upper()
+        self.putb([cls, ['%s (%s): %s' % (s, self.addr_and_len(), \
+                  self.hexbytes(self.chip['addr_bytes'])),
+                  '%s (%s)' % (s, self.addr_and_len()), s, a, s[0]]])
+        self.putbin((0, bytes(self.bytebuf[self.chip['addr_bytes']:])))
+
+    def addr_and_len(self):
+        if self.chip['addr_bytes'] == 1:
+            a = '%02X' % self.bytebuf[0]
+        else:
+            a = '%02X%02X' % tuple(self.bytebuf[:2])
+        num_data_bytes = len(self.bytebuf) - self.chip['addr_bytes']
+        d = '%d bytes' % num_data_bytes
+        if num_data_bytes <= 1:
+            d = d[:-1]
+        return 'addr=%s, %s' % (a, d)
+
+    def decide_on_seq_or_rnd_read(self):
+        if len(self.bytebuf) < 2:
+            self.reset()
+            return
+        if len(self.bytebuf) == 2:
+            self.is_random_access_read = True
+        else:
+            self.is_seq_random_read = True
+
+    def put_operation(self):
+        idx = 1 + self.chip['addr_bytes']
+        if self.is_byte_write:
+            # Byte write: word address, one data byte.
+            self.put_word_addr(self.packets)
+            self.put_data_bytes(idx, 9, 'Byte write')
+        elif self.is_page_write:
+            # Page write: word address, two or more data bytes.
+            self.put_word_addr(self.packets)
+            intitial_addr = self.addr_counter
+            self.put_data_bytes(idx, 10, 'Page write')
+            num_bytes_to_write = len(self.packets[idx:])
+            if num_bytes_to_write > self.chip['page_size']:
+                self.putb([0, ['Warning: Wrote %d bytes but page size is '
+                               'only %d bytes!' % (num_bytes_to_write,
+                               self.chip['page_size'])]])
+            page1 = int(intitial_addr / self.chip['page_size'])
+            page2 = int((self.addr_counter - 1) / self.chip['page_size'])
+            if page1 != page2:
+                self.putb([0, ['Warning: Page write crossed page boundary '
+                               'from page %d to %d!' % (page1, page2)]])
+        elif self.is_cur_addr_read:
+            # Current address read: no word address, one data byte.
+            self.put_data_byte(self.packets[1])
+            self.put(self.packets[1][0], self.packets[-1][1], self.out_ann,
+                [8, ['Data', 'D']])
+            self.putb([11, ['Current address read: %02X' % self.bytebuf[0],
+                       'Current address read', 'Cur addr read', 'CAR', 'C']])
+            self.putbin((0, bytes([self.bytebuf[0]])))
+            self.addr_counter += 1
+        elif self.is_random_access_read:
+            # Random access read: word address, one data byte.
+            self.put_control_word(self.packets[idx][4])
+            self.put_word_addr(self.packets)
+            self.put_data_bytes(idx + 1, 12, 'Random access read')
+        elif self.is_seq_random_read:
+            # Sequential random read: word address, two or more data bytes.
+            self.put_control_word(self.packets[idx][4])
+            self.put_word_addr(self.packets)
+            self.put_data_bytes(idx + 1, 13, 'Sequential random read')
+
+    def handle_wait_for_start(self):
+        # Wait for an I²C START condition.
+        if self.cmd not in ('START', 'START REPEAT'):
+            return
+        self.ss_block = self.ss
+        self.state = 'GET CONTROL WORD'
+
+    def handle_get_control_word(self):
+        # The packet after START must be an ADDRESS READ or ADDRESS WRITE.
+        if self.cmd not in ('ADDRESS READ', 'ADDRESS WRITE'):
+            self.reset()
+            return
+        self.packet_append()
+        self.put_control_word(self.bits)
+        self.state = '%s GET ACK NACK AFTER CONTROL WORD' % self.cmd[8]
+
+    def handle_r_get_ack_nack_after_control_word(self):
+        if self.cmd == 'ACK':
+            self.state = 'R GET WORD ADDR OR BYTE'
+        elif self.cmd == 'NACK':
+            self.es_block = self.es
+            self.putb([0, ['Warning: No reply from slave!']])
+            self.reset()
+        else:
+            self.reset()
+
+    def handle_r_get_word_addr_or_byte(self):
+        if self.cmd == 'STOP':
+            self.es_block = self.es
+            self.putb([0, ['Warning: Slave replied, but master aborted!']])
+            self.reset()
+            return
+        elif self.cmd != 'DATA READ':
+            self.reset()
+            return
+        self.packet_append()
+        self.state = 'R GET ACK NACK AFTER WORD ADDR OR BYTE'
+
+    def handle_r_get_ack_nack_after_word_addr_or_byte(self):
+        if self.cmd == 'ACK':
+            self.state = 'R GET RESTART'
+        elif self.cmd == 'NACK':
+            self.is_cur_addr_read = True
+            self.state = 'GET STOP AFTER LAST BYTE'
+        else:
+            self.reset()
+
+    def handle_r_get_restart(self):
+        if self.cmd == 'RESTART':
+            self.state = 'R READ BYTE'
+        else:
+            self.reset()
+
+    def handle_r_read_byte(self):
+        if self.cmd == 'DATA READ':
+            self.packet_append()
+            self.state = 'R GET ACK NACK AFTER BYTE WAS READ'
+        else:
+            self.reset()
+
+    def handle_r_get_ack_nack_after_byte_was_read(self):
+        if self.cmd == 'ACK':
+            self.state = 'R READ BYTE'
+        elif self.cmd == 'NACK':
+            # It's either a RANDOM READ or a SEQUENTIAL READ.
+            self.state = 'GET STOP AFTER LAST BYTE'
+        else:
+            self.reset()
+
+    def handle_w_get_ack_nack_after_control_word(self):
+        if self.cmd == 'ACK':
+            self.state = 'W GET WORD ADDR'
+        elif self.cmd == 'NACK':
+            self.es_block = self.es
+            self.putb([0, ['Warning: No reply from slave!']])
+            self.reset()
+        else:
+            self.reset()
+
+    def handle_w_get_word_addr(self):
+        if self.cmd == 'STOP':
+            self.es_block = self.es
+            self.putb([0, ['Warning: Slave replied, but master aborted!']])
+            self.reset()
+            return
+        elif self.cmd != 'DATA WRITE':
+            self.reset()
+            return
+        self.packet_append()
+        self.state = 'W GET ACK AFTER WORD ADDR'
+
+    def handle_w_get_ack_after_word_addr(self):
+        if self.cmd == 'ACK':
+            self.state = 'W DETERMINE EEPROM READ OR WRITE'
+        else:
+            self.reset()
+
+    def handle_w_determine_eeprom_read_or_write(self):
+        if self.cmd == 'START REPEAT':
+            # It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ.
+            self.state = 'R2 GET CONTROL WORD'
+        elif self.cmd == 'DATA WRITE':
+            self.packet_append()
+            self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
+        else:
+            self.reset()
+
+    def handle_w_write_byte(self):
+        if self.cmd == 'DATA WRITE':
+            self.packet_append()
+            self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
+        elif self.cmd == 'STOP':
+            if len(self.bytebuf) < 2:
+                self.reset()
+                return
+            self.es_block = self.es
+            if len(self.bytebuf) == 2:
+                self.is_byte_write = True
+            else:
+                self.is_page_write = True
+            self.put_operation()
+            self.reset()
+        elif self.cmd == 'START REPEAT':
+            # It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ.
+            self.state = 'R2 GET CONTROL WORD'
+        else:
+            self.reset()
+
+    def handle_w_get_ack_nack_after_byte_was_written(self):
+        if self.cmd == 'ACK':
+            self.state = 'W WRITE BYTE'
+        else:
+            self.reset()
+
+    def handle_r2_get_control_word(self):
+        if self.cmd == 'ADDRESS READ':
+            self.packet_append()
+            self.state = 'R2 GET ACK AFTER ADDR READ'
+        else:
+            self.reset()
+
+    def handle_r2_get_ack_after_addr_read(self):
+        if self.cmd == 'ACK':
+            self.state = 'R2 READ BYTE'
+        else:
+            self.reset()
+
+    def handle_r2_read_byte(self):
+        if self.cmd == 'DATA READ':
+            self.packet_append()
+            self.state = 'R2 GET ACK NACK AFTER BYTE WAS READ'
+        elif self.cmd == 'STOP':
+            self.decide_on_seq_or_rnd_read()
+            self.es_block = self.es
+            self.putb([0, ['Warning: STOP expected after a NACK (not ACK)']])
+            self.put_operation()
+            self.reset()
+        else:
+            self.reset()
+
+    def handle_r2_get_ack_nack_after_byte_was_read(self):
+        if self.cmd == 'ACK':
+            self.state = 'R2 READ BYTE'
+        elif self.cmd == 'NACK':
+            self.decide_on_seq_or_rnd_read()
+            self.state = 'GET STOP AFTER LAST BYTE'
+        else:
+            self.reset()
+
+    def handle_get_stop_after_last_byte(self):
+        if self.cmd == 'STOP':
+            self.es_block = self.es
+            self.put_operation()
+            self.reset()
+        elif self.cmd == 'START REPEAT':
+            self.es_block = self.es
+            self.putb([0, ['Warning: STOP expected (not RESTART)']])
+            self.put_operation()
+            self.reset()
+            self.ss_block = self.ss
+            self.state = 'GET CONTROL WORD'
+        else:
+            self.reset()
+
+    def decode(self, ss, es, data):
+        self.cmd, self.databyte = data
+
+        # Collect the 'BITS' packet, then return. The next packet is
+        # guaranteed to belong to these bits we just stored.
+        if self.cmd == 'BITS':
+            self.bits = self.databyte
+            return
+
+        # Store the start/end samples of this I²C packet.
+        self.ss, self.es = ss, es
+
+        # State machine.
+        s = 'handle_%s' % self.state.lower().replace(' ', '_')
+        handle_state = getattr(self, s)
+        handle_state()
index 9c7e6a47aba7d2912d0c2c7981a85129bc04e8cb..cd8f15e724f5a67bb0099bf41864160d79ad8139 100644 (file)
@@ -34,5 +34,4 @@ recommended to use a logic analyzer samplerate that is much higher than
 the expected bitrate/baudrate that might be used on the channel.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index e86316fb86d7c384c2fb74ec0a1869c1d22aebdc..250d519977430f08721deaa5d4548f29e9aff4f8 100644 (file)
@@ -20,6 +20,9 @@
 
 import sigrokdecode as srd
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'guess_bitrate'
@@ -50,11 +53,11 @@ class Decoder(srd.Decoder):
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
-            self.samplerate = value;
+            self.samplerate = value
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
 
             data = pins[0]
@@ -64,22 +67,21 @@ class Decoder(srd.Decoder):
                 continue
 
             # Initialize first self.olddata with the first sample value.
-            if self.olddata == None:
+            if self.olddata is None:
                 self.olddata = data
                 continue
 
             # Get the smallest distance between two transitions
             # and use that to calculate the bitrate/baudrate.
-            if self.first_transition == True:
+            if self.first_transition:
                 self.ss_edge = self.samplenum
                 self.first_transition = False
             else:
                 b = self.samplenum - self.ss_edge
-                if self.bitwidth == None or b < self.bitwidth:
+                if self.bitwidth is None or b < self.bitwidth:
                     self.bitwidth = b
                     bitrate = int(float(self.samplerate) / float(b))
                     self.putx([0, ['%d' % bitrate]])
                 self.ss_edge = self.samplenum
 
             self.olddata = data
-
index ba40245ebc6aeb3d44d897789e81b4ff4b27f0b2..c3b0cd5095ea9b991171f85cc343f9d5df83019a 100644 (file)
@@ -23,5 +23,4 @@ I²C (Inter-Integrated Circuit) is a bidirectional, multi-master
 bus using two signals (SCL = serial clock line, SDA = serial data line).
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 2d6e5c2986bdf26d1ca54b0e9cc23641dfc58a2b..ce0d699e9511c807bca1556fa2574636abc732ac 100644 (file)
@@ -63,6 +63,9 @@ proto = {
     'DATA WRITE':      [9, 'Data write',    'DW'],
 }
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'i2c'
@@ -107,7 +110,7 @@ class Decoder(srd.Decoder):
 
     def __init__(self, **kwargs):
         self.samplerate = None
-        self.ss = self.es = self.byte_ss = -1
+        self.ss = self.es = self.ss_byte = -1
         self.samplenum = None
         self.bitcount = 0
         self.databyte = 0
@@ -179,7 +182,7 @@ class Decoder(srd.Decoder):
 
         # Remember the start of the first data/address bit.
         if self.bitcount == 0:
-            self.byte_ss = self.samplenum
+            self.ss_byte = self.samplenum
 
         # Store individual bits and their start/end samplenumbers.
         # In the list, index 0 represents the LSB (I²C transmits MSB-first).
@@ -216,7 +219,7 @@ class Decoder(srd.Decoder):
             cmd = 'DATA READ'
             bin_class = 2
 
-        self.ss, self.es = self.byte_ss, self.samplenum + self.bitwidth
+        self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth
 
         self.putp(['BITS', self.bits])
         self.putp([cmd, d])
@@ -230,7 +233,7 @@ class Decoder(srd.Decoder):
             self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
             w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R']
             self.putx([proto[cmd][0], w])
-            self.ss, self.es = self.byte_ss, self.samplenum
+            self.ss, self.es = self.ss_byte, self.samplenum
 
         self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], d),
                    '%s: %02X' % (proto[cmd][2], d), '%02X' % d]])
@@ -253,7 +256,7 @@ class Decoder(srd.Decoder):
         # Meta bitrate
         elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
         bitrate = int(1 / elapsed * self.pdu_bits)
-        self.put(self.byte_ss, self.samplenum, self.out_bitrate, bitrate)
+        self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
 
         cmd = 'STOP'
         self.ss, self.es = self.samplenum, self.samplenum
@@ -265,8 +268,8 @@ class Decoder(srd.Decoder):
         self.bits = []
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
 
             # Ignore identical samples early on (for performance reasons).
@@ -293,9 +296,6 @@ class Decoder(srd.Decoder):
             elif self.state == 'FIND ACK':
                 if self.is_data_bit(scl, sda):
                     self.get_ack(scl, sda)
-            else:
-                raise Exception('Invalid state: %s' % self.state)
 
             # Save current SDA/SCL values for the next round.
             self.oldscl, self.oldsda = scl, sda
-
index c5202c5acc9b7695a5f8f527fd18a3838944da3e..75fcd8607bb69c5a105f9b6cf579392a1e9381bb 100644 (file)
@@ -25,5 +25,4 @@ It takes an I²C stream as input and outputs multiple I²C streams, each
 stream containing only I²C packets for one specific I²C slave.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index e5cf47ac6696576098ae923a42fd9b17deb6bfc0..68b75a0d66fc70c9854cd3ece0520eaac279c075 100644 (file)
@@ -75,4 +75,3 @@ class Decoder(srd.Decoder):
             self.stream = -1
         else:
             pass # Do nothing, only add the I²C packet to our cache.
-
index 6cbab8a1fe3da1354e3b02bf47589e6bdc38f641..be97bf0a0f7e951cf543008046e16dde31a122bd 100644 (file)
@@ -34,5 +34,4 @@ Both of these are optional; if no options are specified the entire payload
 of the I²C session will be output.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index f1ac6a40211c9f4a3e2ababc63737a4b6975e7f6..3c02a2e40ec46aabc115f4b4ed94ac3fc81e01a6 100644 (file)
@@ -39,7 +39,6 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self, **kwargs):
-        self.state = None
         self.curslave = -1
         self.curdirection = None
         self.packets = [] # Local cache of I²C packets
@@ -48,8 +47,6 @@ class Decoder(srd.Decoder):
         self.out_python = self.register(srd.OUTPUT_PYTHON, proto_id='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).')
 
     # Grab I²C packets into a local cache, until an I²C STOP condition
     # packet comes along. At some point before that STOP condition, there
@@ -90,4 +87,3 @@ class Decoder(srd.Decoder):
             self.packets = []
         else:
             pass # Do nothing, only add the I²C packet to our cache.
-
index b9c0ed72fb683e63a13585d97020ecd34694ff35..e114cd0b973458c6dcefd7ab2367fe1331effbe9 100644 (file)
@@ -27,5 +27,4 @@ http://www.nxp.com/acrobat_download/various/I2SBUS.pdf
 http://en.wikipedia.org/wiki/I2s
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 989a5a5eaf9433ec653b60d3bf12c0d4f4e82468..ee642d7dcf8aea3743ad1a4b7cbcd19868656d1a 100644 (file)
@@ -33,6 +33,9 @@ Packet:
 <value>: integer
 '''
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'i2s'
@@ -64,7 +67,7 @@ class Decoder(srd.Decoder):
         self.data = 0
         self.samplesreceived = 0
         self.first_sample = None
-        self.start_sample = None
+        self.ss_block = None
         self.wordlength = -1
         self.wrote_wav_header = False
 
@@ -78,23 +81,23 @@ class Decoder(srd.Decoder):
             self.samplerate = value
 
     def putpb(self, data):
-        self.put(self.start_sample, self.samplenum, self.out_python, data)
+        self.put(self.ss_block, self.samplenum, self.out_python, data)
 
     def putbin(self, data):
-        self.put(self.start_sample, self.samplenum, self.out_bin, data)
+        self.put(self.ss_block, self.samplenum, self.out_bin, data)
 
     def putb(self, data):
-        self.put(self.start_sample, self.samplenum, self.out_ann, data)
+        self.put(self.ss_block, self.samplenum, self.out_ann, data)
 
     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:
+        if self.ss_block is not None and \
+            self.first_sample is not None and \
+            self.ss_block > self.first_sample:
             samplerate = '%d' % (self.samplesreceived *
-                self.samplerate / (self.start_sample -
+                self.samplerate / (self.ss_block -
                 self.first_sample))
 
         return 'I²S: %d %d-bit samples received at %sHz' % \
@@ -128,8 +131,8 @@ class Decoder(srd.Decoder):
         return bytes([lo, hi])
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for self.samplenum, (sck, ws, sd) in data:
 
             # Ignore sample if the bit clock hasn't changed.
@@ -148,7 +151,7 @@ class Decoder(srd.Decoder):
                 continue
 
             # Only submit the sample, if we received the beginning of it.
-            if self.start_sample != None:
+            if self.ss_block is not None:
 
                 if not self.wrote_wav_header:
                     self.put(0, 0, self.out_bin, (0, self.wav_header()))
@@ -176,11 +179,10 @@ class Decoder(srd.Decoder):
             # Reset decoder state.
             self.data = 0
             self.bitcount = 0
-            self.start_sample = self.samplenum
+            self.ss_block = self.samplenum
 
             # Save the first sample position.
-            if self.first_sample == None:
+            if self.first_sample is None:
                 self.first_sample = self.samplenum
 
             self.oldws = ws
-
index 84bf428897a6a191d78e4344fd91020c52ba9bc4..6b15f1944a71eb3fc420566045125d324936c544 100644 (file)
@@ -22,5 +22,4 @@
 NEC is a pulse-distance based infrared remote control protocol.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index c9dc595e6b82928810c7712a3d6495e9d7104da6..3f730d9c61c55c82f4a5397b798f2e0f0fec07d8 100644 (file)
@@ -49,4 +49,3 @@ command = {
         68: ['AV', 'AV'],
     }.items())),
 }
-
index 94e232f0b743b8cef8a21d1307ee084f0e46af16..d14c7d33215b093fe0b74a6a23eb7d7c877d1eb3 100644 (file)
@@ -21,6 +21,9 @@
 import sigrokdecode as srd
 from .lists import *
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'ir_nec'
@@ -148,8 +151,8 @@ class Decoder(srd.Decoder):
         return ret == 0
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
             self.ir = pins[0]
 
@@ -199,8 +202,5 @@ class Decoder(srd.Decoder):
                 self.putremote()
                 self.ss_bit = self.ss_start = self.samplenum
                 self.state = 'IDLE'
-            else:
-                raise Exception('Invalid state: %s' % self.state)
 
             self.old_ir = self.ir
-
index 085082fe894288bac69817ddbe97de12c1f0a75a..8f5f5a3715b6781afd3e8fa4d4dba38c32b65d4c 100644 (file)
@@ -22,5 +22,4 @@
 RC-5 is a biphase/manchester based infrared remote control protocol.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 8a81ef7b73dde8af9dfecd2d6d36c9d23f586488..2ac227eb7941eee0920dc6177185bd2bfdfd6b0d 100644 (file)
@@ -92,4 +92,3 @@ command = {
         55: ['Recording', 'Rec'],
     }.items())),
 }
-
index 38b479327eaab9c2d6f5e4793faf7ba0c71f9833..e1dd42f3c0a0b60f382232146fc6b468bff23201 100644 (file)
@@ -21,6 +21,9 @@
 import sigrokdecode as srd
 from .lists import *
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'ir_rc5'
@@ -56,7 +59,7 @@ class Decoder(srd.Decoder):
     def __init__(self, **kwargs):
         self.samplerate = None
         self.samplenum = None
-        self.edges, self.bits, self.bits_ss_es = [], [], []
+        self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
     def start(self):
@@ -70,7 +73,7 @@ class Decoder(srd.Decoder):
             self.halfbit = int((self.samplerate * 0.00178) / 2.0)
 
     def putb(self, bit1, bit2, data):
-        ss, es = self.bits_ss_es[bit1][0], self.bits_ss_es[bit2][1]
+        ss, es = self.ss_es_bits[bit1][0], self.ss_es_bits[bit2][1]
         self.put(ss, es, self.out_ann, data)
 
     def handle_bits(self):
@@ -80,9 +83,9 @@ class Decoder(srd.Decoder):
             if i == 0:
                 ss = max(0, self.bits[0][0] - self.halfbit)
             else:
-                ss = self.bits_ss_es[i - 1][1]
+                ss = self.ss_es_bits[i - 1][1]
             es = self.bits[i][0] + self.halfbit
-            self.bits_ss_es.append([ss, es])
+            self.ss_es_bits.append([ss, es])
             self.putb(i, i, [0, ['%d' % self.bits[i][1]]])
         # Bits[0:0]: Startbit 1
         s = ['Startbit1: %d' % b[0][1], 'SB1: %d' % b[0][1], 'SB1', 'S1', 'S']
@@ -129,12 +132,12 @@ class Decoder(srd.Decoder):
             return 'e' # Error, invalid edge distance.
 
     def reset_decoder_state(self):
-        self.edges, self.bits, self.bits_ss_es = [], [], []
+        self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
 
             self.ir = pins[0]
@@ -168,11 +171,9 @@ class Decoder(srd.Decoder):
                 if edge == 's':
                     self.state = 'MID0'
                 bit = 0 if edge == 's' else None
-            else:
-                raise Exception('Invalid state: %s' % self.state)
 
             self.edges.append(self.samplenum)
-            if bit != None:
+            if bit is not None:
                 self.bits.append([self.samplenum, bit])
 
             if len(self.bits) == 14:
@@ -180,4 +181,3 @@ class Decoder(srd.Decoder):
                 self.reset_decoder_state()
 
             self.old_ir = self.ir
-
diff --git a/decoders/jitter/__init__.py b/decoders/jitter/__init__.py
new file mode 100644 (file)
index 0000000..e223e73
--- /dev/null
@@ -0,0 +1,29 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.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
+##
+
+'''
+This protocol decoder retrieves the timing jitter between two digital signals.
+
+It allows to define a clock source channel and a resulting signal channel.
+Each time a significant edge is detected in the clock source, we calculate the
+elapsed time before the resulting signal answers and report the timing jitter.
+'''
+
+from .pd import Decoder
diff --git a/decoders/jitter/pd.py b/decoders/jitter/pd.py
new file mode 100644 (file)
index 0000000..d6db8e5
--- /dev/null
@@ -0,0 +1,203 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.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
+##
+
+import sigrokdecode as srd
+
+# Helper dictionary for edge detection.
+edge_detector = {
+    'rising':  lambda x, y: bool(not x and y),
+    'falling': lambda x, y: bool(x and not y),
+    'both':    lambda x, y: bool(x ^ y),
+}
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'jitter'
+    name = 'Jitter'
+    longname = 'Timing jitter calculation'
+    desc = 'Retrieves the timing jitter between two digital signals.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['jitter']
+    channels = (
+        {'id': 'clk', 'name': 'Clock', 'desc': 'Clock reference channel'},
+        {'id': 'sig', 'name': 'Resulting signal', 'desc': 'Resulting signal controlled by the clock'},
+    )
+    options = (
+        {'id': 'clk_polarity', 'desc': 'Clock edge polarity',
+            'default': 'rising', 'values': ('rising', 'falling', 'both')},
+        {'id': 'sig_polarity', 'desc': 'Resulting signal edge polarity',
+            'default': 'rising', 'values': ('rising', 'falling', 'both')},
+    )
+    annotations = (
+        ('jitter', 'Jitter value'),
+        ('clk_missed', 'Clock missed'),
+        ('sig_missed', 'Signal missed'),
+    )
+    annotation_rows = (
+        ('jitter', 'Jitter values', (0,)),
+        ('clk_missed', 'Clock missed', (1,)),
+        ('sig_missed', 'Signal missed', (2,)),
+    )
+    binary = (
+        ('ascii-float', 'Jitter values as newline-separated ASCII floats'),
+    )
+
+    def __init__(self, **kwargs):
+        self.state = 'CLK'
+        self.samplerate = None
+        self.oldpin = None
+        self.oldclk = self.oldsig = None
+        self.clk_start = None
+        self.sig_start = None
+        self.clk_missed = 0
+        self.sig_missed = 0
+
+    def start(self):
+        self.clk_edge = edge_detector[self.options['clk_polarity']]
+        self.sig_edge = edge_detector[self.options['sig_polarity']]
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_bin = self.register(srd.OUTPUT_BINARY)
+        self.out_clk_missed = self.register(srd.OUTPUT_META,
+            meta=(int, 'Clock missed', 'Clock transition missed'))
+        self.out_sig_missed = self.register(srd.OUTPUT_META,
+            meta=(int, 'Signal missed', 'Resulting signal transition missed'))
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    # Helper function for jitter time annotations.
+    def putx(self, delta):
+        # Adjust granularity.
+        if delta == 0 or delta >= 1:
+            delta_s = u"%us" % (delta)
+        elif delta <= 1e-12:
+            delta_s = u"%.1ffs" % (delta * 1e15)
+        elif delta <= 1e-9:
+            delta_s = u"%.1fps" % (delta * 1e12)
+        elif delta <= 1e-6:
+            delta_s = u"%.1fns" % (delta * 1e9)
+        elif delta <= 1e-3:
+            delta_s = u"%.1fμs" % (delta * 1e6)
+        else:
+            delta_s = u"%.1fms" % (delta * 1e3)
+
+        self.put(self.clk_start, self.sig_start, self.out_ann, [0, [delta_s]])
+
+    # Helper function for ASCII float jitter values (one value per line).
+    def putb(self, delta):
+        if delta is None:
+            return
+        # Format the delta to an ASCII float value terminated by a newline.
+        x = str(delta) + '\n'
+        self.put(self.clk_start, self.sig_start, self.out_bin,
+                 (0, x.encode('UTF-8')))
+
+    # Helper function for missed clock and signal annotations.
+    def putm(self, data):
+        self.put(self.samplenum, self.samplenum, self.out_ann, data)
+
+    def handle_clk(self, clk, sig):
+        if self.clk_start == self.samplenum:
+            # Clock transition already treated.
+            # We have done everything we can with this sample.
+            return True
+
+        if self.clk_edge(self.oldclk, clk):
+            # Clock edge found.
+            # We note the sample and move to the next state.
+            self.clk_start = self.samplenum
+            self.state = 'SIG'
+            return False
+        else:
+            if self.sig_start is not None \
+               and self.sig_start != self.samplenum \
+               and self.sig_edge(self.oldsig, sig):
+                # If any transition in the resulting signal
+                # occurs while we are waiting for a clock,
+                # we increase the missed signal counter.
+                self.sig_missed += 1
+                self.put(self.samplenum, self.samplenum, self.out_sig_missed, self.sig_missed)
+                self.putm([2, ['Missed signal', 'MS']])
+            # No clock edge found, we have done everything we
+            # can with this sample.
+            return True
+
+    def handle_sig(self, clk, sig):
+        if self.sig_start == self.samplenum:
+            # Signal transition already treated.
+            # We have done everything we can with this sample.
+            return True
+
+        if self.sig_edge(self.oldsig, sig):
+            # Signal edge found.
+            # We note the sample, calculate the jitter
+            # and move to the next state.
+            self.sig_start = self.samplenum
+            self.state = 'CLK'
+            # Calculate and report the timing jitter.
+            delta = (self.sig_start - self.clk_start) / self.samplerate
+            self.putx(delta)
+            self.putb(delta)
+            return False
+        else:
+            if self.clk_start != self.samplenum \
+               and self.clk_edge(self.oldclk, clk):
+                # If any transition in the clock signal
+                # occurs while we are waiting for a resulting
+                # signal, we increase the missed clock counter.
+                self.clk_missed += 1
+                self.put(self.samplenum, self.samplenum, self.out_clk_missed, self.clk_missed)
+                self.putm([1, ['Missed clock', 'MC']])
+            # No resulting signal edge found, we have done
+            # everything we can with this sample.
+            return True
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        for (self.samplenum, pins) in data:
+            # We are only interested in transitions.
+            if self.oldpin == pins:
+                continue
+
+            self.oldpin, (clk, sig) = pins, pins
+
+            if self.oldclk is None and self.oldsig is None:
+                self.oldclk, self.oldsig = clk, sig
+
+            # State machine:
+            # For each sample we can move 2 steps forward in the state machine.
+            while True:
+                # Clock state has the lead.
+                if self.state == 'CLK':
+                    if self.handle_clk(clk, sig):
+                        break
+                if self.state == 'SIG':
+                    if self.handle_sig(clk, sig):
+                        break
+
+            # Save current CLK/SIG values for the next round.
+            self.oldclk, self.oldsig = clk, sig
index 047e54a86530aa19d369cd8692f37ef71b7f8c3e..863ef09abbfbf44220b30d6d12a0d112c8d00c40 100644 (file)
@@ -28,5 +28,4 @@ https://en.wikipedia.org/wiki/Joint_Test_Action_Group
 http://focus.ti.com/lit/an/ssya002c/ssya002c.pdf
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 25f26342183ba14bebc181fbeffeb85d4616ee58..49077ed942eb08d68bbd938cd4eec450155a3cef 100644 (file)
@@ -139,14 +139,11 @@ class Decoder(srd.Decoder):
         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)
 
-        if self.first == True:
+        if self.first:
             # Save the start sample and item for later (no output yet).
             self.ss_item = self.samplenum
             self.first = False
@@ -217,4 +214,3 @@ class Decoder(srd.Decoder):
                 self.handle_rising_tck_edge(tdi, tdo, tck, tms)
 
             self.oldtck = tck
-
index 8a28528380afecdf53bc122da1a36c9a2d542db7..9d60c1cbc7be2e1c67b2f3c4be5d7e5a10476d7e 100644 (file)
@@ -27,5 +27,4 @@ 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 .pd import *
-
+from .pd import Decoder
index 5aac07f7a4e3dbba2be6e548a506864833d77da2..f2dd3c7726dfce97e6a5040204be57afac2b8547 100644 (file)
@@ -218,6 +218,3 @@ class Decoder(srd.Decoder):
             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 d0964184bfd87dc31499d60d5715c247ca397f19..1026df1b030cac953ffd38d15887ce245e981676 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'i2c' PD and decodes the National LM75
 (and compatibles) temperature sensor protocol.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 89d6c61150c396530ca5fe75f9786f1db1c1a545..6b2bfa8105f32bc18f5752be2cb31addc33c4f38 100644 (file)
@@ -76,7 +76,7 @@ class Decoder(srd.Decoder):
 
     def putb(self, data):
         # Helper for annotations which span a block of I²C packets.
-        self.put(self.block_start, self.block_end, self.out_ann, data)
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
 
     def warn_upon_invalid_slave(self, addr):
         # LM75 and compatible devices have a 7-bit I²C slave address where
@@ -102,11 +102,11 @@ class Decoder(srd.Decoder):
     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.ss_block = self.ss
             self.databytes.append(b)
             return
         self.databytes.append(b)
-        self.block_end = self.es
+        self.es_block = self.es
         self.output_temperature(s, rw)
         self.databytes = []
 
@@ -181,6 +181,3 @@ class Decoder(srd.Decoder):
             else:
                 # self.putx([0, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
                 pass
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
index 62a43075a2a524f6c481ac1c11310736264e597b..2c2c430c66ac042af46287def3c57781fc1a0903 100644 (file)
@@ -23,5 +23,4 @@ LPC (Low-Pin Count) is a protocol for low-bandwidth devices used on
 some PC mainboards, such as the "BIOS chip" or the so-called "Super I/O".
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 6e0396696d6cf905e595953ce86840e8e44f3629..5e25db4779ba886070fecd752ecf848926eec966 100644 (file)
@@ -346,7 +346,7 @@ class Decoder(srd.Decoder):
             if self.state == 'IDLE':
                 # A valid LPC cycle starts with LFRAME# being asserted (low).
                 if lframe != 0:
-                   continue
+                    continue
                 self.ss_block = self.samplenum
                 self.state = 'GET START'
                 self.lad = -1
@@ -365,6 +365,3 @@ class Decoder(srd.Decoder):
                 self.handle_get_data(lad, lad_bits)
             elif self.state == 'GET TAR2':
                 self.handle_get_tar2(lad, lad_bits)
-            else:
-                raise Exception('Invalid state: %s' % self.state)
-
index 34d1d088e0ce70cad81f53bd7ef4cc87784afbc1..049f96ab1cb5b18004c14a446ef916fde0d1c527 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'onewire_network' PD and decodes the
 Maxim DS28EA00 1-Wire digital thermometer protocol.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 4693cd9aa2f7b3aa047362729fc513b05b1aa9e6..229331fd393d0e917ab5bc0624df7218f9d6a3ce 100644 (file)
@@ -88,6 +88,3 @@ class Decoder(srd.Decoder):
                 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/mdio/__init__.py b/decoders/mdio/__init__.py
new file mode 100644 (file)
index 0000000..d9028a3
--- /dev/null
@@ -0,0 +1,29 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+The MDIO (Management Data Input/Output) protocol decoder supports the
+MII Management serial bus, with a clock line (MDC) and a bi-directional
+data line (MDIO).
+
+MDIO is also known as SMI (Serial Management Interface).
+'''
+
+from .pd import Decoder
diff --git a/decoders/mdio/pd.py b/decoders/mdio/pd.py
new file mode 100644 (file)
index 0000000..bb1f53f
--- /dev/null
@@ -0,0 +1,260 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'mdio'
+    name = 'MDIO'
+    longname = 'Management Data Input/Output'
+    desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['mdio']
+    channels = (
+        {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
+        {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
+    )
+    annotations = (
+        ('mdio-data', 'MDIO data'),
+        ('mdio-bits', 'MDIO bits'),
+        ('errors', 'Human-readable errors'),
+    )
+    annotation_rows = (
+        ('mdio-data', 'MDIO data', (0,)),
+        ('mdio-bits', 'MDIO bits', (1,)),
+        ('other', 'Other', (2,)),
+    )
+
+    def __init__(self):
+        self.oldmdc = 0
+        self.ss_block = -1
+        self.samplenum = -1
+        self.oldpins = None
+        self.reset_decoder_state()
+
+    def start(self):
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putw(self, data):
+        self.put(self.ss_block, self.samplenum, self.out_ann, data)
+
+    def putbit(self, mdio, start, stop):
+        # Bit annotations.
+        self.put(start, stop, self.out_ann, [1, ['%d' % mdio]])
+
+    def putdata(self):
+        # FIXME: Only pass data, no bits.
+        # Pass MDIO bits and then data to the next PD up the stack.
+        ss, es = self.mdiobits[-1][1], self.mdiobits[0][2]
+
+        # self.put(ss, es, self.out_python, ['BITS', self.mdiobits])
+        self.put(ss, es, self.out_python, ['DATA', self.mdiodata])
+
+        # Bit annotations.
+        for bit in self.mdiobits:
+            self.put(bit[1], bit[2], self.out_ann, [1, ['%d' % bit[0]]])
+
+        # Error annotation if an error happened.
+        if self.error:
+            self.put(self.ss_bit, self.es_error, self.out_ann, [2, [self.error]])
+            return
+
+        op = 'READ' if self.operation else 'WRITE'
+
+        # Dataword annotations.
+        if self.ss_preamble != -1:
+            self.put(self.ss_preamble, self.ss_start, self.out_ann, [0, ['PREAMBLE']])
+        self.put(self.ss_start, self.ss_operation, self.out_ann, [0, ['START']])
+        self.put(self.ss_operation, self.ss_phy, self.out_ann, [0, [op]])
+        self.put(self.ss_phy, self.ss_reg, self.out_ann, [0, ['PHY: %d' % self.phy]])
+        self.put(self.ss_reg, self.ss_turnaround, self.out_ann, [0, ['REG: %d' % self.reg]])
+        self.put(self.ss_turnaround, self.ss_data, self.out_ann, [0, ['TURNAROUND']])
+        self.put(self.ss_data, self.es_data, self.out_ann, [0, ['DATA: %04X' % self.data]])
+
+    def reset_decoder_state(self):
+        self.mdiodata = 0
+        self.mdiobits = []
+        self.bitcount = 0
+        self.ss_preamble = -1
+        self.ss_start = -1
+        self.ss_operation = -1
+        self.ss_phy = -1
+        self.ss_reg = -1
+        self.ss_turnaround = -1
+        self.ss_data = -1
+        self.phy = 0
+        self.phy_bits = 0
+        self.reg = 0
+        self.reg_bits = 0
+        self.data = 0
+        self.data_bits = 0
+        self.state = 'PREAMBLE'
+        self.error = None
+
+    def parse_preamble(self, mdio):
+        if self.ss_preamble == -1:
+            self.ss_preamble = self.samplenum
+        if mdio != 1:
+            self.error = 'Invalid preamble: could not find 32 consecutive bits set to 1'
+            self.state = 'ERROR'
+        elif self.bitcount == 31:
+            self.state = 'START'
+
+    def parse_start(self, mdio):
+        if self.ss_start == -1:
+            if mdio != 0:
+                self.error = 'Invalid start bits: should be 01'
+                self.state = 'ERROR'
+            else:
+                self.ss_start = self.samplenum
+        else:
+            if mdio != 1:
+                self.error = 'Invalid start bits: should be 01'
+                self.state = 'ERROR'
+            else:
+                self.state = 'OPERATION'
+
+    def parse_operation(self, mdio):
+        if self.ss_operation == -1:
+            self.ss_operation = self.samplenum
+            self.operation = mdio
+        else:
+            if mdio == self.operation:
+                self.error = 'Invalid operation bits'
+                self.state = 'ERROR'
+            else:
+                self.state = 'PHY'
+
+    def parse_phy(self, mdio):
+        if self.ss_phy == -1:
+            self.ss_phy = self.samplenum
+        self.phy_bits += 1
+        self.phy |= mdio << (5 - self.phy_bits)
+        if self.phy_bits == 5:
+            self.state = 'REG'
+
+    def parse_reg(self, mdio):
+        if self.ss_reg == -1:
+            self.ss_reg = self.samplenum
+        self.reg_bits += 1
+        self.reg |= mdio << (5 - self.reg_bits)
+        if self.reg_bits == 5:
+            self.state = 'TURNAROUND'
+
+    def parse_turnaround(self, mdio):
+        if self.ss_turnaround == -1:
+            if self.operation == 0 and mdio != 1:
+                self.error = 'Invalid turnaround bits'
+                self.state = 'ERROR'
+            else:
+                self.ss_turnaround = self.samplenum
+        else:
+            if mdio != 0:
+                self.error = 'Invalid turnaround bits'
+                self.state = 'ERROR'
+            else:
+                self.state = 'DATA'
+
+    def parse_data(self, mdio):
+        if self.ss_data == -1:
+            self.ss_data = self.samplenum
+        self.data_bits += 1
+        self.data |= mdio << (16 - self.data_bits)
+        if self.data_bits == 16:
+            self.es_data = self.samplenum + int((self.samplenum - self.ss_data) / 15)
+            self.state = 'DONE'
+
+    def parse_error(self, mdio):
+        if self.bitcount == 63:
+            self.es_error = self.samplenum + int((self.samplenum - self.ss_bit) / 63)
+            self.state = 'DONE'
+
+    def handle_bit(self, mdio):
+        # If this is the first bit of a command, save its sample number.
+        if self.bitcount == 0:
+            self.ss_bit = self.samplenum
+            # No preamble?
+            if mdio == 0:
+                self.state = 'START'
+
+        # Guesstimate the endsample for this bit (can be overridden below).
+        es = self.samplenum
+        if self.bitcount > 0:
+            es += self.samplenum - self.mdiobits[0][1]
+
+        self.mdiobits.insert(0, [mdio, self.samplenum, es])
+
+        if self.bitcount > 0:
+            self.bitsamples = (self.samplenum - self.ss_bit) / self.bitcount
+            self.mdiobits[1][2] = self.samplenum
+
+        if self.state == 'PREAMBLE':
+            self.parse_preamble(mdio)
+        elif self.state == 'START':
+            self.parse_start(mdio)
+        elif self.state == 'OPERATION':
+            self.parse_operation(mdio)
+        elif self.state == 'PHY':
+            self.parse_phy(mdio)
+        elif self.state == 'REG':
+            self.parse_reg(mdio)
+        elif self.state == 'TURNAROUND':
+            self.parse_turnaround(mdio)
+        elif self.state == 'DATA':
+            self.parse_data(mdio)
+        elif self.state == 'ERROR':
+            self.parse_error(mdio)
+
+        self.bitcount += 1
+        if self.state == 'DONE':
+            self.putdata()
+            self.reset_decoder_state()
+
+    def find_mdc_edge(self, mdc, mdio):
+        # Output the current error annotation if the clock stopped running
+        if self.state == 'ERROR' and self.samplenum - self.clocksample > (1.5 * self.bitsamples):
+            self.es_error = self.clocksample + int((self.clocksample - self.ss_bit) / self.bitcount)
+            self.putdata()
+            self.reset_decoder_state()
+
+        # Ignore sample if the clock pin hasn't changed.
+        if mdc == self.oldmdc:
+            return
+
+        self.oldmdc = mdc
+
+        if mdc == 0:   # Sample on rising clock edge.
+            return
+
+        # Found the correct clock edge, now get/handle the bit(s).
+        self.clocksample = self.samplenum
+        self.handle_bit(mdio)
+
+    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, (mdc, mdio) = pins, pins
+
+            self.find_mdc_edge(mdc, mdio)
index a453346db4700eb3edd27604161e912b2f5518a8..4dbcf96d8f23915a97d170b2acabf6acf47d92a3 100644 (file)
@@ -26,5 +26,4 @@ MIDI is layered on top of the UART (async serial) protocol, with a fixed
 baud rate of 31250 baud (+/- 1%) and 8n1 settings. Bytes are sent LSB-first.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index bc0f9b8ab5c59582ea335eb4c071501dbc9ee83b..c72f5c9c1627f9883da844eb986c55ae60ca2925 100644 (file)
@@ -267,7 +267,7 @@ sysex_manufacturer_ids = {
     (0x00, 0x00, 0x5c): 'AT&T Bell Labs',
     (0x00, 0x00, 0x5e): 'Symetrix',
     (0x00, 0x00, 0x5f): 'MIDI the World',
-    
+
     (0x00, 0x00, 0x60): 'Desper Products',
     (0x00, 0x00, 0x61): 'Micros\'N MIDI',
     (0x00, 0x00, 0x62): 'Accordians Intl',
@@ -467,4 +467,3 @@ control_functions = {
     0x7e: 'poly mode off', # mono mode on, all notes off
     0x7f: 'poly mode on', # mono mode off, all notes off
 }
-
index b1f0053c912b86c44a374381ac183dedae82751c..5915976c164535d42c10d29b920f9a81795777a3 100644 (file)
@@ -70,7 +70,7 @@ class Decoder(srd.Decoder):
         c = self.cmd
         if len(c) < 3:
             return
-        self.es_block = self.ss
+        self.es_block = self.es
         msg, chan, note, velocity = c[0] & 0xf0, (c[0] & 0x0f) + 1, c[1], c[2]
         s = 'note off' if (velocity == 0) else status_bytes[msg]
         self.putx([0, ['Channel %d: %s (note = %d, velocity = %d)' % \
@@ -162,7 +162,7 @@ class Decoder(srd.Decoder):
 
     def handle_sysrealtime_msg(self, newbyte):
         # System realtime message: 0b11111ttt (t = message type)
-        self.es_block = self.ss
+        self.es_block = self.es
         self.putx([0, ['System realtime message: %s' % status_bytes[newbyte]]])
         self.cmd, self.state = [], 'IDLE'
 
@@ -175,6 +175,9 @@ class Decoder(srd.Decoder):
 
         self.ss, self.es = ss, es
 
+        # We're only interested in the byte value (not individual bits).
+        pdata = pdata[0]
+
         # Short MIDI overview:
         #  - Status bytes are 0x80-0xff, data bytes are 0x00-0x7f.
         #  - Most messages: 1 status byte, 1-2 data bytes.
@@ -206,6 +209,3 @@ class Decoder(srd.Decoder):
             self.handle_syscommon_msg(pdata)
         elif self.state == 'HANDLE SYSREALTIME MSG':
             self.handle_sysrealtime_msg(pdata)
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
index 29cb2a93bcb487c82fbdad90769633c1f62ff190..e985c9107a610d7dbe469209fba0e4d8887a2444 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'i2c' PD and decodes the Melexis MLX90614
 infrared thermometer protocol.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index edc770d4ccc4d96f63cc20c8e3e68639c5cf157d..aa1ead548fe0b4a9878a1a4b83f027f20311b634 100644 (file)
@@ -73,6 +73,3 @@ class Decoder(srd.Decoder):
                 self.putx([1, ['Temperature: %3.2f K' % kelvin]])
                 self.state = 'IGNORE START REPEAT'
                 self.data = []
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/mrf24j40/__init__.py b/decoders/mrf24j40/__init__.py
new file mode 100644 (file)
index 0000000..f0820bd
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Karl Palsson <karlp@tweak.net.au>
+##
+## 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
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes Microchip MRF24J40
+IEEE 802.15.4 2.4 GHz RF tranceiver commands and data.
+'''
+
+from .pd import Decoder
diff --git a/decoders/mrf24j40/lists.py b/decoders/mrf24j40/lists.py
new file mode 100644 (file)
index 0000000..c81975b
--- /dev/null
@@ -0,0 +1,166 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Karl Palsson <karlp@tweak.net.au>
+##
+## 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
+##
+
+sregs = {
+    0: 'RXMCR',
+    1: 'PANIDL',
+    2: 'PANIDH',
+    3: 'SADRL',
+    4: 'SADRH',
+    5: 'EADR0',
+    6: 'EADR1',
+    7: 'EADR2',
+    8: 'EADR3',
+    9: 'EADR4',
+    0xa: 'EADR5',
+    0xb: 'EADR6',
+    0xc: 'EADR7',
+    0xd: 'RXFLUSH',
+    0xe: 'Reserved',
+    0xf: 'Reserved',
+    0x10: 'ORDER',
+    0x11: 'TXMCR',
+    0x12: 'ACKTMOUT',
+    0x13: 'ESLOTG1',
+    0x14: 'SYMTICKL',
+    0x15: 'SYMTICKH',
+    0x16: 'PACON0',
+    0x17: 'PACON1',
+    0x18: 'PACON2',
+    0x19: 'Reserved',
+    0x1a: 'TXBCON0',
+    0x1b: 'TXNCON',
+    0x1c: 'TXG1CON',
+    0x1d: 'TXG2CON',
+    0x1e: 'ESLOTG23',
+    0x1f: 'ESLOTG45',
+    0x20: 'ESLOTG67',
+    0x21: 'TXPEND',
+    0x22: 'WAKECON',
+    0x23: 'FRMOFFSET',
+    0x24: 'TXSTAT',
+    0x25: 'TXBCON1',
+    0x26: 'GATECLK',
+    0x27: 'TXTIME',
+    0x28: 'HSYMTIMRL',
+    0x29: 'HSYMTIMRH',
+    0x2a: 'SOFTRST',
+    0x2b: 'Reserved',
+    0x2c: 'SECCON0',
+    0x2d: 'SECCON1',
+    0x2e: 'TXSTBL',
+    0x3f: 'Reserved',
+    0x30: 'RXSR',
+    0x31: 'INTSTAT',
+    0x32: 'INTCON',
+    0x33: 'GPIO',
+    0x34: 'TRISGPIO',
+    0x35: 'SLPACK',
+    0x36: 'RFCTL',
+    0x37: 'SECCR2',
+    0x38: 'BBREG0',
+    0x39: 'BBREG1',
+    0x3a: 'BBREG2',
+    0x3b: 'BBREG3',
+    0x3c: 'BBREG4',
+    0x3d: 'Reserved',
+    0x3e: 'BBREG6',
+    0x3f: 'CCAEDTH',
+}
+
+lregs = {
+    0x200: 'RFCON0',
+    0x201: 'RFCON1',
+    0x202: 'RFCON2',
+    0x203: 'RFCON3',
+    0x204: 'Reserved',
+    0x205: 'RFCON5',
+    0x206: 'RFCON6',
+    0x207: 'RFCON7',
+    0x208: 'RFCON8',
+    0x209: 'SLPCAL0',
+    0x20A: 'SLPCAL1',
+    0x20B: 'SLPCAL2',
+    0x20C: 'Reserved',
+    0x20D: 'Reserved',
+    0x20E: 'Reserved',
+    0x20F: 'RFSTATE',
+    0x210: 'RSSI',
+    0x211: 'SLPCON0',
+    0x212: 'Reserved',
+    0x213: 'Reserved',
+    0x214: 'Reserved',
+    0x215: 'Reserved',
+    0x216: 'Reserved',
+    0x217: 'Reserved',
+    0x218: 'Reserved',
+    0x219: 'Reserved',
+    0x21A: 'Reserved',
+    0x21B: 'Reserved',
+    0x21C: 'Reserved',
+    0x21D: 'Reserved',
+    0x21E: 'Reserved',
+    0x21F: 'Reserved',
+    0x220: 'SLPCON1',
+    0x221: 'Reserved',
+    0x222: 'WAKETIMEL',
+    0x223: 'WAKETIMEH',
+    0x224: 'REMCNTL',
+    0x225: 'REMCNTH',
+    0x226: 'MAINCNT0',
+    0x227: 'MAINCNT1',
+    0x228: 'MAINCNT2',
+    0x229: 'MAINCNT3',
+    0x22A: 'Reserved',
+    0x22B: 'Reserved',
+    0x22C: 'Reserved',
+    0x22D: 'Reserved',
+    0x22E: 'Reserved',
+    0x22F: 'TESTMODE',
+    0x230: 'ASSOEADR0',
+    0x231: 'ASSOEADR1',
+    0x232: 'ASSOEADR2',
+    0x233: 'ASSOEADR3',
+    0x234: 'ASSOEADR4',
+    0x235: 'ASSOEADR5',
+    0x236: 'ASSOEADR6',
+    0x237: 'ASSOEADR7',
+    0x238: 'ASSOSADR0',
+    0x239: 'ASSOSADR1',
+    0x23A: 'Reserved',
+    0x23B: 'Reserved',
+    0x23C: 'Unimplemented',
+    0x23D: 'Unimplemented',
+    0x23E: 'Unimplemented',
+    0x23F: 'Unimplemented',
+    0x240: 'UPNONCE0',
+    0x241: 'UPNONCE1',
+    0x242: 'UPNONCE2',
+    0x243: 'UPNONCE3',
+    0x244: 'UPNONCE4',
+    0x245: 'UPNONCE5',
+    0x246: 'UPNONCE6',
+    0x247: 'UPNONCE7',
+    0x248: 'UPNONCE8',
+    0x249: 'UPNONCE9',
+    0x24A: 'UPNONCE10',
+    0x24B: 'UPNONCE11',
+    0x24C: 'UPNONCE12'
+}
diff --git a/decoders/mrf24j40/pd.py b/decoders/mrf24j40/pd.py
new file mode 100644 (file)
index 0000000..286fa52
--- /dev/null
@@ -0,0 +1,134 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Karl Palsson <karlp@tweak.net.au>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+from .lists import *
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'mrf24j40'
+    name = 'MRF24J40'
+    longname = 'Microchip MRF24J40'
+    desc = 'IEEE 802.15.4 2.4 GHz RF tranceiver chip.'
+    license = 'gplv2'
+    inputs = ['spi']
+    outputs = ['mrf24j40']
+    annotations = (
+        ('sread', 'Short register read commands'),
+        ('swrite', 'Short register write commands'),
+        ('lread', 'Long register read commands'),
+        ('lwrite', 'Long register write commands'),
+        ('warning', 'Warnings'),
+    )
+    annotation_rows = (
+        ('read', 'Read', (0, 2)),
+        ('write', 'Write', (1, 3)),
+        ('warnings', 'Warnings', (4,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.ss_cmd, self.es_cmd = 0, 0
+        self.mosi_bytes = []
+        self.miso_bytes = []
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+
+    def putw(self, pos, msg):
+        self.put(pos[0], pos[1], self.out_ann, [4, [msg]])
+
+    def reset(self):
+        self.mosi_bytes = []
+        self.miso_bytes = []
+
+    def handle_short(self):
+        write = self.mosi_bytes[0] & 0x1
+        reg = (self.mosi_bytes[0] >> 1) & 0x3f
+        reg_desc = sregs.get(reg, 'illegal')
+        if write:
+            self.putx([1, ['%s: %#x' % (reg_desc, self.mosi_bytes[1])]])
+        else:
+            self.putx([0, ['%s: %#x' % (reg_desc, self.miso_bytes[1])]])
+
+    def handle_long(self):
+        dword = self.mosi_bytes[0] << 8 | self.mosi_bytes[1]
+        write = dword & (0x1 << 4)
+        reg = dword >> 5 & 0x3ff
+        if reg >= 0x0:
+            reg_desc = 'TX:%#x' % reg
+        if reg >= 0x80:
+            reg_desc = 'TX beacon:%#x' % reg
+        if reg >= 0x100:
+            reg_desc = 'TX GTS1:%#x' % reg
+        if reg >= 0x180:
+            reg_desc = 'TX GTS2:%#x' % reg
+        if reg >= 0x200:
+            reg_desc = lregs.get(reg, 'illegal')
+        if reg >= 0x280:
+            reg_desc = 'Security keys:%#x' % reg
+        if reg >= 0x2c0:
+            reg_desc = 'Reserved:%#x' % reg
+        if reg >= 0x300:
+            reg_desc = 'RX:%#x' % reg
+
+        if write:
+            self.putx([3, ['%s: %#x' % (reg_desc, self.mosi_bytes[2])]])
+        else:
+            self.putx([2, ['%s: %#x' % (reg_desc, self.miso_bytes[2])]])
+
+    def decode(self, ss, es, data):
+        ptype = data[0]
+        if ptype == 'CS-CHANGE':
+            # If we transition high mid-stream, toss out our data and restart.
+            cs_old, cs_new = data[1:]
+            if cs_old is not None and cs_old == 0 and cs_new == 1:
+                if len(self.mosi_bytes) not in (0, 2, 3):
+                    self.putw([self.ss_cmd, es], 'Misplaced CS!')
+                    self.reset()
+            return
+
+        # Don't care about anything else.
+        if ptype != 'DATA':
+            return
+        mosi, miso = data[1:]
+
+        self.ss, self.es = ss, es
+
+        if len(self.mosi_bytes) == 0:
+            self.ss_cmd = ss
+        self.mosi_bytes.append(mosi)
+        self.miso_bytes.append(miso)
+
+        # Everything is either 2 bytes or 3 bytes.
+        if len(self.mosi_bytes) < 2:
+            return
+
+        if self.mosi_bytes[0] & 0x80:
+            if len(self.mosi_bytes) == 3:
+                self.es_cmd = es
+                self.handle_long()
+                self.reset()
+        else:
+            self.es_cmd = es
+            self.handle_short()
+            self.reset()
diff --git a/decoders/mx25lxx05d/__init__.py b/decoders/mx25lxx05d/__init__.py
deleted file mode 100644 (file)
index 71d7b3c..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-'''
-This decoder stacks on top of the 'spi' PD and decodes the Macronix
-MX25Lxx05D SPI (NOR) flash chip protocol.
-
-It works for the MX25L1605D/MX25L3205D/MX25L6405D.
-
-Details:
-http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/h_Index/3F21BAC2E121E17848257639003A3146/$File/MX25L1605D-3205D-6405D-1.5.pdf
-'''
-
-from .pd import *
-
diff --git a/decoders/mx25lxx05d/pd.py b/decoders/mx25lxx05d/pd.py
deleted file mode 100644 (file)
index a6c72d8..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-##
-
-import sigrokdecode as srd
-
-# 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 cmd_annotation_classes():
-    return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
-
-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 = 2
-    id = 'mx25lxx05d'
-    name = 'MX25Lxx05D'
-    longname = 'Macronix MX25Lxx05D'
-    desc = 'SPI (NOR) flash chip protocol.'
-    license = 'gplv2+'
-    inputs = ['logic']
-    outputs = ['mx25lxx05d']
-    annotations = cmd_annotation_classes() + (
-        ('bits', 'Bits'),
-        ('bits2', 'Bits2'),
-        ('warnings', 'Warnings'),
-    )
-    annotation_rows = (
-        ('bits', 'Bits', (24, 25)),
-        ('commands', 'Commands', tuple(range(23 + 1))),
-        ('warnings', 'Warnings', (26,)),
-    )
-
-    def __init__(self, **kwargs):
-        self.state = None
-        self.cmdstate = 1
-        self.addr = 0
-        self.data = []
-
-    def start(self):
-        self.out_ann = self.register(srd.OUTPUT_ANN)
-
-    def putx(self, data):
-        # Simplification, most annotations span exactly one SPI byte/packet.
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def 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([2, ['Command: %s' % cmds[self.state][1]]])
-        elif self.cmdstate == 2:
-            # Byte 2: Slave sends the JEDEC manufacturer ID.
-            self.putx([2, ['Manufacturer ID: 0x%02x' % miso]])
-        elif self.cmdstate == 3:
-            # Byte 3: Slave sends the memory type (0x20 for this chip).
-            self.putx([2, ['Memory type: 0x%02x' % miso]])
-        elif self.cmdstate == 4:
-            # Byte 4: Slave sends the device ID.
-            self.device_id = miso
-            self.putx([2, ['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([3, ['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([24, ['Status register: 0x%02x' % miso]])
-                self.putx([25, [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([5, ['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([24, ['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([24, ['Read data']])
-                self.putx([25, ['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([8, ['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, [24, [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, [101, [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([12, ['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([24, ['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([24, ['Page data']])
-                self.putx([25, ['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([16, ['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([24, ['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([24, ['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([24, ['%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([24, ['%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([24, ['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([24, ['Unknown command: 0x%02x' % mosi]])
-            self.state = None
-
index c4209c86dab61d5219e0fe34b49c4e43dd0433ce..daeb89eb824bd59b3adece65ea5304ce5941fb6b 100644 (file)
@@ -26,5 +26,4 @@ The chip's I²C interface supports standard mode and fast mode (max. 400kHz).
 Its I²C slave address is 0x2a.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index e0c857f30f62870772a225fc4098483321210f6b..962f963fa63e137cb976872978b5f51017f58d41 100644 (file)
@@ -97,7 +97,7 @@ class Decoder(srd.Decoder):
         # 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)
+        ann = 'INT = %d: Orientation %s shake event occurred\n' % (int_val, s)
 
         # Bits[6:5]: SH[1:0]
         sh = (((b >> 6) & 1) << 1) | ((b >> 5) & 1)
@@ -165,7 +165,6 @@ class Decoder(srd.Decoder):
             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(?)
@@ -213,6 +212,3 @@ class Decoder(srd.Decoder):
                 self.state = 'IDLE'
             else:
                 pass # TODO?
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/nrf24l01/__init__.py b/decoders/nrf24l01/__init__.py
new file mode 100644 (file)
index 0000000..7b4d748
--- /dev/null
@@ -0,0 +1,30 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Jens Steinhauser <jens.steinhauser@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
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
+by the Nordic Semiconductor nRF24L01 and nRF24L01+ 2.4GHz transceiver chips.
+
+Details:
+http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01
+http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01P
+'''
+
+from .pd import Decoder
diff --git a/decoders/nrf24l01/pd.py b/decoders/nrf24l01/pd.py
new file mode 100644 (file)
index 0000000..2337d4b
--- /dev/null
@@ -0,0 +1,329 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Jens Steinhauser <jens.steinhauser@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
+##
+
+import sigrokdecode as srd
+
+class ChannelError(Exception):
+    pass
+
+regs = {
+#   addr: ('name',        size)
+    0x00: ('CONFIG',      1),
+    0x01: ('EN_AA',       1),
+    0x02: ('EN_RXADDR',   1),
+    0x03: ('SETUP_AW',    1),
+    0x04: ('SETUP_RETR',  1),
+    0x05: ('RF_CH',       1),
+    0x06: ('RF_SETUP',    1),
+    0x07: ('STATUS',      1),
+    0x08: ('OBSERVE_TX',  1),
+    0x09: ('RPD',         1),
+    0x0a: ('RX_ADDR_P0',  5),
+    0x0b: ('RX_ADDR_P1',  5),
+    0x0c: ('RX_ADDR_P2',  1),
+    0x0d: ('RX_ADDR_P3',  1),
+    0x0e: ('RX_ADDR_P4',  1),
+    0x0f: ('RX_ADDR_P5',  1),
+    0x10: ('TX_ADDR',     5),
+    0x11: ('RX_PW_P0',    1),
+    0x12: ('RX_PW_P1',    1),
+    0x13: ('RX_PW_P2',    1),
+    0x14: ('RX_PW_P3',    1),
+    0x15: ('RX_PW_P4',    1),
+    0x16: ('RX_PW_P5',    1),
+    0x17: ('FIFO_STATUS', 1),
+    0x1c: ('DYNPD',       1),
+    0x1d: ('FEATURE',     1),
+}
+
+xn297_regs = {
+    0x19: ('DEMOD_CAL',   5),
+    0x1e: ('RF_CAL',      7),
+    0x1f: ('BB_CAL',      5),
+}
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'nrf24l01'
+    name = 'nRF24L01(+)'
+    longname = 'Nordic Semiconductor nRF24L01/nRF24L01+'
+    desc = '2.4GHz transceiver chip.'
+    license = 'gplv2+'
+    inputs = ['spi']
+    outputs = ['nrf24l01']
+    options = (
+        {'id': 'chip', 'desc': 'Chip type',
+            'default': 'nrf24l01', 'values': ('nrf24l01', 'xn297')},
+    )
+    annotations = (
+        # Sent from the host to the chip.
+        ('cmd', 'Commands sent to the device'),
+        ('tx-data', 'Payload sent to the device'),
+
+        # Returned by the chip.
+        ('register', 'Registers read from the device'),
+        ('rx-data', 'Payload read from the device'),
+
+        ('warning', 'Warnings'),
+    )
+    ann_cmd = 0
+    ann_tx = 1
+    ann_reg = 2
+    ann_rx = 3
+    ann_warn = 4
+    annotation_rows = (
+        ('commands', 'Commands', (ann_cmd, ann_tx)),
+        ('responses', 'Responses', (ann_reg, ann_rx)),
+        ('warnings', 'Warnings', (ann_warn,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.next()
+        self.requirements_met = True
+        self.cs_was_released = False
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        if self.options['chip'] == 'xn297':
+            regs.update(xn297_regs)
+
+    def warn(self, pos, msg):
+        '''Put a warning message 'msg' at 'pos'.'''
+        self.put(pos[0], pos[1], self.out_ann, [self.ann_warn, [msg]])
+
+    def putp(self, pos, ann, msg):
+        '''Put an annotation message 'msg' at 'pos'.'''
+        self.put(pos[0], pos[1], self.out_ann, [ann, [msg]])
+
+    def next(self):
+        '''Resets the decoder after a complete command was decoded.'''
+        # 'True' for the first byte after CS went low.
+        self.first = True
+
+        # The current command, and the minimum and maximum number
+        # of data bytes to follow.
+        self.cmd = None
+        self.min = 0
+        self.max = 0
+
+        # Used to collect the bytes after the command byte
+        # (and the start/end sample number).
+        self.mb = []
+        self.mb_s = -1
+        self.mb_e = -1
+
+    def mosi_bytes(self):
+        '''Returns the collected MOSI bytes of a multi byte command.'''
+        return [b[0] for b in self.mb]
+
+    def miso_bytes(self):
+        '''Returns the collected MISO bytes of a multi byte command.'''
+        return [b[1] for b in self.mb]
+
+    def decode_command(self, pos, b):
+        '''Decodes the command byte 'b' at position 'pos' and prepares
+        the decoding of the following data bytes.'''
+        c = self.parse_command(b)
+        if c is None:
+            self.warn(pos, 'unknown command')
+            return
+
+        self.cmd, self.dat, self.min, self.max = c
+
+        if self.cmd in ('W_REGISTER', 'ACTIVATE'):
+            # Don't output anything now, the command is merged with
+            # the data bytes following it.
+            self.mb_s = pos[0]
+        else:
+            self.putp(pos, self.ann_cmd, self.format_command())
+
+    def format_command(self):
+        '''Returns the label for the current command.'''
+        if self.cmd == 'R_REGISTER':
+            reg = regs[self.dat][0] if self.dat in regs else 'unknown register'
+            return 'Cmd R_REGISTER "{}"'.format(reg)
+        else:
+            return 'Cmd {}'.format(self.cmd)
+
+    def parse_command(self, b):
+        '''Parses the command byte.
+
+        Returns a tuple consisting of:
+        - the name of the command
+        - additional data needed to dissect the following bytes
+        - minimum number of following bytes
+        - maximum number of following bytes
+        '''
+
+        if (b & 0xe0) in (0b00000000, 0b00100000):
+            c = 'R_REGISTER' if not (b & 0xe0) else 'W_REGISTER'
+            d = b & 0x1f
+            m = regs[d][1] if d in regs else 1
+            return (c, d, 1, m)
+        if b == 0b01010000:
+            # nRF24L01 only
+            return ('ACTIVATE', None, 1, 1)
+        if b == 0b01100001:
+            return ('R_RX_PAYLOAD', None, 1, 32)
+        if b == 0b01100000:
+            return ('R_RX_PL_WID', None, 1, 1)
+        if b == 0b10100000:
+            return ('W_TX_PAYLOAD', None, 1, 32)
+        if b == 0b10110000:
+            return ('W_TX_PAYLOAD_NOACK', None, 1, 32)
+        if (b & 0xf8) == 0b10101000:
+            return ('W_ACK_PAYLOAD', b & 0x07, 1, 32)
+        if b == 0b11100001:
+            return ('FLUSH_TX', None, 0, 0)
+        if b == 0b11100010:
+            return ('FLUSH_RX', None, 0, 0)
+        if b == 0b11100011:
+            return ('REUSE_TX_PL', None, 0, 0)
+        if b == 0b11111111:
+            return ('NOP', None, 0, 0)
+
+    def decode_register(self, pos, ann, regid, data):
+        '''Decodes a register.
+
+        pos   -- start and end sample numbers of the register
+        ann   -- is the annotation number that is used to output the register.
+        regid -- may be either an integer used as a key for the 'regs'
+                 dictionary, or a string directly containing a register name.'
+        data  -- is the register content.
+        '''
+
+        if type(regid) == int:
+            # Get the name of the register.
+            if regid not in regs:
+                self.warn(pos, 'unknown register')
+                return
+            name = regs[regid][0]
+        else:
+            name = regid
+
+        # Multi byte register come LSByte first.
+        data = reversed(data)
+
+        if self.cmd == 'W_REGISTER' and ann == self.ann_cmd:
+            # The 'W_REGISTER' command is merged with the following byte(s).
+            label = '{}: {}'.format(self.format_command(), name)
+        else:
+            label = 'Reg {}'.format(name)
+
+        self.decode_mb_data(pos, ann, data, label, True)
+
+    def decode_mb_data(self, pos, ann, data, label, always_hex):
+        '''Decodes the data bytes 'data' of a multibyte command at position
+        'pos'. The decoded data is prefixed with 'label'. If 'always_hex' is
+        True, all bytes are decoded as hex codes, otherwise only non
+        printable characters are escaped.'''
+
+        if always_hex:
+            def escape(b):
+                return '{:02X}'.format(b)
+        else:
+            def escape(b):
+                c = chr(b)
+                if not str.isprintable(c):
+                    return '\\x{:02X}'.format(b)
+                return c
+
+        data = ''.join([escape(b) for b in data])
+        text = '{} = "{}"'.format(label, data)
+        self.putp(pos, ann, text)
+
+    def finish_command(self, pos):
+        '''Decodes the remaining data bytes at position 'pos'.'''
+
+        if self.cmd == 'R_REGISTER':
+            self.decode_register(pos, self.ann_reg,
+                                 self.dat, self.miso_bytes())
+        elif self.cmd == 'W_REGISTER':
+            self.decode_register(pos, self.ann_cmd,
+                                 self.dat, self.mosi_bytes())
+        elif self.cmd == 'R_RX_PAYLOAD':
+            self.decode_mb_data(pos, self.ann_rx,
+                                self.miso_bytes(), 'RX payload', False)
+        elif (self.cmd == 'W_TX_PAYLOAD' or
+              self.cmd == 'W_TX_PAYLOAD_NOACK'):
+            self.decode_mb_data(pos, self.ann_tx,
+                                self.mosi_bytes(), 'TX payload', False)
+        elif self.cmd == 'W_ACK_PAYLOAD':
+            lbl = 'ACK payload for pipe {}'.format(self.dat)
+            self.decode_mb_data(pos, self.ann_tx,
+                                self.mosi_bytes(), lbl, False)
+        elif self.cmd == 'R_RX_PL_WID':
+            msg = 'Payload width = {}'.format(self.mb[0][1])
+            self.putp(pos, self.ann_reg, msg)
+        elif self.cmd == 'ACTIVATE':
+            self.putp(pos, self.ann_cmd, self.format_command())
+            if self.mosi_bytes()[0] != 0x73:
+                self.warn(pos, 'wrong data for "ACTIVATE" command')
+
+    def decode(self, ss, es, data):
+        if not self.requirements_met:
+            return
+
+        ptype, data1, data2 = data
+
+        if ptype == 'CS-CHANGE':
+            if data1 is None:
+                if data2 is None:
+                    self.requirements_met = False
+                    raise ChannelError('CS# pin required.')
+                elif data2 == 1:
+                    self.cs_was_released = True
+
+            if data1 == 0 and data2 == 1:
+                # Rising edge, the complete command is transmitted, process
+                # the bytes that were send after the command byte.
+                if self.cmd:
+                    # Check if we got the minimum number of data bytes
+                    # after the command byte.
+                    if len(self.mb) < self.min:
+                        self.warn((ss, ss), 'missing data bytes')
+                    elif self.mb:
+                        self.finish_command((self.mb_s, self.mb_e))
+
+                self.next()
+                self.cs_was_released = True
+        elif ptype == 'DATA' and self.cs_was_released:
+            mosi, miso = data1, data2
+            pos = (ss, es)
+
+            if miso is None or mosi is None:
+                self.requirements_met = False
+                raise ChannelError('Both MISO and MOSI pins required.')
+
+            if self.first:
+                self.first = False
+                # First MOSI byte is always the command.
+                self.decode_command(pos, mosi)
+                # First MISO byte is always the status register.
+                self.decode_register(pos, self.ann_reg, 'STATUS', [miso])
+            else:
+                if not self.cmd or len(self.mb) >= self.max:
+                    self.warn(pos, 'excess byte')
+                else:
+                    # Collect the bytes after the command byte.
+                    if self.mb_s == -1:
+                        self.mb_s = ss
+                    self.mb_e = es
+                    self.mb.append((mosi, miso))
index 2fdaaeb430c7c1697353846ca5d69af7e49c4e7a..c5791455d8df127c25635c15bf40d1f44c8d192e 100644 (file)
@@ -28,5 +28,4 @@ http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/
 https://www.sparkfun.com/products/9281
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 95d7bdbd690153cdca538f7943a8dbc954033bbb..afcf1de2d417936b2cd9ea5becb84c7c15940b74 100644 (file)
@@ -52,7 +52,7 @@ class Decoder(srd.Decoder):
         self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1
         self.databytecount = 0
         self.reg = 0x00
-        self.ss = self.es = self.block_ss = self.block_es = 0
+        self.ss = self.es = self.ss_block = self.es_block = 0
         self.init_seq = []
 
     def start(self):
@@ -62,13 +62,13 @@ class Decoder(srd.Decoder):
         self.put(self.ss, self.es, self.out_ann, data)
 
     def putb(self, data):
-        self.put(self.block_ss, self.block_es, self.out_ann, data)
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
 
     def putd(self, bit1, bit2, data):
         self.put(self.bits[bit1][1], self.bits[bit2][2], self.out_ann, data)
 
     def handle_reg_0x00(self, databyte):
-        self.block_ss = self.ss
+        self.ss_block = self.ss
         self.sx = databyte
         self.putx([0, ['Analog stick X position: 0x%02X' % self.sx,
                        'SX: 0x%02X' % self.sx]])
@@ -94,7 +94,7 @@ class Decoder(srd.Decoder):
                        'AZ[9:2]: 0x%03X' % self.az]])
 
     def handle_reg_0x05(self, databyte):
-        self.block_es = self.es
+        self.es_block = self.es
         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]
@@ -170,7 +170,7 @@ class Decoder(srd.Decoder):
             if cmd != 'START':
                 return
             self.state = 'GET SLAVE ADDR'
-            self.block_start_sample = ss
+            self.ss_block = ss
         elif self.state == 'GET SLAVE ADDR':
             # Wait for an address read/write operation.
             if cmd == 'ADDRESS READ':
@@ -183,7 +183,7 @@ class Decoder(srd.Decoder):
                 handle_reg(databyte)
                 self.reg += 1
             elif cmd == 'STOP':
-                self.block_end_sample = es
+                self.es_block = es
                 self.output_full_block_if_possible()
                 self.sx = self.sy = self.ax = self.ay = self.az = -1
                 self.bz = self.bc = -1
@@ -195,13 +195,10 @@ class Decoder(srd.Decoder):
             if cmd == 'DATA WRITE':
                 self.handle_reg_write(databyte)
             elif cmd == 'STOP':
-                self.block_end_sample = es
+                self.es_block = es
                 self.output_init_seq()
                 self.init_seq = []
                 self.state = 'IDLE'
             else:
                 # self.putx([14, ['Ignoring: %s (data=%s)' % (cmd, databyte)]])
                 pass
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
index d153f93ad46f27a73c93b11581ac3c726fb9f960..12aad259290b23db0b283c05fa4f852344dc53d0 100644 (file)
@@ -65,5 +65,4 @@ These options should be configured only on very rare cases and the user should
 read the decoder source code to understand them correctly.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 3641b9849bde1eb9c904be0f9d675e3caaa35bb7..2be02426aa593ad835e049413e09030abf485e1d 100644 (file)
@@ -20,6 +20,9 @@
 
 import sigrokdecode as srd
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'onewire_link'
@@ -106,11 +109,7 @@ class Decoder(srd.Decoder):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
-    def metadata(self, key, value):
-        if key != srd.SRD_CONF_SAMPLERATE:
-            return
-        self.samplerate = value
-
+    def checks(self):
         # Check if samplerate is appropriate.
         if self.options['overdrive'] == 'yes':
             if self.samplerate < 2000000:
@@ -123,37 +122,10 @@ class Decoder(srd.Decoder):
             if self.samplerate < 400000:
                 self.putm([1, ['Sampling rate is too low. Must be above ' +
                                '400kHz for proper normal mode decoding.']])
-            elif (self.samplerate < 1000000):
+            elif self.samplerate < 1000000:
                 self.putm([1, ['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)
-
-        x = float(self.options['cnt_normal_bit']) / 1000000.0
-        self.cnt_normal_bit = int(samplerate * x) - 1
-        x = float(self.options['cnt_normal_slot']) / 1000000.0
-        self.cnt_normal_slot = int(samplerate * x) - 1
-        x = float(self.options['cnt_normal_presence']) / 1000000.0
-        self.cnt_normal_presence = int(samplerate * x) - 1
-        x = float(self.options['cnt_normal_reset']) / 1000000.0
-        self.cnt_normal_reset = int(samplerate * x) - 1
-        x = float(self.options['cnt_overdrive_bit']) / 1000000.0
-        self.cnt_overdrive_bit = int(samplerate * x) - 1
-        x = float(self.options['cnt_overdrive_slot']) / 1000000.0
-        self.cnt_overdrive_slot = int(samplerate * x) - 1
-        x = float(self.options['cnt_overdrive_presence']) / 1000000.0
-        self.cnt_overdrive_presence = int(samplerate * x) - 1
-        x = float(self.options['cnt_overdrive_reset']) / 1000000.0
-        self.cnt_overdrive_reset = int(samplerate * x) - 1
-
-        # 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
@@ -182,12 +154,47 @@ class Decoder(srd.Decoder):
         if (time_min < 0.0000073) or (time_max > 0.000010):
             self.putm([1, ['The overdrive mode presence sample time interval ' +
                  '(%2.1fus-%2.1fus) should be inside (7.3us, 10.0us).'
-                 % (time_min*1000000, time_max*1000000)]])
+                 % (time_min * 1000000, time_max * 1000000)]])
+
+
+    def metadata(self, key, value):
+        if key != srd.SRD_CONF_SAMPLERATE:
+            return
+        self.samplerate = value
+
+        # The default 1-Wire time base is 30us. This is used to calculate
+        # sampling times.
+        samplerate = float(self.samplerate)
+
+        x = float(self.options['cnt_normal_bit']) / 1000000.0
+        self.cnt_normal_bit = int(samplerate * x) - 1
+        x = float(self.options['cnt_normal_slot']) / 1000000.0
+        self.cnt_normal_slot = int(samplerate * x) - 1
+        x = float(self.options['cnt_normal_presence']) / 1000000.0
+        self.cnt_normal_presence = int(samplerate * x) - 1
+        x = float(self.options['cnt_normal_reset']) / 1000000.0
+        self.cnt_normal_reset = int(samplerate * x) - 1
+        x = float(self.options['cnt_overdrive_bit']) / 1000000.0
+        self.cnt_overdrive_bit = int(samplerate * x) - 1
+        x = float(self.options['cnt_overdrive_slot']) / 1000000.0
+        self.cnt_overdrive_slot = int(samplerate * x) - 1
+        x = float(self.options['cnt_overdrive_presence']) / 1000000.0
+        self.cnt_overdrive_presence = int(samplerate * x) - 1
+        x = float(self.options['cnt_overdrive_reset']) / 1000000.0
+        self.cnt_overdrive_reset = int(samplerate * x) - 1
+
+        # 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]
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, (owr, pwr)) in data:
+            if self.samplenum == 0:
+                self.checks()
             # State machine.
             if self.state == 'WAIT FOR FALLING EDGE':
                 # The start of a cycle is a falling edge.
@@ -251,10 +258,10 @@ class Decoder(srd.Decoder):
                     # Save the sample number for the rising edge.
                     self.rise = self.samplenum
                     self.putfr([2, ['Reset', 'Rst', 'R']])
-                    self.state = "WAIT FOR PRESENCE DETECT"
+                    self.state = 'WAIT FOR PRESENCE DETECT'
                 # Otherwise this is assumed to be a data bit.
                 else:
-                    self.state = "WAIT FOR FALLING EDGE"
+                    self.state = 'WAIT FOR FALLING EDGE'
             elif self.state == 'WAIT FOR PRESENCE DETECT':
                 # Sample presence status.
                 t = self.samplenum - self.rise
@@ -278,5 +285,3 @@ class Decoder(srd.Decoder):
 
                 # Wait for next slot.
                 self.state = 'WAIT FOR FALLING EDGE'
-            else:
-                raise Exception('Invalid state: %s' % self.state)
index 31e9134fe7d9c11e6adf8ea0e3a11d6b5e14ce95..44b9bcc700a1d3e57e8f55177b5e93968bd9e42a 100644 (file)
@@ -54,5 +54,4 @@ TODO:
  - Add reporting original/complement address values from the search algorithm.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 23402a63ebae8dc86147b0f9a458cfebc8439478..bddc4a8434246d12cbef28623c19ab191be57350 100644 (file)
@@ -46,8 +46,8 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self, **kwargs):
-        self.beg = 0
-        self.end = 0
+        self.ss_block = 0
+        self.es_block = 0
         self.state = 'COMMAND'
         self.bit_cnt = 0
         self.search = 'P'
@@ -62,11 +62,11 @@ class Decoder(srd.Decoder):
 
     def putx(self, data):
         # Helper function for most annotations.
-        self.put(self.beg, self.end, self.out_ann, data)
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
 
     def puty(self, data):
         # Helper function for most protocol packets.
-        self.put(self.beg, self.end, self.out_python, data)
+        self.put(self.ss_block, self.es_block, self.out_python, data)
 
     def decode(self, ss, es, data):
         code, val = data
@@ -126,20 +126,18 @@ class Decoder(srd.Decoder):
             if self.onewire_collect(8, val, ss, es) == 0:
                 return
             self.putx([0, ['ROM error data: 0x%02x' % self.data]])
-        else:
-            raise Exception('Invalid state: %s' % self.state)
 
     # Data collector.
     def onewire_collect(self, length, val, ss, es):
         # Storing the sample this sequence begins with.
         if self.bit_cnt == 1:
-            self.beg = ss
+            self.ss_block = ss
         self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt)
         self.bit_cnt += 1
         # Storing the sample this sequence ends with.
         # In case the full length of the sequence is received, return 1.
         if self.bit_cnt == length:
-            self.end = es
+            self.es_block = es
             self.data = self.data & ((1 << length) - 1)
             self.bit_cnt = 0
             return 1
@@ -150,7 +148,7 @@ class Decoder(srd.Decoder):
     def onewire_search(self, length, val, ss, es):
         # Storing the sample this sequence begins with.
         if (self.bit_cnt == 0) and (self.search == 'P'):
-            self.beg = ss
+            self.ss_block = ss
 
         if self.search == 'P':
             # Master receives an original address bit.
@@ -171,7 +169,7 @@ class Decoder(srd.Decoder):
         # Storing the sample this sequence ends with.
         # In case the full length of the sequence is received, return 1.
         if self.bit_cnt == length:
-            self.end = es
+            self.es_block = es
             self.data_p = self.data_p & ((1 << length) - 1)
             self.data_n = self.data_n & ((1 << length) - 1)
             self.data = self.data & ((1 << length) - 1)
index 9bda5232ba227f6b42d80607cdc1429e292b4d73..c14236df4215cd9539f973fc10d034b2d8178c43 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'uart' PD and decodes the Panasonic PAN1321
 Bluetooth module Serial Port Profile (SPP) protocol.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index d173e1c71de5e52b0bb2d4e903e85c894e9c62ef..b70defc4f852b70b9831ef38529c38488bbb8fa8 100644 (file)
@@ -136,6 +136,9 @@ class Decoder(srd.Decoder):
         if ptype != 'DATA':
             return
 
+        # We're only interested in the byte value (not individual bits).
+        pdata = pdata[0]
+
         # If this is the start of a command/reply, remember the start sample.
         if self.cmd[rxtx] == '':
             self.ss_block = ss
@@ -154,8 +157,5 @@ class Decoder(srd.Decoder):
             self.handle_device_reply(rxtx, self.cmd[rxtx][:-2])
         elif rxtx == TX:
             self.handle_host_command(rxtx, self.cmd[rxtx][:-2])
-        else:
-            raise Exception('Invalid rxtx value: %d' % rxtx)
 
         self.cmd[rxtx] = ''
-
index cc1f3d1322a755e20fab2da9d4d1450fb502eeab..a7077bb93363fbe286ad2c661d11a3cc5037b113 100644 (file)
@@ -32,5 +32,4 @@ should be used. Using combinations like D7/D12/D3/D15 is not supported.
 For an 8-bit bus you should use D0-D7, for a 16-bit bus use D0-D15 and so on.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 03b5e2fb03da6dec2ecb366f9fa06c179337d061..cafaefc071b4904718c84ab5e589bb695d005ea5 100644 (file)
@@ -61,6 +61,9 @@ def channel_list(num_channels):
         l.append(d)
     return tuple(l)
 
+class ChannelError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'parallel'
@@ -92,7 +95,6 @@ class Decoder(srd.Decoder):
         self.oldpins = None
         self.ss_item = self.es_item = None
         self.first = True
-        self.state = 'IDLE'
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
@@ -123,7 +125,7 @@ class Decoder(srd.Decoder):
         self.items.append(item)
         self.itemcount += 1
 
-        if self.first == True:
+        if self.first:
             # Save the start sample and item for later (no output yet).
             self.ss_item = self.samplenum
             self.first = False
@@ -181,12 +183,10 @@ class Decoder(srd.Decoder):
                 continue
             self.oldpins = pins
 
-            # State machine.
-            if self.state == 'IDLE':
-                if pins[0] not in (0, 1):
-                    self.handle_bits(pins[1:])
-                else:
-                    self.find_clk_edge(pins[0], pins[1:])
-            else:
-                raise Exception('Invalid state: %s' % self.state)
+            if sum(1 for p in pins if p in (0, 1)) == 0:
+                raise ChannelError('At least one channel has to be supplied.')
 
+            if pins[0] not in (0, 1):
+                self.handle_bits(pins[1:])
+            else:
+                self.find_clk_edge(pins[0], pins[1:])
diff --git a/decoders/pwm/__init__.py b/decoders/pwm/__init__.py
new file mode 100644 (file)
index 0000000..096e077
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Torsten Duwe <duwe@suse.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
+##
+
+'''
+Pulse-width modulation (a.k.a pulse-duration modulation, PDM) decoder.
+'''
+
+from .pd import Decoder
diff --git a/decoders/pwm/pd.py b/decoders/pwm/pd.py
new file mode 100644 (file)
index 0000000..45e96e2
--- /dev/null
@@ -0,0 +1,150 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Torsten Duwe <duwe@suse.de>
+## Copyright (C) 2014 Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.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
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'pwm'
+    name = 'PWM'
+    longname = 'Pulse-width modulation'
+    desc = 'Analog level encoded in duty cycle percentage.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['pwm']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    options = (
+        {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-high',
+            'values': ('active-low', 'active-high')},
+    )
+    annotations = (
+        ('duty-cycle', 'Duty cycle'),
+        ('period', 'Period'),
+    )
+    annotation_rows = (
+         ('duty-cycle', 'Duty cycle', (0,)),
+         ('period', 'Period', (1,)),
+    )
+    binary = (
+        ('raw', 'RAW file'),
+    )
+
+    def __init__(self, **kwargs):
+        self.ss = self.es = None
+        self.first_transition = True
+        self.first_samplenum = None
+        self.start_samplenum = None
+        self.end_samplenum = None
+        self.oldpin = None
+        self.num_cycles = 0
+        self.average = 0
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.startedge = 0 if self.options['polarity'] == 'active-low' else 1
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_bin = self.register(srd.OUTPUT_BINARY)
+        self.out_average = \
+            self.register(srd.OUTPUT_META,
+                          meta=(float, 'Average', 'PWM base (cycle) frequency'))
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def putp(self, period_t):
+        # Adjust granularity.
+        if period_t == 0 or period_t >= 1:
+            period_s = u'%u s' % (period_t)
+        elif period_t <= 1e-12:
+            period_s = u'%.1f fs' % (period_t * 1e15)
+        elif period_t <= 1e-9:
+            period_s = u'%.1f ps' % (period_t * 1e12)
+        elif period_t <= 1e-6:
+            period_s = u'%.1f ns' % (period_t * 1e9)
+        elif period_t <= 1e-3:
+            period_s = u'%.1f μs' % (period_t * 1e6)
+        else:
+            period_s = u'%.1f ms' % (period_t * 1e3)
+
+        self.put(self.ss, self.es, self.out_ann, [1, [period_s]])
+
+    def putb(self, data):
+        self.put(self.num_cycles, self.num_cycles, self.out_bin, data)
+
+    def decode(self, ss, es, data):
+
+        for (self.samplenum, pins) in data:
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpin == pins[0]:
+                continue
+
+            # Initialize self.oldpins with the first sample value.
+            if self.oldpin is None:
+                self.oldpin = pins[0]
+                continue
+
+            if self.first_transition:
+                # First rising edge
+                if self.oldpin != self.startedge:
+                    self.first_samplenum = self.samplenum
+                    self.start_samplenum = self.samplenum
+                    self.first_transition = False
+            else:
+                if self.oldpin != self.startedge:
+                    # Rising edge
+                    # We are on a full cycle we can calculate
+                    # the period, the duty cycle and its ratio.
+                    period = self.samplenum - self.start_samplenum
+                    duty = self.end_samplenum - self.start_samplenum
+                    ratio = float(duty / period)
+
+                    # This interval starts at this edge.
+                    self.ss = self.start_samplenum
+                    # Store the new rising edge position and the ending
+                    # edge interval.
+                    self.start_samplenum = self.es = self.samplenum
+
+                    # Report the duty cycle in percent.
+                    percent = float(ratio * 100)
+                    self.putx([0, ['%f%%' % percent]])
+
+                    # Report the duty cycle in the binary output.
+                    self.putb((0, bytes([int(ratio * 256)])))
+
+                    # Report the period in units of time.
+                    period_t = float(period / self.samplerate)
+                    self.putp(period_t)
+
+                    # Update and report the new duty cycle average.
+                    self.num_cycles += 1
+                    self.average += percent
+                    self.put(self.first_samplenum, self.es, self.out_average,
+                             float(self.average / self.num_cycles))
+                else:
+                    # Falling edge
+                    self.end_samplenum = self.ss = self.samplenum
+
+            self.oldpin = pins[0]
diff --git a/decoders/qi/__init__.py b/decoders/qi/__init__.py
new file mode 100644 (file)
index 0000000..35ffe5b
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Josef Gajdusek <atx@atx.name>
+##
+## 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
+##
+
+'''
+This decoder decodes demodulated data streams used by the Qi standard
+for communication from the receiver to the charging station.
+'''
+
+from .pd import Decoder
diff --git a/decoders/qi/pd.py b/decoders/qi/pd.py
new file mode 100644 (file)
index 0000000..12155c9
--- /dev/null
@@ -0,0 +1,244 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Josef Gajdusek <atx@atx.name>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+import operator
+import collections
+from functools import reduce
+
+end_codes = (
+    'Unknown',
+    'Charge Complete',
+    'Internal Fault',
+    'Over Temperature',
+    'Over Voltage',
+    'Over Current',
+    'Battery Failure',
+    'Reconfigure',
+    'No Response',
+)
+
+class SamplerateError(Exception):
+    pass
+
+def calc_checksum(packet):
+    return reduce(operator.xor, packet[:-1])
+
+def bits_to_uint(bits):
+    # LSB first
+    return reduce(lambda i, v: (i >> 1) | (v << (len(bits) - 1)), bits, 0)
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'qi'
+    name = 'Qi'
+    longname = 'Qi charger protocol'
+    desc = 'Async serial protocol for Qi charger receivers.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['qi']
+    channels = (
+        {'id': 'qi', 'name': 'Qi', 'desc': 'Demodulated Qi data line'},
+    )
+    annotations = (
+        ('bits', 'Bits'),
+        ('bytes-errors', 'Bit errors'),
+        ('bytes-start', 'Start bits'),
+        ('bytes-info', 'Info bits'),
+        ('bytes-data', 'Data bytes'),
+        ('packets-data', 'Packet data'),
+        ('packets-checksum-ok', 'Packet checksum'),
+        ('packets-checksum-err', 'Packet checksum'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0,)),
+        ('bytes', 'Bytes', (1, 2, 3, 4)),
+        ('packets', 'Packets', (5, 6, 7)),
+    )
+
+    def __init__(self, **kwargs):
+        self.samplerate = None
+        self.reset_variables()
+
+    def reset_variables(self):
+        self.counter = 0
+        self.prev = None
+        self.state = 'IDLE'
+        self.lastbit = 0
+        self.bytestart = 0
+        self.deq = collections.deque(maxlen = 2)
+        self.bits = []
+        self.bitsi = [0]
+        self.bytesi = []
+        self.packet = []
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+            self.bit_width = float(self.samplerate) / 2e3
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.reset_variables()
+
+    def packet_len(self, byte):
+        if 0x00 <= byte <= 0x1f:
+            return int(1 + (byte - 0) / 32)
+        if 0x20 <= byte <= 0x7f:
+            return int(2 + (byte - 32) / 16)
+        if 0x80 <= byte <= 0xdf:
+            return int(8 + (byte - 128) / 8)
+        if 0xe0 <= byte <= 0xff:
+            return int(20 + (byte - 224) / 4)
+
+    def in_tolerance(self, l):
+        return (0.75 * self.bit_width) < l < (1.25 * self.bit_width)
+
+    def putp(self, data):
+        self.put(self.bytesi[0], self.bytesi[-1], self.out_ann, [5, data])
+
+    def process_packet(self):
+        if self.packet[0] == 0x01: # Signal Strength
+            self.putp(['Signal Strength: %d' % self.packet[1],
+                       'SS: %d' % self.packet[1], 'SS'])
+        elif self.packet[0] == 0x02: # End Power Transfer
+            reason = end_codes[self.packet[1]] if self.packet[1] < len(end_codes) else 'Reserved'
+            self.putp(['End Power Transfer: %s' % reason,
+                       'EPT: %s' % reason, 'EPT'])
+        elif self.packet[0] == 0x03: # Control Error
+            val = self.packet[1] if self.packet[1] < 128 else (self.packet[1] & 0x7f) - 128
+            self.putp(['Control Error: %d' % val, 'CE: %d' % val, 'CE'])
+        elif self.packet[0] == 0x04: # Received Power
+            self.putp(['Received Power: %d' % self.packet[1],
+                       'RP: %d' % self.packet[1], 'RP'])
+        elif self.packet[0] == 0x05: # Charge Status
+            self.putp(['Charge Status: %d' % self.packet[1],
+                       'CS: %d' % self.packet[1], 'CS'])
+        elif self.packet[0] == 0x06: # Power Control Hold-off
+            self.putp(['Power Control Hold-off: %dms' % self.packet[1],
+                       'PCH: %d' % self.packet[1]], 'PCH')
+        elif self.packet[0] == 0x51: # Configuration
+            powerclass = (self.packet[1] & 0xc0) >> 7
+            maxpower = self.packet[1] & 0x3f
+            prop = (self.packet[3] & 0x80) >> 7
+            count = self.packet[3] & 0x07
+            winsize = (self.packet[4] & 0xf8) >> 3
+            winoff = self.packet[4] & 0x07
+            self.putp(['Configuration: Power Class = %d, Maximum Power = %d, Prop = %d,'
+                       'Count = %d, Window Size = %d, Window Offset = %d' %
+                       (powerclass, maxpower, prop, count, winsize, winoff),
+                       'C: PC = %d MP = %d P = %d C = %d WS = %d WO = %d' %
+                       (powerclass, maxpower, prop, count, winsize, winoff),
+                       'Configuration', 'C'])
+        elif self.packet[0] == 0x71: # Identification 
+            version = '%d.%d' % ((self.packet[1] & 0xf0) >> 4, self.packet[1] & 0x0f)
+            mancode = '%02x%02x' % (self.packet[2], self.packet[3])
+            devid = '%02x%02x%02x%02x' % (self.packet[4] & ~0x80,
+                    self.packet[5], self.packet[6], self.packet[7])
+            self.putp(['Identification: Version = %s, Manufacturer = %s, ' \
+                       'Device = %s' % (version, mancode, devid),
+                       'ID: %s %s %s' % (version, mancode, devid), 'ID'])
+        elif self.packet[0] == 0x81: # Extended Identification
+            edevid = '%02x%02x%02x%02x%02x%02x%02x%02x' % self.packet[1:-1]
+            self.putp(['Extended Identification: %s' % edevid,
+                       'EI: %s' % edevid, 'EI'])
+        elif self.packet[0] in (0x18, 0x19, 0x28, 0x29, 0x38, 0x48, 0x58, 0x68,
+                0x78, 0x85, 0xa4, 0xc4, 0xe2): # Proprietary
+            self.putp(['Proprietary', 'P'])
+        else: # Unknown
+            self.putp(['Unknown', '?'])
+        self.put(self.bytesi[-1], self.samplenum, self.out_ann,
+                 [6, ['Checksum OK', 'OK']] if \
+                 calc_checksum(self.packet) == self.packet[-1]
+                 else [6, ['Checksum error', 'ERR']])
+
+    def process_byte(self):
+        self.put(self.bytestart, self.bitsi[0], self.out_ann,
+                 ([2, ['Start bit', 'Start', 'S']]) if self.bits[0] == 0 else
+                 ([1, ['Start error', 'Start err', 'SE']]))
+        databits = self.bits[1:9]
+        data = bits_to_uint(databits)
+        parity = reduce(lambda i, v: (i + v) % 2, databits, 1)
+        self.put(self.bitsi[0], self.bitsi[8], self.out_ann, [4, ['%02x' % data]])
+        self.put(self.bitsi[8], self.bitsi[9], self.out_ann,
+                 ([3, ['Parity bit', 'Parity', 'P']]) if self.bits[9] == parity else
+                 ([1, ['Parity error', 'Parity err', 'PE']]))
+        self.put(self.bitsi[9], self.bitsi[10], self.out_ann,
+                 ([3, ['Stop bit', 'Stop', 'S']]) if self.bits[10] == 1 else
+                 ([1, ['Stop error', 'Stop err', 'SE']]))
+
+        self.bytesi.append(self.bytestart)
+        self.packet.append(data)
+        if self.packet_len(self.packet[0]) + 2 == len(self.packet):
+            self.process_packet()
+            self.bytesi.clear()
+            self.packet.clear()
+
+    def add_bit(self, bit):
+        self.bits.append(bit)
+        self.bitsi.append(self.samplenum)
+
+        if self.state == 'IDLE' and len(self.bits) >= 5 and \
+                                    self.bits[-5:] == [1, 1, 1, 1, 0]:
+            self.state = 'DATA'
+            self.bytestart = self.bitsi[-2]
+            self.bits = [0]
+            self.bitsi = [self.samplenum]
+            self.packet.clear()
+        elif self.state == 'DATA' and len(self.bits) == 11:
+            self.process_byte()
+            self.bytestart = self.samplenum
+            self.bits.clear()
+            self.bitsi.clear()
+        if self.state != 'IDLE':
+            self.put(self.lastbit, self.samplenum, self.out_ann, [0, ['%d' % bit]])
+        self.lastbit = self.samplenum
+
+    def handle_transition(self, l, htl):
+        self.deq.append(l)
+        if len(self.deq) >= 2 and \
+                (self.in_tolerance(self.deq[-1] + self.deq[-2]) or \
+                htl and self.in_tolerance(l * 2) and \
+                self.deq[-2] > 1.25 * self.bit_width):
+            self.add_bit(1)
+            self.deq.clear()
+        elif self.in_tolerance(l):
+            self.add_bit(0)
+            self.deq.clear()
+        elif l > (1.25 * self.bit_width):
+            self.state = 'IDLE'
+            self.bytesi.clear()
+            self.packet.clear()
+            self.bits.clear()
+            self.bitsi.clear()
+
+    def next_sample(self, s):
+        if s == self.prev:
+            self.counter += 1
+        else:
+            self.handle_transition(self.counter, s == 0)
+            self.prev = s
+            self.counter = 1
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        for (self.samplenum, (qi,)) in data:
+            self.next_sample(qi)
diff --git a/decoders/rfm12/__init__.py b/decoders/rfm12/__init__.py
new file mode 100644 (file)
index 0000000..725d443
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Sławek Piotrowski <sentinel@atteo.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the HopeRF RFM12
+wireless transceiver control protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/rfm12/pd.py b/decoders/rfm12/pd.py
new file mode 100644 (file)
index 0000000..3065383
--- /dev/null
@@ -0,0 +1,494 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Sławek Piotrowski <sentinel@atteo.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'rfm12'
+    name = 'RFM12'
+    longname = 'RFM12 control protocol'
+    desc = 'HopeRF RFM12 wireless transceiver control protocol.'
+    license = 'gplv2+'
+    inputs = ['spi']
+    outputs = ['rfm12']
+    annotations = (
+        ('cmd', 'Command'),
+        ('params', 'Command parameters'),
+        ('disabled', 'Disabled bits'),
+        ('return', 'Returned values'),
+        ('disabled_return', 'Disabled returned values'),
+        ('interpretation', 'Interpretation'),
+    )
+    annotation_rows = (
+        ('commands', 'Commands', (0, 1, 2)),
+        ('return', 'Return', (3, 4)),
+        ('interpretation', 'Interpretation', (5,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.mosi_bytes, self.miso_bytes = [], []
+        self.mosi_bits, self.miso_bits = [], []
+        self.row_pos = [0, 0, 0]
+
+        self.ann_to_row = [0, 0, 0, 1, 1, 2]
+
+        # Initialize with Power-On-Reset values.
+        self.last_status = [0x00, 0x00]
+        self.last_config = 0x08
+        self.last_power = 0x08
+        self.last_freq = 0x680
+        self.last_data_rate = 0x23
+        self.last_fifo_and_reset = 0x80
+        self.last_afc = 0xF7
+        self.last_transceiver = 0x00
+        self.last_pll = 0x77
+
+    def advance_ann(self, ann, length):
+        row = self.ann_to_row[ann]
+        self.row_pos[row] += length
+
+    def putx(self, ann, length, description):
+        if not isinstance(description, list):
+            description = [description]
+        row = self.ann_to_row[ann]
+        bit = self.row_pos[row]
+        self.put(self.mosi_bits[bit][1], self.mosi_bits[bit + length - 1][2],
+                 self.out_ann, [ann, description])
+        bit += length
+        self.row_pos[row] = bit
+
+    def describe_bits(self, data, names):
+        i = 0x01 << len(names) - 1
+        bit = 0
+        while i != 0:
+            if names[bit] != '':
+                self.putx(1 if (data & i) else 2, 1, names[bit])
+            i >>= 1
+            bit += 1
+
+    def describe_return_bits(self, data, names):
+        i = 0x01 << len(names) - 1
+        bit = 0
+        while i != 0:
+            if names[bit] != '':
+                self.putx(3 if (data & i) else 4, 1, names[bit])
+            else:
+                self.advance_ann(3, 1)
+            i >>= 1
+            bit += 1
+
+    def describe_changed_bits(self, data, old_data, names):
+        changes = data ^ old_data
+        i = 0x01 << (len(names) - 1)
+        bit = 0
+        while i != 0:
+            if names[bit] != '' and changes & i:
+                s = ['+', 'Turning on'] if (data & i) else ['-', 'Turning off']
+                self.putx(5, 1, s)
+            else:
+                self.advance_ann(5, 1)
+            i >>= 1
+            bit += 1
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def handle_configuration_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Configuration command', 'Configuration'])
+        NAMES = [['Internal data register', 'el'], ['FIFO mode', 'ef']]
+
+        bits = (cmd[1] & 0xC0) >> 6
+        old_bits = (self.last_config & 0xC0) >> 6
+        self.describe_bits(bits, NAMES)
+        self.describe_changed_bits(bits, old_bits, NAMES)
+
+        FREQUENCIES = ['315', '433', '868', '915']
+        f = FREQUENCIES[(cmd[1] & 0x30) >> 4] + 'MHz'
+        self.putx(1, 2, ['Frequency: ' + f, f])
+        if cmd[1] & 0x30 != self.last_config & 0x30:
+            self.putx(5, 2, ['Changed', '~'])
+
+        c = '%.1fpF' % (8.5 + (cmd[1] & 0xF) * 0.5)
+        self.putx(1, 4, ['Capacitance: ' + c, c])
+        if cmd[1] & 0xF != self.last_config & 0xF:
+            self.putx(5, 4, ['Changed', '~'])
+
+        self.last_config = cmd[1]
+
+    def handle_power_management_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Power management', 'Power'])
+        NAMES = [['Receiver chain', 'er'], ['Baseband circuit', 'ebb'],
+                 ['Transmission', 'et'], ['Synthesizer', 'es'],
+                 ['Crystal oscillator', 'ex'], ['Low battery detector', 'eb'],
+                 ['Wake-up timer', 'ew'], ['Clock output off switch', 'dc']]
+
+        self.describe_bits(cmd[1], NAMES)
+
+        power = cmd[1]
+
+        # Some bits imply other, even if they are set to 0.
+        if power & 0x80:
+            power |= 0x58
+        if power & 0x20:
+            power |= 0x18
+        self.describe_changed_bits(power, self.last_power, NAMES)
+
+        self.last_power = power
+
+    def handle_frequency_setting_cmd(self, cmd, ret):
+        self.putx(0, 4, ['Frequency setting', 'Frequency'])
+        f = ((cmd[1] & 0xF) << 8) + cmd[2]
+        self.putx(0, 12, ['F = %3.4f' % f])
+        self.row_pos[2] -= 4
+        if self.last_freq != f:
+            self.putx(5, 12, ['Changing', '~'])
+        self.last_freq = f
+
+    def handle_data_rate_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Data rate command', 'Data rate'])
+        r = cmd[1] & 0x7F
+        cs = (cmd[1] & 0x80) >> 7
+        rate = 10000 / 29.0 / (r + 1) / (1 + 7 * cs)
+        self.putx(0, 8, ['%3.1fkbps' % rate])
+        if self.last_data_rate != cmd[1]:
+            self.putx(5, 8, ['Changing', '~'])
+        self.last_data_rate = cmd[1]
+
+    def handle_receiver_control_cmd(self, cmd, ret):
+        self.putx(0, 5, ['Receiver control command'])
+        s = 'interrupt input' if (cmd[0] & 0x04) else 'VDI output'
+        self.putx(0, 1, ['pin16 = ' + s])
+        VDI_NAMES = ['Fast', 'Medium', 'Slow', 'Always on']
+        vdi_speed = VDI_NAMES[cmd[0] & 0x3]
+        self.putx(0, 2, ['VDI: %s' % vdi_speed])
+        BANDWIDTH_NAMES = ['Reserved', '400kHz', '340kHz', '270kHz', '200kHz',
+                           '134kHz', '67kHz', 'Reserved']
+        bandwidth = BANDWIDTH_NAMES[(cmd[1] & 0xE0) >> 5]
+        self.putx(0, 3, ['Bandwidth: %s' % bandwidth])
+        LNA_GAIN_NAMES = [0, -6, -14, -20]
+        lna_gain = LNA_GAIN_NAMES[(cmd[1] & 0x18) >> 3]
+        self.putx(0, 2, ['LNA gain: %ddB' % lna_gain])
+        RSSI_THRESHOLD_NAMES = ['-103', '-97', '-91', '-85', '-79', '-73',
+                                'Reserved', 'Reserved']
+        rssi_threshold = RSSI_THRESHOLD_NAMES[cmd[1] & 0x7]
+        self.putx(0, 3, ['RSSI threshold: %s' % rssi_threshold])
+
+    def handle_data_filter_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Data filter command'])
+        if cmd[1] & 0x80:
+            clock_recovery = 'auto'
+        elif cmd[1] & 0x40:
+            clock_recovery = 'fast'
+        else:
+            clock_recovery = 'slow'
+        self.putx(0, 2, ['Clock recovery: %s mode' % clock_recovery])
+        self.advance_ann(0, 1) # Should always be 1.
+        s = 'analog' if (cmd[1] & 0x10) else 'digital'
+        self.putx(0, 1, ['Data filter: ' + s])
+        self.advance_ann(0, 1) # Should always be 1.
+        self.putx(0, 3, ['DQD threshold: %d' % (cmd[1] & 0x7)])
+
+    def handle_fifo_and_reset_cmd(self, cmd, ret):
+        self.putx(0, 8, ['FIFO and reset command'])
+        fifo_level = (cmd[1] & 0xF0) >> 4
+        self.putx(0, 4, ['FIFO trigger level: %d' % fifo_level])
+        last_fifo_level = (self.last_fifo_and_reset & 0xF0) >> 4
+        if fifo_level != last_fifo_level:
+            self.putx(5, 4, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 4)
+        s = 'one byte' if (cmd[1] & 0x08) else 'two bytes'
+        self.putx(0, 1, ['Synchron length: ' + s])
+        if (cmd[1] & 0x08) != (self.last_fifo_and_reset & 0x08):
+            self.putx(5, 1, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 1)
+
+        if cmd[1] & 0x04:
+            fifo_fill = 'Always'
+        elif cmd[1] & 0x02:
+            fifo_fill = 'After synchron pattern'
+        else:
+            fifo_fill = 'Never'
+        self.putx(0, 2, ['FIFO fill: %s' % fifo_fill])
+        if (cmd[1] & 0x06) != (self.last_fifo_and_reset & 0x06):
+            self.putx(5, 2, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 2)
+
+        s = 'non-sensitive' if (cmd[1] & 0x01) else 'sensitive'
+        self.putx(0, 1, ['Reset mode: ' + s])
+        if (cmd[1] & 0x01) != (self.last_fifo_and_reset & 0x01):
+            self.putx(5, 1, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 1)
+
+        self.last_fifo_and_reset = cmd[1]
+
+    def handle_synchron_pattern_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Synchron pattern command'])
+        if self.last_fifo_and_reset & 0x08:
+            self.putx(0, 8, ['Pattern: 0x2D%02X' % pattern])
+        else:
+            self.putx(0, 8, ['Pattern: %02X' % pattern])
+
+    def handle_fifo_read_cmd(self, cmd, ret):
+        self.putx(0, 8, ['FIFO read command', 'FIFO read'])
+        self.putx(3, 8, ['Data: %02X' % ret[1]])
+
+    def handle_afc_cmd(self, cmd, ret):
+        self.putx(0, 8, ['AFC command'])
+        MODES = ['Off', 'Once', 'During receiving', 'Always']
+        mode = (cmd[1] & 0xC0) >> 6
+        self.putx(0, 2, ['Mode: %s' % MODES[mode]])
+        if (cmd[1] & 0xC0) != (self.last_afc & 0xC0):
+            self.putx(5, 2, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 2)
+
+        range_limit = (cmd[1] & 0x30) >> 4
+        FREQ_TABLE = [0.0, 2.5, 5.0, 7.5]
+        freq_delta = FREQ_TABLE[(self.last_config & 0x30) >> 4]
+
+        if range_limit == 0:
+            self.putx(0, 2, ['Range: No limit'])
+        elif range_limit == 1:
+            self.putx(0, 2, ['Range: +/-%dkHz' % (15 * freq_delta)])
+        elif range_limit == 2:
+            self.putx(0, 2, ['Range: +/-%dkHz' % (7 * freq_delta)])
+        elif range_limit == 3:
+            self.putx(0, 2, ['Range: +/-%dkHz' % (3 * freq_delta)])
+
+        if (cmd[1] & 0x30) != (self.last_afc & 0x30):
+            self.putx(5, 2, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 2)
+
+        NAMES = ['Strobe edge', 'High accuracy mode', 'Enable offset register',
+                 'Enable offset calculation']
+        self.describe_bits(cmd[1] & 0xF, NAMES)
+        self.describe_changed_bits(cmd[1] & 0xF, self.last_afc & 0xF, NAMES)
+
+        self.last_afc = cmd[1]
+
+    def handle_transceiver_control_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Transceiver control command'])
+        self.putx(0, 4, ['FSK frequency delta: %dkHz' % (15 * ((cmd[1] & 0xF0) >> 4))])
+        if cmd[1] & 0xF0 != self.last_transceiver & 0xF0:
+            self.putx(5, 4, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 4)
+
+        POWERS = [0, -2.5, -5, -7.5, -10, -12.5, -15, -17.5]
+        self.advance_ann(0, 1)
+        self.advance_ann(5, 1)
+        self.putx(0,3, ['Relative power: %dB' % (cmd[1] & 0x07)])
+        if (cmd[1] & 0x07) != (self.last_transceiver & 0x07):
+            self.putx(5, 3, ['Changing', '~'])
+        else:
+            self.advance_ann(5, 3)
+        self.last_transceiver = cmd[1]
+
+    def handle_pll_setting_cmd(self, cmd, ret):
+        self.putx(0, 8, ['PLL setting command'])
+        self.advance_ann(0, 1)
+        self.putx(0, 2, ['Clock buffer rise and fall time'])
+        self.advance_ann(0, 1)
+        self.advance_ann(5, 4)
+        NAMES = [['Delay in phase detector', 'dly'], ['Disable dithering', 'ddit']]
+        self.describe_bits((cmd[1] & 0xC) >> 2, NAMES)
+        self.describe_changed_bits((cmd[1] & 0xC) >> 2, (self.last_pll & 0xC) >> 2, NAMES)
+        s = '256kbps, high' if (cmd[1] & 0x01) else '86.2kbps, low'
+        self.putx(0, 1, ['Max bit rate: %s noise' % s])
+
+        self.advance_ann(5, 1)
+        if (cmd[1] & 0x01) != (self.last_pll & 0x01):
+            self.putx(5, 1, ['Changing', '~'])
+
+        self.last_pll = cmd[1]
+
+    def handle_transmitter_register_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Transmitter register command', 'Transmit'])
+        self.putx(0, 8, ['Data: %s' % cmd[1], '%s' % cmd[1]])
+
+    def handle_software_reset_cmd(self, cmd, ret):
+        self.putx(0, 16, ['Software reset command'])
+
+    def handle_wake_up_timer_cmd(self, cmd, ret):
+        self.putx(0, 3, ['Wake-up timer command', 'Timer'])
+        r = cmd[0] & 0x1F
+        m = cmd[1]
+        time = 1.03 * m * pow(2, r) + 0.5
+        self.putx(0, 13, ['Time: %7.2f' % time])
+
+    def handle_low_duty_cycle_cmd(self, cmd, ret):
+        self.putx(0, 16, ['Low duty cycle command'])
+
+    def handle_low_battery_detector_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Low battery detector command'])
+        NAMES = ['1', '1.25', '1.66', '2', '2.5', '3.33', '5', '10']
+        clock = NAMES[(cmd[1] & 0xE0) >> 5]
+        self.putx(0, 3, ['Clock output: %sMHz' % clock, '%sMHz' % clock])
+        self.advance_ann(0, 1)
+        v = 2.25 + (cmd[1] & 0x0F) * 0.1
+        self.putx(0, 4, ['Low battery voltage: %1.2fV' % v, '%1.2fV' % v])
+
+    def handle_status_read_cmd(self, cmd, ret):
+        self.putx(0, 8, ['Status read command', 'Status'])
+        NAMES = ['RGIT/FFIT', 'POR', 'RGUR/FFOV', 'WKUP', 'EXT', 'LBD',
+                 'FFEM', 'RSSI/ATS', 'DQD', 'CRL', 'ATGL']
+        status = (ret[0] << 3) + (ret[1] >> 5)
+        self.row_pos[1] -= 8
+        self.row_pos[2] -= 8
+        self.describe_return_bits(status, NAMES)
+        receiver_enabled = (self.last_power & 0x80) >> 7
+
+        if ret[0] & 0x80:
+            if receiver_enabled:
+                s = 'Received data in FIFO'
+            else:
+                s = 'Transmit register ready'
+            self.putx(5, 1, s)
+        else:
+            self.advance_ann(5, 1)
+        if ret[0] & 0x40:
+            self.putx(5, 1, 'Power on Reset')
+        else:
+            self.advance_ann(5, 1)
+        if ret[0] & 0x20:
+            if receiver_enabled:
+                s = 'RX FIFO overflow'
+            else:
+                s = 'Transmit register under run'
+            self.putx(5, 1, s)
+        else:
+            self.advance_ann(5, 1)
+        if ret[0] & 0x10:
+            self.putx(5, 1, 'Wake-up timer')
+        else:
+            self.advance_ann(5, 1)
+        if ret[0] & 0x08:
+            self.putx(5, 1, 'External interrupt')
+        else:
+            self.advance_ann(5, 1)
+        if ret[0] & 0x04:
+            self.putx(5, 1, 'Low battery')
+        else:
+            self.advance_ann(5, 1)
+        if ret[0] & 0x02:
+            self.putx(5, 1, 'FIFO is empty')
+        else:
+            self.advance_ann(5, 1)
+        if ret[0] & 0x01:
+            if receiver_enabled:
+                s = 'Incoming signal above limit'
+            else:
+                s = 'Antenna detected RF signal'
+            self.putx(5, 1, s)
+        else:
+            self.advance_ann(5, 1)
+        if ret[1] & 0x80:
+            self.putx(5, 1, 'Data quality detector')
+        else:
+            self.advance_ann(5, 1)
+        if ret[1] & 0x40:
+            self.putx(5, 1, 'Clock recovery locked')
+        else:
+            self.advance_ann(5, 1)
+        self.advance_ann(5, 1)
+
+        self.putx(3, 5, ['AFC offset'])
+        if (self.last_status[1] & 0x1F) != (ret[1] & 0x1F):
+            self.putx(5, 5, ['Changed', '~'])
+        self.last_status = ret
+
+    def handle_cmd(self, cmd, ret):
+        if cmd[0] == 0x80:
+            self.handle_configuration_cmd(cmd, ret)
+        elif cmd[0] == 0x82:
+            self.handle_power_management_cmd(cmd, ret)
+        elif cmd[0] & 0xF0 == 0xA0:
+            self.handle_frequency_setting_cmd(cmd, ret)
+        elif cmd[0] == 0xC6:
+            self.handle_data_rate_cmd(cmd, ret)
+        elif cmd[0] & 0xF8 == 0x90:
+            self.handle_receiver_control_cmd(cmd, ret)
+        elif cmd[0] == 0xC2:
+            self.handle_data_filter_cmd(cmd, ret)
+        elif cmd[0] == 0xCA:
+            self.handle_fifo_and_reset_cmd(cmd, ret)
+        elif cmd[0] == 0xCE:
+            self.handle_synchron_pattern_cmd(cmd, ret)
+        elif cmd[0] == 0xB0:
+            self.handle_fifo_read_cmd(cmd, ret)
+        elif cmd[0] == 0xC4:
+            self.handle_afc_cmd(cmd, ret)
+        elif cmd[0] & 0xFE == 0x98:
+            self.handle_transceiver_control_cmd(cmd, ret)
+        elif cmd[0] == 0xCC:
+            self.handle_pll_setting_cmd(cmd, ret)
+        elif cmd[0] == 0xB8:
+            self.handle_transmitter_register_cmd(cmd, ret)
+        elif cmd[0] == 0xFE:
+            self.handle_software_reset_cmd(cmd, ret)
+        elif cmd[0] & 0xE0 == 0xE0:
+            self.handle_wake_up_timer_cmd(cmd, ret)
+        elif cmd[0] == 0xC8:
+            self.handle_low_duty_cycle_cmd(cmd, ret)
+        elif cmd[0] == 0xC0:
+            self.handle_low_battery_detector_cmd(cmd, ret)
+        elif cmd[0] == 0x00:
+            self.handle_status_read_cmd(cmd, ret)
+        else:
+            c = '%02x %02x' % tuple(cmd)
+            r = '%02x %02x' % tuple(ret)
+            self.putx(0, 16, ['Uknown command: %s (reply: %s)!' % (c, r)])
+
+    def decode(self, ss, es, data):
+        ptype, mosi, miso = data
+
+        # For now, only use DATA and BITS packets.
+        if ptype not in ('DATA', 'BITS'):
+            return
+
+        # Store the individual bit values and ss/es numbers. The next packet
+        # is guaranteed to be a 'DATA' packet belonging to this 'BITS' one.
+        if ptype == 'BITS':
+            if mosi is not None:
+                self.mosi_bits.extend(reversed(mosi))
+            if miso is not None:
+                self.miso_bits.extend(reversed(miso))
+            return
+
+        # Append new bytes.
+        self.mosi_bytes.append(mosi)
+        self.miso_bytes.append(miso)
+
+        # All commands consist of 2 bytes.
+        if len(self.mosi_bytes) < 2:
+            return
+
+        self.row_pos = [0, 8, 8]
+
+        self.handle_cmd(self.mosi_bytes, self.miso_bytes)
+
+        self.mosi_bytes, self.miso_bytes = [], []
+        self.mosi_bits, self.miso_bits = [], []
index 3d41ea588461be3dead4d30710aabdbf3c0aad74..c0e0ea19cf6ae51fbd0431fb8d5de8036a34992a 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'spi' PD and decodes generic RGB LED string
 values that are clocked over SPI in RGB values.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 8577928678b9a1c48d5a341697919d554d3b5104..c6e1032fbe08487df1431f242562289b83a40a37 100644 (file)
@@ -25,7 +25,7 @@ class Decoder(srd.Decoder):
     id = 'rgb_led_spi'
     name = 'RGB LED (SPI)'
     longname = 'RGB LED string decoder (SPI)'
-    desc = 'Generic RGB LED string protocol (RGB values clocked over SPI).'
+    desc = 'RGB LED string protocol (RGB values clocked over SPI).'
     license = 'gplv2'
     inputs = ['spi']
     outputs = ['rgb_led_spi']
@@ -34,14 +34,14 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self, **kwargs):
-        self.cmd_ss, self.cmd_es = 0, 0
+        self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def putx(self, data):
-        self.put(self.cmd_ss, self.cmd_es, self.out_ann, data)
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
 
     def decode(self, ss, es, data):
         ptype, mosi, miso = data
@@ -52,7 +52,7 @@ class Decoder(srd.Decoder):
         self.ss, self.es = ss, es
 
         if len(self.mosi_bytes) == 0:
-            self.cmd_ss = ss
+            self.ss_cmd = ss
         self.mosi_bytes.append(mosi)
 
         # RGB value == 3 bytes
@@ -60,10 +60,8 @@ class Decoder(srd.Decoder):
             return
 
         red, green, blue = self.mosi_bytes
-        rgb_value  = int(red) << 16
-        rgb_value |= int(green) << 8
-        rgb_value |= int(blue)
+        rgb_value = int(red) << 16 | int(green) << 8 | int(blue)
 
-        self.cmd_es = es
-        self.putx([0, ["#%.6x" % rgb_value]])
+        self.es_cmd = es
+        self.putx([0, ['#%.6x' % rgb_value]])
         self.mosi_bytes = []
index 9a397b1fb3ab057fd14c8a0c7da0c22de647dc3f..e2776a6a1d7d0b386acb79b77ee65188564ff01a 100644 (file)
@@ -23,5 +23,4 @@ This decoder stacks on top of the 'i2c' PD and decodes the Epson
 RTC-8564 JE/NB real-time clock (RTC) protocol.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index f13af364fe14dd731c639667ac7419989dc0c339..24a68fbe28da6f50c8d30f4a57ddf1de8f0e8d9f 100644 (file)
@@ -189,7 +189,7 @@ class Decoder(srd.Decoder):
             if cmd != 'START':
                 return
             self.state = 'GET SLAVE ADDR'
-            self.block_start_sample = ss
+            self.ss_block = ss
         elif self.state == 'GET SLAVE ADDR':
             # Wait for an address write operation.
             # TODO: We should only handle packets to the RTC slave (0xa2/0xa3).
@@ -220,7 +220,7 @@ class Decoder(srd.Decoder):
                 # TODO: Handle read/write of only parts of these items.
                 d = '%02d.%02d.%02d %02d:%02d:%02d' % (self.days, self.months,
                     self.years, self.hours, self.minutes, self.seconds)
-                self.put(self.block_start_sample, es, self.out_ann,
+                self.put(self.ss_block, es, self.out_ann,
                          [9, ['Write date/time: %s' % d, 'Write: %s' % d,
                               'W: %s' % d]])
                 self.state = 'IDLE'
@@ -246,12 +246,9 @@ class Decoder(srd.Decoder):
             elif cmd == 'STOP':
                 d = '%02d.%02d.%02d %02d:%02d:%02d' % (self.days, self.months,
                     self.years, self.hours, self.minutes, self.seconds)
-                self.put(self.block_start_sample, es, self.out_ann,
+                self.put(self.ss_block, es, self.out_ann,
                          [10, ['Read date/time: %s' % d, 'Read: %s' % d,
                                'R: %s' % d]])
                 self.state = 'IDLE'
             else:
                 pass # TODO?
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
index 293b654fd033aa13da177b838ac4ace9ab55fab4..7ce66bf968638bf2210d697933c2ee7844120811 100644 (file)
@@ -66,5 +66,4 @@ SPI mode properties (differences to SD mode):
  * The RCA register is not accessible in SPI mode.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index be20ee3b356a1c907865e65dfc6e269eedcf7e10..bc761ee822914ad0fe67dbd873d18f3c50128d8a 100644 (file)
@@ -98,10 +98,9 @@ class Decoder(srd.Decoder):
 
     def __init__(self, **kwargs):
         self.state = 'IDLE'
-        self.samplenum = 0
         self.ss, self.es = 0, 0
-        self.bit_ss, self.bit_es = 0, 0
-        self.cmd_ss, self.cmd_es = 0, 0
+        self.ss_bit, self.es_bit = 0, 0
+        self.ss_cmd, self.es_cmd = 0, 0
         self.cmd_token = []
         self.cmd_token_bits = []
         self.is_acmd = False # Indicates CMD vs. ACMD
@@ -113,13 +112,13 @@ class Decoder(srd.Decoder):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def putx(self, data):
-        self.put(self.cmd_ss, self.cmd_es, self.out_ann, data)
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
 
     def putc(self, cmd, desc):
         self.putx([cmd, ['%s: %s' % (self.cmd_str, desc)]])
 
     def putb(self, data):
-        self.put(self.bit_ss, self.bit_es, self.out_ann, data)
+        self.put(self.ss_bit, self.es_bit, self.out_ann, data)
 
     def cmd_name(self, cmd):
         c = acmd_names if self.is_acmd else cmd_names
@@ -137,7 +136,7 @@ class Decoder(srd.Decoder):
         #  - CMD[00:00]: End bit (always 1)
 
         if len(self.cmd_token) == 0:
-            self.cmd_ss = self.ss
+            self.ss_cmd = self.ss
 
         self.cmd_token.append(mosi)
         self.cmd_token_bits.append(self.mosi_bits)
@@ -146,7 +145,7 @@ class Decoder(srd.Decoder):
         if len(self.cmd_token) < 6:
             return
 
-        self.cmd_es = self.es
+        self.es_cmd = self.es
 
         t = self.cmd_token
 
@@ -157,14 +156,14 @@ class Decoder(srd.Decoder):
             return self.cmd_token_bits[5 - byte][bit]
 
         # Bits[47:47]: Start bit (always 0)
-        bit, self.bit_ss, self.bit_es = tb(5, 7)[0], tb(5, 7)[1], tb(5, 7)[2]
+        bit, self.ss_bit, self.es_bit = tb(5, 7)[0], tb(5, 7)[1], tb(5, 7)[2]
         if bit == 0:
             self.putb([134, ['Start bit: %d' % bit]])
         else:
             self.putb([135, ['Start bit: %s (Warning: Must be 0!)' % bit]])
 
         # Bits[46:46]: Transmitter bit (1 == host)
-        bit, self.bit_ss, self.bit_es = tb(5, 6)[0], tb(5, 6)[1], tb(5, 6)[2]
+        bit, self.ss_bit, self.es_bit = tb(5, 6)[0], tb(5, 6)[1], tb(5, 6)[2]
         if bit == 1:
             self.putb([134, ['Transmitter bit: %d' % bit]])
         else:
@@ -172,22 +171,22 @@ class Decoder(srd.Decoder):
 
         # Bits[45:40]: Command index (BCD; valid: 0-63)
         cmd = self.cmd_index = t[0] & 0x3f
-        self.bit_ss, self.bit_es = tb(5, 5)[1], tb(5, 0)[2]
+        self.ss_bit, self.es_bit = tb(5, 5)[1], tb(5, 0)[2]
         self.putb([134, ['Command: %s%d (%s)' % (s, cmd, self.cmd_name(cmd))]])
 
         # Bits[39:8]: Argument
         self.arg = (t[1] << 24) | (t[2] << 16) | (t[3] << 8) | t[4]
-        self.bit_ss, self.bit_es = tb(4, 7)[1], tb(1, 0)[2]
+        self.ss_bit, self.es_bit = tb(4, 7)[1], tb(1, 0)[2]
         self.putb([134, ['Argument: 0x%04x' % self.arg]])
 
         # Bits[7:1]: CRC7
         # TODO: Check CRC7.
         crc = t[5] >> 1
-        self.bit_ss, self.bit_es = tb(0, 7)[1], tb(0, 1)[2]
+        self.ss_bit, self.es_bit = tb(0, 7)[1], tb(0, 1)[2]
         self.putb([134, ['CRC7: 0x%01x' % crc]])
 
         # Bits[0:0]: End bit (always 1)
-        bit, self.bit_ss, self.bit_es = tb(0, 0)[0], tb(0, 0)[1], tb(0, 0)[2]
+        bit, self.ss_bit, self.es_bit = tb(0, 0)[0], tb(0, 0)[1], tb(0, 0)[2]
         self.putb([134, ['End bit: %d' % bit]])
         if bit == 1:
             self.putb([134, ['End bit: %d' % bit]])
@@ -212,8 +211,8 @@ class Decoder(srd.Decoder):
         # CMD1: SEND_OP_COND
         self.putc(1, 'Send HCS info and activate the card init process')
         hcs = (self.arg & (1 << 30)) >> 30
-        self.bit_ss = self.cmd_token_bits[5 - 4][6][1]
-        self.bit_es = self.cmd_token_bits[5 - 4][6][2]
+        self.ss_bit = self.cmd_token_bits[5 - 4][6][1]
+        self.es_bit = self.cmd_token_bits[5 - 4][6][2]
         self.putb([134, ['HCS: %d' % hcs]])
         self.state = 'GET RESPONSE R1'
 
@@ -221,14 +220,14 @@ class Decoder(srd.Decoder):
         # CMD9: SEND_CSD (128 bits / 16 bytes)
         self.putc(9, 'Ask card to send its card specific data (CSD)')
         if len(self.read_buf) == 0:
-            self.cmd_ss = self.ss
+            self.ss_cmd = self.ss
         self.read_buf.append(self.miso)
         # FIXME
         ### if len(self.read_buf) < 16:
         if len(self.read_buf) < 16 + 4:
             return
-        self.cmd_es = self.es
-        self.read_buf = self.read_buf[4:] ### TODO: Document or redo.
+        self.es_cmd = self.es
+        self.read_buf = self.read_buf[4:] # TODO: Document or redo.
         self.putx([9, ['CSD: %s' % self.read_buf]])
         # TODO: Decode all bits.
         self.read_buf = []
@@ -257,11 +256,11 @@ class Decoder(srd.Decoder):
         # CMD17: READ_SINGLE_BLOCK
         self.putc(17, 'Read a block from address 0x%04x' % self.arg)
         if len(self.read_buf) == 0:
-            self.cmd_ss = self.ss
+            self.ss_cmd = self.ss
         self.read_buf.append(self.miso)
         if len(self.read_buf) < self.blocklen + 2: # FIXME
             return
-        self.cmd_es = self.es
+        self.es_cmd = self.es
         self.read_buf = self.read_buf[2:] # FIXME
         self.putx([17, ['Block data: %s' % self.read_buf]])
         self.read_buf = []
@@ -332,12 +331,12 @@ class Decoder(srd.Decoder):
         # The R1 response token format (1 byte).
         # Sent by the card after every command except for SEND_STATUS.
 
-        self.cmd_ss, self.cmd_es = self.miso_bits[7][1], self.miso_bits[0][2]
+        self.ss_cmd, self.es_cmd = self.miso_bits[7][1], self.miso_bits[0][2]
         self.putx([65, ['R1: 0x%02x' % res]])
 
         def putbit(bit, data):
             b = self.miso_bits[bit]
-            self.bit_ss, self.bit_es = b[1], b[2]
+            self.ss_bit, self.es_bit = b[1], b[2]
             self.putb([134, data])
 
         # Bit 0: 'In idle state' bit
@@ -439,6 +438,3 @@ class Decoder(srd.Decoder):
             handle_response(miso)
 
             self.state = 'IDLE'
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
diff --git a/decoders/spdif/__init__.py b/decoders/spdif/__init__.py
new file mode 100644 (file)
index 0000000..38363f0
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Guenther Wenninger <robin@bitschubbser.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+S/PDIF (Sony/Philips Digital Interface Format) is a serial bus for
+transmitting audio data.
+'''
+
+from .pd import Decoder
diff --git a/decoders/spdif/pd.py b/decoders/spdif/pd.py
new file mode 100644 (file)
index 0000000..e6977e2
--- /dev/null
@@ -0,0 +1,257 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Guenther Wenninger <robin@bitschubbser.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'spdif'
+    name = 'S/PDIF'
+    longname = 'Sony/Philips Digital Interface Format'
+    desc = 'Serial bus for connecting digital audio devices.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['spdif']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    annotations = (
+        ('bitrate', 'Bitrate / baudrate'),
+        ('preamble', 'Preamble'),
+        ('bits', 'Bits'),
+        ('aux', 'Auxillary-audio-databits'),
+        ('samples', 'Audio Samples'),
+        ('validity', 'Data Valid'),
+        ('subcode', 'Subcode data'),
+        ('chan_stat', 'Channnel Status'),
+        ('parity', 'Parity Bit'),
+    )
+    annotation_rows = (
+        ('info', 'Info', (0, 1, 3, 5, 6, 7, 8)),
+        ('bits', 'Bits', (2,)),
+        ('samples', 'Samples', (4,)),
+    )
+
+    def putx(self, ss, es, data):
+        self.put(ss, es, self.out_ann, data)
+
+    def puty(self, data):
+        self.put(self.ss_edge, self.samplenum, self.out_ann, data)
+
+    def __init__(self, **kwargs):
+        self.state = 'GET FIRST PULSE WIDTH'
+        self.olddata = None
+        self.ss_edge = None
+        self.first_edge = True
+        self.pulse_width = 0
+
+        self.clocks = []
+        self.range1 = 0
+        self.range2 = 0
+
+        self.preamble_state = 0
+        self.preamble = []
+        self.seen_preamble = False
+        self.last_preamble = 0
+
+        self.first_one = True
+        self.subframe = []
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def get_pulse_type(self):
+        if self.range1 == 0 or self.range2 == 0:
+            return -1
+        if self.pulse_width >= self.range2:
+            return 2
+        elif self.pulse_width >= self.range1:
+            return 0
+        else:
+            return 1
+
+    def find_first_pulse_width(self):
+        if self.pulse_width != 0:
+            self.clocks.append(self.pulse_width)
+            self.state = 'GET SECOND PULSE WIDTH'
+
+    def find_second_pulse_width(self):
+        if self.pulse_width > (self.clocks[0] * 1.3) or \
+                self.pulse_width < (self.clocks[0] * 0.7):
+            self.clocks.append(self.pulse_width)
+            self.state = 'GET THIRD PULSE WIDTH'
+
+    def find_third_pulse_width(self):
+        if not ((self.pulse_width > (self.clocks[0] * 1.3) or \
+                self.pulse_width < (self.clocks[0] * 0.7)) \
+                and (self.pulse_width > (self.clocks[1] * 1.3) or \
+                self.pulse_width < (self.clocks[1] * 0.7))):
+            return
+
+        self.clocks.append(self.pulse_width)
+        self.clocks.sort()
+        self.range1 = (self.clocks[0] + self.clocks[1]) / 2
+        self.range2 = (self.clocks[1] + self.clocks[2]) / 2
+        spdif_bitrate = int(self.samplerate / (self.clocks[2] / 1.5))
+        self.ss_edge = 0
+
+        self.puty([0, ['Signal Bitrate: %d Mbit/s (=> %d kHz)' % \
+                  (spdif_bitrate, (spdif_bitrate/ (2 * 32)))]])
+
+        clock_period_nsec = 1000000000 / spdif_bitrate
+
+        self.last_preamble = self.samplenum
+
+        # We are done recovering the clock, now let's decode the data stream.
+        self.state = 'DECODE STREAM'
+
+    def decode_stream(self):
+        pulse = self.get_pulse_type()
+
+        if not self.seen_preamble:
+            # This is probably the start of a preamble, decode it.
+            if pulse == 2:
+                self.preamble.append(self.get_pulse_type())
+                self.state = 'DECODE PREAMBLE'
+                self.ss_edge = self.samplenum - self.pulse_width - 1
+            return
+
+        # We've seen a preamble.
+        if pulse == 1 and self.first_one:
+            self.first_one = False
+            self.subframe.append([pulse, self.samplenum - \
+                self.pulse_width - 1, self.samplenum])
+        elif pulse == 1 and not self.first_one:
+            self.subframe[-1][2] = self.samplenum
+            self.putx(self.subframe[-1][1], self.samplenum, [2, ['1']])
+            self.bitcount += 1
+            self.first_one = True
+        else:
+            self.subframe.append([pulse, self.samplenum - \
+                self.pulse_width - 1, self.samplenum])
+            self.putx(self.samplenum - self.pulse_width - 1,
+                      self.samplenum, [2, ['0']])
+            self.bitcount += 1
+
+        if self.bitcount == 28:
+            aux_audio_data = self.subframe[0:4]
+            sam, sam_rot = '', ''
+            for a in aux_audio_data:
+                sam = sam + str(a[0])
+                sam_rot = str(a[0]) + sam_rot
+            sample = self.subframe[4:24]
+            for s in sample:
+                sam = sam + str(s[0])
+                sam_rot = str(s[0]) + sam_rot
+            validity = self.subframe[24:25]
+            subcode_data = self.subframe[25:26]
+            channel_status = self.subframe[26:27]
+            parity = self.subframe[27:28]
+
+            self.putx(aux_audio_data[0][1], aux_audio_data[3][2], \
+                      [3, ['Aux 0x%x' % int(sam, 2), '0x%x' % int(sam, 2)]])
+            self.putx(sample[0][1], sample[19][2], \
+                      [3, ['Sample 0x%x' % int(sam, 2), '0x%x' % int(sam, 2)]])
+            self.putx(aux_audio_data[0][1], sample[19][2], \
+                      [4, ['Audio 0x%x' % int(sam_rot, 2), '0x%x' % int(sam_rot, 2)]])
+            if validity[0][0] == 0:
+                self.putx(validity[0][1], validity[0][2], [5, ['V']])
+            else:
+                self.putx(validity[0][1], validity[0][2], [5, ['E']])
+            self.putx(subcode_data[0][1], subcode_data[0][2],
+                [6, ['S: %d' % subcode_data[0][0]]])
+            self.putx(channel_status[0][1], channel_status[0][2],
+                [7, ['C: %d' % channel_status[0][0]]])
+            self.putx(parity[0][1], parity[0][2], [8, ['P: %d' % parity[0][0]]])
+
+            self.subframe = []
+            self.seen_preamble = False
+            self.bitcount = 0
+
+    def decode_preamble(self):
+        if self.preamble_state == 0:
+            self.preamble.append(self.get_pulse_type())
+            self.preamble_state = 1
+        elif self.preamble_state == 1:
+            self.preamble.append(self.get_pulse_type())
+            self.preamble_state = 2
+        elif self.preamble_state == 2:
+            self.preamble.append(self.get_pulse_type())
+            self.preamble_state = 0
+            self.state = 'DECODE STREAM'
+            if self.preamble == [2, 0, 1, 0]:
+                self.puty([1, ['Preamble W', 'W']])
+            elif self.preamble == [2, 2, 1, 1]:
+                self.puty([1, ['Preamble M', 'M']])
+            elif self.preamble == [2, 1, 1, 2]:
+                self.puty([1, ['Preamble B', 'B']])
+            else:
+                self.puty([1, ['Unknown Preamble', 'Unknown Prea.', 'U']])
+            self.preamble = []
+            self.seen_preamble = True
+            self.bitcount = 0
+            self.first_one = True
+
+        self.last_preamble = self.samplenum
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        for (self.samplenum, pins) in data:
+            data = pins[0]
+
+            # Initialize self.olddata with the first sample value.
+            if self.olddata is None:
+                self.olddata = data
+                continue
+
+            # First we need to recover the clock.
+            if self.olddata == data:
+                self.pulse_width += 1
+                continue
+
+            # Found rising or falling edge.
+            if self.first_edge:
+                # Throw away first detected edge as it might be mangled data.
+                self.first_edge = False
+                self.pulse_width = 0
+            else:
+                if self.state == 'GET FIRST PULSE WIDTH':
+                    self.find_first_pulse_width()
+                elif self.state == 'GET SECOND PULSE WIDTH':
+                    self.find_second_pulse_width()
+                elif self.state == 'GET THIRD PULSE WIDTH':
+                    self.find_third_pulse_width()
+                elif self.state == 'DECODE STREAM':
+                    self.decode_stream()
+                elif self.state == 'DECODE PREAMBLE':
+                    self.decode_preamble()
+
+            self.pulse_width = 0
+
+            self.olddata = data
index a1f0f1c3ebf1fe044a9e84352cfad9c014a13e34..f76bb064fc8ca72773bae17bec531703496b88e3 100644 (file)
@@ -29,5 +29,4 @@ transitions where CS# is not asserted are ignored). If CS# is not supplied,
 data is decoded on every clock transition (depending on SPI mode).
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 5bab8f75f16c4d305e08b4937deb57b1be44791f..4a686ddd204a9fbff6b439f37e90db2f9ef14dda 100644 (file)
@@ -28,16 +28,19 @@ Packet:
 [<ptype>, <data1>, <data2>]
 
 <ptype>:
- - 'DATA': <data1> contains the MISO data, <data2> contains the MOSI data.
+ - 'DATA': <data1> contains the MOSI data, <data2> contains the MISO data.
    The data is _usually_ 8 bits (but can also be fewer or more bits).
    Both data items are Python numbers (not strings), or None if the respective
    channel was not supplied.
- - 'BITS': <data1>/<data2> contain a list of bit values in this MISO/MOSI data
+ - 'BITS': <data1>/<data2> contain a list of bit values in this MOSI/MISO data
    item, and for each of those also their respective start-/endsample numbers.
  - 'CS CHANGE': <data1> is the old CS# pin value, <data2> is the new value.
-   Both data items are Python numbers (0/1), not strings.
+   Both data items are Python numbers (0/1), not strings. At the beginning of
+   the decoding a packet is generated with <data1> = None and <data2> being the
+   initial state of the CS# pin or None if the chip select pin is not supplied.
 
 Examples:
+ ['CS-CHANGE', None, 1]
  ['CS-CHANGE', 1, 0]
  ['DATA', 0xff, 0x3a]
  ['BITS', [[1, 80, 82], [1, 83, 84], [1, 85, 86], [1, 87, 88],
@@ -60,6 +63,12 @@ spi_mode = {
     (1, 1): 3, # Mode 3
 }
 
+class SamplerateError(Exception):
+    pass
+
+class ChannelError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'spi'
@@ -102,6 +111,10 @@ class Decoder(srd.Decoder):
         ('mosi-bits', 'MOSI bits', (3,)),
         ('other', 'Other', (4,)),
     )
+    binary = (
+        ('miso', 'MISO'),
+        ('mosi', 'MOSI'),
+    )
 
     def __init__(self):
         self.samplerate = None
@@ -110,13 +123,13 @@ class Decoder(srd.Decoder):
         self.misodata = self.mosidata = 0
         self.misobits = []
         self.mosibits = []
-        self.startsample = -1
+        self.ss_block = -1
         self.samplenum = -1
         self.cs_was_deasserted = False
-        self.oldcs = -1
+        self.oldcs = None
         self.oldpins = None
         self.have_cs = self.have_miso = self.have_mosi = None
-        self.state = 'IDLE'
+        self.no_cs_notification = False
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -125,11 +138,12 @@ class Decoder(srd.Decoder):
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_bin = self.register(srd.OUTPUT_BINARY)
         self.out_bitrate = self.register(srd.OUTPUT_META,
                 meta=(int, 'Bitrate', 'Bitrate during transfers'))
 
     def putw(self, data):
-        self.put(self.startsample, self.samplenum, self.out_ann, data)
+        self.put(self.ss_block, self.samplenum, self.out_ann, data)
 
     def putdata(self):
         # Pass MISO and MOSI bits and then data to the next PD up the stack.
@@ -140,8 +154,10 @@ class Decoder(srd.Decoder):
 
         if self.have_miso:
             ss, es = self.misobits[-1][1], self.misobits[0][2]
+            self.put(ss, es, self.out_bin, (0, bytes([so])))
         if self.have_mosi:
             ss, es = self.mosibits[-1][1], self.mosibits[0][2]
+            self.put(ss, es, self.out_bin, (1, bytes([si])))
 
         self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits])
         self.put(ss, es, self.out_python, ['DATA', si, so])
@@ -167,16 +183,16 @@ class Decoder(srd.Decoder):
         self.mosibits = [] if self.have_mosi else None
         self.bitcount = 0
 
+    def cs_asserted(self, cs):
+        active_low = (self.options['cs_polarity'] == 'active-low')
+        return (cs == 0) if active_low else (cs == 1)
+
     def handle_bit(self, miso, mosi, clk, cs):
         # If this is the first bit of a dataword, save its sample number.
         if self.bitcount == 0:
-            self.startsample = self.samplenum
-            self.cs_was_deasserted = False
-            if self.have_cs:
-                active_low = (self.options['cs_polarity'] == 'active-low')
-                deasserted = (cs == 1) if active_low else (cs == 0)
-                if deasserted:
-                    self.cs_was_deasserted = True
+            self.ss_block = self.samplenum
+            self.cs_was_deasserted = \
+                not self.cs_asserted(cs) if self.have_cs else False
 
         ws = self.options['wordsize']
 
@@ -222,9 +238,9 @@ class Decoder(srd.Decoder):
 
         # Meta bitrate.
         elapsed = 1 / float(self.samplerate)
-        elapsed *= (self.samplenum - self.startsample + 1)
+        elapsed *= (self.samplenum - self.ss_block + 1)
         bitrate = int(1 / elapsed * self.options['wordsize'])
-        self.put(self.startsample, self.samplenum, self.out_bitrate, bitrate)
+        self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
 
         if self.have_cs and self.cs_was_deasserted:
             self.putw([4, ['CS# was deasserted during this data word!']])
@@ -240,6 +256,10 @@ class Decoder(srd.Decoder):
             # Reset decoder state when CS# changes (and the CS# pin is used).
             self.reset_decoder_state()
 
+        # We only care about samples if CS# is asserted.
+        if self.have_cs and not self.cs_asserted(cs):
+            return
+
         # Ignore sample if the clock pin hasn't changed.
         if clk == self.oldclk:
             return
@@ -261,8 +281,8 @@ class Decoder(srd.Decoder):
         self.handle_bit(miso, mosi, clk, cs)
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         # Either MISO or MOSI can be omitted (but not both). CS# is optional.
         for (self.samplenum, pins) in data:
 
@@ -276,11 +296,11 @@ class Decoder(srd.Decoder):
 
             # Either MISO or MOSI (but not both) can be omitted.
             if not (self.have_miso or self.have_mosi):
-                raise Exception('Either MISO or MOSI (or both) pins required.')
+                raise ChannelError('Either MISO or MOSI (or both) pins required.')
 
-            # State machine.
-            if self.state == 'IDLE':
-                self.find_clk_edge(miso, mosi, clk, cs)
-            else:
-                raise Exception('Invalid state: %s' % self.state)
+            # Tell stacked decoders that we don't have a CS# signal.
+            if not self.no_cs_notification and not self.have_cs:
+                self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
+                self.no_cs_notification = True
 
+            self.find_clk_edge(miso, mosi, clk, cs)
diff --git a/decoders/spiflash/__init__.py b/decoders/spiflash/__init__.py
new file mode 100644 (file)
index 0000000..6ffb4da
--- /dev/null
@@ -0,0 +1,31 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012-2015 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
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the xx25 series
+SPI (NOR) flash chip protocol.
+
+It currently supports the MX25L1605D/MX25L3205D/MX25L6405D.
+
+Details:
+http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/h_Index/3F21BAC2E121E17848257639003A3146/$File/MX25L1605D-3205D-6405D-1.5.pdf
+'''
+
+from .pd import Decoder
diff --git a/decoders/spiflash/lists.py b/decoders/spiflash/lists.py
new file mode 100644 (file)
index 0000000..4ed6aaf
--- /dev/null
@@ -0,0 +1,90 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 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
+##
+
+# 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',
+}
+
+chips = {
+    # Macronix
+    'macronix_mx25l1605d': {
+        'vendor': 'Macronix',
+        'model': 'MX25L1605D',
+        'res_id': 0x14,
+        'rems_id': 0xc214,
+        'rems2_id': 0xc214,
+        'rdid_id': 0xc22015,
+        'page_size': 256,
+        'sector_size': 4 * 1024,
+        'block_size': 64 * 1024,
+    },
+    'macronix_mx25l3205d': {
+        'vendor': 'Macronix',
+        'model': 'MX25L3205D',
+        'res_id': 0x15,
+        'rems_id': 0xc215,
+        'rems2_id': 0xc215,
+        'rdid_id': 0xc22016,
+        'page_size': 256,
+        'sector_size': 4 * 1024,
+        'block_size': 64 * 1024,
+    },
+    'macronix_mx25l6405d': {
+        'vendor': 'Macronix',
+        'model': 'MX25L6405D',
+        'res_id': 0x16,
+        'rems_id': 0xc216,
+        'rems2_id': 0xc216,
+        'rdid_id': 0xc22017,
+        'page_size': 256,
+        'sector_size': 4 * 1024,
+        'block_size': 64 * 1024,
+    },
+}
diff --git a/decoders/spiflash/pd.py b/decoders/spiflash/pd.py
new file mode 100644 (file)
index 0000000..0f4dd9b
--- /dev/null
@@ -0,0 +1,379 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2011-2015 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+from .lists import *
+
+def cmd_annotation_classes():
+    return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
+
+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 = 2
+    id = 'spiflash'
+    name = 'SPI flash'
+    longname = 'SPI flash chips'
+    desc = 'xx25 series SPI (NOR) flash chip protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['spiflash']
+    annotations = cmd_annotation_classes() + (
+        ('bits', 'Bits'),
+        ('bits2', 'Bits2'),
+        ('warnings', 'Warnings'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (24, 25)),
+        ('commands', 'Commands', tuple(range(23 + 1))),
+        ('warnings', 'Warnings', (26,)),
+    )
+    options = (
+        {'id': 'chip', 'desc': 'Chip', 'default': tuple(chips.keys())[0],
+            'values': tuple(chips.keys())},
+    )
+
+    def __init__(self, **kwargs):
+        self.state = None
+        self.cmdstate = 1
+        self.addr = 0
+        self.data = []
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.chip = chips[self.options['chip']]
+
+    def putx(self, data):
+        # Simplification, most annotations span exactly one SPI byte/packet.
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def putb(self, data):
+        self.put(self.block_ss, self.block_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.ss_block = self.ss
+            self.putx([2, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate == 2:
+            # Byte 2: Slave sends the JEDEC manufacturer ID.
+            self.putx([2, ['Manufacturer ID: 0x%02x' % miso]])
+        elif self.cmdstate == 3:
+            # Byte 3: Slave sends the memory type (0x20 for this chip).
+            self.putx([2, ['Memory type: 0x%02x' % miso]])
+        elif self.cmdstate == 4:
+            # Byte 4: Slave sends the device ID.
+            self.device_id = miso
+            self.putx([2, ['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.ss_block, 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([3, ['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([24, ['Status register: 0x%02x' % miso]])
+                self.putx([25, [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([5, ['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([24, ['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([24, ['Read data']])
+                self.putx([25, ['Read data: %s' % s]])
+                self.data = []
+                self.state = None
+                return
+
+        self.cmdstate += 1
+
+    def handle_fast_read(self, mosi, miso):
+        # Fast read: Master asserts CS#, sends FAST READ command, sends
+        # 3-byte address + 1 dummy byte, reads >= 1 data bytes, de-asserts CS#.
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.putx([5, ['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.putx([24, ['AD%d: 0x%02x' % (self.cmdstate - 1, mosi)]])
+            if self.cmdstate == 2:
+                self.block_ss = self.ss
+            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+        elif self.cmdstate == 5:
+            self.putx([24, ['Dummy byte: 0x%02x' % mosi]])
+            self.block_es = self.es
+            self.putb([5, ['Read address: 0x%06x' % self.addr]])
+            self.addr = 0
+        elif self.cmdstate >= 6:
+            # Bytes 6-x: Master reads data bytes (until CS# de-asserted).
+            # TODO: For now we hardcode 32 bytes per FAST READ command.
+            if self.cmdstate == 6:
+                self.block_ss = self.ss
+            if self.cmdstate <= 32 + 5: # TODO: While CS# asserted.
+                self.data.append(miso)
+            if self.cmdstate == 32 + 5: # TODO: If CS# got de-asserted.
+                self.block_es = self.es
+                s = ' '.join([hex(b)[2:] for b in self.data])
+                self.putb([25, ['Read data: %s' % s]])
+                self.data = []
+                self.state = None
+                return
+
+        self.cmdstate += 1
+
+    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.ss_block = self.ss
+            self.putx([8, ['Command: %s' % cmds[self.state][1]]])
+        elif self.cmdstate in (2, 3, 4):
+            # Bytes 2/3/4: Master sends sector 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.ss_block, self.es, self.out_ann, [24, [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.ss_block, self.es, self.out_ann, [101, [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([12, ['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([24, ['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([24, ['Page data']])
+                self.putx([25, ['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.ss_block = self.ss
+            self.putx([16, ['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([24, ['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([24, ['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([24, ['%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([24, ['%s ID' % d]])
+
+        if self.cmdstate == 6:
+            id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
+            self.putx([24, ['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 is 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([24, ['Unknown command: 0x%02x' % mosi]])
+            self.state = None
diff --git a/decoders/stepper_motor/__init__.py b/decoders/stepper_motor/__init__.py
new file mode 100644 (file)
index 0000000..222d393
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This PD decodes the stepper motor controller signals (step / dir) and
+shows the step speed and absolute position of the stepper motor.
+'''
+
+from .pd import Decoder
diff --git a/decoders/stepper_motor/pd.py b/decoders/stepper_motor/pd.py
new file mode 100644 (file)
index 0000000..1527d48
--- /dev/null
@@ -0,0 +1,97 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'stepper_motor'
+    name = 'Stepper motor'
+    longname = 'Stepper motor position / speed'
+    desc = 'Absolute position and movement speed from step/dir.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['stepper_motor']
+    channels = (
+        {'id': 'step', 'name': 'Step', 'desc': 'Step pulse'},
+        {'id': 'dir', 'name': 'Direction', 'desc': 'Direction select'},
+    )
+    options = (
+        {'id': 'unit', 'desc': 'Unit', 'default': 'steps',
+         'values': ('steps', 'mm')},
+        {'id': 'steps_per_mm', 'desc': 'Steps per mm', 'default': 100.0},
+    )
+    annotations = (
+        ('speed', 'Speed'),
+        ('position', 'Position')
+    )
+    annotation_rows = (
+        ('speed', 'Speed', (0,)),
+        ('position', 'Position', (1,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.oldstep = None
+        self.prev_step_ss = None
+        self.pos = 0
+        self.prev_speed = None
+        self.prev_pos = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+        if self.options['unit'] == 'steps':
+            self.scale = 1
+            self.format = '%0.0f'
+            self.unit = 'steps'
+        else:
+            self.scale = self.options['steps_per_mm']
+            self.format = '%0.2f'
+            self.unit = 'mm'
+
+    def step(self, ss, direction):
+        if self.prev_step_ss is not None:
+            delta = ss - self.prev_step_ss
+            speed = self.samplerate / delta / self.scale
+            speed_txt = self.format % speed
+            pos_txt = self.format % (self.pos / self.scale)
+            self.put(self.prev_step_ss, ss, self.out_ann,
+                [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]])
+            self.put(self.prev_step_ss, ss, self.out_ann,
+                [1, [pos_txt + ' ' + self.unit, pos_txt]])
+
+        self.pos += (1 if direction else -1)
+        self.prev_step_ss = ss
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        for (self.samplenum, (step, direction)) in data:
+            if step == 1 and self.oldstep == 0:
+                self.step(self.samplenum, direction)
+            self.oldstep = step
diff --git a/decoders/swd/__init__.py b/decoders/swd/__init__.py
new file mode 100644 (file)
index 0000000..3a65143
--- /dev/null
@@ -0,0 +1,35 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Angus Gratton <gus@projectgus.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
+##
+
+'''
+This PD decodes the ARM SWD (version 1) protocol, as described in the
+"ARM Debug Interface v5.2" Architecture Specification.
+
+Details:
+http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0031c/index.html
+(Registration required)
+
+Not supported:
+ * Turnaround periods other than the default 1, as set in DLCR.TURNROUND
+   (should be trivial to add)
+ * SWD protocol version 2 (multi-drop support, etc.)
+'''
+
+from .pd import Decoder
diff --git a/decoders/swd/pd.py b/decoders/swd/pd.py
new file mode 100644 (file)
index 0000000..d53d149
--- /dev/null
@@ -0,0 +1,349 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Angus Gratton <gus@projectgus.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
+##
+
+import sigrokdecode as srd
+import re
+
+'''
+OUTPUT_PYTHON format:
+
+Packet:
+[<ptype>, <pdata>]
+
+<ptype>:
+ - 'AP_READ' (AP read)
+ - 'DP_READ' (DP read)
+ - 'AP_WRITE' (AP write)
+ - 'DP_WRITE' (DP write)
+ - 'LINE_RESET' (line reset sequence)
+
+<pdata>:
+  - tuple of address, ack state, data for the given sequence
+'''
+
+swd_states = [
+    'IDLE', # Idle/unknown
+    'REQUEST', # Request phase (first 8 bits)
+    'ACK', # Ack phase (next 3 bits)
+    'READ', # Reading phase (next 32 bits for reads)
+    'WRITE', # Writing phase (next 32 bits for write)
+    'DPARITY', # Data parity phase
+]
+
+# Regexes for matching SWD data out of bitstring ('1' / '0' characters) format
+RE_SWDSWITCH = re.compile(bin(0xE79E)[:1:-1] + '$')
+RE_SWDREQ = re.compile(r'1(?P<apdp>.)(?P<rw>.)(?P<addr>..)(?P<parity>.)01$')
+RE_IDLE = re.compile('0' * 50 + '$')
+
+# Sample edges
+RISING = 1
+FALLING = 0
+
+ADDR_DP_SELECT = 0x8
+ADDR_DP_CTRLSTAT = 0x4
+
+BIT_SELECT_CTRLSEL = 1
+BIT_CTRLSTAT_ORUNDETECT = 1
+
+ANNOTATIONS = ['reset', 'enable', 'read', 'write', 'ack', 'data', 'parity']
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'swd'
+    name = 'SWD'
+    longname = 'Serial Wire Debug'
+    desc = 'Two-wire protocol for debug access to ARM CPUs.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['swd']
+    channels = (
+        {'id': 'swclk', 'name': 'SWCLK', 'desc': 'Master clock'},
+        {'id': 'swdio', 'name': 'SWDIO', 'desc': 'Data input/output'},
+    )
+    options = (
+        {'id': 'strict_start',
+         'desc': 'Wait for a line reset before starting to decode',
+         'default': 'no', 'values': ('yes', 'no')},
+    )
+    annotations = (
+        ('reset', 'RESET'),
+        ('enable', 'ENABLE'),
+        ('read', 'READ'),
+        ('write', 'WRITE'),
+        ('ack', 'ACK'),
+        ('data', 'DATA'),
+        ('parity', 'PARITY'),
+    )
+
+    def __init__(self, **kwargs):
+        # SWD data/clock state
+        self.state = 'UNKNOWN'
+        self.oldclk = -1
+        self.sample_edge = RISING
+        self.ack = None # Ack state of the current phase
+        self.ss_req = 0 # Start sample of current req
+        self.turnaround = 0 # Number of turnaround edges to ignore before continuing
+        self.bits = '' # Bits from SWDIO are accumulated here, matched against expected sequences
+        self.samplenums = [] # Sample numbers that correspond to the samples in self.bits
+        self.linereset_count = 0
+
+        # SWD debug port state
+        self.data = None
+        self.addr = None
+        self.rw = None # Are we inside an SWD read or a write?
+        self.ctrlsel = 0 # 'ctrlsel' is bit 0 in the SELECT register.
+        self.orundetect = 0 # 'orundetect' is bit 0 in the CTRLSTAT register.
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
+        if self.options['strict_start'] == 'no':
+            self.state = 'REQ' # No need to wait for a LINE RESET.
+
+    def putx(self, ann, length, data):
+        '''Output annotated data.'''
+        ann = ANNOTATIONS.index(ann)
+        try:
+            ss = self.samplenums[-length]
+        except IndexError:
+            ss = self.samplenums[0]
+        if self.state == 'REQ':
+            self.ss_req = ss
+        es = self.samplenum
+        self.put(ss, es, self.out_ann, [ann, [data]])
+
+    def putp(self, ptype, pdata):
+        self.put(self.ss_req, self.samplenum, self.out_python, [ptype, pdata])
+
+    def put_python_data(self):
+        '''Emit Python data item based on current SWD packet contents.'''
+        ptype = {
+            ('AP', 'R'): 'AP_READ',
+            ('AP', 'W'): 'AP_WRITE',
+            ('DP', 'R'): 'DP_READ',
+            ('DP', 'W'): 'DP_WRITE',
+        }[(self.apdp, self.rw)]
+        self.putp(ptype, (self.addr, self.data, self.ack))
+
+    def decode(self, ss, es, data):
+        for (self.samplenum, (clk, dio)) in data:
+            if clk == self.oldclk:
+                continue # Not a clock edge.
+            self.oldclk = clk
+
+            # Count rising edges with DIO held high,
+            # as a line reset (50+ high edges) can happen from any state.
+            if clk == RISING:
+                if dio == 1:
+                    self.linereset_count += 1
+                else:
+                    if self.linereset_count >= 50:
+                        self.putx('reset', self.linereset_count, 'LINERESET')
+                        self.putp('LINE_RESET', None)
+                        self.reset_state()
+                    self.linereset_count = 0
+
+            # Otherwise, we only care about either rising or falling edges
+            # (depending on sample_edge, set according to current state).
+            if clk != self.sample_edge:
+                continue
+
+            # Turnaround bits get skipped.
+            if self.turnaround > 0:
+                self.turnaround -= 1
+                continue
+
+            self.bits += str(dio)
+            self.samplenums.append(self.samplenum)
+            {
+                'UNKNOWN': self.handle_unknown_edge,
+                'REQ': self.handle_req_edge,
+                'ACK': self.handle_ack_edge,
+                'DATA': self.handle_data_edge,
+                'DPARITY': self.handle_dparity_edge,
+            }[self.state]()
+
+    def next_state(self):
+        '''Step to the next SWD state, reset internal counters accordingly.'''
+        self.bits = ''
+        self.samplenums = []
+        self.linereset_count = 0
+        if self.state == 'UNKNOWN':
+            self.state = 'REQ'
+            self.sample_edge = RISING
+            self.turnaround = 0
+        elif self.state == 'REQ':
+            self.state = 'ACK'
+            self.sample_edge = FALLING
+            self.turnaround = 1
+        elif self.state == 'ACK':
+            self.state = 'DATA'
+            self.sample_edge = RISING if self.rw == 'W' else FALLING
+            self.turnaround = 0 if self.rw == 'R' else 2
+        elif self.state == 'DATA':
+            self.state = 'DPARITY'
+        elif self.state == 'DPARITY':
+            self.put_python_data()
+            self.state = 'REQ'
+            self.sample_edge = RISING
+            self.turnaround = 1 if self.rw == 'R' else 0
+
+    def reset_state(self):
+        '''Line reset (or equivalent), wait for a new pending SWD request.'''
+        if self.state != 'REQ': # Emit a Python data item.
+            self.put_python_data()
+        # Clear state.
+        self.bits = ''
+        self.samplenums = []
+        self.linereset_count = 0
+        self.turnaround = 0
+        self.sample_edge = RISING
+        self.data = ''
+        self.ack = None
+        self.state = 'REQ'
+
+    def handle_unknown_edge(self):
+        '''
+        Clock edge in the UNKNOWN state.
+        In the unknown state, clock edges get ignored until we see a line
+        reset (which is detected in the decode method, not here.)
+        '''
+        pass
+
+    def handle_req_edge(self):
+        '''Clock edge in the REQ state (waiting for SWD r/w request).'''
+        # Check for a JTAG->SWD enable sequence.
+        m = re.search(RE_SWDSWITCH, self.bits)
+        if m is not None:
+            self.putx('enable', 16, 'JTAG->SWD')
+            self.reset_state()
+            return
+
+        # Or a valid SWD Request packet.
+        m = re.search(RE_SWDREQ, self.bits)
+        if m is not None:
+            calc_parity = sum([int(x) for x in m.group('rw') + m.group('apdp') + m.group('addr')]) % 2
+            parity = '' if str(calc_parity) == m.group('parity') else 'E'
+            self.rw = 'R' if m.group('rw') == '1' else 'W'
+            self.apdp = 'AP' if m.group('apdp') == '1' else 'DP'
+            self.addr = int(m.group('addr')[::-1], 2) << 2
+            self.putx('read' if self.rw == 'R' else 'write', 8, self.get_address_description())
+            self.next_state()
+            return
+
+    def handle_ack_edge(self):
+        '''Clock edge in the ACK state (waiting for complete ACK sequence).'''
+        if len(self.bits) < 3:
+            return
+        if self.bits == '100':
+            self.putx('ack', 3, 'OK')
+            self.ack = 'OK'
+            self.next_state()
+        elif self.bits == '001':
+            self.putx('ack', 3, 'FAULT')
+            self.ack = 'FAULT'
+            if self.orundetect == 1:
+                self.next_state()
+            else:
+                self.reset_state()
+            self.turnaround = 1
+        elif self.bits == '010':
+            self.putx('ack', 3, 'WAIT')
+            self.ack = 'WAIT'
+            if self.orundetect == 1:
+                self.next_state()
+            else:
+                self.reset_state()
+            self.turnaround = 1
+        elif self.bits == '111':
+            self.putx('ack', 3, 'NOREPLY')
+            self.ack = 'NOREPLY'
+            self.reset_state()
+        else:
+            self.putx('ack', 3, 'ERROR')
+            self.ack = 'ERROR'
+            self.reset_state()
+
+    def handle_data_edge(self):
+        '''Clock edge in the DATA state (waiting for 32 bits to clock past).'''
+        if len(self.bits) < 32:
+            return
+        self.data = 0
+        self.dparity = 0
+        for x in range(32):
+            if self.bits[x] == '1':
+                self.data += (1 << x)
+                self.dparity += 1
+        self.dparity = self.dparity % 2
+
+        self.putx('data', 32, '0x%08x' % self.data)
+        self.next_state()
+
+    def handle_dparity_edge(self):
+        '''Clock edge in the DPARITY state (clocking in parity bit).'''
+        if str(self.dparity) != self.bits:
+            self.putx('parity', 1, str(self.dparity) + self.bits) # PARITY ERROR
+        elif self.rw == 'W':
+            self.handle_completed_write()
+        self.next_state()
+
+    def handle_completed_write(self):
+        '''
+        Update internal state of the debug port based on a completed
+        write operation.
+        '''
+        if self.apdp != 'DP':
+            return
+        elif self.addr == ADDR_DP_SELECT:
+            self.ctrlsel = self.data & BIT_SELECT_CTRLSEL
+        elif self.addr == ADDR_DP_CTRLSTAT and self.ctrlsel == 0:
+            self.orundetect = self.data & BIT_CTRLSTAT_ORUNDETECT
+
+    def get_address_description(self):
+        '''
+        Return a human-readable description of the currently selected address,
+        for annotated results.
+        '''
+        if self.apdp == 'DP':
+            if self.rw == 'R':
+                # Tables 2-4 & 2-5 in ADIv5.2 spec ARM document IHI 0031C
+                return {
+                    0: 'IDCODE',
+                    0x4: 'R CTRL/STAT' if self.ctrlsel == 0 else 'R DLCR',
+                    0x8: 'RESEND',
+                    0xC: 'RDBUFF'
+                }[self.addr]
+            elif self.rw == 'W':
+                # Tables 2-4 & 2-5 in ADIv5.2 spec ARM document IHI 0031C
+                return {
+                    0: 'W ABORT',
+                    0x4: 'W CTRL/STAT' if self.ctrlsel == 0 else 'W DLCR',
+                    0x8: 'W SELECT',
+                    0xC: 'W RESERVED'
+                }[self.addr]
+        elif self.apdp == 'AP':
+            if self.rw == 'R':
+                return 'R AP%x' % self.addr
+            elif self.rw == 'W':
+                return 'W AP%x' % self.addr
+
+        # Any legitimate operations shouldn't fall through to here, probably
+        # a decoder bug.
+        return '? %s%s%x' % (self.rw, self.apdp, self.addr)
diff --git a/decoders/tca6408a/__init__.py b/decoders/tca6408a/__init__.py
new file mode 100644 (file)
index 0000000..f679674
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 alberink <alberink@stampfini.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'i2c' PD and decodes the Texas Instruments
+TCA6408A 8-bit I²C I/O expander protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/tca6408a/pd.py b/decoders/tca6408a/pd.py
new file mode 100644 (file)
index 0000000..1f90245
--- /dev/null
@@ -0,0 +1,129 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2013 Matt Ranostay <mranostay@gmail.com>
+## Copyright (C) 2014 alberink <alberink@stampfini.org>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'tca6408a'
+    name = 'TI TCA6408A'
+    longname = 'Texas Instruments TCA6408A'
+    desc = 'Texas Instruments TCA6408A 8-bit I²C I/O expander.'
+    license = 'gplv2+'
+    inputs = ['i2c']
+    outputs = ['tca6408a']
+    annotations = (
+        ('register', 'Register type'),
+        ('value', 'Register value'),
+        ('warnings', 'Warning messages'),
+    )
+    annotation_rows = (
+        ('regs', 'Registers', (0, 1)),
+        ('warnings', 'Warnings', (2,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.state = 'IDLE'
+        self.chip = -1
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def handle_reg_0x00(self, b):
+        self.putx([1, ['State of inputs: %02X' % b]])
+
+    def handle_reg_0x01(self, b):
+        self.putx([1, ['Outputs set: %02X' % b ]])
+
+    def handle_reg_0x02(self, b):
+        self.putx([1, ['Polarity inverted: %02X' % b]])
+
+    def handle_reg_0x03(self, b):
+        self.putx([1, ['Configuration: %02X' % b]])
+
+    def handle_write_reg(self, b):
+        if b == 0:
+            self.putx([0, ['Input port', 'In', 'I']])
+        elif b == 1:
+            self.putx([0, ['Output port', 'Out', 'O']])
+        elif b == 2:
+            self.putx([0, ['Polarity inversion register', 'Pol', 'P']])
+        elif b == 3:
+            self.putx([0, ['Configuration register', 'Conf', 'C']])
+
+    def check_correct_chip(self, addr):
+        if addr not in (0x20, 0x21):
+            self.putx([2, ['Warning: I²C slave 0x%02X not a TCA6408A '
+                           'compatible chip.' % addr]])
+            self.state = 'IDLE'
+
+    def decode(self, ss, es, data):
+        cmd, databyte = data
+
+        # Store the start/end samples of this I²C packet.
+        self.ss, self.es = ss, es
+
+        # State machine.
+        if self.state == 'IDLE':
+            # Wait for an I²C START condition.
+            if cmd != 'START':
+                return
+            self.state = 'GET SLAVE ADDR'
+        elif self.state == 'GET SLAVE ADDR':
+            self.chip = databyte  
+            self.state = 'GET REG ADDR'
+        elif self.state == 'GET REG ADDR':
+            # Wait for a data write (master selects the slave register).
+            if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
+                self.check_correct_chip(databyte)
+            if cmd != 'DATA WRITE':
+                return
+            self.reg = databyte
+            self.handle_write_reg(self.reg)
+            self.state = 'WRITE IO REGS'
+        elif self.state == 'WRITE IO REGS':
+            # If we see a Repeated Start here, the master wants to read.
+            if cmd == 'START REPEAT':
+                self.state = 'READ IO 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)
+            elif cmd == 'STOP':
+                self.state = 'IDLE'
+                self.chip = -1
+        elif self.state == 'READ IO REGS':
+            # Wait for an address read operation.
+            if cmd == 'ADDRESS READ':
+                self.state = 'READ IO REGS2'
+                self.chip = databyte
+                return
+        elif self.state == 'READ IO REGS2':
+            if cmd == 'DATA READ':
+                handle_reg = getattr(self, 'handle_reg_0x%02x' % self.reg)
+                handle_reg(databyte)
+            elif cmd == 'STOP':
+                self.state = 'IDLE'
diff --git a/decoders/timing/__init__.py b/decoders/timing/__init__.py
new file mode 100644 (file)
index 0000000..ee31509
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Torsten Duwe <duwe@suse.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
+##
+
+'''
+Timing decoder, find the time between edges.
+'''
+
+from .pd import Decoder
diff --git a/decoders/timing/pd.py b/decoders/timing/pd.py
new file mode 100644 (file)
index 0000000..6bca120
--- /dev/null
@@ -0,0 +1,94 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Torsten Duwe <duwe@suse.de>
+## Copyright (C) 2014 Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.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
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+def normalize_time(t):
+    if t >= 1.0:
+        return '%.3f s' % t
+    elif t >= 0.001:
+        return '%.3f ms' % (t * 1000.0)
+    elif t >= 0.000001:
+        return '%.3f μs' % (t * 1000.0 * 1000.0)
+    elif t >= 0.000000001:
+        return '%.3f ns' % (t * 1000.0 * 1000.0 * 1000.0)
+    else:
+        return '%f' % t
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'timing'
+    name = 'Timing'
+    longname = 'Timing calculation'
+    desc = 'Calculate time between edges.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['timing']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    annotations = (
+        ('time', 'Time'),
+    )
+    annotation_rows = (
+        ('time', 'Time', (0,)),
+    )
+
+    def __init__(self, **kwargs):
+        self.samplerate = None
+        self.oldpin = None
+        self.last_samplenum = None
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        for (samplenum, (pin,)) in data:
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpin == pin:
+                continue
+
+            if self.oldpin is None:
+                self.oldpin = pin
+                self.last_samplenum = samplenum
+                continue
+
+            if self.oldpin != pin:
+                samples = samplenum - self.last_samplenum
+                t = samples / self.samplerate
+
+                # Report the timing normalized.
+                self.put(self.last_samplenum, samplenum, self.out_ann,
+                         [0, [normalize_time(t)]])
+
+                # Store data for next round.
+                self.last_samplenum = samplenum
+                self.oldpin = pin
index c6157600db2e46d390df40f5d3f3279144451ba5..cb953853c64e5e682c408e4a58df77d4b904fd3e 100644 (file)
@@ -22,5 +22,4 @@
 The Texas Instruments TLC5620 is an 8-bit quad DAC.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index f8c90d96b52c05c818bdd89d949edf4e75174ea9..2a407d1687cc288a15349544a94107af5f1518fe 100644 (file)
@@ -65,7 +65,7 @@ class Decoder(srd.Decoder):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
     def handle_11bits(self):
-        s = "".join(str(i) for i in self.bits[:2])
+        s = ''.join(str(i) for i in self.bits[:2])
         self.dac_select = s = dacs[int(s, 2)]
         self.put(self.ss_dac, self.es_dac, self.out_ann,
                  [0, ['DAC select: %s' % s, 'DAC sel: %s' % s,
@@ -75,7 +75,7 @@ class Decoder(srd.Decoder):
         self.put(self.ss_gain, self.es_gain, self.out_ann,
                  [1, ['Gain: x%d' % g, 'G: x%d' % g, 'x%d' % g]])
 
-        s = "".join(str(i) for i in self.bits[3:])
+        s = ''.join(str(i) for i in self.bits[3:])
         self.dac_value = v = int(s, 2)
         self.put(self.ss_value, self.es_value, self.out_ann,
                  [2, ['DAC value: %d' % v, 'Value: %d' % v, 'Val: %d' % v,
@@ -128,4 +128,3 @@ class Decoder(srd.Decoder):
             self.oldclk = clk
             self.oldload = load
             self.oldldac = ldac
-
index f3c0693a9923a7e37ea9c1e2cf0a9d717db0d26e..efe0e523a8954df2696dadb0ee8829ada823d607 100644 (file)
@@ -38,5 +38,4 @@ it though, no matter whether the source was on TTL UART levels, or RS232,
 or others.
 '''
 
-from .pd import *
-
+from .pd import Decoder
index ae99874c8f48762cfb10396d22e96bc03dcf09d4..7995e9979674833c56b4fd571fafccd9a1621242 100644 (file)
@@ -19,6 +19,7 @@
 ##
 
 import sigrokdecode as srd
+from math import floor, ceil
 
 '''
 OUTPUT_PYTHON format:
@@ -28,9 +29,10 @@ Packet:
 
 This is the list of <ptype>s and their respective <pdata> values:
  - 'STARTBIT': The data is the (integer) value of the start bit (0/1).
- - 'DATA': The data is the (integer) value of the UART data. Valid values
-   range from 0 to 512 (as the data can be up to 9 bits in size).
- - 'DATABITS': List of data bits and their ss/es numbers.
+ - 'DATA': This is always a tuple containing two items:
+   - 1st item: the (integer) value of the UART data. Valid values
+     range from 0 to 512 (as the data can be up to 9 bits in size).
+   - 2nd item: the list of individual data bits and their ss/es numbers.
  - 'PARITYBIT': The data is the (integer) value of the parity bit (0/1).
  - 'STOPBIT': The data is the (integer) value of the stop bit (0 or 1).
  - 'INVALID STARTBIT': The data is the (integer) value of the start bit (0/1).
@@ -66,8 +68,12 @@ def parity_ok(parity_type, parity_bit, data, num_data_bits):
         return (ones % 2) == 1
     elif parity_type == 'even':
         return (ones % 2) == 0
-    else:
-        raise Exception('Invalid parity type: %d' % parity_type)
+
+class SamplerateError(Exception):
+    pass
+
+class ChannelError(Exception):
+    pass
 
 class Decoder(srd.Decoder):
     api_version = 2
@@ -98,7 +104,10 @@ class Decoder(srd.Decoder):
             'values': ('lsb-first', 'msb-first')},
         {'id': 'format', 'desc': 'Data format', 'default': 'ascii',
             'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
-        # TODO: Options to invert the signal(s).
+        {'id': 'invert_rx', 'desc': 'Invert RX?', 'default': 'no',
+            'values': ('yes', 'no')},
+        {'id': 'invert_tx', 'desc': 'Invert TX?', 'default': 'no',
+            'values': ('yes', 'no')},
     )
     annotations = (
         ('rx-data', 'RX data'),
@@ -131,24 +140,24 @@ class Decoder(srd.Decoder):
     )
 
     def putx(self, rxtx, data):
-        s, halfbit = self.startsample[rxtx], int(self.bit_width / 2)
-        self.put(s - halfbit, self.samplenum + halfbit, self.out_ann, data)
+        s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
+        self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
 
     def putpx(self, rxtx, data):
-        s, halfbit = self.startsample[rxtx], int(self.bit_width / 2)
-        self.put(s - halfbit, self.samplenum + halfbit, self.out_python, data)
+        s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
+        self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_python, data)
 
     def putg(self, data):
-        s, halfbit = self.samplenum, int(self.bit_width / 2)
-        self.put(s - halfbit, s + halfbit, self.out_ann, data)
+        s, halfbit = self.samplenum, self.bit_width / 2.0
+        self.put(s - floor(halfbit), s + ceil(halfbit), self.out_ann, data)
 
     def putp(self, data):
-        s, halfbit = self.samplenum, int(self.bit_width / 2)
-        self.put(s - halfbit, s + halfbit, self.out_python, data)
+        s, halfbit = self.samplenum, self.bit_width / 2.0
+        self.put(s - floor(halfbit), s + ceil(halfbit), self.out_python, data)
 
     def putbin(self, rxtx, data):
-        s, halfbit = self.startsample[rxtx], int(self.bit_width / 2)
-        self.put(s - halfbit, self.samplenum + halfbit, self.out_bin, data)
+        s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
+        self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_bin, data)
 
     def __init__(self, **kwargs):
         self.samplerate = None
@@ -172,7 +181,7 @@ class Decoder(srd.Decoder):
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
-            self.samplerate = value;
+            self.samplerate = value
             # The width of one UART bit in number of samples.
             self.bit_width = float(self.samplerate) / float(self.options['baudrate'])
 
@@ -181,7 +190,9 @@ class Decoder(srd.Decoder):
         # bitpos is the samplenumber which is in the middle of the
         # specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit
         # (if used) or the first stop bit, and so on).
-        bitpos = self.frame_start[rxtx] + (self.bit_width / 2.0)
+        # The samples within bit are 0, 1, ..., (bit_width - 1), therefore
+        # index of the middle sample within bit window is (bit_width - 1) / 2.
+        bitpos = self.frame_start[rxtx] + (self.bit_width - 1) / 2.0
         bitpos += bitnum * self.bit_width
         if self.samplenum >= bitpos:
             return True
@@ -239,12 +250,9 @@ class Decoder(srd.Decoder):
             self.databyte[rxtx] >>= 1
             self.databyte[rxtx] |= \
                 (signal << (self.options['num_data_bits'] - 1))
-        elif self.options['bit_order'] == 'msb-first':
+        else:
             self.databyte[rxtx] <<= 1
             self.databyte[rxtx] |= (signal << 0)
-        else:
-            raise Exception('Invalid bit order value: %s',
-                            self.options['bit_order'])
 
         self.putg([rxtx + 12, ['%d' % signal]])
 
@@ -259,8 +267,8 @@ class Decoder(srd.Decoder):
 
         self.state[rxtx] = 'GET PARITY BIT'
 
-        self.putpx(rxtx, ['DATABITS', rxtx, self.databits[rxtx]])
-        self.putpx(rxtx, ['DATA', rxtx, self.databyte[rxtx]])
+        self.putpx(rxtx, ['DATA', rxtx,
+            (self.databyte[rxtx], self.databits[rxtx])])
 
         b, f = self.databyte[rxtx], self.options['format']
         if f == 'ascii':
@@ -274,8 +282,6 @@ class Decoder(srd.Decoder):
             self.putx(rxtx, [rxtx, [oct(b)[2:].zfill(3)]])
         elif f == 'bin':
             self.putx(rxtx, [rxtx, [bin(b)[2:].zfill(8)]])
-        else:
-            raise Exception('Invalid data format option: %s' % f)
 
         self.putbin(rxtx, (rxtx, bytes([b])))
         self.putbin(rxtx, (2, bytes([b])))
@@ -327,8 +333,8 @@ class Decoder(srd.Decoder):
         self.putg([rxtx + 4, ['Stop bit', 'Stop', 'T']])
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
 
             # Note: Ignoring identical samples here for performance reasons
@@ -337,10 +343,15 @@ class Decoder(srd.Decoder):
             #     continue
             self.oldpins, (rx, tx) = pins, pins
 
+            if self.options['invert_rx'] == 'yes':
+                rx = not rx
+            if self.options['invert_tx'] == 'yes':
+                tx = not tx
+
             # Either RX or TX (but not both) can be omitted.
             has_pin = [rx in (0, 1), tx in (0, 1)]
             if has_pin == [False, False]:
-                raise Exception('Either TX or RX (or both) pins required.')
+                raise ChannelError('Either TX or RX (or both) pins required.')
 
             # State machine.
             for rxtx in (RX, TX):
@@ -360,9 +371,6 @@ class Decoder(srd.Decoder):
                     self.get_parity_bit(rxtx, signal)
                 elif self.state[rxtx] == 'GET STOP BITS':
                     self.get_stop_bits(rxtx, signal)
-                else:
-                    raise Exception('Invalid state: %s' % self.state[rxtx])
 
                 # Save current RX/TX values for the next round.
                 self.oldbit[rxtx] = signal
-
index 06b67b9ddede960cd3aa51769f3e551e6488d42d..f841d20b277098e304b5f62b22674be995c9d4e9 100644 (file)
@@ -41,5 +41,4 @@ https://en.wikipedia.org/wiki/USB
 http://www.usb.org/developers/docs/
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 8f86cc72aed0f604499a25d78e36937441f3a541..a9c8b252eb27f01e9b1967530c9ef7baaa9fd640 100644 (file)
@@ -192,7 +192,6 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
-        self.samplenum = 0
         self.bits = []
         self.packet = []
         self.packet_summary = ''
@@ -338,6 +337,3 @@ class Decoder(srd.Decoder):
                 self.bits, self.state = [], 'WAIT FOR SOP'
             else:
                 pass # TODO: Error
-        else:
-            raise Exception('Invalid state: %s' % self.state)
-
index 2311b1f3c5af73376d0dcea41c83a178d3beecbf..b2ceb4d69b9e6c6dcd19621825f59bd77c370aff 100644 (file)
@@ -48,5 +48,4 @@ https://en.wikipedia.org/wiki/USB
 http://www.usb.org/developers/docs/
 '''
 
-from .pd import *
-
+from .pd import Decoder
index 8c6a9c30dc1043da64d9ae19788996992e657ab6..8ba55d90cae07ce5333e892adf94eb5aeb49a27b 100644 (file)
@@ -73,6 +73,9 @@ sym_idx = {
     'SE1': 3,
 }
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'usb_signalling'
@@ -217,8 +220,8 @@ class Decoder(srd.Decoder):
         self.oldsym = sym
 
     def decode(self, ss, es, data):
-        if self.samplerate is None:
-            raise Exception("Cannot decode without samplerate.")
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
         for (self.samplenum, pins) in data:
             # State machine.
             if self.state == 'IDLE':
@@ -237,6 +240,3 @@ class Decoder(srd.Decoder):
                     self.get_bit(sym)
                 elif self.state == 'GET EOP':
                     self.get_eop(sym)
-            else:
-                raise Exception('Invalid state: %s' % self.state)
-
index fa4e8a230c06e2452cfe4b092ab84ff8c50cda7d..51936e5537052eb23837c420cbd556be2a9cc85e 100644 (file)
@@ -37,5 +37,4 @@ The XFP specification is available here:
   ftp://ftp.seagate.com/sff/INF-8077.PDF
 '''
 
-from .pd import *
-
+from .pd import Decoder
index b8ec8edb22920cc2fbd012dcf952d7535c137efa..41aca0dbb4ed3f67d4de5b7d28cd295925bc176d 100644 (file)
@@ -318,7 +318,7 @@ class Decoder(srd.Decoder):
                 self.annotate("Vendor ID", chr(data[i]), cnt, cnt)
 
     # Convert 16-bit two's complement values, with each increment
-    # representing 1/256C, to degrees Celcius.
+    # representing 1/256C, to degrees Celsius.
     def to_temp(self, value):
         if value & 0x8000:
             value = -((value ^ 0xffff) + 1)
@@ -644,4 +644,3 @@ class Decoder(srd.Decoder):
         self.annotate("AUX1 monitoring", aux)
         aux = AUX_TYPES[data[0] & 0x0f]
         self.annotate("AUX2 monitoring", aux)
-