diff --git a/libsigrokdecode4DSL/decoders/ade77xx/__init__.py b/libsigrokdecode4DSL/decoders/ade77xx/__init__.py
index cbe8689d..7da0419a 100644
--- a/libsigrokdecode4DSL/decoders/ade77xx/__init__.py
+++ b/libsigrokdecode4DSL/decoders/ade77xx/__init__.py
@@ -14,8 +14,7 @@
## 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
+## along with this program; if not, see .
##
'''
diff --git a/libsigrokdecode4DSL/decoders/ade77xx/pd.py b/libsigrokdecode4DSL/decoders/ade77xx/pd.py
index 74219eee..5421cf47 100644
--- a/libsigrokdecode4DSL/decoders/ade77xx/pd.py
+++ b/libsigrokdecode4DSL/decoders/ade77xx/pd.py
@@ -36,13 +36,13 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['Analog/digital', 'IC', 'Sensor']
annotations = (
- ('read', 'Register read commands'),
- ('write', 'Register write commands'),
- ('warning', 'Warnings'),
+ ('read', 'Register read'),
+ ('write', 'Register write'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('read', 'Read', (0,)),
- ('write', 'Write', (1,)),
+ ('reads', 'Reads', (0,)),
+ ('writes', 'Writes', (1,)),
('warnings', 'Warnings', (2,)),
)
@@ -124,8 +124,8 @@ def decode(self, ss, es, data):
vali = self.miso_bytes[1]
if write:
- self.putx([1, ['%s: {$}' % rblob[0], '@%02X' % valo]])
+ self.putx([1, ['%s: %#x' % (rblob[0], valo)]])
else:
- self.putx([0, ['%s: {$}' % rblob[0], '@%02X' % valo]])
+ self.putx([0, ['%s: %#x' % (rblob[0], vali)]])
self.reset_data()
diff --git a/libsigrokdecode4DSL/decoders/adf435x/pd.py b/libsigrokdecode4DSL/decoders/adf435x/pd.py
index f6c6e6e0..c60ed4e4 100644
--- a/libsigrokdecode4DSL/decoders/adf435x/pd.py
+++ b/libsigrokdecode4DSL/decoders/adf435x/pd.py
@@ -99,10 +99,10 @@ class Decoder(srd.Decoder):
tags = ['Clock/timing', 'IC', 'Wireless/RF']
annotations = (
# Sent from the host to the chip.
- ('register', 'Register written to the device'),
+ ('write', 'Register write'),
)
annotation_rows = (
- ('registers', 'Register writes', (ANN_REG,)),
+ ('writes', 'Register writes', (ANN_REG,)),
)
def __init__(self):
diff --git a/libsigrokdecode4DSL/decoders/adns5020/pd.py b/libsigrokdecode4DSL/decoders/adns5020/pd.py
index e25aa5a6..b03fcaa6 100644
--- a/libsigrokdecode4DSL/decoders/adns5020/pd.py
+++ b/libsigrokdecode4DSL/decoders/adns5020/pd.py
@@ -49,13 +49,13 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['IC', 'PC', 'Sensor']
annotations = (
- ('read', 'Register read commands'),
- ('write', 'Register write commands'),
- ('warning', 'Warnings'),
+ ('read', 'Register read'),
+ ('write', 'Register write'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('read', 'Read', (0,)),
- ('write', 'Write', (1,)),
+ ('reads', 'Reads', (0,)),
+ ('writes', 'Writes', (1,)),
('warnings', 'Warnings', (2,)),
)
@@ -108,10 +108,9 @@ def decode(self, ss, es, data):
reg_desc = regs.get(reg, 'Reserved %#x' % reg)
if reg > 0x63:
reg_desc = 'Unknown'
-
if write:
- self.putx([1, ['%s: {$}' % reg_desc, '@%02X' % arg]])
+ self.putx([1, ['%s: %#x' % (reg_desc, arg)]])
else:
- self.putx([0, ['%s: {$}' % reg_desc, '@%02X' % arg]])
+ self.putx([0, ['%s: %d' % (reg_desc, arg)]])
self.mosi_bytes = []
diff --git a/libsigrokdecode4DSL/decoders/adxl345/pd.py b/libsigrokdecode4DSL/decoders/adxl345/pd.py
index 8ccb6cad..2d53e4c0 100644
--- a/libsigrokdecode4DSL/decoders/adxl345/pd.py
+++ b/libsigrokdecode4DSL/decoders/adxl345/pd.py
@@ -410,7 +410,8 @@ def decode(self, ss, es, data):
self.address <<= 1
self.address >>= 1
self.put(start_sample, addr_bit[2], self.out_ann,
- [Ann.REG_ADDRESS, ['ADDRESS: {$}', 'ADDR: {$}', '{$}', '@%02X' % self.address]])
+ [Ann.REG_ADDRESS, ['ADDRESS: 0x%02X' % self.address, 'ADDR: 0x%02X'
+ % self.address, '0x%02X' % self.address]])
self.ss = -1
self.state = St.DATA
diff --git a/libsigrokdecode4DSL/decoders/am230x/pd.py b/libsigrokdecode4DSL/decoders/am230x/pd.py
index fbc68d39..eedf9428 100644
--- a/libsigrokdecode4DSL/decoders/am230x/pd.py
+++ b/libsigrokdecode4DSL/decoders/am230x/pd.py
@@ -56,8 +56,8 @@ class Decoder(srd.Decoder):
('bit', 'Bit'),
('end', 'End'),
('byte', 'Byte'),
- ('humidity', 'Relative humidity in percent'),
- ('temperature', 'Temperature in degrees Celsius'),
+ ('humidity', 'Relative humidity'),
+ ('temperature', 'Temperature'),
('checksum', 'Checksum'),
)
annotation_rows = (
diff --git a/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py b/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py
index 6649b46e..a0bbd94c 100644
--- a/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py
+++ b/libsigrokdecode4DSL/decoders/arm_etmv3/pd.py
@@ -138,26 +138,26 @@ class Decoder(srd.Decoder):
tags = ['Debug/trace']
annotations = (
('trace', 'Trace info'),
- ('branch', 'Branches'),
- ('exception', 'Exceptions'),
+ ('branch', 'Branch'),
+ ('exception', 'Exception'),
('execution', 'Instruction execution'),
('data', 'Data access'),
('pc', 'Program counter'),
- ('instr_e', 'Executed instructions'),
- ('instr_n', 'Not executed instructions'),
+ ('instr_e', 'Executed instruction'),
+ ('instr_n', 'Not executed instruction'),
('source', 'Source code'),
('location', 'Current location'),
('function', 'Current function'),
)
annotation_rows = (
- ('trace', 'Trace info', (0,)),
+ ('traces', '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,)),
+ ('data-vals', 'Data access', (4,)),
+ ('pc-vals', 'Program counters', (5,)),
+ ('instructions', 'Instructions', (6, 7,)),
+ ('sources', 'Source code', (8,)),
+ ('locations', 'Current locations', (9,)),
+ ('functions', 'Current functions', (10,)),
)
options = (
{'id': 'objdump', 'desc': 'objdump path',
diff --git a/libsigrokdecode4DSL/decoders/arm_itm/pd.py b/libsigrokdecode4DSL/decoders/arm_itm/pd.py
index 64149787..8ceeac46 100644
--- a/libsigrokdecode4DSL/decoders/arm_itm/pd.py
+++ b/libsigrokdecode4DSL/decoders/arm_itm/pd.py
@@ -55,7 +55,7 @@ class Decoder(srd.Decoder):
'default': ''},
)
annotations = (
- ('trace', 'Trace information'),
+ ('trace', 'Trace info'),
('timestamp', 'Timestamp'),
('software', 'Software message'),
('dwt_event', 'DWT event'),
@@ -69,15 +69,15 @@ class Decoder(srd.Decoder):
('function', 'Current function'),
)
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,)),
- ('function', 'Current function', (11,)),
+ ('traces', 'Trace info', (0, 1)),
+ ('softwares', 'Software traces', (2,)),
+ ('dwt_events', 'DWT events', (3,)),
+ ('dwt_watchpoints', 'DWT watchpoints', (4,)),
+ ('dwt_excs', 'Exception traces', (5,)),
+ ('dwt_pcs', 'Program counters', (6,)),
+ ('modes', 'Current modes', (7, 8, 9)),
+ ('locations', 'Current locations', (10,)),
+ ('functions', 'Current functions', (11,)),
)
def __init__(self):
diff --git a/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py b/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py
index 29b4605f..eac932fe 100644
--- a/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py
+++ b/libsigrokdecode4DSL/decoders/arm_tpiu/pd.py
@@ -38,8 +38,8 @@ class Decoder(srd.Decoder):
('data', 'Stream data'),
)
annotation_rows = (
- ('stream', 'Current stream', (0,)),
- ('data', 'Stream data', (1,)),
+ ('streams', 'Current streams', (0,)),
+ ('data-vals', 'Stream data', (1,)),
)
def __init__(self):
diff --git a/libsigrokdecode4DSL/decoders/avr_isp/pd.py b/libsigrokdecode4DSL/decoders/avr_isp/pd.py
index a0719b73..e3af4d6f 100644
--- a/libsigrokdecode4DSL/decoders/avr_isp/pd.py
+++ b/libsigrokdecode4DSL/decoders/avr_isp/pd.py
@@ -20,6 +20,10 @@
import sigrokdecode as srd
from .parts import *
+class Ann:
+ PE, RSB0, RSB1, RSB2, CE, RFB, RHFB, REFB, \
+ RLB, REEM, RP, LPMP, WP, WARN, DEV, = range(15)
+
VENDOR_CODE_ATMEL = 0x1e
class Decoder(srd.Decoder):
@@ -41,14 +45,20 @@ class Decoder(srd.Decoder):
('rfb', 'Read fuse bits'),
('rhfb', 'Read high fuse bits'),
('refb', 'Read extended fuse bits'),
- ('warnings', 'Warnings'),
+ ('rlb', 'Read lock bits'),
+ ('reem', 'Read EEPROM memory'),
+ ('rp', 'Read program memory'),
+ ('lpmp' , 'Load program memory page'),
+ ('wp', 'Write program memory'),
+ ('warning', 'Warning'),
('dev', 'Device'),
)
annotation_rows = (
- ('bits', 'Bits', ()),
- ('commands', 'Commands', tuple(range(7 + 1))),
- ('warnings', 'Warnings', (8,)),
- ('dev', 'Device', (9,)),
+ ('commands', 'Commands', (Ann.PE, Ann.RSB0, Ann.RSB1, Ann.RSB2,
+ Ann.CE, Ann.RFB, Ann.RHFB, Ann.REFB,
+ Ann.RLB, Ann.REEM, Ann.RP, Ann.LPMP, Ann.WP,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ ('devs', 'Devices', (Ann.DEV,)),
)
def __init__(self):
@@ -70,17 +80,17 @@ def putx(self, data):
def handle_cmd_programming_enable(self, cmd, ret):
# Programming enable.
# Note: The chip doesn't send any ACK for 'Programming enable'.
- self.putx([0, ['Programming enable']])
+ self.putx([Ann.PE, ['Programming enable']])
# Sanity check on reply.
if ret[1:4] != [0xac, 0x53, cmd[2]]:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
def handle_cmd_read_signature_byte_0x00(self, cmd, ret):
# Signature byte 0x00: vendor code.
self.vendor_code = ret[3]
v = vendor_code[self.vendor_code]
- self.putx([1, ['Vendor code: 0x%02x (%s)' % (ret[3], v)]])
+ self.putx([Ann.RSB0, ['Vendor code: 0x%02x (%s)' % (ret[3], v)]])
# Store for later.
self.xx = cmd[1] # Same as ret[2].
@@ -89,16 +99,16 @@ def handle_cmd_read_signature_byte_0x00(self, cmd, ret):
# Sanity check on reply.
if ret[1] != 0x30 or ret[2] != cmd[1]:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
# Sanity check for the vendor code.
if self.vendor_code != VENDOR_CODE_ATMEL:
- self.putx([8, ['Warning: Vendor code was not 0x1e (Atmel)!']])
+ self.putx([Ann.WARN, ['Warning: Vendor code was not 0x1e (Atmel)!']])
def handle_cmd_read_signature_byte_0x01(self, cmd, ret):
# Signature byte 0x01: part family and memory size.
self.part_fam_flash_size = ret[3]
- self.putx([2, ['Part family / memory size: 0x%02x' % ret[3]]])
+ self.putx([Ann.RSB1, ['Part family / memory size: 0x%02x' % ret[3]]])
# Store for later.
self.mm = cmd[3]
@@ -106,20 +116,20 @@ def handle_cmd_read_signature_byte_0x01(self, cmd, ret):
# Sanity check on reply.
if ret[1] != 0x30 or ret[2] != cmd[1] or ret[0] != self.yy:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
def handle_cmd_read_signature_byte_0x02(self, cmd, ret):
# Signature byte 0x02: part number.
self.part_number = ret[3]
- self.putx([3, ['Part number: 0x%02x' % ret[3]]])
+ self.putx([Ann.RSB2, ['Part number: 0x%02x' % ret[3]]])
p = part[(self.part_fam_flash_size, self.part_number)]
- data = [9, ['Device: Atmel %s' % p]]
+ data = [Ann.DEV, ['Device: Atmel %s' % p]]
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:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
self.xx, self.yy, self.zz, self.mm = 0, 0, 0, 0
@@ -127,36 +137,78 @@ def handle_cmd_chip_erase(self, cmd, ret):
# Chip erase (erases both flash an EEPROM).
# Upon successful chip erase, the lock bits will also be erased.
# The only way to end a Chip Erase cycle is to release RESET#.
- self.putx([4, ['Chip erase']])
+ self.putx([Ann.CE, ['Chip erase']])
# TODO: Check/handle RESET#.
# Sanity check on reply.
bit = (ret[2] & (1 << 7)) >> 7
if ret[1] != 0xac or bit != 1 or ret[3] != cmd[2]:
- self.putx([8, ['Warning: Unexpected bytes in reply!']])
+ self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
def handle_cmd_read_fuse_bits(self, cmd, ret):
# Read fuse bits.
- self.putx([5, ['Read fuse bits: 0x%02x' % ret[3]]])
+ self.putx([Ann.RFB, ['Read fuse bits: 0x%02x' % ret[3]]])
# TODO: Decode fuse bits.
# TODO: Sanity check on reply.
def handle_cmd_read_fuse_high_bits(self, cmd, ret):
# Read fuse high bits.
- self.putx([6, ['Read fuse high bits: 0x%02x' % ret[3]]])
+ self.putx([Ann.RHFB, ['Read fuse high bits: 0x%02x' % ret[3]]])
# TODO: Decode fuse bits.
# TODO: Sanity check on reply.
def handle_cmd_read_extended_fuse_bits(self, cmd, ret):
# Read extended fuse bits.
- self.putx([7, ['Read extended fuse bits: 0x%02x' % ret[3]]])
+ self.putx([Ann.REFB, ['Read extended fuse bits: 0x%02x' % ret[3]]])
# TODO: Decode fuse bits.
# TODO: Sanity check on reply.
+ def handle_cmd_read_lock_bits(self, cmd, ret):
+ # Read lock bits
+ self.putx([Ann.RLB, ['Read lock bits: 0x%02x' % ret[3]]])
+
+ def handle_cmd_read_eeprom_memory(self, cmd, ret):
+ # Read EEPROM Memory
+ _addr = ((cmd[1] & 1) << 8) + cmd[2]
+ self.putx([Ann.REEM, ['Read EEPROM Memory: [0x%03x]: 0x%02x' % (_addr, ret[3])]])
+
+ def handle_cmd_read_program_memory(self, cmd, ret):
+ # Read Program Memory
+ _HL = 'Low'
+ _H = 'L'
+ if cmd[0] & 0x08:
+ _HL = 'High'
+ _H = 'H'
+ _addr = ((cmd[1] & 0x0f) << 8) + cmd[2]
+ self.putx([Ann.RP, [
+ 'Read program memory %s: [0x%03x]: 0x%02x' % (_HL, _addr, ret[3]),
+ '[%03x%s]:%02x' % (_addr, _H, ret[3]),
+ '%02x' % ret[3]
+ ]])
+
+ def handle_cmd_load_program_memory_page(self, cmd, ret):
+ # Load Program Memory Page
+ _HL = 'Low'
+ _H = 'L'
+ if cmd[0] & 0x08:
+ _HL = 'High'
+ _H = 'H'
+ _addr = cmd[2] & 0x1F
+ self.putx([Ann.LPMP, [
+ 'Load program memory page %s: [0x%03x]: 0x%02x' % (_HL, _addr, cmd[3]),
+ '[%03x%s]=%02x' % (_addr, _H, cmd[3]),
+ '%02x' % cmd[3]
+ ]])
+
+ def handle_cmd_write_program_memory_page(self, cmd, ret):
+ # Write Program Memory Page
+ _addr = ((cmd[1] & 0x0F) << 3) + (cmd[2] << 5)
+ self.putx([Ann.WP, ['Write program memory page: 0x%02x' % _addr]])
+
def handle_command(self, cmd, ret):
if cmd[:2] == [0xac, 0x53]:
self.handle_cmd_programming_enable(cmd, ret)
@@ -174,10 +226,20 @@ def handle_command(self, cmd, ret):
self.handle_cmd_read_signature_byte_0x01(cmd, ret)
elif cmd[0] == 0x30 and cmd[2] == 0x02:
self.handle_cmd_read_signature_byte_0x02(cmd, ret)
+ elif cmd[:2] == [0x58, 0x00]:
+ self.handle_cmd_read_lock_bits(cmd,ret)
+ elif cmd[0] == 0xa0 and (cmd[1] & (3 << 6)) == (0 << 6):
+ self.handle_cmd_read_eeprom_memory(cmd, ret)
+ elif (cmd[0] == 0x20 or cmd[0] == 0x28) and ((cmd[1] & 0xf0) == 0x00):
+ self.handle_cmd_read_program_memory(cmd, ret)
+ elif (cmd[0] == 0x40 or cmd[0] == 0x48) and ((cmd[1] & 0xf0) == 0x00):
+ self.handle_cmd_load_program_memory_page(cmd, ret)
+ elif (cmd[0] == 0x4C and ((cmd[1] & 0xf0) == 0x00)):
+ self.handle_cmd_write_program_memory_page(cmd, ret)
else:
c = '%02x %02x %02x %02x' % tuple(cmd)
r = '%02x %02x %02x %02x' % tuple(ret)
- self.putx([0, ['Unknown command: %s (reply: %s)!' % (c, r)]])
+ self.putx([Ann.WARN, ['Unknown command: %s (reply: %s)!' % (c, r)]])
def decode(self, ss, es, data):
ptype, mosi, miso = data
diff --git a/libsigrokdecode4DSL/decoders/caliper/pd.py b/libsigrokdecode4DSL/decoders/caliper/pd.py
index b9e5851f..20a2a555 100644
--- a/libsigrokdecode4DSL/decoders/caliper/pd.py
+++ b/libsigrokdecode4DSL/decoders/caliper/pd.py
@@ -91,7 +91,7 @@ def decode(self):
# after inactivity for a user specified period. Present the
# number of unprocessed bits to the user for diagnostics.
clk, data = self.wait(wait_cond)
- if timeout_ms and not self.matched & 0b1 == 0b1:
+ if timeout_ms and not self.matched[0]:
if self.number_bits or self.flags_bits:
count = len(self.number_bits) + len(self.flags_bits)
self.putg(self.ss, self.samplenum, 1, [
diff --git a/libsigrokdecode4DSL/decoders/can/__init__.py b/libsigrokdecode4DSL/decoders/can/__init__.py
index 47f571d6..888bb819 100644
--- a/libsigrokdecode4DSL/decoders/can/__init__.py
+++ b/libsigrokdecode4DSL/decoders/can/__init__.py
@@ -24,6 +24,8 @@
This decoder assumes that a single CAN_RX line is sampled (e.g. on
the digital output side of a CAN transceiver IC such as the Microchip
MCP-2515DM-BM).
+
+It also has support for CAN-FD.
'''
from .pd import Decoder
diff --git a/libsigrokdecode4DSL/decoders/can/pd.py b/libsigrokdecode4DSL/decoders/can/pd.py
index ceb79b74..46d91b16 100644
--- a/libsigrokdecode4DSL/decoders/can/pd.py
+++ b/libsigrokdecode4DSL/decoders/can/pd.py
@@ -3,6 +3,7 @@
##
## Copyright (C) 2012-2013 Uwe Hermann
## Copyright (C) 2019 DreamSourceLab
+## Copyright (C) 2019 Stephan Thiele
##
## 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
@@ -18,11 +19,15 @@
## along with this program; if not, see .
##
+from common.srdhelper import bitpack_msb
import sigrokdecode as srd
class SamplerateError(Exception):
pass
+def dlc2len(dlc):
+ return [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64][dlc]
+
class Decoder(srd.Decoder):
api_version = 3
id = 'can'
@@ -31,17 +36,18 @@ class Decoder(srd.Decoder):
desc = 'Field bus protocol for distributed realtime control.'
license = 'gplv2+'
inputs = ['logic']
- outputs = []
+ outputs = ['can']
tags = ['Automotive']
channels = (
- {'id': 'can_rx', 'name': 'CAN', 'desc': 'CAN bus line'},
+ {'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'},
)
options = (
- {'id': 'bitrate', 'desc': 'Bitrate (bits/s)', 'default': 1000000},
+ {'id': 'nominal_bitrate', 'desc': 'Nominal bitrate (bits/s)', 'default': 1000000},
+ {'id': 'fast_bitrate', 'desc': 'Fast bitrate (bits/s)', 'default': 2000000},
{'id': 'sample_point', 'desc': 'Sample point (%)', 'default': 70.0},
)
annotations = (
- ('data', 'CAN payload data'),
+ ('data', 'Payload data'),
('sof', 'Start of frame'),
('eof', 'End of frame'),
('id', 'Identifier'),
@@ -57,7 +63,7 @@ class Decoder(srd.Decoder):
('ack-slot', 'ACK slot'),
('ack-delimiter', 'ACK delimiter'),
('stuff-bit', 'Stuff bit'),
- ('warnings', 'Human-readable warnings'),
+ ('warning', 'Warning'),
('bit', 'Bit'),
)
annotation_rows = (
@@ -75,11 +81,22 @@ def reset(self):
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+ def set_bit_rate(self, bitrate):
+ self.bit_width = float(self.samplerate) / float(bitrate)
+ self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
+
+ def set_nominal_bitrate(self):
+ self.set_bit_rate(self.options['nominal_bitrate'])
+
+ def set_fast_bitrate(self):
+ self.set_bit_rate(self.options['fast_bitrate'])
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- self.bit_width = float(self.samplerate) / float(self.options['bitrate'])
+ self.bit_width = float(self.samplerate) / float(self.options['nominal_bitrate'])
self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
# Generic helper for CAN bit annotations.
@@ -95,10 +112,17 @@ def putx(self, data):
def put12(self, data):
self.putg(self.ss_bit12, self.ss_bit12, data)
+ # Single-CAN-bit annotation using the samplenum of CAN bit 32.
+ def put32(self, data):
+ self.putg(self.ss_bit32, self.ss_bit32, data)
+
# Multi-CAN-bit annotation from self.ss_block to current samplenum.
def putb(self, data):
self.putg(self.ss_block, self.samplenum, data)
+ def putpy(self, data):
+ self.put(self.ss_packet, self.es_packet, self.out_python, data)
+
def reset_variables(self):
self.state = 'IDLE'
self.sof = self.frame_type = self.dlc = None
@@ -108,7 +132,12 @@ def reset_variables(self):
self.last_databit = 999 # Positive value that bitnum+x will never match
self.ss_block = None
self.ss_bit12 = None
+ self.ss_bit32 = None
self.ss_databytebits = []
+ self.frame_bytes = []
+ self.rtr_type = None
+ self.fd = False
+ self.rtr = None
# Poor man's clock synchronization. Use signal edges which change to
# dominant state in rather simple ways. This naive approach is neither
@@ -119,16 +148,12 @@ def dom_edge_seen(self, force = False):
self.dom_edge_snum = self.samplenum
self.dom_edge_bcount = self.curbit
- def bit_sampled(self):
- # EMPTY
- pass
-
# Determine the position of the next desired bit's sample point.
def get_sample_point(self, bitnum):
samplenum = self.dom_edge_snum
- samplenum += int(self.bit_width * (bitnum - self.dom_edge_bcount))
- samplenum += int(self.sample_point)
- return samplenum
+ samplenum += self.bit_width * (bitnum - self.dom_edge_bcount)
+ samplenum += self.sample_point
+ return int(samplenum)
def is_stuff_bit(self):
# CAN uses NRZ encoding and bit stuffing.
@@ -161,45 +186,67 @@ def decode_frame_end(self, can_rx, bitnum):
# Remember start of CRC sequence (see below).
if bitnum == (self.last_databit + 1):
self.ss_block = self.samplenum
+ if self.fd:
+ if dlc2len(self.dlc) < 16:
+ self.crc_len = 27 # 17 + SBC + stuff bits
+ else:
+ self.crc_len = 32 # 21 + SBC + stuff bits
+ else:
+ self.crc_len = 15
+
+ # CRC sequence (15 bits, 17 bits or 21 bits)
+ elif bitnum == (self.last_databit + self.crc_len):
+ if self.fd:
+ if dlc2len(self.dlc) < 16:
+ crc_type = "CRC-17"
+ else:
+ crc_type = "CRC-21"
+ else:
+ crc_type = "CRC-15"
- # CRC sequence (15 bits)
- elif bitnum == (self.last_databit + 15):
x = self.last_databit + 1
- crc_bits = self.bits[x:x + 15 + 1]
- self.crc = int(''.join(str(d) for d in crc_bits), 2)
- self.putb([11, ['CRC sequence: 0x%04x' % self.crc,
- 'CRC: 0x%04x' % self.crc, 'CRC']])
+ crc_bits = self.bits[x:x + self.crc_len + 1]
+ self.crc = bitpack_msb(crc_bits)
+ self.putb([11, ['%s sequence: 0x%04x' % (crc_type, self.crc),
+ '%s: 0x%04x' % (crc_type, self.crc), '%s' % crc_type]])
if not self.is_valid_crc(crc_bits):
self.putb([16, ['CRC is invalid']])
# CRC delimiter bit (recessive)
- elif bitnum == (self.last_databit + 16):
+ elif bitnum == (self.last_databit + self.crc_len + 1):
self.putx([12, ['CRC delimiter: %d' % can_rx,
'CRC d: %d' % can_rx, 'CRC d']])
if can_rx != 1:
self.putx([16, ['CRC delimiter must be a recessive bit']])
+ if self.fd:
+ self.set_nominal_bitrate()
+
# ACK slot bit (dominant: ACK, recessive: NACK)
- elif bitnum == (self.last_databit + 17):
+ elif bitnum == (self.last_databit + self.crc_len + 2):
ack = 'ACK' if can_rx == 0 else 'NACK'
self.putx([13, ['ACK slot: %s' % ack, 'ACK s: %s' % ack, 'ACK s']])
# ACK delimiter bit (recessive)
- elif bitnum == (self.last_databit + 18):
+ elif bitnum == (self.last_databit + self.crc_len + 3):
self.putx([14, ['ACK delimiter: %d' % can_rx,
'ACK d: %d' % can_rx, 'ACK d']])
if can_rx != 1:
self.putx([16, ['ACK delimiter must be a recessive bit']])
# Remember start of EOF (see below).
- elif bitnum == (self.last_databit + 19):
+ elif bitnum == (self.last_databit + self.crc_len + 4):
self.ss_block = self.samplenum
# End of frame (EOF), 7 recessive bits
- elif bitnum == (self.last_databit + 25):
+ elif bitnum == (self.last_databit + self.crc_len + 10):
self.putb([2, ['End of frame', 'EOF', 'E']])
if self.rawbits[-7:] != [1, 1, 1, 1, 1, 1, 1]:
self.putb([16, ['End of frame (EOF) must be 7 recessive bits']])
+ self.es_packet = self.samplenum
+ py_data = tuple([self.frame_type, self.fullid, self.rtr_type,
+ self.dlc, self.frame_bytes])
+ self.putpy(py_data)
self.reset_variables()
return True
@@ -208,43 +255,66 @@ def decode_frame_end(self, can_rx, bitnum):
# Returns True if the frame ended (EOF), False otherwise.
def decode_standard_frame(self, can_rx, bitnum):
- # Bit 14: RB0 (reserved bit)
- # Has to be sent dominant, but receivers should accept recessive too.
+ # Bit 14: FDF (Flexible data format)
+ # Has to be sent dominant when FD frame, has to be sent recessive
+ # when classic CAN frame.
if bitnum == 14:
- self.putx([7, ['Reserved bit 0: %d' % can_rx,
- 'RB0: %d' % can_rx, 'RB0']])
+ self.fd = True if can_rx else False
+ if self.fd:
+ self.putx([7, ['Flexible data format: %d' % can_rx,
+ 'FDF: %d' % can_rx, 'FDF']])
+ else:
+ self.putx([7, ['Reserved bit 0: %d' % can_rx,
+ 'RB0: %d' % can_rx, 'RB0']])
+
+ if self.fd:
+ # Bit 12: Substitute remote request (SRR) bit
+ self.put12([8, ['Substitute remote request', 'SRR']])
+ self.dlc_start = 18
+ else:
+ # Bit 12: Remote transmission request (RTR) bit
+ # Data frame: dominant, remote frame: recessive
+ # Remote frames do not contain a data field.
+ rtr = 'remote' if self.bits[12] == 1 else 'data'
+ self.put12([8, ['Remote transmission request: %s frame' % rtr,
+ 'RTR: %s frame' % rtr, 'RTR']])
+ self.rtr_type = rtr
+ self.dlc_start = 15
- # Bit 12: Remote transmission request (RTR) bit
- # Data frame: dominant, remote frame: recessive
- # Remote frames do not contain a data field.
- rtr = 'remote' if self.bits[12] == 1 else 'data'
- self.put12([8, ['Remote transmission request: %s frame' % rtr,
- 'RTR: %s frame' % rtr, 'RTR']])
+ if bitnum == 15 and self.fd:
+ self.putx([7, ['Reserved: %d' % can_rx, 'R0: %d' % can_rx, 'R0']])
+
+ if bitnum == 16 and self.fd:
+ self.putx([7, ['Bit rate switch: %d' % can_rx, 'BRS: %d' % can_rx, 'BRS']])
+
+ if bitnum == 17 and self.fd:
+ self.putx([7, ['Error state indicator: %d' % can_rx, 'ESI: %d' % can_rx, 'ESI']])
# Remember start of DLC (see below).
- elif bitnum == 15:
+ elif bitnum == self.dlc_start:
self.ss_block = self.samplenum
# Bits 15-18: Data length code (DLC), in number of bytes (0-8).
- elif bitnum == 18:
- self.dlc = int(''.join(str(d) for d in self.bits[15:18 + 1]), 2)
+ elif bitnum == self.dlc_start + 3:
+ self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])
self.putb([10, ['Data length code: %d' % self.dlc,
'DLC: %d' % self.dlc, 'DLC']])
- self.last_databit = 18 + (self.dlc * 8)
- if self.dlc > 8:
+ self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
+ if self.dlc > 8 and not self.fd:
self.putb([16, ['Data length code (DLC) > 8 is not allowed']])
# Remember all databyte bits, except the very last one.
- elif bitnum in range(19, self.last_databit):
+ elif bitnum in range(self.dlc_start + 4, self.last_databit):
self.ss_databytebits.append(self.samplenum)
# Bits 19-X: Data field (0-8 bytes, depending on DLC)
# The bits within a data byte are transferred MSB-first.
elif bitnum == self.last_databit:
self.ss_databytebits.append(self.samplenum) # Last databyte bit.
- for i in range(self.dlc):
- x = 18 + (8 * i) + 1
- b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+ for i in range(dlc2len(self.dlc)):
+ x = self.dlc_start + 4 + (8 * i)
+ b = bitpack_msb(self.bits[x:x + 8])
+ self.frame_bytes.append(b)
ss = self.ss_databytebits[i * 8]
es = self.ss_databytebits[((i + 1) * 8) - 1]
self.putg(ss, es, [0, ['Data byte %d: 0x%02x' % (i, b),
@@ -262,15 +332,17 @@ def decode_extended_frame(self, can_rx, bitnum):
# Remember start of EID (see below).
if bitnum == 14:
self.ss_block = self.samplenum
+ self.fd = False
+ self.dlc_start = 35
# Bits 14-31: Extended identifier (EID[17..0])
elif bitnum == 31:
- self.eid = int(''.join(str(d) for d in self.bits[14:]), 2)
+ self.eid = bitpack_msb(self.bits[14:])
s = '%d (0x%x)' % (self.eid, self.eid)
self.putb([4, ['Extended Identifier: %s' % s,
'Extended ID: %s' % s, 'Extended ID', 'EID']])
- self.fullid = self.id << 18 | self.eid
+ self.fullid = self.ident << 18 | self.eid
s = '%d (0x%x)' % (self.fullid, self.fullid)
self.putb([5, ['Full Identifier: %s' % s, 'Full ID: %s' % s,
'Full ID', 'FID']])
@@ -282,43 +354,67 @@ def decode_extended_frame(self, can_rx, bitnum):
# Bit 32: Remote transmission request (RTR) bit
# Data frame: dominant, remote frame: recessive
# Remote frames do not contain a data field.
+
+ # Remember start of RTR (see below).
if bitnum == 32:
- rtr = 'remote' if can_rx == 1 else 'data'
- self.putx([8, ['Remote transmission request: %s frame' % rtr,
- 'RTR: %s frame' % rtr, 'RTR']])
+ self.ss_bit32 = self.samplenum
+ self.rtr = can_rx
+
+ if not self.fd:
+ rtr = 'remote' if can_rx == 1 else 'data'
+ self.putx([8, ['Remote transmission request: %s frame' % rtr,
+ 'RTR: %s frame' % rtr, 'RTR']])
+ self.rtr_type = rtr
# Bit 33: RB1 (reserved bit)
elif bitnum == 33:
- self.putx([7, ['Reserved bit 1: %d' % can_rx,
- 'RB1: %d' % can_rx, 'RB1']])
+ self.fd = True if can_rx else False
+ if self.fd:
+ self.dlc_start = 37
+ self.putx([7, ['Flexible data format: %d' % can_rx,
+ 'FDF: %d' % can_rx, 'FDF']])
+ self.put32([7, ['Reserved bit 1: %d' % self.rtr,
+ 'RB1: %d' % self.rtr, 'RB1']])
+ else:
+ self.putx([7, ['Reserved bit 1: %d' % can_rx,
+ 'RB1: %d' % can_rx, 'RB1']])
# Bit 34: RB0 (reserved bit)
elif bitnum == 34:
self.putx([7, ['Reserved bit 0: %d' % can_rx,
'RB0: %d' % can_rx, 'RB0']])
+ elif bitnum == 35 and self.fd:
+ self.putx([7, ['Bit rate switch: %d' % can_rx,
+ 'BRS: %d' % can_rx, 'BRS']])
+
+ elif bitnum == 36 and self.fd:
+ self.putx([7, ['Error state indicator: %d' % can_rx,
+ 'ESI: %d' % can_rx, 'ESI']])
+
# Remember start of DLC (see below).
- elif bitnum == 35:
+ elif bitnum == self.dlc_start:
self.ss_block = self.samplenum
# Bits 35-38: Data length code (DLC), in number of bytes (0-8).
- elif bitnum == 38:
- self.dlc = int(''.join(str(d) for d in self.bits[35:38 + 1]), 2)
+ elif bitnum == self.dlc_start + 3:
+ self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])
self.putb([10, ['Data length code: %d' % self.dlc,
'DLC: %d' % self.dlc, 'DLC']])
- self.last_databit = 38 + (self.dlc * 8)
+ self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
# Remember all databyte bits, except the very last one.
- elif bitnum in range(39, self.last_databit):
+ elif bitnum in range(self.dlc_start + 4, self.last_databit):
self.ss_databytebits.append(self.samplenum)
# Bits 39-X: Data field (0-8 bytes, depending on DLC)
# The bits within a data byte are transferred MSB-first.
elif bitnum == self.last_databit:
self.ss_databytebits.append(self.samplenum) # Last databyte bit.
- for i in range(self.dlc):
- x = 38 + (8 * i) + 1
- b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+ for i in range(dlc2len(self.dlc)):
+ x = self.dlc_start + 4 + (8 * i)
+ b = bitpack_msb(self.bits[x:x + 8])
+ self.frame_bytes.append(b)
ss = self.ss_databytebits[i * 8]
es = self.ss_databytebits[((i + 1) * 8) - 1]
self.putg(ss, es, [0, ['Data byte %d: 0x%02x' % (i, b),
@@ -337,6 +433,12 @@ def handle_bit(self, can_rx):
# Get the index of the current CAN frame bit (without stuff bits).
bitnum = len(self.bits) - 1
+ if self.fd and can_rx:
+ if bitnum == 16 and self.frame_type == 'standard' \
+ or bitnum == 35 and self.frame_type == 'extended':
+ self.dom_edge_seen(force=True)
+ self.set_fast_bitrate()
+
# If this is a stuff bit, remove it from self.bits and ignore it.
if self.is_stuff_bit():
self.putx([15, [str(can_rx)]])
@@ -347,6 +449,7 @@ def handle_bit(self, can_rx):
# Bit 0: Start of frame (SOF) bit
if bitnum == 0:
+ self.ss_packet = self.samplenum
self.putx([1, ['Start of frame', 'SOF', 'S']])
if can_rx != 0:
self.putx([16, ['Start of frame (SOF) must be a dominant bit']])
@@ -358,10 +461,13 @@ def handle_bit(self, can_rx):
# Bits 1-11: Identifier (ID[10..0])
# The bits ID[10..4] must NOT be all recessive.
elif bitnum == 11:
- self.id = int(''.join(str(d) for d in self.bits[1:]), 2)
- s = '%d (0x%x)' % (self.id, self.id),
+ # BEWARE! Don't clobber the decoder's .id field which is
+ # part of its boiler plate!
+ self.ident = bitpack_msb(self.bits[1:])
+ self.fullid = self.ident
+ s = '%d (0x%x)' % (self.ident, self.ident),
self.putb([3, ['Identifier: %s' % s, 'ID: %s' % s, 'ID']])
- if (self.id & 0x7f0) == 0x7f0:
+ if (self.ident & 0x7f0) == 0x7f0:
self.putb([16, ['Identifier bits 10..4 must not be all recessive']])
# RTR or SRR bit, depending on frame type (gets handled later).
@@ -408,8 +514,7 @@ def decode(self):
# Wait until we're in the correct bit/sampling position.
pos = self.get_sample_point(self.curbit)
(can_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
- if (self.matched & (0b1 << 1)):
+ if self.matched[1]:
self.dom_edge_seen()
- if (self.matched & (0b1 << 0)):
+ if self.matched[0]:
self.handle_bit(can_rx)
- self.bit_sampled()
diff --git a/libsigrokdecode4DSL/decoders/cc1101/pd.py b/libsigrokdecode4DSL/decoders/cc1101/pd.py
index a2038907..f62f18ec 100644
--- a/libsigrokdecode4DSL/decoders/cc1101/pd.py
+++ b/libsigrokdecode4DSL/decoders/cc1101/pd.py
@@ -19,17 +19,15 @@
import sigrokdecode as srd
from collections import namedtuple
+from common.srdhelper import SrdIntEnum
from .lists import *
-ANN_STROBE, ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ, \
- ANN_BURST_WRITE, ANN_STATUS_READ, ANN_STATUS, ANN_WARN = range(8)
+Ann = SrdIntEnum.from_str('Ann', 'STROBE SINGLE_READ SINGLE_WRITE BURST_READ \
+ BURST_WRITE STATUS_READ STATUS WARN')
Pos = namedtuple('Pos', ['ss', 'es'])
Data = namedtuple('Data', ['mosi', 'miso'])
-class ChannelError(Exception):
- pass
-
class Decoder(srd.Decoder):
api_version = 3
id = 'cc1101'
@@ -47,15 +45,14 @@ class Decoder(srd.Decoder):
('burst_read', 'Burst register read'),
('burst_write', 'Burst register write'),
('status_read', 'Status read'),
- ('status', 'Status register'),
+ ('status_reg', 'Status register'),
('warning', 'Warning'),
)
annotation_rows = (
- ('cmd', 'Commands', (ANN_STROBE,)),
- ('data', 'Data', (ANN_SINGLE_READ, ANN_SINGLE_WRITE, ANN_BURST_READ,
- ANN_BURST_WRITE, ANN_STATUS_READ)),
- ('status', 'Status register', (ANN_STATUS,)),
- ('warnings', 'Warnings', (ANN_WARN,)),
+ ('cmds', 'Commands', (Ann.STROBE,)),
+ ('data', 'Data', (Ann.prefixes('SINGLE_ BURST_ STATUS_'))),
+ ('status', 'Status register', (Ann.STATUS,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
)
def __init__(self):
@@ -71,10 +68,15 @@ def start(self):
def warn(self, pos, msg):
'''Put a warning message 'msg' at 'pos'.'''
- self.put(pos.ss, pos.es, self.out_ann, [ANN_WARN, [msg]])
+ self.put(pos.ss, pos.es, self.out_ann, [Ann.WARN, [msg]])
+
+ def putp(self, pos, ann, msg):
+ '''Put an annotation message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [ann, [msg]])
- def put_ann(self, pos, ann, data):
- self.put(pos.ss, pos.es, self.out_ann, [ann, data])
+ def putp2(self, pos, ann, msg1, msg2):
+ '''Put an annotation message 'msg' at 'pos'.'''
+ self.put(pos.ss, pos.es, self.out_ann, [ann, [msg1, msg2]])
def next(self):
'''Resets the decoder after a complete command was decoded.'''
@@ -112,7 +114,7 @@ def decode_command(self, pos, b):
self.cmd, self.dat, self.min, self.max = c
if self.cmd == 'Strobe':
- self.put_ann(pos, ANN_STROBE, [self.format_command()])
+ self.putp(pos, Ann.STROBE, self.format_command())
else:
# Don't output anything now, the command is merged with
# the data bytes following it.
@@ -177,7 +179,7 @@ def decode_reg(self, pos, ann, regid, data):
else:
name = regid
- if regid == 'STATUS' and ann == ANN_STATUS:
+ if regid == 'STATUS' and ann == Ann.STATUS:
label = 'Status'
self.decode_status_reg(pos, ann, data, label)
else:
@@ -206,10 +208,9 @@ def decode_status_reg(self, pos, ann, data, label):
else:
longtext_fifo = '{} bytes free in TX FIFO'.format(fifo_bytes)
- text = '{} = '.format(label) + '{$}'
+ text = '{} = {:02X}'.format(label, status)
longtext = ''.join([text, '; ', longtext_chiprdy, longtext_state, longtext_fifo])
- #self.printlog(longtext + ' ,' + text + '\n')
- self.put_ann(pos, ann, [longtext, text, '@%02X' % status])
+ self.putp2(pos, ann, longtext, text)
def decode_mb_data(self, pos, ann, data, label):
'''Decodes the data bytes 'data' of a multibyte command at position
@@ -219,24 +220,24 @@ def escape(b):
return '{:02X}'.format(b)
data = ' '.join([escape(b) for b in data])
- text = '{} = '.format(label) + '{$}'
- self.put_ann(pos, ann, [text, '@' + 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 == 'Write':
- self.decode_reg(pos, ANN_SINGLE_WRITE, self.dat, self.mosi_bytes())
+ self.decode_reg(pos, Ann.SINGLE_WRITE, self.dat, self.mosi_bytes())
elif self.cmd == 'Burst write':
- self.decode_reg(pos, ANN_BURST_WRITE, self.dat, self.mosi_bytes())
+ self.decode_reg(pos, Ann.BURST_WRITE, self.dat, self.mosi_bytes())
elif self.cmd == 'Read':
- self.decode_reg(pos, ANN_SINGLE_READ, self.dat, self.miso_bytes())
+ self.decode_reg(pos, Ann.SINGLE_READ, self.dat, self.miso_bytes())
elif self.cmd == 'Burst read':
- self.decode_reg(pos, ANN_BURST_READ, self.dat, self.miso_bytes())
+ self.decode_reg(pos, Ann.BURST_READ, self.dat, self.miso_bytes())
elif self.cmd == 'Strobe':
- self.decode_reg(pos, ANN_STROBE, self.dat, self.mosi_bytes())
+ self.decode_reg(pos, Ann.STROBE, self.dat, self.mosi_bytes())
elif self.cmd == 'Status read':
- self.decode_reg(pos, ANN_STATUS_READ, self.dat, self.miso_bytes())
+ self.decode_reg(pos, Ann.STATUS_READ, self.dat, self.miso_bytes())
else:
self.warn(pos, 'unhandled command')
@@ -281,7 +282,7 @@ def decode(self, ss, es, data):
# First MOSI byte is always the command.
self.decode_command(pos, mosi)
# First MISO byte is always the status register.
- self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+ self.decode_reg(pos, Ann.STATUS, 'STATUS', [miso])
else:
if not self.cmd or len(self.mb) >= self.max:
self.warn(pos, 'excess byte')
diff --git a/libsigrokdecode4DSL/decoders/cjtag/__init__.py b/libsigrokdecode4DSL/decoders/cjtag/__init__.py
new file mode 100644
index 00000000..168d4d60
--- /dev/null
+++ b/libsigrokdecode4DSL/decoders/cjtag/__init__.py
@@ -0,0 +1,36 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012 Uwe Hermann
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see .
+##
+
+'''
+JTAG (Joint Test Action Group), a.k.a. "IEEE 1149.1: Standard Test Access Port
+and Boundary-Scan Architecture", is a protocol used for testing, debugging,
+and flashing various digital ICs.
+
+Details:
+https://en.wikipedia.org/wiki/Joint_Test_Action_Group
+http://focus.ti.com/lit/an/ssya002c/ssya002c.pdf
+
+This decoder handles a tiny part of IEEE 1149.7, the CJTAG OSCAN1 format.
+ZBS is currently not supported.
+
+Details:
+http://developers-club.com/posts/237885/
+'''
+
+from .pd import Decoder
diff --git a/libsigrokdecode4DSL/decoders/cjtag/pd.py b/libsigrokdecode4DSL/decoders/cjtag/pd.py
new file mode 100644
index 00000000..cd4dc4e5
--- /dev/null
+++ b/libsigrokdecode4DSL/decoders/cjtag/pd.py
@@ -0,0 +1,323 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012-2020 Uwe Hermann
+## Copyright (C) 2019 Zhiyuan Wan
+## Copyright (C) 2019 Kongou Hikari
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see .
+##
+
+import sigrokdecode as srd
+from common.srdhelper import SrdStrEnum
+
+'''
+OUTPUT_PYTHON format:
+
+Packet:
+[, ]
+
+:
+ - 'NEW STATE': is the new state of the JTAG state machine.
+ Valid values: 'TEST-LOGIC-RESET', 'RUN-TEST/IDLE', 'SELECT-DR-SCAN',
+ 'CAPTURE-DR', 'SHIFT-DR', 'EXIT1-DR', 'PAUSE-DR', 'EXIT2-DR', 'UPDATE-DR',
+ 'SELECT-IR-SCAN', 'CAPTURE-IR', 'SHIFT-IR', 'EXIT1-IR', 'PAUSE-IR',
+ 'EXIT2-IR', 'UPDATE-IR'.
+ - 'IR TDI': Bitstring that was clocked into the IR register.
+ - 'IR TDO': Bitstring that was clocked out of the IR register.
+ - 'DR TDI': Bitstring that was clocked into the DR register.
+ - 'DR TDO': Bitstring that was clocked out of the DR register.
+
+All bitstrings are a list consisting of two items. The first is a sequence
+of '1' and '0' characters (the right-most character is the LSB. Example:
+'01110001', where 1 is the LSB). The second item is a list of ss/es values
+for each bit that is in the bitstring.
+'''
+
+s = 'TEST-LOGIC-RESET RUN-TEST/IDLE \
+ SELECT-DR-SCAN CAPTURE-DR UPDATE-DR PAUSE-DR SHIFT-DR EXIT1-DR EXIT2-DR \
+ SELECT-IR-SCAN CAPTURE-IR UPDATE-IR PAUSE-IR SHIFT-IR EXIT1-IR EXIT2-IR'
+St = SrdStrEnum.from_str('St', s)
+
+jtag_states = [s.value for s in St]
+
+s = 'EC SPARE TPDEL TPREV TPST RDYC DLYC SCNFMT CP OAC'.split()
+s = ['CJTAG_' + x for x in s] + ['OSCAN1', 'FOUR_WIRE']
+CSt = SrdStrEnum.from_list('CSt', s)
+
+cjtag_states = [s.value for s in CSt]
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'cjtag'
+ name = 'cJTAG'
+ longname = 'Compact Joint Test Action Group (IEEE 1149.7)'
+ desc = 'Protocol for testing, debugging, and flashing ICs.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['jtag']
+ tags = ['Debug/trace']
+ channels = (
+ {'id': 'tckc', 'name': 'TCKC', 'desc': 'Test clock'},
+ {'id': 'tmsc', 'name': 'TMSC', 'desc': 'Test mode select'},
+ )
+ annotations = \
+ tuple([tuple([s.lower(), s]) for s in jtag_states]) + \
+ tuple([tuple([s.lower(), s]) for s in cjtag_states]) + ( \
+ ('bit-tdi', 'Bit (TDI)'),
+ ('bit-tdo', 'Bit (TDO)'),
+ ('bitstring-tdi', 'Bitstring (TDI)'),
+ ('bitstring-tdo', 'Bitstring (TDO)'),
+ ('bit-tms', 'Bit (TMS)'),
+ )
+ annotation_rows = (
+ ('bits-tdi', 'Bits (TDI)', (28,)),
+ ('bits-tdo', 'Bits (TDO)', (29,)),
+ ('bitstrings-tdi', 'Bitstrings (TDI)', (30,)),
+ ('bitstrings-tdo', 'Bitstrings (TDO)', (31,)),
+ ('bits-tms', 'Bits (TMS)', (32,)),
+ ('cjtag-states', 'CJTAG states',
+ tuple(range(len(jtag_states), len(jtag_states + cjtag_states)))),
+ ('jtag-states', 'JTAG states', tuple(range(len(jtag_states)))),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ # self.state = St.TEST_LOGIC_RESET
+ self.state = St.RUN_TEST_IDLE
+ self.cjtagstate = CSt.FOUR_WIRE
+ self.oldcjtagstate = None
+ self.escape_edges = 0
+ self.oaclen = 0
+ self.oldtms = 0
+ self.oacp = 0
+ self.oscan1cycle = 0
+ self.oldstate = None
+ self.bits_tdi = []
+ self.bits_tdo = []
+ self.bits_samplenums_tdi = []
+ self.bits_samplenums_tdo = []
+ self.ss_item = self.es_item = None
+ self.ss_bitstring = self.es_bitstring = None
+ self.saved_item = None
+ self.first = True
+ self.first_bit = True
+
+ def start(self):
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putx(self, data):
+ self.put(self.ss_item, self.es_item, self.out_ann, data)
+
+ def putp(self, data):
+ self.put(self.ss_item, self.es_item, self.out_python, data)
+
+ def putx_bs(self, data):
+ self.put(self.ss_bitstring, self.es_bitstring, self.out_ann, data)
+
+ def putp_bs(self, data):
+ self.put(self.ss_bitstring, self.es_bitstring, self.out_python, data)
+
+ def advance_state_machine(self, tms):
+ self.oldstate = self.state
+
+ if self.cjtagstate.value.startswith('CJTAG_'):
+ self.oacp += 1
+ if self.oacp > 4 and self.oaclen == 12:
+ self.cjtagstate = CSt.CJTAG_EC
+
+ if self.oacp == 8 and tms == 0:
+ self.oaclen = 36
+ if self.oacp > 8 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_SPARE
+ if self.oacp > 13 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_TPDEL
+ if self.oacp > 16 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_TPREV
+ if self.oacp > 18 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_TPST
+ if self.oacp > 23 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_RDYC
+ if self.oacp > 25 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_DLYC
+ if self.oacp > 27 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_SCNFMT
+
+ if self.oacp > 8 and self.oaclen == 12:
+ self.cjtagstate = CSt.CJTAG_CP
+ if self.oacp > 32 and self.oaclen == 36:
+ self.cjtagstate = CSt.CJTAG_CP
+
+ if self.oacp > self.oaclen:
+ self.cjtagstate = CSt.OSCAN1
+ self.oscan1cycle = 1
+ # Because Nuclei cJTAG device asserts a reset during cJTAG
+ # online activating.
+ self.state = St.TEST_LOGIC_RESET
+ return
+
+ # Intro "tree"
+ if self.state == St.TEST_LOGIC_RESET:
+ self.state = St.TEST_LOGIC_RESET if (tms) else St.RUN_TEST_IDLE
+ elif self.state == St.RUN_TEST_IDLE:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
+
+ # DR "tree"
+ elif self.state == St.SELECT_DR_SCAN:
+ self.state = St.SELECT_IR_SCAN if (tms) else St.CAPTURE_DR
+ elif self.state == St.CAPTURE_DR:
+ self.state = St.EXIT1_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.SHIFT_DR:
+ self.state = St.EXIT1_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.EXIT1_DR:
+ self.state = St.UPDATE_DR if (tms) else St.PAUSE_DR
+ elif self.state == St.PAUSE_DR:
+ self.state = St.EXIT2_DR if (tms) else St.PAUSE_DR
+ elif self.state == St.EXIT2_DR:
+ self.state = St.UPDATE_DR if (tms) else St.SHIFT_DR
+ elif self.state == St.UPDATE_DR:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
+
+ # IR "tree"
+ elif self.state == St.SELECT_IR_SCAN:
+ self.state = St.TEST_LOGIC_RESET if (tms) else St.CAPTURE_IR
+ elif self.state == St.CAPTURE_IR:
+ self.state = St.EXIT1_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.SHIFT_IR:
+ self.state = St.EXIT1_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.EXIT1_IR:
+ self.state = St.UPDATE_IR if (tms) else St.PAUSE_IR
+ elif self.state == St.PAUSE_IR:
+ self.state = St.EXIT2_IR if (tms) else St.PAUSE_IR
+ elif self.state == St.EXIT2_IR:
+ self.state = St.UPDATE_IR if (tms) else St.SHIFT_IR
+ elif self.state == St.UPDATE_IR:
+ self.state = St.SELECT_DR_SCAN if (tms) else St.RUN_TEST_IDLE
+
+ def handle_rising_tckc_edge(self, tdi, tdo, tck, tms):
+
+ # Rising TCK edges always advance the state machine.
+ self.advance_state_machine(tms)
+
+ if self.first:
+ # Save the start sample and item for later (no output yet).
+ self.ss_item = self.samplenum
+ self.first = False
+ else:
+ # Output the saved item (from the last CLK edge to the current).
+ self.es_item = self.samplenum
+ # Output the old state (from last rising TCK edge to current one).
+ self.putx([jtag_states.index(self.oldstate.value), [self.oldstate.value]])
+ self.putp(['NEW STATE', self.state.value])
+
+ self.putx([len(jtag_states) + cjtag_states.index(self.oldcjtagstate.value),
+ [self.oldcjtagstate.value]])
+ if (self.oldcjtagstate.value.startswith('CJTAG_')):
+ self.putx([32, [str(self.oldtms)]])
+ self.oldtms = tms
+
+ # Upon SHIFT-*/EXIT1-* collect the current TDI/TDO values.
+ if self.oldstate.value.startswith('SHIFT-') or \
+ self.oldstate.value.startswith('EXIT1-'):
+ if self.first_bit:
+ self.ss_bitstring = self.samplenum
+ self.first_bit = False
+ else:
+ self.putx([28, [str(self.bits_tdi[0])]])
+ self.putx([29, [str(self.bits_tdo[0])]])
+ # Use self.samplenum as ES of the previous bit.
+ self.bits_samplenums_tdi[0][1] = self.samplenum
+ self.bits_samplenums_tdo[0][1] = self.samplenum
+
+ self.bits_tdi.insert(0, tdi)
+ self.bits_tdo.insert(0, tdo)
+
+ # Use self.samplenum as SS of the current bit.
+ self.bits_samplenums_tdi.insert(0, [self.samplenum, -1])
+ self.bits_samplenums_tdo.insert(0, [self.samplenum, -1])
+
+ # Output all TDI/TDO bits if we just switched to UPDATE-*.
+ if self.state.value.startswith('UPDATE-'):
+
+ self.es_bitstring = self.samplenum
+
+ t = self.state.value[-2:] + ' TDI'
+ b = ''.join(map(str, self.bits_tdi[1:]))
+ h = ' (0x%x' % int('0b0' + b, 2) + ')'
+ s = t + ': ' + b + h + ', ' + str(len(self.bits_tdi[1:])) + ' bits'
+ self.putx_bs([30, [s]])
+ self.putp_bs([t, [b, self.bits_samplenums_tdi[1:]]])
+ self.bits_tdi = []
+ self.bits_samplenums_tdi = []
+
+ t = self.state.value[-2:] + ' TDO'
+ b = ''.join(map(str, self.bits_tdo[1:]))
+ h = ' (0x%x' % int('0b0' + b, 2) + ')'
+ s = t + ': ' + b + h + ', ' + str(len(self.bits_tdo[1:])) + ' bits'
+ self.putx_bs([31, [s]])
+ self.putp_bs([t, [b, self.bits_samplenums_tdo[1:]]])
+ self.bits_tdo = []
+ self.bits_samplenums_tdo = []
+
+ self.first_bit = True
+
+ self.ss_bitstring = self.samplenum
+
+ self.ss_item = self.samplenum
+
+ def handle_tmsc_edge(self):
+ self.escape_edges += 1
+
+ def handle_tapc_state(self):
+ self.oldcjtagstate = self.cjtagstate
+
+ if self.escape_edges >= 8:
+ self.cjtagstate = CSt.FOUR_WIRE
+ if self.escape_edges == 6:
+ self.cjtagstate = CSt.CJTAG_OAC
+ self.oacp = 0
+ self.oaclen = 12
+
+ self.escape_edges = 0
+
+ def decode(self):
+ tdi = tms = tdo = 0
+
+ while True:
+ # Wait for a rising edge on TCKC.
+ tckc, tmsc = self.wait({0: 'r'})
+ self.handle_tapc_state()
+
+ if self.cjtagstate == CSt.OSCAN1:
+ if self.oscan1cycle == 0: # nTDI
+ tdi = 1 if (tmsc == 0) else 0
+ self.oscan1cycle = 1
+ elif self.oscan1cycle == 1: # TMS
+ tms = tmsc
+ self.oscan1cycle = 2
+ elif self.oscan1cycle == 2: # TDO
+ tdo = tmsc
+ self.handle_rising_tckc_edge(tdi, tdo, tckc, tms)
+ self.oscan1cycle = 0
+ else:
+ self.handle_rising_tckc_edge(None, None, tckc, tmsc)
+
+ while (tckc == 1):
+ tckc, tmsc_n = self.wait([{0: 'f'}, {1: 'e'}])
+ if tmsc_n != tmsc:
+ tmsc = tmsc_n
+ self.handle_tmsc_edge()
diff --git a/libsigrokdecode4DSL/decoders/dali/pd.py b/libsigrokdecode4DSL/decoders/dali/pd.py
index 53147463..419e364b 100644
--- a/libsigrokdecode4DSL/decoders/dali/pd.py
+++ b/libsigrokdecode4DSL/decoders/dali/pd.py
@@ -42,7 +42,7 @@ class Decoder(srd.Decoder):
)
annotations = (
('bit', 'Bit'),
- ('startbit', 'Startbit'),
+ ('startbit', 'Start bit'),
('sbit', 'Select bit'),
('ybit', 'Individual or group'),
('address', 'Address'),
@@ -52,7 +52,7 @@ class Decoder(srd.Decoder):
)
annotation_rows = (
('bits', 'Bits', (0,)),
- ('raw', 'Raw data', (7,)),
+ ('raw-data', 'Raw data', (7,)),
('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
)
@@ -61,7 +61,6 @@ def __init__(self):
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
self.dev_type = None
diff --git a/libsigrokdecode4DSL/decoders/dcf77/pd.py b/libsigrokdecode4DSL/decoders/dcf77/pd.py
index 7365134e..acb6bdaf 100644
--- a/libsigrokdecode4DSL/decoders/dcf77/pd.py
+++ b/libsigrokdecode4DSL/decoders/dcf77/pd.py
@@ -39,7 +39,7 @@ class Decoder(srd.Decoder):
)
annotations = (
('start-of-minute', 'Start of minute'),
- ('special-bits', 'Special bits (civil warnings, weather forecast)'),
+ ('special-bit', 'Special bit (civil warnings, weather forecast)'),
('call-bit', 'Call bit'),
('summer-time', 'Summer time announcement'),
('cest', 'CEST bit'),
@@ -55,9 +55,9 @@ class Decoder(srd.Decoder):
('month', 'Month'),
('year', 'Year'),
('date-parity', 'Date parity bit'),
- ('raw-bits', 'Raw bits'),
- ('unknown-bits', 'Unknown bits'),
- ('warnings', 'Human-readable warnings'),
+ ('raw-bit', 'Raw bit'),
+ ('unknown-bit', 'Unknown bit'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('bits', 'Bits', (17, 18)),
diff --git a/libsigrokdecode4DSL/decoders/dmx512/__init__.py b/libsigrokdecode4DSL/decoders/dmx512/__init__.py
index b5e57836..588f6977 100644
--- a/libsigrokdecode4DSL/decoders/dmx512/__init__.py
+++ b/libsigrokdecode4DSL/decoders/dmx512/__init__.py
@@ -1,7 +1,7 @@
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2016 Fabian J. Stumpf
+## Copyright (C) 2019 Gerhard Sittig
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
diff --git a/libsigrokdecode4DSL/decoders/dmx512/pd.py b/libsigrokdecode4DSL/decoders/dmx512/pd.py
index 355095ee..a0cd83f3 100644
--- a/libsigrokdecode4DSL/decoders/dmx512/pd.py
+++ b/libsigrokdecode4DSL/decoders/dmx512/pd.py
@@ -2,6 +2,7 @@
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Fabian J. Stumpf
+## Copyright (C) 2019-2020 Gerhard Sittig
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -17,8 +18,54 @@
## along with this program; if not, see .
##
+'''
+OUTPUT_PYTHON format:
+
+Packet:
+[, ]
+
+This is the list of codes and their respective values:
+ - 'PACKET': The data is a list of tuples with the bytes' start and end
+ positions as well as a byte value and a validity flag. This output
+ represents a DMX packet. The sample numbers span the range beginning
+ at the start of the start code and ending at the end of the last data
+ byte in the packet. The start code value resides at index 0.
+
+Developer notes on the DMX512 protocol:
+
+See Wikipedia for an overview:
+ https://en.wikipedia.org/wiki/DMX512#Electrical (physics, transport)
+ https://en.wikipedia.org/wiki/DMX512#Protocol (UART frames, DMX frames)
+ RS-485 transport, differential thus either polarity (needs user spec)
+ 8n2 UART frames at 250kbps, BREAK to start a new DMX frame
+ slot 0 carries start code, slot 1 up to 512 max carry data for peripherals
+ start code 0 for "boring lights", non-zero start code for extensions.
+
+TODO
+- Cover more DMX packet types beyond start code 0x00 (standard). See
+ https://en.wikipedia.org/wiki/DMX512#Protocol for a list (0x17 text,
+ 0xcc RDM, 0xcf sysinfo) and a reference to the ESTA database. These
+ can either get added here or can get implemented in a stacked decoder.
+- Run on more captures as these become available. Verify the min/max
+ BREAK, MARK, and RESET to RESET period checks. Add more conditions that
+ are worth checking to determine the health of the bus, see the (German)
+ http://www.soundlight.de/techtips/dmx512/dmx2000a.htm article for ideas.
+- Is there a more user friendly way of having the DMX512 decoder configure
+ the UART decoder's parameters? Currently users need to setup the polarity
+ (which is acceptable, and an essential feature), but also the bitrate and
+ frame format (which may or may not be considered acceptable).
+- (Not a DMX512 decoder TODO item) Current UART decoder implementation does
+ not handle two STOP bits, but DMX512 will transparently benefit when UART
+ gets adjusted. Until then the second STOP bit will be mistaken for a MARK
+ but that's just cosmetics, available data gets interpreted correctly.
+'''
+
import sigrokdecode as srd
+class Ann:
+ BREAK, MAB, INTERFRAME, INTERPACKET, STARTCODE, DATABYTE, CHANNEL_DATA, \
+ SLOT_DATA, RESET, WARN, ERROR = range(11)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'dmx512'
@@ -26,34 +73,49 @@ class Decoder(srd.Decoder):
longname = 'Digital MultipleX 512'
desc = 'Digital MultipleX 512 (DMX512) lighting protocol.'
license = 'gplv2+'
- inputs = ['logic']
- outputs = []
+ inputs = ['uart']
+ outputs = ['dmx512']
tags = ['Embedded/industrial', 'Lighting']
- channels = (
- {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'},
- )
options = (
- {'id': 'invert', 'desc': 'Invert Signal?', 'default': 'no',
- 'values': ('yes', 'no')},
+ {'id': 'min_break', 'desc': 'Minimum BREAK length (us)', 'default': 88},
+ {'id': 'max_mark', 'desc': 'Maximum MARK length (us)', 'default': 1000000},
+ {'id': 'min_break_break', 'desc': 'Minimum BREAK to BREAK interval (us)',
+ 'default': 1196},
+ {'id': 'max_reset_reset', 'desc': 'Maximum RESET to RESET interval (us)',
+ 'default': 1250000},
+ {'id': 'show_zero', 'desc': 'Display all-zero set-point values',
+ 'default': 'no', 'values': ('yes', 'no')},
+ {'id': 'format', 'desc': 'Data format', 'default': 'dec',
+ 'values': ('dec', 'hex', 'bin')},
)
annotations = (
- ('bit', 'Bit'),
+ # Lowest layer (above UART): BREAK MARK ( FRAME [MARK] )*
+ # with MARK being after-break or inter-frame or inter-packet.
('break', 'Break'),
('mab', 'Mark after break'),
- ('startbit', 'Start bit'),
- ('stopbits', 'Stop bit'),
- ('startcode', 'Start code'),
- ('channel', 'Channel'),
('interframe', 'Interframe'),
('interpacket', 'Interpacket'),
- ('data', 'Data'),
+ # Next layer: STARTCODE ( DATABYTE )*
+ ('startcode', 'Start code'),
+ ('databyte', 'Data byte'),
+ # Next layer: CHANNEL or SLOT values
+ ('chan_data', 'Channel data'),
+ ('slot_data', 'Slot data'),
+ # Next layer: RESET
+ ('reset', 'Reset sequence'),
+ # Warnings and errors.
+ ('warning', 'Warning'),
('error', 'Error'),
)
annotation_rows = (
- ('name', 'Logical', (1, 2, 5, 6, 7, 8)),
- ('data', 'Data', (9,)),
- ('bits', 'Bits', (0, 3, 4)),
- ('errors', 'Errors', (10,)),
+ ('dmx_fields', 'Fields', (Ann.BREAK, Ann.MAB,
+ Ann.STARTCODE, Ann.INTERFRAME,
+ Ann.DATABYTE, Ann.INTERPACKET)),
+ ('chans_data', 'Channels data', (Ann.CHANNEL_DATA,)),
+ ('slots_data', 'Slots data', (Ann.SLOT_DATA,)),
+ ('resets', 'Reset sequences', (Ann.RESET,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ ('errors', 'Errors', (Ann.ERROR,)),
)
def __init__(self):
@@ -61,119 +123,239 @@ def __init__(self):
def reset(self):
self.samplerate = None
- self.sample_usec = None
- self.run_start = -1
- self.state = 'FIND BREAK'
+ self.samples_per_usec = None
+ self.last_reset = None
+ self.last_break = None
+ self.packet = None
+ self.last_es = None
+ self.last_frame = None
+ self.start_code = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_python = self.register(srd.OUTPUT_PYTHON)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- self.sample_usec = 1 / value * 1000000
- self.skip_per_bit = int(4 / self.sample_usec)
-
- def putr(self, data):
- self.put(self.run_start, self.samplenum, self.out_ann, data)
-
- def decode(self):
- if not self.samplerate:
- raise SamplerateError('Cannot decode without samplerate.')
-
- inv = self.options['invert'] == 'yes'
-
- (dmx,) = self.wait({0: 'h' if inv else 'l'})
- self.run_start = self.samplenum
-
- while True:
- # Seek for an interval with no state change with a length between
- # 88 and 1000000 us (BREAK).
- if self.state == 'FIND BREAK':
- (dmx,) = self.wait({0: 'f' if inv else 'r'})
- runlen = (self.samplenum - self.run_start) * self.sample_usec
- if runlen > 88 and runlen < 1000000:
- self.putr([1, ['Break']])
- self.state = 'MARK MAB'
- self.channel = 0
- elif runlen >= 1000000:
- # Error condition.
- self.putr([10, ['Invalid break length']])
- else:
- (dmx,) = self.wait({0: 'h' if inv else 'l'})
- self.run_start = self.samplenum
- # Directly following the BREAK is the MARK AFTER BREAK.
- elif self.state == 'MARK MAB':
- self.run_start = self.samplenum
- (dmx,) = self.wait({0: 'r' if inv else 'f'})
- self.putr([2, ['MAB']])
- self.state = 'READ BYTE'
- self.channel = 0
- self.bit = 0
- self.aggreg = dmx
- self.run_start = self.samplenum
- # Mark and read a single transmitted byte
- # (start bit, 8 data bits, 2 stop bits).
- elif self.state == 'READ BYTE':
- bit_start = self.samplenum
- bit_end = self.run_start + (self.bit + 1) * self.skip_per_bit
- (dmx,) = self.wait({'skip': round(self.skip_per_bit/2)})
- bit_value = not dmx if inv else dmx
-
- if self.bit == 0:
- self.byte = 0
- self.put(bit_start, bit_end,
- self.out_ann, [3, ['Start bit']])
- if bit_value != 0:
- # (Possibly) invalid start bit, mark but don't fail.
- self.put(bit_start, bit_end,
- self.out_ann, [10, ['Invalid start bit']])
- elif self.bit >= 9:
- self.put(bit_start, bit_end,
- self.out_ann, [4, ['Stop bit']])
- if bit_value != 1:
- # Invalid stop bit, mark.
- self.put(bit_start, bit_end,
- self.out_ann, [10, ['Invalid stop bit']])
- if self.bit == 10:
- # On invalid 2nd stop bit, search for new break.
- self.state = 'FIND BREAK'
- else:
- # Label and process one bit.
- self.put(bit_start, bit_end,
- self.out_ann, [0, [str(bit_value)]])
- self.byte |= bit_value << (self.bit - 1)
-
- # Label a complete byte.
- if self.state == 'READ BYTE' and self.bit == 10:
- if self.channel == 0:
- d = [5, ['Start code']]
- else:
- d = [6, ['Channel ' + str(self.channel)]]
- self.put(self.run_start, bit_end, self.out_ann, d)
- self.put(self.run_start + self.skip_per_bit,
- bit_end - 2 * self.skip_per_bit,
- self.out_ann, [9, [str(self.byte) + ' / ' + \
- str(hex(self.byte))]])
- # Continue by scanning the IFT.
- self.channel += 1
- self.run_start = self.samplenum
- self.state = 'MARK IFT'
-
- self.bit += 1
- (dmx,) = self.wait({'skip': round(bit_end - self.samplenum)})
- # Mark the INTERFRAME-TIME between bytes / INTERPACKET-TIME between packets.
- elif self.state == 'MARK IFT':
- self.run_start = self.samplenum
- if self.channel > 512:
- (dmx,) = self.wait({0: 'h' if inv else 'l'})
- self.putr([8, ['Interpacket']])
- self.state = 'FIND BREAK'
- self.run_start = self.samplenum
- else:
- if (not dmx if inv else dmx):
- (dmx,) = self.wait({0: 'h' if inv else 'l'})
- self.putr([7, ['Interframe']])
- self.state = 'READ BYTE'
- self.bit = 0
- self.run_start = self.samplenum
+ self.samples_per_usec = value / 1000000
+
+ def have_samplerate(self):
+ return bool(self.samplerate)
+
+ def samples_to_usecs(self, count):
+ return count / self.samples_per_usec
+
+ def putg(self, ss, es, data):
+ self.put(ss, es, self.out_ann, data)
+
+ def putpy(self, ss, es, data):
+ self.put(ss, es, self.out_python, data)
+
+ def format_value(self, v):
+ fmt = self.options['format']
+ if fmt == 'dec':
+ return '{:d}'.format(v)
+ if fmt == 'hex':
+ return '{:02X}'.format(v)
+ if fmt == 'bin':
+ return '{:08b}'.format(v)
+ return '{}'.format(v)
+
+ def flush_packet(self):
+ if self.packet:
+ ss, es = self.packet[0][0], self.packet[-1][1]
+ self.putpy(ss, es, ['PACKET', self.packet])
+ self.packet = None
+
+ def flush_reset(self, ss, es):
+ if ss is not None and es is not None:
+ self.putg(ss, es, [Ann.RESET, ['RESET SEQUENCE', 'RESET', 'R']])
+ if self.last_reset and self.have_samplerate():
+ duration = self.samples_to_usecs(es - self.last_reset)
+ if duration > self.options['max_reset_reset']:
+ txts = ['Excessive RESET to RESET interval', 'RESET to RESET', 'RESET']
+ self.putg(self.last_reset, es, [Ann.WARN, txts])
+ self.last_reset = es
+
+ def flush_break(self, ss, es):
+ self.putg(ss, es, [Ann.BREAK, ['BREAK', 'B']])
+ if self.have_samplerate():
+ duration = self.samples_to_usecs(es - ss)
+ if duration < self.options['min_break']:
+ txts = ['Short BREAK period', 'Short BREAK', 'BREAK']
+ self.putg(ss, es, [Ann.WARN, txts])
+ if self.last_break:
+ duration = self.samples_to_usecs(ss - self.last_break)
+ if duration < self.options['min_break_break']:
+ txts = ['Short BREAK to BREAK interval', 'Short BREAK to BREAK', 'BREAK']
+ self.putg(ss, es, [Ann.WARN, txts])
+ self.last_break = ss
+ self.last_es = es
+
+ def flush_mark(self, ss, es, is_mab = False, is_if = False, is_ip = False):
+ '''Handle several kinds of MARK conditions.'''
+
+ if ss is None or es is None or ss >= es:
+ return
+
+ if is_mab:
+ ann = Ann.MAB
+ txts = ['MARK AFTER BREAK', 'MAB']
+ elif is_if:
+ ann = Ann.INTERFRAME
+ txts = ['INTER FRAME', 'IF']
+ elif is_ip:
+ ann = Ann.INTERPACKET
+ txts = ['INTER PACKET', 'IP']
+ else:
+ return
+ self.putg(ss, es, [ann, txts])
+
+ if self.have_samplerate():
+ duration = self.samples_to_usecs(es - ss)
+ if duration > self.options['max_mark']:
+ txts = ['Excessive MARK length', 'MARK length', 'MARK']
+ self.putg(ss, es, [Ann.ERROR, txts])
+
+ def flush_frame(self, ss, es, value, valid):
+ '''Handle UART frame content. Accumulate DMX packet.'''
+
+ if not valid:
+ txts = ['Invalid frame', 'Frame']
+ self.putg(ss, es, [Ann.ERROR, txts])
+
+ self.last_es = es
+
+ # Cease packet inspection before first BREAK.
+ if not self.last_break:
+ return
+
+ # Accumulate the sequence of bytes for the current DMX frame.
+ # Emit the annotation at the "DMX fields" level.
+ is_start = self.packet is None
+ if is_start:
+ self.packet = []
+ slot_nr = len(self.packet)
+ item = (ss, es, value, valid)
+ self.packet.append(item)
+ if is_start:
+ # Slot 0, the start code. Determines the DMX frame type.
+ self.start_code = value
+ ann = Ann.STARTCODE
+ val_text = self.format_value(value)
+ txts = [
+ 'STARTCODE {}'.format(val_text),
+ 'START {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ else:
+ # Slot 1+, the payload bytes.
+ ann = Ann.DATABYTE
+ val_text = self.format_value(value)
+ txts = [
+ 'DATABYTE {:d}: {}'.format(slot_nr, val_text),
+ 'DATA {:d}: {}'.format(slot_nr, val_text),
+ 'DATA {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ self.putg(ss, es, [ann, txts])
+
+ # Tell channel data for peripherals from arbitrary slot values.
+ # Can get extended for other start code types in case protocol
+ # extensions are handled here and not in stacked decoders.
+ if is_start:
+ ann = None
+ elif self.start_code == 0:
+ # Start code was 0. Slots carry values for channels.
+ # Optionally suppress zero-values to make used channels
+ # stand out, to help users focus their attention.
+ ann = Ann.CHANNEL_DATA
+ if value == 0 and self.options['show_zero'] == 'no':
+ ann = None
+ else:
+ val_text = self.format_value(value)
+ txts = [
+ 'CHANNEL {:d}: {}'.format(slot_nr, val_text),
+ 'CH {:d}: {}'.format(slot_nr, val_text),
+ 'CH {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ else:
+ # Unhandled start code. Provide "anonymous" values.
+ ann = Ann.SLOT_DATA
+ val_text = self.format_value(value)
+ txts = [
+ 'SLOT {:d}: {}'.format(slot_nr, val_text),
+ 'SL {:d}: {}'.format(slot_nr, val_text),
+ 'SL {}'.format(val_text),
+ '{}'.format(val_text),
+ ]
+ if ann is not None:
+ self.putg(ss, es, [ann, txts])
+
+ if is_start and value == 0:
+ self.flush_reset(self.last_break, es)
+
+ def handle_break(self, ss, es):
+ '''Handle UART BREAK conditions.'''
+
+ # Check the last frame before BREAK if one was queued. It could
+ # have been "invalid" since the STOP bit check failed. If there
+ # is an invalid frame which happens to start at the start of the
+ # BREAK condition, then discard it. Otherwise flush its output.
+ last_frame = self.last_frame
+ self.last_frame = None
+ frame_invalid = last_frame and not last_frame[3]
+ frame_zero_data = last_frame and last_frame[2] == 0
+ frame_is_break = last_frame and last_frame[0] == ss
+ if frame_invalid and frame_zero_data and frame_is_break:
+ last_frame = None
+ if last_frame is not None:
+ self.flush_frame(*last_frame)
+
+ # Handle inter-packet MARK (works for zero length, too).
+ self.flush_mark(self.last_es, ss, is_ip = True)
+
+ # Handle accumulated packets.
+ self.flush_packet()
+ self.packet = None
+
+ # Annotate the BREAK condition. Start accumulation of a packet.
+ self.flush_break(ss, es)
+
+ def handle_frame(self, ss, es, value, valid):
+ '''Handle UART data frames.'''
+
+ # Flush previously deferred frame (if available). Can't have been
+ # BREAK if another data frame follows.
+ last_frame = self.last_frame
+ self.last_frame = None
+ if last_frame:
+ self.flush_frame(*last_frame)
+
+ # Handle inter-frame MARK (works for zero length, too).
+ is_mab = self.last_break and self.packet is None
+ is_if = self.packet
+ self.flush_mark(self.last_es, ss, is_mab = is_mab, is_if = is_if)
+
+ # Defer handling of invalid frames, because they may start a new
+ # BREAK which we will only learn about much later. Immediately
+ # annotate valid frames.
+ if valid:
+ self.flush_frame(ss, es, value, valid)
+ else:
+ self.last_frame = (ss, es, value, valid)
+
+ def decode(self, ss, es, data):
+ # Lack of a sample rate in the input capture only disables the
+ # optional warnings about exceeded timespans here at the DMX512
+ # decoder level. That the lower layer UART decoder depends on a
+ # sample rate is handled there, and is not relevant here.
+
+ ptype, rxtx, pdata = data
+ if ptype == 'BREAK':
+ self.handle_break(ss, es)
+ elif ptype == 'FRAME':
+ value, valid = pdata
+ self.handle_frame(ss, es, value, valid)
diff --git a/libsigrokdecode4DSL/decoders/ds1307/pd.py b/libsigrokdecode4DSL/decoders/ds1307/pd.py
index f8ebe195..51d673d3 100644
--- a/libsigrokdecode4DSL/decoders/ds1307/pd.py
+++ b/libsigrokdecode4DSL/decoders/ds1307/pd.py
@@ -1,7 +1,7 @@
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2012-2014 Uwe Hermann
+## Copyright (C) 2012-2020 Uwe Hermann
## Copyright (C) 2013 Matt Ranostay
##
## This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,7 @@
import re
import sigrokdecode as srd
-from common.srdhelper import bcd2int
+from common.srdhelper import bcd2int, SrdIntEnum
days_of_week = (
'Sunday', 'Monday', 'Tuesday', 'Wednesday',
@@ -47,10 +47,15 @@
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]
+ 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)
+a = ['REG_' + r.upper() for r in regs] + \
+ ['BIT_' + re.sub('\/| ', '_', b).upper() for b in bits] + \
+ ['READ_DATE_TIME', 'WRITE_DATE_TIME', 'READ_REG', 'WRITE_REG', 'WARNING']
+Ann = SrdIntEnum.from_list('Ann', a)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'ds1307'
@@ -62,17 +67,17 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['Clock/timing', 'IC']
annotations = regs_and_bits() + (
- ('read-datetime', 'Read date/time'),
- ('write-datetime', 'Write date/time'),
- ('reg-read', 'Register read'),
- ('reg-write', 'Register write'),
- ('warnings', 'Warnings'),
+ ('read_date_time', 'Read date/time'),
+ ('write_date_time', 'Write date/time'),
+ ('read_reg', 'Register read'),
+ ('write_reg', 'Register write'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('bits', 'Bits', tuple(range(9, 24))),
- ('regs', 'Registers', tuple(range(9))),
- ('date-time', 'Date/time', (24, 25, 26, 27)),
- ('warnings', 'Warnings', (28,)),
+ ('bits', 'Bits', Ann.prefixes('BIT_')),
+ ('regs', 'Registers', Ann.prefixes('REG_')),
+ ('date_time', 'Date/time', Ann.prefixes('READ_ WRITE_')),
+ ('warnings', 'Warnings', (Ann.WARNING,)),
)
def __init__(self):
@@ -100,84 +105,85 @@ def putd(self, bit1, bit2, data):
def putr(self, bit):
self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann,
- [11, ['Reserved bit', 'Reserved', 'Rsvd', 'R']])
+ [Ann.BIT_RESERVED, ['Reserved bit', 'Reserved', 'Rsvd', 'R']])
def handle_reg_0x00(self, b): # Seconds (0-59) / Clock halt bit
- self.putd(7, 0, [0, ['Seconds', 'Sec', 'S']])
+ self.putd(7, 0, [Ann.REG_SECONDS, ['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']])
+ self.putd(7, 7, [Ann.BIT_CLOCK_HALT, ['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']])
+ self.putd(6, 0, [Ann.BIT_SECONDS, ['Second: %d' % s, 'Sec: %d' % s,
+ 'S: %d' % s, 'S']])
def handle_reg_0x01(self, b): # Minutes (0-59)
- self.putd(7, 0, [1, ['Minutes', 'Min', 'M']])
+ self.putd(7, 0, [Ann.REG_MINUTES, ['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']])
+ self.putd(6, 0, [Ann.BIT_MINUTES, ['Minute: %d' % m, 'Min: %d' % m, 'M: %d' % m, 'M']])
def handle_reg_0x02(self, b): # Hours (1-12+AM/PM or 0-23)
- self.putd(7, 0, [2, ['Hours', 'H']])
+ self.putd(7, 0, [Ann.REG_HOURS, ['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']])
+ self.putd(6, 6, [Ann.BIT_12_24_HOURS, ['12-hour mode', '12h mode', '12h']])
a = 'PM' if (b & (1 << 5)) else 'AM'
- self.putd(5, 5, [14, [a, a[0]]])
+ self.putd(5, 5, [Ann.BIT_AM_PM, [a, a[0]]])
h = self.hours = bcd2int(b & 0x1f)
- self.putd(4, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
+ self.putd(4, 0, [Ann.BIT_HOURS, ['Hour: %d' % h, 'H: %d' % h, 'H']])
else:
- self.putd(6, 6, [13, ['24-hour mode', '24h mode', '24h']])
+ self.putd(6, 6, [Ann.BIT_12_24_HOURS, ['24-hour mode', '24h mode', '24h']])
h = self.hours = bcd2int(b & 0x3f)
- self.putd(5, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
+ self.putd(5, 0, [Ann.BIT_HOURS, ['Hour: %d' % h, 'H: %d' % h, 'H']])
def handle_reg_0x03(self, b): # Day / day of week (1-7)
- self.putd(7, 0, [3, ['Day of week', 'Day', 'D']])
+ self.putd(7, 0, [Ann.REG_DAY, ['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']])
+ self.putd(2, 0, [Ann.BIT_DAY, ['Weekday: %s' % ws, 'WD: %s' % ws, 'WD', 'W']])
def handle_reg_0x04(self, b): # Date (1-31)
- self.putd(7, 0, [4, ['Date', 'D']])
+ self.putd(7, 0, [Ann.REG_DATE, ['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']])
+ self.putd(5, 0, [Ann.BIT_DATE, ['Date: %d' % d, 'D: %d' % d, 'D']])
def handle_reg_0x05(self, b): # Month (1-12)
- self.putd(7, 0, [5, ['Month', 'Mon', 'M']])
+ self.putd(7, 0, [Ann.REG_MONTH, ['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']])
+ self.putd(4, 0, [Ann.BIT_MONTH, ['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']])
+ self.putd(7, 0, [Ann.REG_YEAR, ['Year', 'Y']])
y = self.years = bcd2int(b & 0xff)
self.years += 2000
- self.putd(7, 0, [19, ['Year: %d' % y, 'Y: %d' % y, 'Y']])
+ self.putd(7, 0, [Ann.BIT_YEAR, ['Year: %d' % y, 'Y: %d' % y, 'Y']])
def handle_reg_0x07(self, b): # Control Register
- self.putd(7, 0, [7, ['Control', 'Ctrl', 'C']])
+ self.putd(7, 0, [Ann.REG_CONTROL, ['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,
+ self.putd(7, 7, [Ann.BIT_OUT, ['Output control: %d' % o,
'OUT: %d' % o, 'O: %d' % o, 'O']])
- self.putd(4, 4, [21, ['Square wave output: %sabled' % s2,
+ self.putd(4, 4, [Ann.BIT_SQWE, ['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,
+ self.putd(1, 0, [Ann.BIT_RS, ['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]])
+ self.putd(7, 0, [Ann.REG_RAM, ['RAM', 'R']])
+ self.putd(7, 0, [Ann.BIT_RAM, ['SRAM: 0x%02X' % b, '0x%02X' % b]])
def output_datetime(self, cls, rw):
# TODO: Handle read/write of only parts of these items.
@@ -201,7 +207,7 @@ 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]])
+ [Ann.WARNING, ['Ignoring non-DS1307 data (slave 0x%02X)' % addr]])
return False
def decode(self, ss, es, data):
@@ -246,7 +252,7 @@ def decode(self, ss, es, data):
if cmd == 'DATA WRITE':
self.handle_reg(databyte)
elif cmd == 'STOP':
- self.output_datetime(25, 'Written')
+ self.output_datetime(Ann.WRITE_DATE_TIME, 'Written')
self.state = 'IDLE'
elif self.state == 'READ RTC REGS':
# Wait for an address read operation.
@@ -260,5 +266,5 @@ def decode(self, ss, es, data):
if cmd == 'DATA READ':
self.handle_reg(databyte)
elif cmd == 'STOP':
- self.output_datetime(24, 'Read')
+ self.output_datetime(Ann.READ_DATE_TIME, 'Read')
self.state = 'IDLE'
diff --git a/libsigrokdecode4DSL/decoders/ds2408/pd.py b/libsigrokdecode4DSL/decoders/ds2408/pd.py
index 33f2873f..97fb650f 100644
--- a/libsigrokdecode4DSL/decoders/ds2408/pd.py
+++ b/libsigrokdecode4DSL/decoders/ds2408/pd.py
@@ -40,7 +40,7 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['Embedded/industrial', 'IC']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
def __init__(self):
diff --git a/libsigrokdecode4DSL/decoders/ds243x/pd.py b/libsigrokdecode4DSL/decoders/ds243x/pd.py
index 7f9f6660..0fbdcdf5 100644
--- a/libsigrokdecode4DSL/decoders/ds243x/pd.py
+++ b/libsigrokdecode4DSL/decoders/ds243x/pd.py
@@ -71,7 +71,7 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['IC', 'Memory']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
binary = (
('mem_read', 'Data read from memory'),
diff --git a/libsigrokdecode4DSL/decoders/ds28ea00/pd.py b/libsigrokdecode4DSL/decoders/ds28ea00/pd.py
index 9a578449..39290b45 100644
--- a/libsigrokdecode4DSL/decoders/ds28ea00/pd.py
+++ b/libsigrokdecode4DSL/decoders/ds28ea00/pd.py
@@ -45,7 +45,7 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['IC', 'Sensor']
annotations = (
- ('text', 'Human-readable text'),
+ ('text', 'Text'),
)
def __init__(self):
diff --git a/libsigrokdecode4DSL/decoders/dsi/pd.py b/libsigrokdecode4DSL/decoders/dsi/pd.py
index 7ce95179..b5faf6f6 100644
--- a/libsigrokdecode4DSL/decoders/dsi/pd.py
+++ b/libsigrokdecode4DSL/decoders/dsi/pd.py
@@ -47,7 +47,7 @@ class Decoder(srd.Decoder):
)
annotation_rows = (
('bits', 'Bits', (0,)),
- ('raw', 'Raw data', (3,)),
+ ('raw-vals', 'Raw data', (3,)),
('fields', 'Fields', (1, 2)),
)
@@ -56,7 +56,6 @@ def __init__(self):
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
@@ -112,6 +111,7 @@ def decode(self):
raise SamplerateError('Cannot decode without samplerate.')
bit = 0
while True:
+ # TODO: Come up with more appropriate self.wait() conditions.
(self.dsi,) = self.wait()
if self.options['polarity'] == 'active-high':
self.dsi ^= 1 # Invert.
diff --git a/libsigrokdecode4DSL/decoders/edid/pd.py b/libsigrokdecode4DSL/decoders/edid/pd.py
index 2d7460ce..73405352 100644
--- a/libsigrokdecode4DSL/decoders/edid/pd.py
+++ b/libsigrokdecode4DSL/decoders/edid/pd.py
@@ -26,8 +26,11 @@
# - Extensions
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
import os
+St = SrdIntEnum.from_str('St', 'OFFSET EXTENSIONS HEADER EDID')
+
EDID_HEADER = [0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00]
OFF_VENDOR = 8
OFF_VERSION = 18
@@ -83,12 +86,12 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['Display', 'Memory', 'PC']
annotations = (
- ('fields', 'EDID structure fields'),
- ('sections', 'EDID structure sections'),
+ ('field', 'Field'),
+ ('section', 'Section'),
)
annotation_rows = (
- ('sections', 'Sections', (1,)),
('fields', 'Fields', (0,)),
+ ('sections', 'Sections', (1,)),
)
def __init__(self):
@@ -116,22 +119,22 @@ def decode(self, ss, es, data):
cmd, data = data
if cmd == 'ADDRESS WRITE' and data == 0x50:
- self.state = 'offset'
+ self.state = St.OFFSET
self.ss = ss
return
if cmd == 'ADDRESS READ' and data == 0x50:
if self.extension > 0:
- self.state = 'extensions'
+ self.state = St.EXTENSIONS
s = str(self.extension)
t = ["Extension: " + s, "X: " + s, s]
else:
- self.state = 'header'
+ self.state = St.HEADER
t = ["EDID"]
self.put(ss, es, self.out_ann, [ANN_SECTIONS, t])
return
- if cmd == 'DATA WRITE' and self.state == 'offset':
+ if cmd == 'DATA WRITE' and self.state == St.OFFSET:
self.offset = data
self.extension = self.offset // 128
self.cnt = self.offset % 128
@@ -163,7 +166,7 @@ def decode(self, ss, es, data):
self.sn.append([ss, es])
self.cache.append(data)
- if self.state is None or self.state == 'header':
+ if self.state is None or self.state == St.HEADER:
# Wait for the EDID header
if self.cnt >= OFF_VENDOR:
if self.cache[-8:] == EDID_HEADER:
@@ -171,12 +174,12 @@ def decode(self, ss, es, data):
self.sn = self.sn[-8:]
self.cache = self.cache[-8:]
self.cnt = 8
- self.state = 'edid'
+ self.state = St.EDID
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':
+ elif self.state == St.EDID:
if self.cnt == OFF_VERSION:
self.decode_vid(-10)
self.decode_pid(-8)
@@ -224,9 +227,9 @@ def decode(self, ss, es, data):
csstr = 'WRONG!'
self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % (
self.cache[self.cnt-1], csstr)]])
- self.state = 'extensions'
+ self.state = St.EXTENSIONS
- elif self.state == 'extensions':
+ elif self.state == St.EXTENSIONS:
cache = self.ext_cache[self.extension - 1]
sn = self.ext_sn[self.extension - 1]
v = cache[self.cnt - 1]
diff --git a/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py b/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py
index c6ee63d5..a66cb19a 100644
--- a/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py
+++ b/libsigrokdecode4DSL/decoders/eeprom24xx/lists.py
@@ -189,6 +189,16 @@
'addr_pins': 3, # Called E0, E1, E2 on this chip.
'max_speed': 400,
},
+ 'st_m24c32': {
+ 'vendor': 'ST',
+ 'model': 'M24C32',
+ 'size': 4 * 1024,
+ 'page_size': 32,
+ 'page_wraparound': True,
+ 'addr_bytes': 2,
+ 'addr_pins': 3, # Called E0, E1, E2 on this chip.
+ 'max_speed': 1000,
+ },
# Xicor
'xicor_x24c02': {
diff --git a/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py b/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py
index 033a44b2..549ee2df 100644
--- a/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py
+++ b/libsigrokdecode4DSL/decoders/eeprom24xx/pd.py
@@ -38,7 +38,7 @@ class Decoder(srd.Decoder):
)
annotations = (
# Warnings
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
# Bits/bytes
('control-code', 'Control code'),
('address-pin', 'Address pin (A0/A1/A2)'),
diff --git a/libsigrokdecode4DSL/decoders/eeprom93xx/pd.py b/libsigrokdecode4DSL/decoders/eeprom93xx/pd.py
index 7b64e59a..8c08fc80 100644
--- a/libsigrokdecode4DSL/decoders/eeprom93xx/pd.py
+++ b/libsigrokdecode4DSL/decoders/eeprom93xx/pd.py
@@ -32,6 +32,8 @@ class Decoder(srd.Decoder):
options = (
{'id': 'addresssize', 'desc': 'Address size', 'default': 8},
{'id': 'wordsize', 'desc': 'Word size', 'default': 16},
+ {'id': 'format', 'desc': 'Data format', 'default': 'hex',
+ 'values': ('ascii', 'hex')},
)
annotations = (
('si-data', 'SI data'),
@@ -42,6 +44,10 @@ class Decoder(srd.Decoder):
('data', 'Data', (0, 1)),
('warnings', 'Warnings', (2,)),
)
+ binary = (
+ ('address', 'Address'),
+ ('data', 'Data'),
+ )
def __init__(self):
self.reset()
@@ -51,6 +57,7 @@ def reset(self):
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_binary = self.register(srd.OUTPUT_BINARY)
self.addresssize = self.options['addresssize']
self.wordsize = self.options['wordsize']
@@ -60,7 +67,8 @@ def put_address(self, data):
for b in range(len(data)):
a += (data[b].si << (len(data) - b - 1))
self.put(data[0].ss, data[-1].es, self.out_ann,
- [0, ['Address: 0x%x' % a, 'Addr: 0x%x' % a, '0x%x' % a]])
+ [0, ['Address: 0x%04x' % a, 'Addr: 0x%04x' % a, '0x%04x' % a]])
+ self.put(data[0].ss, data[-1].es, self.out_binary, [0, bytes([a])])
def put_word(self, si, data):
# Decode word (MSb first).
@@ -69,8 +77,22 @@ def put_word(self, si, data):
d = data[b].si if si else data[b].so
word += (d << (len(data) - b - 1))
idx = 0 if si else 1
- self.put(data[0].ss, data[-1].es,
- self.out_ann, [idx, ['Data: 0x%x' % word, '0x%x' % word]])
+
+ if self.options['format'] == 'ascii':
+ word_str = ''
+ for s in range(0, len(data), 8):
+ c = 0xff & (word >> s)
+ if c in range(32, 126 + 1):
+ word_str = chr(c) + word_str
+ else:
+ word_str = '[{:02X}]'.format(c) + word_str
+ self.put(data[0].ss, data[-1].es,
+ self.out_ann, [idx, ['Data: %s' % word_str, '%s' % word_str]])
+ else:
+ self.put(data[0].ss, data[-1].es,
+ self.out_ann, [idx, ['Data: 0x%04x' % word, '0x%04x' % word]])
+ self.put(data[0].ss, data[-1].es, self.out_binary,
+ [1, bytes([(word & 0xff00) >> 8, word & 0xff])])
def decode(self, ss, es, data):
if len(data) < (2 + self.addresssize):
diff --git a/libsigrokdecode4DSL/decoders/enc28j60/pd.py b/libsigrokdecode4DSL/decoders/enc28j60/pd.py
index 1bd69a50..e8ce6e7b 100644
--- a/libsigrokdecode4DSL/decoders/enc28j60/pd.py
+++ b/libsigrokdecode4DSL/decoders/enc28j60/pd.py
@@ -67,9 +67,9 @@ class Decoder(srd.Decoder):
('warning', 'Warning'),
)
annotation_rows = (
+ ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
('commands', 'Commands',
(ANN_RCR, ANN_RBM, ANN_WCR, ANN_WBM, ANN_BFS, ANN_BFC, ANN_SRC)),
- ('fields', 'Fields', (ANN_DATA, ANN_REG_ADDR)),
('warnings', 'Warnings', (ANN_WARNING,)),
)
@@ -130,10 +130,14 @@ def _put_register_header(self):
if reg_name is None:
# We don't know the bank we're in yet.
- self.putr([ANN_REG_ADDR, ['Reg Bank ? Addr {$}', '?:{$}', '@%02X' % reg_addr]])
- self.putr([ANN_WARNING, ['Warning: Register bank not known yet.', 'Warning']])
+ self.putr([ANN_REG_ADDR, [
+ 'Reg Bank ? Addr 0x{0:02X}'.format(reg_addr),
+ '?:{0:02X}'.format(reg_addr)]])
+ self.putr([ANN_WARNING, ['Warning: Register bank not known yet.',
+ 'Warning']])
else:
- self.putr([ANN_REG_ADDR, ['Reg {0}'.format(reg_name), '{0}'.format(reg_name)]])
+ self.putr([ANN_REG_ADDR, ['Reg {0}'.format(reg_name),
+ '{0}'.format(reg_name)]])
if (reg_name == '-') or (reg_name == 'Reserved'):
self.putr([ANN_WARNING, ['Warning: Invalid register accessed.',
@@ -147,9 +151,11 @@ def _put_data_byte(self, data, byte_index, binary=False):
self.range_es = self.ranges[byte_index + 1][0]
if binary:
- self.putr([ANN_DATA, ['Data 0b{0:08b}'.format(data), '{0:08b}'.format(data)]])
+ self.putr([ANN_DATA, ['Data 0b{0:08b}'.format(data),
+ '{0:08b}'.format(data)]])
else:
- self.putr([ANN_DATA, ['Data {$}','{S}', '@%02X' % data]])
+ self.putr([ANN_DATA, ['Data 0x{0:02X}'.format(data),
+ '{0:02X}'.format(data)]])
def _put_command_warning(self, reason):
self.putc([ANN_WARNING, ['Warning: {0}'.format(reason), 'Warning']])
diff --git a/libsigrokdecode4DSL/decoders/flexray/pd.py b/libsigrokdecode4DSL/decoders/flexray/pd.py
index fc52b95f..8ec30439 100644
--- a/libsigrokdecode4DSL/decoders/flexray/pd.py
+++ b/libsigrokdecode4DSL/decoders/flexray/pd.py
@@ -407,7 +407,7 @@ def decode(self):
# Wait until we're in the correct bit/sampling position.
pos = self.get_sample_point(self.curbit)
(fr_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
- if self.matched & 0b10:
+ if self.matched[1]:
self.dom_edge_seen()
- if self.matched & 0b01:
- self.handle_bit(fr_rx)
\ No newline at end of file
+ if self.matched[0]:
+ self.handle_bit(fr_rx)
diff --git a/libsigrokdecode4DSL/decoders/graycode/pd.py b/libsigrokdecode4DSL/decoders/graycode/pd.py
index 9303c33a..58301cee 100644
--- a/libsigrokdecode4DSL/decoders/graycode/pd.py
+++ b/libsigrokdecode4DSL/decoders/graycode/pd.py
@@ -95,12 +95,12 @@ class Decoder(srd.Decoder):
('phase', 'Phase'),
('increment', 'Increment'),
('count', 'Count'),
- ('turns', 'Turns'),
+ ('turn', 'Turn'),
('interval', 'Interval'),
('average', 'Average'),
('rpm', 'Rate'),
)
- annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+ annotation_rows = tuple((u + 's', v + 's', (i,)) for i, (u, v) in enumerate(annotations))
def __init__(self):
self.reset()
@@ -148,8 +148,7 @@ def decode(self):
self.ENCODER_STEPS = 1 << self.num_channels
- (d0, d1, d2, d3, d4, d5, d6, d7) = self.wait()
- startbits = (d0, d1, d2, d3, d4, d5, d6, d7)
+ startbits = self.wait()
curtime = self.samplenum
self.turns.set(self.samplenum, 0)
@@ -158,8 +157,7 @@ def decode(self):
while True:
prevtime = curtime
- (d0, d1, d2, d3, d4, d5, d6, d7) = self.wait([{i: 'e'} for i in range(self.num_channels)])
- bits = (d0, d1, d2, d3, d4, d5, d6, d7)
+ bits = self.wait([{i: 'e'} for i in range(self.num_channels)])
curtime = self.samplenum
oldcount = self.count.get()
diff --git a/libsigrokdecode4DSL/decoders/i2cfilter/pd.py b/libsigrokdecode4DSL/decoders/i2cfilter/pd.py
index 7798e17a..a54baab2 100644
--- a/libsigrokdecode4DSL/decoders/i2cfilter/pd.py
+++ b/libsigrokdecode4DSL/decoders/i2cfilter/pd.py
@@ -33,7 +33,7 @@ class Decoder(srd.Decoder):
outputs = ['i2c']
tags = ['Util']
options = (
- {'id': 'address', 'desc': 'Address to filter out of the I²C stream',
+ {'id': 'address', 'desc': 'Slave address to filter (decimal)',
'default': 0},
{'id': 'direction', 'desc': 'Direction to filter', 'default': 'both',
'values': ('read', 'write', 'both')}
diff --git a/libsigrokdecode4DSL/decoders/ieee488/pd.py b/libsigrokdecode4DSL/decoders/ieee488/pd.py
index 5fb6fa88..b0948a68 100644
--- a/libsigrokdecode4DSL/decoders/ieee488/pd.py
+++ b/libsigrokdecode4DSL/decoders/ieee488/pd.py
@@ -69,6 +69,8 @@
when addressing channels within the device.
- 'DATA_BYTE': is the talker address (when available),
is the raw data byte (transport layer, ATN inactive).
+ - 'PPOLL': is not applicable, is a list of bit indices
+ (DIO1 to DIO8 order) which responded to the PP request.
Extracted payload information (peers and their communicated data):
- 'TALK_LISTEN': is the current talker, is the list of
@@ -239,11 +241,12 @@ def _get_data_text(b):
ANN_RAW_BIT, ANN_RAW_BYTE,
ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,
ANN_EOI,
+ ANN_PP,
ANN_TEXT,
# TODO Want to provide one annotation class per talker address (0-30)?
ANN_IEC_PERIPH,
ANN_WARN,
-) = range(11)
+) = range(12)
(
BIN_RAW,
@@ -284,10 +287,12 @@ class Decoder(srd.Decoder):
{'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock'},
)
options = (
- {'id': 'iec_periph', 'desc': 'Decode Commodore IEC bus peripherals details',
+ {'id': 'iec_periph', 'desc': 'Decode Commodore IEC peripherals',
'default': 'no', 'values': ('no', 'yes')},
{'id': 'delim', 'desc': 'Payload data delimiter',
'default': 'eol', 'values': ('none', 'eol')},
+ {'id': 'atn_parity', 'desc': 'ATN commands use parity',
+ 'default': 'no', 'values': ('no', 'yes')},
)
annotations = (
('bit', 'IEC bit'),
@@ -298,6 +303,7 @@ class Decoder(srd.Decoder):
('saddr', 'Secondary address'),
('data', 'Data byte'),
('eoi', 'EOI'),
+ ('pp', 'Parallel poll'),
('text', 'Talker text'),
('periph', 'IEC bus peripherals'),
('warning', 'Warning'),
@@ -307,6 +313,7 @@ class Decoder(srd.Decoder):
('raws', 'Raw bytes', (ANN_RAW_BYTE,)),
('gpib', 'Commands/data', (ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,)),
('eois', 'EOI', (ANN_EOI,)),
+ ('polls', 'Polls', (ANN_PP,)),
('texts', 'Talker texts', (ANN_TEXT,)),
('periphs', 'IEC peripherals', (ANN_IEC_PERIPH,)),
('warnings', 'Warnings', (ANN_WARN,)),
@@ -333,6 +340,7 @@ def reset(self):
self.es_eoi = None
self.ss_text = None
self.es_text = None
+ self.ss_pp = None
self.last_talker = None
self.last_listener = []
self.last_iec_addr = None
@@ -401,6 +409,63 @@ def check_extra_flush(self, b):
if had_eol and not is_eol:
self.flush_bytes_text_accu()
+ def check_pp(self, dio = None):
+ # The combination of ATN and EOI means PP (parallel poll). Track
+ # this condition's start and end, and keep grabing the DIO lines'
+ # state as long as the condition is seen, since DAV is not used
+ # in the PP communication.
+ capture_in_pp = self.curr_atn and self.curr_eoi
+ decoder_in_pp = self.ss_pp is not None
+ if capture_in_pp and not decoder_in_pp:
+ # Phase starts. Track its ss. Start collecting DIO state.
+ self.ss_pp = self.samplenum
+ self.dio_pp = []
+ return 'enter'
+ if not capture_in_pp and decoder_in_pp:
+ # Phase ends. Void its ss. Process collected DIO state.
+ ss, es = self.ss_pp, self.samplenum
+ dio = self.dio_pp or []
+ self.ss_pp, self.dio_pp = None, None
+ if ss == es:
+ # False positive, caused by low oversampling.
+ return 'leave'
+ # Emit its annotation. Translate bit indices 0..7 for the
+ # DIO1..DIO8 signals to display text. Pass bit indices in
+ # the Python output for upper layers.
+ #
+ # TODO The presentation of this information may need more
+ # adjustment. The bit positions need not translate to known
+ # device addresses. Bits need not even belong to a single
+ # device. Participants and their location in the DIO pattern
+ # is configurable. Leave the interpretation to upper layers.
+ bits = [i for i, b in enumerate(dio) if b]
+ bits_text = ' '.join(['{}'.format(i + 1) for i in bits])
+ dios = ['DIO{}'.format(i + 1) for i in bits]
+ dios_text = ' '.join(dios or ['-'])
+ text = [
+ 'PPOLL {}'.format(dios_text),
+ 'PP {}'.format(bits_text),
+ 'PP',
+ ]
+ self.emit_data_ann(ss, es, ANN_PP, text)
+ self.putpy(ss, es, 'PPOLL', None, bits)
+ # Cease collecting DIO state.
+ return 'leave'
+ if decoder_in_pp:
+ # Keep collecting DIO state for each individual sample in
+ # the PP phase. Logically OR all DIO values that were seen.
+ # This increases robustness for low oversampling captures,
+ # where DIO may no longer be asserted when ATN/EOI deassert,
+ # and DIO was not asserted yet when ATN/EOI start asserting.
+ if dio is None:
+ dio = []
+ if len(dio) > len(self.dio_pp):
+ self.dio_pp.extend([ 0, ] * (len(dio) - len(self.dio_pp)))
+ for i, b in enumerate(dio):
+ self.dio_pp[i] |= b
+ return 'keep'
+ return 'idle'
+
def handle_ifc_change(self, ifc):
# Track IFC line for parallel input.
# Assertion of IFC de-selects all talkers and listeners.
@@ -486,6 +551,13 @@ def handle_data_byte(self):
upd_iec = False,
py_type = None
py_peers = False
+ if self.options['atn_parity'] == 'yes':
+ par = 1 if b & 0x80 else 0
+ b &= ~0x80
+ ones = bin(b).count('1') + par
+ if ones % 2:
+ warn_texts = ['Command parity error', 'parity', 'PAR']
+ self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
is_cmd, is_unl, is_unt = _is_command(b)
laddr = _is_listen_addr(b)
taddr = _is_talk_addr(b)
@@ -624,12 +696,12 @@ def decode_serial(self, has_clk, has_data_1, has_atn, has_srq):
data, clk = pins[PIN_DATA], pins[PIN_CLK]
atn, = self.invert_pins([pins[PIN_ATN]])
- if self.matched & 0b1:
+ if self.matched[0]:
# Falling edge on ATN, reset step.
step = STEP_WAIT_READY_TO_SEND
if step == STEP_WAIT_READY_TO_SEND:
- # Don't use self.matched_[1] here since we might come from
+ # Don't use self.matched[1] here since we might come from
# a step with different conds due to the code above.
if data == 0 and clk == 1:
# Rising edge on CLK while DATA is low: Ready to send.
@@ -654,7 +726,7 @@ def decode_serial(self, has_clk, has_data_1, has_atn, has_srq):
step = STEP_CLOCK_DATA_BITS
ss_bit = self.samplenum
elif step == STEP_CLOCK_DATA_BITS:
- if self.matched & 0b10:
+ if self.matched[1]:
if clk == 1:
# Rising edge on CLK; latch DATA.
bits.append(data)
@@ -671,10 +743,6 @@ def decode_serial(self, has_clk, has_data_1, has_atn, has_srq):
self.handle_eoi_change(False)
step = STEP_WAIT_READY_TO_SEND
- def check_bit(self, d):
- v = self.matched & (1 << d)
- return (v >> d) == 1
-
def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
if False in has_data_n or not has_dav or not has_atn:
@@ -687,6 +755,11 @@ def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
# low signal levels, i.e. won't include the initial falling edge.
# Scan for ATN/EOI edges as well (including the trick which works
# around initial pin state).
+ #
+ # Use efficient edge based wait conditions for most activities,
+ # though some phases may require individual inspection of each
+ # sample (think parallel poll in combination with slow sampling).
+ #
# Map low-active physical transport lines to positive logic here,
# to simplify logical inspection/decoding of communicated data,
# and to avoid redundancy and inconsistency in later code paths.
@@ -703,6 +776,14 @@ def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
if has_ifc:
idx_ifc = len(waitcond)
waitcond.append({PIN_IFC: 'l'})
+ idx_pp_check = None
+ def add_data_cond(conds):
+ idx = len(conds)
+ conds.append({'skip': 1})
+ return idx
+ def del_data_cond(conds, idx):
+ conds.pop(idx)
+ return None
while True:
pins = self.wait(waitcond)
pins = self.invert_pins(pins)
@@ -711,19 +792,35 @@ def decode_parallel(self, has_data_n, has_dav, has_atn, has_eoi, has_srq):
# captures, many edges fall onto the same sample number. So
# we process active edges of flags early (before processing
# data bits), and inactive edges late (after data got processed).
- if idx_ifc is not None and self.check_bit(idx_ifc) and pins[PIN_IFC] == 1:
+ want_pp_check = False
+ if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 1:
self.handle_ifc_change(pins[PIN_IFC])
- if idx_eoi is not None and self.check_bit(idx_eoi) and pins[PIN_EOI] == 1:
+ if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 1:
self.handle_eoi_change(pins[PIN_EOI])
- if self.check_bit(idx_atn) and pins[PIN_ATN] == 1:
+ want_pp_check = True
+ if self.matched[idx_atn] and pins[PIN_ATN] == 1:
self.handle_atn_change(pins[PIN_ATN])
- if self.check_bit(idx_dav):
+ want_pp_check = True
+ if want_pp_check and not idx_pp_check:
+ pp = self.check_pp()
+ if pp in ('enter',):
+ idx_pp_check = add_data_cond(waitcond)
+ if self.matched[idx_dav]:
self.handle_dav_change(pins[PIN_DAV], pins[PIN_DIO1:PIN_DIO8 + 1])
- if self.check_bit(idx_atn) and pins[PIN_ATN] == 0:
+ if idx_pp_check:
+ pp = self.check_pp(pins[PIN_DIO1:PIN_DIO8 + 1])
+ want_pp_check = False
+ if self.matched[idx_atn] and pins[PIN_ATN] == 0:
self.handle_atn_change(pins[PIN_ATN])
- if idx_eoi is not None and self.check_bit(idx_eoi) and pins[PIN_EOI] == 0:
+ want_pp_check = True
+ if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 0:
self.handle_eoi_change(pins[PIN_EOI])
- if idx_ifc is not None and self.check_bit(idx_ifc) and pins[PIN_IFC] == 0:
+ want_pp_check = True
+ if idx_pp_check is not None and want_pp_check:
+ pp = self.check_pp(pins[PIN_DIO1:PIN_DIO8 + 1])
+ if pp in ('leave',) and idx_pp_check is not None:
+ idx_pp_check = del_data_cond(waitcond, idx_pp_check)
+ if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 0:
self.handle_ifc_change(pins[PIN_IFC])
waitcond[idx_dav][PIN_DAV] = 'e'
diff --git a/libsigrokdecode4DSL/decoders/ir_irmp/irmp_library.py b/libsigrokdecode4DSL/decoders/ir_irmp/irmp_library.py
index 5ec65222..a1bc2583 100644
--- a/libsigrokdecode4DSL/decoders/ir_irmp/irmp_library.py
+++ b/libsigrokdecode4DSL/decoders/ir_irmp/irmp_library.py
@@ -2,6 +2,7 @@
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Rene Staffen
+## Copyright (C) 2020-2021 Gerhard Sittig
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -61,12 +62,24 @@ def _library_setup_api(self):
Lookup the C library's API routines. Declare their prototypes.
'''
- if not self._lib:
- return False
-
self._lib.irmp_get_sample_rate.restype = ctypes.c_uint32
self._lib.irmp_get_sample_rate.argtypes = []
+ self._lib.irmp_instance_alloc.restype = ctypes.c_void_p
+ self._lib.irmp_instance_alloc.argtypes = []
+
+ self._lib.irmp_instance_free.restype = None
+ self._lib.irmp_instance_free.argtypes = [ ctypes.c_void_p, ]
+
+ self._lib.irmp_instance_id.restype = ctypes.c_size_t
+ self._lib.irmp_instance_id.argtypes = [ ctypes.c_void_p, ]
+
+ self._lib.irmp_instance_lock.restype = ctypes.c_int
+ self._lib.irmp_instance_lock.argtypes = [ ctypes.c_void_p, ctypes.c_int, ]
+
+ self._lib.irmp_instance_unlock.restype = None
+ self._lib.irmp_instance_unlock.argtypes = [ ctypes.c_void_p, ]
+
self._lib.irmp_reset_state.restype = None
self._lib.irmp_reset_state.argtypes = []
@@ -85,6 +98,7 @@ def _library_setup_api(self):
# Create a result buffer that's local to the library instance.
self._data = self.ResultData()
+ self._inst = None
return True
@@ -93,30 +107,47 @@ def __init__(self):
Create a library instance.
'''
- # Only create a working instance for the first invocation.
- # Degrade all other instances, make them fail "late" during
- # execution, so that users will see the errors.
- self._lib = None
- self._data = None
- if IrmpLibrary.__usable_instance is None:
- filename = self._library_filename()
- self._lib = ctypes.cdll.LoadLibrary(filename)
- self._library_setup_api()
- IrmpLibrary.__usable_instance = self
+ filename = self._library_filename()
+ self._lib = ctypes.cdll.LoadLibrary(filename)
+ self._library_setup_api()
+
+ def __del__(self):
+ '''
+ Release a disposed library instance.
+ '''
+
+ if self._inst:
+ self._lib.irmp_instance_free(self._inst)
+ self._inst = None
+
+ def __enter__(self):
+ '''
+ Enter a context (lock management).
+ '''
+
+ if self._inst is None:
+ self._inst = self._lib.irmp_instance_alloc()
+ self._lib.irmp_instance_lock(self._inst, 1)
+ return self
+
+ def __exit__(self, extype, exvalue, trace):
+ '''
+ Leave a context (lock management).
+ '''
+
+ self._lib.irmp_instance_unlock(self._inst)
+ return False
+
+ def client_id(self):
+ return self._lib.irmp_instance_id(self._inst)
def get_sample_rate(self):
- if not self._lib:
- return None
return self._lib.irmp_get_sample_rate()
def reset_state(self):
- if not self._lib:
- return None
self._lib.irmp_reset_state()
def add_one_sample(self, level):
- if not self._lib:
- raise Exception("IRMP library limited to a single instance.")
if not self._lib.irmp_add_one_sample(int(level)):
return False
self._lib.irmp_get_result_data(ctypes.byref(self._data))
diff --git a/libsigrokdecode4DSL/decoders/ir_irmp/pd.py b/libsigrokdecode4DSL/decoders/ir_irmp/pd.py
index 979c1e01..b8df8190 100644
--- a/libsigrokdecode4DSL/decoders/ir_irmp/pd.py
+++ b/libsigrokdecode4DSL/decoders/ir_irmp/pd.py
@@ -3,6 +3,7 @@
##
## Copyright (C) 2014 Gump Yang
## Copyright (C) 2019 Rene Staffen
+## Copyright (C) 2020-2021 Gerhard Sittig
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -97,7 +98,7 @@ def __init__(self):
self.reset()
def reset(self):
- self.want_reset = True
+ pass
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -113,25 +114,26 @@ def decode(self):
except Exception as e:
txt = e.args[0]
raise LibraryError(txt)
- if self.irmp:
- self.lib_rate = self.irmp.get_sample_rate()
- if not self.irmp or not self.lib_rate:
- raise LibraryError('Cannot access IRMP library. One instance limit exceeded?')
+ if not self.irmp:
+ raise LibraryError('Cannot access IRMP library.')
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- if self.samplerate % self.lib_rate:
- raise SamplerateError('Capture samplerate must be multiple of library samplerate ({})'.format(self.lib_rate))
- self.rate_factor = int(self.samplerate / self.lib_rate)
- if self.want_reset:
- self.irmp.reset_state()
- self.want_reset = False
+ lib_rate = self.irmp.get_sample_rate()
+ if not lib_rate:
+ raise LibraryError('Cannot determine IRMP library\'s samplerate.')
+ if self.samplerate % lib_rate:
+ raise SamplerateError('Capture samplerate must be multiple of library samplerate ({})'.format(lib_rate))
+
+ self.rate_factor = int(self.samplerate / lib_rate)
+ active = 0 if self.options['polarity'] == 'active-low' else 1
- self.active = 0 if self.options['polarity'] == 'active-low' else 1
ir, = self.wait()
- while True:
- if self.active == 1:
- ir = 1 - ir
- if self.irmp.add_one_sample(ir):
- data = self.irmp.get_result_data()
- self.putframe(data)
- ir, = self.wait([{'skip': self.rate_factor}])
+ with self.irmp:
+ self.irmp.reset_state()
+ while True:
+ if active == 1:
+ ir = 1 - ir
+ if self.irmp.add_one_sample(ir):
+ data = self.irmp.get_result_data()
+ self.putframe(data)
+ ir, = self.wait([{'skip': self.rate_factor}])
diff --git a/libsigrokdecode4DSL/decoders/ir_nec/__init__.py b/libsigrokdecode4DSL/decoders/ir_nec/__init__.py
index c361c3dc..c3ab2936 100644
--- a/libsigrokdecode4DSL/decoders/ir_nec/__init__.py
+++ b/libsigrokdecode4DSL/decoders/ir_nec/__init__.py
@@ -19,6 +19,7 @@
'''
NEC is a pulse-distance based infrared remote control protocol.
+See https://www.sbprojects.net/knowledge/ir/nec.php for a description.
'''
from .pd import Decoder
diff --git a/libsigrokdecode4DSL/decoders/ir_nec/lists.py b/libsigrokdecode4DSL/decoders/ir_nec/lists.py
index 7d47a46d..dbb9288d 100644
--- a/libsigrokdecode4DSL/decoders/ir_nec/lists.py
+++ b/libsigrokdecode4DSL/decoders/ir_nec/lists.py
@@ -19,7 +19,9 @@
# Addresses/devices. Items that are not listed are reserved/unknown.
address = {
+ 0x00: 'Joy-it SBC-IRC01',
0x40: 'Matsui TV',
+ 0xEA41: 'Unknown LED Panel',
}
digits = {
@@ -47,4 +49,45 @@
31: ['Program down', 'P-'],
68: ['AV', 'AV'],
}.items())),
+
+ # This is most likely a generic remote control. The PCB
+ # has space for 16 buttons total, of which not all are
+ # connected. The PCB is marked "JSY", "XSK-5462", and
+ # "2014-6-12 JW". It consists of only a single IC, marked
+ # "BJEC107BNE" or similar. The following buttons are
+ # marked for the remote control of a LED panel this was
+ # found in.
+ 0xEA41: {
+ 0x10: ['Warmer', 'T+'],
+ 0x11: ['Colder', 'T-'],
+ 0x12: ['Brighter', '+'],
+ 0x13: ['Darker', '-'],
+ 0x14: ['Off', 'O'],
+ 0x15: ['On', 'I'],
+ 0x41: ['Min Brightness', 'Min'],
+ 0x48: ['Max Brightness', 'Max'],
+ },
+ 0x00: {
+ 0x45: ['Volume down', 'Vol-'],
+ 0x46: ['Play/Pause', 'P/P'],
+ 0x47: ['Volume up', 'Vol+'],
+ 0x44: ['Setup', 'Set'],
+ 0x40: ['Up', 'U'],
+ 0x43: ['Stop / Mode', 'S/M'],
+ 0x07: ['Left', 'L'],
+ 0x15: ['Enter', 'E'],
+ 0x09: ['Right', 'R'],
+ 0x16: ['0 / 10+', '0'],
+ 0x19: ['Down', 'D'],
+ 0x0D: ['Back', 'B'],
+ 0x0C: ['1', '1'],
+ 0x18: ['2', '2'],
+ 0x5E: ['3', '3'],
+ 0x08: ['4', '4'],
+ 0x1C: ['5', '5'],
+ 0x5A: ['6', '6'],
+ 0x42: ['7', '7'],
+ 0x52: ['8', '8'],
+ 0x4A: ['9', '9'],
+ }
}
diff --git a/libsigrokdecode4DSL/decoders/ir_nec/pd.py b/libsigrokdecode4DSL/decoders/ir_nec/pd.py
index 830892e8..d46d18c3 100644
--- a/libsigrokdecode4DSL/decoders/ir_nec/pd.py
+++ b/libsigrokdecode4DSL/decoders/ir_nec/pd.py
@@ -18,12 +18,34 @@
## along with this program; if not, see .
##
-import sigrokdecode as srd
+from common.srdhelper import bitpack
from .lists import *
+import sigrokdecode as srd
+
+# Concentrate all timing constraints of the IR protocol here in a single
+# location at the top of the source, to raise awareness and to simplify
+# review and adjustment. The tolerance is an arbitrary choice, available
+# literature does not mention any. The inter-frame timeout is not a part
+# of the protocol, but an implementation detail of this sigrok decoder.
+_TIME_TOL = 8 # tolerance, in percent
+_TIME_IDLE = 20.0 # inter-frame timeout, in ms
+_TIME_LC = 13.5 # leader code, in ms
+_TIME_RC = 11.25 # repeat code, in ms
+_TIME_ONE = 2.25 # one data bit, in ms
+_TIME_ZERO = 1.125 # zero data bit, in ms
+_TIME_STOP = 0.562 # stop bit, in ms
class SamplerateError(Exception):
pass
+class Pin:
+ IR, = range(1)
+
+class Ann:
+ BIT, AGC, LONG_PAUSE, SHORT_PAUSE, STOP_BIT, \
+ LEADER_CODE, ADDR, ADDR_INV, CMD, CMD_INV, REPEAT_CODE, \
+ REMOTE, WARN = range(13)
+
class Decoder(srd.Decoder):
api_version = 3
id = 'ir_nec'
@@ -39,8 +61,10 @@ class Decoder(srd.Decoder):
)
options = (
{'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
- 'values': ('active-low', 'active-high')},
+ 'values': ('auto', 'active-low', 'active-high')},
{'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
+ {'id': 'extended', 'desc': 'Extended NEC Protocol',
+ 'default': 'no', 'values': ('yes', 'no')},
)
annotations = (
('bit', 'Bit'),
@@ -55,13 +79,13 @@ class Decoder(srd.Decoder):
('cmd-inv', 'Command#'),
('repeat-code', 'Repeat code'),
('remote', 'Remote'),
- ('warnings', 'Warnings'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('bits', 'Bits', (0, 1, 2, 3, 4)),
- ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
- ('remote', 'Remote', (11,)),
- ('warnings', 'Warnings', (12,)),
+ ('bits', 'Bits', (Ann.BIT, Ann.AGC, Ann.LONG_PAUSE, Ann.SHORT_PAUSE, Ann.STOP_BIT)),
+ ('fields', 'Fields', (Ann.LEADER_CODE, Ann.ADDR, Ann.ADDR_INV, Ann.CMD, Ann.CMD_INV, Ann.REPEAT_CODE)),
+ ('remote-vals', 'Remote', (Ann.REMOTE,)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
)
def putx(self, data):
@@ -70,36 +94,44 @@ def putx(self, data):
def putb(self, data):
self.put(self.ss_bit, self.samplenum, self.out_ann, data)
- def putd(self, data):
+ def putd(self, data, bit_count):
name = self.state.title()
- d = {'ADDRESS': 6, 'ADDRESS#': 7, 'COMMAND': 8, 'COMMAND#': 9}
+ d = {'ADDRESS': Ann.ADDR, 'ADDRESS#': Ann.ADDR_INV,
+ 'COMMAND': Ann.CMD, 'COMMAND#': Ann.CMD_INV}
s = {'ADDRESS': ['ADDR', 'A'], 'ADDRESS#': ['ADDR#', 'A#'],
'COMMAND': ['CMD', 'C'], 'COMMAND#': ['CMD#', 'C#']}
- self.putx([d[self.state], ['%s: 0x%02X' % (name, data),
- '%s: 0x%02X' % (s[self.state][0], data),
- '%s: 0x%02X' % (s[self.state][1], data), s[self.state][1]]])
+ fmt = '{{}}: 0x{{:0{}X}}'.format(bit_count // 4)
+ self.putx([d[self.state], [
+ fmt.format(name, data),
+ fmt.format(s[self.state][0], data),
+ fmt.format(s[self.state][1], data),
+ s[self.state][1],
+ ]])
def putstop(self, ss):
self.put(ss, ss + self.stop, self.out_ann,
- [4, ['Stop bit', 'Stop', 'St', 'S']])
+ [Ann.STOP_BIT, ['Stop bit', 'Stop', 'St', 'S']])
def putpause(self, p):
self.put(self.ss_start, self.ss_other_edge, self.out_ann,
- [1, ['AGC pulse', 'AGC', 'A']])
- idx = 2 if p == 'Long' else 3
- self.put(self.ss_other_edge, self.samplenum, self.out_ann,
- [idx, [p + ' pause', '%s-pause' % p[0], '%sP' % p[0], 'P']])
+ [Ann.AGC, ['AGC pulse', 'AGC', 'A']])
+ idx = Ann.LONG_PAUSE if p == 'Long' else Ann.SHORT_PAUSE
+ self.put(self.ss_other_edge, self.samplenum, self.out_ann, [idx, [
+ '{} pause'.format(p),
+ '{}-pause'.format(p[0]),
+ '{}P'.format(p[0]),
+ 'P',
+ ]])
def putremote(self):
dev = address.get(self.addr, 'Unknown device')
- buttons = command.get(self.addr, None)
- if buttons is None:
- btn = ['Unknown', 'Unk']
- else:
- btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
- self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann,
- [11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
- '%s' % btn[1]]])
+ buttons = command.get(self.addr, {})
+ btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
+ self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann, [Ann.REMOTE, [
+ '{}: {}'.format(dev, btn[0]),
+ '{}: {}'.format(dev, btn[1]),
+ '{}'.format(btn[1]),
+ ]])
def __init__(self):
self.reset()
@@ -107,22 +139,24 @@ def __init__(self):
def reset(self):
self.state = 'IDLE'
self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
- self.data = self.count = self.active = None
+ self.data = []
self.addr = self.cmd = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
- self.active = 0 if self.options['polarity'] == 'active-low' else 1
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
- self.tolerance = 0.05 # +/-5%
- self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
- self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
- self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
- self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
- self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
+
+ def calc_rate(self):
+ self.tolerance = _TIME_TOL / 100
+ self.lc = int(self.samplerate * _TIME_LC / 1000) - 1
+ self.rc = int(self.samplerate * _TIME_RC / 1000) - 1
+ self.dazero = int(self.samplerate * _TIME_ZERO / 1000) - 1
+ self.daone = int(self.samplerate * _TIME_ONE / 1000) - 1
+ self.stop = int(self.samplerate * _TIME_STOP / 1000) - 1
+ self.idle_to = int(self.samplerate * _TIME_IDLE / 1000) - 1
def compare_with_tolerance(self, measured, base):
return (measured >= base * (1 - self.tolerance)
@@ -135,37 +169,57 @@ def handle_bit(self, tick):
elif self.compare_with_tolerance(tick, self.daone):
ret = 1
if ret in (0, 1):
- self.putb([0, ['%d' % ret]])
- self.data |= (ret << self.count) # LSB-first
- self.count = self.count + 1
+ self.putb([Ann.BIT, ['{:d}'.format(ret)]])
+ self.data.append(ret)
self.ss_bit = self.samplenum
- def data_ok(self):
- ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
- if self.count == 8:
+ def data_ok(self, check, want_len):
+ name = self.state.title()
+ normal, inverted = bitpack(self.data[:8]), bitpack(self.data[8:])
+ valid = (normal ^ inverted) == 0xff
+ show = inverted if self.state.endswith('#') else normal
+ is_ext_addr = self.is_extended and self.state == 'ADDRESS'
+ if is_ext_addr:
+ normal = bitpack(self.data)
+ show = normal
+ valid = True
+ if len(self.data) == want_len:
if self.state == 'ADDRESS':
- self.addr = self.data
+ self.addr = normal
if self.state == 'COMMAND':
- self.cmd = self.data
- self.putd(self.data)
+ self.cmd = normal
+ self.putd(show, want_len)
self.ss_start = self.samplenum
+ if is_ext_addr:
+ self.data = []
+ self.ss_bit = self.ss_start = self.samplenum
return True
- if ret == 0:
- self.putd(self.data >> 8)
- else:
- self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
- self.data = self.count = 0
+ self.putd(show, want_len)
+ if check and not valid:
+ warn_show = bitpack(self.data)
+ self.putx([Ann.WARN, ['{} error: 0x{:04X}'.format(name, warn_show)]])
+ self.data = []
self.ss_bit = self.ss_start = self.samplenum
- return ret == 0
+ return valid
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
+ self.calc_rate()
cd_count = None
if self.options['cd_freq']:
cd_count = int(self.samplerate / self.options['cd_freq']) + 1
- prev_ir = None
+ prev_ir = None
+
+ if self.options['polarity'] == 'auto':
+ # Take sample 0 as reference.
+ curr_level, = self.wait({'skip': 0})
+ active = 1 - curr_level
+ else:
+ active = 0 if self.options['polarity'] == 'active-low' else 1
+ self.is_extended = self.options['extended'] == 'yes'
+ want_addr_len = 16 if self.is_extended else 8
while True:
# Detect changes in the presence of an active input signal.
@@ -182,54 +236,64 @@ def decode(self):
# active period, but will shift their signal changes by one
# carrier period before they get passed to decoding logic.
if cd_count:
- (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
- if (self.matched & (0b1 << 0)):
- cur_ir = self.active
+ (cur_ir,) = self.wait([{Pin.IR: 'e'}, {'skip': cd_count}])
+ if self.matched[0]:
+ cur_ir = active
if cur_ir == prev_ir:
continue
prev_ir = cur_ir
self.ir = cur_ir
else:
- (self.ir,) = self.wait({0: 'e'})
+ (self.ir,) = self.wait({Pin.IR: 'e'})
- if self.ir != self.active:
- # Save the non-active edge, then wait for the next edge.
+ if self.ir != active:
+ # Save the location of the non-active edge (recessive),
+ # then wait for the next edge. Immediately process the
+ # end of the STOP bit which completes an IR frame.
self.ss_other_edge = self.samplenum
- continue
+ if self.state != 'STOP':
+ continue
- b = self.samplenum - self.ss_bit
+ # Reset internal state for long periods of idle level.
+ width = self.samplenum - self.ss_bit
+ if width >= self.idle_to and self.state != 'STOP':
+ self.reset()
# State machine.
if self.state == 'IDLE':
- if self.compare_with_tolerance(b, self.lc):
+ if self.compare_with_tolerance(width, self.lc):
self.putpause('Long')
- self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
+ self.putx([Ann.LEADER_CODE, ['Leader code', 'Leader', 'LC', 'L']])
self.ss_remote = self.ss_start
- self.data = self.count = 0
+ self.data = []
self.state = 'ADDRESS'
- elif self.compare_with_tolerance(b, self.rc):
+ elif self.compare_with_tolerance(width, self.rc):
self.putpause('Short')
self.putstop(self.samplenum)
self.samplenum += self.stop
- self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
- self.data = self.count = 0
+ self.putx([Ann.REPEAT_CODE, ['Repeat code', 'Repeat', 'RC', 'R']])
+ self.data = []
self.ss_bit = self.ss_start = self.samplenum
elif self.state == 'ADDRESS':
- self.handle_bit(b)
- if self.count == 8:
- self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == want_addr_len:
+ self.data_ok(False, want_addr_len)
+ self.state = 'COMMAND' if self.is_extended else 'ADDRESS#'
elif self.state == 'ADDRESS#':
- self.handle_bit(b)
- if self.count == 16:
- self.state = 'COMMAND' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == 16:
+ self.data_ok(True, 8)
+ self.state = 'COMMAND'
elif self.state == 'COMMAND':
- self.handle_bit(b)
- if self.count == 8:
- self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == 8:
+ self.data_ok(False, 8)
+ self.state = 'COMMAND#'
elif self.state == 'COMMAND#':
- self.handle_bit(b)
- if self.count == 16:
- self.state = 'STOP' if self.data_ok() else 'IDLE'
+ self.handle_bit(width)
+ if len(self.data) == 16:
+ self.data_ok(True, 8)
+ self.state = 'STOP'
elif self.state == 'STOP':
self.putstop(self.ss_bit)
self.putremote()
diff --git a/libsigrokdecode4DSL/decoders/ir_rc5/pd.py b/libsigrokdecode4DSL/decoders/ir_rc5/pd.py
index e18a90bf..bbe0ca76 100644
--- a/libsigrokdecode4DSL/decoders/ir_rc5/pd.py
+++ b/libsigrokdecode4DSL/decoders/ir_rc5/pd.py
@@ -61,13 +61,12 @@ def __init__(self):
def reset(self):
self.samplerate = None
- self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
- self.old_ir = 1 if self.options['polarity'] == 'active-low' else 0
+ self.next_edge = 'l' if self.options['polarity'] == 'active-low' else 'h'
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
@@ -143,11 +142,7 @@ def decode(self):
raise SamplerateError('Cannot decode without samplerate.')
while True:
- (self.ir,) = self.wait()
-
- # Wait for any edge (rising or falling).
- if self.old_ir == self.ir:
- continue
+ (self.ir,) = self.wait({0: self.next_edge})
# State machine.
if self.state == 'IDLE':
@@ -155,7 +150,7 @@ def decode(self):
self.edges.append(self.samplenum)
self.bits.append([self.samplenum, bit])
self.state = 'MID1'
- self.old_ir = self.ir
+ self.next_edge = 'l' if self.ir else 'h'
continue
edge = self.edge_type()
if edge == 'e':
@@ -184,4 +179,4 @@ def decode(self):
self.handle_bits()
self.reset_decoder_state()
- self.old_ir = self.ir
+ self.next_edge = 'l' if self.ir else 'h'
diff --git a/libsigrokdecode4DSL/decoders/ir_rc6/pd.py b/libsigrokdecode4DSL/decoders/ir_rc6/pd.py
index bd570173..e195dbd1 100644
--- a/libsigrokdecode4DSL/decoders/ir_rc6/pd.py
+++ b/libsigrokdecode4DSL/decoders/ir_rc6/pd.py
@@ -146,7 +146,7 @@ def decode(self):
(self.ir,) = self.wait(conditions)
if len(conditions) == 2:
- if self.matched & 0b10:
+ if self.matched[1]:
self.state = 'IDLE'
self.edges.append(self.samplenum)
diff --git a/libsigrokdecode4DSL/decoders/jitter/pd.py b/libsigrokdecode4DSL/decoders/jitter/pd.py
index 8ea1aa67..220790ef 100644
--- a/libsigrokdecode4DSL/decoders/jitter/pd.py
+++ b/libsigrokdecode4DSL/decoders/jitter/pd.py
@@ -51,13 +51,13 @@ class Decoder(srd.Decoder):
)
annotations = (
('jitter', 'Jitter value'),
- ('clk_missed', 'Clock missed'),
- ('sig_missed', 'Signal missed'),
+ ('clk_miss', 'Clock miss'),
+ ('sig_miss', 'Signal miss'),
)
annotation_rows = (
- ('jitter', 'Jitter values', (0,)),
- ('clk_missed', 'Clock missed', (1,)),
- ('sig_missed', 'Signal missed', (2,)),
+ ('jitter_vals', 'Jitter values', (0,)),
+ ('clk_misses', 'Clock misses', (1,)),
+ ('sig_misses', 'Signal misses', (2,)),
)
binary = (
('ascii-float', 'Jitter values as newline-separated ASCII floats'),
@@ -181,7 +181,7 @@ def decode(self):
raise SamplerateError('Cannot decode without samplerate.')
while True:
# Wait for a transition on CLK and/or SIG.
- (clk, sig) = self.wait([{0: 'e'}, {1: 'e'}])
+ clk, sig = self.wait([{0: 'e'}, {1: 'e'}])
# State machine:
# For each sample we can move 2 steps forward in the state machine.
diff --git a/libsigrokdecode4DSL/decoders/jtag_ejtag/pd.py b/libsigrokdecode4DSL/decoders/jtag_ejtag/pd.py
index f16f0b4e..191526b2 100644
--- a/libsigrokdecode4DSL/decoders/jtag_ejtag/pd.py
+++ b/libsigrokdecode4DSL/decoders/jtag_ejtag/pd.py
@@ -18,7 +18,7 @@
##
import sigrokdecode as srd
-from common.srdhelper import bin2int
+from common.srdhelper import bin2int, SrdIntEnum
class Instruction(object):
IDCODE = 0x01
@@ -53,7 +53,7 @@ class ControlReg(object):
PRACC = (1 << 18)
PRNW = (1 << 19)
-class Ann(object):
+class Ann(SrdIntEnum):
INSTRUCTION = 0
REGISTER = 1
CONTROL_FIELD_IN = 10
@@ -207,10 +207,10 @@ class Decoder(srd.Decoder):
)
annotation_rows = (
('instructions', 'Instructions', (0,)),
- ('regs', 'Registers', regs_items['rows_range']),
('control_fields_in', 'Control fields in', (10,)),
('control_fields_out', 'Control fields out', (11,)),
- ('pracc', 'PrAcc', (12,)),
+ ('regs', 'Registers', regs_items['rows_range']),
+ ('pracc-vals', 'PrAcc', (12,)),
)
def __init__(self):
diff --git a/libsigrokdecode4DSL/decoders/lfast/pd.py b/libsigrokdecode4DSL/decoders/lfast/pd.py
index bd4c8ec4..7476e59a 100644
--- a/libsigrokdecode4DSL/decoders/lfast/pd.py
+++ b/libsigrokdecode4DSL/decoders/lfast/pd.py
@@ -256,15 +256,11 @@ def handle_sleepbit(self):
self.put_payload()
def decode(self):
- bMoreMatch = False
-
while True:
if self.timeout == 0:
rising_edge, = self.wait({0: 'e'})
- bMoreMatch = False
else:
rising_edge, = self.wait([{0: 'e'}, {'skip': self.timeout}])
- bMoreMatch = True
# If this is the first edge, we only update ss
if self.ss == 0:
@@ -276,7 +272,7 @@ def decode(self):
self.es = self.samplenum
# Check for the sleep bit if this is a timeout condition
- if bMoreMatch and self.matched & 0b10 == 0b10:
+ if (len(self.matched) == 2) and self.matched[1]:
rising_edge = ~rising_edge
if self.state == state_sync:
self.reset()
@@ -335,5 +331,5 @@ def decode(self):
# If we got here when a timeout occurred, we have processed all null
# bits that we could and should reset now to find the next packet
- if bMoreMatch and self.matched & 0b10 == 0b10:
+ if (len(self.matched) == 2) and self.matched[1]:
self.reset()
diff --git a/libsigrokdecode4DSL/decoders/lin/__init__.py b/libsigrokdecode4DSL/decoders/lin/__init__.py
index f5b2835f..4e8c5790 100644
--- a/libsigrokdecode4DSL/decoders/lin/__init__.py
+++ b/libsigrokdecode4DSL/decoders/lin/__init__.py
@@ -1,28 +1,28 @@
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2018 Stephan Thiele
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see .
-##
-
-'''
-This decoder stacks on top of the 'uart' PD and decodes the LIN
-(Local Interconnect Network) protocol.
-
-LIN is layered on top of the UART (async serial) protocol, with 8n1 settings.
-Bytes are sent LSB-first.
-'''
-
-from .pd import Decoder
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stephan Thiele
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see .
+##
+
+'''
+This decoder stacks on top of the 'uart' PD and decodes the LIN
+(Local Interconnect Network) protocol.
+
+LIN is layered on top of the UART (async serial) protocol, with 8n1 settings.
+Bytes are sent LSB-first.
+'''
+
+from .pd import Decoder
diff --git a/libsigrokdecode4DSL/decoders/lin/pd.py b/libsigrokdecode4DSL/decoders/lin/pd.py
index a22be37d..11495d11 100644
--- a/libsigrokdecode4DSL/decoders/lin/pd.py
+++ b/libsigrokdecode4DSL/decoders/lin/pd.py
@@ -1,240 +1,247 @@
-##
-## This file is part of the libsigrokdecode project.
-##
-## Copyright (C) 2018 Stephan Thiele
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, see .
-##
-
-import sigrokdecode as srd
-
-class LinFsm:
- class State:
- WaitForBreak = 'WAIT_FOR_BREAK'
- Sync = 'SYNC'
- Pid = 'PID'
- Data = 'DATA'
- Checksum = 'CHECKSUM'
- Error = 'ERROR'
-
- def transit(self, target_state):
- if not self._transition_allowed(target_state):
- return False
- self.state = target_state
- return True
-
- def _transition_allowed(self, target_state):
- if target_state == LinFsm.State.Error:
- return True
- return target_state in self.allowed_state[self.state]
-
- def reset(self):
- self.state = LinFsm.State.WaitForBreak
-
- def __init__(self):
- a = dict()
- a[LinFsm.State.WaitForBreak] = (LinFsm.State.Sync,)
- a[LinFsm.State.Sync] = (LinFsm.State.Pid,)
- a[LinFsm.State.Pid] = (LinFsm.State.Data,)
- a[LinFsm.State.Data] = (LinFsm.State.Data, LinFsm.State.Checksum)
- a[LinFsm.State.Checksum] = (LinFsm.State.WaitForBreak,)
- a[LinFsm.State.Error] = (LinFsm.State.Sync,)
- self.allowed_state = a
-
- self.state = None
- self.reset()
-
-class Decoder(srd.Decoder):
- api_version = 3
- id = 'lin'
- name = 'LIN'
- longname = 'Local Interconnect Network'
- desc = 'Local Interconnect Network (LIN) protocol.'
- license = 'gplv2+'
- inputs = ['uart']
- outputs = []
- tags = ['Automotive']
- options = (
- {'id': 'version', 'desc': 'Protocol version', 'default': 2, 'values': (1, 2)},
- )
- annotations = (
- ('data', 'LIN data'),
- ('control', 'Protocol info'),
- ('error', 'Error descriptions'),
- ('inline_error', 'Protocol violations and errors'),
- )
- annotation_rows = (
- ('data', 'Data', (0, 1, 3)),
- ('error', 'Error', (2,)),
- )
-
- def __init__(self):
- self.reset()
-
- def reset(self):
- self.fsm = LinFsm()
- self.lin_header = []
- self.lin_rsp = []
- self.lin_version = None
- self.out_ann = None
- self.ss_block = None
- self.es_block = None
- self.done_break = False
-
- def start(self):
- self.out_ann = self.register(srd.OUTPUT_ANN)
- self.lin_version = self.options['version']
-
- def putx(self, data):
- self.put(self.ss_block, self.es_block, self.out_ann, data)
-
- def wipe_break_null_byte(self, value):
- # Upon a break condition a null byte is received which must be ignored.
- if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
- if len(self.lin_rsp):
- value = self.lin_rsp.pop()[2]
- else:
- self.lin_header.pop()
-
- if value != 0:
- self.fsm.transit(LinFsm.State.Error)
- self.handle_error(None)
- return False
-
- return True
-
- def handle_wait_for_break(self, value):
- self.wipe_break_null_byte(value)
-
- def handle_break(self, value):
- if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
- if self.wipe_break_null_byte(value):
- self.fsm.transit(LinFsm.State.Checksum)
- self.handle_checksum()
-
- self.fsm.reset()
- self.fsm.transit(LinFsm.State.Sync)
- self.done_break = True
- self.putx([1, ['Break condition', 'Break', 'Brk', 'B']])
-
- def handle_sync(self, value):
- self.fsm.transit(LinFsm.State.Pid)
- self.lin_header.append((self.ss_block, self.es_block, value))
-
- def handle_pid(self, value):
- self.fsm.transit(LinFsm.State.Data)
- self.lin_header.append((self.ss_block, self.es_block, value))
-
- def handle_data(self, value):
- self.lin_rsp.append((self.ss_block, self.es_block, value))
-
- def handle_checksum(self):
- sync = self.lin_header.pop(0) if len(self.lin_header) else None
-
- self.put(sync[0], sync[1], self.out_ann, [0, ['Sync', 'S']])
-
- if sync[2] != 0x55:
- self.put(sync[0], sync[1], self.out_ann,
- [2, ['Sync is not 0x55', 'Not 0x55', '!= 0x55']])
-
- pid = self.lin_header.pop(0) if len(self.lin_header) else None
- checksum = self.lin_rsp.pop() if len(self.lin_rsp) else None
-
- if pid:
- id_ = pid[2] & 0x3F
- parity = pid[2] >> 6
-
- expected_parity = self.calc_parity(pid[2])
- parity_valid = parity == expected_parity
-
- if not parity_valid:
- self.put(pid[0], pid[1], self.out_ann, [2, ['P != %d' % expected_parity]])
-
- ann_class = 0 if parity_valid else 3
- self.put(pid[0], pid[1], self.out_ann, [ann_class, [
- 'ID: %02X Parity: %d (%s)' % (id_, parity, 'ok' if parity_valid else 'bad'),
- 'ID: 0x%02X' % id_, 'I: %d' % id_
- ]])
-
- if len(self.lin_rsp):
- checksum_valid = self.checksum_is_valid(pid[2], self.lin_rsp, checksum[2])
-
- for b in self.lin_rsp:
- self.put(b[0], b[1], self.out_ann, [0, ['Data: 0x%02X' % b[2], 'D: 0x%02X' % b[2]]])
-
- ann_class = 0 if checksum_valid else 3
- self.put(checksum[0], checksum[1], self.out_ann,
- [ann_class, ['Checksum: 0x%02X' % checksum[2], 'Checksum', 'Chk', 'C']])
-
- if not checksum_valid:
- self.put(checksum[0], checksum[1], self.out_ann, [2, ['Checksum invalid']])
- else:
- pass # No response.
-
- self.lin_header.clear()
- self.lin_rsp.clear()
-
- def handle_error(self, dummy):
- self.putx([3, ['Error', 'Err', 'E']])
-
- def checksum_is_valid(self, pid, data, checksum):
- if self.lin_version == 2:
- id_ = pid & 0x3F
-
- if id_ != 60 and id_ != 61:
- checksum += pid
-
- for d in data:
- checksum += d[2]
-
- carry_bits = int(checksum / 256)
- checksum += carry_bits
-
- return checksum & 0xFF == 0xFF
-
- @staticmethod
- def calc_parity(pid):
- id_ = [((pid & 0x3F) >> i) & 1 for i in range(8)]
-
- p0 = id_[0] ^ id_[1] ^ id_[2] ^ id_[4]
- p1 = not (id_[1] ^ id_[3] ^ id_[4] ^ id_[5])
-
- return (p0 << 0) | (p1 << 1)
-
- def end(self):
- if self.done_break and len(self.lin_rsp):
- self.handle_checksum();
-
- def decode(self, ss, es, data):
- ptype, rxtx, pdata = data
-
- self.ss_block, self.es_block = ss, es
-
- # Ignore all UART packets except the actual data packets or BREAK.
- if ptype == 'BREAK':
- self.handle_break(pdata)
- if ptype != 'DATA':
- return
-
- # We're only interested in the byte value (not individual bits).
- pdata = pdata[0]
-
- # Short LIN overview:
- # - Message begins with a BREAK (0x00) for at least 13 bittimes.
- # - Break is always followed by a SYNC byte (0x55).
- # - Sync byte is followed by a PID byte (Protected Identifier).
- # - PID byte is followed by 1 - 8 data bytes and a final checksum byte.
-
- handler = getattr(self, 'handle_%s' % self.fsm.state.lower())
- handler(pdata)
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stephan Thiele
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see .
+##
+
+import sigrokdecode as srd
+
+class LinFsm:
+ class State:
+ WaitForBreak = 'WAIT_FOR_BREAK'
+ Sync = 'SYNC'
+ Pid = 'PID'
+ Data = 'DATA'
+ Checksum = 'CHECKSUM'
+ Error = 'ERROR'
+
+ def transit(self, target_state):
+ if not self._transition_allowed(target_state):
+ return False
+ self.state = target_state
+ return True
+
+ def _transition_allowed(self, target_state):
+ if target_state == LinFsm.State.Error:
+ return True
+ return target_state in self.allowed_state[self.state]
+
+ def reset(self):
+ self.state = LinFsm.State.WaitForBreak
+ self.uart_idle_count = 0
+
+ def __init__(self):
+ a = dict()
+ a[LinFsm.State.WaitForBreak] = (LinFsm.State.Sync,)
+ a[LinFsm.State.Sync] = (LinFsm.State.Pid,)
+ a[LinFsm.State.Pid] = (LinFsm.State.Data,)
+ a[LinFsm.State.Data] = (LinFsm.State.Data, LinFsm.State.Checksum)
+ a[LinFsm.State.Checksum] = (LinFsm.State.WaitForBreak,)
+ a[LinFsm.State.Error] = (LinFsm.State.Sync,)
+ self.allowed_state = a
+
+ self.state = None
+ self.uart_idle_count = 0
+ self.reset()
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'lin'
+ name = 'LIN'
+ longname = 'Local Interconnect Network'
+ desc = 'Local Interconnect Network (LIN) protocol.'
+ license = 'gplv2+'
+ inputs = ['uart']
+ outputs = []
+ tags = ['Automotive']
+ options = (
+ {'id': 'version', 'desc': 'Protocol version', 'default': 2, 'values': (1, 2)},
+ )
+ annotations = (
+ ('data', 'LIN data'),
+ ('control', 'Protocol info'),
+ ('error', 'Error description'),
+ ('inline_error', 'Protocol violation or error'),
+ )
+ annotation_rows = (
+ ('data_vals', 'Data', (0, 1, 3)),
+ ('errors', 'Errors', (2,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.fsm = LinFsm()
+ self.lin_header = []
+ self.lin_rsp = []
+ self.lin_version = None
+ self.ss_block = None
+ self.es_block = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.lin_version = self.options['version']
+
+ def putx(self, data):
+ self.put(self.ss_block, self.es_block, self.out_ann, data)
+
+ def wipe_break_null_byte(self, value):
+ # Upon a break condition a null byte is received which must be ignored.
+ if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+ if len(self.lin_rsp):
+ value = self.lin_rsp.pop()[2]
+ else:
+ self.lin_header.pop()
+
+ if value != 0:
+ self.fsm.transit(LinFsm.State.Error)
+ self.handle_error(None)
+ return False
+
+ return True
+
+ def handle_uart_idle(self):
+ if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+ self.fsm.uart_idle_count += 1
+
+ if self.fsm.uart_idle_count == 2:
+ self.fsm.transit(LinFsm.State.Checksum)
+ self.handle_checksum()
+ self.fsm.reset()
+
+ def handle_wait_for_break(self, value):
+ self.wipe_break_null_byte(value)
+
+ def handle_break(self, value):
+ if self.fsm.state not in (LinFsm.State.WaitForBreak, LinFsm.State.Error):
+ if self.wipe_break_null_byte(value):
+ self.fsm.transit(LinFsm.State.Checksum)
+ self.handle_checksum()
+
+ self.fsm.reset()
+ self.fsm.transit(LinFsm.State.Sync)
+
+ self.putx([1, ['Break condition', 'Break', 'Brk', 'B']])
+
+ def handle_sync(self, value):
+ self.fsm.transit(LinFsm.State.Pid)
+ self.lin_header.append((self.ss_block, self.es_block, value))
+
+ def handle_pid(self, value):
+ self.fsm.transit(LinFsm.State.Data)
+ self.lin_header.append((self.ss_block, self.es_block, value))
+
+ def handle_data(self, value):
+ self.lin_rsp.append((self.ss_block, self.es_block, value))
+
+ def handle_checksum(self):
+ sync = self.lin_header.pop(0) if len(self.lin_header) else None
+
+ self.put(sync[0], sync[1], self.out_ann, [0, ['Sync', 'S']])
+
+ if sync[2] != 0x55:
+ self.put(sync[0], sync[1], self.out_ann,
+ [2, ['Sync is not 0x55', 'Not 0x55', '!= 0x55']])
+
+ pid = self.lin_header.pop(0) if len(self.lin_header) else None
+ checksum = self.lin_rsp.pop() if len(self.lin_rsp) else None
+
+ if pid:
+ id_ = pid[2] & 0x3F
+ parity = pid[2] >> 6
+
+ expected_parity = self.calc_parity(pid[2])
+ parity_valid = parity == expected_parity
+
+ if not parity_valid:
+ self.put(pid[0], pid[1], self.out_ann, [2, ['P != %d' % expected_parity]])
+
+ ann_class = 0 if parity_valid else 3
+ self.put(pid[0], pid[1], self.out_ann, [ann_class, [
+ 'ID: %02X Parity: %d (%s)' % (id_, parity, 'ok' if parity_valid else 'bad'),
+ 'ID: 0x%02X' % id_, 'I: %d' % id_
+ ]])
+
+ if len(self.lin_rsp):
+ checksum_valid = self.checksum_is_valid(pid[2], self.lin_rsp, checksum[2])
+
+ for b in self.lin_rsp:
+ self.put(b[0], b[1], self.out_ann, [0, ['Data: 0x%02X' % b[2], 'D: 0x%02X' % b[2]]])
+
+ ann_class = 0 if checksum_valid else 3
+ self.put(checksum[0], checksum[1], self.out_ann,
+ [ann_class, ['Checksum: 0x%02X' % checksum[2], 'Checksum', 'Chk', 'C']])
+
+ if not checksum_valid:
+ self.put(checksum[0], checksum[1], self.out_ann, [2, ['Checksum invalid']])
+ else:
+ pass # No response.
+
+ self.lin_header.clear()
+ self.lin_rsp.clear()
+
+ def handle_error(self, dummy):
+ self.putx([3, ['Error', 'Err', 'E']])
+
+ def checksum_is_valid(self, pid, data, checksum):
+ if self.lin_version == 2:
+ id_ = pid & 0x3F
+
+ if id_ != 60 and id_ != 61:
+ checksum += pid
+
+ for d in data:
+ checksum += d[2]
+
+ carry_bits = int(checksum / 256)
+ checksum += carry_bits
+
+ return checksum & 0xFF == 0xFF
+
+ @staticmethod
+ def calc_parity(pid):
+ id_ = [((pid & 0x3F) >> i) & 1 for i in range(8)]
+
+ p0 = id_[0] ^ id_[1] ^ id_[2] ^ id_[4]
+ p1 = not (id_[1] ^ id_[3] ^ id_[4] ^ id_[5])
+
+ return (p0 << 0) | (p1 << 1)
+
+ def decode(self, ss, es, data):
+ ptype, rxtx, pdata = data
+
+ self.ss_block, self.es_block = ss, es
+
+ # Ignore all UART packets except the actual data packets or BREAK.
+ if ptype == 'IDLE':
+ self.handle_uart_idle()
+ if ptype == 'BREAK':
+ self.handle_break(pdata)
+ if ptype != 'DATA':
+ return
+
+ # We're only interested in the byte value (not individual bits).
+ pdata = pdata[0]
+
+ # Short LIN overview:
+ # - Message begins with a BREAK (0x00) for at least 13 bittimes.
+ # - Break is always followed by a SYNC byte (0x55).
+ # - Sync byte is followed by a PID byte (Protected Identifier).
+ # - PID byte is followed by 1 - 8 data bytes and a final checksum byte.
+
+ handler = getattr(self, 'handle_%s' % self.fsm.state.lower())
+ handler(pdata)
diff --git a/libsigrokdecode4DSL/decoders/lm75/pd.py b/libsigrokdecode4DSL/decoders/lm75/pd.py
index 14df1b52..d59fa5f4 100644
--- a/libsigrokdecode4DSL/decoders/lm75/pd.py
+++ b/libsigrokdecode4DSL/decoders/lm75/pd.py
@@ -55,11 +55,11 @@ class Decoder(srd.Decoder):
'values': (9, 10, 11, 12)},
)
annotations = (
- ('celsius', 'Temperature in degrees Celsius'),
- ('kelvin', 'Temperature in Kelvin'),
- ('text-verbose', 'Human-readable text (verbose)'),
- ('text', 'Human-readable text'),
- ('warnings', 'Human-readable warnings'),
+ ('celsius', 'Temperature / °C'),
+ ('kelvin', 'Temperature / Kelvin'),
+ ('text-verbose', 'Text (verbose)'),
+ ('text', 'Text'),
+ ('warning', 'Warning'),
)
def __init__(self):
diff --git a/libsigrokdecode4DSL/decoders/maple_bus/pd.py b/libsigrokdecode4DSL/decoders/maple_bus/pd.py
index 0e4e6043..9c4ad046 100644
--- a/libsigrokdecode4DSL/decoders/maple_bus/pd.py
+++ b/libsigrokdecode4DSL/decoders/maple_bus/pd.py
@@ -19,6 +19,9 @@
##
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+
+Pin = SrdIntEnum.from_str('Pin', 'SDCKA SDCKB')
ann = [
['Size', 'L'],
@@ -143,14 +146,14 @@ def frame_error(self):
self.putx([7, ['Frame error', 'F error', 'FE']])
def handle_start(self):
- self.wait({0: 'l', 1: 'h'})
+ self.wait({Pin.SDCKA: 'l', Pin.SDCKB: 'h'})
self.ss = self.samplenum
count = 0
while True:
- (sdcka, sdckb) = self.wait([{1: 'f'}, {0: 'r'}])
- if (self.matched & (0b1 << 0)):
+ sdcka, sdckb = self.wait([{Pin.SDCKB: 'f'}, {Pin.SDCKA: 'r'}])
+ if self.matched[0]:
count = count + 1
- if (self.matched & (0b1 << 1)):
+ if self.matched[1]:
self.es = self.samplenum
if sdckb == 1:
if count == 4:
@@ -176,16 +179,17 @@ def handle_byte_or_stop(self):
countb = 0
self.data = 0
while countb < 4:
- (sdcka, sdckb) = self.wait([{0: 'f'}, {1: 'f'}])
+ sdcka, sdckb = self.wait([{Pin.SDCKA: 'f'}, {Pin.SDCKB: 'f'}])
self.es = self.samplenum
- if (self.matched & (0b1 << 0)):
+ if self.matched[0]:
if counta == countb:
self.got_bit(sdckb)
counta = counta + 1
elif counta == 1 and countb == 0 and self.data == 0 and sdckb == 0:
- self.wait([{0: 'h', 1: 'h'}, {0: 'f'}, {1: 'f'}])
+ self.wait([{Pin.SDCKA: 'h', Pin.SDCKB: 'h'},
+ {Pin.SDCKA: 'f'}, {Pin.SDCKB: 'f'}])
self.es = self.samplenum
- if (self.matched & (0b1 << 0)):
+ if self.matched[0]:
self.got_end()
else:
self.frame_error()
@@ -193,7 +197,7 @@ def handle_byte_or_stop(self):
else:
self.frame_error()
return False
- elif (self.matched & (0b1 << 1)):
+ elif self.matched[1]:
if counta == countb + 1:
self.got_bit(sdcka)
countb = countb + 1
@@ -203,7 +207,7 @@ def handle_byte_or_stop(self):
else:
self.frame_error()
return False
- self.wait({0: 'h'})
+ self.wait({Pin.SDCKA: 'h'})
self.es = self.samplenum
self.got_byte()
return True
diff --git a/libsigrokdecode4DSL/decoders/max7219/pd.py b/libsigrokdecode4DSL/decoders/max7219/pd.py
index 4f3e4a68..eb26e105 100644
--- a/libsigrokdecode4DSL/decoders/max7219/pd.py
+++ b/libsigrokdecode4DSL/decoders/max7219/pd.py
@@ -51,9 +51,9 @@ class Decoder(srd.Decoder):
outputs = []
tags = ['Display']
annotations = (
- ('register', 'Registers written to the device'),
- ('digit', 'Digits displayed on the device'),
- ('warnings', 'Human-readable warnings'),
+ ('register', 'Register write'),
+ ('digit', 'Digit displayed'),
+ ('warning', 'Warning'),
)
annotation_rows = (
('commands', 'Commands', (ann_reg, ann_digit)),
@@ -75,14 +75,11 @@ def putreg(self, ss, es, reg, value):
self.put(ss, es, self.out_ann, [ann_reg, ['%s: %s' % (reg, value)]])
def putdigit(self, ss, es, digit, value):
- self.put(ss, es, self.out_ann, [ann_digit, ['Digit %d: {$}' % digit, '@%02X' % value]])
+ self.put(ss, es, self.out_ann, [ann_digit, ['Digit %d: %02X' % (digit, value)]])
def putwarn(self, ss, es, message):
self.put(ss, es, self.out_ann, [ann_warning, [message]])
- def putwarn_ann(self, ss, es, message):
- self.put(ss, es, self.out_ann, [ann_warning, message])
-
def decode(self, ss, es, data):
ptype, mosi, _ = data
@@ -100,7 +97,8 @@ def decode(self, ss, es, data):
name, decoder = registers[self.addr]
self.putreg(self.addr_start, es, name, decoder(mosi))
else:
- self.putwarn_ann(self.addr_start, es,['Unknown register {$}', '@%02X' % self.addr])
+ self.putwarn(self.addr_start, es,
+ 'Unknown register %02X' % (self.addr))
self.pos += 1
elif ptype == 'CS-CHANGE':
diff --git a/libsigrokdecode4DSL/decoders/mcs48/pd.py b/libsigrokdecode4DSL/decoders/mcs48/pd.py
index 50216a41..098c6433 100644
--- a/libsigrokdecode4DSL/decoders/mcs48/pd.py
+++ b/libsigrokdecode4DSL/decoders/mcs48/pd.py
@@ -19,6 +19,10 @@
##
import sigrokdecode as srd
+from common.srdhelper import SrdIntEnum
+
+Ann = SrdIntEnum.from_str('Ann', 'ROMDATA')
+Bin = SrdIntEnum.from_str('Bin', 'ROMDATA')
class ChannelError(Exception):
pass
@@ -59,6 +63,10 @@ class Decoder(srd.Decoder):
binary = (
('romdata', 'AAAA:DD'),
)
+ OFF_ALE, OFF_PSEN = 0, 1
+ OFF_DATA_BOT, OFF_DATA_TOP = 2, 10
+ OFF_ADDR_BOT, OFF_ADDR_TOP = 10, 14
+ OFF_BANK_BOT, OFF_BANK_TOP = 14, 15
def __init__(self):
self.reset()
@@ -92,28 +100,28 @@ def newdata(self, data):
self.data_s = self.samplenum
if self.started:
anntext = '{:04X}:{:02X}'.format(self.addr, self.data)
- self.put(self.addr_s, self.data_s, self.out_ann, [0, [anntext]])
+ self.put(self.addr_s, self.data_s, self.out_ann, [Ann.ROMDATA, [anntext]])
bindata = self.addr.to_bytes(2, byteorder='big')
bindata += self.data.to_bytes(1, byteorder='big')
- self.put(self.addr_s, self.data_s, self.out_bin, [0, bindata])
+ self.put(self.addr_s, self.data_s, self.out_bin, [Bin.ROMDATA, bindata])
def decode(self):
# Address bits above A11 are optional, and are considered to be A12+.
# This logic needs more adjustment when more bank address pins are
# to get supported. For now, having just A12 is considered sufficient.
- has_bank = self.has_channel(14)
+ has_bank = self.has_channel(self.OFF_BANK_BOT)
bank_pin_count = 1 if has_bank else 0
# Sample address on the falling ALE edge.
# Save data on falling edge of PSEN.
while True:
- (ale, psen, d0, d1, d2, d3, d4, d5, d6, d7, a8, a9, a10, a11, a12) = self.wait([{0: 'f'}, {1: 'r'}])
- data = (d0, d1, d2, d3, d4, d5, d6, d7)
- addr = (a8, a9, a10, a11)
- bank = (a12, )
+ pins = self.wait([{self.OFF_ALE: 'f'}, {self.OFF_PSEN: 'r'}])
+ data = pins[self.OFF_DATA_BOT:self.OFF_DATA_TOP]
+ addr = pins[self.OFF_ADDR_BOT:self.OFF_ADDR_TOP]
+ bank = pins[self.OFF_BANK_BOT:self.OFF_BANK_TOP]
if has_bank:
addr += bank[:bank_pin_count]
# Handle those conditions (one or more) that matched this time.
- if (self.matched & (0b1 << 0)):
+ if self.matched[0]:
self.newaddr(addr, data)
- if (self.matched & (0b1 << 1)):
+ if self.matched[1]:
self.newdata(data)
diff --git a/libsigrokdecode4DSL/decoders/sbus_futaba/__init__.py b/libsigrokdecode4DSL/decoders/sbus_futaba/__init__.py
new file mode 100644
index 00000000..9404f4f2
--- /dev/null
+++ b/libsigrokdecode4DSL/decoders/sbus_futaba/__init__.py
@@ -0,0 +1,35 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2022 Gerhard Sittig
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see .
+##
+
+'''
+SBUS by Futaba, a hobby remote control protocol on top of UART.
+Sometimes referred to as "Serial BUS" or S-BUS.
+
+UART communication typically runs at 100kbps with 8e2 frame format and
+inverted signals (high voltage level is logic low).
+
+SBUS messages take 3ms to transfer, and typically repeat in intervals
+of 7ms or 14ms. An SBUS message consists of 25 UART bytes, and carries
+16 proportional channels with 11 bits each, and 2 digital channels
+(boolean, 1 bit), and flags which represent current communication state.
+Proportional channel values typically are in the 192..1792 range, but
+individual implementations may differ.
+'''
+
+from .pd import Decoder
diff --git a/libsigrokdecode4DSL/decoders/sbus_futaba/pd.py b/libsigrokdecode4DSL/decoders/sbus_futaba/pd.py
new file mode 100644
index 00000000..75c2cfba
--- /dev/null
+++ b/libsigrokdecode4DSL/decoders/sbus_futaba/pd.py
@@ -0,0 +1,273 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2022 Gerhard Sittig
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see .
+##
+
+"""
+OUTPUT_PYTHON format:
+
+Packet:
+(, )
+
+This is the list of codes and their respective values:
+ - 'HEADER': The data is the header byte's value.
+ - 'PROPORTIONAL': The data is a tuple of the channel number (1-based)
+ and the channel's value.
+ - 'DIGITAL': The data is a tuple of the channel number (1-based)
+ and the channel's value.
+ - 'FLAG': The data is a tuple of the flag's name, and the flag's value.
+ - 'FOOTER': The data is the footer byte's value.
+"""
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack_lsb
+
+class Ann:
+ HEADER, PROPORTIONAL, DIGITAL, FRAME_LOST, FAILSAFE, FOOTER, \
+ WARN = range(7)
+ FLAG_LSB = FRAME_LOST
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'sbus_futaba'
+ name = 'SBUS (Futaba)'
+ longname = 'Futaba SBUS (Serial bus)'
+ desc = 'Serial bus for hobby remote control by Futaba'
+ license = 'gplv2+'
+ inputs = ['uart']
+ outputs = ['sbus_futaba']
+ tags = ['Remote Control']
+ options = (
+ {'id': 'prop_val_min', 'desc': 'Proportional value lower boundary', 'default': 0},
+ {'id': 'prop_val_max', 'desc': 'Proportional value upper boundary', 'default': 2047},
+ )
+ annotations = (
+ ('header', 'Header'),
+ ('proportional', 'Proportional'),
+ ('digital', 'Digital'),
+ ('framelost', 'Frame Lost'),
+ ('failsafe', 'Failsafe'),
+ ('footer', 'Footer'),
+ ('warning', 'Warning'),
+ )
+ annotation_rows = (
+ ('framing', 'Framing', (Ann.HEADER, Ann.FOOTER,
+ Ann.FRAME_LOST, Ann.FAILSAFE)),
+ ('channels', 'Channels', (Ann.PROPORTIONAL, Ann.DIGITAL)),
+ ('warnings', 'Warnings', (Ann.WARN,)),
+ )
+
+ def __init__(self):
+ self.bits_accum = []
+ self.sent_fields = None
+ self.msg_complete = None
+ self.failed = None
+ self.reset()
+
+ def reset(self):
+ self.bits_accum.clear()
+ self.sent_fields = 0
+ self.msg_complete = False
+ self.failed = None
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self.out_py = self.register(srd.OUTPUT_PYTHON)
+
+ def putg(self, ss, es, data):
+ # Put a graphical annotation.
+ self.put(ss, es, self.out_ann, data)
+
+ def putpy(self, ss, es, data):
+ # Pass Python to upper layers.
+ self.put(ss, es, self.out_py, data)
+
+ def get_ss_es_bits(self, bitcount):
+ # Get start/end times, and bit values of given length.
+ # Gets all remaining data when 'bitcount' is None.
+ if bitcount is None:
+ bitcount = len(self.bits_accum)
+ if len(self.bits_accum) < bitcount:
+ return None, None, None
+ bits = self.bits_accum[:bitcount]
+ self.bits_accum = self.bits_accum[bitcount:]
+ ss, es = bits[0][1], bits[-1][2]
+ bits = [b[0] for b in bits]
+ return ss, es, bits
+
+ def flush_accum_bits(self):
+ # Valid data was queued. See if we got full SBUS fields so far.
+ # Annotate them early, cease inspection of failed messages. The
+ # implementation is phrased to reduce the potential for clipboard
+ # errors: 'upto' is the next supported field count, 'want' is one
+ # field's bit count. Grab as many as we find in an invocation.
+ upto = 0
+ if self.failed:
+ return
+ # Annotate the header byte. Not seeing the expected bit pattern
+ # emits a warning annotation, but by design won't fail the SBUS
+ # message. It's considered more useful to present the channels'
+ # values instead. The warning still raises awareness.
+ upto += 1
+ want = 8
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['0x{:02x}'.format(value)]
+ self.putg(ss, es, [Ann.HEADER, text])
+ if value != 0x0f:
+ text = ['Unexpected header', 'Header']
+ self.putg(ss, es, [Ann.WARN, text])
+ self.putpy(ss, es, ['HEADER', value])
+ self.sent_fields += 1
+ # Annotate the proportional channels' data. Check for user
+ # provided value range violations. Channel numbers are in
+ # the 1..18 range (1-based).
+ upto += 16
+ want = 11
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['{:d}'.format(value)]
+ self.putg(ss, es, [Ann.PROPORTIONAL, text])
+ if value < self.options['prop_val_min']:
+ text = ['Low proportional value', 'Low value', 'Low']
+ self.putg(ss, es, [Ann.WARN, text])
+ if value > self.options['prop_val_max']:
+ text = ['High proportional value', 'High value', 'High']
+ self.putg(ss, es, [Ann.WARN, text])
+ idx = self.sent_fields - (upto - 16)
+ ch_nr = 1 + idx
+ self.putpy(ss, es, ['PROPORTIONAL', (ch_nr, value)])
+ self.sent_fields += 1
+ # Annotate the digital channels' data.
+ upto += 2
+ want = 1
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['{:d}'.format(value)]
+ self.putg(ss, es, [Ann.DIGITAL, text])
+ idx = self.sent_fields - (upto - 2)
+ ch_nr = 17 + idx
+ self.putpy(ss, es, ['DIGITAL', (ch_nr, value)])
+ self.sent_fields += 1
+ # Annotate the flags' state. Index starts from LSB.
+ flag_names = ['framelost', 'failsafe', 'msb']
+ upto += 2
+ want = 1
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['{:d}'.format(value)]
+ idx = self.sent_fields - (upto - 2)
+ cls = Ann.FLAG_LSB + idx
+ self.putg(ss, es, [cls, text])
+ flg_name = flag_names[idx]
+ self.putpy(ss, es, ['FLAG', (flg_name, value)])
+ self.sent_fields += 1
+ # Warn when flags' padding (bits [7:4]) is unexpexted.
+ upto += 1
+ want = 4
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ if value != 0x0:
+ text = ['Unexpected MSB flags', 'Flags']
+ self.putg(ss, es, [Ann.WARN, text])
+ flg_name = flag_names[-1]
+ self.putpy(ss, es, ['FLAG', (flg_name, value)])
+ self.sent_fields += 1
+ # Annotate the footer byte. Warn when unexpected.
+ upto += 1
+ want = 8
+ while self.sent_fields < upto:
+ if len(self.bits_accum) < want:
+ return
+ ss, es, bits = self.get_ss_es_bits(want)
+ value = bitpack_lsb(bits)
+ text = ['0x{:02x}'.format(value)]
+ self.putg(ss, es, [Ann.FOOTER, text])
+ if value != 0x00:
+ text = ['Unexpected footer', 'Footer']
+ self.putg(ss, es, [Ann.WARN, text])
+ self.putpy(ss, es, ['FOOTER', value])
+ self.sent_fields += 1
+ # Check for the completion of an SBUS message. Warn when more
+ # UART data is seen after the message. Defer the warning until
+ # more bits were collected, flush at next IDLE or BREAK, which
+ # spans all unprocessed data, and improves perception.
+ if self.sent_fields >= upto:
+ self.msg_complete = True
+ if self.msg_complete and self.bits_accum:
+ self.failed = ['Excess data bits', 'Excess']
+
+ def handle_bits(self, ss, es, bits):
+ # UART data bits were seen. Store them, validity is yet unknown.
+ self.bits_accum.extend(bits)
+
+ def handle_frame(self, ss, es, value, valid):
+ # A UART frame became complete. Get its validity. Process its bits.
+ if not valid:
+ self.failed = ['Invalid data', 'Invalid']
+ self.flush_accum_bits()
+
+ def handle_idle(self, ss, es):
+ # An IDLE period was seen in the UART level. Flush, reset state.
+ if self.bits_accum and not self.failed:
+ self.failed = ['Unprocessed data bits', 'Unprocessed']
+ if self.bits_accum and self.failed:
+ ss, es, _ = self.get_ss_es_bits(None)
+ self.putg(ss, es, [Ann.WARN, self.failed])
+ self.reset()
+
+ def handle_break(self, ss, es):
+ # A BREAK period was seen in the UART level. Warn, reset state.
+ break_ss, break_es = ss, es
+ if not self.failed:
+ self.failed = ['BREAK condition', 'Break']
+ # Re-use logic for "annotated bits warning".
+ self.handle_idle(None, None)
+ # Unconditionally annotate BREAK as warning.
+ text = ['BREAK condition', 'Break']
+ self.putg(ss, es, [Ann.WARN, text])
+ self.reset()
+
+ def decode(self, ss, es, data):
+ # Implementor's note: Expects DATA bits to arrive before FRAME
+ # validity. Either of IDLE or BREAK terminates an SBUS message.
+ ptype, rxtx, pdata = data
+ if ptype == 'DATA':
+ _, bits = pdata
+ self.handle_bits(ss, es, bits)
+ elif ptype == 'FRAME':
+ value, valid = pdata
+ self.handle_frame(ss, es, value, valid)
+ elif ptype == 'IDLE':
+ self.handle_idle(ss, es)
+ elif ptype == 'BREAK':
+ self.handle_break(ss, es)