2
0
forked from Ivasoft/DSView

move decoders

This commit is contained in:
DreamSourceLab
2022-01-27 19:23:31 -08:00
parent ac1d160411
commit 7d32e53d02
308 changed files with 50027 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
I²C (Inter-Integrated Circuit) is a bidirectional, multi-master
bus using two signals (SCL = serial clock line, SDA = serial data line).
'''
from .pd import Decoder

View File

@@ -0,0 +1,259 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2010-2016 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# TODO: Look into arbitration, collision detection, clock synchronisation, etc.
# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
# TODO: Implement support for detecting various bus errors.
import sigrokdecode as srd
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <pdata>]
<ptype>:
- 'START' (START condition)
- 'START REPEAT' (Repeated START condition)
- 'ADDRESS READ' (Slave address, read)
- 'ADDRESS WRITE' (Slave address, write)
- 'DATA READ' (Data, read)
- 'DATA WRITE' (Data, write)
- 'STOP' (STOP condition)
- 'ACK' (ACK bit)
- 'NACK' (NACK bit)
- 'BITS' (<pdata>: list of data/address bits and their ss/es numbers)
<pdata> is the data or address byte associated with the 'ADDRESS*' and 'DATA*'
command. Slave addresses do not include bit 0 (the READ/WRITE indication bit).
For example, a slave address field could be 0x51 (instead of 0xa2).
For 'START', 'START REPEAT', 'STOP', 'ACK', and 'NACK' <pdata> is None.
'''
# CMD: [annotation-type-index, long annotation, short annotation]
proto = {
'START': [0, 'Start', 'S'],
'START REPEAT': [1, 'Start repeat', 'Sr'],
'STOP': [2, 'Stop', 'P'],
'ACK': [3, 'ACK', 'A'],
'NACK': [4, 'NACK', 'N'],
'ADDRESS READ': [5, 'Address read', 'AR'],
'ADDRESS WRITE': [6, 'Address write', 'AW'],
'DATA READ': [7, 'Data read', 'DR'],
'DATA WRITE': [8, 'Data write', 'DW'],
}
class Decoder(srd.Decoder):
api_version = 3
id = '0:i2c'
name = '0:I²C'
longname = 'Inter-Integrated Circuit'
desc = 'Two-wire, multi-master, serial bus.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['i2c']
tags = ['Embedded/industrial']
channels = (
{'id': 'scl', 'type': 8, 'name': 'SCL', 'desc': 'Serial clock line'},
{'id': 'sda', 'type': 108, 'name': 'SDA', 'desc': 'Serial data line'},
)
options = (
{'id': 'address_format', 'desc': 'Displayed slave address format',
'default': 'shifted', 'values': ('shifted', 'unshifted')},
)
annotations = (
('7', 'start', 'Start condition'),
('6', 'repeat-start', 'Repeat start condition'),
('1', 'stop', 'Stop condition'),
('5', 'ack', 'ACK'),
('0', 'nack', 'NACK'),
('112', 'address-read', 'Address read'),
('111', 'address-write', 'Address write'),
('110', 'data-read', 'Data read'),
('109', 'data-write', 'Data write'),
('1000', 'warnings', 'Human-readable warnings'),
)
annotation_rows = (
('addr-data', 'Address/Data', (0, 1, 2, 3, 4, 5, 6, 7, 8)),
('warnings', 'Warnings', (9,)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.ss = self.es = self.ss_byte = -1
self.bitcount = 0
self.databyte = 0
self.wr = -1
self.is_repeat_start = 0
self.state = 'FIND START'
self.pdu_start = None
self.pdu_bits = 0
self.bits = []
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def handle_start(self):
self.ss, self.es = self.samplenum, self.samplenum
self.pdu_start = self.samplenum
self.pdu_bits = 0
cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
self.putx([proto[cmd][0], proto[cmd][1:]])
self.state = 'FIND ADDRESS'
self.bitcount = self.databyte = 0
self.is_repeat_start = 1
self.wr = -1
self.bits = []
# Gather 8 bits of data plus the ACK/NACK bit.
def handle_address_or_data(self, scl, sda):
self.pdu_bits += 1
# Address and data are transmitted MSB-first.
self.databyte <<= 1
self.databyte |= sda
# Remember the start of the first data/address bit.
if self.bitcount == 0:
self.ss_byte = self.samplenum
# Store individual bits and their start/end samplenumbers.
# In the list, index 0 represents the LSB (I²C transmits MSB-first).
self.bits.insert(0, [sda, self.samplenum, self.samplenum])
if self.bitcount > 0:
self.bits[1][2] = self.samplenum
if self.bitcount == 7:
self.bitwidth = self.bits[1][2] - self.bits[2][2]
self.bits[0][2] += self.bitwidth
# Return if we haven't collected all 8 + 1 bits, yet.
if self.bitcount < 7:
self.bitcount += 1
return
d = self.databyte
if self.state == 'FIND ADDRESS':
# The READ/WRITE bit is only in address bytes, not data bytes.
self.wr = 0 if (self.databyte & 1) else 1
if self.options['address_format'] == 'shifted':
d = d >> 1
bin_class = -1
if self.state == 'FIND ADDRESS' and self.wr == 1:
cmd = 'ADDRESS WRITE'
bin_class = 1
elif self.state == 'FIND ADDRESS' and self.wr == 0:
cmd = 'ADDRESS READ'
bin_class = 0
elif self.state == 'FIND DATA' and self.wr == 1:
cmd = 'DATA WRITE'
bin_class = 3
elif self.state == 'FIND DATA' and self.wr == 0:
cmd = 'DATA READ'
bin_class = 2
self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth
if cmd.startswith('ADDRESS'):
self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R']
self.putx([proto[cmd][0], w])
self.ss, self.es = self.ss_byte, self.samplenum
self.putx([proto[cmd][0], ['%s: {$}' % proto[cmd][1], '%s: {$}' % proto[cmd][2], '{$}', d]])
# Done with this packet.
self.bitcount = self.databyte = 0
self.bits = []
self.state = 'FIND ACK'
def get_ack(self, scl, sda):
self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
cmd = 'NACK' if (sda == 1) else 'ACK'
self.putx([proto[cmd][0], proto[cmd][1:]])
# There could be multiple data bytes in a row, so either find
# another data byte or a STOP condition next.
self.state = 'FIND DATA'
def handle_stop(self):
cmd = 'STOP'
self.ss, self.es = self.samplenum, self.samplenum
self.putx([proto[cmd][0], proto[cmd][1:]])
self.state = 'FIND START'
self.is_repeat_start = 0
self.wr = -1
self.bits = []
def decode(self):
while True:
# State machine.
if self.state == 'FIND START':
# Wait for a START condition (S): SCL = high, SDA = falling.
self.wait({0: 'h', 1: 'f'})
self.handle_start()
elif self.state == 'FIND ADDRESS':
# Wait for any of the following conditions (or combinations):
# a) Data sampling of receiver: SCL = rising, and/or
# b) START condition (S): SCL = high, SDA = falling, and/or
# c) STOP condition (P): SCL = high, SDA = rising
(scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}])
# Check which of the condition(s) matched and handle them.
if (self.matched & (0b1 << 0)):
self.handle_address_or_data(scl, sda)
elif (self.matched & (0b1 << 1)):
self.handle_start()
elif (self.matched & (0b1 << 2)):
self.handle_stop()
elif self.state == 'FIND DATA':
# Wait for any of the following conditions (or combinations):
# a) Data sampling of receiver: SCL = rising, and/or
# b) START condition (S): SCL = high, SDA = falling, and/or
# c) STOP condition (P): SCL = high, SDA = rising
(scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}])
# Check which of the condition(s) matched and handle them.
if (self.matched & (0b1 << 0)):
self.handle_address_or_data(scl, sda)
elif (self.matched & (0b1 << 1)):
self.handle_start()
elif (self.matched & (0b1 << 2)):
self.handle_stop()
elif self.state == 'FIND ACK':
# Wait for any of the following conditions (or combinations):
# a) a data/ack bit: SCL = rising.
# b) STOP condition (P): SCL = high, SDA = rising
(scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'r'}])
if (self.matched & (0b1 << 0)):
self.get_ack(scl, sda)
elif (self.matched & (0b1 << 1)):
self.handle_stop()

View File

@@ -0,0 +1,32 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
The SPI (Serial Peripheral Interface) protocol decoder supports synchronous
SPI(-like) protocols with a clock line, a MISO and MOSI line for data
transfer in two directions, and an optional CS# pin.
Either MISO or MOSI (but not both) can be optional.
If CS# is supplied, data is only decoded when CS# is asserted (clock
transitions where CS# is not asserted are ignored). If CS# is not supplied,
data is decoded on every clock transition (depending on SPI mode).
'''
from .pd import Decoder

View File

@@ -0,0 +1,275 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from collections import namedtuple
Data = namedtuple('Data', ['ss', 'es', 'val'])
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <data1>, <data2>]
<ptype>:
- 'DATA': <data1> contains the MOSI data, <data2> contains the MISO data.
The data is _usually_ 8 bits (but can also be fewer or more bits).
Both data items are Python numbers (not strings), or None if the respective
channel was not supplied.
- 'BITS': <data1>/<data2> contain a list of bit values in this MOSI/MISO data
item, and for each of those also their respective start-/endsample numbers.
- 'CS-CHANGE': <data1> is the old CS# pin value, <data2> is the new value.
Both data items are Python numbers (0/1), not strings. At the beginning of
the decoding a packet is generated with <data1> = None and <data2> being the
initial state of the CS# pin or None if the chip select pin is not supplied.
- 'TRANSFER': <data1>/<data2> contain a list of Data() namedtuples for each
byte transferred during this block of CS# asserted time. Each Data() has
fields ss, es, and val.
Examples:
['CS-CHANGE', None, 1]
['CS-CHANGE', 1, 0]
['DATA', 0xff, 0x3a]
['BITS', [[1, 80, 82], [1, 83, 84], [1, 85, 86], [1, 87, 88],
[1, 89, 90], [1, 91, 92], [1, 93, 94], [1, 95, 96]],
[[0, 80, 82], [1, 83, 84], [0, 85, 86], [1, 87, 88],
[1, 89, 90], [1, 91, 92], [0, 93, 94], [0, 95, 96]]]
['DATA', 0x65, 0x00]
['DATA', 0xa8, None]
['DATA', None, 0x55]
['CS-CHANGE', 0, 1]
['TRANSFER', [Data(ss=80, es=96, val=0xff), ...],
[Data(ss=80, es=96, val=0x3a), ...]]
'''
# Key: (CPOL, CPHA). Value: SPI mode.
# Clock polarity (CPOL) = 0/1: Clock is low/high when inactive.
# Clock phase (CPHA) = 0/1: Data is valid on the leading/trailing clock edge.
spi_mode = {
(0, 0): 0, # Mode 0
(0, 1): 1, # Mode 1
(1, 0): 2, # Mode 2
(1, 1): 3, # Mode 3
}
class ChannelError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = '0:spi'
name = '0:SPI'
longname = 'Serial Peripheral Interface'
desc = 'Full-duplex, synchronous, serial bus.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['spi']
tags = ['Embedded/industrial']
channels = (
{'id': 'clk', 'type': 0, 'name': 'CLK', 'desc': 'Clock'},
)
optional_channels = (
{'id': 'miso', 'type': 107, 'name': 'MISO', 'desc': 'Master in, slave out'},
{'id': 'mosi', 'type': 109, 'name': 'MOSI', 'desc': 'Master out, slave in'},
{'id': 'cs', 'type': -1, 'name': 'CS#', 'desc': 'Chip-select'},
)
options = (
{'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
'values': ('active-low', 'active-high')},
{'id': 'cpol', 'desc': 'Clock polarity (CPOL)', 'default': 0,
'values': (0, 1)},
{'id': 'cpha', 'desc': 'Clock phase (CPHA)', 'default': 0,
'values': (0, 1)},
{'id': 'bitorder', 'desc': 'Bit order',
'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
{'id': 'wordsize', 'desc': 'Word size', 'default': 8,
'values': tuple(range(4,129,1))},
)
annotations = (
('106', 'miso-data', 'MISO data'),
('108', 'mosi-data', 'MOSI data'),
)
annotation_rows = (
('miso-data', 'MISO data', (0,)),
('mosi-data', 'MOSI data', (1,)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.bitcount = 0
self.misodata = self.mosidata = 0
self.misobits = []
self.mosibits = []
self.ss_block = -1
self.samplenum = -1
self.ss_transfer = -1
self.cs_was_deasserted = False
self.have_cs = self.have_miso = self.have_mosi = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.bw = (self.options['wordsize'] + 7) // 8
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def putw(self, data):
self.put(self.ss_block, self.samplenum, self.out_ann, data)
def putdata(self):
# Pass MISO and MOSI bits and then data to the next PD up the stack.
so = self.misodata if self.have_miso else None
si = self.mosidata if self.have_mosi else None
if self.have_miso:
ss, es = self.misobits[-1][1], self.misobits[0][2]
if self.have_mosi:
ss, es = self.mosibits[-1][1], self.mosibits[0][2]
# Dataword annotations.
if self.have_miso:
self.put(ss, es, self.out_ann, [0, [self.misodata]])
if self.have_mosi:
self.put(ss, es, self.out_ann, [1, [self.mosidata]])
def reset_decoder_state(self):
self.misodata = 0 if self.have_miso else None
self.mosidata = 0 if self.have_mosi else None
self.misobits = [] if self.have_miso else None
self.mosibits = [] if self.have_mosi else None
self.bitcount = 0
def cs_asserted(self, cs):
active_low = (self.options['cs_polarity'] == 'active-low')
return (cs == 0) if active_low else (cs == 1)
def handle_bit(self, miso, mosi, clk, cs):
# If this is the first bit of a dataword, save its sample number.
if self.bitcount == 0:
self.ss_block = self.samplenum
self.cs_was_deasserted = \
not self.cs_asserted(cs) if self.have_cs else False
ws = self.options['wordsize']
bo = self.options['bitorder']
# Receive MISO bit into our shift register.
if self.have_miso:
if bo == 'msb-first':
self.misodata |= miso << (ws - 1 - self.bitcount)
else:
self.misodata |= miso << self.bitcount
# Receive MOSI bit into our shift register.
if self.have_mosi:
if bo == 'msb-first':
self.mosidata |= mosi << (ws - 1 - self.bitcount)
else:
self.mosidata |= mosi << self.bitcount
# Guesstimate the endsample for this bit (can be overridden below).
es = self.samplenum
if self.bitcount > 0:
if self.have_miso:
es += self.samplenum - self.misobits[0][1]
elif self.have_mosi:
es += self.samplenum - self.mosibits[0][1]
if self.have_miso:
self.misobits.insert(0, [miso, self.samplenum, es])
if self.have_mosi:
self.mosibits.insert(0, [mosi, self.samplenum, es])
if self.bitcount > 0 and self.have_miso:
self.misobits[1][2] = self.samplenum
if self.bitcount > 0 and self.have_mosi:
self.mosibits[1][2] = self.samplenum
self.bitcount += 1
# Continue to receive if not enough bits were received, yet.
if self.bitcount != ws:
return
self.putdata()
self.reset_decoder_state()
def find_clk_edge(self, miso, mosi, clk, cs, first):
if self.have_cs and (first or (self.matched & (0b1 << self.have_cs))):
# Send all CS# pin value changes.
oldcs = None if first else 1 - cs
# Reset decoder state when CS# changes (and the CS# pin is used).
self.reset_decoder_state()
# We only care about samples if CS# is asserted.
if self.have_cs and not self.cs_asserted(cs):
return
# Ignore sample if the clock pin hasn't changed.
if first or not (self.matched & (0b1 << 0)):
return
# Found the correct clock edge, now get the SPI bit(s).
self.handle_bit(miso, mosi, clk, cs)
def decode(self):
# The CLK input is mandatory. Other signals are (individually)
# optional. Yet either MISO or MOSI (or both) must be provided.
# Tell stacked decoders when we don't have a CS# signal.
if not self.has_channel(0):
raise ChannelError('CLK pin required.')
self.have_miso = self.has_channel(1)
self.have_mosi = self.has_channel(2)
if not self.have_miso and not self.have_mosi:
raise ChannelError('Either MISO or MOSI (or both) pins required.')
self.have_cs = self.has_channel(3)
# We want all CLK changes. We want all CS changes if CS is used.
# Map 'have_cs' from boolean to an integer index. This simplifies
# evaluation in other locations.
# Sample data on rising/falling clock edge (depends on mode).
mode = spi_mode[self.options['cpol'], self.options['cpha']]
if mode == 0 or mode == 3: # Sample on rising clock edge
wait_cond = [{0: 'r'}]
else: # Sample on falling clock edge
wait_cond = [{0: 'f'}]
if self.have_cs:
self.have_cs = len(wait_cond)
wait_cond.append({3: 'e'})
# "Pixel compatibility" with the v2 implementation. Grab and
# process the very first sample before checking for edges. The
# previous implementation did this by seeding old values with
# None, which led to an immediate "change" in comparison.
(clk, miso, mosi, cs) = self.wait({})
self.find_clk_edge(miso, mosi, clk, cs, True)
while True:
(clk, miso, mosi, cs) = self.wait(wait_cond)
self.find_clk_edge(miso, mosi, clk, cs, False)

View File

@@ -0,0 +1,40 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
UART (Universal Asynchronous Receiver Transmitter) is a simple serial
communication protocol which allows two devices to talk to each other.
This decoder should work on all "UART-like" async protocols with one
start bit (0), 5-9 databits, an (optional) parity bit, and one or more
stop bits (1), in this order.
It can be run on one signal line (RX or TX) only, or on two lines (RX + TX).
There are various standards for the physical layer specification of the
signals, including RS232, (TTL) UART, RS485, and others. However, the logic
level of the respective pins is only relevant when acquiring the data via
a logic analyzer (you have to select the correct logic analyzer and/or
the correct place where to probe). Once the data is in digital form and
matches the "UART" description above, this protocol decoder can work with
it though, no matter whether the source was on TTL UART levels, or RS232,
or others.
'''
from .pd import Decoder

View File

@@ -0,0 +1,353 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from common.srdhelper import bitpack
from math import floor, ceil
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <rxtx>, <pdata>]
This is the list of <ptype>s and their respective <pdata> values:
- 'STARTBIT': The data is the (integer) value of the start bit (0/1).
- 'DATA': This is always a tuple containing two items:
- 1st item: the (integer) value of the UART data. Valid values
range from 0 to 511 (as the data can be up to 9 bits in size).
- 2nd item: the list of individual data bits and their ss/es numbers.
- 'PARITYBIT': The data is the (integer) value of the parity bit (0/1).
- 'STOPBIT': The data is the (integer) value of the stop bit (0 or 1).
- 'INVALID STARTBIT': The data is the (integer) value of the start bit (0/1).
- 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1).
- 'PARITY ERROR': The data is a tuple with two entries. The first one is
the expected parity value, the second is the actual parity value.
- 'FRAME': The data is always a tuple containing two items: The (integer)
value of the UART data, and a boolean which reflects the validity of the
UART frame.
'''
# Given a parity type to check (odd, even, zero, one), the value of the
# parity bit, the value of the data, and the length of the data (5-9 bits,
# usually 8 bits) return True if the parity is correct, False otherwise.
# 'none' is _not_ allowed as value for 'parity_type'.
def parity_ok(parity_type, parity_bit, data, num_data_bits):
# Handle easy cases first (parity bit is always 1 or 0).
if parity_type == 'zero':
return parity_bit == 0
elif parity_type == 'one':
return parity_bit == 1
# Count number of 1 (high) bits in the data (and the parity bit itself!).
ones = bin(data).count('1') + parity_bit
# Check for odd/even parity.
if parity_type == 'odd':
return (ones % 2) == 1
elif parity_type == 'even':
return (ones % 2) == 0
class SamplerateError(Exception):
pass
class ChannelError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = '0:uart'
name = '0:UART'
longname = 'Universal Asynchronous Receiver/Transmitter'
desc = 'Asynchronous, serial bus.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['uart']
tags = ['Embedded/industrial']
channels = (
{'id': 'rxtx', 'type': 209, 'name': 'RX/TX', 'desc': 'UART transceive line'},
)
options = (
{'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200},
{'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8,
'values': tuple(range(4,129,1))},
{'id': 'parity_type', 'desc': 'Parity type', 'default': 'none',
'values': ('none', 'odd', 'even', 'zero', 'one')},
{'id': 'parity_check', 'desc': 'Check parity?', 'default': 'yes',
'values': ('yes', 'no')},
{'id': 'num_stop_bits', 'desc': 'Stop bits', 'default': 1.0,
'values': (0.0, 0.5, 1.0, 1.5, 2.0, 2.5)},
{'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
'values': ('lsb-first', 'msb-first')},
{'id': 'format', 'desc': 'Data format', 'default': 'hex',
'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
{'id': 'invert', 'desc': 'Invert Signal?', 'default': 'no',
'values': ('yes', 'no')},
{'id': 'anno_startstop', 'desc': 'Display Start/Stop?', 'default': 'no',
'values': ('yes', 'no')},
)
annotations = (
('108', 'data', 'data'),
('7', 'start', 'start bits'),
('6', 'parity-ok', 'parity OK bits'),
('0', 'parity-err', 'parity error bits'),
('1', 'stop', 'stop bits'),
('1000', 'warnings', 'warnings'),
)
annotation_rows = (
('data', 'RX/TX', (0, 1, 2, 3, 4)),
('warnings', 'Warnings', (5,)),
)
idle_state = 'WAIT FOR START BIT'
def putx(self, data):
s, halfbit = self.startsample, self.bit_width / 2.0
if self.options['anno_startstop'] == 'yes' :
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
else :
self.put(self.frame_start, self.samplenum + ceil(halfbit * (1+self.options['num_stop_bits'])), self.out_ann, data)
def putg(self, data):
s, halfbit = self.samplenum, self.bit_width / 2.0
self.put(s - floor(halfbit), s + ceil(halfbit), self.out_ann, data)
def putgse(self, ss, es, data):
self.put(ss, es, self.out_ann, data)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.samplenum = 0
self.frame_start = -1
self.frame_valid = None
self.startbit = -1
self.cur_data_bit = 0
self.datavalue = 0
self.paritybit = -1
self.stopbit1 = -1
self.startsample = -1
self.state = 'WAIT FOR START BIT'
self.databits = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.bw = (self.options['num_data_bits'] + 7) // 8
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# The width of one UART bit in number of samples.
self.bit_width = float(self.samplerate) / float(self.options['baudrate'])
def get_sample_point(self, bitnum):
# Determine absolute sample number of a bit slot's sample point.
# bitpos is the samplenumber which is in the middle of the
# specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit
# (if used) or the first stop bit, and so on).
# The samples within bit are 0, 1, ..., (bit_width - 1), therefore
# index of the middle sample within bit window is (bit_width - 1) / 2.
bitpos = self.frame_start + (self.bit_width - 1) / 2.0
bitpos += bitnum * self.bit_width
return bitpos
def wait_for_start_bit(self, signal):
# Save the sample number where the start bit begins.
self.frame_start = self.samplenum
self.frame_valid = True
self.state = 'GET START BIT'
def get_start_bit(self, signal):
self.startbit = signal
# The startbit must be 0. If not, we report an error and wait
# for the next start bit (assuming this one was spurious).
if self.startbit != 0:
self.putg([5, ['Frame error', 'Frame err', 'FE']])
self.frame_valid = False
es = self.samplenum + ceil(self.bit_width / 2.0)
self.state = 'WAIT FOR START BIT'
return
self.cur_data_bit = 0
self.datavalue = 0
self.startsample = -1
if self.options['anno_startstop'] == 'yes':
self.putg([1, ['Start bit', 'Start', 'S']])
self.state = 'GET DATA BITS'
def get_data_bits(self, signal):
# Save the sample number of the middle of the first data bit.
if self.startsample == -1:
self.startsample = self.samplenum
# Store individual data bits and their start/end samplenumbers.
s, halfbit = self.samplenum, int(self.bit_width / 2)
self.databits.append([signal, s - halfbit, s + halfbit])
# Return here, unless we already received all data bits.
self.cur_data_bit += 1
if self.cur_data_bit < self.options['num_data_bits']:
return
# Convert accumulated data bits to a data value.
bits = [b[0] for b in self.databits]
if self.options['bit_order'] == 'msb-first':
bits.reverse()
self.datavalue = bitpack(bits)
self.putx([0, [self.datavalue]])
#b = self.datavalue
#formatted = self.format_value(b)
#if formatted is not None:
# self.putx([0, [formatted]])
self.databits = []
# Advance to either reception of the parity bit, or reception of
# the STOP bits if parity is not applicable.
self.state = 'GET PARITY BIT'
if self.options['parity_type'] == 'none':
self.state = 'GET STOP BITS'
def format_value(self, v):
# Format value 'v' according to configured options.
# Reflects the user selected kind of representation, as well as
# the number of data bits in the UART frames.
fmt, bits = self.options['format'], self.options['num_data_bits']
# Assume "is printable" for values from 32 to including 126,
# below 32 is "control" and thus not printable, above 127 is
# "not ASCII" in its strict sense, 127 (DEL) is not printable,
# fall back to hex representation for non-printables.
if fmt == 'ascii':
if v in range(32, 126 + 1):
return chr(v)
hexfmt = "[{:02X}]" if bits <= 8 else "[{:03X}]"
return hexfmt.format(v)
# Mere number to text conversion without prefix and padding
# for the "decimal" output format.
if fmt == 'dec':
return "{:d}".format(v)
# Padding with leading zeroes for hex/oct/bin formats, but
# without a prefix for density -- since the format is user
# specified, there is no ambiguity.
if fmt == 'hex':
digits = (bits + 4 - 1) // 4
fmtchar = "X"
elif fmt == 'oct':
digits = (bits + 3 - 1) // 3
fmtchar = "o"
elif fmt == 'bin':
digits = bits
fmtchar = "b"
else:
fmtchar = None
if fmtchar is not None:
fmt = "{{:0{:d}{:s}}}".format(digits, fmtchar)
return fmt.format(v)
return None
def get_parity_bit(self, signal):
self.paritybit = signal
if parity_ok(self.options['parity_type'], self.paritybit,
self.datavalue, self.options['num_data_bits']):
self.putg([2, ['Parity bit', 'Parity', 'P']])
else:
# TODO: Return expected/actual parity values.
self.putg([3, ['Parity error', 'Parity err', 'PE']])
self.frame_valid = False
self.state = 'GET STOP BITS'
# TODO: Currently only supports 1 stop bit.
def get_stop_bits(self, signal):
self.stopbit1 = signal
# Stop bits must be 1. If not, we report an error.
if self.stopbit1 != 1:
self.putg([5, ['Frame error', 'Frame err', 'FE']])
self.frame_valid = False
if self.options['anno_startstop'] == 'yes':
self.putg([2, ['Stop bit', 'Stop', 'T']])
# Pass the complete UART frame to upper layers.
es = self.samplenum + ceil(self.bit_width / 2.0)
self.state = 'WAIT FOR START BIT'
def get_wait_cond(self, inv):
# Return condititions that are suitable for Decoder.wait(). Those
# conditions either match the falling edge of the START bit, or
# the sample point of the next bit time.
state = self.state
if state == 'WAIT FOR START BIT':
return {0: 'r' if inv else 'f'}
if state == 'GET START BIT':
bitnum = 0
elif state == 'GET DATA BITS':
bitnum = 1 + self.cur_data_bit
elif state == 'GET PARITY BIT':
bitnum = 1 + self.options['num_data_bits']
elif state == 'GET STOP BITS':
bitnum = 1 + self.options['num_data_bits']
bitnum += 0 if self.options['parity_type'] == 'none' else 1
want_num = ceil(self.get_sample_point(bitnum))
return {'skip': want_num - self.samplenum}
def inspect_sample(self, signal, inv):
# Inspect a sample returned by .wait() for the specified UART line.
if inv:
signal = not signal
state = self.state
if state == 'WAIT FOR START BIT':
self.wait_for_start_bit(signal)
elif state == 'GET START BIT':
self.get_start_bit(signal)
elif state == 'GET DATA BITS':
self.get_data_bits(signal)
elif state == 'GET PARITY BIT':
self.get_parity_bit(signal)
elif state == 'GET STOP BITS':
self.get_stop_bits(signal)
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
inv = self.options['invert'] == 'yes'
while True:
conds = self.get_wait_cond(inv)
(rxtx, ) = self.wait(conds)
if (self.matched & (0b1 << 0)):
self.inspect_sample(rxtx, inv)

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
I²C (Inter-Integrated Circuit) is a bidirectional, multi-master
bus using two signals (SCL = serial clock line, SDA = serial data line).
'''
from .pd import Decoder

View File

@@ -0,0 +1,295 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2010-2016 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# TODO: Look into arbitration, collision detection, clock synchronisation, etc.
# TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
# TODO: Implement support for detecting various bus errors.
import sigrokdecode as srd
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <pdata>]
<ptype>:
- 'START' (START condition)
- 'START REPEAT' (Repeated START condition)
- 'ADDRESS READ' (Slave address, read)
- 'ADDRESS WRITE' (Slave address, write)
- 'DATA READ' (Data, read)
- 'DATA WRITE' (Data, write)
- 'STOP' (STOP condition)
- 'ACK' (ACK bit)
- 'NACK' (NACK bit)
- 'BITS' (<pdata>: list of data/address bits and their ss/es numbers)
<pdata> is the data or address byte associated with the 'ADDRESS*' and 'DATA*'
command. Slave addresses do not include bit 0 (the READ/WRITE indication bit).
For example, a slave address field could be 0x51 (instead of 0xa2).
For 'START', 'START REPEAT', 'STOP', 'ACK', and 'NACK' <pdata> is None.
'''
# CMD: [annotation-type-index, long annotation, short annotation]
proto = {
'START': [0, 'Start', 'S'],
'START REPEAT': [1, 'Start repeat', 'Sr'],
'STOP': [2, 'Stop', 'P'],
'ACK': [3, 'ACK', 'A'],
'NACK': [4, 'NACK', 'N'],
'BIT': [5, 'Bit', 'B'],
'ADDRESS READ': [6, 'Address read', 'AR'],
'ADDRESS WRITE': [7, 'Address write', 'AW'],
'DATA READ': [8, 'Data read', 'DR'],
'DATA WRITE': [9, 'Data write', 'DW'],
}
class Decoder(srd.Decoder):
api_version = 3
id = '1:i2c'
name = '1:I²C'
longname = 'Inter-Integrated Circuit'
desc = 'Two-wire, multi-master, serial bus.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['i2c']
tags = ['Embedded/industrial']
channels = (
{'id': 'scl', 'type': 8, 'name': 'SCL', 'desc': 'Serial clock line'},
{'id': 'sda', 'type': 108, 'name': 'SDA', 'desc': 'Serial data line'},
)
options = (
{'id': 'address_format', 'desc': 'Displayed slave address format',
'default': 'shifted', 'values': ('shifted', 'unshifted')},
)
annotations = (
('7', 'start', 'Start condition'),
('6', 'repeat-start', 'Repeat start condition'),
('1', 'stop', 'Stop condition'),
('5', 'ack', 'ACK'),
('0', 'nack', 'NACK'),
('208', 'bit', 'Data/address bit'),
('112', 'address-read', 'Address read'),
('111', 'address-write', 'Address write'),
('110', 'data-read', 'Data read'),
('109', 'data-write', 'Data write'),
('1000', 'warnings', 'Human-readable warnings'),
)
annotation_rows = (
('bits', 'Bits', (5,)),
('addr-data', 'Address/Data', (0, 1, 2, 3, 4, 6, 7, 8, 9)),
('warnings', 'Warnings', (10,)),
)
binary = (
('address-read', 'Address read'),
('address-write', 'Address write'),
('data-read', 'Data read'),
('data-write', 'Data write'),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.ss = self.es = self.ss_byte = -1
self.bitcount = 0
self.databyte = 0
self.wr = -1
self.is_repeat_start = 0
self.state = 'FIND START'
self.pdu_start = None
self.pdu_bits = 0
self.bits = []
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
self.out_bitrate = self.register(srd.OUTPUT_META,
meta=(int, 'Bitrate', 'Bitrate from Start bit to Stop bit'))
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def putp(self, data):
self.put(self.ss, self.es, self.out_python, data)
def putb(self, data):
self.put(self.ss, self.es, self.out_binary, data)
def handle_start(self):
self.ss, self.es = self.samplenum, self.samplenum
self.pdu_start = self.samplenum
self.pdu_bits = 0
cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
self.putp([cmd, None])
self.putx([proto[cmd][0], proto[cmd][1:]])
self.state = 'FIND ADDRESS'
self.bitcount = self.databyte = 0
self.is_repeat_start = 1
self.wr = -1
self.bits = []
# Gather 8 bits of data plus the ACK/NACK bit.
def handle_address_or_data(self, scl, sda):
self.pdu_bits += 1
# Address and data are transmitted MSB-first.
self.databyte <<= 1
self.databyte |= sda
# Remember the start of the first data/address bit.
if self.bitcount == 0:
self.ss_byte = self.samplenum
# Store individual bits and their start/end samplenumbers.
# In the list, index 0 represents the LSB (I²C transmits MSB-first).
self.bits.insert(0, [sda, self.samplenum, self.samplenum])
if self.bitcount > 0:
self.bits[1][2] = self.samplenum
if self.bitcount == 7:
self.bitwidth = self.bits[1][2] - self.bits[2][2]
self.bits[0][2] += self.bitwidth
# Return if we haven't collected all 8 + 1 bits, yet.
if self.bitcount < 7:
self.bitcount += 1
return
d = self.databyte
if self.state == 'FIND ADDRESS':
# The READ/WRITE bit is only in address bytes, not data bytes.
self.wr = 0 if (self.databyte & 1) else 1
if self.options['address_format'] == 'shifted':
d = d >> 1
bin_class = -1
if self.state == 'FIND ADDRESS' and self.wr == 1:
cmd = 'ADDRESS WRITE'
bin_class = 1
elif self.state == 'FIND ADDRESS' and self.wr == 0:
cmd = 'ADDRESS READ'
bin_class = 0
elif self.state == 'FIND DATA' and self.wr == 1:
cmd = 'DATA WRITE'
bin_class = 3
elif self.state == 'FIND DATA' and self.wr == 0:
cmd = 'DATA READ'
bin_class = 2
self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth
self.putp(['BITS', self.bits])
self.putp([cmd, d])
self.putb([bin_class, bytes([d])])
for bit in self.bits:
self.put(bit[1], bit[2], self.out_ann, [5, ['%d' % bit[0]]])
if cmd.startswith('ADDRESS'):
self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R']
self.putx([0, w])
self.ss, self.es = self.ss_byte, self.samplenum
self.putx([proto[cmd][0], ['%s: {$}' % proto[cmd][1], '%s: {$}' % proto[cmd][2], '{$}', d]])
# Done with this packet.
self.bitcount = self.databyte = 0
self.bits = []
self.state = 'FIND ACK'
def get_ack(self, scl, sda):
self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
cmd = 'NACK' if (sda == 1) else 'ACK'
self.putp([cmd, None])
self.putx([proto[cmd][0], proto[cmd][1:]])
# There could be multiple data bytes in a row, so either find
# another data byte or a STOP condition next.
self.state = 'FIND DATA'
def handle_stop(self):
# Meta bitrate
if self.samplerate:
elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
bitrate = int(1 / elapsed * self.pdu_bits)
self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
cmd = 'STOP'
self.ss, self.es = self.samplenum, self.samplenum
self.putp([cmd, None])
self.putx([proto[cmd][0], proto[cmd][1:]])
self.state = 'FIND START'
self.is_repeat_start = 0
self.wr = -1
self.bits = []
def decode(self):
while True:
# State machine.
if self.state == 'FIND START':
# Wait for a START condition (S): SCL = high, SDA = falling.
self.wait({0: 'h', 1: 'f'})
self.handle_start()
elif self.state == 'FIND ADDRESS':
# Wait for any of the following conditions (or combinations):
# a) Data sampling of receiver: SCL = rising, and/or
# b) START condition (S): SCL = high, SDA = falling, and/or
# c) STOP condition (P): SCL = high, SDA = rising
(scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}])
# Check which of the condition(s) matched and handle them.
if (self.matched & (0b1 << 0)):
self.handle_address_or_data(scl, sda)
elif (self.matched & (0b1 << 1)):
self.handle_start()
elif (self.matched & (0b1 << 2)):
self.handle_stop()
elif self.state == 'FIND DATA':
# Wait for any of the following conditions (or combinations):
# a) Data sampling of receiver: SCL = rising, and/or
# b) START condition (S): SCL = high, SDA = falling, and/or
# c) STOP condition (P): SCL = high, SDA = rising
(scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'f'}, {0: 'h', 1: 'r'}])
# Check which of the condition(s) matched and handle them.
if (self.matched & (0b1 << 0)):
self.handle_address_or_data(scl, sda)
elif (self.matched & (0b1 << 1)):
self.handle_start()
elif (self.matched & (0b1 << 2)):
self.handle_stop()
elif self.state == 'FIND ACK':
# Wait for any of the following conditions (or combinations):
# a) a data/ack bit: SCL = rising.
# b) STOP condition (P): SCL = high, SDA = rising
(scl, sda) = self.wait([{0: 'r'}, {0: 'h', 1: 'r'}])
if (self.matched & (0b1 << 0)):
self.get_ack(scl, sda)
elif (self.matched & (0b1 << 1)):
self.handle_stop()

View File

@@ -0,0 +1,32 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
The SPI (Serial Peripheral Interface) protocol decoder supports synchronous
SPI(-like) protocols with a clock line, a MISO and MOSI line for data
transfer in two directions, and an optional CS# pin.
Either MISO or MOSI (but not both) can be optional.
If CS# is supplied, data is only decoded when CS# is asserted (clock
transitions where CS# is not asserted are ignored). If CS# is not supplied,
data is decoded on every clock transition (depending on SPI mode).
'''
from .pd import Decoder

View File

@@ -0,0 +1,352 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from collections import namedtuple
Data = namedtuple('Data', ['ss', 'es', 'val'])
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <data1>, <data2>]
<ptype>:
- 'DATA': <data1> contains the MOSI data, <data2> contains the MISO data.
The data is _usually_ 8 bits (but can also be fewer or more bits).
Both data items are Python numbers (not strings), or None if the respective
channel was not supplied.
- 'BITS': <data1>/<data2> contain a list of bit values in this MOSI/MISO data
item, and for each of those also their respective start-/endsample numbers.
- 'CS-CHANGE': <data1> is the old CS# pin value, <data2> is the new value.
Both data items are Python numbers (0/1), not strings. At the beginning of
the decoding a packet is generated with <data1> = None and <data2> being the
initial state of the CS# pin or None if the chip select pin is not supplied.
- 'TRANSFER': <data1>/<data2> contain a list of Data() namedtuples for each
byte transferred during this block of CS# asserted time. Each Data() has
fields ss, es, and val.
Examples:
['CS-CHANGE', None, 1]
['CS-CHANGE', 1, 0]
['DATA', 0xff, 0x3a]
['BITS', [[1, 80, 82], [1, 83, 84], [1, 85, 86], [1, 87, 88],
[1, 89, 90], [1, 91, 92], [1, 93, 94], [1, 95, 96]],
[[0, 80, 82], [1, 83, 84], [0, 85, 86], [1, 87, 88],
[1, 89, 90], [1, 91, 92], [0, 93, 94], [0, 95, 96]]]
['DATA', 0x65, 0x00]
['DATA', 0xa8, None]
['DATA', None, 0x55]
['CS-CHANGE', 0, 1]
['TRANSFER', [Data(ss=80, es=96, val=0xff), ...],
[Data(ss=80, es=96, val=0x3a), ...]]
'''
# Key: (CPOL, CPHA). Value: SPI mode.
# Clock polarity (CPOL) = 0/1: Clock is low/high when inactive.
# Clock phase (CPHA) = 0/1: Data is valid on the leading/trailing clock edge.
spi_mode = {
(0, 0): 0, # Mode 0
(0, 1): 1, # Mode 1
(1, 0): 2, # Mode 2
(1, 1): 3, # Mode 3
}
class ChannelError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = '1:spi'
name = '1:SPI'
longname = 'Serial Peripheral Interface'
desc = 'Full-duplex, synchronous, serial bus.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['spi']
tags = ['Embedded/industrial']
channels = (
{'id': 'clk', 'type': 0, 'name': 'CLK', 'desc': 'Clock'},
)
optional_channels = (
{'id': 'miso', 'type': 107, 'name': 'MISO', 'desc': 'Master in, slave out'},
{'id': 'mosi', 'type': 109, 'name': 'MOSI', 'desc': 'Master out, slave in'},
{'id': 'cs', 'type': -1, 'name': 'CS#', 'desc': 'Chip-select'},
)
options = (
{'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
'values': ('active-low', 'active-high')},
{'id': 'cpol', 'desc': 'Clock polarity (CPOL)', 'default': 0,
'values': (0, 1)},
{'id': 'cpha', 'desc': 'Clock phase (CPHA)', 'default': 0,
'values': (0, 1)},
{'id': 'bitorder', 'desc': 'Bit order',
'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
{'id': 'wordsize', 'desc': 'Word size', 'default': 8,
'values': tuple(range(5,129,1))},
{'id': 'frame', 'desc': 'Frame Decoder', 'default': 'no',
'values': ('yes', 'no')},
)
annotations = (
('106', 'miso-data', 'MISO data'),
('108', 'mosi-data', 'MOSI data'),
('207', 'miso-bits', 'MISO bits'),
('209', 'mosi-bits', 'MOSI bits'),
('1000', 'warnings', 'Human-readable warnings'),
('6', 'miso-transfer', 'MISO transfer'),
('8', 'mosi-transfer', 'MOSI transfer'),
)
annotation_rows = (
('miso-bits', 'MISO bits', (2,)),
('miso-data', 'MISO data', (0,)),
('miso-transfer', 'MISO transfer', (5,)),
('mosi-bits', 'MOSI bits', (3,)),
('mosi-data', 'MOSI data', (1,)),
('mosi-transfer', 'MOSI transfer', (6,)),
('other', 'Other', (4,)),
)
binary = (
('miso', 'MISO'),
('mosi', 'MOSI'),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.bitcount = 0
self.misodata = self.mosidata = 0
self.misobits = []
self.mosibits = []
self.misobytes = []
self.mosibytes = []
self.ss_block = -1
self.samplenum = -1
self.ss_transfer = -1
self.cs_was_deasserted = False
self.have_cs = self.have_miso = self.have_mosi = None
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
self.out_bitrate = self.register(srd.OUTPUT_META,
meta=(int, 'Bitrate', 'Bitrate during transfers'))
self.bw = (self.options['wordsize'] + 7) // 8
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def putw(self, data):
self.put(self.ss_block, self.samplenum, self.out_ann, data)
def putdata(self, frame):
# Pass MISO and MOSI bits and then data to the next PD up the stack.
so = self.misodata if self.have_miso else None
si = self.mosidata if self.have_mosi else None
so_bits = self.misobits if self.have_miso else None
si_bits = self.mosibits if self.have_mosi else None
if self.have_miso:
ss, es = self.misobits[-1][1], self.misobits[0][2]
bdata = so.to_bytes(self.bw, byteorder='big')
self.put(ss, es, self.out_binary, [0, bdata])
if self.have_mosi:
ss, es = self.mosibits[-1][1], self.mosibits[0][2]
bdata = si.to_bytes(self.bw, byteorder='big')
self.put(ss, es, self.out_binary, [1, bdata])
self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits])
self.put(ss, es, self.out_python, ['DATA', si, so])
if frame:
if self.have_miso:
self.misobytes.append(Data(ss=ss, es=es, val=so))
if self.have_mosi:
self.mosibytes.append(Data(ss=ss, es=es, val=si))
# Bit annotations.
if self.have_miso:
for bit in self.misobits:
self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]])
if self.have_mosi:
for bit in self.mosibits:
self.put(bit[1], bit[2], self.out_ann, [3, ['%d' % bit[0]]])
# Dataword annotations.
if self.have_miso:
self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]])
if self.have_mosi:
self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]])
def reset_decoder_state(self):
self.misodata = 0 if self.have_miso else None
self.mosidata = 0 if self.have_mosi else None
self.misobits = [] if self.have_miso else None
self.mosibits = [] if self.have_mosi else None
self.bitcount = 0
def cs_asserted(self, cs):
active_low = (self.options['cs_polarity'] == 'active-low')
return (cs == 0) if active_low else (cs == 1)
def handle_bit(self, miso, mosi, clk, cs, frame):
# If this is the first bit of a dataword, save its sample number.
if self.bitcount == 0:
self.ss_block = self.samplenum
self.cs_was_deasserted = \
not self.cs_asserted(cs) if self.have_cs else False
ws = self.options['wordsize']
bo = self.options['bitorder']
# Receive MISO bit into our shift register.
if self.have_miso:
if bo == 'msb-first':
self.misodata |= miso << (ws - 1 - self.bitcount)
else:
self.misodata |= miso << self.bitcount
# Receive MOSI bit into our shift register.
if self.have_mosi:
if bo == 'msb-first':
self.mosidata |= mosi << (ws - 1 - self.bitcount)
else:
self.mosidata |= mosi << self.bitcount
# Guesstimate the endsample for this bit (can be overridden below).
es = self.samplenum
if self.bitcount > 0:
if self.have_miso:
es += self.samplenum - self.misobits[0][1]
elif self.have_mosi:
es += self.samplenum - self.mosibits[0][1]
if self.have_miso:
self.misobits.insert(0, [miso, self.samplenum, es])
if self.have_mosi:
self.mosibits.insert(0, [mosi, self.samplenum, es])
if self.bitcount > 0 and self.have_miso:
self.misobits[1][2] = self.samplenum
if self.bitcount > 0 and self.have_mosi:
self.mosibits[1][2] = self.samplenum
self.bitcount += 1
# Continue to receive if not enough bits were received, yet.
if self.bitcount != ws:
return
self.putdata(frame)
# Meta bitrate.
if self.samplerate:
elapsed = 1 / float(self.samplerate)
elapsed *= (self.samplenum - self.ss_block + 1)
bitrate = int(1 / elapsed * ws)
self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
if self.have_cs and self.cs_was_deasserted:
self.putw([4, ['CS# was deasserted during this data word!']])
self.reset_decoder_state()
def find_clk_edge(self, miso, mosi, clk, cs, first, frame):
if self.have_cs and (first or (self.matched & (0b1 << self.have_cs))):
# Send all CS# pin value changes.
oldcs = None if first else 1 - cs
self.put(self.samplenum, self.samplenum, self.out_python,
['CS-CHANGE', oldcs, cs])
if frame:
if self.cs_asserted(cs):
self.ss_transfer = self.samplenum
self.misobytes = []
self.mosibytes = []
elif self.ss_transfer != -1:
if self.have_miso:
self.put(self.ss_transfer, self.samplenum, self.out_ann,
[5, [' '.join(format(x.val, '02X') for x in self.misobytes)]])
if self.have_mosi:
self.put(self.ss_transfer, self.samplenum, self.out_ann,
[6, [' '.join(format(x.val, '02X') for x in self.mosibytes)]])
self.put(self.ss_transfer, self.samplenum, self.out_python,
['TRANSFER', self.mosibytes, self.misobytes])
# Reset decoder state when CS# changes (and the CS# pin is used).
self.reset_decoder_state()
# We only care about samples if CS# is asserted.
if self.have_cs and not self.cs_asserted(cs):
return
# Ignore sample if the clock pin hasn't changed.
if first or not (self.matched & (0b1 << 0)):
return
# Found the correct clock edge, now get the SPI bit(s).
self.handle_bit(miso, mosi, clk, cs, frame)
def decode(self):
# The CLK input is mandatory. Other signals are (individually)
# optional. Yet either MISO or MOSI (or both) must be provided.
# Tell stacked decoders when we don't have a CS# signal.
if not self.has_channel(0):
raise ChannelError('CLK pin required.')
self.have_miso = self.has_channel(1)
self.have_mosi = self.has_channel(2)
if not self.have_miso and not self.have_mosi:
raise ChannelError('Either MISO or MOSI (or both) pins required.')
self.have_cs = self.has_channel(3)
if not self.have_cs:
self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
frame = self.options['frame'] == 'yes'
# We want all CLK changes. We want all CS changes if CS is used.
# Map 'have_cs' from boolean to an integer index. This simplifies
# evaluation in other locations.
# Sample data on rising/falling clock edge (depends on mode).
mode = spi_mode[self.options['cpol'], self.options['cpha']]
if mode == 0 or mode == 3: # Sample on rising clock edge
wait_cond = [{0: 'r'}]
else: # Sample on falling clock edge
wait_cond = [{0: 'f'}]
if self.have_cs:
self.have_cs = len(wait_cond)
wait_cond.append({3: 'e'})
# "Pixel compatibility" with the v2 implementation. Grab and
# process the very first sample before checking for edges. The
# previous implementation did this by seeding old values with
# None, which led to an immediate "change" in comparison.
(clk, miso, mosi, cs) = self.wait({})
self.find_clk_edge(miso, mosi, clk, cs, True, frame)
while True:
(clk, miso, mosi, cs) = self.wait(wait_cond)
self.find_clk_edge(miso, mosi, clk, cs, False, frame)

View File

@@ -0,0 +1,40 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
UART (Universal Asynchronous Receiver Transmitter) is a simple serial
communication protocol which allows two devices to talk to each other.
This decoder should work on all "UART-like" async protocols with one
start bit (0), 5-9 databits, an (optional) parity bit, and one or more
stop bits (1), in this order.
It can be run on one signal line (RX or TX) only, or on two lines (RX + TX).
There are various standards for the physical layer specification of the
signals, including RS232, (TTL) UART, RS485, and others. However, the logic
level of the respective pins is only relevant when acquiring the data via
a logic analyzer (you have to select the correct logic analyzer and/or
the correct place where to probe). Once the data is in digital form and
matches the "UART" description above, this protocol decoder can work with
it though, no matter whether the source was on TTL UART levels, or RS232,
or others.
'''
from .pd import Decoder

View File

@@ -0,0 +1,441 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from common.srdhelper import bitpack
from math import floor, ceil
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <rxtx>, <pdata>]
This is the list of <ptype>s and their respective <pdata> values:
- 'STARTBIT': The data is the (integer) value of the start bit (0/1).
- 'DATA': This is always a tuple containing two items:
- 1st item: the (integer) value of the UART data. Valid values
range from 0 to 511 (as the data can be up to 9 bits in size).
- 2nd item: the list of individual data bits and their ss/es numbers.
- 'PARITYBIT': The data is the (integer) value of the parity bit (0/1).
- 'STOPBIT': The data is the (integer) value of the stop bit (0 or 1).
- 'INVALID STARTBIT': The data is the (integer) value of the start bit (0/1).
- 'INVALID STOPBIT': The data is the (integer) value of the stop bit (0/1).
- 'PARITY ERROR': The data is a tuple with two entries. The first one is
the expected parity value, the second is the actual parity value.
- 'BREAK': The data is always 0.
- 'FRAME': The data is always a tuple containing two items: The (integer)
value of the UART data, and a boolean which reflects the validity of the
UART frame.
'''
# Given a parity type to check (odd, even, zero, one), the value of the
# parity bit, the value of the data, and the length of the data (5-9 bits,
# usually 8 bits) return True if the parity is correct, False otherwise.
# 'none' is _not_ allowed as value for 'parity_type'.
def parity_ok(parity_type, parity_bit, data, num_data_bits):
# Handle easy cases first (parity bit is always 1 or 0).
if parity_type == 'zero':
return parity_bit == 0
elif parity_type == 'one':
return parity_bit == 1
# Count number of 1 (high) bits in the data (and the parity bit itself!).
ones = bin(data).count('1') + parity_bit
# Check for odd/even parity.
if parity_type == 'odd':
return (ones % 2) == 1
elif parity_type == 'even':
return (ones % 2) == 0
class SamplerateError(Exception):
pass
class ChannelError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = '1:uart'
name = '1:UART'
longname = 'Universal Asynchronous Receiver/Transmitter'
desc = 'Asynchronous, serial bus.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['uart']
tags = ['Embedded/industrial']
channels = (
{'id': 'rxtx', 'type': 209, 'name': 'RX/TX', 'desc': 'UART transceive line'},
)
options = (
{'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200},
{'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8,
'values': tuple(range(4,129,1))},
{'id': 'parity_type', 'desc': 'Parity type', 'default': 'none',
'values': ('none', 'odd', 'even', 'zero', 'one')},
{'id': 'parity_check', 'desc': 'Check parity?', 'default': 'yes',
'values': ('yes', 'no')},
{'id': 'num_stop_bits', 'desc': 'Stop bits', 'default': 1.0,
'values': (0.0, 0.5, 1.0, 1.5, 2.0, 2.5)},
{'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
'values': ('lsb-first', 'msb-first')},
{'id': 'format', 'desc': 'Data format', 'default': 'hex',
'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
{'id': 'invert', 'desc': 'Invert Signal?', 'default': 'no',
'values': ('yes', 'no')},
{'id': 'anno_startstop', 'desc': 'Display Start/Stop?', 'default': 'yes',
'values': ('yes', 'no')},
)
annotations = (
('108', 'data', 'data'),
('7', 'start', 'start bits'),
('6', 'parity-ok', 'parity OK bits'),
('0', 'parity-err', 'parity error bits'),
('1', 'stop', 'stop bits'),
('1000', 'warnings', 'warnings'),
('209', 'data-bits', 'data bits'),
('10', 'break', 'break'),
)
annotation_rows = (
('data', 'RX/TX', (0, 1, 2, 3, 4)),
('data-bits', 'Bits', (6,)),
('warnings', 'Warnings', (5,)),
('break', 'break', (7,)),
)
binary = (
('rxtx', 'RX/TX dump'),
)
idle_state = 'WAIT FOR START BIT'
def putx(self, data):
s, halfbit = self.startsample, self.bit_width / 2.0
if self.options['anno_startstop'] == 'yes' :
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_ann, data)
else :
self.put(self.frame_start, self.samplenum + ceil(halfbit * (1+self.options['num_stop_bits'])), self.out_ann, data)
def putpx(self, data):
s, halfbit = self.startsample, self.bit_width / 2.0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_python, data)
def putg(self, data):
s, halfbit = self.samplenum, self.bit_width / 2.0
self.put(s - floor(halfbit), s + ceil(halfbit), self.out_ann, data)
def putp(self, data):
s, halfbit = self.samplenum, self.bit_width / 2.0
self.put(s - floor(halfbit), s + ceil(halfbit), self.out_python, data)
def putgse(self, ss, es, data):
self.put(ss, es, self.out_ann, data)
def putpse(self, ss, es, data):
self.put(ss, es, self.out_python, data)
def putbin(self, data):
s, halfbit = self.startsample, self.bit_width / 2.0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.samplenum = 0
self.frame_start = -1
self.frame_valid = None
self.startbit = -1
self.cur_data_bit = 0
self.datavalue = 0
self.paritybit = -1
self.stopbit1 = -1
self.startsample = -1
self.state = 'WAIT FOR START BIT'
self.databits = []
self.break_start = None
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_binary = self.register(srd.OUTPUT_BINARY)
self.out_ann = self.register(srd.OUTPUT_ANN)
self.bw = (self.options['num_data_bits'] + 7) // 8
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# The width of one UART bit in number of samples.
self.bit_width = float(self.samplerate) / float(self.options['baudrate'])
def get_sample_point(self, bitnum):
# Determine absolute sample number of a bit slot's sample point.
# bitpos is the samplenumber which is in the middle of the
# specified UART bit (0 = start bit, 1..x = data, x+1 = parity bit
# (if used) or the first stop bit, and so on).
# The samples within bit are 0, 1, ..., (bit_width - 1), therefore
# index of the middle sample within bit window is (bit_width - 1) / 2.
bitpos = self.frame_start + (self.bit_width - 1) / 2.0
bitpos += bitnum * self.bit_width
return bitpos
def wait_for_start_bit(self, signal):
# Save the sample number where the start bit begins.
self.frame_start = self.samplenum
self.frame_valid = True
self.state = 'GET START BIT'
def get_start_bit(self, signal):
self.startbit = signal
# The startbit must be 0. If not, we report an error and wait
# for the next start bit (assuming this one was spurious).
if self.startbit != 0:
self.putp(['INVALID STARTBIT', 0, self.startbit])
self.putg([5, ['Frame error', 'Frame err', 'FE']])
self.frame_valid = False
es = self.samplenum + ceil(self.bit_width / 2.0)
self.putpse(self.frame_start, es, ['FRAME', 0,
(self.datavalue, self.frame_valid)])
self.state = 'WAIT FOR START BIT'
return
self.cur_data_bit = 0
self.datavalue = 0
self.startsample = -1
self.putp(['STARTBIT', 0, self.startbit])
if self.options['anno_startstop'] == 'yes':
self.putg([1, ['Start bit', 'Start', 'S']])
self.state = 'GET DATA BITS'
def get_data_bits(self, signal):
# Save the sample number of the middle of the first data bit.
if self.startsample == -1:
self.startsample = self.samplenum
self.putg([6, ['%d' % signal]])
# Store individual data bits and their start/end samplenumbers.
s, halfbit = self.samplenum, int(self.bit_width / 2)
self.databits.append([signal, s - halfbit, s + halfbit])
# Return here, unless we already received all data bits.
self.cur_data_bit += 1
if self.cur_data_bit < self.options['num_data_bits']:
return
# Convert accumulated data bits to a data value.
bits = [b[0] for b in self.databits]
if self.options['bit_order'] == 'msb-first':
bits.reverse()
self.datavalue = bitpack(bits)
self.putpx(['DATA', 0, (self.datavalue, self.databits)])
self.putx([0, [self.datavalue]])
b = self.datavalue
#formatted = self.format_value(b)
#if formatted is not None:
# self.putx([0, [formatted]])
bdata = b.to_bytes(self.bw, byteorder='big')
self.putbin([0, bdata])
self.putbin([1, bdata])
self.databits = []
# Advance to either reception of the parity bit, or reception of
# the STOP bits if parity is not applicable.
self.state = 'GET PARITY BIT'
if self.options['parity_type'] == 'none':
self.state = 'GET STOP BITS'
def format_value(self, v):
# Format value 'v' according to configured options.
# Reflects the user selected kind of representation, as well as
# the number of data bits in the UART frames.
fmt, bits = self.options['format'], self.options['num_data_bits']
# Assume "is printable" for values from 32 to including 126,
# below 32 is "control" and thus not printable, above 127 is
# "not ASCII" in its strict sense, 127 (DEL) is not printable,
# fall back to hex representation for non-printables.
if fmt == 'ascii':
if v in range(32, 126 + 1):
return chr(v)
hexfmt = "[{:02X}]" if bits <= 8 else "[{:03X}]"
return hexfmt.format(v)
# Mere number to text conversion without prefix and padding
# for the "decimal" output format.
if fmt == 'dec':
return "{:d}".format(v)
# Padding with leading zeroes for hex/oct/bin formats, but
# without a prefix for density -- since the format is user
# specified, there is no ambiguity.
if fmt == 'hex':
digits = (bits + 4 - 1) // 4
fmtchar = "X"
elif fmt == 'oct':
digits = (bits + 3 - 1) // 3
fmtchar = "o"
elif fmt == 'bin':
digits = bits
fmtchar = "b"
else:
fmtchar = None
if fmtchar is not None:
fmt = "{{:0{:d}{:s}}}".format(digits, fmtchar)
return fmt.format(v)
return None
def get_parity_bit(self, signal):
self.paritybit = signal
if parity_ok(self.options['parity_type'], self.paritybit,
self.datavalue, self.options['num_data_bits']):
self.putp(['PARITYBIT', 0, self.paritybit])
self.putg([2, ['Parity bit', 'Parity', 'P']])
else:
# TODO: Return expected/actual parity values.
self.putp(['PARITY ERROR', 0, (0, 1)]) # FIXME: Dummy tuple...
self.putg([3, ['Parity error', 'Parity err', 'PE']])
self.frame_valid = False
self.state = 'GET STOP BITS'
# TODO: Currently only supports 1 stop bit.
def get_stop_bits(self, signal):
self.stopbit1 = signal
# Stop bits must be 1. If not, we report an error.
if self.stopbit1 != 1:
self.putp(['INVALID STOPBIT', 0, self.stopbit1])
self.putg([5, ['Frame error', 'Frame err', 'FE']])
self.frame_valid = False
self.putp(['STOPBIT', 0, self.stopbit1])
if self.options['anno_startstop'] == 'yes':
self.putg([2, ['Stop bit', 'Stop', 'T']])
# Pass the complete UART frame to upper layers.
es = self.samplenum + ceil(self.bit_width / 2.0)
self.putpse(self.frame_start, es, ['FRAME', 0,
(self.datavalue, self.frame_valid)])
self.state = 'WAIT FOR START BIT'
def handle_break(self):
self.putpse(self.frame_start, self.samplenum,
['BREAK', 0, 0])
self.putgse(self.frame_start, self.samplenum,
[7, ['Break condition', 'Break', 'Brk', 'B']])
self.state = 'WAIT FOR START BIT'
def get_wait_cond(self, inv):
# Return condititions that are suitable for Decoder.wait(). Those
# conditions either match the falling edge of the START bit, or
# the sample point of the next bit time.
state = self.state
if state == 'WAIT FOR START BIT':
return {0: 'r' if inv else 'f'}
if state == 'GET START BIT':
bitnum = 0
elif state == 'GET DATA BITS':
bitnum = 1 + self.cur_data_bit
elif state == 'GET PARITY BIT':
bitnum = 1 + self.options['num_data_bits']
elif state == 'GET STOP BITS':
bitnum = 1 + self.options['num_data_bits']
bitnum += 0 if self.options['parity_type'] == 'none' else 1
want_num = ceil(self.get_sample_point(bitnum))
return {'skip': want_num - self.samplenum}
def inspect_sample(self, signal, inv):
# Inspect a sample returned by .wait() for the specified UART line.
if inv:
signal = not signal
state = self.state
if state == 'WAIT FOR START BIT':
self.wait_for_start_bit(signal)
elif state == 'GET START BIT':
self.get_start_bit(signal)
elif state == 'GET DATA BITS':
self.get_data_bits(signal)
elif state == 'GET PARITY BIT':
self.get_parity_bit(signal)
elif state == 'GET STOP BITS':
self.get_stop_bits(signal)
def inspect_edge(self, signal, inv):
# Inspect edges, independently from traffic, to detect break conditions.
if inv:
signal = not signal
if not signal:
# Signal went low. Start another interval.
self.break_start = self.samplenum
return
# Signal went high. Was there an extended period with low signal?
if self.break_start is None:
return
diff = self.samplenum - self.break_start
if diff >= self.break_min_sample_count:
self.handle_break()
self.break_start = None
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
inv = self.options['invert'] == 'yes'
cond_data_idx = None
# Determine the number of samples for a complete frame's time span.
# A period of low signal (at least) that long is a break condition.
frame_samples = 1 # START
frame_samples += self.options['num_data_bits']
frame_samples += 0 if self.options['parity_type'] == 'none' else 1
frame_samples += self.options['num_stop_bits']
frame_samples *= self.bit_width
self.break_min_sample_count = ceil(frame_samples)
cond_edge_idx = None
while True:
conds = []
cond_data_idx = len(conds)
conds.append(self.get_wait_cond(inv))
cond_edge_idx = len(conds)
conds.append({0: 'e'})
(rxtx, ) = self.wait(conds)
if cond_data_idx is not None and (self.matched & (0b1 << cond_data_idx)):
self.inspect_sample(rxtx, inv)
if cond_edge_idx is not None and (self.matched & (0b1 << cond_edge_idx)):
self.inspect_edge(rxtx, inv)

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2014 Jens Steinhauser <jens.steinhauser@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
by the AMICCOM A7105 2.4GHz transceiver chips.
'''
from .pd import Decoder

View File

@@ -0,0 +1,356 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Richard Li <richard.li@ces.hk>
##
## 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 <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
class ChannelError(Exception):
pass
regs = {
# addr: ('name', size)
0x00: ('MODE', 1),
0x01: ('MODE_CTRL', 1),
0x02: ('CALC', 1),
0x03: ('FIFO_I', 1),
0x04: ('FIFO_II', 1),
0x05: ('FIFO_DATA', 1),
0x06: ('ID_DATA', 1),
0x07: ('RC_OSC_I', 1),
0x08: ('RC_OSC_II', 1),
0x09: ('RC_OSC_III', 1),
0x0a: ('CKO_PIN', 1),
0x0b: ('GPIO1_PIN_I', 1),
0x0c: ('GPIO2_PIN_II', 1),
0x0d: ('CLOCK', 1),
0x0e: ('DATA_RATE', 1),
0x0f: ('PLL_I', 1),
0x10: ('PLL_II', 1),
0x11: ('PLL_III', 1),
0x12: ('PLL_IV', 1),
0x13: ('PLL_V', 1),
0x14: ('TX_I', 1),
0x15: ('TX_II', 1),
0x16: ('DELAY_I', 1),
0x17: ('DELAY_II', 1),
0x18: ('RX', 1),
0x19: ('RX_GAIN_I', 1),
0x1a: ('RX_GAIN_II', 1),
0x1b: ('RX_GAIN_III', 1),
0x1c: ('RX_GAIN_IV', 1),
0x1d: ('RSSI_THRES', 1),
0x1e: ('ADC', 1),
0x1f: ('CODE_I', 1),
0x20: ('CODE_II', 1),
0x21: ('CODE_III', 1),
0x22: ('IF_CAL_I', 1),
0x23: ('IF_CAL_II', 1),
0x24: ('VCO_CURR_CAL', 1),
0x25: ('VCO_SB_CALC_I', 1),
0x26: ('VCO_SB_CALC_II', 1),
0x27: ('BATT_DETECT', 1),
0x28: ('TX_TEST', 1),
0x29: ('RX_DEM_TEST_I', 1),
0x2a: ('RX_DEM_TEST_II', 1),
0x2b: ('CPC', 1),
0x2c: ('CRYSTAL_TEST', 1),
0x2d: ('PLL_TEST', 1),
0x2e: ('VCO_TEST_I', 1),
0x2f: ('VCO_TEST_II', 1),
0x30: ('IFAT', 1),
0x31: ('RSCALE', 1),
0x32: ('FILTER_TEST', 1),
0x33: ('UNKNOWN', 1),
}
class Decoder(srd.Decoder):
api_version = 3
id = 'a7105'
name = 'A7105'
longname = 'AMICCOM A7105'
desc = '2.4GHz FSK/GFSK Transceiver with 2K ~ 500Kbps data rate.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
tags = ['IC', 'Wireless/RF']
options = (
{'id': 'hex_display', 'desc': 'Display payload in Hex', 'default': 'yes',
'values': ('yes', 'no')},
)
annotations = (
# Sent from the host to the chip.
('cmd', 'Commands sent to the device'),
('tx-data', 'Payload sent to the device'),
# Returned by the chip.
('rx-data', 'Payload read from the device'),
('warning', 'Warnings'),
)
ann_cmd = 0
ann_tx = 1
ann_rx = 2
ann_warn = 3
annotation_rows = (
('commands', 'Commands', (ann_cmd, ann_tx, ann_rx)),
('warnings', 'Warnings', (ann_warn,)),
)
def __init__(self):
self.reset()
def reset(self):
self.next()
self.requirements_met = True
self.cs_was_released = False
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def warn(self, pos, msg):
'''Put a warning message 'msg' at 'pos'.'''
self.put(pos[0], pos[1], self.out_ann, [self.ann_warn, [msg]])
def putp(self, pos, ann, msg):
'''Put an annotation message 'msg' at 'pos'.'''
self.put(pos[0], pos[1], self.out_ann, [ann, [msg]])
def next(self):
'''Resets the decoder after a complete command was decoded.'''
# 'True' for the first byte after CS went low.
self.first = True
# The current command, and the minimum and maximum number
# of data bytes to follow.
self.cmd = None
self.min = 0
self.max = 0
# Used to collect the bytes after the command byte
# (and the start/end sample number).
self.mb = []
self.mb_s = -1
self.mb_e = -1
def mosi_bytes(self):
'''Returns the collected MOSI bytes of a multi byte command.'''
return [b[0] for b in self.mb]
def miso_bytes(self):
'''Returns the collected MISO bytes of a multi byte command.'''
return [b[1] for b in self.mb]
def decode_command(self, pos, b):
'''Decodes the command byte 'b' at position 'pos' and prepares
the decoding of the following data bytes.'''
c = self.parse_command(b)
if c is None:
self.warn(pos, 'unknown command')
return
self.cmd, self.dat, self.min, self.max = c
if self.cmd in ('W_REGISTER', 'R_REGISTER'):
# Don't output anything now, the command is merged with
# the data bytes following it.
self.mb_s = pos[0]
else:
self.putp(pos, self.ann_cmd, self.format_command())
def format_command(self):
'''Returns the label for the current command.'''
return 'Cmd {}'.format(self.cmd)
def parse_command(self, b):
'''Parses the command byte.
Returns a tuple consisting of:
- the name of the command
- additional data needed to dissect the following bytes
- minimum number of following bytes
- maximum number of following bytes
'''
if b == 0x05:
return ('W_TX_FIFO', None, 1, 32)
elif b == 0x45:
return ('R_RX_FIFO', None, 1, 32)
if b == 0x06:
return ('W_ID', None, 1, 4)
elif b == 0x46:
return ('R_ID', None, 1, 4)
elif (b & 0b10000000) == 0:
if (b & 0b01000000) == 0:
c = 'W_REGISTER'
else:
c = 'R_REGISTER'
d = b & 0b00111111
return (c, d, 1, 1)
else:
cmd = b & 0b11110000
if cmd == 0b10000000:
return ('SLEEP_MODE', None, 0, 0)
if cmd == 0b10010000:
return ('IDLE_MODE', None, 0, 0)
if cmd == 0b10100000:
return ('STANDBY_MODE', None, 0, 0)
if cmd == 0b10110000:
return ('PLL_MODE', None, 0, 0)
if cmd == 0b11000000:
return ('RX_MODE', None, 0, 0)
if cmd == 0b11010000:
return ('TX_MODE', None, 0, 0)
if cmd == 0b11100000:
return ('FIFO_WRITE_PTR_RESET', None, 0, 0)
if cmd == 0b11110000:
return ('FIFO_READ_PTR_RESET', None, 0, 0)
def decode_register(self, pos, ann, regid, data):
'''Decodes a register.
pos -- start and end sample numbers of the register
ann -- is the annotation number that is used to output the register.
regid -- may be either an integer used as a key for the 'regs'
dictionary, or a string directly containing a register name.'
data -- is the register content.
'''
if type(regid) == int:
# Get the name of the register.
if regid not in regs:
self.warn(pos, 'unknown register')
return
name = regs[regid][0]
else:
name = regid
# Multi byte register come LSByte first.
data = reversed(data)
label = '{}: {}'.format(self.format_command(), name)
self.decode_mb_data(pos, ann, data, label, True)
def decode_mb_data(self, pos, ann, data, label, always_hex):
'''Decodes the data bytes 'data' of a multibyte command at position
'pos'. The decoded data is prefixed with 'label'. If 'always_hex' is
True, all bytes are decoded as hex codes, otherwise only non
printable characters are escaped.'''
if always_hex:
def escape(b):
return '{:02X}'.format(b)
else:
def escape(b):
c = chr(b)
if not str.isprintable(c):
return '\\x{:02X}'.format(b)
return c
data = ''.join([escape(b) for b in data])
text = '{} = "{}"'.format(label, data.strip())
self.putp(pos, ann, text)
def finish_command(self, pos):
'''Decodes the remaining data bytes at position 'pos'.'''
always_hex = self.options['hex_display'] == 'yes'
if self.cmd == 'R_REGISTER':
self.decode_register(pos, self.ann_cmd,
self.dat, self.miso_bytes())
elif self.cmd == 'W_REGISTER':
self.decode_register(pos, self.ann_cmd,
self.dat, self.mosi_bytes())
elif self.cmd == 'R_RX_FIFO':
self.decode_mb_data(pos, self.ann_rx,
self.miso_bytes(), 'RX FIFO', always_hex)
elif self.cmd == 'W_TX_FIFO':
self.decode_mb_data(pos, self.ann_tx,
self.mosi_bytes(), 'TX FIFO', always_hex)
elif self.cmd == 'R_ID':
self.decode_mb_data(pos, self.ann_rx,
self.miso_bytes(), 'R ID', always_hex)
elif self.cmd == 'W_ID':
self.decode_mb_data(pos, self.ann_tx,
self.mosi_bytes(), 'W ID', always_hex)
def decode(self, ss, es, data):
if not self.requirements_met:
return
ptype, data1, data2 = data
if ptype == 'TRANSFER':
if self.cmd:
# Check if we got the minimum number of data bytes
# after the command byte.
if len(self.mb) < self.min:
self.warn((ss, ss), 'missing data bytes')
elif self.mb:
self.finish_command((self.mb_s, self.mb_e))
self.next()
self.cs_was_released = True
elif ptype == 'CS-CHANGE':
if data1 is None:
if data2 is None:
self.requirements_met = False
raise ChannelError('CS# pin required.')
elif data2 == 1:
self.cs_was_released = True
if data1 == 0 and data2 == 1:
# Rising edge, the complete command is transmitted, process
# the bytes that were send after the command byte.
if self.cmd:
# Check if we got the minimum number of data bytes
# after the command byte.
if len(self.mb) < self.min:
self.warn((ss, ss), 'missing data bytes')
elif self.mb:
self.finish_command((self.mb_s, self.mb_e))
self.next()
self.cs_was_released = True
elif ptype == 'DATA' and self.cs_was_released:
mosi, miso = data1, data2
pos = (ss, es)
if miso is None and mosi is None:
self.requirements_met = False
raise ChannelError('Either MISO or MOSI pins required (3 wires SPI).')
if miso is None:
miso = mosi
if mosi is None:
mosi = miso
if self.first:
self.first = False
# First byte is always the command.
self.decode_command(pos, mosi)
else:
if not self.cmd or len(self.mb) >= self.max:
self.warn(pos, 'excess byte')
else:
# Collect the bytes after the command byte.
if self.mb_s == -1:
self.mb_s = ss
self.mb_e = es
self.mb.append((mosi, miso))

View File

@@ -0,0 +1,36 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Gerhard Sittig <gerhard.sittig@gmx.net>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
AC'97 (Audio Codec '97) was specifically designed by Intel for audio and
modem I/O functionality in mainstream PC systems. See the specification in
http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
AC'97 communicates full duplex data (SDATA_IN, SDATA_OUT), where bits
are clocked by the BIT_CLK and frames are signalled by the SYNC signals.
A low active RESET# line completes the set of signals.
Frames repeat at a nominal frequency of 48kHz, and consist of 256 bits
each. One 16bit slot contains management information, twelve 20bit slots
follow which carry data for three management and nine audio/modem channels.
Optionally two slots of one frame can get combined for higher resolution
on fewer channels, or double data rate.
'''
from .pd import Decoder

View File

@@ -0,0 +1,505 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Gerhard Sittig <gerhard.sittig@gmx.net>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# This implementation is incomplete. TODO items:
# - Support the optional RESET# pin, detect cold and warm reset.
# - Split slot values into audio samples of their respective width and
# frequency (either on user provided parameters, or from inspection of
# decoded register access).
import sigrokdecode as srd
class ChannelError(Exception):
pass
class Pins:
(SYNC, BIT_CLK, SDATA_OUT, SDATA_IN, RESET) = range(5)
class Ann:
(
BITS_OUT, BITS_IN,
SLOT_OUT_RAW, SLOT_OUT_TAG, SLOT_OUT_ADDR, SLOT_OUT_DATA,
SLOT_OUT_03, SLOT_OUT_04, SLOT_OUT_05, SLOT_OUT_06,
SLOT_OUT_07, SLOT_OUT_08, SLOT_OUT_09, SLOT_OUT_10,
SLOT_OUT_11, SLOT_OUT_IO,
SLOT_IN_RAW, SLOT_IN_TAG, SLOT_IN_ADDR, SLOT_IN_DATA,
SLOT_IN_03, SLOT_IN_04, SLOT_IN_05, SLOT_IN_06,
SLOT_IN_07, SLOT_IN_08, SLOT_IN_09, SLOT_IN_10,
SLOT_IN_11, SLOT_IN_IO,
WARN, ERROR,
) = range(32)
(
BIN_FRAME_OUT,
BIN_FRAME_IN,
BIN_SLOT_RAW_OUT,
BIN_SLOT_RAW_IN,
) = range(4)
class Decoder(srd.Decoder):
api_version = 3
id = 'ac97'
name = "AC '97"
longname = "Audio Codec '97"
desc = 'Audio and modem control for PC systems.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Audio', 'PC']
channels = (
{'id': 'sync', 'name': 'SYNC', 'desc': 'Frame synchronization'},
{'id': 'clk', 'name': 'BIT_CLK', 'desc': 'Data bits clock'},
)
optional_channels = (
{'id': 'out', 'name': 'SDATA_OUT', 'desc': 'Data output'},
{'id': 'in', 'name': 'SDATA_IN', 'desc': 'Data input'},
{'id': 'rst', 'name': 'RESET#', 'desc': 'Reset line'},
)
annotations = (
('bit-out', 'Output bits'),
('bit-in', 'Input bits'),
('slot-out-raw', 'Output raw value'),
('slot-out-tag', 'Output TAG'),
('slot-out-cmd-addr', 'Output command address'),
('slot-out-cmd-data', 'Output command data'),
('slot-out-03', 'Output slot 3'),
('slot-out-04', 'Output slot 4'),
('slot-out-05', 'Output slot 5'),
('slot-out-06', 'Output slot 6'),
('slot-out-07', 'Output slot 7'),
('slot-out-08', 'Output slot 8'),
('slot-out-09', 'Output slot 9'),
('slot-out-10', 'Output slot 10'),
('slot-out-11', 'Output slot 11'),
('slot-out-io-ctrl', 'Output I/O control'),
('slot-in-raw', 'Input raw value'),
('slot-in-tag', 'Input TAG'),
('slot-in-sts-addr', 'Input status address'),
('slot-in-sts-data', 'Input status data'),
('slot-in-03', 'Input slot 3'),
('slot-in-04', 'Input slot 4'),
('slot-in-05', 'Input slot 5'),
('slot-in-06', 'Input slot 6'),
('slot-in-07', 'Input slot 7'),
('slot-in-08', 'Input slot 8'),
('slot-in-09', 'Input slot 9'),
('slot-in-10', 'Input slot 10'),
('slot-in-11', 'Input slot 11'),
('slot-in-io-sts', 'Input I/O status'),
# TODO: Add more annotation classes:
# TAG: 'ready', 'valid', 'id', 'rsv'
# CMD ADDR: 'r/w', 'addr', 'unused'
# CMD DATA: 'data', 'unused'
# 3-11: 'data', 'unused', 'double data'
('warning', 'Warning'),
('error', 'Error'),
)
annotation_rows = (
('bits-out', 'Output bits', (Ann.BITS_OUT,)),
('slots-out-raw', 'Output numbers', (Ann.SLOT_OUT_RAW,)),
('slots-out', 'Output slots', (
Ann.SLOT_OUT_TAG, Ann.SLOT_OUT_ADDR, Ann.SLOT_OUT_DATA,
Ann.SLOT_OUT_03, Ann.SLOT_OUT_04, Ann.SLOT_OUT_05, Ann.SLOT_OUT_06,
Ann.SLOT_OUT_07, Ann.SLOT_OUT_08, Ann.SLOT_OUT_09, Ann.SLOT_OUT_10,
Ann.SLOT_OUT_11, Ann.SLOT_OUT_IO,)),
('bits-in', 'Input bits', (Ann.BITS_IN,)),
('slots-in-raw', 'Input numbers', (Ann.SLOT_IN_RAW,)),
('slots-in', 'Input slots', (
Ann.SLOT_IN_TAG, Ann.SLOT_IN_ADDR, Ann.SLOT_IN_DATA,
Ann.SLOT_IN_03, Ann.SLOT_IN_04, Ann.SLOT_IN_05, Ann.SLOT_IN_06,
Ann.SLOT_IN_07, Ann.SLOT_IN_08, Ann.SLOT_IN_09, Ann.SLOT_IN_10,
Ann.SLOT_IN_11, Ann.SLOT_IN_IO,)),
('warnings', 'Warnings', (Ann.WARN,)),
('errors', 'Errors', (Ann.ERROR,)),
)
binary = (
('frame-out', 'Frame bits, output data'),
('frame-in', 'Frame bits, input data'),
('slot-raw-out', 'Raw slot bits, output data'),
('slot-raw-in', 'Raw slot bits, input data'),
# TODO: Which (other) binary classes to implement?
# - Are binary annotations per audio slot useful?
# - Assume 20bit per slot, in 24bit units? Or assume 16bit
# audio samples? Observe register access and derive width
# of the audio data? Dump channels 3-11 or 1-12?
)
def putx(self, ss, es, cls, data):
self.put(ss, es, self.out_ann, [cls, data])
def putf(self, frombit, bitcount, cls, data):
ss = self.frame_ss_list[frombit]
es = self.frame_ss_list[frombit + bitcount]
self.putx(ss, es, cls, data)
def putb(self, frombit, bitcount, cls, data):
ss = self.frame_ss_list[frombit]
es = self.frame_ss_list[frombit + bitcount]
self.put(ss, es, self.out_binary, [cls, data])
def __init__(self):
self.out_binary = None
self.out_ann = None
self.reset()
def reset(self):
self.frame_ss_list = None
self.frame_slot_lens = [0, 16] + [16 + 20 * i for i in range(1, 13)]
self.frame_total_bits = self.frame_slot_lens[-1]
self.handle_slots = {
0: self.handle_slot_00,
1: self.handle_slot_01,
2: self.handle_slot_02,
}
def start(self):
if not self.out_binary:
self.out_binary = self.register(srd.OUTPUT_BINARY)
if not self.out_ann:
self.out_ann = self.register(srd.OUTPUT_ANN)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def bits_to_int(self, bits):
# Convert MSB-first bit sequence to integer value.
if not bits:
return 0
count = len(bits)
value = sum([2 ** (count - 1 - i) for i in range(count) if bits[i]])
return value
def bits_to_bin_ann(self, bits):
# Convert MSB-first bit sequence to binary annotation data.
# It's assumed that the number of bits does not (in useful ways)
# fit into an integer, and we need to create an array of bytes
# from the data afterwards, anyway. Hence the separate routine
# and the conversion of eight bits each.
out = []
count = len(bits)
while count > 0:
count -= 8
by, bits = bits[:8], bits[8:]
by = self.bits_to_int(by)
out.append(by)
out = bytes(out)
return out
def int_to_nibble_text(self, value, bitcount):
# Convert number to hex digits for given bit count.
digits = (bitcount + 3) // 4
text = '{{:0{:d}x}}'.format(digits).format(value)
return text
def get_bit_field(self, data, size, off, count):
shift = size - off - count
data >>= shift
mask = (1 << count) - 1
data &= mask
return data
def flush_frame_bits(self):
# Flush raw frame bits to binary annotation.
anncls = Ann.BIN_FRAME_OUT
data = self.frame_bits_out[:]
count = len(data)
data = self.bits_to_bin_ann(data)
self.putb(0, count, anncls, data)
anncls = Ann.BIN_FRAME_IN
data = self.frame_bits_in[:]
count = len(data)
data = self.bits_to_bin_ann(data)
self.putb(0, count, anncls, data)
def start_frame(self, ss):
# Mark the start of a frame.
if self.frame_ss_list:
# Flush bits if we had a frame before the frame which is
# starting here.
self.flush_frame_bits()
self.frame_ss_list = [ss]
self.frame_bits_out = []
self.frame_bits_in = []
self.frame_slot_data_out = []
self.frame_slot_data_in = []
self.have_slots = {True: None, False: None}
def handle_slot_dummy(self, slotidx, bitidx, bitcount, is_out, data):
# Handle slot x, default/fallback handler.
# Only process data of slots 1-12 when slot 0 says "valid".
if not self.have_slots[is_out]:
return
if not self.have_slots[is_out][slotidx]:
return
# Emit a naive annotation with just the data bits that we saw
# for the slot (hex nibbles for density). For audio data this
# can be good enough. Slots with special meaning should not end
# up calling the dummy handler.
text = self.int_to_nibble_text(data, bitcount)
anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
self.putf(bitidx, bitcount, anncls + slotidx, [text])
# Emit binary output for the data that is contained in slots
# which end up calling the default handler. This transparently
# should translate to "the slots with audio data", as other
# slots which contain management data should have their specific
# handler routines. In the present form, this approach might be
# good enough to get a (header-less) audio stream for typical
# setups where only line-in or line-out are in use.
#
# TODO: Improve this early prototype implementation. For now the
# decoder just exports the upper 16 bits of each audio channel
# that happens to be valid. For an improved implementation, it
# either takes user provided specs or more smarts like observing
# register access (if the capture includes it).
anncls = Ann.BIN_SLOT_RAW_OUT if is_out else Ann.BIN_SLOT_RAW_IN
data_bin = data >> 4
data_bin &= 0xffff
data_bin = data_bin.to_bytes(2, byteorder = 'big')
self.putb(bitidx, bitcount, anncls, data_bin)
def handle_slot_00(self, slotidx, bitidx, bitcount, is_out, data):
# Handle slot 0, TAG.
slotpos = self.frame_slot_lens[slotidx]
fieldoff = 0
anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
fieldlen = 1
ready = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
text = ['READY: 1', 'READY', 'RDY', 'R'] if ready else ['ready: 0', 'rdy', '-']
self.putf(slotpos + fieldoff, fieldlen, anncls, text)
fieldoff += fieldlen
fieldlen = 12
valid = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
text = ['VALID: {:3x}'.format(valid), '{:3x}'.format(valid)]
self.putf(slotpos + fieldoff, fieldlen, anncls, text)
have_slots = [True] + [False] * 12
for idx in range(12):
have_slots[idx + 1] = bool(valid & (1 << (11 - idx)))
self.have_slots[is_out] = have_slots
fieldoff += fieldlen
fieldlen = 1
rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
if rsv != 0:
text = ['reserved bit error', 'rsv error', 'rsv']
self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
fieldoff += fieldlen
# TODO: Will input slot 0 have a Codec ID, or 3 reserved bits?
fieldlen = 2
codec = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
text = ['CODEC: {:1x}'.format(codec), '{:1x}'.format(codec)]
self.putf(slotpos + fieldoff, fieldlen, anncls, text)
fieldoff += fieldlen
def handle_slot_01(self, slotidx, bitidx, bitcount, is_out, data):
# Handle slot 1, command/status address.
slotpos = self.frame_slot_lens[slotidx]
if not self.have_slots[is_out]:
return
if not self.have_slots[is_out][slotidx]:
return
fieldoff = 0
anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
anncls += slotidx
fieldlen = 1
if is_out:
is_read = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
text = ['READ', 'RD', 'R'] if is_read else ['WRITE', 'WR', 'W']
self.putf(slotpos + fieldoff, fieldlen, anncls, text)
# TODO: Check for the "atomic" constraint? Some operations
# involve address _and_ data, which cannot be spread across
# several frames. Slot 0 and 1 _must_ be provided within the
# same frame (the test should occur in the handler for slot
# 2 of course, in slot 1 we don't know what will follow).
else:
rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
if rsv != 0:
text = ['reserved bit error', 'rsv error', 'rsv']
self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
fieldoff += fieldlen
fieldlen = 7
regaddr = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
# TODO: Present 0-63 or 0-126 as the address of the 16bit register?
text = ['ADDR: {:2x}'.format(regaddr), '{:2x}'.format(regaddr)]
self.putf(slotpos + fieldoff, fieldlen, anncls, text)
if regaddr & 0x01:
text = ['odd register address', 'odd reg addr', 'odd addr', 'odd']
self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
fieldoff += fieldlen
# Strictly speaking there are 10 data request bits and 2 reserved
# bits for input slots, and 12 reserved bits for output slots. We
# test for 10 and 2 bits, to simplify the logic. Only in case of
# non-zero reserved bits for outputs this will result in "a little
# strange" an annotation. This is a cosmetic issue, we don't mind.
fieldlen = 10
reqdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
if is_out and reqdata != 0:
text = ['reserved bit error', 'rsv error', 'rsv']
self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
if not is_out:
text = ['REQ: {:3x}'.format(reqdata), '{:3x}'.format(reqdata)]
self.putf(slotpos + fieldoff, fieldlen, anncls, text)
fieldoff += fieldlen
fieldlen = 2
rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
if rsv != 0:
text = ['reserved bit error', 'rsv error', 'rsv']
self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
fieldoff += fieldlen
def handle_slot_02(self, slotidx, bitidx, bitcount, is_out, data):
# Handle slot 2, command/status data.
slotpos = self.frame_slot_lens[slotidx]
if not self.have_slots[is_out]:
return
if not self.have_slots[is_out][slotidx]:
return
fieldoff = 0
anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
anncls += slotidx
fieldlen = 16
rwdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
# TODO: Check for zero output data when the operation is a read.
# TODO: Check for the "atomic" constraint.
text = ['DATA: {:4x}'.format(rwdata), '{:4x}'.format(rwdata)]
self.putf(slotpos + fieldoff, fieldlen, anncls, text)
fieldoff += fieldlen
fieldlen = 4
rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
if rsv != 0:
text = ['reserved bits error', 'rsv error', 'rsv']
self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
fieldoff += fieldlen
# TODO: Implement other slots.
# - 1: cmd/status addr (check status vs command)
# - 2: cmd/status data (check status vs command)
# - 3-11: audio out/in
# - 12: io control/status (modem GPIO(?))
def handle_slot(self, slotidx, data_out, data_in):
# Process a received slot of a frame.
func = self.handle_slots.get(slotidx, self.handle_slot_dummy)
bitidx = self.frame_slot_lens[slotidx]
bitcount = self.frame_slot_lens[slotidx + 1] - bitidx
if data_out is not None:
func(slotidx, bitidx, bitcount, True, data_out)
if data_in is not None:
func(slotidx, bitidx, bitcount, False, data_in)
def handle_bits(self, ss, es, bit_out, bit_in):
# Process a received pair of bits.
# Emit the bits' annotations. Only interpret the data when we
# are in a frame (have seen the start of the frame, and don't
# exceed the expected number of bits in a frame).
if bit_out is not None:
self.putx(ss, es, Ann.BITS_OUT, ['{:d}'.format(bit_out)])
if bit_in is not None:
self.putx(ss, es, Ann.BITS_IN, ['{:d}'.format(bit_in)])
if self.frame_ss_list is None:
return
self.frame_ss_list.append(es)
have_len = len(self.frame_ss_list) - 1
if have_len > self.frame_total_bits:
return
# Accumulate the bits within the frame, until one slot of the
# frame has become available.
slot_idx = 0
if bit_out is not None:
self.frame_bits_out.append(bit_out)
slot_idx = len(self.frame_slot_data_out)
if bit_in is not None:
self.frame_bits_in.append(bit_in)
slot_idx = len(self.frame_slot_data_in)
want_len = self.frame_slot_lens[slot_idx + 1]
if have_len != want_len:
return
prev_len = self.frame_slot_lens[slot_idx]
# Convert bits to integer values. This shall simplify extraction
# of bit fields in multiple other locations.
slot_data_out = None
if bit_out is not None:
slot_bits = self.frame_bits_out[prev_len:]
slot_data = self.bits_to_int(slot_bits)
self.frame_slot_data_out.append(slot_data)
slot_data_out = slot_data
slot_data_in = None
if bit_in is not None:
slot_bits = self.frame_bits_in[prev_len:]
slot_data = self.bits_to_int(slot_bits)
self.frame_slot_data_in.append(slot_data)
slot_data_in = slot_data
# Emit simple annotations for the integer values, until upper
# layer decode stages will be implemented.
slot_len = have_len - prev_len
slot_ss = self.frame_ss_list[prev_len]
slot_es = self.frame_ss_list[have_len]
if slot_data_out is not None:
slot_text = self.int_to_nibble_text(slot_data_out, slot_len)
self.putx(slot_ss, slot_es, Ann.SLOT_OUT_RAW, [slot_text])
if slot_data_in is not None:
slot_text = self.int_to_nibble_text(slot_data_in, slot_len)
self.putx(slot_ss, slot_es, Ann.SLOT_IN_RAW, [slot_text])
self.handle_slot(slot_idx, slot_data_out, slot_data_in)
def decode(self):
have_sdo = self.has_channel(Pins.SDATA_OUT)
have_sdi = self.has_channel(Pins.SDATA_IN)
if not have_sdo and not have_sdi:
raise ChannelError('Either SDATA_OUT or SDATA_IN (or both) are required.')
have_reset = self.has_channel(Pins.RESET)
# Data is sampled at falling CLK edges. Annotations need to span
# the period between rising edges. SYNC rises one cycle _before_
# the start of a frame. Grab the earliest SYNC sample we can get
# and advance to the start of a bit time. Then keep getting the
# samples and the end of all subsequent bit times.
prev_sync = [None, None, None]
(sync, bit_clk, sdata_out, sdata_in, reset) = self.wait({Pins.BIT_CLK: 'e'})
if bit_clk == 0:
prev_sync[-1] = sync
(sync, bit_clk, sdata_out, sdata_in, reset) = self.wait({Pins.BIT_CLK: 'r'})
bit_ss = self.samplenum
while True:
(sync, bit_clk, sdata_out, sdata_in, reset) = self.wait({Pins.BIT_CLK: 'f'})
prev_sync.pop(0)
prev_sync.append(sync)
self.wait({Pins.BIT_CLK: 'r'})
if prev_sync[0] == 0 and prev_sync[1] == 1:
self.start_frame(bit_ss)
self.handle_bits(bit_ss, self.samplenum,
sdata_out if have_sdo else None,
sdata_in if have_sdi else None)
bit_ss = self.samplenum

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Analog Devices Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'spi' PD and decodes the
Analog Devices AD5626 protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,62 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Analog Devices Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
class Decoder(srd.Decoder):
api_version = 3
id = 'ad5626'
name = 'AD5626'
longname = 'Analog Devices AD5626'
desc = 'Analog Devices AD5626 12-bit nanoDAC.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
tags = ['IC', 'Analog/digital']
annotations = (
('voltage', 'Voltage'),
)
def __init__(self,):
self.reset()
def reset(self):
self.data = 0
self.ss = 0
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def decode(self, ss, es, data):
ptype = data[0]
if ptype == 'CS-CHANGE':
cs_old, cs_new = data[1:]
if cs_old is not None and cs_old == 0 and cs_new == 1:
self.data >>= 1
self.data /= 1000
self.put(self.ss, es, self.out_ann, [0, ['%.3fV' % self.data]])
self.data = 0
elif cs_old is not None and cs_old == 1 and cs_new == 0:
self.ss = ss
elif ptype == 'BITS':
mosi = data[1]
for bit in reversed(mosi):
self.data = self.data | bit[0]
self.data <<= 1

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Analog Devices Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'spi' PD and decodes the
Analog Devices AD7910/AD7920 protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,137 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Analog Devices Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
modes = {
0: ['Normal Mode', 'Normal', 'Norm', 'N'],
1: ['Power Down Mode', 'Power Down', 'PD'],
2: ['Power Up Mode', 'Power Up', 'PU'],
}
input_voltage_format = ['%.6fV', '%.2fV']
validation = {
'invalid': ['Invalid data', 'Invalid', 'N/A'],
'incomplete': ['Incomplete conversion', 'Incomplete', 'I'],
'complete': ['Complete conversion', 'Complete', 'C'],
}
class Decoder(srd.Decoder):
api_version = 3
id = 'ad79x0'
name = 'AD79x0'
longname = 'Analog Devices AD79x0'
desc = 'Analog Devices AD7910/AD7920 12-bit ADC.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
tags = ['IC', 'Analog/digital']
annotations = (
('mode', 'Mode'),
('voltage', 'Voltage'),
('validation', 'Validation'),
)
annotation_rows = (
('modes', 'Modes', (0,)),
('voltages', 'Voltages', (1,)),
('data_validation', 'Data validation', (2,)),
)
options = (
{'id': 'vref', 'desc': 'Reference voltage (V)', 'default': 1.5},
)
def __init__(self,):
self.reset()
def reset(self):
self.samplerate = 0
self.samples_bit = -1
self.ss = -1
self.start_sample = 0
self.previous_state = 0
self.data = 0
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def put_validation(self, pos, msg):
self.put(pos[0], pos[1], self.out_ann, [2, validation[msg]])
def put_data(self, pos, input_voltage):
ann = []
for format in input_voltage_format:
ann.append(format % input_voltage)
self.put(pos[0], pos[1], self.out_ann, [1, ann])
def put_mode(self, pos, msg):
self.put(pos[0], pos[1], self.out_ann, [0, modes[msg]])
def decode(self, ss, es, data):
ptype = data[0]
if ptype == 'CS-CHANGE':
cs_old, cs_new = data[1:]
if cs_old is not None and cs_old == 0 and cs_new == 1:
if self.samples_bit == -1:
return
self.data >>= 1
nb_bits = (ss - self.ss) // self.samples_bit
if nb_bits >= 10:
if self.data == 0xFFF:
self.put_mode([self.start_sample, es], 2)
self.previous_state = 0
self.put_validation([self.start_sample, es], 'invalid')
else:
self.put_mode([self.start_sample, es], 0)
if nb_bits == 16:
self.put_validation([self.start_sample, es], 'complete')
elif nb_bits < 16:
self.put_validation([self.start_sample, es], 'incomplete')
vin = (self.data / ((2**12) - 1)) * self.options['vref']
self.put_data([self.start_sample, es], vin)
elif nb_bits < 10:
self.put_mode([self.start_sample, es], 1)
self.previous_state = 1
self.put_validation([self.start_sample, es], 'invalid')
self.ss = -1
self.samples_bit = -1
self.data = 0
elif cs_old is not None and cs_old == 1 and cs_new == 0:
self.start_sample = ss
self.samples_bit = -1
elif ptype == 'BITS':
if data[2] is None:
return
miso = data[2]
if self.samples_bit == -1:
self.samples_bit = miso[0][2] - miso[0][1]
if self.ss == -1:
self.ss = ss
for bit in reversed(miso):
self.data = self.data | bit[0]
self.data <<= 1

View File

@@ -0,0 +1,32 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Karl Palsson <karlp@etactica.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
'''
This decoder stacks on top of the 'spi' PD and decodes Analog Devices
ADE77xx command/responses.
The ADE77xx is a "Poly Phase Multifunction Energy Metering IC with Per Phase
Information".
This PD has been tested with an ADE7758 so far, support for other devices
from the ADE77xx series can be added in the future.
'''
from .pd import Decoder

View File

@@ -0,0 +1,102 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Karl Palsson <karlp@etactica.com>
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all
## copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
from collections import OrderedDict
# Generated from datasheet rev E, using tabula.
regs = OrderedDict([
(0x1, ('AWATTHR', 'Watt-Hour Accumulation Register for Phase A. Active power is accumulated over time in this read-only register. The AWATTHR register can hold a maximum of 0.52 seconds of active energy information with full-scale analog inputs before it overflows (see the Active Energy Calculation section). Bit 0 and Bit 1 of the COMPMODE register determine how the active energy is processed from the six analog inputs.', 'R', 16, 'S', 0x0)),
(0x2, ('BWATTHR', 'Watt-Hour Accumulation Register for Phase B.', 'R', 16, 'S', 0x0)),
(0x3, ('CWATTHR', 'Watt-Hour Accumulation Register for Phase C.', 'R', 16, 'S', 0x0)),
(0x4, ('AVARHR', 'VAR-Hour Accumulation Register for Phase A. Reactive power is accumulated over time in this read-only register. The AVARHR register can hold a maximum of 0.52 seconds of reactive energy information with full-scale analog inputs before it overflows (see the Reactive Energy Calculation section). Bit 0 and Bit 1 of the COMPMODE register determine how the reactive energy is processed from the six analog inputs.', 'R', 16, 'S', 0x0)),
(0x5, ('BVARHR', 'VAR-Hour Accumulation Register for Phase B.', 'R', 16, 'S', 0x0)),
(0x6, ('CVARHR', 'VAR-Hour Accumulation Register for Phase C.', 'R', 16, 'S', 0x0)),
(0x7, ('AVAHR', 'VA-Hour Accumulation Register for Phase A. Apparent power is accumulated over time in this read-only register. The AVAHR register can hold a maximum of 1.15 seconds of apparent energy information with full-scale analog inputs before it overflows (see the Apparent Energy Calculation section). Bit 0 and Bit 1 of the COMPMODE register determine how the apparent energy is processed from the six analog inputs.', 'R', 16, 'S', 0x0)),
(0x8, ('BVAHR', 'VA-Hour Accumulation Register for Phase B.', 'R', 16, 'S', 0x0)),
(0x9, ('CVAHR', 'VA-Hour Accumulation Register for Phase C.', 'R', 16, 'S', 0x0)),
(0xa, ('AIRMS', 'Phase A Current Channel RMS Register. The register contains the rms component of the Phase A input of the current channel. The source is selected by data bits in the mode register.', 'R', 24, 'S', 0x0)),
(0xb, ('BIRMS', 'Phase B Current Channel RMS Register.', 'R', 24, 'S', 0x0)),
(0xc, ('CIRMS', 'Phase C Current Channel RMS Register.', 'R', 24, 'S', 0x0)),
(0xd, ('AVRMS', 'Phase A Voltage Channel RMS Register.', 'R', 24, 'S', 0x0)),
(0xe, ('BVRMS', 'Phase B Voltage Channel RMS Register.', 'R', 24, 'S', 0x0)),
(0xf, ('CVRMS', 'Phase C Voltage Channel RMS Register.', 'R', 24, 'S', 0x0)),
(0x10, ('FREQ', 'Frequency of the Line Input Estimated by the Zero-Crossing Processing. It can also display the period of the line input. Bit 7 of the LCYCMODE register determines if the reading is frequency or period. Default is frequency. Data Bit 0 and Bit 1 of the MMODE register determine the voltage channel used for the frequency or period calculation.', 'R', 12, 'U', 0x0)),
(0x11, ('TEMP', 'Temperature Register. This register contains the result of the latest temperature conversion. Refer to the Temperature Measurement section for details on how to interpret the content of this register.', 'R', 8, 'S', 0x0)),
(0x12, ('WFORM', 'Waveform Register. This register contains the digitized waveform of one of the six analog inputs or the digitized power waveform. The source is selected by Data Bit 0 to Bit 4 in the WAVMODE register.', 'R', 24, 'S', 0x0)),
(0x13, ('OPMODE', 'Operational Mode Register. This register defines the general configuration of the ADE7758 (see Table 18).', 'R/W', 8, 'U', 0x4)),
(0x14, ('MMODE', 'Measurement Mode Register. This register defines the channel used for period and peak detection measurements (see Table 19).', 'R/W', 8, 'U', 0xfc)),
(0x15, ('WAVMODE', 'Waveform Mode Register. This register defines the channel and sampling frequency used in the waveform sampling mode (see Table 20).', 'R/W', 8, 'U', 0x0)),
(0x16, ('COMPMODE', 'Computation Mode Register. This register configures the formula applied for the energy and line active energy measurements (see Table 22).', 'R/W', 8, 'U', 0x1c)),
(0x17, ('LCYCMODE', 'Line Cycle Mode Register. This register configures the line cycle accumulation mode for WATT-HR', 'R/W', 8, 'U', 0x78)),
(0x18, ('Mask', 'IRQ Mask Register. It determines if an interrupt event generates an active-low output at the IRQ pin (see the Interrupts section).', 'R/W', 24, 'U', 0x0)),
(0x19, ('Status', 'IRQ Status Register. This register contains information regarding the source of the ADE7758 interrupts (see the Interrupts section).', 'R', 24, 'U', 0x0)),
(0x1a, ('RSTATUS', 'IRQ Reset Status Register. Same as the STATUS register, except that its contents are reset to 0 (all flags cleared) after a read operation.', 'R', 24, 'U', 0x0)),
(0x1b, ('ZXTOUT', 'Zero-Cross Timeout Register. If no zero crossing is detected within the time period specified by this register', 'R/W', 16, 'U', 0xffff)),
(0x1c, ('LINECYC', 'Line Cycle Register. The content of this register sets the number of half-line cycles that the active', 'R/W', 16, 'U', 0xffff)),
(0x1d, ('SAGCYC', 'SAG Line Cycle Register. This register specifies the number of consecutive half-line cycles where voltage channel input may fall below a threshold level. This register is common to the three line voltage SAG detection. The detection threshold is specified by the SAGLVL register (see the Line Voltage SAG Detection section).', 'R/W', 8, 'U', 0xff)),
(0x1e, ('SAGLVL', 'SAG Voltage Level. This register specifies the detection threshold for the SAG event. This register is common to all three phases line voltage SAG detections. See the description of the SAGCYC register for details.', 'R/W', 8, 'U', 0x0)),
(0x1f, ('VPINTLVL', 'Voltage Peak Level Interrupt Threshold Register. This register sets the level of the voltage peak detection. Bit 5 to Bit 7 of the MMODE register determine which phases are to be monitored. If the selected voltage phase exceeds this level', 'R/W', 8, 'U', 0xff)),
(0x20, ('IPINTLVL', 'Current Peak Level Interrupt Threshold Register. This register sets the level of the current peak detection. Bit 5 to Bit 7 of the MMODE register determine which phases are to be monitored. If the selected current phase exceeds this level', 'R/W', 8, 'U', 0xff)),
(0x21, ('VPEAK', 'Voltage Peak Register. This register contains the value of the peak voltage waveform that has occurred within a fixed number of half-line cycles. The number of half-line cycles is set by the LINECYC register.', 'R', 8, 'U', 0x0)),
(0x22, ('IPEAK', 'Current Peak Register. This register holds the value of the peak current waveform that has occurred within a fixed number of half-line cycles. The number of half-line cycles is set by the LINECYC register.', 'R', 8, 'U', 0x0)),
(0x23, ('Gain', 'PGA Gain Register. This register is used to adjust the gain selection for the PGA in the current and voltage channels (see the Analog Inputs section).', 'R/W', 8, 'U', 0x0)),
(0x24, ('AVRMSGAIN', 'Phase A VRMS Gain Register. The range of the voltage rms calculation can be adjusted by writing to this register. It has an adjustment range of ±50% with a resolution of 0.0244%/LSB.', 'R/W', 12, 'S', 0x0)),
(0x25, ('BVRMSGAIN', 'Phase B VRMS Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x26, ('CVRMSGAIN', 'Phase C VRMS Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x27, ('AIGAIN', 'Phase A Current Gain Register. This register is not recommended to be used and it should be kept at 0', 'R/W', 12, 'S', 0x0)),
(0x28, ('BIGAIN', 'Phase B Current Gain Register. This register is not recommended to be used and it should be kept at 0', 'R/W', 12, 'S', 0x0)),
(0x29, ('CIGAIN', 'Phase C Current Gain Register. This register is not recommended to be used and it should be kept at 0', 'R/W', 12, 'S', 0x0)),
(0x2a, ('AWG', 'Phase A Watt Gain Register. The range of the watt calculation can be adjusted by writing to this register. It has an adjustment range of ±50% with a resolution of 0.0244%/LSB.', 'R/W', 12, 'S', 0x0)),
(0x2b, ('BWG', 'Phase B Watt Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x2c, ('CWG', 'Phase C Watt Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x2d, ('AVARG', 'Phase A VAR Gain Register. The range of the VAR calculation can be adjusted by writing to this register. It has an adjustment range of ±50% with a resolution of 0.0244%/LSB.', 'R/W', 12, 'S', 0x0)),
(0x2e, ('BVARG', 'Phase B VAR Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x2f, ('CVARG', 'Phase C VAR Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x30, ('AVAG', 'Phase A VA Gain Register. The range of the VA calculation can be adjusted by writing to this register. It has an adjustment range of ±50% with a resolution of 0.0244%/LSB.', 'R/W', 12, 'S', 0x0)),
(0x31, ('BVAG', 'Phase B VA Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x32, ('CVAG', 'Phase C VA Gain Register.', 'R/W', 12, 'S', 0x0)),
(0x33, ('AVRMSOS', 'Phase A Voltage RMS Offset Correction Register.', 'R/W', 12, 'S', 0x0)),
(0x34, ('BVRMSOS', 'Phase B Voltage RMS Offset Correction Register.', 'R/W', 12, 'S', 0x0)),
(0x35, ('CVRMSOS', 'Phase C Voltage RMS Offset Correction Register.', 'R/W', 12, 'S', 0x0)),
(0x36, ('AIRMSOS', 'Phase A Current RMS Offset Correction Register.', 'R/W', 12, 'S', 0x0)),
(0x37, ('BIRMSOS', 'Phase B Current RMS Offset Correction Register.', 'R/W', 12, 'S', 0x0)),
(0x38, ('CIRMSOS', 'Phase C Current RMS Offset Correction Register.', 'R/W', 12, 'S', 0x0)),
(0x39, ('AWATTOS', 'Phase A Watt Offset Calibration Register.', 'R/W', 12, 'S', 0x0)),
(0x3a, ('BWATTOS', 'Phase B Watt Offset Calibration Register.', 'R/W', 12, 'S', 0x0)),
(0x3b, ('CWATTOS', 'Phase C Watt Offset Calibration Register.', 'R/W', 12, 'S', 0x0)),
(0x3c, ('AVAROS', 'Phase A VAR Offset Calibration Register.', 'R/W', 12, 'S', 0x0)),
(0x3d, ('BVAROS', 'Phase B VAR Offset Calibration Register.', 'R/W', 12, 'S', 0x0)),
(0x3e, ('CVAROS', 'Phase C VAR Offset Calibration Register.', 'R/W', 12, 'S', 0x0)),
(0x3f, ('APHCAL', 'Phase A Phase Calibration Register. The phase relationship between the current and voltage channel can be adjusted by writing to this signed 7-bit register (see the Phase Compensation section).', 'R/W', 7, 'S', 0x0)),
(0x40, ('BPHCAL', 'Phase B Phase Calibration Register.', 'R/W', 7, 'S', 0x0)),
(0x41, ('CPHCAL', 'Phase C Phase Calibration Register.', 'R/W', 7, 'S', 0x0)),
(0x42, ('WDIV', 'Active Energy Register Divider.', 'R/W', 8, 'U', 0x0)),
(0x43, ('VARDIV', 'Reactive Energy Register Divider.', 'R/W', 8, 'U', 0x0)),
(0x44, ('VADIV', 'Apparent Energy Register Divider.', 'R/W', 8, 'U', 0x0)),
(0x45, ('APCFNUM', 'Active Power CF Scaling Numerator Register. The content of this register is used in the numerator of the APCF output scaling calculation. Bits [15:13] indicate reverse polarity active power measurement for Phase A', 'R/W', 16, 'U', 0x0)),
(0x46, ('APCFDEN', 'Active Power CF Scaling Denominator Register. The content of this register is used in the denominator of the APCF output scaling.', 'R/W', 12, 'U', 0x3f)),
(0x47, ('VARCFNUM', 'Reactive Power CF Scaling Numerator Register. The content of this register is used in the numerator of the VARCF output scaling. Bits [15:13] indicate reverse polarity reactive power measurement for Phase A', 'R/W', 16, 'U', 0x0)),
(0x48, ('VARCFDEN', 'Reactive Power CF Scaling Denominator Register. The content of this register is used in the denominator of the VARCF output scaling.', 'R/W', 12, 'U', 0x3f)),
(0x7e, ('CHKSUM', 'Checksum Register. The content of this register represents the sum of all the ones in the last register read from the SPI port.', 'R', 8, 'U', None)),
(0x7f, ('Version', 'Version of the Die.', 'R', 8, 'U', None)),
])

View File

@@ -0,0 +1,131 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Karl Palsson <karlp@etactica.com>
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all
## copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
import math
import sigrokdecode as srd
from .lists import *
class Decoder(srd.Decoder):
api_version = 3
id = 'ade77xx'
name = 'ADE77xx'
longname = 'Analog Devices ADE77xx'
desc = 'Poly phase multifunction energy metering IC protocol.'
license = 'mit'
inputs = ['spi']
outputs = []
tags = ['Analog/digital', 'IC', 'Sensor']
annotations = (
('read', 'Register read commands'),
('write', 'Register write commands'),
('warning', 'Warnings'),
)
annotation_rows = (
('read', 'Read', (0,)),
('write', 'Write', (1,)),
('warnings', 'Warnings', (2,)),
)
def reset_data(self):
self.expected = 0
self.mosi_bytes, self.miso_bytes = [], []
def __init__(self):
self.reset()
def reset(self):
self.ss_cmd, self.es_cmd = 0, 0
self.reset_data()
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
def put_warn(self, pos, msg):
self.put(pos[0], pos[1], self.out_ann, [2, [msg]])
def decode(self, ss, es, data):
ptype = data[0]
if ptype == 'CS-CHANGE':
# Bear in mind, that CS is optional according to the datasheet.
# If we transition high mid-stream, toss out our data and restart.
cs_old, cs_new = data[1:]
if cs_old is not None and cs_old == 0 and cs_new == 1:
if len(self.mosi_bytes) > 0 and len(self.mosi_bytes[1:]) < self.expected:
# Mark short read/write for reg at least!
self.es_cmd = es
write, reg = self.cmd & 0x80, self.cmd & 0x7f
rblob = regs.get(reg)
idx = 1 if write else 0
self.putx([idx, ['%s: %s' % (rblob[0], "SHORT")]])
self.put_warn([self.ss_cmd, es], "Short transfer!")
self.reset_data()
return
# Don't care about anything else.
if ptype != 'DATA':
return
mosi, miso = data[1:]
if len(self.mosi_bytes) == 0:
self.ss_cmd = ss
self.mosi_bytes.append(mosi)
self.miso_bytes.append(miso)
# A transfer is 2-4 bytes, (command + 1..3 byte reg).
if len(self.mosi_bytes) < 2:
return
self.cmd = self.mosi_bytes[0]
write, reg = self.cmd & 0x80, self.cmd & 0x7f
rblob = regs.get(reg)
if not rblob:
# If you don't have CS, this will _destroy_ comms!
self.put_warn([self.ss_cmd, es], 'Unknown register!')
return
self.expected = math.ceil(rblob[3] / 8)
if len(self.mosi_bytes[1:]) != self.expected:
return
valo, vali = None, None
self.es_cmd = es
if self.expected == 3:
valo = self.mosi_bytes[1] << 16 | self.mosi_bytes[2] << 8 | \
self.mosi_bytes[3]
vali = self.miso_bytes[1] << 16 | self.miso_bytes[2] << 8 | \
self.miso_bytes[3]
elif self.expected == 2:
valo = self.mosi_bytes[1] << 8 | self.mosi_bytes[2]
vali = self.miso_bytes[1] << 8 | self.miso_bytes[2]
elif self.expected == 1:
valo = self.mosi_bytes[1]
vali = self.miso_bytes[1]
if write:
self.putx([1, ['%s: %#x' % (rblob[0], valo)]])
else:
self.putx([0, ['%s: %#x' % (rblob[0], vali)]])
self.reset_data()

View File

@@ -0,0 +1,29 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Joel Holdsworth <joel@airwebreathe.org.uk>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
by the Analog Devices ADF4350 and ADF4351 RF synthesizer chips.
Details:
http://www.analog.com/media/en/technical-documentation/data-sheets/ADF4350.pdf
http://www.analog.com/media/en/technical-documentation/data-sheets/ADF4351.pdf
'''
from .pd import Decoder

View File

@@ -0,0 +1,144 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Joel Holdsworth <joel@airwebreathe.org.uk>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
def disabled_enabled(v):
return ['Disabled', 'Enabled'][v]
def output_power(v):
return '%+ddBm' % [-4, -1, 2, 5][v]
regs = {
# reg: name offset width parser
0: [
('FRAC', 3, 12, None),
('INT', 15, 16, lambda v: 'Not Allowed' if v < 32 else v)
],
1: [
('MOD', 3, 12, None),
('Phase', 15, 12, None),
('Prescalar', 27, 1, lambda v: ['4/5', '8/9'][v]),
('Phase Adjust', 28, 1, lambda v: ['Off', 'On'][v]),
],
2: [
('Counter Reset', 3, 1, disabled_enabled),
('Charge Pump Three-State', 4, 1, disabled_enabled),
('Power-Down', 5, 1, disabled_enabled),
('PD Polarity', 6, 1, lambda v: ['Negative', 'Positive'][v]),
('LDP', 7, 1, lambda v: ['10ns', '6ns'][v]),
('LDF', 8, 1, lambda v: ['FRAC-N', 'INT-N'][v]),
('Charge Pump Current Setting', 9, 4, lambda v: '%0.2fmA @ 5.1kΩ' %
[0.31, 0.63, 0.94, 1.25, 1.56, 1.88, 2.19, 2.50,
2.81, 3.13, 3.44, 3.75, 4.06, 4.38, 4.69, 5.00][v]),
('Double Buffer', 13, 1, disabled_enabled),
('R Counter', 14, 10, None),
('RDIV2', 24, 1, disabled_enabled),
('Reference Doubler', 25, 1, disabled_enabled),
('MUXOUT', 26, 3, lambda v:
['Three-State Output', 'DVdd', 'DGND', 'R Counter Output', 'N Divider Output',
'Analog Lock Detect', 'Digital Lock Detect', 'Reserved'][v]),
('Low Noise and Low Spur Modes', 29, 2, lambda v:
['Low Noise Mode', 'Reserved', 'Reserved', 'Low Spur Mode'][v])
],
3: [
('Clock Divider', 3, 12, None),
('Clock Divider Mode', 15, 2, lambda v:
['Clock Divider Off', 'Fast Lock Enable', 'Resync Enable', 'Reserved'][v]),
('CSR Enable', 18, 1, disabled_enabled),
('Charge Cancellation', 21, 1, disabled_enabled),
('ABP', 22, 1, lambda v: ['6ns (FRAC-N)', '3ns (INT-N)'][v]),
('Band Select Clock Mode', 23, 1, lambda v: ['Low', 'High'][v])
],
4: [
('Output Power', 3, 2, output_power),
('Output Enable', 5, 1, disabled_enabled),
('AUX Output Power', 6, 2, output_power),
('AUX Output Select', 8, 1, lambda v: ['Divided Output', 'Fundamental'][v]),
('AUX Output Enable', 9, 1, disabled_enabled),
('MTLD', 10, 1, disabled_enabled),
('VCO Power-Down', 11, 1, lambda v:
'VCO Powered ' + ('Down' if v == 1 else 'Up')),
('Band Select Clock Divider', 12, 8, None),
('RF Divider Select', 20, 3, lambda v: '÷' + str(2**v)),
('Feedback Select', 23, 1, lambda v: ['Divided', 'Fundamental'][v]),
],
5: [
('LD Pin Mode', 22, 2, lambda v:
['Low', 'Digital Lock Detect', 'Low', 'High'][v])
]
}
ANN_REG = 0
class Decoder(srd.Decoder):
api_version = 3
id = 'adf435x'
name = 'ADF435x'
longname = 'Analog Devices ADF4350/1'
desc = 'Wideband synthesizer with integrated VCO.'
license = 'gplv3+'
inputs = ['spi']
outputs = []
tags = ['Clock/timing', 'IC', 'Wireless/RF']
annotations = (
# Sent from the host to the chip.
('register', 'Register written to the device'),
)
annotation_rows = (
('registers', 'Register writes', (ANN_REG,)),
)
def __init__(self):
self.reset()
def reset(self):
self.bits = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def decode_bits(self, offset, width):
return (sum([(1 << i) if self.bits[offset + i][0] else 0 for i in range(width)]),
(self.bits[offset + width - 1][1], self.bits[offset][2]))
def decode_field(self, name, offset, width, parser):
val, pos = self.decode_bits(offset, width)
self.put(pos[0], pos[1], self.out_ann, [ANN_REG,
['%s: %s' % (name, parser(val) if parser else str(val))]])
return val
def decode(self, ss, es, data):
ptype, data1, data2 = data
if ptype == 'CS-CHANGE':
if data1 == 1:
if len(self.bits) == 32:
reg_value, reg_pos = self.decode_bits(0, 3)
self.put(reg_pos[0], reg_pos[1], self.out_ann, [ANN_REG,
['Register: %d' % reg_value, 'Reg: %d' % reg_value,
'[%d]' % reg_value]])
if reg_value < len(regs):
field_descs = regs[reg_value]
for field_desc in field_descs:
field = self.decode_field(*field_desc)
self.bits = []
if ptype == 'BITS':
self.bits = data1 + self.bits

View File

@@ -0,0 +1,27 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Karl Palsson <karlp@tweak.net.au>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'spi' PD and decodes ADNS-5020 optical mouse
sensor commands and data.
Use MOSI for the SDIO shared line.
'''
from .pd import Decoder

View File

@@ -0,0 +1,116 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Karl Palsson <karlp@tweak.net.au>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
regs = {
0: 'Product_ID',
1: 'Revision_ID',
2: 'Motion',
3: 'Delta_X',
4: 'Delta_Y',
5: 'SQUAL',
6: 'Shutter_Upper',
7: 'Shutter_Lower',
8: 'Maximum_Pixel',
9: 'Pixel_Sum',
0xa: 'Minimum_Pixel',
0xb: 'Pixel_Grab',
0xd: 'Mouse_Control',
0x3a: 'Chip_Reset',
0x3f: 'Inv_Rev_ID',
0x63: 'Motion_Burst',
}
class Decoder(srd.Decoder):
api_version = 3
id = 'adns5020'
name = 'ADNS-5020'
longname = 'Avago ADNS-5020'
desc = 'Bidirectional optical mouse sensor protocol.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
tags = ['IC', 'PC', 'Sensor']
annotations = (
('read', 'Register read commands'),
('write', 'Register write commands'),
('warning', 'Warnings'),
)
annotation_rows = (
('read', 'Read', (0,)),
('write', 'Write', (1,)),
('warnings', 'Warnings', (2,)),
)
def __init__(self):
self.reset()
def reset(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
def put_warn(self, pos, msg):
self.put(pos[0], pos[1], self.out_ann, [2, [msg]])
def decode(self, ss, es, data):
ptype = data[0]
if ptype == 'CS-CHANGE':
# If we transition high mid-stream, toss out our data and restart.
cs_old, cs_new = data[1:]
if cs_old is not None and cs_old == 0 and cs_new == 1:
if len(self.mosi_bytes) not in [0, 2]:
self.put_warn([self.ss_cmd, es], 'Misplaced CS#!')
self.mosi_bytes = []
return
# Don't care about anything else.
if ptype != 'DATA':
return
mosi, miso = data[1:]
self.ss, self.es = ss, es
if len(self.mosi_bytes) == 0:
self.ss_cmd = ss
self.mosi_bytes.append(mosi)
# Writes/reads are mostly two transfers (burst mode is different).
if len(self.mosi_bytes) != 2:
return
self.es_cmd = es
cmd, arg = self.mosi_bytes
write = cmd & 0x80
reg = cmd & 0x7f
reg_desc = regs.get(reg, 'Reserved %#x' % reg)
if reg > 0x63:
reg_desc = 'Unknown'
if write:
self.putx([1, ['%s: %#x' % (reg_desc, arg)]])
else:
self.putx([0, ['%s: %d' % (reg_desc, arg)]])
self.mosi_bytes = []

View File

@@ -0,0 +1,26 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Analog Devices Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
'''
This decoder stacks on top of the 'spi' PD and decodes the
Analog Devices ADXL345 protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,96 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Analog Devices Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
error_messages = {
'interrupt': ['Interrupt'],
'undesirable': ['Undesirable behavior'],
'dis_single': ['Disable single tap'],
'dis_double': ['Disable double tap'],
'dis_single_double': ['Disable single/double tap'],
}
rate_code = {
0x00: 0.1,
0x01: 0.2,
0x02: 0.39,
0x03: 0.78,
0x04: 1.56,
0x05: 3.13,
0x06: 6.25,
0x07: 12.5,
0x08: 25,
0x09: 50,
0x0A: 100,
0x0B: 200,
0x0C: 400,
0x0D: 800,
0x0E: 1600,
0x0F: 3200,
}
fifo_modes = {
0x00: 'Bypass',
0x01: 'FIFO',
0x02: 'Stream',
0x03: 'Trigger',
}
operations = {
0x00: ['WRITE REG', 'WRITE', 'W'],
0x01: ['READ REG', 'READ', 'R'],
}
number_bytes = {
0x00: ['SINGLE BYTE', 'SING BYTE', '1 BYTE', '1B'],
0x01: ['MULTIPLE BYTES', 'MULTI BYTES', 'n*BYTES', 'n*B'],
}
registers = {
0x00: ['DEVID', 'DID', 'ID'],
0x1D: ['THRESH_TAP', 'TH_TAP', 'TH_T'],
0x1E: ['OFSX', 'OFX'],
0x1F: ['OFSY', 'OFY'],
0x20: ['OFSZ', 'OFZ'],
0x21: ['DUR'],
0x22: ['Latent', 'Lat'],
0x23: ['Window', 'Win'],
0x24: ['THRESH_ACT', 'TH_ACT', 'TH_A'],
0x25: ['THRESH_INACT', 'TH_INACT', 'TH_I'],
0x26: ['TIME_INACT', 'TI_INACT', 'TI_I'],
0x27: ['ACT_INACT_CTL', 'ACT_I_CTL', 'A_I_C'],
0x28: ['THRESH_FF', 'TH_FF'],
0x29: ['TIME_FF', 'TI_FF'],
0x2A: ['TAP_AXES', 'TAP_AX', 'TP_AX'],
0x2B: ['ACT_TAP_STATUS', 'ACT_TAP_STAT', 'ACT_TP_ST', 'A_T_S'],
0x2C: ['BW_RATE', 'BW_R'],
0x2D: ['POWER_CTL', 'PW_CTL', 'PW_C'],
0x2E: ['INT_ENABLE', 'INT_EN', 'I_EN'],
0x2F: ['INT_MAP', 'I_M'],
0x30: ['INT_SOURCE', 'INT_SRC', 'I_SRC', 'I_S'],
0x31: ['DATA_FORMAT', 'DATA_FRM', 'D_FRM', 'D_F'],
0x32: ['DATAX0', 'DX0', 'X0'],
0x33: ['DATAX1', 'DX1', 'X1'],
0x34: ['DATAY0', 'DY0', 'Y0'],
0x35: ['DATAY1', 'DY1', 'Y1'],
0x36: ['DATAZ0', 'DZ0', 'Z0'],
0x37: ['DATAZ1', 'DZ1', 'Z1'],
0x38: ['FIFO_CTL', 'FIF_CT', 'F_C'],
0x39: ['FIFO_STATUS', 'FIFO_STAT', 'FIF_ST', 'F_S'],
}

View File

@@ -0,0 +1,453 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Analog Devices Inc.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from common.srdhelper import SrdIntEnum
from .lists import *
WORD_SIZE = 8
class Channel():
MISO, MOSI = range(2)
class Operation():
READ, WRITE = range(2)
class BitType():
ENABLE = {1: ['Enable %s', 'En %s', '%s '], 0: ['Disable %s', 'Dis %s', '!%s '],}
SOURCE = {1: ['Involve %s', 'Inv %s', '%s'], 0: ['Not involve %s', 'Not inv %s', '!%s'],}
INTERRUPT = {1: ['INT2 %s', 'I2: %s '], 0: ['INT1 %s', 'I1:%s '],}
AC_DC = {1: ['%s ac', 'ac'], 0: ['%s dc', 'dc'],}
UNUSED = {1: ['N/A'], 0: ['N/A'],}
OTHER = 0
class Bit():
def __init__(self, name, type, values=None):
self.value = 0
self.name = name
self.type = type
self.values = values
def set_value(self, value):
self.value = value
def get_bit_annotation(self):
if self.type == BitType.OTHER:
annotation = self.values[self.value].copy()
else:
annotation = self.type[self.value].copy()
for index in range(len(annotation)):
if '%s' in annotation[index]:
annotation[index] = str(annotation[index] % self.name)
return annotation
Ann = SrdIntEnum.from_str('Ann', 'READ WRITE MB REG_ADDRESS REG_DATA WARNING')
St = SrdIntEnum.from_str('St', 'IDLE ADDRESS_BYTE DATA')
class Decoder(srd.Decoder):
api_version = 3
id = 'adxl345'
name = 'ADXL345'
longname = 'Analog Devices ADXL345'
desc = 'Analog Devices ADXL345 3-axis accelerometer.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
tags = ['IC', 'Sensor']
annotations = (
('read', 'Read'),
('write', 'Write'),
('mb', 'Multiple bytes'),
('reg-address', 'Register address'),
('reg-data', 'Register data'),
('warning', 'Warning'),
)
annotation_rows = (
('reg', 'Registers', (Ann.READ, Ann.WRITE, Ann.MB, Ann.REG_ADDRESS)),
('data', 'Data', (Ann.REG_DATA, Ann.WARNING)),
)
def __init__(self):
self.reset()
def reset(self):
self.mosi, self.miso = [], []
self.reg = []
self.operation = None
self.address = 0
self.data = -1
self.state = St.IDLE
self.ss, self.es = -1, -1
self.samples_per_bit = 0
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def putb(self, data, index):
start = self.ss + (self.samples_per_bit * index)
self.put(start, start + self.samples_per_bit, self.out_ann, data)
def putbs(self, data, start_index, stop_index):
start_index = self.reverse_bit_index(start_index, WORD_SIZE)
stop_index = self.reverse_bit_index(stop_index, WORD_SIZE)
start = self.ss + (self.samples_per_bit * start_index)
stop = start + (self.samples_per_bit * (stop_index - start_index + 1))
self.put(start, stop, self.out_ann, data)
def handle_reg_with_scaling_factor(self, data, factor, name, unit, error_msg):
if data == 0 and error_msg is not None:
self.putx([Ann.WARNING, error_msg])
else:
result = (data * factor) / 1000
self.putx([Ann.REG_DATA, ['%s: %f %s' % (name, result, unit), '%f %s' % (result, unit)]])
def handle_reg_bit_msg(self, bit, index, en_msg, dis_msg):
self.putb([Ann.REG_DATA, [en_msg if bit else dis_msg]], index)
def interpret_bits(self, data, bits):
bits_values = []
for offset in range(8):
bits_values.insert(0, (data & (1 << offset)) >> offset)
for index in range(len(bits)):
if bits[index] is None:
continue
bit = bits[index]
bit.set_value(bits_values[index])
self.putb([Ann.REG_DATA, bit.get_bit_annotation()], index)
return list(reversed(bits_values))
def reverse_bit_index(self, index, word_size):
return word_size - index - 1
def get_decimal_number(self, bits, start_index, stop_index):
number = 0
interval = range(start_index, stop_index + 1, 1)
for index, offset in zip(interval, range(len(interval))):
bit = bits[index]
number = number | (bit << offset)
return number
def get_axis_value(self, data, axis):
if self.data != - 1:
data <<= 8
self.data |= data
self.put(self.start_index, self.es, self.out_ann,
[Ann.REG_DATA, ['%s: 0x%04X' % (axis, self.data), str(data)]])
self.data = -1
else:
self.putx([Ann.REG_DATA, [str(data)]])
def handle_reg_0x1d(self, data):
self.handle_reg_with_scaling_factor(data, 62.5, 'Threshold', 'g',
error_messages['undesirable'])
def handle_reg_0x1e(self, data):
self.handle_reg_with_scaling_factor(data, 15.6, 'OFSX', 'g', None)
def handle_reg_0x1f(self, data):
self.handle_reg_with_scaling_factor(data, 15.6, 'OFSY', 'g', None)
def handle_reg_0x20(self, data):
self.handle_reg_with_scaling_factor(data, 15.6, 'OFSZ', 'g', None)
def handle_reg_0x21(self, data):
self.handle_reg_with_scaling_factor(data, 0.625, 'Duration', 's',
error_messages['dis_single_double'])
def handle_reg_0x22(self, data):
self.handle_reg_with_scaling_factor(data, 1.25, 'Latency', 's',
error_messages['dis_double'])
def handle_reg_0x23(self, data):
self.handle_reg_with_scaling_factor(data, 1.25, 'Window', 's',
error_messages['dis_double'])
def handle_reg_0x24(self, data):
self.handle_reg_0x1d(data)
def handle_reg_0x25(self, data):
self.handle_reg_0x1d(data)
def handle_reg_0x26(self, data):
self.handle_reg_with_scaling_factor(data, 1000, 'Time', 's',
error_messages['interrupt'])
def handle_reg_0x27(self, data):
bits = [Bit('ACT', BitType.AC_DC),
Bit('ACT_X', BitType.ENABLE),
Bit('ACT_Y', BitType.ENABLE),
Bit('ACT_Z', BitType.ENABLE),
Bit('INACT', BitType.AC_DC),
Bit('INACT_X', BitType.ENABLE),
Bit('INACT_Y', BitType.ENABLE),
Bit('INACT_Z', BitType.ENABLE)]
self.interpret_bits(data, bits)
def handle_reg_0x28(self, data):
self.handle_reg_0x1d(data)
def handle_reg_0x29(self, data):
self.handle_reg_with_scaling_factor(data, 5, 'Time', 's',
error_messages['undesirable'])
def handle_reg_0x2a(self, data):
bits = [Bit('', BitType.UNUSED),
Bit('', BitType.UNUSED),
Bit('', BitType.UNUSED),
Bit('', BitType.UNUSED),
Bit('', BitType.OTHER, {1: ['Suppressed', 'Suppr', 'S'],
0: ['Unsuppressed', 'Unsuppr', 'Uns'],}),
Bit('TAP_X', BitType.ENABLE),
Bit('TAP_Y', BitType.ENABLE),
Bit('TAP_Z', BitType.ENABLE)]
self.interpret_bits(data, bits)
def handle_reg_0x2b(self, data):
bits = [Bit('', BitType.UNUSED),
Bit('ACT_X', BitType.SOURCE),
Bit('ACT_Y', BitType.SOURCE),
Bit('ACT_Z', BitType.SOURCE),
Bit('', BitType.OTHER, {1: ['Asleep', 'Asl'],
0: ['Not asleep', 'Not asl', '!Asl'],}),
Bit('TAP_X', BitType.SOURCE),
Bit('TAP_Y', BitType.SOURCE),
Bit('TAP_Z', BitType.SOURCE)]
self.interpret_bits(data, bits)
def handle_reg_0x2c(self, data):
bits = [Bit('', BitType.UNUSED),
Bit('', BitType.UNUSED),
Bit('', BitType.UNUSED),
Bit('', BitType.OTHER, {1: ['Reduce power', 'Reduce pw', 'Red pw'], 0: ['Normal operation', 'Normal op', 'Norm op'],})]
bits_values = self.interpret_bits(data, bits)
start_index, stop_index = 0, 3
rate = self.get_decimal_number(bits_values, start_index, stop_index)
self.putbs([Ann.REG_DATA, ['%f' % rate_code[rate]]], stop_index, start_index)
def handle_reg_0x2d(self, data):
bits = [Bit('', BitType.UNUSED),
Bit('', BitType.UNUSED),
Bit('', BitType.OTHER, {1: ['Link'], 0: ['Unlink'], }),
Bit('AUTO_SLEEP', BitType.ENABLE),
Bit('', BitType.OTHER, {1: ['Measurement mode', 'Measurement', 'Meas'], 0: ['Standby mode', 'Standby'], }),
Bit('', BitType.OTHER, {1: ['Sleep mode', 'Sleep', 'Slp'], 0: ['Normal mode', 'Normal', 'Nrm'],})]
bits_values = self.interpret_bits(data, bits)
start_index, stop_index = 0, 1
wakeup = self.get_decimal_number(bits_values, start_index, stop_index)
frequency = 2 ** (~wakeup & 0x03)
self.putbs([Ann.REG_DATA, ['%d Hz' % frequency]], stop_index, start_index)
def handle_reg_0x2e(self, data):
bits = [Bit('DATA_READY', BitType.ENABLE),
Bit('SINGLE_TAP', BitType.ENABLE),
Bit('DOUBLE_TAP', BitType.ENABLE),
Bit('Activity', BitType.ENABLE),
Bit('Inactivity', BitType.ENABLE),
Bit('FREE_FALL', BitType.ENABLE),
Bit('Watermark', BitType.ENABLE),
Bit('Overrun', BitType.ENABLE)]
self.interpret_bits(data, bits)
def handle_reg_0x2f(self, data):
bits = [Bit('DATA_READY', BitType.INTERRUPT),
Bit('SINGLE_TAP', BitType.INTERRUPT),
Bit('DOUBLE_TAP', BitType.INTERRUPT),
Bit('Activity', BitType.INTERRUPT),
Bit('Inactivity', BitType.INTERRUPT),
Bit('FREE_FALL', BitType.INTERRUPT),
Bit('Watermark', BitType.INTERRUPT),
Bit('Overrun', BitType.INTERRUPT)]
self.interpret_bits(data, bits)
def handle_reg_0x30(self, data):
bits = [Bit('DATA_READY', BitType.SOURCE),
Bit('SINGLE_TAP', BitType.SOURCE),
Bit('DOUBLE_TAP', BitType.SOURCE),
Bit('Activity', BitType.SOURCE),
Bit('Inactivity', BitType.SOURCE),
Bit('FREE_FALL', BitType.SOURCE),
Bit('Watermark', BitType.SOURCE),
Bit('Overrun', BitType.SOURCE)]
self.interpret_bits(data, bits)
def handle_reg_0x31(self, data):
bits = [Bit('SELF_TEST', BitType.ENABLE),
Bit('', BitType.OTHER, {1: ['3-wire SPI', '3-SPI'], 0: ['4-wire SPI', '4-SPI'],}),
Bit('', BitType.OTHER, {1: ['INT ACT LOW', 'INT LOW'], 0: ['INT ACT HIGH', 'INT HIGH'],}),
Bit('', BitType.UNUSED),
Bit('', BitType.OTHER, {1: ['Full resolution', 'Full res'], 0: ['10-bit mode', '10-bit'],}),
Bit('', BitType.OTHER, {1: ['MSB mode', 'MSB'], 0: ['LSB mode', 'LSB'],})]
bits_values = self.interpret_bits(data, bits)
start_index, stop_index = 0, 1
range_g = self.get_decimal_number(bits_values, start_index, stop_index)
result = 2 ** (range_g + 1)
self.putbs([Ann.REG_DATA, ['+/-%d g' % result]], stop_index, start_index)
def handle_reg_0x32(self, data):
self.data = data
self.putx([Ann.REG_DATA, [str(data)]])
def handle_reg_0x33(self, data):
self.get_axis_value(data, 'X')
def handle_reg_0x34(self, data):
self.handle_reg_0x32(data)
def handle_reg_0x35(self, data):
self.get_axis_value(data, 'Y')
def handle_reg_0x36(self, data):
self.handle_reg_0x32(data)
def handle_reg_0x37(self, data):
self.get_axis_value(data, 'Z')
def handle_reg_0x38(self, data):
bits = [None,
None,
Bit('', BitType.OTHER, {1: ['Trig-INT2', 'INT2'], 0: ['Trig-INT1', 'INT1'], })]
bits_values = self.interpret_bits(data, bits)
start_index, stop_index = 6, 7
fifo = self.get_decimal_number(bits_values, start_index, stop_index)
self.putbs([Ann.REG_DATA, [fifo_modes[fifo]]], stop_index, start_index)
start_index, stop_index = 0, 4
samples = self.get_decimal_number(bits_values, start_index, stop_index)
self.putbs([Ann.REG_DATA, ['Samples: %d' % samples, '%d' % samples]], stop_index, start_index)
def handle_reg_0x39(self, data):
bits = [Bit('', BitType.OTHER, {1: ['Triggered', 'Trigg'], 0: ['Not triggered', 'Not trigg'],}),
Bit('', BitType.UNUSED)]
bits_values = self.interpret_bits(data, bits)
start_index, stop_index = 0, 5
entries = self.get_decimal_number(bits_values, start_index, stop_index)
self.putbs([Ann.REG_DATA, ['Entries: %d' % entries, '%d' % entries]], stop_index, start_index)
def get_bit(self, channel):
if (channel == Channel.MOSI and self.mosi is None) or \
(channel == Channel.MISO and self.miso is None):
raise Exception('No available data')
mosi_bit, miso_bit = 0, 0
if self.miso is not None:
if len(self.mosi) < 0:
raise Exception('No available data')
miso_bit = self.miso.pop(0)
if self.miso is not None:
if len(self.miso) < 0:
raise Exception('No available data')
mosi_bit = self.mosi.pop(0)
if channel == Channel.MOSI:
return mosi_bit
return miso_bit
def decode(self, ss, es, data):
ptype = data[0]
if ptype == 'CS-CHANGE':
cs_old, cs_new = data[1:]
if cs_old is not None and cs_old == 1 and cs_new == 0:
self.ss, self.es = ss, es
self.state = St.ADDRESS_BYTE
else:
self.state = St.IDLE
elif ptype == 'BITS':
if data[1] is not None:
self.mosi = list(reversed(data[1]))
if data[2] is not None:
self.miso = list(reversed(data[2]))
if self.mosi is None and self.miso is None:
return
if self.state == St.ADDRESS_BYTE:
# OPERATION BIT
op_bit = self.get_bit(Channel.MOSI)
self.put(op_bit[1], op_bit[2], self.out_ann,
[Ann.READ if op_bit[0] else Ann.WRITE, operations[op_bit[0]]])
self.operation = Operation.READ if op_bit[0] else Operation.WRITE
# MULTIPLE-BYTE BIT
mb_bit = self.get_bit(Channel.MOSI)
self.put(mb_bit[1], mb_bit[2], self.out_ann, [Ann.MB, number_bytes[mb_bit[0]]])
# REGISTER 6-BIT ADDRESS
self.address = 0
start_sample = self.mosi[0][1]
addr_bit = []
for i in range(6):
addr_bit = self.get_bit(Channel.MOSI)
self.address |= addr_bit[0]
self.address <<= 1
self.address >>= 1
self.put(start_sample, addr_bit[2], self.out_ann,
[Ann.REG_ADDRESS, ['ADDRESS: 0x%02X' % self.address, 'ADDR: 0x%02X'
% self.address, '0x%02X' % self.address]])
self.ss = -1
self.state = St.DATA
elif self.state == St.DATA:
self.reg.extend(self.mosi if self.operation == Operation.WRITE else self.miso)
self.mosi, self.miso = [], []
if self.ss == -1:
self.ss, self.es = self.reg[0][1], es
self.samples_per_bit = self.reg[0][2] - self.ss
if len(self.reg) < 8:
return
else:
reg_value = 0
reg_bit = []
for offset in range(7, -1, -1):
reg_bit = self.reg.pop(0)
mask = reg_bit[0] << offset
reg_value |= mask
if self.address < 0x00 or self.address > 0x39:
return
if self.address in [0x32, 0x34, 0x36]:
self.start_index = self.ss
if 0x1D > self.address >= 0x00:
self.put(self.ss, reg_bit[2], self.out_ann, [Ann.REG_ADDRESS, [str(self.address)]])
self.put(self.ss, reg_bit[2], self.out_ann, [Ann.REG_DATA, [str(reg_value)]])
else:
self.put(self.ss, reg_bit[2], self.out_ann, [Ann.REG_ADDRESS, registers[self.address]])
handle_reg = getattr(self, 'handle_reg_0x%02x' % self.address)
handle_reg(reg_value)
self.reg = []
self.address += 1
self.ss = -1

View File

@@ -0,0 +1,36 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2014 Johannes Roemer <jroemer@physik.uni-wuerzburg.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder handles the proprietary single wire communication protocol used
by the Aosong AM230x/DHTxx/RHTxx series of digital humidity and temperature
sensors.
Sample rate:
A sample rate of at least 200kHz is recommended to properly detect all the
elements of the protocol.
Options:
The AM230x and DHTxx/RHTxx digital humidity and temperature sensors use the
same single-wire protocol with different encoding of the measured values.
Therefore the option 'device' must be used to properly decode the
communication of the respective sensor.
'''
from .pd import Decoder

View File

@@ -0,0 +1,229 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2014 Johannes Roemer <jroemer@physik.uni-wuerzburg.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
# Define valid timing values (in microseconds).
timing = {
'START LOW' : {'min': 750, 'max': 25000},
'START HIGH' : {'min': 10, 'max': 10000},
'RESPONSE LOW' : {'min': 50, 'max': 90},
'RESPONSE HIGH' : {'min': 50, 'max': 90},
'BIT LOW' : {'min': 45, 'max': 90},
'BIT 0 HIGH' : {'min': 20, 'max': 35},
'BIT 1 HIGH' : {'min': 65, 'max': 80},
}
class SamplerateError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = 'am230x'
name = 'AM230x'
longname = 'Aosong AM230x/DHTxx/RHTxx'
desc = 'Aosong AM230x/DHTxx/RHTxx humidity/temperature sensor.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['IC', 'Sensor']
channels = (
{'id': 'sda', 'name': 'SDA', 'desc': 'Single wire serial data line'},
)
options = (
{'id': 'device', 'desc': 'Device type',
'default': 'am230x', 'values': ('am230x/rht', 'dht11')},
)
annotations = (
('start', 'Start'),
('response', 'Response'),
('bit', 'Bit'),
('end', 'End'),
('byte', 'Byte'),
('humidity', 'Relative humidity in percent'),
('temperature', 'Temperature in degrees Celsius'),
('checksum', 'Checksum'),
)
annotation_rows = (
('bits', 'Bits', (0, 1, 2, 3)),
('bytes', 'Bytes', (4,)),
('results', 'Results', (5, 6, 7)),
)
def putfs(self, data):
self.put(self.fall, self.samplenum, self.out_ann, data)
def putb(self, data):
self.put(self.bytepos[-1], self.samplenum, self.out_ann, data)
def putv(self, data):
self.put(self.bytepos[-2], self.samplenum, self.out_ann, data)
def reset_variables(self):
self.state = 'WAIT FOR START LOW'
self.fall = 0
self.rise = 0
self.bits = []
self.bytepos = []
def is_valid(self, name):
dt = 0
if name.endswith('LOW'):
dt = self.samplenum - self.fall
elif name.endswith('HIGH'):
dt = self.samplenum - self.rise
if dt >= self.cnt[name]['min'] and dt <= self.cnt[name]['max']:
return True
return False
def bits2num(self, bitlist):
number = 0
for i in range(len(bitlist)):
number += bitlist[-1 - i] * 2**i
return number
def calculate_humidity(self, bitlist):
h = 0
if self.options['device'] == 'dht11':
h = self.bits2num(bitlist[0:8])
else:
h = self.bits2num(bitlist) / 10
return h
def calculate_temperature(self, bitlist):
t = 0
if self.options['device'] == 'dht11':
t = self.bits2num(bitlist[0:8])
else:
t = self.bits2num(bitlist[1:]) / 10
if bitlist[0] == 1:
t = -t
return t
def calculate_checksum(self, bitlist):
checksum = 0
for i in range(8, len(bitlist) + 1, 8):
checksum += self.bits2num(bitlist[i-8:i])
return checksum % 256
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.reset_variables()
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def metadata(self, key, value):
if key != srd.SRD_CONF_SAMPLERATE:
return
self.samplerate = value
# Convert microseconds to sample counts.
self.cnt = {}
for e in timing:
self.cnt[e] = {}
for t in timing[e]:
self.cnt[e][t] = timing[e][t] * self.samplerate / 1000000
def handle_byte(self, bit):
self.bits.append(bit)
self.putfs([2, ['Bit: %d' % bit, '%d' % bit]])
self.fall = self.samplenum
self.state = 'WAIT FOR BIT HIGH'
if len(self.bits) % 8 == 0:
byte = self.bits2num(self.bits[-8:])
self.putb([4, ['Byte: %#04x' % byte, '%#04x' % byte]])
if len(self.bits) == 16:
h = self.calculate_humidity(self.bits[-16:])
self.putv([5, ['Humidity: %.1f %%' % h, 'RH = %.1f %%' % h]])
elif len(self.bits) == 32:
t = self.calculate_temperature(self.bits[-16:])
self.putv([6, ['Temperature: %.1f °C' % t, 'T = %.1f °C' % t]])
elif len(self.bits) == 40:
parity = self.bits2num(self.bits[-8:])
if parity == self.calculate_checksum(self.bits[0:32]):
self.putb([7, ['Checksum: OK', 'OK']])
else:
self.putb([7, ['Checksum: not OK', 'NOK']])
self.state = 'WAIT FOR END'
self.bytepos.append(self.samplenum)
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
while True:
# State machine.
if self.state == 'WAIT FOR START LOW':
self.wait({0: 'f'})
self.fall = self.samplenum
self.state = 'WAIT FOR START HIGH'
elif self.state == 'WAIT FOR START HIGH':
self.wait({0: 'r'})
if self.is_valid('START LOW'):
self.rise = self.samplenum
self.state = 'WAIT FOR RESPONSE LOW'
else:
self.reset_variables()
elif self.state == 'WAIT FOR RESPONSE LOW':
self.wait({0: 'f'})
if self.is_valid('START HIGH'):
self.putfs([0, ['Start', 'S']])
self.fall = self.samplenum
self.state = 'WAIT FOR RESPONSE HIGH'
else:
self.reset_variables()
elif self.state == 'WAIT FOR RESPONSE HIGH':
self.wait({0: 'r'})
if self.is_valid('RESPONSE LOW'):
self.rise = self.samplenum
self.state = 'WAIT FOR FIRST BIT'
else:
self.reset_variables()
elif self.state == 'WAIT FOR FIRST BIT':
self.wait({0: 'f'})
if self.is_valid('RESPONSE HIGH'):
self.putfs([1, ['Response', 'R']])
self.fall = self.samplenum
self.bytepos.append(self.samplenum)
self.state = 'WAIT FOR BIT HIGH'
else:
self.reset_variables()
elif self.state == 'WAIT FOR BIT HIGH':
self.wait({0: 'r'})
if self.is_valid('BIT LOW'):
self.rise = self.samplenum
self.state = 'WAIT FOR BIT LOW'
else:
self.reset_variables()
elif self.state == 'WAIT FOR BIT LOW':
self.wait({0: 'f'})
if self.is_valid('BIT 0 HIGH'):
bit = 0
elif self.is_valid('BIT 1 HIGH'):
bit = 1
else:
self.reset_variables()
continue
self.handle_byte(bit)
elif self.state == 'WAIT FOR END':
self.wait({0: 'r'})
self.putfs([3, ['End', 'E']])
self.reset_variables()

View File

@@ -0,0 +1,28 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'uart' PD and decodes the ASCII protocol
for Amulet LCD display controllers.
Currently the decoder treats both RX and TX the same way, decoding all
message types.
'''
from .pd import Decoder

View File

@@ -0,0 +1,73 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
from collections import OrderedDict
# OrderedDict which maps command IDs to their names and descriptions.
cmds = OrderedDict([
(0xA0, ('PAGE', 'Jump to page')),
(0xD0, ('GBV', 'Get byte variable')),
(0xD1, ('GWV', 'Get word variable')),
(0xD2, ('GSV', 'Get string variable')),
(0xD3, ('GLV', 'Get label variable')),
(0xD4, ('GRPC', 'Get RPC buffer')),
(0xD5, ('SBV', 'Set byte variable')),
(0xD6, ('SWV', 'Set word variable')),
(0xD7, ('SSV', 'Set string variable')),
(0xD8, ('RPC', 'Invoke RPC')),
(0xD9, ('LINE', 'Draw line')),
(0xDA, ('RECT', 'Draw rectangle')),
(0xDB, ('FRECT', 'Draw filled rectangle')),
(0xDC, ('PIXEL', 'Draw pixel')),
(0xDD, ('GBVA', 'Get byte variable array')),
(0xDE, ('GWVA', 'Get word variable array')),
(0xDF, ('SBVA', 'Set byte variable array')),
(0xE0, ('GBVR', 'Get byte variable reply')),
(0xE1, ('GWVR', 'Get word variable reply')),
(0xE2, ('GSVR', 'Get string variable reply')),
(0xE3, ('GLVR', 'Get label variable reply')),
(0xE4, ('GRPCR', 'Get RPC buffer reply')),
(0xE5, ('SBVR', 'Set byte variable reply')),
(0xE6, ('SWVR', 'Set word variable reply')),
(0xE7, ('SSVR', 'Set string variable reply')),
(0xE8, ('RPCR', 'Invoke RPC reply')),
(0xE9, ('LINER', 'Draw line reply')),
(0xEA, ('RECTR', 'Draw rectangle')),
(0xEB, ('FRECTR', 'Draw filled rectangle reply')),
(0xEC, ('PIXELR', 'Draw pixel reply')),
(0xED, ('GBVAR', 'Get byte variable array reply')),
(0xEE, ('GWVAR', 'Get word variable array reply')),
(0xEF, ('SBVAR', 'Set byte variable array reply')),
(0xF0, ('ACK', 'Acknowledgment')),
(0xF1, ('NACK', 'Negative acknowledgment')),
(0xF2, ('SWVA', 'Set word variable array')),
(0xF3, ('SWVAR', 'Set word variable array reply')),
(0xF4, ('GCV', 'Get color variable')),
(0xF5, ('GCVR', 'Get color variable reply')),
(0xF6, ('SCV', 'Set color variable')),
(0xF7, ('SCVR', 'Set color variable reply')),
])
cmds_with_high_bytes = [
0xA0, # PAGE - Page change
0xD7, # SVV - Set string variable
0xE7, # SVVR - Set string variable reply
0xE2, # GSVR - Get string variable reply
0xE3, # GLVR - Get label variable reply
]

View File

@@ -0,0 +1,699 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Vesa-Pekka Palmu <vpalmu@depili.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from math import ceil
from common.srdhelper import SrdIntEnum
from .lists import *
L = len(cmds)
RX = 0
TX = 1
Ann = SrdIntEnum.from_list('Ann',
[c[0] for c in cmds.values()] + ['BIT', 'FIELD', 'WARN'])
def cmd_annotation_classes():
return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
class Decoder(srd.Decoder):
api_version = 3
id = 'amulet_ascii'
name = 'Amulet ASCII'
longname = 'Amulet LCD ASCII'
desc = 'Amulet Technologies LCD controller ASCII protocol.'
license = 'gplv3+'
inputs = ['uart']
outputs = []
tags = ['Display']
annotations = cmd_annotation_classes() + (
('bit', 'Bit'),
('field', 'Field'),
('warning', 'Warning'),
)
annotation_rows = (
('bits', 'Bits', (L + 0,)),
('fields', 'Fields', (L + 1,)),
('commands', 'Commands', tuple(range(L))),
('warnings', 'Warnings', (L + 2,)),
)
options = (
{'id': 'ms_chan', 'desc': 'Master -> slave channel',
'default': 'RX', 'values': ('RX', 'TX')},
{'id': 'sm_chan', 'desc': 'Slave -> master channel',
'default': 'TX', 'values': ('RX', 'TX')},
)
def __init__(self):
self.reset()
def reset(self):
self.state = None
self.cmdstate = None
# Build dict mapping command keys to handler functions. Each
# command in 'cmds' (defined in lists.py) has a matching
# handler self.handle_<shortname>.
def get_handler(cmd):
s = 'handle_%s' % cmds[cmd][0].lower().replace('/', '_')
return getattr(self, s)
self.cmd_handlers = dict((cmd, get_handler(cmd)) for cmd in cmds.keys())
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
# Simplification, most annotations span exactly one SPI byte/packet.
self.put(self.ss, self.es, self.out_ann, data)
def putf(self, data):
self.put(self.ss_field, self.es_field, self.out_ann, data)
def putc(self, data):
self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
def cmd_ann_list(self):
x, s = cmds[self.state][0], cmds[self.state][1]
return ['Command: %s (%s)' % (s, x), 'Command: %s' % s,
'Cmd: %s' % s, 'Cmd: %s' % x, x]
def emit_cmd_byte(self):
self.ss_cmd = self.ss
self.putx([Ann.FIELD, self.cmd_ann_list()])
def emit_addr_bytes(self, pdata):
if self.cmdstate == 2:
self.ss_field = self.ss
self.addr = chr(pdata)
self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
'Addr high 0x%c' % pdata, 'Addr h 0x%c' % pdata]])
elif self.cmdstate == 3:
self.es_field = self.es
self.addr += chr(pdata)
self.addr = int(self.addr, 16)
self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
'Addr low 0x%c' % pdata, 'Addr l 0x%c' % pdata]])
self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
def emit_cmd_end(self, data):
self.es_cmd = self.es
self.putc(data)
self.state = None
def handle_read(self, data):
if self.cmdstate == 1:
self.emit_cmd_byte()
self.addr = 0
elif self.cmdstate == 2:
self.emit_addr_bytes(pdata)
elif self.cmdstate == 3:
self.emit_addr_bytes(pdata)
self.cmdstate += 1
def handle_set_common(self, pdata):
if self.cmdstate == 1:
self.addr = 0
self.emit_addr_bytes(pdata)
def emit_not_implemented(self, data):
self.es_cmd = self.es
self.putc([Ann.WARN, ['Command not decoded', 'Not decoded']])
self.emit_cmd_end(data)
def handle_string(self, pdata, ann_class):
# TODO: unicode / string modifiers...
self.handle_set_common(pdata)
if self.cmdstate == 4:
self.ss_field = self.ss
self.value = ''
if pdata == 0x00:
# Null terminated string ends.
self.es_field = self.es
self.putx([Ann.BIT, ['NULL']])
self.putf([Ann.FIELD, ['Value: %s' % self.value,
'Val: %s' % self.value, '%s' % self.value]])
self.emit_cmd_end([ann_class, self.cmd_ann_list()])
return
if self.cmdstate > 3:
self.value += chr(pdata)
self.putx([Ann.BIT, ['%c' % pdata]])
self.cmdstate += 1
# Command handlers
# Page change 0xA0, 0x02, index_high, index_low, checksum
def handle_page(self, pdata):
if self.cmdstate == 2:
if pdata == 0x02:
self.ss_field = self.ss_cmd
self.es_field = self.es
self.putf([Ann.FIELD, self.cmd_ann_list()])
self.checksum = 0xA0 + 0x02
else:
self.putx([Ann.WARN, ['Illegal second byte for page change',
'Illegal byte']])
self.state = None
elif self.cmdstate == 3:
self.ss_field = self.ss
self.checksum += pdata
self.page[0] = pdata
elif self.cmdstate == 4:
self.checksum += pdata
self.page[1] = pdata
self.es_field = self.es
if self.page[0] == self.page [1] == 0xFF:
# Soft reset trigger
self.putf(Ann.WARN, ['Soft reset', 'Reset'])
else:
page = chr(self.page[0]) + chr(self.page[1])
self.putf(Ann.FIELD, ['Page index: 0x%s' % page,
'Page: 0x%s' % page, '0x%s' % page])
elif self.cmdstate == 5:
self.checksum += pdata
if (self.checksum & 0xFF) != 0:
self.putx([Ann.WARN, ['Checksum error', 'Error', 'ERR']])
else:
self.putx([Ann.FIELD, ['Checksum OK', 'OK']])
self.emit_cmd_end(Ann.PAGE)
self.cmdstate += 1
# Value reads: command byte, address high nibble, address low nibble
# Get byte value
def handle_gbv(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.GBV, self.cmd_ann_list()])
# Get word value
def handle_gwv(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.GWV, self.cmd_ann_list()])
# Get string value
def handle_gsv(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.GSV, self.cmd_ann_list()])
# Get label value
def handle_glv(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.GLV, self.cmd_ann_list()])
# Get RPC buffer
def handle_grpc(self, pdata):
if self.cmdstate == 2:
self.ss_field = self.ss
self.flags = int(chr(pdata), 16) << 4
elif self.cmdstate == 3:
self.flags += int(chr(pdata), 16)
self.es_field = self.es
self.putf([Ann.FIELD, ['RPC flag: 0x%02X' % self.flags]])
self.emit_cmd_end([Ann.GRPC, self.cmd_ann_list()])
# Get byte value array
def handle_gbva(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.GBVA, self.cmd_ann_list()])
# Get word value array
def handle_gwva(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.GWVA, self.cmd_ann_list()])
# Get color variable
def handle_gcv(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.GCV, self.cmd_ann_list()])
# Value setters: command byte, address high nibble, address low nibble, data bytes
# Set byte value data = high nibble, low nibble
def handle_sbv(self, pdata):
self.handle_set_common(pdata)
if self.cmdstate == 4:
self.ss_field = self.ss
self.value = chr(pdata)
elif self.cmdstate == 5:
self.value += chr(pdata)
self.es_field = self.es
self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
'Val: 0x%s' % self.value, '0x%s' % self.value]])
self.emit_cmd_end([Ann.SBV, self.cmd_ann_list()])
self.cmdstate += 1
# Set word value, msb high, msb low, lsb high, lsb low
def handle_swv(self, pdata):
self.handle_set_common(pdata)
if self.cmdstate > 3:
nibble = self.cmdstate - 4
if nibble == 0:
self.ss_field = self.ss
self.value = 0
self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
if nibble == 3:
self.es_field = self.es
self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
self.emit_cmd_end([Ann.SWV, self.cmd_ann_list()])
return
self.cmdstate += 1
# Set string value, null terminated utf8 strings
def handle_ssv(self, pdata):
self.handle_string(pdata, Ann.SSV)
# Set byte value array
def handle_sbva(self, pdata):
nibble = (self.cmdstate - 3) % 2
if self.cmdstate == 2:
self.addr = int(chr(pdata), 16) << 4
self.ss_field = self.ss
self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
'Addr high 0x%c' % pdata, '0x%c' % pdata]])
elif self.cmdstate == 3:
self.addr += int(chr(pdata), 16)
self.es_field = self.ss
self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
'Addr low 0x%c' % pdata, '0x%c' % pdata]])
self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
elif stage == 2:
if pdata == 0x00:
# Null terminated list
self.emit_cmd_end([Ann.SBVA, self.cmd_ann_list()])
return
self.value = int(chr(pdata), 16) << 4
else:
self.value += int(chr(pdata), 16)
self.es_field = self.es
self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
'0x%02X' % self.value]])
self.cmdstate += 1
# Set word value array
def handle_swva(self, pdata):
nibble = (self.cmdstate - 3) % 4
if self.cmdstate == 2:
self.addr = int(chr(pdata), 16) << 4
self.ss_field = self.ss
self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
'Addr high 0x%c' % pdata, '0x%c' % pdata]])
elif self.cmdstate == 3:
self.addr += int(chr(pdata), 16)
self.es_field = self.ss
self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
'Addr low 0x%c' % pdata, '0x%c' % pdata]])
self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
self.value = 0
else:
self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
if nibble == 0:
if pdata == 0x00:
# Null terminated list
self.emit_cmd_end([Ann.SWVA, self.cmd_ann_list()])
return
self.ss_field = self.ss
if nibble == 3:
self.es_field = self.es
self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
'0x%04X' % self.value]])
self.cmdstate += 1
# Set color variable
def handle_scv(self, pdata):
if self.cmdstate == 8:
self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
self.cmdstate += 1
# RPC trigger
def handle_rpc(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.RPC, self.cmd_ann_list()])
# Drawing
# Decode pair of (x,y) 16bit coordinates
def decode_coords(self, pdata):
if self.cmdstate == 1:
self.coords[0] = 0
self.coords[1] = 0
self.coords[2] = 0
self.coords[3] = 0
if self.cmdstate < 18:
# Coordinates
nibble = (self.cmdstate - 1) % 4
i = (self.cmdstate - 1) / 4
self.coords[i] += int(chr(pdata), 16) << 12 - (4 * nibble)
if nibble == 0:
self.ss_field = self.ss
elif nibble == 3:
self.es_field = self.es
self.putf([Ann.FIELD, ['Coordinate 0x%04X' % self.coords[i]],
['0x%04X' % self.coords[i]]])
# TODO: There are actually two protocol revisions for drawing.
# Both use 4 bytes for 16bit x and y pairs for start and end.
# The older follows this by a pattern selector and then line weight.
# Newer version has 6 bytes for 8bit RGB color...
# Draw line
def handle_line(self, pdata):
decode_coords(pdata)
if self.cmdstate == 18:
self.es_cmd = self.es
self.putc([Ann.LINE, self.cmd_ann_list()])
self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
self.state = None
self.cmdstate += 1
# Draw rectange
def handle_rect(self, pdata):
decode_coords(pdata)
if self.cmdstate == 18:
self.es_cmd = self.es
self.putc([Ann.RECT, self.cmd_ann_list()])
self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
self.state = None
self.cmdstate += 1
# Draw filled rectangle
def handle_frect(self, pdata):
decode_coords(pdata)
if self.cmdstate == 18:
self.es_cmd = self.es
self.putc([Ann.FRECT, self.cmd_ann_list()])
self.putc([Ann.WARN, ['Fill pattern / Color not implemented']])
self.state = None
self.cmdstate += 1
# Draw pixel
def handle_pixel(self, pdata):
self.es_cmd = self.es
self.putc([Ann.WARN, ['Draw pixel documentation is missing.', 'Undocumented']])
self.state = None
# Replies
def handle_gbvr(self, pdata):
self.emit_add_bytes(pdata)
if self.cmdstate == 4:
self.ss_field = self.ss
self.value = int(chr(pdata), 16) << 4
self.putx([Ann.BIT, ['High nibble 0x%s' % pdata, '0x%s' % pdata]])
elif self.cmdstate == 5:
self.value += int(chr(pdata), 16)
self.putx([Ann.BIT, ['Low nibble 0x%s' % pdata, '0x%s' % pdata]])
self.es_field = self.es
self.putf([Ann.FIELD, ['Value: 0x%02X' % self.value,
'0x%02X' % self.value]])
self.emit_cmd_end([Ann.GBVR, self.cmd_ann_list()])
self.cmdstate += 1
def handle_gwvr(self, pdata):
self.emit_add_bytes(pdata)
if self.cmdstate > 3:
nibble = self.cmdstate - 3
if nibble == 0:
self.value = 0
self.ss_field = self.ss
self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
self.putx([Ann.BIT, ['0x%s' % pdata]])
if nibble == 3:
self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
'0x%04X' % self.value]])
self.es_cmd = self.ss
self.emit_cmd_end([Ann.GWVR, self.cmd_ann_list()])
self.cmdstate += 1
def handle_gsvr(self, pdata):
self.handle_string(pdata, Ann.GSVR)
def handle_glvr(self, pdata):
self.handle_string(pdata, Ann.GLVR)
def handle_grpcr(self, pdata):
self.handle_addr(pdata)
if self.cmdstate > 3:
nibble = (self.cmdstate - 3) % 2
if nibble == 0:
if pdata == 0x00:
self.emit_cmd_end([Ann.GRPCR, self.cmd_ann_list()])
return
self.value = int(chr(pdata), 16) << 4
self.ss_field = self.ss
self.putx([Ann.BIT, ['0x%s' % pdata]])
if nibble == 2:
self.value += int(chr(pdata), 16)
self.es_field = self.es
self.putx([Ann.BIT, ['0x%s' % pdata]])
self.putf([Ann.FIELD, ['0x%02X' % self.value]])
self.cmdstate += 1
def handle_sbvr(self, pdata):
self.handle_set_common(pdata)
if self.cmdstate == 4:
self.ss_field = self.ss
self.value = chr(pdata)
elif self.cmdstate == 5:
self.value += chr(pdata)
self.es_field = self.es
self.putf([Ann.FIELD, ['Value: 0x%s' % self.value,
'Val: 0x%s' % self.value, '0x%s' % self.value]])
self.emit_cmd_end([Ann.SBVR, self.cmd_ann_list()])
self.cmdstate += 1
def handle_swvr(self, pdata):
self.handle_set_common(pdata)
if self.cmdstate == 4:
self.ss_field = self.ss
self.value = (pdata - 0x30) << 4
elif self.cmdstate == 5:
self.value += (pdata - 0x30)
self.value = self.value << 8
elif self.cmdstate == 6:
self.value += (pdata - 0x30) << 4
elif self.cmdstate == 7:
self.value += (pdata - 0x30)
self.es_field = self.es
self.putf([Ann.FIELD, ['Value: 0x%04x' % self.value,
'Val: 0x%04x' % self.value, '0x%04x' % self.value]])
self.emit_cmd_end([Ann.SWVR, self.cmd_ann_list()])
self.state = None
self.cmdstate += 1
def handle_ssvr(self, pdata):
self.handle_string(pdata, Ann.SSVR)
def handle_rpcr(self, pdata):
self.handle_read(pdata)
self.emit_cmd_end([Ann.RPCR, self.cmd_ann_list()])
def handle_liner(self, pdata):
decode_coords(pdata)
if self.cmdstate == 18:
self.es_cmd = self.es
self.putc([Ann.LINER, self.cmd_ann_list()])
self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
self.state = None
self.cmdstate += 1
def handle_rectr(self, pdata):
decode_coords(pdata)
if self.cmdstate == 18:
self.es_cmd = self.es
self.putc([Ann.RECTR, self.cmd_ann_list()])
self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
self.state = None
self.cmdstate += 1
def handle_frectr(self, pdata):
decode_coords(pdata)
if self.cmdstate == 18:
self.es_cmd = self.es
self.putc([Ann.FRECTR, self.cmd_ann_list()])
self.putc([Ann.WARN, ['Line pattern / Color not implemented']])
self.state = None
self.cmdstate += 1
def handle_pixelr(self, pdata):
self.es_cmd = self.es
self.putc([Ann.WARN,['Draw pixel documentation is missing.', 'Undocumented']])
self.state = None
def handle_gbvar(self, pdata):
nibble = (self.cmdstate - 3) % 2
if self.cmdstate == 2:
self.addr = int(chr(pdata), 16) << 4
self.ss_field = self.ss
self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
'Addr high 0x%c' % pdata, '0x%c' % pdata]])
elif self.cmdstate == 3:
self.addr += int(chr(pdata), 16)
self.es_field = self.ss
self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
'Addr low 0x%c' % pdata, '0x%c' % pdata]])
self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
elif stage == 2:
if pdata == 0x00:
# Null terminated list
self.emit_cmd_end([Ann.GBVAR, self.cmd_ann_list()])
return
self.value = int(chr(pdata), 16) << 4
else:
self.value += int(chr(pdata), 16)
self.es_field = self.es
self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
'0x%02X' % self.value]])
self.cmdstate += 1
def handle_gwvar(self, pdata):
nibble = (self.cmdstate - 3) % 4
if self.cmdstate == 2:
self.addr = int(chr(pdata), 16) << 4
self.ss_field = self.ss
self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
'Addr high 0x%c' % pdata, '0x%c' % pdata]])
elif self.cmdstate == 3:
self.addr += int(chr(pdata), 16)
self.es_field = self.ss
self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
'Addr low 0x%c' % pdata, '0x%c' % pdata]])
self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
self.value = 0
else:
self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
if nibble == 0:
if pdata == 0x00:
# Null terminated list
self.emit_cmd_end([Ann.GWVAR, self.cmd_ann_list()])
return
self.ss_field = self.ss
if nibble == 3:
self.es_field = self.es
self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
'0x%04X' % self.value]])
self.cmdstate += 1
# Get byte variable array reply
def handle_sbvar(self, pdata):
nibble = (self.cmdstate - 3) % 2
if self.cmdstate == 2:
self.addr = int(chr(pdata), 16) << 4
self.ss_field = self.ss
self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
'Addr high 0x%c' % pdata, '0x%c' % pdata]])
elif self.cmdstate == 3:
self.addr += int(chr(pdata), 16)
self.es_field = self.ss
self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
'Addr low 0x%c' % pdata, '0x%c' % pdata]])
self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
elif stage == 2:
if pdata == 0x00:
# Null terminated list
self.emit_cmd_end([Ann.SBVAR, self.cmd_ann_list()])
return
self.value = int(chr(pdata), 16) << 4
else:
self.value += int(chr(pdata), 16)
self.es_field = self.es
self.putf([Ann.FIELD, ['Value 0x%02X' % self.value,
'0x%02X' % self.value]])
self.cmdstate += 1
# Set word variable array reply
def handle_swvar(self, pdata):
nibble = (self.cmdstate - 3) % 4
if self.cmdstate == 2:
self.addr = int(chr(pdata), 16) << 4
self.ss_field = self.ss
self.putx([Ann.BIT, ['Address high nibble: %c' % pdata,
'Addr high 0x%c' % pdata, '0x%c' % pdata]])
elif self.cmdstate == 3:
self.addr += int(chr(pdata), 16)
self.es_field = self.ss
self.putx([Ann.BIT, ['Address low nibble: %c' % pdata,
'Addr low 0x%c' % pdata, '0x%c' % pdata]])
self.putf([Ann.FIELD, ['Address: 0x%02X' % self.addr,
'Addr: 0x%02X' % self.addr, '0x%02X' % self.addr]])
self.value = 0
else:
self.value += int(chr(pdata), 16) << 12 - (4 * nibble)
if nibble == 0:
if pdata == 0x00:
# Null terminated list
self.emit_cmd_end([Ann.SWVAR, self.cmd_ann_list()])
return
self.ss_field = self.ss
if nibble == 3:
self.es_field = self.es
self.putf([Ann.FIELD, ['Value 0x%04X' % self.value,
'0x%04X' % self.value]])
self.cmdstate += 1
def handle_gcvr(self, pdata):
if self.cmdstate == 8:
self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
self.cmdstate += 1
def handle_scvr(self, pdata):
if self.cmdstate == 8:
self.emit_not_implemented([Ann.SCV, self.cmd_ann_list()])
self.cmdstate += 1
# ACK & NACK
def handle_ack(self, pdata):
self.putx([Ann.ACK, self.cmd_ann_list()])
self.state = None
def handle_nack(self, pdata):
self.putx([Ann.NACK, self.cmd_ann_list()])
self.state = None
def decode(self, ss, es, data):
ptype, rxtx, pdata = data
self.ss, self.es = ss, es
if ptype != 'DATA':
return
# Handle commands.
try:
abort_current = (0xD0 <= pdata[0] <= 0xF7) and \
(not (self.state in cmds_with_high_bytes)) and \
self.state != None
if abort_current:
self.putx([Ann.WARN, ['Command aborted by invalid byte', 'Abort']])
self.state = pdata[0]
self.emit_cmd_byte()
self.cmdstate = 1
if self.state is None:
self.state = pdata[0]
self.emit_cmd_byte()
self.cmdstate = 1
self.cmd_handlers[self.state](pdata[0])
except KeyError:
self.putx([Ann.WARN, ['Unknown command: 0x%02x' % pdata[0]]])
self.state = None

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'uart' PD and decodes packets of
the ARMv7m Embedded Trace Macroblock v3.x.
'''
from .pd import Decoder

View File

@@ -0,0 +1,567 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
import subprocess
import re
# See ETMv3 Signal Protocol table 7-11: 'Encoding of Exception[8:0]'.
exc_names = [
'No exception', 'IRQ1', 'IRQ2', 'IRQ3', 'IRQ4', 'IRQ5', 'IRQ6', 'IRQ7',
'IRQ0', 'UsageFault', 'NMI', 'SVC', 'DebugMon', 'MemManage', 'PendSV',
'SysTick', 'Reserved', 'Reset', 'BusFault', 'Reserved', 'Reserved'
]
for i in range(8, 496):
exc_names.append('IRQ%d' % i)
def parse_varint(bytes_):
'''Parse an integer where the top bit is the continuation bit.
Returns value and number of parsed bytes.'''
v = 0
for i, b in enumerate(bytes_):
v |= (b & 0x7F) << (i * 7)
if b & 0x80 == 0:
return v, i+1
return v, len(bytes_)
def parse_uint(bytes_):
'''Parse little-endian integer.'''
v = 0
for i, b in enumerate(bytes_):
v |= b << (i * 8)
return v
def parse_exc_info(bytes_):
'''Parse exception information bytes from a branch packet.'''
if len(bytes_) < 1:
return None
excv, exclen = parse_varint(bytes_)
if bytes_[exclen - 1] & 0x80 != 0x00:
return None # Exception info not complete.
if exclen == 2 and excv & (1 << 13):
# Exception byte 1 was skipped, fix up the decoding.
excv = (excv & 0x7F) | ((excv & 0x3F80) << 7)
ns = excv & 1
exc = ((excv >> 1) & 0x0F) | ((excv >> 7) & 0x1F0)
cancel = (excv >> 5) & 1
altisa = (excv >> 6) & 1
hyp = (excv >> 12) & 1
resume = (excv >> 14) & 0x0F
return (ns, exc, cancel, altisa, hyp, resume)
def parse_branch_addr(bytes_, ref_addr, cpu_state, branch_enc):
'''Parse encoded branch address.
Returns addr, addrlen, cpu_state, exc_info.
Returns None if packet is not yet complete'''
addr, addrlen = parse_varint(bytes_)
if bytes_[addrlen - 1] & 0x80 != 0x00:
return None # Branch address not complete.
addr_bits = 7 * addrlen
have_exc_info = False
if branch_enc == 'original':
if addrlen == 5 and bytes_[4] & 0x40:
have_exc_info = True
elif branch_enc == 'alternative':
addr_bits -= 1 # Top bit of address indicates exc_info.
if addrlen >= 2 and addr & (1 << addr_bits):
have_exc_info = True
addr &= ~(1 << addr_bits)
exc_info = None
if have_exc_info:
exc_info = parse_exc_info(bytes_[addrlen:])
if exc_info is None:
return None # Exception info not complete.
if addrlen == 5:
# Possible change in CPU state.
if bytes_[4] & 0xB8 == 0x08:
cpu_state = 'arm'
elif bytes_[4] & 0xB0 == 0x10:
cpu_state = 'thumb'
elif bytes_[4] & 0xA0 == 0x20:
cpu_state = 'jazelle'
else:
raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes_[4])
# Shift the address according to current CPU state.
if cpu_state == 'arm':
addr = (addr & 0xFFFFFFFE) << 1
addr_bits += 1
elif cpu_state == 'thumb':
addr = addr & 0xFFFFFFFE
elif cpu_state == 'jazelle':
addr = (addr & 0xFFFFFFFFE) >> 1
addr_bits -= 1
else:
raise NotImplementedError('Unhandled state: ' + cpu_state)
# If the address wasn't full, fill in with the previous address.
if addrlen < 5:
addr |= ref_addr & (0xFFFFFFFF << addr_bits)
return addr, addrlen, cpu_state, exc_info
class Decoder(srd.Decoder):
api_version = 3
id = 'arm_etmv3'
name = 'ARM ETMv3'
longname = 'ARM Embedded Trace Macroblock v3'
desc = 'ARM ETM v3 instruction trace protocol.'
license = 'gplv2+'
inputs = ['uart']
outputs = []
tags = ['Debug/trace']
annotations = (
('trace', 'Trace info'),
('branch', 'Branches'),
('exception', 'Exceptions'),
('execution', 'Instruction execution'),
('data', 'Data access'),
('pc', 'Program counter'),
('instr_e', 'Executed instructions'),
('instr_n', 'Not executed instructions'),
('source', 'Source code'),
('location', 'Current location'),
('function', 'Current function'),
)
annotation_rows = (
('trace', 'Trace info', (0,)),
('flow', 'Code flow', (1, 2, 3,)),
('data', 'Data access', (4,)),
('pc', 'Program counter', (5,)),
('instruction', 'Instructions', (6, 7,)),
('source', 'Source code', (8,)),
('location', 'Current location', (9,)),
('function', 'Current function', (10,)),
)
options = (
{'id': 'objdump', 'desc': 'objdump path',
'default': 'arm-none-eabi-objdump'},
{'id': 'objdump_opts', 'desc': 'objdump options',
'default': '-lSC'},
{'id': 'elffile', 'desc': '.elf path',
'default': ''},
{'id': 'branch_enc', 'desc': 'Branch encoding',
'default': 'alternative', 'values': ('alternative', 'original')},
)
def __init__(self):
self.reset()
def reset(self):
self.buf = []
self.syncbuf = []
self.prevsample = 0
self.last_branch = 0
self.cpu_state = 'arm'
self.current_pc = 0
self.current_loc = None
self.current_func = None
self.next_instr_lookup = {}
self.file_lookup = {}
self.func_lookup = {}
self.disasm_lookup = {}
self.source_lookup = {}
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.load_objdump()
def load_objdump(self):
'''Parse disassembly obtained from objdump into two tables:
next_instr_lookup: Find the next PC addr from current PC.
disasm_lookup: Find the instruction text from current PC.
source_lookup: Find the source code line from current PC.
'''
if not (self.options['objdump'] and self.options['elffile']):
return
opts = [self.options['objdump']]
opts += self.options['objdump_opts'].split()
opts += [self.options['elffile']]
try:
disasm = subprocess.check_output(opts)
except subprocess.CalledProcessError:
return
disasm = disasm.decode('utf-8', 'replace')
instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
branchpat = re.compile('(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)')
filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
prev_src = ''
prev_file = ''
prev_func = ''
for line in disasm.split('\n'):
m = instpat.match(line)
if m:
addr = int(m.group(1), 16)
raw = m.group(2)
disas = m.group(3).strip().replace('\t', ' ')
self.disasm_lookup[addr] = disas
self.source_lookup[addr] = prev_src
self.file_lookup[addr] = prev_file
self.func_lookup[addr] = prev_func
# Next address in direct sequence.
ilen = len(raw.replace(' ', '')) // 2
next_n = addr + ilen
# Next address if branch is taken.
bm = branchpat.match(disas)
if bm:
next_e = int(bm.group(2), 16)
else:
next_e = next_n
self.next_instr_lookup[addr] = (next_n, next_e)
else:
m = funcpat.match(line)
if m:
prev_func = m.group(1)
prev_src = None
else:
m = filepat.match(line)
if m:
prev_file = m.group(1)
prev_src = None
else:
prev_src = line.strip()
def flush_current_loc(self):
if self.current_loc is not None:
ss, es, loc, src = self.current_loc
if loc:
self.put(ss, es, self.out_ann, [9, [loc]])
if src:
self.put(ss, es, self.out_ann, [8, [src]])
self.current_loc = None
def flush_current_func(self):
if self.current_func is not None:
ss, es, func = self.current_func
if func:
self.put(ss, es, self.out_ann, [10, [func]])
self.current_func = None
def instructions_executed(self, exec_status):
'''Advance program counter based on executed instructions.
Argument is a list of False for not executed and True for executed
instructions.
'''
if len(exec_status) == 0:
return
tdelta = max(1, (self.prevsample - self.startsample) / len(exec_status))
for i, exec_status in enumerate(exec_status):
pc = self.current_pc
default_next = pc + 2 if self.cpu_state == 'thumb' else pc + 4
target_n, target_e = self.next_instr_lookup.get(pc, (default_next, default_next))
ss = self.startsample + round(tdelta * i)
es = self.startsample + round(tdelta * (i+1))
self.put(ss, es, self.out_ann,
[5, ['PC 0x%08x' % pc, '0x%08x' % pc, '%08x' % pc]])
new_loc = self.file_lookup.get(pc)
new_src = self.source_lookup.get(pc)
new_dis = self.disasm_lookup.get(pc)
new_func = self.func_lookup.get(pc)
# Report source line only when it changes.
if self.current_loc is not None:
if new_loc != self.current_loc[2] or new_src != self.current_loc[3]:
self.flush_current_loc()
if self.current_loc is None:
self.current_loc = [ss, es, new_loc, new_src]
else:
self.current_loc[1] = es
# Report function name only when it changes.
if self.current_func is not None:
if new_func != self.current_func[2]:
self.flush_current_func()
if self.current_func is None:
self.current_func = [ss, es, new_func]
else:
self.current_func[1] = es
# Report instruction every time.
if new_dis:
if exec_status:
a = [6, ['Executed: ' + new_dis, new_dis, new_dis.split()[0]]]
else:
a = [7, ['Not executed: ' + new_dis, new_dis, new_dis.split()[0]]]
self.put(ss, es, self.out_ann, a)
if exec_status:
self.current_pc = target_e
else:
self.current_pc = target_n
def get_packet_type(self, byte):
'''Identify packet type based on its first byte.
See ARM IHI0014Q section "ETMv3 Signal Protocol" "Packet Types"
'''
if byte & 0x01 == 0x01:
return 'branch'
elif byte == 0x00:
return 'a_sync'
elif byte == 0x04:
return 'cyclecount'
elif byte == 0x08:
return 'i_sync'
elif byte == 0x0C:
return 'trigger'
elif byte & 0xF3 in (0x20, 0x40, 0x60):
return 'ooo_data'
elif byte == 0x50:
return 'store_failed'
elif byte == 0x70:
return 'i_sync'
elif byte & 0xDF in (0x54, 0x58, 0x5C):
return 'ooo_place'
elif byte == 0x3C:
return 'vmid'
elif byte & 0xD3 == 0x02:
return 'data'
elif byte & 0xFB == 0x42:
return 'timestamp'
elif byte == 0x62:
return 'data_suppressed'
elif byte == 0x66:
return 'ignore'
elif byte & 0xEF == 0x6A:
return 'value_not_traced'
elif byte == 0x6E:
return 'context_id'
elif byte == 0x76:
return 'exception_exit'
elif byte == 0x7E:
return 'exception_entry'
elif byte & 0x81 == 0x80:
return 'p_header'
else:
return 'unknown'
def fallback(self, buf):
ptype = self.get_packet_type(buf[0])
return [0, ['Unhandled ' + ptype + ': ' + ' '.join(['%02x' % b for b in buf])]]
def handle_a_sync(self, buf):
if buf[-1] == 0x80:
return [0, ['Synchronization']]
def handle_exception_exit(self, buf):
return [2, ['Exception exit']]
def handle_exception_entry(self, buf):
return [2, ['Exception entry']]
def handle_i_sync(self, buf):
contextid_bytes = 0 # This is the default ETM config.
if len(buf) < 6:
return None # Packet definitely not full yet.
if buf[0] == 0x08: # No cycle count.
cyclecount = None
idx = 1 + contextid_bytes # Index to info byte.
elif buf[0] == 0x70: # With cycle count.
cyclecount, cyclen = parse_varint(buf[1:6])
idx = 1 + cyclen + contextid_bytes
if len(buf) <= idx + 4:
return None
infobyte = buf[idx]
addr = parse_uint(buf[idx+1:idx+5])
reasoncode = (infobyte >> 5) & 3
reason = ('Periodic', 'Tracing enabled', 'After overflow', 'Exit from debug')[reasoncode]
jazelle = (infobyte >> 4) & 1
nonsec = (infobyte >> 3) & 1
altisa = (infobyte >> 2) & 1
hypervisor = (infobyte >> 1) & 1
thumb = addr & 1
addr &= 0xFFFFFFFE
if reasoncode == 0 and self.current_pc != addr:
self.put(self.startsample, self.prevsample, self.out_ann,
[0, ['WARN: Unexpected PC change 0x%08x -> 0x%08x' % \
(self.current_pc, addr)]])
elif reasoncode != 0:
# Reset location when the trace has been interrupted.
self.flush_current_loc()
self.flush_current_func()
self.last_branch = addr
self.current_pc = addr
if jazelle:
self.cpu_state = 'jazelle'
elif thumb:
self.cpu_state = 'thumb'
else:
self.cpu_state = 'arm'
cycstr = ''
if cyclecount is not None:
cycstr = ', cyclecount %d' % cyclecount
if infobyte & 0x80: # LSIP packet
self.put(self.startsample, self.prevsample, self.out_ann,
[0, ['WARN: LSIP I-Sync packet not implemented']])
return [0, ['I-Sync: %s, PC 0x%08x, %s state%s' % \
(reason, addr, self.cpu_state, cycstr), \
'I-Sync: %s 0x%08x' % (reason, addr)]]
def handle_trigger(self, buf):
return [0, ['Trigger event', 'Trigger']]
def handle_p_header(self, buf):
# Only non cycle-accurate mode supported.
if buf[0] & 0x83 == 0x80:
n = (buf[0] >> 6) & 1
e = (buf[0] >> 2) & 15
self.instructions_executed([1] * e + [0] * n)
if n:
return [3, ['%d instructions executed, %d skipped due to ' \
'condition codes' % (e, n),
'%d ins exec, %d skipped' % (e, n),
'%dE,%dN' % (e, n)]]
else:
return [3, ['%d instructions executed' % e,
'%d ins exec' % e, '%dE' % e]]
elif buf[0] & 0xF3 == 0x82:
i1 = (buf[0] >> 3) & 1
i2 = (buf[0] >> 2) & 1
self.instructions_executed([not i1, not i2])
txt1 = ('executed', 'skipped')
txt2 = ('E', 'S')
return [3, ['Instruction 1 %s, instruction 2 %s' % (txt1[i1], txt1[i2]),
'I1 %s, I2 %s' % (txt2[i1], txt2[i2]),
'%s,%s' % (txt2[i1], txt2[i2])]]
else:
return self.fallback(buf)
def handle_branch(self, buf):
if buf[-1] & 0x80 != 0x00:
return None # Not complete yet.
brinfo = parse_branch_addr(buf, self.last_branch, self.cpu_state,
self.options['branch_enc'])
if brinfo is None:
return None # Not complete yet.
addr, addrlen, cpu_state, exc_info = brinfo
self.last_branch = addr
self.current_pc = addr
txt = ''
if cpu_state != self.cpu_state:
txt += ', to %s state' % cpu_state
self.cpu_state = cpu_state
annidx = 1
if exc_info:
annidx = 2
ns, exc, cancel, altisa, hyp, resume = exc_info
if ns:
txt += ', to non-secure state'
if exc:
if exc < len(exc_names):
txt += ', exception %s' % exc_names[exc]
else:
txt += ', exception 0x%02x' % exc
if cancel:
txt += ', instr cancelled'
if altisa:
txt += ', to AltISA'
if hyp:
txt += ', to hypervisor'
if resume:
txt += ', instr resume 0x%02x' % resume
return [annidx, ['Branch to 0x%08x%s' % (addr, txt),
'B 0x%08x%s' % (addr, txt)]]
def decode(self, ss, es, data):
ptype, rxtx, pdata = data
if ptype != 'DATA':
return
# Reset packet if there is a long pause between bytes.
# This helps getting the initial synchronization.
self.byte_len = es - ss
if ss - self.prevsample > 16 * self.byte_len:
self.flush_current_loc()
self.flush_current_func()
self.buf = []
self.prevsample = es
self.buf.append(pdata[0])
# Store the start time of the packet.
if len(self.buf) == 1:
self.startsample = ss
# Keep separate buffer for detection of sync packets.
# Sync packets override everything else, so that we can regain sync
# even if some packets are corrupted.
self.syncbuf = self.syncbuf[-4:] + [pdata[0]]
if self.syncbuf == [0x00, 0x00, 0x00, 0x00, 0x80]:
self.buf = self.syncbuf
self.syncbuf = []
# See if it is ready to be decoded.
ptype = self.get_packet_type(self.buf[0])
if hasattr(self, 'handle_' + ptype):
func = getattr(self, 'handle_' + ptype)
data = func(self.buf)
else:
data = self.fallback(self.buf)
if data is not None:
if data:
self.put(self.startsample, es, self.out_ann, data)
self.buf = []

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'uart' or 'arm_tpiu' PD and decodes the
ARM Cortex-M processor trace data from Instrumentation Trace Macroblock.
'''
from .pd import Decoder

View File

@@ -0,0 +1,373 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
import string
import subprocess
import re
ARM_EXCEPTIONS = {
0: 'Thread',
1: 'Reset',
2: 'NMI',
3: 'HardFault',
4: 'MemManage',
5: 'BusFault',
6: 'UsageFault',
11: 'SVCall',
12: 'Debug Monitor',
14: 'PendSV',
15: 'SysTick',
}
class Decoder(srd.Decoder):
api_version = 3
id = 'arm_itm'
name = 'ARM ITM'
longname = 'ARM Instrumentation Trace Macroblock'
desc = 'ARM Cortex-M / ARMv7m ITM trace protocol.'
license = 'gplv2+'
inputs = ['uart']
outputs = []
tags = ['Debug/trace']
options = (
{'id': 'objdump', 'desc': 'objdump path',
'default': 'arm-none-eabi-objdump'},
{'id': 'objdump_opts', 'desc': 'objdump options',
'default': '-lSC'},
{'id': 'elffile', 'desc': '.elf path',
'default': ''},
)
annotations = (
('trace', 'Trace information'),
('timestamp', 'Timestamp'),
('software', 'Software message'),
('dwt_event', 'DWT event'),
('dwt_watchpoint', 'DWT watchpoint'),
('dwt_exc', 'Exception trace'),
('dwt_pc', 'Program counter'),
('mode_thread', 'Current mode: thread'),
('mode_irq', 'Current mode: IRQ'),
('mode_exc', 'Current mode: Exception'),
('location', 'Current location'),
('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,)),
)
def __init__(self):
self.reset()
def reset(self):
self.buf = []
self.syncbuf = []
self.swpackets = {}
self.prevsample = 0
self.dwt_timestamp = 0
self.current_mode = None
self.file_lookup = {}
self.func_lookup = {}
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.load_objdump()
def load_objdump(self):
'''Parse disassembly obtained from objdump into a lookup tables'''
if not (self.options['objdump'] and self.options['elffile']):
return
opts = [self.options['objdump']]
opts += self.options['objdump_opts'].split()
opts += [self.options['elffile']]
try:
disasm = subprocess.check_output(opts)
except subprocess.CalledProcessError:
return
disasm = disasm.decode('utf-8', 'replace')
instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
prev_file = ''
prev_func = ''
for line in disasm.split('\n'):
m = instpat.match(line)
if m:
addr = int(m.group(1), 16)
self.file_lookup[addr] = prev_file
self.func_lookup[addr] = prev_func
else:
m = funcpat.match(line)
if m:
prev_func = m.group(1)
else:
m = filepat.match(line)
if m:
prev_file = m.group(1)
def get_packet_type(self, byte):
'''Identify packet type based on its first byte.
See ARMv7-M_ARM.pdf section "Debug ITM and DWT" "Packet Types"
'''
if byte & 0x7F == 0:
return 'sync'
elif byte == 0x70:
return 'overflow'
elif byte & 0x0F == 0 and byte & 0xF0 != 0:
return 'timestamp'
elif byte & 0x0F == 0x08:
return 'sw_extension'
elif byte & 0x0F == 0x0C:
return 'hw_extension'
elif byte & 0x0F == 0x04:
return 'reserved'
elif byte & 0x04 == 0x00:
return 'software'
else:
return 'hardware'
def mode_change(self, new_mode):
if self.current_mode is not None:
start, mode = self.current_mode
if mode.startswith('Thread'):
ann_idx = 7
elif mode.startswith('IRQ'):
ann_idx = 8
else:
ann_idx = 9
self.put(start, self.startsample, self.out_ann, [ann_idx, [mode]])
if new_mode is None:
self.current_mode = None
else:
self.current_mode = (self.startsample, new_mode)
def location_change(self, pc):
new_loc = self.file_lookup.get(pc)
new_func = self.func_lookup.get(pc)
ss = self.startsample
es = self.prevsample
if new_loc is not None:
self.put(ss, es, self.out_ann, [10, [new_loc]])
if new_func is not None:
self.put(ss, es, self.out_ann, [11, [new_func]])
def fallback(self, buf):
ptype = self.get_packet_type(buf[0])
return [0, [('Unhandled %s: ' % ptype) + ' '.join(['%02x' % b for b in buf])]]
def handle_overflow(self, buf):
return [0, ['Overflow']]
def handle_hardware(self, buf):
'''Handle packets from hardware source, i.e. DWT block.'''
plen = (0, 1, 2, 4)[buf[0] & 0x03]
pid = buf[0] >> 3
if len(buf) != plen + 1:
return None # Not complete yet.
if pid == 0:
text = 'DWT events:'
if buf[1] & 0x20:
text += ' Cyc'
if buf[1] & 0x10:
text += ' Fold'
if buf[1] & 0x08:
text += ' LSU'
if buf[1] & 0x04:
text += ' Sleep'
if buf[1] & 0x02:
text += ' Exc'
if buf[1] & 0x01:
text += ' CPI'
return [3, [text]]
elif pid == 1:
excnum = ((buf[2] & 1) << 8) | buf[1]
event = (buf[2] >> 4)
excstr = ARM_EXCEPTIONS.get(excnum, 'IRQ %d' % (excnum - 16))
if event == 1:
self.mode_change(excstr)
return [5, ['Enter: ' + excstr, 'E ' + excstr]]
elif event == 2:
self.mode_change(None)
return [5, ['Exit: ' + excstr, 'X ' + excstr]]
elif event == 3:
self.mode_change(excstr)
return [5, ['Resume: ' + excstr, 'R ' + excstr]]
elif pid == 2:
pc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
self.location_change(pc)
return [6, ['PC: 0x%08x' % pc]]
elif (buf[0] & 0xC4) == 0x84:
comp = (buf[0] & 0x30) >> 4
what = 'Read' if (buf[0] & 0x08) == 0 else 'Write'
if plen == 1:
data = '0x%02x' % (buf[1])
elif plen == 2:
data = '0x%04x' % (buf[1] | (buf[2] << 8))
else:
data = '0x%08x' % (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24))
return [4, ['Watchpoint %d: %s data %s' % (comp, what, data),
'WP%d: %s %s' % (comp, what[0], data)]]
elif (buf[0] & 0xCF) == 0x47:
comp = (buf[0] & 0x30) >> 4
addr = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24)
self.location_change(addr)
return [4, ['Watchpoint %d: PC 0x%08x' % (comp, addr),
'WP%d: PC 0x%08x' % (comp, addr)]]
elif (buf[0] & 0xCF) == 0x4E:
comp = (buf[0] & 0x30) >> 4
offset = buf[1] | (buf[2] << 8)
return [4, ['Watchpoint %d: address 0x????%04x' % (comp, offset),
'WP%d: A 0x%04x' % (comp, offset)]]
return self.fallback(buf)
def handle_software(self, buf):
'''Handle packets generated by software running on the CPU.'''
plen = (0, 1, 2, 4)[buf[0] & 0x03]
pid = buf[0] >> 3
if len(buf) != plen + 1:
return None # Not complete yet.
if plen == 1 and chr(buf[1]) in string.printable:
self.add_delayed_sw(pid, chr(buf[1]))
return [] # Handled but no data to output.
self.push_delayed_sw()
if plen == 1:
return [2, ['%d: 0x%02x' % (pid, buf[1])]]
elif plen == 2:
return [2, ['%d: 0x%02x%02x' % (pid, buf[2], buf[1])]]
elif plen == 4:
return [2, ['%d: 0x%02x%02x%02x%02x' % (pid, buf[4], buf[3], buf[2], buf[1])]]
def handle_timestamp(self, buf):
'''Handle timestamp packets, which indicate the time of some DWT event packet.'''
if buf[-1] & 0x80 != 0:
return None # Not complete yet.
if buf[0] & 0x80 == 0:
tc = 0
ts = buf[0] >> 4
else:
tc = (buf[0] & 0x30) >> 4
ts = buf[1] & 0x7F
if len(buf) > 2:
ts |= (buf[2] & 0x7F) << 7
if len(buf) > 3:
ts |= (buf[3] & 0x7F) << 14
if len(buf) > 4:
ts |= (buf[4] & 0x7F) << 21
self.dwt_timestamp += ts
if tc == 0:
msg = '(exact)'
elif tc == 1:
msg = '(timestamp delayed)'
elif tc == 2:
msg = '(event delayed)'
elif tc == 3:
msg = '(event and timestamp delayed)'
return [1, ['Timestamp: %d %s' % (self.dwt_timestamp, msg)]]
def add_delayed_sw(self, pid, c):
'''We join printable characters from software source so that printed
strings are easy to read. Joining is done by PID so that different
sources do not get confused with each other.'''
if self.swpackets.get(pid) is not None:
self.swpackets[pid][1] = self.prevsample
self.swpackets[pid][2] += c
else:
self.swpackets[pid] = [self.startsample, self.prevsample, c]
def push_delayed_sw(self):
for pid, packet in self.swpackets.items():
if packet is None:
continue
ss, prevtime, text = packet
# Heuristic criterion: Text has ended if at least 16 byte
# durations after previous received byte. Actual delay depends
# on printf implementation on target.
if self.prevsample - prevtime > 16 * self.byte_len:
self.put(ss, prevtime, self.out_ann, [2, ['%d: "%s"' % (pid, text)]])
self.swpackets[pid] = None
def decode(self, ss, es, data):
ptype, rxtx, pdata = data
# For now, ignore all UART packets except the actual data packets.
if ptype != 'DATA':
return
self.byte_len = es - ss
# Reset packet if there is a long pause between bytes.
# TPIU framing can introduce small pauses, but more than 1 frame
# should reset packet.
if ss - self.prevsample > 16 * self.byte_len:
self.push_delayed_sw()
self.buf = []
self.prevsample = es
# Build up the current packet byte by byte.
self.buf.append(pdata[0])
# Store the start time of the packet.
if len(self.buf) == 1:
self.startsample = ss
# Keep separate buffer for detection of sync packets.
# Sync packets override everything else, so that we can regain sync
# even if some packets are corrupted.
self.syncbuf = self.syncbuf[-5:] + [pdata[0]]
if self.syncbuf == [0, 0, 0, 0, 0, 0x80]:
self.buf = self.syncbuf
# See if it is ready to be decoded.
ptype = self.get_packet_type(self.buf[0])
if hasattr(self, 'handle_' + ptype):
func = getattr(self, 'handle_' + ptype)
data = func(self.buf)
else:
data = self.fallback(self.buf)
if data is not None:
if data:
self.put(self.startsample, es, self.out_ann, data)
self.buf = []

View File

@@ -0,0 +1,28 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'uart' PD and decodes the frame format
of ARMv7m Trace Port Interface Unit.
It filters the data coming from various trace sources (such as ARMv7m ITM
and ETM blocks) into separate streams that can be further decoded by other PDs.
'''
from .pd import Decoder

View File

@@ -0,0 +1,131 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
class Decoder(srd.Decoder):
api_version = 3
id = 'arm_tpiu'
name = 'ARM TPIU'
longname = 'ARM Trace Port Interface Unit'
desc = 'Filter TPIU formatted trace data into separate streams.'
license = 'gplv2+'
inputs = ['uart']
outputs = ['uart'] # Emulate uart output so that arm_itm/arm_etm can stack.
tags = ['Debug/trace']
options = (
{'id': 'stream', 'desc': 'Stream index', 'default': 1},
{'id': 'sync_offset', 'desc': 'Initial sync offset', 'default': 0},
)
annotations = (
('stream', 'Current stream'),
('data', 'Stream data'),
)
annotation_rows = (
('stream', 'Current stream', (0,)),
('data', 'Stream data', (1,)),
)
def __init__(self):
self.reset()
def reset(self):
self.buf = []
self.syncbuf = []
self.prevsample = 0
self.stream = 0
self.ss_stream = None
self.bytenum = 0
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_python = self.register(srd.OUTPUT_PYTHON)
def stream_changed(self, ss, stream):
if self.stream != stream:
if self.stream != 0:
self.put(self.ss_stream, ss, self.out_ann,
[0, ['Stream %d' % self.stream, 'S%d' % self.stream]])
self.stream = stream
self.ss_stream = ss
def emit_byte(self, ss, es, byte):
if self.stream == self.options['stream']:
self.put(ss, es, self.out_ann, [1, ['0x%02x' % byte]])
self.put(ss, es, self.out_python, ['DATA', 0, (byte, [])])
def process_frame(self, buf):
# Byte 15 contains the lowest bits of bytes 0, 2, ... 14.
lowbits = buf[15][2]
for i in range(0, 15, 2):
# Odd bytes can be stream ID or data.
delayed_stream_change = None
lowbit = (lowbits >> (i // 2)) & 0x01
if buf[i][2] & 0x01 != 0:
if lowbit:
delayed_stream_change = buf[i][2] >> 1
else:
self.stream_changed(buf[i][0], buf[i][2] >> 1)
else:
byte = buf[i][2] | lowbit
self.emit_byte(buf[i][0], buf[i][1], byte)
# Even bytes are always data.
if i < 14:
self.emit_byte(buf[i+1][0], buf[i+1][1], buf[i+1][2])
# The stream change can be delayed to occur after the data byte.
if delayed_stream_change is not None:
self.stream_changed(buf[i+1][1], delayed_stream_change)
def decode(self, ss, es, data):
ptype, rxtx, pdata = data
if ptype != 'DATA':
return
# Reset packet if there is a long pause between bytes.
self.byte_len = es - ss
if ss - self.prevsample > self.byte_len:
self.buf = []
self.prevsample = es
self.buf.append((ss, es, pdata[0]))
self.bytenum += 1
# Allow skipping N first bytes of the data. By adjusting the sync
# value, one can get initial synchronization as soon as the trace
# starts.
if self.bytenum < self.options['sync_offset']:
self.buf = []
return
# Keep separate buffer for detection of sync packets.
# Sync packets override everything else, so that we can regain sync
# even if some packets are corrupted.
self.syncbuf = self.syncbuf[-3:] + [pdata[0]]
if self.syncbuf == [0xFF, 0xFF, 0xFF, 0x7F]:
self.buf = []
self.syncbuf = []
return
if len(self.buf) == 16:
self.process_frame(self.buf)
self.buf = []

View File

@@ -0,0 +1,30 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Michalis Pappas <mpappas@fastmail.fm>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'i2c' PD and decodes the
Microchip ATSHA204A and ATECC508A crypto authentication protocol.
The decoder might also support the following devices (untested):
* ATSHA204
* ATECC108
* ATECC108A
'''
from .pd import Decoder

View File

@@ -0,0 +1,323 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Michalis Pappas <mpappas@fastmail.fm>
##
## 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 <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
WORD_ADDR_RESET = 0x00
WORD_ADDR_SLEEP = 0x01
WORD_ADDR_IDLE = 0x02
WORD_ADDR_COMMAND = 0x03
WORD_ADDR = {0x00: 'RESET', 0x01: 'SLEEP', 0x02: 'IDLE', 0x03: 'COMMAND'}
OPCODE_COUNTER = 0x24
OPCODE_DERIVE_KEY = 0x1c
OPCODE_DEV_REV = 0x30
OPCODE_ECDH = 0x43
OPCODE_GEN_DIG = 0x15
OPCODE_GEN_KEY = 0x40
OPCODE_HMAC = 0x11
OPCODE_CHECK_MAC = 0x28
OPCODE_LOCK = 0x17
OPCODE_MAC = 0x08
OPCODE_NONCE = 0x16
OPCODE_PAUSE = 0x01
OPCODE_PRIVWRITE = 0x46
OPCODE_RANDOM = 0x1b
OPCODE_READ = 0x02
OPCODE_SHA = 0x47
OPCODE_SIGN = 0x41
OPCODE_UPDATE_EXTRA = 0x20
OPCODE_VERIFY = 0x45
OPCODE_WRITE = 0x12
OPCODES = {
0x01: 'Pause',
0x02: 'Read',
0x08: 'MAC',
0x11: 'HMAC',
0x12: 'Write',
0x15: 'GenDig',
0x16: 'Nonce',
0x17: 'Lock',
0x1b: 'Random',
0x1c: 'DeriveKey',
0x20: 'UpdateExtra',
0x24: 'Counter',
0x28: 'CheckMac',
0x30: 'DevRev',
0x40: 'GenKey',
0x41: 'Sign',
0x43: 'ECDH',
0x45: 'Verify',
0x46: 'PrivWrite',
0x47: 'SHA',
}
ZONE_CONFIG = 0x00
ZONE_OTP = 0x01
ZONE_DATA = 0x02
ZONES = {0x00: 'CONFIG', 0x01: 'OTP', 0x02: 'DATA'}
STATUS_SUCCESS = 0x00
STATUS_CHECKMAC_FAIL = 0x01
STATUS_PARSE_ERROR = 0x03
STATUS_EXECUTION_ERROR = 0x0f
STATUS_READY = 0x11
STATUS_CRC_COMM_ERROR = 0xff
STATUS = {
0x00: 'Command success',
0x01: 'Checkmac failure',
0x03: 'Parse error',
0x0f: 'Execution error',
0x11: 'Ready',
0xff: 'CRC / communications error',
}
class Decoder(srd.Decoder):
api_version = 3
id = 'atsha204a'
name = 'ATSHA204A'
longname = 'Microchip ATSHA204A'
desc = 'Microchip ATSHA204A family crypto authentication protocol.'
license = 'gplv2+'
inputs = ['i2c']
outputs = []
tags = ['Security/crypto', 'IC', 'Memory']
annotations = (
('waddr', 'Word address'),
('count', 'Count'),
('opcode', 'Opcode'),
('param1', 'Param1'),
('param2', 'Param2'),
('data', 'Data'),
('crc', 'CRC'),
('status', 'Status'),
('warning', 'Warning'),
)
annotation_rows = (
('frame', 'Frame', (0, 1, 2, 3, 4, 5, 6)),
('status', 'Status', (7,)),
('warnings', 'Warnings', (8,)),
)
def __init__(self):
self.reset()
def reset(self):
self.state = 'IDLE'
self.waddr = self.opcode = -1
self.ss_block = self.es_block = 0
self.bytes = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def output_tx_bytes(self):
b = self.bytes
if len(b) < 1: # Ignore wakeup.
return
self.waddr = b[0][2]
self.put_waddr(b[0])
if self.waddr == WORD_ADDR_COMMAND:
count = b[1][2]
self.put_count(b[1])
if len(b) - 1 != count:
self.put_warning(b[0][0], b[-1][1],
'Invalid frame length: Got {}, expecting {} '.format(
len(b) - 1, count))
return
self.opcode = b[2][2]
self.put_opcode(b[2])
self.put_param1(b[3])
self.put_param2([b[4], b[5]])
self.put_data(b[6:-2])
self.put_crc([b[-2], b[-1]])
def output_rx_bytes(self):
b = self.bytes
count = b[0][2]
self.put_count(b[0])
if self.waddr == WORD_ADDR_RESET:
self.put_data([b[1]])
self.put_crc([b[2], b[3]])
self.put_status(b[0][0], b[-1][1], b[1][2])
elif self.waddr == WORD_ADDR_COMMAND:
if count == 4: # Status / Error.
self.put_data([b[1]])
self.put_crc([b[2], b[3]])
self.put_status(b[0][0], b[-1][1], b[1][2])
else:
self.put_data(b[1:-2])
self.put_crc([b[-2], b[-1]])
def putx(self, s, data):
self.put(s[0], s[1], self.out_ann, data)
def puty(self, s, data):
self.put(s[0][0], s[1][1], self.out_ann, data)
def putz(self, ss, es, data):
self.put(ss, es, self.out_ann, data)
def put_waddr(self, s):
self.putx(s, [0, ['Word addr: %s' % WORD_ADDR[s[2]]]])
def put_count(self, s):
self.putx(s, [1, ['Count: %s' % s[2]]])
def put_opcode(self, s):
self.putx(s, [2, ['Opcode: %s' % OPCODES[s[2]]]])
def put_param1(self, s):
op = self.opcode
if op in (OPCODE_CHECK_MAC, OPCODE_COUNTER, OPCODE_DEV_REV, \
OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_HMAC, OPCODE_MAC, \
OPCODE_NONCE, OPCODE_RANDOM, OPCODE_SHA, OPCODE_SIGN, \
OPCODE_VERIFY):
self.putx(s, [3, ['Mode: %02X' % s[2]]])
elif op == OPCODE_DERIVE_KEY:
self.putx(s, [3, ['Random: %s' % s[2]]])
elif op == OPCODE_PRIVWRITE:
self.putx(s, [3, ['Encrypted: {}'.format('Yes' if s[2] & 0x40 else 'No')]])
elif op == OPCODE_GEN_DIG:
self.putx(s, [3, ['Zone: %s' % ZONES[s[2]]]])
elif op == OPCODE_LOCK:
self.putx(s, [3, ['Zone: {}, Summary: {}'.format(
'DATA/OTP' if s[2] else 'CONFIG',
'Ignored' if s[2] & 0x80 else 'Used')]])
elif op == OPCODE_PAUSE:
self.putx(s, [3, ['Selector: %02X' % s[2]]])
elif op == OPCODE_READ:
self.putx(s, [3, ['Zone: {}, Length: {}'.format(ZONES[s[2] & 0x03],
'32 bytes' if s[2] & 0x90 else '4 bytes')]])
elif op == OPCODE_WRITE:
self.putx(s, [3, ['Zone: {}, Encrypted: {}, Length: {}'.format(ZONES[s[2] & 0x03],
'Yes' if s[2] & 0x40 else 'No', '32 bytes' if s[2] & 0x90 else '4 bytes')]])
else:
self.putx(s, [3, ['Param1: %02X' % s[2]]])
def put_param2(self, s):
op = self.opcode
if op == OPCODE_DERIVE_KEY:
self.puty(s, [4, ['TargetKey: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
elif op in (OPCODE_COUNTER, OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_PRIVWRITE, \
OPCODE_SIGN, OPCODE_VERIFY):
self.puty(s, [4, ['KeyID: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
elif op in (OPCODE_NONCE, OPCODE_PAUSE, OPCODE_RANDOM):
self.puty(s, [4, ['Zero: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
elif op in (OPCODE_HMAC, OPCODE_MAC, OPCODE_CHECK_MAC, OPCODE_GEN_DIG):
self.puty(s, [4, ['SlotID: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
elif op == OPCODE_LOCK:
self.puty(s, [4, ['Summary: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
elif op in (OPCODE_READ, OPCODE_WRITE):
self.puty(s, [4, ['Address: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
elif op == OPCODE_UPDATE_EXTRA:
self.puty(s, [4, ['NewValue: {:02x}'.format(s[0][2])]])
else:
self.puty(s, [4, ['-']])
def put_data(self, s):
if len(s) == 0:
return
op = self.opcode
if op == OPCODE_CHECK_MAC:
self.putz(s[0][0], s[31][1], [5, ['ClientChal: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
self.putz(s[32][0], s[63][1], [5, ['ClientResp: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
self.putz(s[64][0], s[76][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:77])]])
elif op == OPCODE_DERIVE_KEY:
self.putz(s[0][0], s[31][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
elif op == OPCODE_ECDH:
self.putz(s[0][0], s[31][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
self.putz(s[32][0], s[63][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
elif op in (OPCODE_GEN_DIG, OPCODE_GEN_KEY):
self.putz(s[0][0], s[3][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s)]])
elif op == OPCODE_MAC:
self.putz(s[0][0], s[31][1], [5, ['Challenge: %s' % ' '.join(format(i[2], '02x') for i in s)]])
elif op == OPCODE_PRIVWRITE:
if len(s) > 36: # Key + MAC.
self.putz(s[0][0], s[-35][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
self.putz(s[-32][0], s[-1][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
else: # Just value.
self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
elif op == OPCODE_VERIFY:
if len(s) >= 64: # ECDSA components (always present)
self.putz(s[0][0], s[31][1], [5, ['ECDSA R: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
self.putz(s[32][0], s[63][1], [5, ['ECDSA S: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
if len(s) == 83: # OtherData (follow ECDSA components in validate / invalidate mode)
self.putz(s[64][0], s[82][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:83])]])
if len(s) == 128: # Public key components (follow ECDSA components in external mode)
self.putz(s[64][0], s[95][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[64:96])]])
self.putz(s[96][0], s[127][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[96:128])]])
elif op == OPCODE_WRITE:
if len(s) > 32: # Value + MAC.
self.putz(s[0][0], s[-31][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
self.putz(s[-32][0], s[-1][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
else: # Just value.
self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
else:
self.putz(s[0][0], s[-1][1], [5, ['Data: %s' % ' '.join(format(i[2], '02x') for i in s)]])
def put_crc(self, s):
self.puty(s, [6, ['CRC: {:02X} {:02X}'.format(s[0][2], s[1][2])]])
def put_status(self, ss, es, status):
self.putz(ss, es, [7, ['Status: %s' % STATUS[status]]])
def put_warning(self, ss, es, msg):
self.putz(ss, es, [8, ['Warning: %s' % msg]])
def decode(self, ss, es, data):
cmd, databyte = data
# State machine.
if self.state == 'IDLE':
# Wait for an I²C START condition.
if cmd != 'START':
return
self.state = 'GET SLAVE ADDR'
self.ss_block = ss
elif self.state == 'GET SLAVE ADDR':
# Wait for an address read/write operation.
if cmd == 'ADDRESS READ':
self.state = 'READ REGS'
elif cmd == 'ADDRESS WRITE':
self.state = 'WRITE REGS'
elif self.state == 'READ REGS':
if cmd == 'DATA READ':
self.bytes.append([ss, es, databyte])
elif cmd == 'STOP':
self.es_block = es
# Reset the opcode before received data, as this causes
# responses to be displayed incorrectly.
self.opcode = -1
if len(self.bytes) > 0:
self.output_rx_bytes()
self.waddr = -1
self.bytes = []
self.state = 'IDLE'
elif self.state == 'WRITE REGS':
if cmd == 'DATA WRITE':
self.bytes.append([ss, es, databyte])
elif cmd == 'STOP':
self.es_block = es
self.output_tx_bytes()
self.bytes = []
self.state = 'IDLE'

View File

@@ -0,0 +1,31 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 fenugrec <fenugrec users.sourceforge.net>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
This protocol decoder decodes the AUD (Advanced User Debugger) interface
of certain Renesas / Hitachi microcontrollers, when set in Branch Trace mode.
AUD has two modes, this PD currently only supports "Branch Trace" mode.
Details:
http://www.renesas.eu/products/mpumcu/superh/sh7050/sh7058/Documentation.jsp
("rej09b0046 - SH7058 Hardware manual")
'''
from .pd import Decoder

View File

@@ -0,0 +1,107 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 fenugrec <fenugrec users.sourceforge.net>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# TODO:
# - Annotations are very crude and could be improved.
# - Annotate every nibble? Would give insight on interrupted shifts.
# - Annotate invalid "command" nibbles while SYNC==1?
import sigrokdecode as srd
class Decoder(srd.Decoder):
api_version = 3
id = 'aud'
name = 'AUD'
longname = 'Advanced User Debugger'
desc = 'Renesas/Hitachi Advanced User Debugger (AUD) protocol.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Debug/trace']
channels = (
{'id': 'audck', 'name': 'AUDCK', 'desc': 'AUD clock'},
{'id': 'naudsync', 'name': 'nAUDSYNC', 'desc': 'AUD sync'},
{'id': 'audata3', 'name': 'AUDATA3', 'desc': 'AUD data line 3'},
{'id': 'audata2', 'name': 'AUDATA2', 'desc': 'AUD data line 2'},
{'id': 'audata1', 'name': 'AUDATA1', 'desc': 'AUD data line 1'},
{'id': 'audata0', 'name': 'AUDATA0', 'desc': 'AUD data line 0'},
)
annotations = (
('dest', 'Destination address'),
)
def __init__(self):
self.reset()
def reset(self):
self.ncnt = 0
self.nmax = 0
self.addr = 0
self.lastaddr = 0
self.ss = 0
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss, self.samplenum, self.out_ann, data)
def handle_clk_edge(self, clk, sync, datapins):
# Reconstruct nibble.
nib = 0
for i in range(4):
nib |= datapins[3-i] << i
# sync == 1: annotate if finished; update cmd.
# TODO: Annotate idle level (nibble = 0x03 && SYNC=1).
if sync == 1:
if (self.ncnt == self.nmax) and (self.nmax != 0):
# Done shifting an address: annotate.
self.putx([0, ['0x%08X' % self.addr]])
self.lastaddr = self.addr
self.ncnt = 0
self.addr = self.lastaddr
self.ss = self.samplenum
if nib == 0x08:
self.nmax = 1
elif nib == 0x09:
self.nmax = 2
elif nib == 0x0a:
self.nmax = 4
elif nib == 0x0b:
self.nmax = 8
else:
# Undefined or idle.
self.nmax = 0
else:
# sync == 0, valid cmd: start or continue shifting in nibbles.
if (self.nmax > 0):
# Clear tgt nibble.
self.addr &= ~(0x0F << (self.ncnt * 4))
# Set nibble.
self.addr |= nib << (self.ncnt * 4)
self.ncnt += 1
def decode(self):
while True:
(clk, sync, d3, d2, d1, d0) = self.wait({0: 'r'})
d = (d3, d2, d1, d0)
self.handle_clk_edge(clk, sync, d)

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'spi' PD and decodes the In-System
Programming (ISP) protocol of some Atmel AVR 8-bit microcontrollers.
'''
from .pd import Decoder

View File

@@ -0,0 +1,41 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# Device code addresses:
# 0x00: vendor code, 0x01: part family + flash size, 0x02: part number
# Vendor code
vendor_code = {
0x1e: 'Atmel',
0x00: 'Device locked',
}
# (Part family + flash size, part number)
part = {
(0x90, 0x01): 'AT90S1200',
(0x91, 0x01): 'AT90S2313',
(0x92, 0x01): 'AT90S4414',
(0x92, 0x05): 'ATmega48', # 4kB flash
(0x93, 0x01): 'AT90S8515',
(0x93, 0x0a): 'ATmega88', # 8kB flash
(0x94, 0x06): 'ATmega168', # 16kB flash
(0xff, 0xff): 'Device code erased, or target missing',
(0x01, 0x02): 'Device locked',
# TODO: Lots more entries.
}

View File

@@ -0,0 +1,213 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from .parts import *
VENDOR_CODE_ATMEL = 0x1e
class Decoder(srd.Decoder):
api_version = 3
id = 'avr_isp'
name = 'AVR ISP'
longname = 'AVR In-System Programming'
desc = 'Atmel AVR In-System Programming (ISP) protocol.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
tags = ['Debug/trace']
annotations = (
('pe', 'Programming enable'),
('rsb0', 'Read signature byte 0'),
('rsb1', 'Read signature byte 1'),
('rsb2', 'Read signature byte 2'),
('ce', 'Chip erase'),
('rfb', 'Read fuse bits'),
('rhfb', 'Read high fuse bits'),
('refb', 'Read extended fuse bits'),
('warnings', 'Warnings'),
('dev', 'Device'),
)
annotation_rows = (
('bits', 'Bits', ()),
('commands', 'Commands', tuple(range(7 + 1))),
('warnings', 'Warnings', (8,)),
('dev', 'Device', (9,)),
)
def __init__(self):
self.reset()
def reset(self):
self.state = 'IDLE'
self.mosi_bytes, self.miso_bytes = [], []
self.ss_cmd, self.es_cmd = 0, 0
self.xx, self.yy, self.zz, self.mm = 0, 0, 0, 0
self.ss_device = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
def 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']])
# Sanity check on reply.
if ret[1:4] != [0xac, 0x53, cmd[2]]:
self.putx([8, ['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)]])
# Store for later.
self.xx = cmd[1] # Same as ret[2].
self.yy = cmd[3]
self.zz = ret[0]
# Sanity check on reply.
if ret[1] != 0x30 or ret[2] != cmd[1]:
self.putx([8, ['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)!']])
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]]])
# Store for later.
self.mm = cmd[3]
self.ss_device = self.ss_cmd
# 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!']])
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]]])
p = part[(self.part_fam_flash_size, self.part_number)]
data = [9, ['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.xx, self.yy, self.zz, self.mm = 0, 0, 0, 0
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']])
# 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!']])
def handle_cmd_read_fuse_bits(self, cmd, ret):
# Read fuse bits.
self.putx([5, ['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]]])
# 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]]])
# TODO: Decode fuse bits.
# TODO: Sanity check on reply.
def handle_command(self, cmd, ret):
if cmd[:2] == [0xac, 0x53]:
self.handle_cmd_programming_enable(cmd, ret)
elif cmd[0] == 0xac and (cmd[1] & (1 << 7)) == (1 << 7):
self.handle_cmd_chip_erase(cmd, ret)
elif cmd[:3] == [0x50, 0x00, 0x00]:
self.handle_cmd_read_fuse_bits(cmd, ret)
elif cmd[:3] == [0x58, 0x08, 0x00]:
self.handle_cmd_read_fuse_high_bits(cmd, ret)
elif cmd[:3] == [0x50, 0x08, 0x00]:
self.handle_cmd_read_extended_fuse_bits(cmd, ret)
elif cmd[0] == 0x30 and cmd[2] == 0x00:
self.handle_cmd_read_signature_byte_0x00(cmd, ret)
elif cmd[0] == 0x30 and cmd[2] == 0x01:
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)
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)]])
def decode(self, ss, es, data):
ptype, mosi, miso = data
# For now, only use DATA and BITS packets.
if ptype not in ('DATA', 'BITS'):
return
# Store the individual bit values and ss/es numbers. The next packet
# is guaranteed to be a 'DATA' packet belonging to this 'BITS' one.
if ptype == 'BITS':
self.miso_bits, self.mosi_bits = miso, mosi
return
self.ss, self.es = ss, es
if len(self.mosi_bytes) == 0:
self.ss_cmd = ss
# Append new bytes.
self.mosi_bytes.append(mosi)
self.miso_bytes.append(miso)
# All commands consist of 4 bytes.
if len(self.mosi_bytes) < 4:
return
self.es_cmd = es
self.handle_command(self.mosi_bytes, self.miso_bytes)
self.mosi_bytes = []
self.miso_bytes = []

View File

@@ -0,0 +1,42 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Gerhard Sittig <gerhard.sittig@gmx.net>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
PDI (Program and Debug Interface) is an Atmel proprietary interface for
external programming and on-chip debugging of the device.
See the Atmel Application Note AVR1612 "PDI programming driver" and the
"Program and Debug Interface" section in the Xmega A manual for details.
The protocol uses two pins: the RESET pin and one dedicated DATA pin.
The RESET pin provides a clock, the DATA pin communicates serial frames
with a start bit, eight data bits, an even parity bit, and two stop bits.
Data communication is bidirectional and half duplex, the device will
provide response data after reception of a respective request.
Protocol frames communicate opcodes and their arguments, which provides
random and sequential access to the device's address space. By accessing
the registers of internal peripherals, especially the NVM controller,
it's possible to identify the device, read from and write to several
kinds of memory (signature rows, fuses and lock bits, internal flash and
EEPROM, memory mapped peripherals), and to control execution of software
on the device.
'''
from .pd import Decoder

View File

@@ -0,0 +1,576 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2016 Gerhard Sittig <gerhard.sittig@gmx.net>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# Note the implementation details:
#
# Although the Atmel literature suggests (does not explicitly mandate,
# but shows in diagrams) that two stop bits are used in the protocol,
# the decoder loses synchronization with ATxmega generated responses
# when it expects more than one stop bit. Since the chip's hardware is
# fixed, this is not an implementation error in some programmer software.
# Since this is a protocol decoder which does not participate in the
# communication (does not actively send data), we can read the data
# stream with one stop bit, and transparently keep working when two
# are used.
#
# Annotations in the UART fields level differ from Atmel literature.
# Wrong parity bits are referred to as "parity error". Low stop bits are
# referred to as "frame error".
#
# The PDI component in the device starts disabled. Enabling PDI
# communication is done by raising DATA and clocking RESET with a
# minimum frequency. PDI communication automatically gets disabled when
# RESET "is inactive" for a certain period of time. The specific timing
# conditions are rather fuzzy in the literature (phrased weakly), and
# are device dependent (refer to the minumum RESET pulse width). This
# protocol decoder implementation internally prepares for but currently
# does not support these enable and disable phases. On the one hand it
# avoids excess external dependencies or wrong results for legal input
# data. On the other hand the decoder works when input streams start in
# the middle of an established connection.
#
# Communication peers detect physical collisions. The decoder can't.
# Upon collisions, a peer will cease any subsequent transmission, until
# a BREAK is seen. Synchronization can get enforced by sending two BREAK
# conditions. The first will cause a collision, the second will re-enable
# the peer. The decoder has no concept of physical collisions. It stops
# the interpretation of instructions when BREAK is seen, and assumes
# that a new instruction will start after BREAK.
#
# This protocol decoder only supports PDI communication over UART frames.
# It lacks support for PDI over JTAG. This would require separation into
# multiple protocol decoder layers (UART physical, JTAG physical, PDI
# instructions, optionally device support on top of PDI. There is some
# more potential for future extensions:
# - The JTAG physical has dedicated TX and RX directions. This decoder
# only picks up communicated bytes but does not check which "line"
# they are communicated on (not applicable to half duplex UART).
# - PDI over JTAG uses "special frame error" conditions to communicate
# additional symbols: BREAK (0xBB with parity 1), DELAY (0xDB with
# parity 1), and EMPTY (0xEB with parity 1).
# - Another "device support" layer might interpret device specific
# timings, and might map addresses used in memory access operations
# to component names, or even register names and bit fields(?). It's
# quite deep a rabbithole though...
import sigrokdecode as srd
from collections import namedtuple
class Ann:
'''Annotation and binary output classes.'''
(
BIT, START, DATA, PARITY_OK, PARITY_ERR,
STOP_OK, STOP_ERR, BREAK,
OPCODE, DATA_PROG, DATA_DEV, PDI_BREAK,
ENABLE, DISABLE, COMMAND,
) = range(15)
(
BIN_BYTES,
) = range(1)
Bit = namedtuple('Bit', 'val ss es')
class PDI:
'''PDI protocol instruction opcodes, and operand formats.'''
(
OP_LDS, OP_LD, OP_STS, OP_ST,
OP_LDCS, OP_REPEAT, OP_STCS, OP_KEY,
) = range(8)
pointer_format_nice = [
'*(ptr)',
'*(ptr++)',
'ptr',
'ptr++ (rsv)',
]
pointer_format_terse = [
'*p',
'*p++',
'p',
'(rsv)',
]
ctrl_reg_name = {
0: 'status',
1: 'reset',
2: 'ctrl',
}
class Decoder(srd.Decoder):
api_version = 3
id = 'avr_pdi'
name = 'AVR PDI'
longname = 'Atmel Program and Debug Interface'
desc = 'Atmel ATxmega Program and Debug Interface (PDI) protocol.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Debug/trace']
channels = (
{'id': 'reset', 'name': 'RESET', 'desc': 'RESET / PDI_CLK'},
{'id': 'data', 'name': 'DATA', 'desc': 'PDI_DATA'},
)
annotations = (
('uart-bit', 'UART bit'),
('start-bit', 'Start bit'),
('data-bit', 'Data bit'),
('parity-ok', 'Parity OK bit'),
('parity-err', 'Parity error bit'),
('stop-ok', 'Stop OK bit'),
('stop-err', 'Stop error bit'),
('break', 'BREAK condition'),
('opcode', 'Instruction opcode'),
('data-prog', 'Programmer data'),
('data-dev', 'Device data'),
('pdi-break', 'BREAK at PDI level'),
('enable', 'Enable PDI'),
('disable', 'Disable PDI'),
('cmd-data', 'PDI command with data'),
)
annotation_rows = (
('uart_bits', 'UART bits', (Ann.BIT,)),
('uart_fields', 'UART fields', (Ann.START, Ann.DATA, Ann.PARITY_OK,
Ann.PARITY_ERR, Ann.STOP_OK, Ann.STOP_ERR, Ann.BREAK)),
('pdi_fields', 'PDI fields', (Ann.OPCODE, Ann.DATA_PROG, Ann.DATA_DEV,
Ann.PDI_BREAK)),
('pdi_cmds', 'PDI Cmds', (Ann.ENABLE, Ann.DISABLE, Ann.COMMAND)),
)
binary = (
('bytes', 'PDI protocol bytes'),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.clear_state()
def clear_state(self):
# Track bit times and bit values.
self.ss_last_fall = None
self.data_sample = None
self.ss_curr_fall = None
# Collect UART frame bits into byte values.
self.bits = []
self.zero_count = 0
self.zero_ss = None
self.break_ss = None
self.break_es = None
self.clear_insn()
def clear_insn(self):
# Collect instructions and their arguments,
# properties of the current instructions.
self.insn_rep_count = 0
self.insn_opcode = None
self.insn_wr_counts = []
self.insn_rd_counts = []
# Accumulation of data items as bytes pass by.
self.insn_dat_bytes = []
self.insn_dat_count = 0
self.insn_ss_data = None
# Next layer "commands", instructions plus operands.
self.cmd_ss = None
self.cmd_insn_parts_nice = []
self.cmd_insn_parts_terse = []
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
def put_ann_bit(self, bit_nr, ann_idx):
b = self.bits[bit_nr]
self.put(b.ss, b.es, self.out_ann, [ann_idx, [str(b.val)]])
def put_ann_data(self, bit_nr, ann_data):
b = self.bits[bit_nr]
self.put(b.ss, b.es, self.out_ann, ann_data)
def put_ann_row_val(self, ss, es, row, value):
self.put(ss, es, self.out_ann, [row, value])
def put_bin_bytes(self, ss, es, row, value):
self.put(ss, es, self.out_binary, [row, value])
def handle_byte(self, ss, es, byteval):
'''Handle a byte at the PDI protocol layer.'''
# Handle BREAK conditions, which will abort any
# potentially currently executing instruction.
is_break = byteval is None
if is_break:
self.cmd_insn_parts_nice.append('BREAK')
self.cmd_insn_parts_terse.append('BRK')
self.insn_rep_count = 0
# Will FALLTHROUGH to "end of instruction" below.
# Decode instruction opcodes and argument sizes
# from the first byte of a transaction.
if self.insn_opcode is None and not is_break:
opcode = (byteval & 0xe0) >> 5
arg30 = byteval & 0x0f
arg32 = (byteval & 0x0c) >> 2
arg10 = byteval & 0x03
self.insn_opcode = opcode
self.cmd_ss = ss
mnemonics = None
if opcode == PDI.OP_LDS:
# LDS: load data, direct addressing.
# Writes an address, reads a data item.
width_addr = arg32 + 1
width_data = arg10 + 1
self.insn_wr_counts = [width_addr]
self.insn_rd_counts = [width_data]
mnemonics = [
'Insn: LDS a{:d}, m{:d}'.format(width_addr, width_data),
'LDS a{:d}, m{:d}'.format(width_addr, width_data), 'LDS',
]
self.cmd_insn_parts_nice = ['LDS']
self.cmd_insn_parts_terse = ['LDS']
elif opcode == PDI.OP_LD:
# LD: load data, indirect addressing.
# Reads a data item, with optional repeat.
ptr_txt = PDI.pointer_format_nice[arg32]
ptr_txt_terse = PDI.pointer_format_terse[arg32]
width_data = arg10 + 1
self.insn_wr_counts = []
self.insn_rd_counts = [width_data]
if self.insn_rep_count:
self.insn_rd_counts.extend(self.insn_rep_count * [width_data])
self.insn_rep_count = 0
mnemonics = [
'Insn: LD {:s} m{:d}'.format(ptr_txt, width_data),
'LD {:s} m{:d}'.format(ptr_txt, width_data), 'LD',
]
self.cmd_insn_parts_nice = ['LD', ptr_txt]
self.cmd_insn_parts_terse = ['LD', ptr_txt_terse]
elif opcode == PDI.OP_STS:
# STS: store data, direct addressing.
# Writes an address, writes a data item.
width_addr = arg32 + 1
width_data = arg10 + 1
self.insn_wr_counts = [width_addr, width_data]
self.insn_rd_counts = []
mnemonics = [
'Insn: STS a{:d}, i{:d}'.format(width_addr, width_data),
'STS a{:d}, i{:d}'.format(width_addr, width_data), 'STS',
]
self.cmd_insn_parts_nice = ['STS']
self.cmd_insn_parts_terse = ['STS']
elif opcode == PDI.OP_ST:
# ST: store data, indirect addressing.
# Writes a data item, with optional repeat.
ptr_txt = PDI.pointer_format_nice[arg32]
ptr_txt_terse = PDI.pointer_format_terse[arg32]
width_data = arg10 + 1
self.insn_wr_counts = [width_data]
self.insn_rd_counts = []
if self.insn_rep_count:
self.insn_wr_counts.extend(self.insn_rep_count * [width_data])
self.insn_rep_count = 0
mnemonics = [
'Insn: ST {:s} i{:d}'.format(ptr_txt, width_data),
'ST {:s} i{:d}'.format(ptr_txt, width_data), 'ST',
]
self.cmd_insn_parts_nice = ['ST', ptr_txt]
self.cmd_insn_parts_terse = ['ST', ptr_txt_terse]
elif opcode == PDI.OP_LDCS:
# LDCS: load control/status.
# Loads exactly one byte.
reg_num = arg30
reg_txt = PDI.ctrl_reg_name.get(reg_num, 'r{:d}'.format(reg_num))
reg_txt_terse = '{:d}'.format(reg_num)
self.insn_wr_counts = []
self.insn_rd_counts = [1]
mnemonics = [
'Insn: LDCS {:s}, m1'.format(reg_txt),
'LDCS {:s}, m1'.format(reg_txt), 'LDCS',
]
self.cmd_insn_parts_nice = ['LDCS', reg_txt]
self.cmd_insn_parts_terse = ['LDCS', reg_txt_terse]
elif opcode == PDI.OP_STCS:
# STCS: store control/status.
# Writes exactly one byte.
reg_num = arg30
reg_txt = PDI.ctrl_reg_name.get(reg_num, 'r{:d}'.format(reg_num))
reg_txt_terse = '{:d}'.format(reg_num)
self.insn_wr_counts = [1]
self.insn_rd_counts = []
mnemonics = [
'Insn: STCS {:s}, i1'.format(reg_txt),
'STCS {:s}, i1'.format(reg_txt), 'STCS',
]
self.cmd_insn_parts_nice = ['STCS', reg_txt]
self.cmd_insn_parts_terse = ['STCS', reg_txt_terse]
elif opcode == PDI.OP_REPEAT:
# REPEAT: sets repeat count for the next instruction.
# Reads repeat count from following bytes.
width_data = arg10 + 1
self.insn_wr_counts = [width_data]
self.insn_rd_counts = []
mnemonics = [
'Insn: REPEAT i{:d}'.format(width_data),
'REPEAT i{:d}'.format(width_data), 'REP',
]
self.cmd_insn_parts_nice = ['REPEAT']
self.cmd_insn_parts_terse = ['REP']
elif opcode == PDI.OP_KEY:
# KEY: set activation key (enables PDIBUS mmap access).
# Writes a sequence of 8 bytes, fixed length.
width_data = 8
self.insn_wr_counts = [width_data]
self.insn_rd_counts = []
mnemonics = [
'Insn: KEY i{:d}'.format(width_data),
'KEY i{:d}'.format(width_data), 'KEY',
]
self.cmd_insn_parts_nice = ['KEY']
self.cmd_insn_parts_terse = ['KEY']
# Emit an annotation for the instruction opcode.
self.put_ann_row_val(ss, es, Ann.OPCODE, mnemonics)
# Prepare to write/read operands/data bytes.
self.insn_dat_bytes = []
if self.insn_wr_counts:
self.insn_dat_count = self.insn_wr_counts[0]
return
if self.insn_rd_counts:
self.insn_dat_count = self.insn_rd_counts[0]
return
# FALLTHROUGH.
# When there are no operands or data bytes to read,
# then fall through to the end of the instruction
# handling below (which emits annotations).
# Read bytes which carry operands (addresses, immediates)
# or data values for memory access.
if self.insn_dat_count and not is_break:
# Accumulate received bytes until another multi byte
# data item is complete.
if not self.insn_dat_bytes:
self.insn_ss_data = ss
self.insn_dat_bytes.append(byteval)
self.insn_dat_count -= 1
if self.insn_dat_count:
return
# Determine the data item's duration and direction,
# "consume" its length spec (to simplify later steps).
data_ss = self.insn_ss_data
data_es = es
if self.insn_wr_counts:
data_ann = Ann.DATA_PROG
data_width = self.insn_wr_counts.pop(0)
elif self.insn_rd_counts:
data_ann = Ann.DATA_DEV
data_width = self.insn_rd_counts.pop(0)
# PDI communicates multi-byte data items in little endian
# order. Get a nice textual representation of the number,
# wide and narrow for several zoom levels.
self.insn_dat_bytes.reverse()
data_txt_digits = ''.join(['{:02x}'.format(b) for b in self.insn_dat_bytes])
data_txt_hex = '0x' + data_txt_digits
data_txt_prefix = 'Data: ' + data_txt_hex
data_txts = [data_txt_prefix, data_txt_hex, data_txt_digits]
self.insn_dat_bytes = []
# Emit an annotation for the data value.
self.put_ann_row_val(data_ss, data_es, data_ann, data_txts)
# Collect detailled information which describes the whole
# command when combined (for a next layer annotation,
# spanning the complete command).
self.cmd_insn_parts_nice.append(data_txt_hex)
self.cmd_insn_parts_terse.append(data_txt_digits)
# Send out write data first until exhausted,
# then drain expected read data.
if self.insn_wr_counts:
self.insn_dat_count = self.insn_wr_counts[0]
return
if self.insn_rd_counts:
self.insn_dat_count = self.insn_rd_counts[0]
return
# FALLTHROUGH.
# When all operands and data bytes were seen,
# terminate the inspection of the instruction.
# Postprocess the instruction after its operands were seen.
cmd_es = es
cmd_txt_nice = ' '.join(self.cmd_insn_parts_nice)
cmd_txt_terse = ' '.join(self.cmd_insn_parts_terse)
cmd_txts = [cmd_txt_nice, cmd_txt_terse]
self.put_ann_row_val(self.cmd_ss, cmd_es, Ann.COMMAND, cmd_txts)
if self.insn_opcode == PDI.OP_REPEAT and not is_break:
# The last communicated data item is the repeat
# count for the next instruction (i.e. it will
# execute N+1 times when "REPEAT N" is specified).
count = int(self.cmd_insn_parts_nice[-1], 0)
self.insn_rep_count = count
# Have the state for instruction decoding cleared, but make sure
# to carry over REPEAT count specs between instructions. They
# start out as zero, will be setup by REPEAT instructions, need
# to get passed to the instruction which follows REPEAT. The
# instruction which sees a non-zero repeat count which will
# consume the counter and drop it to zero, then the counter
# remains at zero until the next REPEAT instruction.
save_rep_count = self.insn_rep_count
self.clear_insn()
self.insn_rep_count = save_rep_count
def handle_bits(self, ss, es, bitval):
'''Handle a bit at the UART layer.'''
# Concentrate annotation literals here for easier maintenance.
ann_class_text = {
Ann.START: ['Start bit', 'Start', 'S'],
Ann.PARITY_OK: ['Parity OK', 'Par OK', 'P'],
Ann.PARITY_ERR: ['Parity error', 'Par ERR', 'PE'],
Ann.STOP_OK: ['Stop bit', 'Stop', 'T'],
Ann.STOP_ERR: ['Stop bit error', 'Stop ERR', 'TE'],
Ann.BREAK: ['Break condition', 'BREAK', 'BRK'],
}
def put_uart_field(bitpos, annclass):
self.put_ann_data(bitpos, [annclass, ann_class_text[annclass]])
# The number of bits which form one UART frame. Note that
# the decoder operates with only one stop bit.
frame_bitcount = 1 + 8 + 1 + 1
# Detect adjacent runs of all-zero bits. This is meant
# to cope when BREAK conditions appear at any arbitrary
# position, it need not be "aligned" to an UART frame.
if bitval == 1:
self.zero_count = 0
elif bitval == 0:
if not self.zero_count:
self.zero_ss = ss
self.zero_count += 1
if self.zero_count == frame_bitcount:
self.break_ss = self.zero_ss
# BREAK conditions are _at_minimum_ the length of a UART frame, but
# can span an arbitrary number of bit times. Track the "end sample"
# value of the last low bit we have seen, and emit the annotation only
# after the line went idle (high) again. Pass BREAK to the upper layer
# as well. When the line is low, BREAK still is pending. When the line
# is high, the current bit cannot be START, thus return from here.
if self.break_ss is not None:
if bitval == '0':
self.break_es = es
return
self.put(self.break_ss, self.break_es, self.out_ann,
[Ann.BREAK, ann_class_text[Ann.BREAK]])
self.handle_byte(self.break_ss, self.break_es, None)
self.break_ss = None
self.break_es = None
self.bits = []
return
# Ignore high bits when waiting for START.
if not self.bits and bitval == 1:
return
# Store individual bits and their start/end sample numbers,
# until a complete frame was received.
self.bits.append(Bit(bitval, ss, es))
if len(self.bits) < frame_bitcount:
return
# Get individual fields of the UART frame.
bits_num = sum([b.val << pos for pos, b in enumerate(self.bits)])
if False:
# This logic could detect BREAK conditions which are aligned to
# UART frames. Which was obsoleted by the above detection at
# arbitrary positions. The code still can be useful to detect
# "other kinds of frame errors" which carry valid symbols for
# upper layers (the Atmel literature suggests "break", "delay",
# and "empty" symbols when PDI is communicated over different
# physical layers).
if bits_num == 0: # BREAK
self.break_ss = self.bits[0].ss
self.break_es = es
self.bits = []
return
start_bit = bits_num & 0x01; bits_num >>= 1
data_val = bits_num & 0xff; bits_num >>= 8
data_text = '{:02x}'.format(data_val)
parity_bit = bits_num & 0x01; bits_num >>= 1
stop_bit = bits_num & 0x01; bits_num >>= 1
# Check for frame errors. START _must_ have been low
# according to the above accumulation logic.
parity_ok = (bin(data_val).count('1') + parity_bit) % 2 == 0
stop_ok = stop_bit == 1
valid_frame = parity_ok and stop_ok
# Emit annotations.
for idx in range(frame_bitcount):
self.put_ann_bit(idx, Ann.BIT)
put_uart_field(0, Ann.START)
self.put(self.bits[1].ss, self.bits[8].es, self.out_ann,
[Ann.DATA, ['Data: ' + data_text, 'D: ' + data_text, data_text]])
put_uart_field(9, Ann.PARITY_OK if parity_ok else Ann.PARITY_ERR)
put_uart_field(10, Ann.STOP_OK if stop_ok else Ann.STOP_ERR)
# Emit binary data stream. Have bytes interpreted at higher layers.
if valid_frame:
byte_ss, byte_es = self.bits[0].ss, self.bits[-1].es
self.put_bin_bytes(byte_ss, byte_es, Ann.BIN_BYTES, bytes([data_val]))
self.handle_byte(byte_ss, byte_es, data_val)
# Reset internal state for the next frame.
self.bits = []
def handle_clk_edge(self, clock_pin, data_pin):
# Sample the data line on rising clock edges. Always, for TX and for
# RX bytes alike.
if clock_pin == 1:
self.data_sample = data_pin
return
# Falling clock edges are boundaries for bit slots. Inspect previously
# sampled bits on falling clock edges, when the start and end sample
# numbers were determined. Only inspect bit slots of known clock
# periods (avoid interpreting the DATA line when the "enabled" state
# has not yet been determined).
self.ss_last_fall = self.ss_curr_fall
self.ss_curr_fall = self.samplenum
if self.ss_last_fall is None:
return
# Have the past bit slot processed.
bit_ss, bit_es = self.ss_last_fall, self.ss_curr_fall
bit_val = self.data_sample
self.handle_bits(bit_ss, bit_es, bit_val)
def decode(self):
while True:
(clock_pin, data_pin) = self.wait({0: 'e'})
self.handle_clk_edge(clock_pin, data_pin)

View File

@@ -0,0 +1,36 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
This decoder interprets the digital output of cheap generic calipers
(usually made in China), and shows the measured value in millimeters
or inches.
Notice that these devices often communicate on voltage levels below
3.3V and may require additional circuitry to capture the signal.
This decoder does not work for calipers using the Digimatic protocol
(eg. Mitutoyo and similar brands).
For more information see:
http://www.shumatech.com/support/chinese_scales.htm
https://www.instructables.com/id/Reading-Digital-Callipers-with-an-Arduino-USB/
'''
from .pd import Decoder

View File

@@ -0,0 +1,146 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all
## copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
import sigrokdecode as srd
from common.srdhelper import bitpack
# Millimeters per inch.
mm_per_inch = 25.4
class Decoder(srd.Decoder):
api_version = 3
id = 'caliper'
name = 'Caliper'
longname = 'Digital calipers'
desc = 'Protocol of cheap generic digital calipers.'
license = 'mit'
inputs = ['logic']
outputs = []
channels = (
{'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'},
{'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'},
)
options = (
{'id': 'timeout_ms', 'desc': 'Packet timeout in ms, 0 to disable',
'default': 10},
{'id': 'unit', 'desc': 'Convert units', 'default': 'keep',
'values': ('keep', 'mm', 'inch')},
{'id': 'changes', 'desc': 'Changes only', 'default': 'no',
'values': ('no', 'yes')},
)
tags = ['Analog/digital', 'Sensor']
annotations = (
('measurement', 'Measurement'),
('warning', 'Warning'),
)
annotation_rows = (
('measurements', 'Measurements', (0,)),
('warnings', 'Warnings', (1,)),
)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def __init__(self):
self.reset()
def reset(self):
self.ss, self.es = 0, 0
self.number_bits = []
self.flags_bits = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putg(self, ss, es, cls, data):
self.put(ss, es, self.out_ann, [cls, data])
def decode(self):
last_sent = None
timeout_ms = self.options['timeout_ms']
want_unit = self.options['unit']
show_all = self.options['changes'] == 'no'
wait_cond = [{0: 'r'}]
if timeout_ms:
snum_per_ms = self.samplerate / 1000
timeout_snum = timeout_ms * snum_per_ms
wait_cond.append({'skip': round(timeout_snum)})
while True:
# Sample data at the rising clock edge. Optionally timeout
# 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[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, [
'timeout with {} bits in buffer'.format(count),
'timeout ({} bits)'.format(count),
'timeout',
])
self.reset()
continue
# Store position of first bit and last activity.
# Shift in measured number and flag bits.
if not self.ss:
self.ss = self.samplenum
self.es = self.samplenum
if len(self.number_bits) < 16:
self.number_bits.append(data)
continue
if len(self.flags_bits) < 8:
self.flags_bits.append(data)
if len(self.flags_bits) < 8:
continue
# Get raw values from received data bits. Run the number
# conversion, controlled by flags and/or user specs.
negative = bool(self.flags_bits[4])
is_inch = bool(self.flags_bits[7])
number = bitpack(self.number_bits)
if negative:
number = -number
if is_inch:
number /= 2000
if want_unit == 'mm':
number *= mm_per_inch
is_inch = False
else:
number /= 100
if want_unit == 'inch':
number = round(number / mm_per_inch, 4)
is_inch = True
unit = 'in' if is_inch else 'mm'
# Construct and emit an annotation.
if show_all or (number, unit) != last_sent:
self.putg(self.ss, self.es, 0, [
'{number}{unit}'.format(**locals()),
'{number}'.format(**locals()),
])
last_sent = (number, unit)
# Reset internal state for the start of the next packet.
self.reset()

View File

@@ -0,0 +1,29 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
CAN (Controller Area Network) is a field bus protocol for distributed
real-time control.
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).
'''
from .pd import Decoder

View File

@@ -0,0 +1,415 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
class SamplerateError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = 'can'
name = 'CAN'
longname = 'Controller Area Network'
desc = 'Field bus protocol for distributed realtime control.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Automotive']
channels = (
{'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'},
)
options = (
{'id': 'bitrate', 'desc': 'Bitrate (bits/s)', 'default': 1000000},
{'id': 'sample_point', 'desc': 'Sample point (%)', 'default': 70.0},
)
annotations = (
('data', 'CAN payload data'),
('sof', 'Start of frame'),
('eof', 'End of frame'),
('id', 'Identifier'),
('ext-id', 'Extended identifier'),
('full-id', 'Full identifier'),
('ide', 'Identifier extension bit'),
('reserved-bit', 'Reserved bit 0 and 1'),
('rtr', 'Remote transmission request'),
('srr', 'Substitute remote request'),
('dlc', 'Data length count'),
('crc-sequence', 'CRC sequence'),
('crc-delimiter', 'CRC delimiter'),
('ack-slot', 'ACK slot'),
('ack-delimiter', 'ACK delimiter'),
('stuff-bit', 'Stuff bit'),
('warnings', 'Human-readable warnings'),
('bit', 'Bit'),
)
annotation_rows = (
('bits', 'Bits', (15, 17)),
('fields', 'Fields', tuple(range(15))),
('warnings', 'Warnings', (16,)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.reset_variables()
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
self.bit_width = float(self.samplerate) / float(self.options['bitrate'])
self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
# Generic helper for CAN bit annotations.
def putg(self, ss, es, data):
left, right = int(self.sample_point), int(self.bit_width - self.sample_point)
self.put(ss - left, es + right, self.out_ann, data)
# Single-CAN-bit annotation using the current samplenum.
def putx(self, data):
self.putg(self.samplenum, self.samplenum, data)
# Single-CAN-bit annotation using the samplenum of CAN bit 12.
def put12(self, data):
self.putg(self.ss_bit12, self.ss_bit12, 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 reset_variables(self):
self.state = 'IDLE'
self.sof = self.frame_type = self.dlc = None
self.rawbits = [] # All bits, including stuff bits
self.bits = [] # Only actual CAN frame bits (no stuff bits)
self.curbit = 0 # Current bit of CAN frame (bit 0 == SOF)
self.last_databit = 999 # Positive value that bitnum+x will never match
self.ss_block = None
self.ss_bit12 = None
self.ss_databytebits = []
# Poor man's clock synchronization. Use signal edges which change to
# dominant state in rather simple ways. This naive approach is neither
# aware of the SYNC phase's width nor the specific location of the edge,
# but improves the decoder's reliability when the input signal's bitrate
# does not exactly match the nominal rate.
def dom_edge_seen(self, force = False):
self.dom_edge_snum = self.samplenum
self.dom_edge_bcount = self.curbit
def bit_sampled(self):
# EMPTY
pass
# Determine the position of the next desired bit's sample point.
def get_sample_point(self, bitnum):
samplenum = self.dom_edge_snum
samplenum += int(self.bit_width * (bitnum - self.dom_edge_bcount))
samplenum += int(self.sample_point)
return samplenum
def is_stuff_bit(self):
# CAN uses NRZ encoding and bit stuffing.
# After 5 identical bits, a stuff bit of opposite value is added.
# But not in the CRC delimiter, ACK, and end of frame fields.
if len(self.bits) > self.last_databit + 17:
return False
last_6_bits = self.rawbits[-6:]
if last_6_bits not in ([0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 0]):
return False
# Stuff bit. Keep it in self.rawbits, but drop it from self.bits.
self.bits.pop() # Drop last bit.
return True
def is_valid_crc(self, crc_bits):
return True # TODO
def decode_error_frame(self, bits):
pass # TODO
def decode_overload_frame(self, bits):
pass # TODO
# Both standard and extended frames end with CRC, CRC delimiter, ACK,
# ACK delimiter, and EOF fields. Handle them in a common function.
# Returns True if the frame ended (EOF), False otherwise.
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
# 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']])
if not self.is_valid_crc(crc_bits):
self.putb([16, ['CRC is invalid']])
# CRC delimiter bit (recessive)
elif bitnum == (self.last_databit + 16):
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']])
# ACK slot bit (dominant: ACK, recessive: NACK)
elif bitnum == (self.last_databit + 17):
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):
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):
self.ss_block = self.samplenum
# End of frame (EOF), 7 recessive bits
elif bitnum == (self.last_databit + 25):
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.reset_variables()
return True
return False
# 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.
if bitnum == 14:
self.putx([7, ['Reserved bit 0: %d' % can_rx,
'RB0: %d' % can_rx, 'RB0']])
# 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']])
# Remember start of DLC (see below).
elif bitnum == 15:
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)
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.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):
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)
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),
'DB %d: 0x%02x' % (i, b), 'DB']])
self.ss_databytebits = []
elif bitnum > self.last_databit:
return self.decode_frame_end(can_rx, bitnum)
return False
# Returns True if the frame ended (EOF), False otherwise.
def decode_extended_frame(self, can_rx, bitnum):
# Remember start of EID (see below).
if bitnum == 14:
self.ss_block = self.samplenum
# Bits 14-31: Extended identifier (EID[17..0])
elif bitnum == 31:
self.eid = int(''.join(str(d) for d in self.bits[14:]), 2)
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
s = '%d (0x%x)' % (self.fullid, self.fullid)
self.putb([5, ['Full Identifier: %s' % s, 'Full ID: %s' % s,
'Full ID', 'FID']])
# Bit 12: Substitute remote request (SRR) bit
self.put12([9, ['Substitute remote request: %d' % self.bits[12],
'SRR: %d' % self.bits[12], 'SRR']])
# Bit 32: Remote transmission request (RTR) bit
# Data frame: dominant, remote frame: recessive
# Remote frames do not contain a data field.
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']])
# Bit 33: RB1 (reserved bit)
elif bitnum == 33:
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']])
# Remember start of DLC (see below).
elif bitnum == 35:
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)
self.putb([10, ['Data length code: %d' % self.dlc,
'DLC: %d' % self.dlc, 'DLC']])
self.last_databit = 38 + (self.dlc * 8)
# Remember all databyte bits, except the very last one.
elif bitnum in range(39, 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)
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),
'DB %d: 0x%02x' % (i, b), 'DB']])
self.ss_databytebits = []
elif bitnum > self.last_databit:
return self.decode_frame_end(can_rx, bitnum)
return False
def handle_bit(self, can_rx):
self.rawbits.append(can_rx)
self.bits.append(can_rx)
# Get the index of the current CAN frame bit (without stuff bits).
bitnum = len(self.bits) - 1
# 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)]])
self.curbit += 1 # Increase self.curbit (bitnum is not affected).
return
else:
self.putx([17, [str(can_rx)]])
# Bit 0: Start of frame (SOF) bit
if bitnum == 0:
self.putx([1, ['Start of frame', 'SOF', 'S']])
if can_rx != 0:
self.putx([16, ['Start of frame (SOF) must be a dominant bit']])
# Remember start of ID (see below).
elif bitnum == 1:
self.ss_block = self.samplenum
# 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),
self.putb([3, ['Identifier: %s' % s, 'ID: %s' % s, 'ID']])
if (self.id & 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).
elif bitnum == 12:
# self.putx([0, ['RTR/SRR: %d' % can_rx]]) # Debug only.
self.ss_bit12 = self.samplenum
# Bit 13: Identifier extension (IDE) bit
# Standard frame: dominant, extended frame: recessive
elif bitnum == 13:
ide = self.frame_type = 'standard' if can_rx == 0 else 'extended'
self.putx([6, ['Identifier extension bit: %s frame' % ide,
'IDE: %s frame' % ide, 'IDE']])
# Bits 14-X: Frame-type dependent, passed to the resp. handlers.
elif bitnum >= 14:
if self.frame_type == 'standard':
done = self.decode_standard_frame(can_rx, bitnum)
else:
done = self.decode_extended_frame(can_rx, bitnum)
# The handlers return True if a frame ended (EOF).
if done:
return
# After a frame there are 3 intermission bits (recessive).
# After these bits, the bus is considered free.
self.curbit += 1
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
while True:
# State machine.
if self.state == 'IDLE':
# Wait for a dominant state (logic 0) on the bus.
(can_rx,) = self.wait({0: 'l'})
self.sof = self.samplenum
self.dom_edge_seen(force = True)
self.state = 'GET BITS'
elif self.state == 'GET BITS':
# Wait until we're in the correct bit/sampling position.
pos = self.get_sample_point(self.curbit)
(can_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
if (self.matched & (0b1 << 1)):
self.dom_edge_seen()
if (self.matched & (0b1 << 0)):
self.handle_bit(can_rx)
self.bit_sampled()

View File

@@ -0,0 +1,28 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'spi' PD and decodes the protocol spoken
by the Texas Instruments low-power sub-1GHz RF transceiver chips.
Details:
http://www.ti.com/lit/ds/symlink/cc1101.pdf
'''
from .pd import Decoder

View File

@@ -0,0 +1,115 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
regs = {
# addr: 'name'
0x00: 'IOCFG2',
0x01: 'IOCFG1',
0x02: 'IOCFG0',
0x03: 'FIFOTHR',
0x04: 'SYNC1',
0x05: 'SYNC0',
0x06: 'PKTLEN',
0x07: 'PKTCTRL1',
0x08: 'PKTCTRL0',
0x09: 'ADDR',
0x0A: 'CHANNR',
0x0B: 'FSCTRL1',
0x0C: 'FSCTRL0',
0x0D: 'FREQ2',
0x0E: 'FREQ1',
0x0F: 'FREQ0',
0x10: 'MDMCFG4',
0x11: 'MDMCFG3',
0x12: 'MDMCFG2',
0x13: 'MDMCFG1',
0x14: 'MDMCFG0',
0x15: 'DEVIATN',
0x16: 'MCSM2',
0x17: 'MCSM1',
0x18: 'MCSM0',
0x19: 'FOCCFG',
0x1A: 'BSCFG',
0x1B: 'AGCTRL2',
0x1C: 'AGCTRL1',
0x1D: 'AGCTRL0',
0x1E: 'WOREVT1',
0x1F: 'WOREVT0',
0x20: 'WORCTRL',
0x21: 'FREND1',
0x22: 'FREND0',
0x23: 'FSCAL3',
0x24: 'FSCAL2',
0x25: 'FSCAL1',
0x26: 'FSCAL0',
0x27: 'RCCTRL1',
0x28: 'RCCTRL0',
0x29: 'FSTEST',
0x2A: 'PTEST',
0x2B: 'AGCTEST',
0x2C: 'TEST2',
0x2D: 'TEST1',
0x2E: 'TEST0',
0x30: 'PARTNUM',
0x31: 'VERSION',
0x32: 'FREQEST',
0x33: 'LQI',
0x34: 'RSSI',
0x35: 'MARCSTATE',
0x36: 'WORTIME1',
0x37: 'WORTIME0',
0x38: 'PKTSTATUS',
0x39: 'VCO_VC_DAC',
0x3A: 'TXBYTES',
0x3B: 'RXBYTES',
0x3C: 'RCCTRL1_STATUS',
0x3D: 'RCCTRL0_STATUS',
0x3E: 'PATABLE',
0x3F: 'FIFO'
}
strobes = {
# addr: 'name'
0x30: 'SRES',
0x31: 'SFSTXON',
0x32: 'SXOFF',
0x33: 'SCAL',
0x34: 'SRX',
0x35: 'STX',
0x36: 'SIDLE',
0x37: '',
0x38: 'SWOR',
0x39: 'SPWD',
0x3A: 'SFRX',
0x3B: 'SFTX',
0x3C: 'SWORRST',
0x3D: 'SNOP'
}
status_reg_states = {
# value: 'state name'
0b000: 'IDLE',
0b001: 'RX',
0b010: 'TX',
0b011: 'FSTXON',
0b100: 'CALIBRATE',
0b101: 'SETTLING',
0b110: 'RXFIFO_OVERFLOW',
0b111: 'TXFIFO_OVERFLOW'
}

View File

@@ -0,0 +1,296 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Marco Geisler <m-sigrok@mageis.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from collections import namedtuple
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)
Pos = namedtuple('Pos', ['ss', 'es'])
Data = namedtuple('Data', ['mosi', 'miso'])
class ChannelError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = 'cc1101'
name = 'CC1101'
longname = 'Texas Instruments CC1101'
desc = 'Low-power sub-1GHz RF transceiver chip.'
license = 'gplv2+'
inputs = ['spi']
outputs = []
tags = ['IC', 'Wireless/RF']
annotations = (
('strobe', 'Command strobe'),
('single_read', 'Single register read'),
('single_write', 'Single register write'),
('burst_read', 'Burst register read'),
('burst_write', 'Burst register write'),
('status', '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,)),
)
def __init__(self):
self.reset()
def reset(self):
self.next()
self.requirements_met = True
self.cs_was_released = False
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def warn(self, pos, msg):
'''Put a warning message 'msg' at 'pos'.'''
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 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.'''
# 'True' for the first byte after CS# went low.
self.first = True
# The current command, and the minimum and maximum number
# of data bytes to follow.
self.cmd = None
self.min = 0
self.max = 0
# Used to collect the bytes after the command byte
# (and the start/end sample number).
self.mb = []
self.ss_mb = -1
self.es_mb = -1
def mosi_bytes(self):
'''Returns the collected MOSI bytes of a multi byte command.'''
return [b.mosi for b in self.mb]
def miso_bytes(self):
'''Returns the collected MISO bytes of a multi byte command.'''
return [b.miso for b in self.mb]
def decode_command(self, pos, b):
'''Decodes the command byte 'b' at position 'pos' and prepares
the decoding of the following data bytes.'''
c = self.parse_command(b)
if c is None:
self.warn(pos, 'unknown command')
return
self.cmd, self.dat, self.min, self.max = c
if self.cmd == 'Strobe':
self.putp(pos, ANN_STROBE, self.format_command())
else:
# Don't output anything now, the command is merged with
# the data bytes following it.
self.ss_mb = pos.ss
def format_command(self):
'''Returns the label for the current command.'''
if self.cmd in ('Read', 'Burst read', 'Write', 'Burst write', 'Status read'):
return self.cmd
if self.cmd == 'Strobe':
reg = strobes.get(self.dat, 'unknown strobe')
return '{} {}'.format(self.cmd, reg)
else:
return 'TODO Cmd {}'.format(self.cmd)
def parse_command(self, b):
'''Parses the command byte.
Returns a tuple consisting of:
- the name of the command
- additional data needed to dissect the following bytes
- minimum number of following bytes
- maximum number of following bytes (None for infinite)
'''
addr = b & 0x3F
if (addr < 0x30) or (addr == 0x3E) or (addr == 0x3F):
if (b & 0xC0) == 0x00:
return ('Write', addr, 1, 1)
if (b & 0xC0) == 0x40:
return ('Burst write', addr, 1, 99999)
if (b & 0xC0) == 0x80:
return ('Read', addr, 1, 1)
if (b & 0xC0) == 0xC0:
return ('Burst read', addr, 1, 99999)
else:
self.warn(pos, 'unknown address/command combination')
else:
if (b & 0x40) == 0x00:
return ('Strobe', addr, 0, 0)
if (b & 0xC0) == 0xC0:
return ('Status read', addr, 1, 99999)
else:
self.warn(pos, 'unknown address/command combination')
def decode_reg(self, pos, ann, regid, data):
'''Decodes a register.
pos -- start and end sample numbers of the register
ann -- the annotation number that is used to output the register.
regid -- may be either an integer used as a key for the 'regs'
dictionary, or a string directly containing a register name.'
data -- the register content.
'''
if type(regid) == int:
# Get the name of the register.
if regid not in regs:
self.warn(pos, 'unknown register')
return
name = '{} ({:02X})'.format(regs[regid], regid)
else:
name = regid
if regid == 'STATUS' and ann == ANN_STATUS:
label = 'Status'
self.decode_status_reg(pos, ann, data, label)
else:
if self.cmd in ('Write', 'Read', 'Status read', 'Burst read', 'Burst write'):
label = '{}: {}'.format(self.format_command(), name)
else:
label = 'Reg ({}) {}'.format(self.cmd, name)
self.decode_mb_data(pos, ann, data, label)
def decode_status_reg(self, pos, ann, data, label):
'''Decodes the data bytes 'data' of a status register at position
'pos'. The decoded data is prefixed with 'label'.'''
status = data[0]
# bit 7 --> CHIP_RDYn
if status & 0b10000000 == 0b10000000:
longtext_chiprdy = 'CHIP_RDYn is high! '
else:
longtext_chiprdy = ''
# bits 6:4 --> STATE
state = (status & 0x70) >> 4
longtext_state = 'STATE is {}, '.format(status_reg_states[state])
# bits 3:0 --> FIFO_BYTES_AVAILABLE
fifo_bytes = status & 0x0F
if self.cmd in ('Single read', 'Status read', 'Burst read'):
longtext_fifo = '{} bytes available in RX FIFO'.format(fifo_bytes)
else:
longtext_fifo = '{} bytes free in TX FIFO'.format(fifo_bytes)
text = '{} = {:02X}'.format(label, status)
longtext = ''.join([text, '; ', longtext_chiprdy, longtext_state, longtext_fifo])
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
'pos'. The decoded data is prefixed with 'label'.'''
def escape(b):
return '{:02X}'.format(b)
data = ' '.join([escape(b) for b in data])
text = '{} = {}'.format(label, data)
self.putp(pos, ann, text)
def finish_command(self, pos):
'''Decodes the remaining data bytes at position 'pos'.'''
if self.cmd == 'Write':
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())
elif self.cmd == 'Read':
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())
elif self.cmd == 'Strobe':
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())
else:
self.warn(pos, 'unhandled command')
def decode(self, ss, es, data):
if not self.requirements_met:
return
ptype, data1, data2 = data
if ptype == 'CS-CHANGE':
if data1 is None:
if data2 is None:
self.requirements_met = False
raise ChannelError('CS# pin required.')
elif data2 == 1:
self.cs_was_released = True
if data1 == 0 and data2 == 1:
# Rising edge, the complete command is transmitted, process
# the bytes that were sent after the command byte.
if self.cmd:
# Check if we got the minimum number of data bytes
# after the command byte.
if len(self.mb) < self.min:
self.warn((ss, ss), 'missing data bytes')
elif self.mb:
self.finish_command(Pos(self.ss_mb, self.es_mb))
self.next()
self.cs_was_released = True
elif ptype == 'DATA' and self.cs_was_released:
mosi, miso = data1, data2
pos = Pos(ss, es)
if miso is None or mosi is None:
self.requirements_met = False
raise ChannelError('Both MISO and MOSI pins required.')
if self.first:
self.first = False
# First MOSI byte is always the command.
self.decode_command(pos, mosi)
# First MISO byte is always the status register.
self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
else:
if not self.cmd or len(self.mb) >= self.max:
self.warn(pos, 'excess byte')
else:
# Collect the bytes after the command byte.
if self.ss_mb == -1:
self.ss_mb = ss
self.es_mb = es
self.mb.append(Data(mosi, miso))

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Jorge Solla Rubiales <jorgesolla@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
The Consumer Electronics Control (CEC) protocol allows users to command and
control devices connected through HDMI.
'''
from .pd import Decoder

View File

@@ -0,0 +1,312 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Jorge Solla Rubiales <jorgesolla@gmail.com>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from .protocoldata import *
# Pulse types
class Pulse:
INVALID, START, ZERO, ONE = range(4)
# Protocol stats
class Stat:
WAIT_START, GET_BITS, WAIT_EOM, WAIT_ACK = range(4)
# Pulse times in milliseconds
timing = {
Pulse.START: {
'low': { 'min': 3.5, 'max': 3.9 },
'total': { 'min': 4.3, 'max': 4.7 }
},
Pulse.ZERO: {
'low': { 'min': 1.3, 'max': 1.7 },
'total': { 'min': 2.05, 'max': 2.75 }
},
Pulse.ONE: {
'low': { 'min': 0.4, 'max': 0.8 },
'total': { 'min': 2.05, 'max': 2.75 }
}
}
class ChannelError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = 'cec'
name = 'CEC'
longname = 'HDMI-CEC'
desc = 'HDMI Consumer Electronics Control (CEC) protocol.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Display', 'PC']
channels = (
{'id': 'cec', 'name': 'CEC', 'desc': 'CEC bus data'},
)
annotations = (
('st', 'Start'),
('eom-0', 'End of message'),
('eom-1', 'Message continued'),
('nack', 'ACK not set'),
('ack', 'ACK set'),
('bits', 'Bits'),
('bytes', 'Bytes'),
('frames', 'Frames'),
('sections', 'Sections'),
('warnings', 'Warnings')
)
annotation_rows = (
('bits', 'Bits', (0, 1, 2, 3, 4, 5)),
('bytes', 'Bytes', (6,)),
('frames', 'Frames', (7,)),
('sections', 'Sections', (8,)),
('warnings', 'Warnings', (9,))
)
def __init__(self):
self.reset()
def precalculate(self):
# Restrict max length of ACK/NACK labels to 2 BIT pulses.
bit_time = timing[Pulse.ZERO]['total']['min'] * 2
self.max_ack_len_samples = round((bit_time / 1000) * self.samplerate)
def reset(self):
self.stat = Stat.WAIT_START
self.samplerate = None
self.fall_start = None
self.fall_end = None
self.rise = None
self.reset_frame_vars()
def reset_frame_vars(self):
self.eom = None
self.bit_count = 0
self.byte_count = 0
self.byte = 0
self.byte_start = None
self.frame_start = None
self.frame_end = None
self.is_nack = 0
self.cmd_bytes = []
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
self.precalculate()
def handle_frame(self, is_nack):
if self.fall_start is None or self.fall_end is None:
return
i = 0
string = ''
while i < len(self.cmd_bytes):
string += '{:02x}'.format(self.cmd_bytes[i]['val'])
if i != (len(self.cmd_bytes) - 1):
string += ':'
i += 1
self.put(self.frame_start, self.frame_end, self.out_ann, [7, [string]])
i = 0
operands = 0
string = ''
while i < len(self.cmd_bytes):
if i == 0: # Parse header
(src, dst) = decode_header(self.cmd_bytes[i]['val'])
string = 'HDR: ' + src + ', ' + dst
elif i == 1: # Parse opcode
string += ' | OPC: ' + opcodes.get(self.cmd_bytes[i]['val'], 'Invalid')
else: # Parse operands
if operands == 0:
string += ' | OPS: '
operands += 1
string += '0x{:02x}'.format(self.cmd_bytes[i]['val'])
if i != len(self.cmd_bytes) - 1:
string += ', '
i += 1
# Header only commands are PINGS
if i == 1:
string += ' | OPC: PING' if self.eom else ' | OPC: NONE. Aborted cmd'
# Add extra information (ack of the command from the destination)
string += ' | R: NACK' if is_nack else ' | R: ACK'
self.put(self.frame_start, self.frame_end, self.out_ann, [8, [string]])
def process(self):
zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0
total_time = ((self.fall_end - self.fall_start) / self.samplerate) * 1000.0
pulse = Pulse.INVALID
# VALIDATION: Identify pulse based on length of the low period
for key in timing:
if zero_time >= timing[key]['low']['min'] and zero_time <= timing[key]['low']['max']:
pulse = key
break
# VALIDATION: Invalid pulse
if pulse == Pulse.INVALID:
self.stat = Stat.WAIT_START
self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Invalid pulse: Wrong timing']])
return
# VALIDATION: If waiting for start, discard everything else
if self.stat == Stat.WAIT_START and pulse != Pulse.START:
self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected START: BIT found']])
return
# VALIDATION: If waiting for ACK or EOM, only BIT pulses (0/1) are expected
if (self.stat == Stat.WAIT_ACK or self.stat == Stat.WAIT_EOM) and pulse == Pulse.START:
self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected BIT: START received)']])
self.stat = Stat.WAIT_START
# VALIDATION: ACK bit pulse remains high till the next frame (if any): Validate only min time of the low period
if self.stat == Stat.WAIT_ACK and pulse != Pulse.START:
if total_time < timing[pulse]['total']['min']:
pulse = Pulse.INVALID
self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['ACK pulse below minimun time']])
self.stat = Stat.WAIT_START
return
# VALIDATION / PING FRAME DETECTION: Initiator doesn't sets the EOM = 1 but stops sending when ack doesn't arrive
if self.stat == Stat.GET_BITS and pulse == Pulse.START:
# Make sure we received a complete byte to consider it a valid ping
if self.bit_count == 0:
self.handle_frame(self.is_nack)
else:
self.put(self.frame_start, self.samplenum, self.out_ann, [9, ['ERROR: Incomplete byte received']])
# Set wait start so we receive next frame
self.stat = Stat.WAIT_START
# VALIDATION: Check timing of the BIT (0/1) pulse in any other case (not waiting for ACK)
if self.stat != Stat.WAIT_ACK and pulse != Pulse.START:
if total_time < timing[pulse]['total']['min'] or total_time > timing[pulse]['total']['max']:
self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Bit pulse exceeds total pulse timespan']])
pulse = Pulse.INVALID
self.stat = Stat.WAIT_START
return
if pulse == Pulse.ZERO:
bit = 0
elif pulse == Pulse.ONE:
bit = 1
# STATE: WAIT START
if self.stat == Stat.WAIT_START:
self.stat = Stat.GET_BITS
self.reset_frame_vars()
self.put(self.fall_start, self.fall_end, self.out_ann, [0, ['ST']])
# STATE: GET BITS
elif self.stat == Stat.GET_BITS:
# Reset stats on first bit
if self.bit_count == 0:
self.byte_start = self.fall_start
self.byte = 0
# If 1st byte of the datagram save its sample num
if len(self.cmd_bytes) == 0:
self.frame_start = self.fall_start
self.byte += (bit << (7 - self.bit_count))
self.bit_count += 1
self.put(self.fall_start, self.fall_end, self.out_ann, [5, [str(bit)]])
if self.bit_count == 8:
self.bit_count = 0
self.byte_count += 1
self.stat = Stat.WAIT_EOM
self.put(self.byte_start, self.samplenum, self.out_ann, [6, ['0x{:02x}'.format(self.byte)]])
self.cmd_bytes.append({'st': self.byte_start, 'ed': self.samplenum, 'val': self.byte})
# STATE: WAIT EOM
elif self.stat == Stat.WAIT_EOM:
self.eom = bit
self.frame_end = self.fall_end
a = [2, ['EOM=Y']] if self.eom else [1, ['EOM=N']]
self.put(self.fall_start, self.fall_end, self.out_ann, a)
self.stat = Stat.WAIT_ACK
# STATE: WAIT ACK
elif self.stat == Stat.WAIT_ACK:
# If a frame with broadcast destination is being sent, the ACK is
# inverted: a 0 is considered a NACK, therefore we invert the value
# of the bit here, so we match the real meaning of it.
if (self.cmd_bytes[0]['val'] & 0x0F) == 0x0F:
bit = ~bit & 0x01
if (self.fall_end - self.fall_start) > self.max_ack_len_samples:
ann_end = self.fall_start + self.max_ack_len_samples
else:
ann_end = self.fall_end
if bit:
# Any NACK detected in the frame is enough to consider the
# whole frame NACK'd.
self.is_nack = 1
self.put(self.fall_start, ann_end, self.out_ann, [3, ['NACK']])
else:
self.put(self.fall_start, ann_end, self.out_ann, [4, ['ACK']])
# After ACK bit, wait for new datagram or continue reading current
# one based on EOM value.
if self.eom or self.is_nack:
self.stat = Stat.WAIT_START
self.handle_frame(self.is_nack)
else:
self.stat = Stat.GET_BITS
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
# Wait for first falling edge.
self.wait({0: 'f'})
self.fall_end = self.samplenum
while True:
self.wait({0: 'r'})
self.rise = self.samplenum
if self.stat == Stat.WAIT_ACK:
self.wait([{0: 'f'}, {'skip': self.max_ack_len_samples}])
else:
self.wait([{0: 'f'}])
self.fall_start = self.fall_end
self.fall_end = self.samplenum
self.process()
# If there was a timeout while waiting for ACK: RESYNC.
# Note: This is an expected situation as no new falling edge will
# happen until next frame is transmitted.
if self.matched == 0b10:
self.wait({0: 'f'})
self.fall_end = self.samplenum

View File

@@ -0,0 +1,117 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Jorge Solla Rubiales <jorgesolla@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
logical_adresses = [
'TV',
'Recording_1',
'Recording_2',
'Tuner_1',
'Playback_1',
'AudioSystem',
'Tuner2',
'Tuner3',
'Playback_2',
'Recording_3',
'Tuner_4',
'Playback_3',
'Backup_1',
'Backup_2',
'FreeUse',
]
# List taken from LibCEC.
opcodes = {
0x82: 'ACTIVE_SOURCE',
0x04: 'IMAGE_VIEW_ON',
0x0D: 'TEXT_VIEW_ON',
0x9D: 'INACTIVE_SOURCE',
0x85: 'REQUEST_ACTIVE_SOURCE',
0x80: 'ROUTING_CHANGE',
0x81: 'ROUTING_INFORMATION',
0x86: 'SET_STREAM_PATH',
0x36: 'STANDBY',
0x0B: 'RECORD_OFF',
0x09: 'RECORD_ON',
0x0A: 'RECORD_STATUS',
0x0F: 'RECORD_TV_SCREEN',
0x33: 'CLEAR_ANALOGUE_TIMER',
0x99: 'CLEAR_DIGITAL_TIMER',
0xA1: 'CLEAR_EXTERNAL_TIMER',
0x34: 'SET_ANALOGUE_TIMER',
0x97: 'SET_DIGITAL_TIMER',
0xA2: 'SET_EXTERNAL_TIMER',
0x67: 'SET_TIMER_PROGRAM_TITLE',
0x43: 'TIMER_CLEARED_STATUS',
0x35: 'TIMER_STATUS',
0x9E: 'CEC_VERSION',
0x9F: 'GET_CEC_VERSION',
0x83: 'GIVE_PHYSICAL_ADDRESS',
0x91: 'GET_MENU_LANGUAGE',
0x84: 'REPORT_PHYSICAL_ADDRESS',
0x32: 'SET_MENU_LANGUAGE',
0x42: 'DECK_CONTROL',
0x1B: 'DECK_STATUS',
0x1A: 'GIVE_DECK_STATUS',
0x41: 'PLAY',
0x08: 'GIVE_TUNER_DEVICE_STATUS',
0x92: 'SELECT_ANALOGUE_SERVICE',
0x93: 'SELECT_DIGITAL_SERVICE',
0x07: 'TUNER_DEVICE_STATUS',
0x06: 'TUNER_STEP_DECREMENT',
0x05: 'TUNER_STEP_INCREMENT',
0x87: 'DEVICE_VENDOR_ID',
0x8C: 'GIVE_DEVICE_VENDOR_ID',
0x89: 'VENDOR_COMMAND',
0xA0: 'VENDOR_COMMAND_WITH_ID',
0x8A: 'VENDOR_REMOTE_BUTTON_DOWN',
0x8B: 'VENDOR_REMOTE_BUTTON_UP',
0x64: 'SET_OSD_STRING',
0x46: 'GIVE_OSD_NAME',
0x47: 'SET_OSD_NAME',
0x8D: 'MENU_REQUEST',
0x8E: 'MENU_STATUS',
0x44: 'USER_CONTROL_PRESSED',
0x45: 'USER_CONTROL_RELEASE',
0x8F: 'GIVE_DEVICE_POWER_STATUS',
0x90: 'REPORT_POWER_STATUS',
0x00: 'FEATURE_ABORT',
0xFF: 'ABORT',
0x71: 'GIVE_AUDIO_STATUS',
0x7D: 'GIVE_SYSTEM_AUDIO_MODE_STATUS',
0x7A: 'REPORT_AUDIO_STATUS',
0x72: 'SET_SYSTEM_AUDIO_MODE',
0x70: 'SYSTEM_AUDIO_MODE_REQUEST',
0x7E: 'SYSTEM_AUDIO_MODE_STATUS',
0x9A: 'SET_AUDIO_RATE',
}
def resolve_logical_address(id_, is_initiator):
if id_ < 0 or id_ > 0x0F:
return 'Invalid'
# Special handling of 0x0F.
if id_ == 0x0F:
return 'Unregistered' if is_initiator else 'Broadcast'
return logical_adresses[id_]
def decode_header(header):
src = (header & 0xF0) >> 4
dst = (header & 0x0F)
return (resolve_logical_address(src, 1), resolve_logical_address(dst, 0))

View File

@@ -0,0 +1,34 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Elias Oenal <sigrok@eliasoenal.com>
## All rights reserved.
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are met:
##
## 1. Redistributions of source code must retain the above copyright notice,
## this list of conditions and the following disclaimer.
## 2. Redistributions in binary form must reproduce the above copyright notice,
## this list of conditions and the following disclaimer in the documentation
## and/or other materials provided with the distribution.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
## POSSIBILITY OF SUCH DAMAGE.
##
'''
This decoder stacks on top of the 'mdio' PD and decodes the CFP 100G
pluggable transceiver protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,110 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Elias Oenal <sigrok@eliasoenal.com>
## All rights reserved.
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are met:
##
## 1. Redistributions of source code must retain the above copyright notice,
## this list of conditions and the following disclaimer.
## 2. Redistributions in binary form must reproduce the above copyright notice,
## this list of conditions and the following disclaimer in the documentation
## and/or other materials provided with the distribution.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
## POSSIBILITY OF SUCH DAMAGE.
##
import sigrokdecode as srd
MODULE_ID = {
0x00: 'Unknown or unspecified',
0x01: 'GBIC',
0x02: 'Module/connector soldered to motherboard',
0x03: 'SFP',
0x04: '300 pin XSBI',
0x05: 'XENPAK',
0x06: 'XFP',
0x07: 'XFF',
0x08: 'XFP-E',
0x09: 'XPAK',
0x0a: 'X2',
0x0B: 'DWDM-SFP',
0x0C: 'QSFP',
0x0D: 'QSFP+',
0x0E: 'CFP',
0x0F: 'CXP (TBD)',
0x11: 'CFP2',
0x12: 'CFP4',
}
class Decoder(srd.Decoder):
api_version = 3
id = 'cfp'
name = 'CFP'
longname = '100 Gigabit C form-factor pluggable'
desc = '100 Gigabit C form-factor pluggable (CFP) protocol.'
license = 'BSD'
inputs = ['mdio']
outputs = []
tags = ['Networking']
annotations = (
('register', 'Register'),
('decode', 'Decode'),
)
annotation_rows = (
('registers', 'Registers', (0,)),
('decodes', 'Decodes', (1,)),
)
def __init__(self):
self.reset()
def reset(self):
pass
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def decode(self, ss, es, data):
self.ss, self.es = ss, es
for (clause45, clause45_addr, is_read, portad, devad, reg) in data:
if not is_read:
continue
if clause45_addr in range(0x8000, 0x807F + 1):
self.putx([0, ['CFP NVR 1: Basic ID register', 'NVR1']])
if clause45_addr == 0x8000:
self.putx([1, ['Module identifier: %s' % \
MODULE_ID.get(reg, 'Reserved')]])
elif clause45_addr in range(0x8080, 0x80FF + 1):
self.putx([0, ['CFP NVR 2: Extended ID register', 'NVR2']])
elif clause45_addr in range(0x8100, 0x817F + 1):
self.putx([0, ['CFP NVR 3: Network lane specific register', 'NVR3']])
elif clause45_addr in range(0x8180, 0x81FF + 1):
self.putx([0, ['CFP NVR 4', 'NVR4']])
elif clause45_addr in range(0x8400, 0x847F + 1):
self.putx([0, ['Vendor NVR 1: Vendor data register', 'V-NVR1']])
elif clause45_addr in range(0x8480, 0x84FF + 1):
self.putx([0, ['Vendor NVR 2: Vendor data register', 'V-NVR2']])
elif clause45_addr in range(0x8800, 0x887F + 1):
self.putx([0, ['User NVR 1: User data register', 'U-NVR1']])
elif clause45_addr in range(0x8880, 0x88FF + 1):
self.putx([0, ['User NVR 2: User data register', 'U-NVR2']])
elif clause45_addr in range(0xA000, 0xA07F + 1):
self.putx([0, ['CFP Module VR 1: CFP Module level control and DDM register', 'Mod-VR1']])
elif clause45_addr in range(0xA080, 0xA0FF + 1):
self.putx([0, ['MLG VR 1: MLG Management Interface register', 'MLG-VR1']])

View File

@@ -0,0 +1,37 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2015 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 Zhiyuan Wan <dv.xw@qq.com>
## Copyright (C) 2019 Kongou Hikari <hikari@iloli.bid>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
'''
JTAG (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 decoders handles a tiny part of IEEE 1149.7, the so called CJTAG OSCAN1
format
http://developers-club.com/posts/237885/
'''
from .pd import Decoder

View File

@@ -0,0 +1,390 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2015 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2019 DreamSourceLab <support@dreamsourcelab.com>
## Copyright (C) 2019 Zhiyuan Wan <dv.xw@qq.com>
## Copyright (C) 2019 Kongou Hikari <hikari@iloli.bid>
##
## Version:
## Modified by Shiqiu Nie(369614718@qq.com)
## Date: 2017-01-11
## Descript:
## 1. 2017-01-10 Fixed TDI/TDO data decode, when JTAG TAP run into
## SHIFT-IR/SHIFT-DR status,the first bit is not a valid bit.
## 2. 2017-01-11 Fixed decode when shift only one bit.
##
## 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 <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
'''
OUTPUT_PYTHON format:
Packet:
[<ptype>, <pdata>]
<ptype>:
- 'NEW STATE': <pdata> 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.
'''
jtag_states = [
# Intro "tree"
'TEST-LOGIC-RESET', 'RUN-TEST/IDLE',
# DR "tree"
'SELECT-DR-SCAN', 'CAPTURE-DR', 'UPDATE-DR', 'PAUSE-DR',
'SHIFT-DR', 'EXIT1-DR', 'EXIT2-DR',
# IR "tree"
'SELECT-IR-SCAN', 'CAPTURE-IR', 'UPDATE-IR', 'PAUSE-IR',
'SHIFT-IR', 'EXIT1-IR', 'EXIT2-IR',
]
class Decoder(srd.Decoder):
api_version = 3
id = 'cjtag_oscan1'
name = 'cJTAG OScan1'
longname = 'Compact Joint Test Action Group (IEEE 1149.7)'
desc = 'Protocol for testing, debugging, and flashing ICs, Now this plugin has no ZBS support, it only supports Oscan1 format.'
license = 'gplv2+'
inputs = ['logic']
outputs = ['jtag']
tags = ['Debug/trace']
channels = (
{'id': 'tdi', 'name': 'TDI', 'desc': 'Test data input'},
{'id': 'tdo', 'name': 'TDO', 'desc': 'Test data output'},
{'id': 'tck', 'name': 'TCK', 'desc': 'Test clock'},
{'id': 'tms', 'name': 'TMS', 'desc': 'Test mode select'},
)
optional_channels = (
{'id': 'trst', 'name': 'TRST#', 'desc': 'Test reset'},
{'id': 'srst', 'name': 'SRST#', 'desc': 'System reset'},
{'id': 'rtck', 'name': 'RTCK', 'desc': 'Return clock signal'},
)
annotations = tuple([tuple([s.lower(), s]) for s in jtag_states]) + ( \
('bit-tdi', 'Bit (TDI)'),
('bit-tdo', 'Bit (TDO)'),
('bitstring-tdi', 'Bitstring (TDI)'),
('bitstring-tdo', 'Bitstring (TDO)'),
('bit-tms', 'Bit (TMS)'),
('state-tapc', 'TAPC State'),
)
annotation_rows = (
('bits-tdi', 'Bits (TDI)', (16,)),
('bits-tdo', 'Bits (TDO)', (17,)),
('bitstrings-tdi', 'Bitstring (TDI)', (18,)),
('bitstrings-tdo', 'Bitstring (TDO)', (19,)),
('bit-tms', 'Bit (TMS)', (20,)),
('state-tapc', 'TAPC State', (21,)),
('states', 'States', tuple(range(15 + 1))),
)
def __init__(self):
self.reset()
def reset(self):
self.state = 'TEST-LOGIC-RESET'
# self.state = 'RUN-TEST/IDLE'
self.cjtagstate = '4-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
self.bits_cnt = 0
self.data_ready = False
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.startswith("CJTAG-"):
self.oacp = self.oacp + 1
if (self.oacp > 4 and self.oaclen == 12):
self.cjtagstate = 'CJTAG-EC'
if (self.oacp == 8 and tms == 0):
self.oaclen = 36
if (self.oacp > 8 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-SPARE'
if (self.oacp > 13 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-TPDEL'
if (self.oacp > 16 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-TPREV'
if (self.oacp > 18 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-TPST'
if (self.oacp > 23 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-RDYC'
if (self.oacp > 25 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-DLYC'
if (self.oacp > 27 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-SCNFMT'
if (self.oacp > 8 and self.oaclen == 12):
self.cjtagstate = 'CJTAG-CP'
if (self.oacp > 32 and self.oaclen == 36):
self.cjtagstate = 'CJTAG-CP'
if (self.oacp > self.oaclen):
self.cjtagstate = 'OSCAN1'
self.oscan1cycle = 1
self.state = 'TEST-LOGIC-RESET' # Because Nuclei cJTAG device asserts a reset during cJTAG online activating.
else :
# Intro "tree"
if self.state == 'TEST-LOGIC-RESET':
self.state = 'TEST-LOGIC-RESET' if (tms) else 'RUN-TEST/IDLE'
elif self.state == 'RUN-TEST/IDLE':
self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
# DR "tree"
elif self.state == 'SELECT-DR-SCAN':
self.state = 'SELECT-IR-SCAN' if (tms) else 'CAPTURE-DR'
elif self.state == 'CAPTURE-DR':
self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
elif self.state == 'SHIFT-DR':
self.state = 'EXIT1-DR' if (tms) else 'SHIFT-DR'
elif self.state == 'EXIT1-DR':
self.state = 'UPDATE-DR' if (tms) else 'PAUSE-DR'
elif self.state == 'PAUSE-DR':
self.state = 'EXIT2-DR' if (tms) else 'PAUSE-DR'
elif self.state == 'EXIT2-DR':
self.state = 'UPDATE-DR' if (tms) else 'SHIFT-DR'
elif self.state == 'UPDATE-DR':
self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
# IR "tree"
elif self.state == 'SELECT-IR-SCAN':
self.state = 'TEST-LOGIC-RESET' if (tms) else 'CAPTURE-IR'
elif self.state == 'CAPTURE-IR':
self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
elif self.state == 'SHIFT-IR':
self.state = 'EXIT1-IR' if (tms) else 'SHIFT-IR'
elif self.state == 'EXIT1-IR':
self.state = 'UPDATE-IR' if (tms) else 'PAUSE-IR'
elif self.state == 'PAUSE-IR':
self.state = 'EXIT2-IR' if (tms) else 'PAUSE-IR'
elif self.state == 'EXIT2-IR':
self.state = 'UPDATE-IR' if (tms) else 'SHIFT-IR'
elif self.state == 'UPDATE-IR':
self.state = 'SELECT-DR-SCAN' if (tms) else 'RUN-TEST/IDLE'
def handle_rising_tck_edge(self, tdi, tdo, tck, tms, trst, srst, rtck):
# 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), [self.oldstate]])
self.putp(['NEW STATE', self.state])
self.putx([21, [self.oldcjtagstate]])
if(self.oldcjtagstate.startswith("CJTAG-")):
self.putx([20, [str(self.oldtms)]])
#self.putx([20, [str(tms)]])
#self.putx([16, [str(tdi)]])
#self.putx([17, [str(tdo)]])
self.oldtms = tms
# Upon SHIFT-IR/SHIFT-DR collect the current TDI/TDO values.
if self.state.startswith('SHIFT-'):
#if self.first_bit:
#self.ss_bitstring = self.samplenum
# self.first_bit = False
#else:
if self.bits_cnt > 0:
if self.bits_cnt == 1:
self.ss_bitstring = self.samplenum
if self.bits_cnt > 1:
self.putx([16, [str(self.bits_tdi[0])]])
self.putx([17, [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])
self.bits_cnt = self.bits_cnt + 1
# Output all TDI/TDO bits if we just switched from SHIFT-* to EXIT1-*.
if self.oldstate.startswith('SHIFT-') and \
self.state.startswith('EXIT1-'):
#self.es_bitstring = self.samplenum
if self.bits_cnt > 0:
if self.bits_cnt == 1: # Only shift one bit
self.ss_bitstring = 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])
else:
### ----------------------------------------------------------------
self.putx([16, [str(self.bits_tdi[0])]])
self.putx([17, [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])
## ----------------------------------------------------------------
self.data_ready = True
self.first_bit = True
self.bits_cnt = 0
if self.oldstate.startswith('EXIT'):# and \
#self.state.startswith('PAUSE-'):
if self.data_ready:
self.data_ready = False
self.es_bitstring = self.samplenum
t = self.state[-2:] + ' TDI'
b = ''.join(map(str, self.bits_tdi))
h = ' (0x%X' % int('0b' + b, 2) + ')'
s = t + ': ' + h + ', ' + str(len(self.bits_tdi)) + ' bits' #b +
self.putx_bs([18, [s]])
self.bits_samplenums_tdi[0][1] = self.samplenum # ES of last bit.
self.putp_bs([t, [b, self.bits_samplenums_tdi]])
self.putx([16, [str(self.bits_tdi[0])]]) # Last bit.
self.bits_tdi = []
self.bits_samplenums_tdi = []
t = self.state[-2:] + ' TDO'
b = ''.join(map(str, self.bits_tdo))
h = ' (0x%X' % int('0b' + b, 2) + ')'
s = t + ': ' + h + ', ' + str(len(self.bits_tdo)) + ' bits' #+ b
self.putx_bs([19, [s]])
self.bits_samplenums_tdo[0][1] = self.samplenum # ES of last bit.
self.putp_bs([t, [b, self.bits_samplenums_tdo]])
self.putx([17, [str(self.bits_tdo[0])]]) # Last bit.
self.bits_tdo = []
self.bits_samplenums_tdo = []
#self.first_bit = True
#self.bits_cnt = 0
#self.ss_bitstring = self.samplenum
self.ss_item = self.samplenum
def handle_tms_edge(self, tck, tms):
self.escape_edges = self.escape_edges + 1
#print ("Detected escape sequence " + str(self.escape_edges))
#self.es_item = self.samplenum
#self.putx([20, [str(self.escape_edges)]])
def handle_tapc_state(self, tck, tms):
self.oldcjtagstate = self.cjtagstate
if self.escape_edges >= 8:
self.cjtagstate = '4-WIRE'
if self.escape_edges == 6 : #| self.escape_edges == 7:
self.cjtagstate = 'CJTAG-OAC'
self.oacp = 0
self.oaclen = 12
self.escape_edges = 0
def decode(self):
tdi_real = 0
tms_real = 0
tdo_real = 0
while True:
# Wait for a rising edge on TCK.
(tdi, tdo, tck, tms, trst, srst, rtck) = self.wait({2: 'r'})
self.handle_tapc_state(tck, tms)
if(self.cjtagstate == 'OSCAN1'):
if(self.oscan1cycle == 0): #nTDI
if(tms == 0):
tdi_real = 1
else:
tdi_real = 0
self.oscan1cycle = 1
elif(self.oscan1cycle == 1): #TMS
tms_real = tms
self.oscan1cycle = 2
elif(self.oscan1cycle == 2): #TDO
tdo_real = tms
self.handle_rising_tck_edge(tdi_real, tdo_real, tck, tms_real, trst, srst, rtck)
self.oscan1cycle = 0
else:
self.handle_rising_tck_edge(tdi, tdo, tck, tms, trst, srst, rtck)
while (tck == 1):
(tdi, tdo, tck, tms_n, trst, srst, rtck) = self.wait([{2: 'f'}, {3: 'e'}])
if(tms_n != tms):
tms = tms_n
self.handle_tms_edge(tck, tms)

View File

@@ -0,0 +1,19 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##

View File

@@ -0,0 +1,20 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Bert Vermeulen <bert@biot.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
from .mod import *

View File

@@ -0,0 +1,192 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Bert Vermeulen <bert@biot.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# This module contains definitions for use by pluggable network adapters,
# such as SFP, XFP etc.
MODULE_ID = {
0x01: 'GBIC',
0x02: 'Integrated module/connector',
0x03: 'SFP',
0x04: '300-pin XBI',
0x05: 'XENPAK',
0x06: 'XFP',
0x07: 'XFF',
0x08: 'XFP-E',
0x09: 'XPAK',
0x0a: 'X2',
}
ALARM_THRESHOLDS = {
0: 'Temp high alarm',
2: 'Temp low alarm',
4: 'Temp high warning',
6: 'Temp low warning',
16: 'Bias high alarm',
18: 'Bias low alarm',
20: 'Bias high warning',
22: 'Bias low warning',
24: 'TX power high alarm',
26: 'TX power low alarm',
28: 'TX power high warning',
30: 'TX power low warning',
32: 'RX power high alarm',
34: 'RX power low alarm',
36: 'RX power high warning',
38: 'RX power low warning',
40: 'AUX 1 high alarm',
42: 'AUX 1 low alarm',
44: 'AUX 1 high warning',
46: 'AUX 1 low warning',
48: 'AUX 2 high alarm',
50: 'AUX 2 low alarm',
52: 'AUX 2 high warning',
54: 'AUX 2 low warning',
}
AD_READOUTS = {
0: 'Module temperature',
4: 'TX bias current',
6: 'Measured TX output power',
8: 'Measured RX input power',
10: 'AUX 1 measurement',
12: 'AUX 2 measurement',
}
GCS_BITS = [
'TX disable',
'Soft TX disable',
'MOD_NR',
'P_Down',
'Soft P_Down',
'Interrupt',
'RX_LOS',
'Data_Not_Ready',
'TX_NR',
'TX_Fault',
'TX_CDR not locked',
'RX_NR',
'RX_CDR not locked',
]
CONNECTOR = {
0x01: 'SC',
0x02: 'Fibre Channel style 1 copper',
0x03: 'Fibre Channel style 2 copper',
0x04: 'BNC/TNC',
0x05: 'Fibre Channel coax',
0x06: 'FiberJack',
0x07: 'LC',
0x08: 'MT-RJ',
0x09: 'MU',
0x0a: 'SG',
0x0b: 'Optical pigtail',
0x20: 'HSSDC II',
0x21: 'Copper pigtail',
}
TRANSCEIVER = [
# 10GB Ethernet
['10GBASE-SR', '10GBASE-LR', '10GBASE-ER', '10GBASE-LRM', '10GBASE-SW',
'10GBASE-LW', '10GBASE-EW'],
# 10GB Fibre Channel
['1200-MX-SN-I', '1200-SM-LL-L', 'Extended Reach 1550 nm',
'Intermediate reach 1300 nm FP'],
# 10GB Copper
[],
# 10GB low speed
['1000BASE-SX / 1xFC MMF', '1000BASE-LX / 1xFC SMF', '2xFC MMF',
'2xFC SMF', 'OC48-SR', 'OC48-IR', 'OC48-LR'],
# 10GB SONET/SDH interconnect
['I-64.1r', 'I-64.1', 'I-64.2r', 'I-64.2', 'I-64.3', 'I-64.5'],
# 10GB SONET/SDH short haul
['S-64.1', 'S-64.2a', 'S-64.2b', 'S-64.3a', 'S-64.3b', 'S-64.5a', 'S-64.5b'],
# 10GB SONET/SDH long haul
['L-64.1', 'L-64.2a', 'L-64.2b', 'L-64.2c', 'L-64.3', 'G.959.1 P1L1-2D2'],
# 10GB SONET/SDH very long haul
['V-64.2a', 'V-64.2b', 'V-64.3'],
]
SERIAL_ENCODING = [
'64B/66B',
'8B/10B',
'SONET scrambled',
'NRZ',
'RZ',
]
XMIT_TECH = [
'850 nm VCSEL',
'1310 nm VCSEL',
'1550 nm VCSEL',
'1310 nm FP',
'1310 nm DFB',
'1550 nm DFB',
'1310 nm EML'
'1550 nm EML'
'copper',
]
CDR = [
'9.95Gb/s',
'10.3Gb/s',
'10.5Gb/s',
'10.7Gb/s',
'11.1Gb/s',
'(unknown)',
'lineside loopback mode',
'XFI loopback mode',
]
DEVICE_TECH = [
['no wavelength control', 'sctive wavelength control'],
['uncooled transmitter device', 'cooled transmitter'],
['PIN detector', 'APD detector'],
['transmitter not tunable', 'transmitter tunable'],
]
ENHANCED_OPTS = [
'VPS',
'soft TX_DISABLE',
'soft P_Down',
'VPS LV regulator mode',
'VPS bypassed regulator mode',
'active FEC control',
'wavelength tunability',
'CMU',
]
AUX_TYPES = [
'not implemented',
'APD bias voltage',
'(unknown)',
'TEC current',
'laser temperature',
'laser wavelength',
'5V supply voltage',
'3.3V supply voltage',
'1.8V supply voltage',
'-5.2V supply voltage',
'5V supply current',
'(unknown)',
'(unknown)',
'3.3V supply current',
'1.8V supply current',
'-5.2V supply current',
]

View File

@@ -0,0 +1,20 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
from .mod import *

View File

@@ -0,0 +1,151 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# Normal commands (CMD)
# Unlisted items are 'Reserved' as per SD spec. The 'Unknown' items don't
# seem to be mentioned in the spec, but aren't marked as reserved either.
cmd_names = {
0: 'GO_IDLE_STATE',
1: 'SEND_OP_COND', # Reserved in SD mode
2: 'ALL_SEND_CID',
3: 'SEND_RELATIVE_ADDR',
4: 'SET_DSR',
5: 'IO_SEND_OP_COND', # SDIO-only
6: 'SWITCH_FUNC', # New since spec 1.10
7: 'SELECT/DESELECT_CARD',
8: 'SEND_IF_COND',
9: 'SEND_CSD',
10: 'SEND_CID',
11: 'VOLTAGE_SWITCH',
12: 'STOP_TRANSMISSION',
13: 'SEND_STATUS',
# 14: Reserved
15: 'GO_INACTIVE_STATE',
16: 'SET_BLOCKLEN',
17: 'READ_SINGLE_BLOCK',
18: 'READ_MULTIPLE_BLOCK',
19: 'SEND_TUNING_BLOCK',
20: 'SPEED_CLASS_CONTROL',
# 21-22: Reserved
23: 'SET_BLOCK_COUNT',
24: 'WRITE_BLOCK',
25: 'WRITE_MULTIPLE_BLOCK',
26: 'Reserved for manufacturer',
27: 'PROGRAM_CSD',
28: 'SET_WRITE_PROT',
29: 'CLR_WRITE_PROT',
30: 'SEND_WRITE_PROT',
# 31: Reserved
32: 'ERASE_WR_BLK_START', # SPI mode: ERASE_WR_BLK_START_ADDR
33: 'ERASE_WR_BLK_END', # SPI mode: ERASE_WR_BLK_END_ADDR
34: 'Reserved for CMD6', # New since spec 1.10
35: 'Reserved for CMD6', # New since spec 1.10
36: 'Reserved for CMD6', # New since spec 1.10
37: 'Reserved for CMD6', # New since spec 1.10
38: 'ERASE',
# 39: Reserved
40: 'Reserved for security specification',
# 41: Reserved
42: 'LOCK_UNLOCK',
# 43-49: Reserved
50: 'Reserved for CMD6', # New since spec 1.10
# 51: Reserved
52: 'IO_RW_DIRECT', # SDIO-only
53: 'IO_RW_EXTENDED', # SDIO-only
54: 'Unknown',
55: 'APP_CMD',
56: 'GEN_CMD',
57: 'Reserved for CMD6', # New since spec 1.10
58: 'READ_OCR', # Reserved in SD mode
59: 'CRC_ON_OFF', # Reserved in SD mode
60: 'Reserved for manufacturer',
61: 'Reserved for manufacturer',
62: 'Reserved for manufacturer',
63: 'Reserved for manufacturer',
}
# Application-specific commands (ACMD)
# Unlisted items are 'Reserved' as per SD spec. The 'Unknown' items don't
# seem to be mentioned in the spec, but aren't marked as reserved either.
acmd_names = {
# 1-5: Reserved
6: 'SET_BUS_WIDTH',
# 7-12: Reserved
13: 'SD_STATUS',
14: 'Reserved for Security Application',
15: 'Reserved for Security Application',
16: 'Reserved for Security Application',
# 17: Reserved
18: 'Reserved for SD security applications',
# 19-21: Reserved
22: 'SEND_NUM_WR_BLOCKS',
23: 'SET_WR_BLK_ERASE_COUNT',
# 24: Reserved
25: 'Reserved for SD security applications',
26: 'Reserved for SD security applications',
27: 'Reserved for security specification',
28: 'Reserved for security specification',
# 29: Reserved
30: 'Reserved for security specification',
31: 'Reserved for security specification',
32: 'Reserved for security specification',
33: 'Reserved for security specification',
34: 'Reserved for security specification',
35: 'Reserved for security specification',
# 36-37: Reserved
38: 'Reserved for SD security applications',
# 39-40: Reserved
41: 'SD_SEND_OP_COND',
42: 'SET_CLR_CARD_DETECT',
43: 'Reserved for SD security applications',
44: 'Reserved for SD security applications',
45: 'Reserved for SD security applications',
46: 'Reserved for SD security applications',
47: 'Reserved for SD security applications',
48: 'Reserved for SD security applications',
49: 'Reserved for SD security applications',
50: 'Unknown',
51: 'SEND_SCR',
52: 'Reserved for security specification',
53: 'Reserved for security specification',
54: 'Reserved for security specification',
55: 'Non-existant', # Doesn't exist (equivalent to CMD55)
56: 'Reserved for security specification',
57: 'Reserved for security specification',
58: 'Reserved for security specification',
59: 'Reserved for security specification',
60: 'Unknown',
61: 'Unknown',
62: 'Unknown',
63: 'Unknown',
}
accepted_voltages = {
0b0001: '2.7-3.6V',
0b0010: 'reserved for low voltage range',
0b0100: 'reserved',
0b1000: 'reserved',
# All other values: "not defined".
}
sd_status = {
# 311:0: Reserved for manufacturer
# 391:312: Reserved
}

View File

@@ -0,0 +1,20 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
from .mod import *

View File

@@ -0,0 +1,84 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2020 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
from enum import Enum, IntEnum, unique
from itertools import chain
import re
# Return the specified BCD number (max. 8 bits) as integer.
def bcd2int(b):
return (b & 0x0f) + ((b >> 4) * 10)
def bin2int(s: str):
return int('0b' + s, 2)
def bitpack(bits):
return sum([b << i for i, b in enumerate(bits)])
def bitunpack(num, minbits=0):
res = []
while num or minbits > 0:
res.append(num & 1)
num >>= 1
minbits -= 1
return tuple(res)
@unique
class SrdStrEnum(Enum):
@classmethod
def from_list(cls, name, l):
# Keys are limited/converted to [A-Z0-9_], values can be any string.
items = [(re.sub('[^A-Z0-9_]', '_', l[i]), l[i]) for i in range(len(l))]
return cls(name, items)
@classmethod
def from_str(cls, name, s):
return cls.from_list(name, s.split())
@unique
class SrdIntEnum(IntEnum):
@classmethod
def _prefix(cls, p):
return tuple([a.value for a in cls if a.name.startswith(p)])
@classmethod
def prefixes(cls, prefix_list):
if isinstance(prefix_list, str):
prefix_list = prefix_list.split()
return tuple(chain(*[cls._prefix(p) for p in prefix_list]))
@classmethod
def _suffix(cls, s):
return tuple([a.value for a in cls if a.name.endswith(s)])
@classmethod
def suffixes(cls, suffix_list):
if isinstance(suffix_list, str):
suffix_list = suffix_list.split()
return tuple(chain(*[cls._suffix(s) for s in suffix_list]))
@classmethod
def from_list(cls, name, l):
# Manually construct (Python 3.4 is missing the 'start' argument).
# Python defaults to start=1, but we want start=0.
return cls(name, [(l[i], i) for i in range(len(l))])
@classmethod
def from_str(cls, name, s):
return cls.from_list(name, s.split())

View File

@@ -0,0 +1,28 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder is a simple edge counter.
It can count rising and/or falling edges, provides an optional reset
signal. It can also divide the count to e.g. count the number of
fixed-length words (where a word corresponds to e.g. 9 clock edges).
'''
from .pd import Decoder

View File

@@ -0,0 +1,145 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
PIN_DATA, PIN_RESET = range(2)
ROW_EDGE, ROW_WORD, ROW_RESET = range(3)
class Decoder(srd.Decoder):
api_version = 3
id = 'counter'
name = 'Counter'
longname = 'Edge counter'
desc = 'Count the number of edges in a signal.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Util']
channels = (
{'id': 'data', 'name': 'Data', 'desc': 'Data line'},
)
optional_channels = (
{'id': 'reset', 'name': 'Reset', 'desc': 'Reset line'},
)
annotations = (
('edge_count', 'Edge count'),
('word_count', 'Word count'),
('word_reset', 'Word reset'),
)
annotation_rows = (
('edge_counts', 'Edges', (ROW_EDGE,)),
('word_counts', 'Words', (ROW_WORD,)),
('word_resets', 'Word resets', (ROW_RESET,)),
)
options = (
{'id': 'data_edge', 'desc': 'Edges to count (data)', 'default': 'any',
'values': ('any', 'rising', 'falling')},
{'id': 'divider', 'desc': 'Count divider (word width)', 'default': 0},
{'id': 'reset_edge', 'desc': 'Edge which clears counters (reset)',
'default': 'falling', 'values': ('rising', 'falling')},
{'id': 'edge_off', 'desc': 'Edge counter value after start/reset', 'default': 0},
{'id': 'word_off', 'desc': 'Word counter value after start/reset', 'default': 0},
{'id': 'dead_cycles', 'desc': 'Ignore this many edges after reset', 'default': 0},
{'id': 'start_with_reset', 'desc': 'Assume decode starts with reset',
'default': 'no', 'values': ('no', 'yes')},
)
def __init__(self):
self.reset()
def reset(self):
pass
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putc(self, cls, ss, annlist):
self.put(ss, self.samplenum, self.out_ann, [cls, annlist])
def decode(self):
opt_edge_map = {'rising': 'r', 'falling': 'f', 'any': 'e'}
data_edge = self.options['data_edge']
divider = self.options['divider']
if divider < 0:
divider = 0
reset_edge = self.options['reset_edge']
condition = [{PIN_DATA: opt_edge_map[data_edge]}]
have_reset = self.has_channel(PIN_RESET)
if have_reset:
cond_reset = len(condition)
condition.append({PIN_RESET: opt_edge_map[reset_edge]})
edge_count = int(self.options['edge_off'])
edge_start = None
word_count = int(self.options['word_off'])
word_start = None
if self.options['start_with_reset'] == 'yes':
dead_count = int(self.options['dead_cycles'])
else:
dead_count = 0
while True:
self.wait(condition)
now = self.samplenum
if have_reset and (self.matched & (0b1 <<cond_reset)):
edge_count = int(self.options['edge_off'])
edge_start = now
word_count = int(self.options['word_off'])
word_start = now
self.putc(ROW_RESET, now, ['Word reset', 'Reset', 'Rst', 'R'])
dead_count = int(self.options['dead_cycles'])
continue
if dead_count:
dead_count -= 1
edge_start = now
word_start = now
continue
# Implementation note: In the absence of a RESET condition
# before the first data edge, any arbitrary choice of where
# to start the annotation is valid. One may choose to emit a
# narrow annotation (where ss=es), or assume that the cycle
# which corresponds to the counter value started at sample
# number 0. We decided to go with the latter here, to avoid
# narrow annotations (see bug #1210). None of this matters in
# the presence of a RESET condition in the input stream.
if edge_start is None:
edge_start = 0
if word_start is None:
word_start = 0
edge_count += 1
self.putc(ROW_EDGE, edge_start, ["{:d}".format(edge_count)])
edge_start = now
word_edge_count = edge_count - int(self.options['edge_off'])
if divider and (word_edge_count % divider) == 0:
word_count += 1
self.putc(ROW_WORD, word_start, ["{:d}".format(word_count)])
word_start = now

View File

@@ -0,0 +1,24 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Jeremy Swanson <jeremy@rakocontrols.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
'''
DALI is a biphase/manchester based lighting control protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,98 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Jeremy Swanson <jeremy@rakocontrols.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
# DALI extended commands
extended_commands = {
0xA1: ['Terminate special processes', 'Terminate'],
0xA3: ['DTR = DATA', 'DTR'],
0xA5: ['INITIALISE', 'INIT'],
0xA7: ['RANDOMISE', 'RAND'],
0xA9: ['COMPARE', 'COMP'],
0xAB: ['WITHDRAW', 'WDRAW'],
0xB1: ['SET SEARCH H', 'SAH'],
0xB3: ['SET SEARCH M', 'SAM'],
0xB5: ['SET SEARCH L', 'SAL'],
0xB7: ['Program Short Address', 'ProgSA'],
0xB9: ['Verify Short Address', 'VfySA'],
0xBB: ['Query Short Address', 'QryShort'],
0xBD: ['Physical Selection', 'PysSel'],
0xC1: ['Enable Device Type X', 'EnTyp'],
0xC3: ['DTR1 = DATA', 'DTR1'],
0xC5: ['DTR2 = DATA', 'DTR2'],
0xC7: ['Write Memory Location', 'WRI'],
}
# List of commands
dali_commands = {
0x00: ['Immediate Off', 'IOFF'],
0x01: ['Up 200ms', 'Up'],
0x02: ['Down 200ms', 'Down'],
0x03: ['Step Up', 'Step+'],
0x04: ['Step Down', 'Step-'],
0x05: ['Recall Maximum Level', 'Recall Max'],
0x06: ['Recall Minimum Level', 'Recall Min'],
0x07: ['Step down and off', 'Down Off'],
0x08: ['Step ON and UP', 'On Up'],
0x20: ['Reset', 'Rst'],
0x21: ['Store Dim Level in DTR', 'Level -> DTR'],
0x2A: ['Store DTR as Max Level', 'DTR->Max'],
0x2B: ['Store DTR as Min Level', 'DTR->Min'],
0x2C: ['Store DTR as Fail Level', 'DTR->Fail'],
0x2D: ['Store DTR as Power On Level', 'DTR->Poweron'],
0x2E: ['Store DTR as Fade Time', 'DTR->Fade'],
0x2F: ['Store DTR as Fade Rate', 'DTR->Rate'],
0x80: ['Store DTR as Short Address', 'DTR->Add'],
0x81: ['Enable Memory Write', 'WEn'],
0x90: ['Query Status', 'Status'],
0x91: ['Query Ballast', 'Ballast'],
0x92: ['Query Lamp Failure', 'LmpFail'],
0x93: ['Query Power On', 'Power On'],
0x94: ['Query Limit Error', 'Limit Err'],
0x95: ['Query Reset', 'Reset State'],
0x96: ['Query Missing Short Address', 'NoSrt'],
0x97: ['Query Version', 'Ver'],
0x98: ['Query DTR', 'GetDTR'],
0x99: ['Query Device Type', 'Type'],
0x9A: ['Query Physical Minimum', 'PhysMin'],
0x9B: ['Query Power Fail', 'PowerFailed'],
0x9C: ['Query DTR1', 'GetDTR1'],
0x9D: ['Query DTR2', 'GetDTR2'],
0xA0: ['Query Level', 'GetLevel'],
0xA1: ['Query Max Level', 'GetMax'],
0xA2: ['Query Min Level', 'GetMin'],
0xA3: ['Query Power On', 'GetPwrOn'],
0xA4: ['Query Fail Level', 'GetFail'],
0xA5: ['Query Fade Rate', 'GetRate'],
0xA6: ['Query Power Fail', 'PwrFail'],
0xC0: ['Query Groups 0-7', 'GetGrpsL'],
0xC1: ['Query Groups 7-15', 'GetGrpsH'],
0xC2: ['Query BRNH', 'BRNH'],
0xC3: ['Query BRNM', 'BRNM'],
0xC4: ['Query BRNL', 'BRNL'],
0xC5: ['Query Memory', 'GetMem'],
}
# DALI device type 8
dali_device_type8 = {
0xE0: ['Set Temp X-Y Coordinate', 'Set X-Y'],
0xE2: ['Activate Colour Set point', 'Activate SetPoint'],
0xE7: ['Set Colour Temperature Tc', 'DTRs->ColTemp'],
0xF9: ['Query Features', 'QryFeats'],
0xFA: ['Query Current Setpoint Colour', 'GetSetPoint'],
}

View File

@@ -0,0 +1,245 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Jeremy Swanson <jeremy@rakocontrols.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from .lists import *
class SamplerateError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = 'dali'
name = 'DALI'
longname = 'Digital Addressable Lighting Interface'
desc = 'Digital Addressable Lighting Interface (DALI) protocol.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Embedded/industrial', 'Lighting']
channels = (
{'id': 'dali', 'name': 'DALI', 'desc': 'DALI data line'},
)
options = (
{'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
'values': ('active-low', 'active-high')},
)
annotations = (
('bit', 'Bit'),
('startbit', 'Startbit'),
('sbit', 'Select bit'),
('ybit', 'Individual or group'),
('address', 'Address'),
('command', 'Command'),
('reply', 'Reply data'),
('raw', 'Raw data'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
('raw', 'Raw data', (7,)),
('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
self.dev_type = None
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.old_dali = 1 if self.options['polarity'] == 'active-low' else 0
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# One bit: 833.33us (one half low, one half high).
# This is how may samples are in 1TE.
self.halfbit = int((self.samplerate * 0.0008333) / 2.0)
def putb(self, bit1, bit2, data):
ss, es = self.ss_es_bits[bit1][0], self.ss_es_bits[bit2][1]
self.put(ss, es, self.out_ann, data)
def handle_bits(self, length):
a, c, f, g, b = 0, 0, 0, 0, self.bits
# Individual raw bits.
for i in range(length):
if i == 0:
ss = max(0, self.bits[0][0])
else:
ss = self.ss_es_bits[i - 1][1]
es = self.bits[i][0] + (self.halfbit * 2)
self.ss_es_bits.append([ss, es])
self.putb(i, i, [0, ['%d' % self.bits[i][1]]])
# Bits[0:0]: Startbit
s = ['Startbit: %d' % b[0][1], 'ST: %d' % b[0][1], 'ST', 'S', 'S']
self.putb(0, 0, [1, s])
self.putb(0, 0, [7, s])
# Bits[1:8]
for i in range(8):
f |= (b[1 + i][1] << (7 - i))
if length == 9: # BACKWARD Frame
s = ['Reply: %02X' % f, 'Rply: %02X' % f,
'Rep: %02X' % f, 'R: %02X' % f, 'R']
self.putb(1, 8, [7, s])
s = ['Reply: %d' % f, 'Rply: %d' % f,
'Rep: %d' % f, 'R: %d' % f, 'R']
self.putb(1, 8, [6, s])
return
# FORWARD FRAME
# Bits[9:16]: Command/data (MSB-first)
for i in range(8):
c |= (b[9 + i][1] << (7 - i))
# Raw output
s = ['Raw data: %02X' % f, 'Raw: %02X' % f,
'Raw: %02X' % f, 'R: %02X' % f, 'R']
self.putb(1, 8, [7, s])
s = ['Raw data: %02X' % c, 'Raw: %02X' % c,
'Raw: %02X' % c, 'R: %02X' % c, 'R']
self.putb(9, 16, [7, s])
# Bits[8:8]: Select bit
# s = ['Selectbit: %d' % b[8][1], 'SEL: %d' % b[8][1], 'SEL', 'SE', 'S']
if b[8][1] == 1:
s = ['Command', 'Comd', 'COM', 'CO', 'C']
else:
s = ['Arc Power Level', 'Arc Pwr', 'ARC', 'AC', 'A']
self.putb(8, 8, [1, s])
# f &= 254 # Clear the select bit.
if f >= 254: # BROADCAST
s = ['BROADCAST', 'Brdcast', 'BC', 'B', 'B']
self.putb(1, 7, [5, s])
elif f >= 160: # Extended command 0b10100000
if f == 0xC1: # DALI_ENABLE_DEVICE_TYPE_X
self.dev_type = -1
x = extended_commands.get(f, ['Unknown', 'Unk'])
s = ['Extended Command: %02X (%s)' % (f, x[0]),
'XC: %02X (%s)' % (f, x[1]),
'XC: %02X' % f, 'X: %02X' % f, 'X']
self.putb(1, 8, [5, s])
elif f >= 128: # Group
# Bits[1:1]: Ybit
s = ['YBit: %d' % b[1][1], 'YB: %d' % b[1][1], 'YB', 'Y', 'Y']
self.putb(1, 1, [3, s])
g = (f & 127) >> 1
s = ['Group address: %d' % g, 'Group: %d' % g,
'GP: %d' % g, 'G: %d' % g, 'G']
self.putb(2,7, [4, s])
else: # Short address
# Bits[1:1]: Ybit
s = ['YBit: %d' % b[1][1], 'YB: %d' % b[1][1], 'YB', 'Y', 'Y']
self.putb(1, 1, [3, s])
a = f >> 1
s = ['Short address: %d' % a, 'Addr: %d' % a,
'Addr: %d' % a, 'A: %d' % a, 'A']
self.putb(2, 7, [4, s])
# Bits[9:16]: Command/data (MSB-first)
if f >= 160 and f < 254:
if self.dev_type == -1:
self.dev_type = c
s = ['Type: %d' % c, 'Typ: %d' % c,
'Typ: %d' % c, 'T: %d' % c, 'D']
else:
self.dev_type = None
s = ['Data: %d' % c, 'Dat: %d' % c,
'Dat: %d' % c, 'D: %d' % c, 'D']
elif b[8][1] == 1:
un = c & 0xF0
ln = c & 0x0F
if un == 0x10: # Set scene command
x = ['Recall Scene %d' % ln, 'SC %d' % ln]
elif un == 0x40:
x = ['Store DTR as Scene %d' % ln, 'SC %d = DTR' % ln]
elif un == 0x50:
x = ['Delete Scene %d' % ln, 'DEL SC %d' % ln]
elif un == 0x60:
x = ['Add to Group %d' % ln, 'Grp %d Add' % ln]
elif un == 0x70:
x = ['Remove from Group %d' % ln, 'Grp %d Del' % ln]
elif un == 0xB0:
x = ['Query Scene %d Level' % ln, 'Sc %d Level' % ln]
elif c >= 224: # Application specific commands
if self.dev_type == 8:
x = dali_device_type8.get(c, ['Unknown App', 'Unk'])
else:
x = ['Application Specific Command %d' % c, 'App Cmd %d' % c]
else:
x = dali_commands.get(c, ['Unknown', 'Unk'])
s = ['Command: %d (%s)' % (c, x[0]), 'Com: %d (%s)' % (c, x[1]),
'Com: %d' % c, 'C: %d' % c, 'C']
else:
s = ['Arc Power Level: %d' % c, 'Level: %d' % c,
'Lev: %d' % c, 'L: %d' % c, 'L']
self.putb(9, 16, [5, s])
def reset_decoder_state(self):
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
bit = 0
while True:
# TODO: Come up with more appropriate self.wait() conditions.
(dali,) = self.wait()
if self.options['polarity'] == 'active-high':
dali ^= 1 # Invert.
# State machine.
if self.state == 'IDLE':
# Wait for any edge (rising or falling).
if self.old_dali == dali:
continue
self.edges.append(self.samplenum)
self.state = 'PHASE0'
self.old_dali = dali
continue
if self.old_dali != dali:
self.edges.append(self.samplenum)
elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)):
self.edges.append(self.samplenum - int(self.halfbit * 0.5))
else:
continue
bit = self.old_dali
if self.state == 'PHASE0':
self.phase0 = bit
self.state = 'PHASE1'
elif self.state == 'PHASE1':
if (bit == 1) and (self.phase0 == 1): # Stop bit.
if len(self.bits) == 17 or len(self.bits) == 9:
# Forward or Backward.
self.handle_bits(len(self.bits))
self.reset_decoder_state() # Reset upon errors.
continue
else:
self.bits.append([self.edges[-3], bit])
self.state = 'PHASE0'
self.old_dali = dali

View File

@@ -0,0 +1,28 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This PD decodes the DCF77 protocol (a European long-wave time signal that
uses a 77.5kHz carrier frequency).
Details:
http://en.wikipedia.org/wiki/DCF77
'''
from .pd import Decoder

View File

@@ -0,0 +1,311 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2016 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
import calendar
from common.srdhelper import bcd2int
class SamplerateError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = 'dcf77'
name = 'DCF77'
longname = 'DCF77 time protocol'
desc = 'European longwave time signal (77.5kHz carrier signal).'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Clock/timing']
channels = (
{'id': 'data', 'name': 'DATA', 'desc': 'DATA line'},
)
annotations = (
('start-of-minute', 'Start of minute'),
('special-bits', 'Special bits (civil warnings, weather forecast)'),
('call-bit', 'Call bit'),
('summer-time', 'Summer time announcement'),
('cest', 'CEST bit'),
('cet', 'CET bit'),
('leap-second', 'Leap second bit'),
('start-of-time', 'Start of encoded time'),
('minute', 'Minute'),
('minute-parity', 'Minute parity bit'),
('hour', 'Hour'),
('hour-parity', 'Hour parity bit'),
('day', 'Day of month'),
('day-of-week', 'Day of week'),
('month', 'Month'),
('year', 'Year'),
('date-parity', 'Date parity bit'),
('raw-bits', 'Raw bits'),
('unknown-bits', 'Unknown bits'),
('warnings', 'Human-readable warnings'),
)
annotation_rows = (
('bits', 'Bits', (17, 18)),
('fields', 'Fields', tuple(range(0, 16 + 1))),
('warnings', 'Warnings', (19,)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.state = 'WAIT FOR RISING EDGE'
self.ss_bit = self.ss_bit_old = self.es_bit = self.ss_block = 0
self.datebits = []
self.bitcount = 0 # Counter for the DCF77 bits (0..58)
self.dcf77_bitnumber_is_known = 0
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def putx(self, data):
# Annotation for a single DCF77 bit.
self.put(self.ss_bit, self.es_bit, self.out_ann, data)
def putb(self, data):
# Annotation for a multi-bit DCF77 field.
self.put(self.ss_block, self.samplenum, self.out_ann, data)
# TODO: Which range to use? Only the 100ms/200ms or full second?
def handle_dcf77_bit(self, bit):
c = self.bitcount
# Create one annotation for each DCF77 bit (containing the 0/1 value).
# Use 'Unknown DCF77 bit x: val' if we're not sure yet which of the
# 0..58 bits it is (because we haven't seen a 'new minute' marker yet).
# Otherwise, use 'DCF77 bit x: val'.
s = 'B' if self.dcf77_bitnumber_is_known else 'Unknown b'
ann = 17 if self.dcf77_bitnumber_is_known else 18
self.putx([ann, ['%sit %d: %d' % (s, c, bit), '%d' % bit]])
# If we're not sure yet which of the 0..58 DCF77 bits we have, return.
# We don't want to decode bogus data.
if not self.dcf77_bitnumber_is_known:
return
# Collect bits 36-58, we'll need them for a parity check later.
if c in range(36, 58 + 1):
self.datebits.append(bit)
# Output specific "decoded" annotations for the respective DCF77 bits.
if c == 0:
# Start of minute: DCF bit 0.
if bit == 0:
self.putx([0, ['Start of minute (always 0)',
'Start of minute', 'SoM']])
else:
self.putx([19, ['Start of minute != 0', 'SoM != 0']])
elif c in range(1, 14 + 1):
# Special bits (civil warnings, weather forecast): DCF77 bits 1-14.
if c == 1:
self.tmp = bit
self.ss_block = self.ss_bit
else:
self.tmp |= (bit << (c - 1))
if c == 14:
s = '{:014b}'.format(self.tmp)
self.putb([1, ['Special bits: %s' % s, 'SB: %s' % s]])
elif c == 15:
s = '' if (bit == 1) else 'not '
self.putx([2, ['Call bit: %sset' % s, 'CB: %sset' % s]])
# TODO: Previously this bit indicated use of the backup antenna.
elif c == 16:
s = '' if (bit == 1) else 'not '
x = 'yes' if (bit == 1) else 'no'
self.putx([3, ['Summer time announcement: %sactive' % s,
'Summer time: %sactive' % s,
'Summer time: %s' % x, 'ST: %s' % x]])
elif c == 17:
s = '' if (bit == 1) else 'not '
x = 'yes' if (bit == 1) else 'no'
self.putx([4, ['CEST: %sin effect' % s, 'CEST: %s' % x]])
elif c == 18:
s = '' if (bit == 1) else 'not '
x = 'yes' if (bit == 1) else 'no'
self.putx([5, ['CET: %sin effect' % s, 'CET: %s' % x]])
elif c == 19:
s = '' if (bit == 1) else 'not '
x = 'yes' if (bit == 1) else 'no'
self.putx([6, ['Leap second announcement: %sactive' % s,
'Leap second: %sactive' % s,
'Leap second: %s' % x, 'LS: %s' % x]])
elif c == 20:
# Start of encoded time: DCF bit 20.
if bit == 1:
self.putx([7, ['Start of encoded time (always 1)',
'Start of encoded time', 'SoeT']])
else:
self.putx([19, ['Start of encoded time != 1', 'SoeT != 1']])
elif c in range(21, 27 + 1):
# Minutes (0-59): DCF77 bits 21-27 (BCD format).
if c == 21:
self.tmp = bit
self.ss_block = self.ss_bit
else:
self.tmp |= (bit << (c - 21))
if c == 27:
m = bcd2int(self.tmp)
self.putb([8, ['Minutes: %d' % m, 'Min: %d' % m]])
elif c == 28:
# Even parity over minute bits (21-28): DCF77 bit 28.
self.tmp |= (bit << (c - 21))
parity = bin(self.tmp).count('1')
s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
self.putx([9, ['Minute parity: %s' % s, 'Min parity: %s' % s]])
elif c in range(29, 34 + 1):
# Hours (0-23): DCF77 bits 29-34 (BCD format).
if c == 29:
self.tmp = bit
self.ss_block = self.ss_bit
else:
self.tmp |= (bit << (c - 29))
if c == 34:
self.putb([10, ['Hours: %d' % bcd2int(self.tmp)]])
elif c == 35:
# Even parity over hour bits (29-35): DCF77 bit 35.
self.tmp |= (bit << (c - 29))
parity = bin(self.tmp).count('1')
s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
self.putx([11, ['Hour parity: %s' % s]])
elif c in range(36, 41 + 1):
# Day of month (1-31): DCF77 bits 36-41 (BCD format).
if c == 36:
self.tmp = bit
self.ss_block = self.ss_bit
else:
self.tmp |= (bit << (c - 36))
if c == 41:
self.putb([12, ['Day: %d' % bcd2int(self.tmp)]])
elif c in range(42, 44 + 1):
# Day of week (1-7): DCF77 bits 42-44 (BCD format).
# A value of 1 means Monday, 7 means Sunday.
if c == 42:
self.tmp = bit
self.ss_block = self.ss_bit
else:
self.tmp |= (bit << (c - 42))
if c == 44:
d = bcd2int(self.tmp)
try:
dn = calendar.day_name[d - 1] # day_name[0] == Monday
self.putb([13, ['Day of week: %d (%s)' % (d, dn),
'DoW: %d (%s)' % (d, dn)]])
except IndexError:
self.putb([19, ['Day of week: %d (%s)' % (d, 'invalid'),
'DoW: %d (%s)' % (d, 'inv')]])
elif c in range(45, 49 + 1):
# Month (1-12): DCF77 bits 45-49 (BCD format).
if c == 45:
self.tmp = bit
self.ss_block = self.ss_bit
else:
self.tmp |= (bit << (c - 45))
if c == 49:
m = bcd2int(self.tmp)
try:
mn = calendar.month_name[m] # month_name[1] == January
self.putb([14, ['Month: %d (%s)' % (m, mn),
'Mon: %d (%s)' % (m, mn)]])
except IndexError:
self.putb([19, ['Month: %d (%s)' % (m, 'invalid'),
'Mon: %d (%s)' % (m, 'inv')]])
elif c in range(50, 57 + 1):
# Year (0-99): DCF77 bits 50-57 (BCD format).
if c == 50:
self.tmp = bit
self.ss_block = self.ss_bit
else:
self.tmp |= (bit << (c - 50))
if c == 57:
self.putb([15, ['Year: %d' % bcd2int(self.tmp)]])
elif c == 58:
# Even parity over date bits (36-58): DCF77 bit 58.
parity = self.datebits.count(1)
s = 'OK' if ((parity % 2) == 0) else 'INVALID!'
self.putx([16, ['Date parity: %s' % s, 'DP: %s' % s]])
self.datebits = []
else:
self.putx([19, ['Invalid DCF77 bit: %d' % c,
'Invalid bit: %d' % c, 'Inv: %d' % c]])
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
while True:
if self.state == 'WAIT FOR RISING EDGE':
# Wait until the next rising edge occurs.
self.wait({0: 'r'})
# Save the sample number where the DCF77 bit begins.
self.ss_bit = self.samplenum
# Calculate the length (in ms) between two rising edges.
len_edges = self.ss_bit - self.ss_bit_old
len_edges_ms = int((len_edges / self.samplerate) * 1000)
# The time between two rising edges is usually around 1000ms.
# For DCF77 bit 59, there is no rising edge at all, i.e. the
# time between DCF77 bit 59 and DCF77 bit 0 (of the next
# minute) is around 2000ms. Thus, if we see an edge with a
# 2000ms distance to the last one, this edge marks the
# beginning of a new minute (and DCF77 bit 0 of that minute).
if len_edges_ms in range(1600, 2400 + 1):
self.bitcount = 0
self.ss_bit_old = self.ss_bit
self.dcf77_bitnumber_is_known = 1
self.ss_bit_old = self.ss_bit
self.state = 'GET BIT'
elif self.state == 'GET BIT':
# Wait until the next falling edge occurs.
self.wait({0: 'f'})
# Save the sample number where the DCF77 bit ends.
self.es_bit = self.samplenum
# Calculate the length (in ms) of the current high period.
len_high = self.samplenum - self.ss_bit
len_high_ms = int((len_high / self.samplerate) * 1000)
# If the high signal was 100ms long, that encodes a 0 bit.
# If it was 200ms long, that encodes a 1 bit.
if len_high_ms in range(40, 160 + 1):
bit = 0
elif len_high_ms in range(161, 260 + 1):
bit = 1
else:
bit = -1
if bit in (0, 1):
self.handle_dcf77_bit(bit)
self.bitcount += 1
else:
self.putx([19, ['Invalid bit timing', 'Inv timing', 'Inv']])
self.state = 'WAIT FOR RISING EDGE'

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Fabian J. Stumpf <sigrok@fabianstumpf.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
DMX512 (Digital MultipleX 512) is a protocol based on RS485, used to control
professional lighting fixtures.
'''
from .pd import Decoder

View File

@@ -0,0 +1,179 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2016 Fabian J. Stumpf <sigrok@fabianstumpf.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
class Decoder(srd.Decoder):
api_version = 3
id = 'dmx512'
name = 'DMX512'
longname = 'Digital MultipleX 512'
desc = 'Digital MultipleX 512 (DMX512) lighting protocol.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
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')},
)
annotations = (
('bit', 'Bit'),
('break', 'Break'),
('mab', 'Mark after break'),
('startbit', 'Start bit'),
('stopbits', 'Stop bit'),
('startcode', 'Start code'),
('channel', 'Channel'),
('interframe', 'Interframe'),
('interpacket', 'Interpacket'),
('data', 'Data'),
('error', 'Error'),
)
annotation_rows = (
('name', 'Logical', (1, 2, 5, 6, 7, 8)),
('data', 'Data', (9,)),
('bits', 'Bits', (0, 3, 4)),
('errors', 'Errors', (10,)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.sample_usec = None
self.run_start = -1
self.state = 'FIND BREAK'
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
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

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2013 Matt Ranostay <mranostay@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'i2c' PD and decodes the Dallas DS1307
real-time clock (RTC) specific registers and commands.
'''
from .pd import Decoder

View File

@@ -0,0 +1,264 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2013 Matt Ranostay <mranostay@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## 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 <http://www.gnu.org/licenses/>.
##
import re
import sigrokdecode as srd
from common.srdhelper import bcd2int
days_of_week = (
'Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday',
)
regs = (
'Seconds', 'Minutes', 'Hours', 'Day', 'Date', 'Month', 'Year',
'Control', 'RAM',
)
bits = (
'Clock halt', 'Seconds', 'Reserved', 'Minutes', '12/24 hours', 'AM/PM',
'Hours', 'Day', 'Date', 'Month', 'Year', 'OUT', 'SQWE', 'RS', 'RAM',
)
rates = {
0b00: '1Hz',
0b01: '4096Hz',
0b10: '8192Hz',
0b11: '32768Hz',
}
DS1307_I2C_ADDRESS = 0x68
def regs_and_bits():
l = [('reg-' + r.lower(), r + ' register') for r in regs]
l += [('bit-' + re.sub('\/| ', '-', b).lower(), b + ' bit') for b in bits]
return tuple(l)
class Decoder(srd.Decoder):
api_version = 3
id = 'ds1307'
name = 'DS1307'
longname = 'Dallas DS1307'
desc = 'Dallas DS1307 realtime clock module protocol.'
license = 'gplv2+'
inputs = ['i2c']
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'),
)
annotation_rows = (
('bits', 'Bits', tuple(range(9, 24))),
('regs', 'Registers', tuple(range(9))),
('date-time', 'Date/time', (24, 25, 26, 27)),
('warnings', 'Warnings', (28,)),
)
def __init__(self):
self.reset()
def reset(self):
self.state = 'IDLE'
self.hours = -1
self.minutes = -1
self.seconds = -1
self.days = -1
self.date = -1
self.months = -1
self.years = -1
self.bits = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def putd(self, bit1, bit2, data):
self.put(self.bits[bit1][1], self.bits[bit2][2], self.out_ann, data)
def putr(self, bit):
self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann,
[11, ['Reserved bit', 'Reserved', 'Rsvd', 'R']])
def handle_reg_0x00(self, b): # Seconds (0-59) / Clock halt bit
self.putd(7, 0, [0, ['Seconds', 'Sec', 'S']])
ch = 1 if (b & (1 << 7)) else 0
self.putd(7, 7, [9, ['Clock halt: %d' % ch, 'Clk hlt: %d' % ch,
'CH: %d' % ch, 'CH']])
s = self.seconds = bcd2int(b & 0x7f)
self.putd(6, 0, [10, ['Second: %d' % s, 'Sec: %d' % s, 'S: %d' % s, 'S']])
def handle_reg_0x01(self, b): # Minutes (0-59)
self.putd(7, 0, [1, ['Minutes', 'Min', 'M']])
self.putr(7)
m = self.minutes = bcd2int(b & 0x7f)
self.putd(6, 0, [12, ['Minute: %d' % m, 'Min: %d' % m, 'M: %d' % m, 'M']])
def handle_reg_0x02(self, b): # Hours (1-12+AM/PM or 0-23)
self.putd(7, 0, [2, ['Hours', 'H']])
self.putr(7)
ampm_mode = True if (b & (1 << 6)) else False
if ampm_mode:
self.putd(6, 6, [13, ['12-hour mode', '12h mode', '12h']])
a = 'PM' if (b & (1 << 5)) else 'AM'
self.putd(5, 5, [14, [a, a[0]]])
h = self.hours = bcd2int(b & 0x1f)
self.putd(4, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
else:
self.putd(6, 6, [13, ['24-hour mode', '24h mode', '24h']])
h = self.hours = bcd2int(b & 0x3f)
self.putd(5, 0, [15, ['Hour: %d' % h, 'H: %d' % h, 'H']])
def handle_reg_0x03(self, b): # Day / day of week (1-7)
self.putd(7, 0, [3, ['Day of week', 'Day', 'D']])
for i in (7, 6, 5, 4, 3):
self.putr(i)
w = self.days = bcd2int(b & 0x07)
ws = days_of_week[self.days - 1]
self.putd(2, 0, [16, ['Weekday: %s' % ws, 'WD: %s' % ws, 'WD', 'W']])
def handle_reg_0x04(self, b): # Date (1-31)
self.putd(7, 0, [4, ['Date', 'D']])
for i in (7, 6):
self.putr(i)
d = self.date = bcd2int(b & 0x3f)
self.putd(5, 0, [17, ['Date: %d' % d, 'D: %d' % d, 'D']])
def handle_reg_0x05(self, b): # Month (1-12)
self.putd(7, 0, [5, ['Month', 'Mon', 'M']])
for i in (7, 6, 5):
self.putr(i)
m = self.months = bcd2int(b & 0x1f)
self.putd(4, 0, [18, ['Month: %d' % m, 'Mon: %d' % m, 'M: %d' % m, 'M']])
def handle_reg_0x06(self, b): # Year (0-99)
self.putd(7, 0, [6, ['Year', 'Y']])
y = self.years = bcd2int(b & 0xff)
self.years += 2000
self.putd(7, 0, [19, ['Year: %d' % y, 'Y: %d' % y, 'Y']])
def handle_reg_0x07(self, b): # Control Register
self.putd(7, 0, [7, ['Control', 'Ctrl', 'C']])
for i in (6, 5, 3, 2):
self.putr(i)
o = 1 if (b & (1 << 7)) else 0
s = 1 if (b & (1 << 4)) else 0
s2 = 'en' if (b & (1 << 4)) else 'dis'
r = rates[b & 0x03]
self.putd(7, 7, [20, ['Output control: %d' % o,
'OUT: %d' % o, 'O: %d' % o, 'O']])
self.putd(4, 4, [21, ['Square wave output: %sabled' % s2,
'SQWE: %sabled' % s2, 'SQWE: %d' % s, 'S: %d' % s, 'S']])
self.putd(1, 0, [22, ['Square wave output rate: %s' % r,
'Square wave rate: %s' % r, 'SQW rate: %s' % r, 'Rate: %s' % r,
'RS: %s' % s, 'RS', 'R']])
def handle_reg_0x3f(self, b): # RAM (bytes 0x08-0x3f)
self.putd(7, 0, [8, ['RAM', 'R']])
self.putd(7, 0, [23, ['SRAM: 0x%02X' % b, '0x%02X' % b]])
def output_datetime(self, cls, rw):
# TODO: Handle read/write of only parts of these items.
d = '%s, %02d.%02d.%4d %02d:%02d:%02d' % (
days_of_week[self.days - 1], self.date, self.months,
self.years, self.hours, self.minutes, self.seconds)
self.put(self.ss_block, self.es, self.out_ann,
[cls, ['%s date/time: %s' % (rw, d)]])
def handle_reg(self, b):
r = self.reg if self.reg < 8 else 0x3f
fn = getattr(self, 'handle_reg_0x%02x' % r)
fn(b)
# Honor address auto-increment feature of the DS1307. When the
# address reaches 0x3f, it will wrap around to address 0.
self.reg += 1
if self.reg > 0x3f:
self.reg = 0
def is_correct_chip(self, addr):
if addr == DS1307_I2C_ADDRESS:
return True
self.put(self.ss_block, self.es, self.out_ann,
[28, ['Ignoring non-DS1307 data (slave 0x%02X)' % addr]])
return False
def decode(self, ss, es, data):
cmd, databyte = data
# Collect the 'BITS' packet, then return. The next packet is
# guaranteed to belong to these bits we just stored.
if cmd == 'BITS':
self.bits = databyte
return
# Store the start/end samples of this I²C packet.
self.ss, self.es = ss, es
# State machine.
if self.state == 'IDLE':
# Wait for an I²C START condition.
if cmd != 'START':
return
self.state = 'GET SLAVE ADDR'
self.ss_block = ss
elif self.state == 'GET SLAVE ADDR':
# Wait for an address write operation.
if cmd != 'ADDRESS WRITE':
return
if not self.is_correct_chip(databyte):
self.state = 'IDLE'
return
self.state = 'GET REG ADDR'
elif self.state == 'GET REG ADDR':
# Wait for a data write (master selects the slave register).
if cmd != 'DATA WRITE':
return
self.reg = databyte
self.state = 'WRITE RTC REGS'
elif self.state == 'WRITE RTC REGS':
# If we see a Repeated Start here, it's an RTC read.
if cmd == 'START REPEAT':
self.state = 'READ RTC REGS'
return
# Otherwise: Get data bytes until a STOP condition occurs.
if cmd == 'DATA WRITE':
self.handle_reg(databyte)
elif cmd == 'STOP':
self.output_datetime(25, 'Written')
self.state = 'IDLE'
elif self.state == 'READ RTC REGS':
# Wait for an address read operation.
if cmd != 'ADDRESS READ':
return
if not self.is_correct_chip(databyte):
self.state = 'IDLE'
return
self.state = 'READ RTC REGS2'
elif self.state == 'READ RTC REGS2':
if cmd == 'DATA READ':
self.handle_reg(databyte)
elif cmd == 'STOP':
self.output_datetime(24, 'Read')
self.state = 'IDLE'

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'onewire_network' PD and decodes the
Maxim DS2408 1-Wire 8-channel addressable switch protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,129 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2019 Mariusz Bialonczyk <manio@skyboo.net>
##
## 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 <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
# Dictionary of FUNCTION commands and their names.
command = {
0xf0: 'Read PIO Registers',
0xf5: 'Channel Access Read',
0x5a: 'Channel Access Write',
0xcc: 'Write Conditional Search Register',
0xc3: 'Reset Activity Latches',
0x3c: 'Disable Test Mode',
}
class Decoder(srd.Decoder):
api_version = 3
id = 'ds2408'
name = 'DS2408'
longname = 'Maxim DS2408'
desc = '1-Wire 8-channel addressable switch.'
license = 'gplv2+'
inputs = ['onewire_network']
outputs = []
tags = ['Embedded/industrial', 'IC']
annotations = (
('text', 'Human-readable text'),
)
def __init__(self):
self.reset()
def reset(self):
# Bytes for function command.
self.bytes = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def decode(self, ss, es, data):
code, val = data
if code == 'RESET/PRESENCE':
self.ss, self.es = ss, es
self.putx([0, ['Reset/presence: %s'
% ('true' if val else 'false')]])
self.bytes = []
elif code == 'ROM':
self.ss, self.es = ss, es
family_code = val & 0xff
self.putx([0, ['ROM: 0x%016x (family code 0x%02x)' % (val, family_code)]])
self.bytes = []
elif code == 'DATA':
self.bytes.append(val)
if 1 == len(self.bytes):
self.ss, self.es = ss, es
if val not in command:
self.putx([0, ['Unrecognized command: 0x%02x' % val]])
else:
self.putx([0, ['%s (0x%02x)' % (command[val], val)]])
elif 0xf0 == self.bytes[0]: # Read PIO Registers
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
self.putx([0, ['Target address: 0x%04x'
% ((self.bytes[2] << 8) + self.bytes[1])]])
elif 3 < len(self.bytes):
self.ss, self.es = ss, es
self.putx([0, ['Data: 0x%02x' % self.bytes[-1]]])
elif 0xf5 == self.bytes[0]: # Channel Access Read
if 2 == len(self.bytes):
self.ss = ss
elif 2 < len(self.bytes):
self.ss, self.es = ss, es
self.putx([0, ['PIO sample: 0x%02x' % self.bytes[-1]]])
elif 0x5a == self.bytes[0]: # Channel Access Write
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
if (self.bytes[-1] == (self.bytes[-2] ^ 0xff)):
self.putx([0, ['Data: 0x%02x (bit-inversion correct: 0x%02x)' % (self.bytes[-2], self.bytes[-1])]])
else:
self.putx([0, ['Data error: second byte (0x%02x) is not bit-inverse of first (0x%02x)' % (self.bytes[-1], self.bytes[-2])]])
elif 3 < len(self.bytes):
self.ss, self.es = ss, es
if 0xaa == self.bytes[-1]:
self.putx([0, ['Success']])
elif 0xff == self.bytes[-1]:
self.putx([0, ['Fail New State']])
elif 0xcc == self.bytes[0]: # Write Conditional Search Register
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
self.putx([0, ['Target address: 0x%04x'
% ((self.bytes[2] << 8) + self.bytes[1])]])
elif 3 < len(self.bytes):
self.ss, self.es = ss, es
self.putx([0, ['Data: 0x%02x' % self.bytes[-1]]])
elif 0xc3 == self.bytes[0]: # Reset Activity Latches
if 2 == len(self.bytes):
self.ss = ss
elif 2 < len(self.bytes):
self.ss, self.es = ss, es
if 0xaa == self.bytes[-1]:
self.putx([0, ['Success']])
else:
self.putx([0, ['Invalid byte']])

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'onewire_network' PD and decodes the
Maxim DS243x (1-Wire EEPROM) protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,270 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
## Copyright (C) 2017 Soeren Apel <soeren@apelpie.net>
##
## 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 <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
# Dictionary of FUNCTION commands and their names.
commands_2432 = {
0x0f: 'Write scratchpad',
0xaa: 'Read scratchpad',
0x55: 'Copy scratchpad',
0xf0: 'Read memory',
0x5a: 'Load first secret',
0x33: 'Compute next secret',
0xa5: 'Read authenticated page',
}
commands_2433 = {
0x0f: 'Write scratchpad',
0xaa: 'Read scratchpad',
0x55: 'Copy scratchpad',
0xf0: 'Read memory',
}
# Maxim DS243x family code, present at the end of the ROM code.
family_codes = {
0x33: ('DS2432', commands_2432),
0x23: ('DS2433', commands_2433),
}
# Calculate the CRC-16 checksum.
# Initial value: 0x0000, xor-in: 0x0000, polynom 0x8005, xor-out: 0xffff.
def crc16(byte_array):
reverse = 0xa001 # Use the reverse polynom to make algo simpler.
crc = 0x0000 # Initial value.
# Reverse CRC calculation.
for byte in byte_array:
for bit in range(8):
if (byte ^ crc) & 1:
crc = (crc >> 1) ^ reverse
else:
crc >>= 1
byte >>= 1
crc ^= 0xffff # Invert CRC.
return crc
class Decoder(srd.Decoder):
api_version = 3
id = 'ds243x'
name = 'DS243x'
longname = 'Maxim DS2432/3'
desc = 'Maxim DS243x series 1-Wire EEPROM protocol.'
license = 'gplv2+'
inputs = ['onewire_network']
outputs = []
tags = ['IC', 'Memory']
annotations = (
('text', 'Human-readable text'),
)
binary = (
('mem_read', 'Data read from memory'),
)
def __init__(self):
self.reset()
def reset(self):
# Bytes for function command.
self.bytes = []
self.family_code = None
self.family = ''
self.commands = commands_2432 # Use max command set until we know better.
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def decode(self, ss, es, data):
code, val = data
if code == 'RESET/PRESENCE':
self.ss, self.es = ss, es
self.putx([0, ['Reset/presence: %s'
% ('true' if val else 'false')]])
self.bytes = []
elif code == 'ROM':
self.ss, self.es = ss, es
self.family_code = val & 0xff
s = None
if self.family_code in family_codes:
self.family, self.commands = family_codes[val & 0xff]
s = 'is 0x%02x, %s detected' % (self.family_code, self.family)
else:
s = '0x%02x unknown' % (self.family_code)
self.putx([0, ['ROM: 0x%016x (%s)' % (val, 'family code ' + s),
'ROM: 0x%016x (%s)' % (val, self.family)]])
self.bytes = []
elif code == 'DATA':
self.bytes.append(val)
if 1 == len(self.bytes):
self.ss, self.es = ss, es
if val not in self.commands:
self.putx([0, ['Unrecognized command: 0x%02x' % val]])
else:
self.putx([0, ['Function command: %s (0x%02x)'
% (self.commands[val], val)]])
elif 0x0f == self.bytes[0]: # Write scratchpad
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
self.putx([0, ['Target address: 0x%04x'
% ((self.bytes[2] << 8) + self.bytes[1])]])
elif 4 == len(self.bytes):
self.ss = ss
elif 11 == len(self.bytes):
self.es = es
self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
for n in self.bytes[3:11]))]])
elif 12 == len(self.bytes):
self.ss = ss
elif 13 == len(self.bytes):
self.es = es
self.putx([0, ['CRC: '
+ ('ok' if crc16(self.bytes[0:11]) == (self.bytes[11]
+ (self.bytes[12] << 8)) else 'error')]])
elif 0xaa == self.bytes[0]: # Read scratchpad
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
self.putx([0, ['Target address: 0x%04x'
% ((self.bytes[2] << 8) + self.bytes[1])]])
elif 4 == len(self.bytes):
self.ss, self.es = ss, es
self.putx([0, ['Data status (E/S): 0x%02x'
% (self.bytes[3])]])
elif 5 == len(self.bytes):
self.ss = ss
elif 12 == len(self.bytes):
self.es = es
self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
for n in self.bytes[4:12]))]])
elif 13 == len(self.bytes):
self.ss = ss
elif 14 == len(self.bytes):
self.es = es
self.putx([0, ['CRC: '
+ ('ok' if crc16(self.bytes[0:12]) == (self.bytes[12]
+ (self.bytes[13] << 8)) else 'error')]])
elif 0x5a == self.bytes[0]: # Load first secret
if 2 == len(self.bytes):
self.ss = ss
elif 4 == len(self.bytes):
self.es = es
self.putx([0, ['Authorization pattern (TA1, TA2, E/S): '
+ (','.join(format(n, '#04x')
for n in self.bytes[1:4]))]])
elif 4 < len(self.bytes):
self.ss, self.es = ss, es
if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
self.putx([0, ['End of operation']])
elif 0x33 == self.bytes[0]: # Compute next secret
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
self.putx([0, ['Target address: 0x%04x'
% ((self.bytes[2] << 8) + self.bytes[1])]])
elif 3 < len(self.bytes):
self.ss, self.es = ss, es
if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
self.putx([0, ['End of operation']])
elif 0x55 == self.bytes[0]: # Copy scratchpad
if 2 == len(self.bytes):
self.ss = ss
elif 4 == len(self.bytes):
self.es = es
self.putx([0, ['Authorization pattern (TA1, TA2, E/S): '
+ (','.join(format(n, '#04x')
for n in self.bytes[1:4]))]])
elif 5 == len(self.bytes):
self.ss = ss
elif 24 == len(self.bytes):
self.es = es
mac = ','.join(format(n, '#04x') for n in self.bytes[4:24])
self.putx([0, ['Message authentication code: ' + mac,
'MAC: ' + mac]])
elif 24 < len(self.bytes):
self.ss, self.es = ss, es
if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
self.putx([0, ['Operation succeeded']])
elif (0 == self.bytes[-1]):
self.putx([0, ['Operation failed']])
elif 0xa5 == self.bytes[0]: # Read authenticated page
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
self.putx([0, ['Target address: 0x%04x'
% ((self.bytes[2] << 8) + self.bytes[1])]])
elif 4 == len(self.bytes):
self.ss = ss
elif 35 == len(self.bytes):
self.es = es
self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
for n in self.bytes[3:35]))]])
elif 36 == len(self.bytes):
self.ss, self.es = ss, es
self.putx([0, ['Padding: '
+ ('ok' if 0xff == self.bytes[-1] else 'error')]])
elif 37 == len(self.bytes):
self.ss = ss
elif 38 == len(self.bytes):
self.es = es
self.putx([0, ['CRC: '
+ ('ok' if crc16(self.bytes[0:36]) == (self.bytes[36]
+ (self.bytes[37] << 8)) else 'error')]])
elif 39 == len(self.bytes):
self.ss = ss
elif 58 == len(self.bytes):
self.es = es
mac = ','.join(format(n, '#04x') for n in self.bytes[38:58])
self.putx([0, ['Message authentication code: ' + mac,
'MAC: ' + mac]])
elif 59 == len(self.bytes):
self.ss = ss
elif 60 == len(self.bytes):
self.es = es
self.putx([0, ['MAC CRC: '
+ ('ok' if crc16(self.bytes[38:58]) == (self.bytes[58]
+ (self.bytes[59] << 8)) else 'error')]])
elif 60 < len(self.bytes):
self.ss, self.es = ss, es
if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
self.putx([0, ['Operation completed']])
elif 0xf0 == self.bytes[0]: # Read memory
if 2 == len(self.bytes):
self.ss = ss
elif 3 == len(self.bytes):
self.es = es
self.putx([0, ['Target address: 0x%04x'
% ((self.bytes[2] << 8) + self.bytes[1])]])
elif 3 < len(self.bytes):
self.ss, self.es = ss, es
self.putx([0, ['Data: 0x%02x' % (self.bytes[-1])]])
bdata = self.bytes[-1].to_bytes(1, byteorder='big')
self.put(ss, es, self.out_binary, [0, bdata])

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'onewire_network' PD and decodes the
Maxim DS28EA00 1-Wire digital thermometer protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,93 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Iztok Jeras <iztok.jeras@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
# Dictionary of FUNCTION commands and their names.
command = {
# Scratchpad
0x4e: 'Write scratchpad',
0xbe: 'Read scratchpad',
0x48: 'Copy scratchpad',
# Thermometer
0x44: 'Convert temperature',
0xb4: 'Read power mode',
0xb8: 'Recall EEPROM',
0xf5: 'PIO access read',
0xA5: 'PIO access write',
0x99: 'Chain',
}
class Decoder(srd.Decoder):
api_version = 3
id = 'ds28ea00'
name = 'DS28EA00'
longname = 'Maxim DS28EA00 1-Wire digital thermometer'
desc = '1-Wire digital thermometer with Sequence Detect and PIO.'
license = 'gplv2+'
inputs = ['onewire_network']
outputs = []
tags = ['IC', 'Sensor']
annotations = (
('text', 'Human-readable text'),
)
def __init__(self):
self.reset()
def reset(self):
self.trn_beg = 0
self.trn_end = 0
self.state = 'ROM'
self.rom = 0x0000000000000000
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def putx(self, data):
self.put(self.ss, self.es, self.out_ann, data)
def decode(self, ss, es, data):
code, val = data
self.ss, self.es = ss, es
# State machine.
if code == 'RESET/PRESENCE':
self.putx([0, ['Reset/presence: %s'
% ('true' if val else 'false')]])
self.state = 'ROM'
elif code == 'ROM':
self.rom = val
self.putx([0, ['ROM: 0x%016x' % (val)]])
self.state = 'COMMAND'
elif code == 'DATA':
if self.state == 'COMMAND':
if val not in command:
self.putx([0, ['Unrecognized command: 0x%02x' % val]])
return
self.putx([0, ['Function command: 0x%02x \'%s\''
% (val, command[val])]])
self.state = command[val].upper()
elif self.state == 'READ SCRATCHPAD':
self.putx([0, ['Scratchpad data: 0x%02x' % val]])
elif self.state == 'CONVERT TEMPERATURE':
self.putx([0, ['Temperature conversion status: 0x%02x' % val]])
elif self.state in [s.upper() for s in command.values()]:
self.putx([0, ['TODO \'%s\': 0x%02x' % (self.state, val)]])

View File

@@ -0,0 +1,24 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Jeremy Swanson <jeremy@rakocontrols.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
'''
DSI is a biphase/manchester based lighting control protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,157 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Jeremy Swanson <jeremy@rakocontrols.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
class SamplerateError(Exception):
pass
class Decoder(srd.Decoder):
api_version = 3
id = 'dsi'
name = 'DSI'
longname = 'Digital Serial Interface'
desc = 'Digital Serial Interface (DSI) lighting protocol.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Embedded/industrial', 'Lighting']
channels = (
{'id': 'dsi', 'name': 'DSI', 'desc': 'DSI data line'},
)
options = (
{'id': 'polarity', 'desc': 'Polarity', 'default': 'active-high',
'values': ('active-low', 'active-high')},
)
annotations = (
('bit', 'Bit'),
('startbit', 'Start bit'),
('level', 'Dimmer level'),
('raw', 'Raw data'),
)
annotation_rows = (
('bits', 'Bits', (0,)),
('raw', 'Raw data', (3,)),
('fields', 'Fields', (1, 2)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.old_dsi = 1 if self.options['polarity'] == 'active-low' else 0
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# One bit: 1666.7us (one half low, one half high).
# This is how many samples are in 1TE.
self.halfbit = int((self.samplerate * 0.0016667) / 2.0)
def putb(self, bit1, bit2, data):
ss, es = self.ss_es_bits[bit1][0], self.ss_es_bits[bit2][1]
self.put(ss, es, self.out_ann, data)
def handle_bits(self, length):
a, c, f, g, b = 0, 0, 0, 0, self.bits
# Individual raw bits.
for i in range(length):
if i == 0:
ss = max(0, self.bits[0][0])
else:
ss = self.ss_es_bits[i - 1][1]
es = self.bits[i][0] + (self.halfbit * 2)
self.ss_es_bits.append([ss, es])
self.putb(i, i, [0, ['%d' % self.bits[i][1]]])
# Bits[0:0]: Startbit
s = ['Startbit: %d' % b[0][1], 'ST: %d' % b[0][1], 'ST', 'S', 'S']
self.putb(0, 0, [1, s])
self.putb(0, 0, [3, s])
# Bits[1:8]
for i in range(8):
f |= (b[1 + i][1] << (7 - i))
g = f / 2.55
if length == 9: # BACKWARD Frame
s = ['Data: %02X' % f, 'Dat: %02X' % f,
'Dat: %02X' % f, 'D: %02X' % f, 'D']
self.putb(1, 8, [3, s])
s = ['Level: %d%%' % g, 'Lev: %d%%' % g,
'Lev: %d%%' % g, 'L: %d' % g, 'D']
self.putb(1, 8, [2, s])
return
def reset_decoder_state(self):
self.edges, self.bits, self.ss_es_bits = [], [], []
self.state = 'IDLE'
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
bit = 0
while True:
(self.dsi,) = self.wait()
if self.options['polarity'] == 'active-high':
self.dsi ^= 1 # Invert.
# State machine.
if self.state == 'IDLE':
# Wait for any edge (rising or falling).
if self.old_dsi == self.dsi:
continue
# Add in the first half of the start bit.
self.edges.append(self.samplenum - int(self.halfbit))
self.edges.append(self.samplenum)
# Start bit is 0->1.
self.phase0 = self.dsi ^ 1
self.state = 'PHASE1'
self.old_dsi = self.dsi
# Get the next sample point.
self.old_dsi = self.dsi
continue
if self.old_dsi != self.dsi:
self.edges.append(self.samplenum)
elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)):
self.edges.append(self.samplenum - int(self.halfbit * 0.5))
else:
continue
bit = self.old_dsi
if self.state == 'PHASE0':
self.phase0 = bit
self.state = 'PHASE1'
elif self.state == 'PHASE1':
if (bit == 1) and (self.phase0 == 1): # Stop bit.
if len(self.bits) == 17 or len(self.bits) == 9:
# Forward or Backward.
self.handle_bits(len(self.bits))
self.reset_decoder_state() # Reset upon errors.
continue
else:
self.bits.append([self.edges[-3], bit])
self.state = 'PHASE0'
self.old_dsi = self.dsi

View File

@@ -0,0 +1,35 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
##
'''
Extended Display Identification Data (EDID) 1.3 structure decoder.
The three-character vendor ID as specified in the EDID standard refers to
a Plug and Play ID (PNPID). The list of PNPID assignments is done by Microsoft.
The 'pnpids.txt' file included with this protocol decoder is derived from
the list of assignments downloadable from that page. It was retrieved in
January 2012.
Details:
https://en.wikipedia.org/wiki/Extended_display_identification_data
http://msdn.microsoft.com/en-us/windows/hardware/gg463195
'''
from .pd import Decoder

View File

@@ -0,0 +1 @@
extra-install pnpids.txt

View File

@@ -0,0 +1,668 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
# TODO:
# - EDID < 1.3
# - add short annotations
# - Signal level standard field in basic display parameters block
# - Additional color point descriptors
# - Additional standard timing descriptors
# - Extensions
import sigrokdecode as srd
import os
EDID_HEADER = [0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00]
OFF_VENDOR = 8
OFF_VERSION = 18
OFF_BASIC = 20
OFF_CHROM = 25
OFF_EST_TIMING = 35
OFF_STD_TIMING = 38
OFF_DET_TIMING = 54
OFF_NUM_EXT = 126
OFF_CHECKSUM = 127
# Pre-EDID established timing modes
est_modes = [
'720x400@70Hz',
'720x400@88Hz',
'640x480@60Hz',
'640x480@67Hz',
'640x480@72Hz',
'640x480@75Hz',
'800x600@56Hz',
'800x600@60Hz',
'800x600@72Hz',
'800x600@75Hz',
'832x624@75Hz',
'1024x768@87Hz(i)',
'1024x768@60Hz',
'1024x768@70Hz',
'1024x768@75Hz',
'1280x1024@75Hz',
'1152x870@75Hz',
]
# X:Y display aspect ratios, as used in standard timing modes
xy_ratio = [
(16, 10),
(4, 3),
(5, 4),
(16, 9),
]
# Annotation classes
ANN_FIELDS = 0
ANN_SECTIONS = 1
class Decoder(srd.Decoder):
api_version = 3
id = 'edid'
name = 'EDID'
longname = 'Extended Display Identification Data'
desc = 'Data structure describing display device capabilities.'
license = 'gplv3+'
inputs = ['i2c']
outputs = []
tags = ['Display', 'Memory', 'PC']
annotations = (
('fields', 'EDID structure fields'),
('sections', 'EDID structure sections'),
)
annotation_rows = (
('sections', 'Sections', (1,)),
('fields', 'Fields', (0,)),
)
def __init__(self):
self.reset()
def reset(self):
self.state = None
# Received data items, used as an index into samplenum/data
self.cnt = 0
# Start/end sample numbers per data item
self.sn = []
# Received data
self.cache = []
# Random read offset
self.offset = 0
# Extensions
self.extension = 0
self.ext_sn = [[]]
self.ext_cache = [[]]
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def decode(self, ss, es, data):
cmd, data = data
if cmd == 'ADDRESS WRITE' and data == 0x50:
self.state = 'offset'
self.ss = ss
return
if cmd == 'ADDRESS READ' and data == 0x50:
if self.extension > 0:
self.state = 'extensions'
s = str(self.extension)
t = ["Extension: " + s, "X: " + s, s]
else:
self.state = 'header'
t = ["EDID"]
self.put(ss, es, self.out_ann, [ANN_SECTIONS, t])
return
if cmd == 'DATA WRITE' and self.state == 'offset':
self.offset = data
self.extension = self.offset // 128
self.cnt = self.offset % 128
if self.extension > 0:
ext = self.extension - 1
l = len(self.ext_sn[ext])
# Truncate or extend to self.cnt.
self.sn = self.ext_sn[ext][0:self.cnt] + [0] * max(0, self.cnt - l)
self.cache = self.ext_cache[ext][0:self.cnt] + [0] * max(0, self.cnt - l)
else:
l = len(self.sn)
self.sn = self.sn[0:self.cnt] + [0] * max(0, self.cnt - l)
self.cache = self.cache[0:self.cnt] + [0] * max(0, self.cnt - l)
ss = self.ss if self.ss else ss
s = str(data)
t = ["Offset: " + s, "O: " + s, s]
self.put(ss, es, self.out_ann, [ANN_SECTIONS, t])
return
# We only care about actual data bytes that are read (for now).
if cmd != 'DATA READ':
return
self.cnt += 1
if self.extension > 0:
self.ext_sn[self.extension - 1].append([ss, es])
self.ext_cache[self.extension - 1].append(data)
else:
self.sn.append([ss, es])
self.cache.append(data)
if self.state is None or self.state == 'header':
# Wait for the EDID header
if self.cnt >= OFF_VENDOR:
if self.cache[-8:] == EDID_HEADER:
# Throw away any garbage before the header
self.sn = self.sn[-8:]
self.cache = self.cache[-8:]
self.cnt = 8
self.state = 'edid'
self.put(self.sn[0][0], es, self.out_ann,
[ANN_SECTIONS, ['Header']])
self.put(self.sn[0][0], es, self.out_ann,
[ANN_FIELDS, ['Header pattern']])
elif self.state == 'edid':
if self.cnt == OFF_VERSION:
self.decode_vid(-10)
self.decode_pid(-8)
self.decode_serial(-6)
self.decode_mfrdate(-2)
self.put(self.sn[OFF_VENDOR][0], es, self.out_ann,
[ANN_SECTIONS, ['Vendor/product']])
elif self.cnt == OFF_BASIC:
self.put(self.sn[OFF_VERSION][0], es, self.out_ann,
[ANN_SECTIONS, ['EDID Version']])
self.put(self.sn[OFF_VERSION][0], self.sn[OFF_VERSION][1],
self.out_ann, [ANN_FIELDS,
['Version %d' % self.cache[-2]]])
self.put(self.sn[OFF_VERSION+1][0], self.sn[OFF_VERSION+1][1],
self.out_ann, [ANN_FIELDS,
['Revision %d' % self.cache[-1]]])
elif self.cnt == OFF_CHROM:
self.put(self.sn[OFF_BASIC][0], es, self.out_ann,
[ANN_SECTIONS, ['Basic display']])
self.decode_basicdisplay(-5)
elif self.cnt == OFF_EST_TIMING:
self.put(self.sn[OFF_CHROM][0], es, self.out_ann,
[ANN_SECTIONS, ['Color characteristics']])
self.decode_chromaticity(-10)
elif self.cnt == OFF_STD_TIMING:
self.put(self.sn[OFF_EST_TIMING][0], es, self.out_ann,
[ANN_SECTIONS, ['Established timings']])
self.decode_est_timing(-3)
elif self.cnt == OFF_DET_TIMING:
self.put(self.sn[OFF_STD_TIMING][0], es, self.out_ann,
[ANN_SECTIONS, ['Standard timings']])
self.decode_std_timing(self.cnt - 16)
elif self.cnt == OFF_NUM_EXT:
self.decode_descriptors(-72)
elif self.cnt == OFF_CHECKSUM:
self.put(ss, es, self.out_ann,
[0, ['Extensions present: %d' % self.cache[self.cnt-1]]])
elif self.cnt == OFF_CHECKSUM+1:
checksum = 0
for i in range(128):
checksum += self.cache[i]
if checksum % 256 == 0:
csstr = 'OK'
else:
csstr = 'WRONG!'
self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % (
self.cache[self.cnt-1], csstr)]])
self.state = 'extensions'
elif self.state == 'extensions':
cache = self.ext_cache[self.extension - 1]
sn = self.ext_sn[self.extension - 1]
v = cache[self.cnt - 1]
if self.cnt == 1:
if v == 2:
self.put(ss, es, self.out_ann, [1, ['Extensions Tag', 'Tag']])
else:
self.put(ss, es, self.out_ann, [1, ['Bad Tag']])
elif self.cnt == 2:
self.put(ss, es, self.out_ann, [1, ['Version']])
self.put(ss, es, self.out_ann, [0, [str(v)]])
elif self.cnt == 3:
self.put(ss, es, self.out_ann, [1, ['DTD offset']])
self.put(ss, es, self.out_ann, [0, [str(v)]])
elif self.cnt == 4:
self.put(ss, es, self.out_ann, [1, ['Format support | DTD count']])
support = "Underscan: {0}, {1} Audio, YCbCr: {2}".format(
"yes" if v & 0x80 else "no",
"Basic" if v & 0x40 else "No",
["None", "422", "444", "422+444"][(v & 0x30) >> 4])
self.put(ss, es, self.out_ann, [0, ['{0}, DTDs: {1}'.format(support, v & 0xf)]])
elif self.cnt <= cache[2]:
if self.cnt == cache[2]:
self.put(sn[4][0], es, self.out_ann, [1, ['Data block collection']])
self.decode_data_block_collection(cache[4:], sn[4:])
elif (self.cnt - cache[2]) % 18 == 0:
n = (self.cnt - cache[2]) / 18
if n <= cache[3] & 0xf:
self.put(sn[self.cnt - 18][0], es, self.out_ann, [1, ['DTD']])
self.decode_descriptors(-18)
elif self.cnt == 127:
dtd_last = cache[2] + (cache[3] & 0xf) * 18
self.put(sn[dtd_last][0], es, self.out_ann, [1, ['Padding']])
elif self.cnt == 128:
checksum = sum(cache) % 256
self.put(ss, es, self.out_ann, [0, ['Checksum: %d (%s)' % (
cache[self.cnt-1], 'Wrong' if checksum else 'OK')]])
def ann_field(self, start, end, annotation):
annotation = annotation if isinstance(annotation, list) else [annotation]
sn = self.ext_sn[self.extension - 1] if self.extension else self.sn
self.put(sn[start][0], sn[end][1],
self.out_ann, [ANN_FIELDS, annotation])
def lookup_pnpid(self, pnpid):
pnpid_file = os.path.join(os.path.dirname(__file__), 'pnpids.txt')
if os.path.exists(pnpid_file):
for line in open(pnpid_file).readlines():
if line.find(pnpid + ';') == 0:
return line[4:].strip()
return ''
def decode_vid(self, offset):
pnpid = chr(64 + ((self.cache[offset] & 0x7c) >> 2))
pnpid += chr(64 + (((self.cache[offset] & 0x03) << 3)
| ((self.cache[offset+1] & 0xe0) >> 5)))
pnpid += chr(64 + (self.cache[offset+1] & 0x1f))
vendor = self.lookup_pnpid(pnpid)
if vendor:
pnpid += ' (%s)' % vendor
self.ann_field(offset, offset+1, pnpid)
def decode_pid(self, offset):
pidstr = 'Product 0x%.2x%.2x' % (self.cache[offset+1], self.cache[offset])
self.ann_field(offset, offset+1, pidstr)
def decode_serial(self, offset):
serialnum = (self.cache[offset+3] << 24) \
+ (self.cache[offset+2] << 16) \
+ (self.cache[offset+1] << 8) \
+ self.cache[offset]
serialstr = ''
is_alnum = True
for i in range(4):
if not chr(self.cache[offset+3-i]).isalnum():
is_alnum = False
break
serialstr += chr(self.cache[offset+3-i])
serial = serialstr if is_alnum else str(serialnum)
self.ann_field(offset, offset+3, 'Serial ' + serial)
def decode_mfrdate(self, offset):
datestr = ''
if self.cache[offset]:
datestr += 'week %d, ' % self.cache[offset]
datestr += str(1990 + self.cache[offset+1])
if datestr:
self.ann_field(offset, offset+1, ['Manufactured ' + datestr, datestr])
def decode_basicdisplay(self, offset):
# Video input definition
vid = self.cache[offset]
if vid & 0x80:
# Digital
self.ann_field(offset, offset, 'Video input: VESA DFP 1.')
else:
# Analog
sls = (vid & 60) >> 5
self.ann_field(offset, offset, 'Signal level standard: %.2x' % sls)
if vid & 0x10:
self.ann_field(offset, offset, 'Blank-to-black setup expected')
syncs = ''
if vid & 0x08:
syncs += 'separate syncs, '
if vid & 0x04:
syncs += 'composite syncs, '
if vid & 0x02:
syncs += 'sync on green, '
if vid & 0x01:
syncs += 'Vsync serration required, '
if syncs:
self.ann_field(offset, offset, 'Supported syncs: %s' % syncs[:-2])
# Max horizontal/vertical image size
if self.cache[offset+1] != 0 and self.cache[offset+2] != 0:
# Projectors have this set to 0
sizestr = '%dx%dcm' % (self.cache[offset+1], self.cache[offset+2])
self.ann_field(offset+1, offset+2, 'Physical size: ' + sizestr)
# Display transfer characteristic (gamma)
if self.cache[offset+3] != 0xff:
gamma = (self.cache[offset+3] + 100) / 100
self.ann_field(offset+3, offset+3, 'Gamma: %1.2f' % gamma)
# Feature support
fs = self.cache[offset+4]
dpms = ''
if fs & 0x80:
dpms += 'standby, '
if fs & 0x40:
dpms += 'suspend, '
if fs & 0x20:
dpms += 'active off, '
if dpms:
self.ann_field(offset+4, offset+4, 'DPMS support: %s' % dpms[:-2])
dt = (fs & 0x18) >> 3
dtstr = ''
if dt == 0:
dtstr = 'Monochrome'
elif dt == 1:
dtstr = 'RGB color'
elif dt == 2:
dtstr = 'non-RGB multicolor'
if dtstr:
self.ann_field(offset+4, offset+4, 'Display type: %s' % dtstr)
if fs & 0x04:
self.ann_field(offset+4, offset+4, 'Color space: standard sRGB')
# Save this for when we decode the first detailed timing descriptor
self.have_preferred_timing = (fs & 0x02) == 0x02
if fs & 0x01:
gft = ''
else:
gft = 'not '
self.ann_field(offset+4, offset+4,
'Generalized timing formula: %ssupported' % gft)
def convert_color(self, value):
# Convert from 10-bit packet format to float
outval = 0.0
for i in range(10):
if value & 0x01:
outval += 2 ** -(10-i)
value >>= 1
return outval
def decode_chromaticity(self, offset):
redx = (self.cache[offset+2] << 2) + ((self.cache[offset] & 0xc0) >> 6)
redy = (self.cache[offset+3] << 2) + ((self.cache[offset] & 0x30) >> 4)
self.ann_field(offset, offset+9, 'Chromacity red: X %1.3f, Y %1.3f' % (
self.convert_color(redx), self.convert_color(redy)))
greenx = (self.cache[offset+4] << 2) + ((self.cache[offset] & 0x0c) >> 6)
greeny = (self.cache[offset+5] << 2) + ((self.cache[offset] & 0x03) >> 4)
self.ann_field(offset, offset+9, 'Chromacity green: X %1.3f, Y %1.3f' % (
self.convert_color(greenx), self.convert_color(greeny)))
bluex = (self.cache[offset+6] << 2) + ((self.cache[offset+1] & 0xc0) >> 6)
bluey = (self.cache[offset+7] << 2) + ((self.cache[offset+1] & 0x30) >> 4)
self.ann_field(offset, offset+9, 'Chromacity blue: X %1.3f, Y %1.3f' % (
self.convert_color(bluex), self.convert_color(bluey)))
whitex = (self.cache[offset+8] << 2) + ((self.cache[offset+1] & 0x0c) >> 6)
whitey = (self.cache[offset+9] << 2) + ((self.cache[offset+1] & 0x03) >> 4)
self.ann_field(offset, offset+9, 'Chromacity white: X %1.3f, Y %1.3f' % (
self.convert_color(whitex), self.convert_color(whitey)))
def decode_est_timing(self, offset):
# Pre-EDID modes
bitmap = (self.cache[offset] << 9) \
+ (self.cache[offset+1] << 1) \
+ ((self.cache[offset+2] & 0x80) >> 7)
modestr = ''
for i in range(17):
if bitmap & (1 << (16-i)):
modestr += est_modes[i] + ', '
if modestr:
self.ann_field(offset, offset+2,
'Supported established modes: %s' % modestr[:-2])
def decode_std_timing(self, offset):
modestr = ''
for i in range(0, 16, 2):
if self.cache[offset+i] == 0x01 and self.cache[offset+i+1] == 0x01:
# Unused field
continue
x = (self.cache[offset+i] + 31) * 8
ratio = (self.cache[offset+i+1] & 0xc0) >> 6
ratio_x, ratio_y = xy_ratio[ratio]
y = x / ratio_x * ratio_y
refresh = (self.cache[offset+i+1] & 0x3f) + 60
modestr += '%dx%d@%dHz, ' % (x, y, refresh)
if modestr:
self.ann_field(offset, offset + 15,
'Supported standard modes: %s' % modestr[:-2])
def decode_detailed_timing(self, cache, sn, offset, is_first):
if is_first and self.have_preferred_timing:
# Only on first detailed timing descriptor
section = 'Preferred'
else:
section = 'Detailed'
section += ' timing descriptor'
self.put(sn[0][0], sn[17][1],
self.out_ann, [ANN_SECTIONS, [section]])
pixclock = float((cache[1] << 8) + cache[0]) / 100
self.ann_field(offset, offset+1, 'Pixel clock: %.2f MHz' % pixclock)
horiz_active = ((cache[4] & 0xf0) << 4) + cache[2]
horiz_blank = ((cache[4] & 0x0f) << 8) + cache[3]
self.ann_field(offset+2, offset+4, 'Horizontal active: %d, blanking: %d' % (horiz_active, horiz_blank))
vert_active = ((cache[7] & 0xf0) << 4) + cache[5]
vert_blank = ((cache[7] & 0x0f) << 8) + cache[6]
self.ann_field(offset+5, offset+7, 'Vertical active: %d, blanking: %d' % (vert_active, vert_blank))
horiz_sync_off = ((cache[11] & 0xc0) << 2) + cache[8]
horiz_sync_pw = ((cache[11] & 0x30) << 4) + cache[9]
vert_sync_off = ((cache[11] & 0x0c) << 2) + ((cache[10] & 0xf0) >> 4)
vert_sync_pw = ((cache[11] & 0x03) << 4) + (cache[10] & 0x0f)
syncs = (horiz_sync_off, horiz_sync_pw, vert_sync_off, vert_sync_pw)
self.ann_field(offset+8, offset+11, [
'Horizontal sync offset: %d, pulse width: %d, Vertical sync offset: %d, pulse width: %d' % syncs,
'HSync off: %d, pw: %d, VSync off: %d, pw: %d' % syncs])
horiz_size = ((cache[14] & 0xf0) << 4) + cache[12]
vert_size = ((cache[14] & 0x0f) << 8) + cache[13]
self.ann_field(offset+12, offset+14, 'Physical size: %dx%dmm' % (horiz_size, vert_size))
horiz_border = cache[15]
self.ann_field(offset+15, offset+15, 'Horizontal border: %d pixels' % horiz_border)
vert_border = cache[16]
self.ann_field(offset+16, offset+16, 'Vertical border: %d lines' % vert_border)
features = 'Flags: '
if cache[17] & 0x80:
features += 'interlaced, '
stereo = (cache[17] & 0x60) >> 5
if stereo:
if cache[17] & 0x01:
features += '2-way interleaved stereo ('
features += ['right image on even lines',
'left image on even lines',
'side-by-side'][stereo-1]
features += '), '
else:
features += 'field sequential stereo ('
features += ['right image on sync=1', 'left image on sync=1',
'4-way interleaved'][stereo-1]
features += '), '
sync = (cache[17] & 0x18) >> 3
sync2 = (cache[17] & 0x06) >> 1
posneg = ['negative', 'positive']
features += 'sync type '
if sync == 0x00:
features += 'analog composite (serrate on RGB)'
elif sync == 0x01:
features += 'bipolar analog composite (serrate on RGB)'
elif sync == 0x02:
features += 'digital composite (serrate on composite polarity ' \
+ (posneg[sync2 & 0x01]) + ')'
elif sync == 0x03:
features += 'digital separate ('
features += 'Vsync polarity ' + (posneg[(sync2 & 0x02) >> 1])
features += ', Hsync polarity ' + (posneg[sync2 & 0x01])
features += ')'
features += ', '
self.ann_field(offset+17, offset+17, features[:-2])
def decode_descriptor(self, cache, offset):
tag = cache[3]
self.ann_field(offset, offset+1, "Flag")
self.ann_field(offset+2, offset+2, "Flag (reserved)")
self.ann_field(offset+3, offset+3, "Tag: {0:X}".format(tag))
self.ann_field(offset+4, offset+4, "Flag")
sn = self.ext_sn[self.extension - 1] if self.extension else self.sn
if tag == 0xff:
# Monitor serial number
self.put(sn[offset][0], sn[offset+17][1], self.out_ann,
[ANN_SECTIONS, ['Serial number']])
text = bytes(cache[5:][:13]).decode(encoding='cp437', errors='replace')
self.ann_field(offset+5, offset+17, text.strip())
elif tag == 0xfe:
# Text
self.put(sn[offset][0], sn[offset+17][1], self.out_ann,
[ANN_SECTIONS, ['Text']])
text = bytes(cache[5:][:13]).decode(encoding='cp437', errors='replace')
self.ann_field(offset+5, offset+17, text.strip())
elif tag == 0xfc:
# Monitor name
self.put(sn[offset][0], sn[offset+17][1], self.out_ann,
[ANN_SECTIONS, ['Monitor name']])
text = bytes(cache[5:][:13]).decode(encoding='cp437', errors='replace')
self.ann_field(offset+5, offset+17, text.strip())
elif tag == 0xfd:
# Monitor range limits
self.put(sn[offset][0], sn[offset+17][1], self.out_ann,
[ANN_SECTIONS, ['Monitor range limits']])
self.ann_field(offset+5, offset+5, [
'Minimum vertical rate: {0}Hz'.format(cache[5]),
'VSync >= {0}Hz'.format(cache[5])])
self.ann_field(offset+6, offset+6, [
'Maximum vertical rate: {0}Hz'.format(cache[6]),
'VSync <= {0}Hz'.format(cache[6])])
self.ann_field(offset+7, offset+7, [
'Minimum horizontal rate: {0}kHz'.format(cache[7]),
'HSync >= {0}kHz'.format(cache[7])])
self.ann_field(offset+8, offset+8, [
'Maximum horizontal rate: {0}kHz'.format(cache[8]),
'HSync <= {0}kHz'.format(cache[8])])
self.ann_field(offset+9, offset+9, [
'Maximum pixel clock: {0}MHz'.format(cache[9] * 10),
'PixClk <= {0}MHz'.format(cache[9] * 10)])
if cache[10] == 0x02:
self.ann_field(offset+10, offset+10, ['Secondary timing formula supported', '2nd GTF: yes'])
self.ann_field(offset+11, offset+17, ['GTF'])
else:
self.ann_field(offset+10, offset+10, ['Secondary timing formula unsupported', '2nd GTF: no'])
self.ann_field(offset+11, offset+17, ['Padding'])
elif tag == 0xfb:
# Additional color point data
self.put(sn[offset][0], sn[offset+17][1], self.out_ann,
[ANN_SECTIONS, ['Additional color point data']])
elif tag == 0xfa:
# Additional standard timing definitions
self.put(sn[offset][0], sn[offset+17][1], self.out_ann,
[ANN_SECTIONS, ['Additional standard timing definitions']])
else:
self.put(sn[offset][0], sn[offset+17][1], self.out_ann,
[ANN_SECTIONS, ['Unknown descriptor']])
def decode_descriptors(self, offset):
# 4 consecutive 18-byte descriptor blocks
cache = self.ext_cache[self.extension - 1] if self.extension else self.cache
sn = self.ext_sn[self.extension - 1] if self.extension else self.sn
for i in range(offset, 0, 18):
if cache[i] != 0 or cache[i+1] != 0:
self.decode_detailed_timing(cache[i:], sn[i:], i, i == offset)
else:
if cache[i+2] == 0 or cache[i+4] == 0:
self.decode_descriptor(cache[i:], i)
def decode_data_block(self, tag, cache, sn):
codes = { 0: ['0: Reserved'],
1: ['1: Audio Data Block', 'Audio'],
2: ['2: Video Data Block', 'Video'],
3: ['3: Vendor Specific Data Block', 'VSDB'],
4: ['4: Speacker Allocation Data Block', 'SADB'],
5: ['5: VESA DTC Data Block', 'DTC'],
6: ['6: Reserved'],
7: ['7: Extended', 'Ext'] }
ext_codes = { 0: [ '0: Video Capability Data Block', 'VCDB'],
1: [ '1: Vendor Specific Video Data Block', 'VSVDB'],
17: ['17: Vendor Specific Audio Data Block', 'VSADB'], }
if tag < 7:
code = codes[tag]
ext_len = 0
if tag == 1:
aformats = { 1: '1 (LPCM)' }
rates = [ '192', '176', '96', '88', '48', '44', '32' ]
aformat = cache[1] >> 3
sup_rates = [ i for i in range(0, 8) if (1 << i) & cache[2] ]
data = "Format: {0} Channels: {1}".format(
aformats.get(aformat, aformat), (cache[1] & 0x7) + 1)
data += " Rates: " + " ".join(rates[6 - i] for i in sup_rates)
data += " Extra: [{0:02X}]".format(cache[3])
elif tag ==2:
data = "VIC: "
data += ", ".join("{0}{1}".format(v & 0x7f,
['', ' (Native)'][v >> 7])
for v in cache[1:])
elif tag ==3:
ouis = { b'\x00\x0c\x03': 'HDMI Licensing, LLC' }
oui = bytes(cache[3:0:-1])
ouis = ouis.get(oui, None)
data = "OUI: " + " ".join('{0:02X}'.format(x) for x in oui)
data += " ({0})".format(ouis) if ouis else ""
data += ", PhyAddr: {0}.{1}.{2}.{3}".format(
cache[4] >> 4, cache[4] & 0xf, cache[5] >> 4, cache[5] & 0xf)
data += ", [" + " ".join('{0:02X}'.format(x) for x in cache[6:]) + "]"
elif tag ==4:
speakers = [ 'FL/FR', 'LFE', 'FC', 'RL/RR',
'RC', 'FLC/FRC', 'RLC/RRC', 'FLW/FRW',
'FLH/FRH', 'TC', 'FCH' ]
sup_speakers = cache[1] + (cache[2] << 8)
sup_speakers = [ i for i in range(0, 8) if (1 << i) & sup_speakers ]
data = "Speakers: " + " ".join(speakers[i] for i in sup_speakers)
else:
data = " ".join('{0:02X}'.format(x) for x in cache[1:])
else:
# Extended tags
ext_len = 1
ext_code = ext_codes.get(cache[1], ['Unknown', '?'])
code = zip(codes[7], [", ", ": "], ext_code)
code = [ "".join(x) for x in code ]
data = " ".join('{0:02X}'.format(x) for x in cache[2:])
self.put(sn[0][0], sn[0 + ext_len][1], self.out_ann,
[ANN_FIELDS, code])
self.put(sn[1 + ext_len][0], sn[len(cache) - 1][1], self.out_ann,
[ANN_FIELDS, [data]])
def decode_data_block_collection(self, cache, sn):
offset = 0
while offset < len(cache):
length = 1 + cache[offset] & 0x1f
tag = cache[offset] >> 5
self.decode_data_block(tag, cache[offset:offset + length], sn[offset:])
offset += length

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'i2c' PD and decodes the
industry standard 24xx series serial EEPROM protocol.
'''
from .pd import Decoder

View File

@@ -0,0 +1,204 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
#
# Chip specific properties:
#
# - vendor: chip manufacturer
# - model: chip model
# - size: total EEPROM size (in number of bytes)
# - page_size: page size (in number of bytes)
# - page_wraparound: Whether writes wrap-around at page boundaries
# - addr_bytes: number of EEPROM address bytes used
# - addr_pins: number of address pins (A0/A1/A2) on this chip
# - max_speed: max. supported I²C speed (in kHz)
#
chips = {
# Generic chip (128 bytes, 8 bytes page size)
'generic': {
'vendor': '',
'model': 'Generic',
'size': 128,
'page_size': 8,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 3,
'max_speed': 400,
},
# Microchip
'microchip_24aa65': {
'vendor': 'Microchip',
'model': '24AA65',
'size': 8 * 1024,
'page_size': 64, # Actually 8, but there are 8 pages of "input cache"
'page_wraparound': True,
'addr_bytes': 2,
'addr_pins': 3,
'max_speed': 400,
},
'microchip_24lc65': {
'vendor': 'Microchip',
'model': '24LC65',
'size': 8 * 1024,
'page_size': 64, # Actually 8, but there are 8 pages of "input cache"
'page_wraparound': True,
'addr_bytes': 2,
'addr_pins': 3,
'max_speed': 400,
},
'microchip_24c65': {
'vendor': 'Microchip',
'model': '24C65',
'size': 8 * 1024,
'page_size': 64, # Actually 8, but there are 8 pages of "input cache"
'page_wraparound': True,
'addr_bytes': 2,
'addr_pins': 3,
'max_speed': 400,
},
'microchip_24aa64': {
'vendor': 'Microchip',
'model': '24AA64',
'size': 8 * 1024,
'page_size': 32,
'page_wraparound': True,
'addr_bytes': 2,
'addr_pins': 3,
'max_speed': 400, # 100 for VCC < 2.5V
},
'microchip_24lc64': {
'vendor': 'Microchip',
'model': '24LC64',
'size': 8 * 1024,
'page_size': 32,
'page_wraparound': True,
'addr_bytes': 2,
'addr_pins': 3,
'max_speed': 400,
},
'microchip_24aa02uid': {
'vendor': 'Microchip',
'model': '24AA02UID',
'size': 256,
'page_size': 8,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 0, # Pins A0, A1, A2 not used
'max_speed': 400,
},
'microchip_24aa025uid': {
'vendor': 'Microchip',
'model': '24AA025UID',
'size': 256,
'page_size': 16,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 3,
'max_speed': 400,
},
'microchip_24aa025uid_sot23': {
'vendor': 'Microchip',
'model': '24AA025UID (SOT-23)',
'size': 256,
'page_size': 16,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 2, # SOT-23 package: A2 not available
'max_speed': 400,
},
# ON Semiconductor
'onsemi_cat24c256': {
'vendor': 'ON Semiconductor',
'model': 'CAT24C256',
'size': 32 * 1024,
'page_size': 64,
'page_wraparound': True,
'addr_bytes': 2,
'addr_pins': 3,
'max_speed': 1000,
},
'onsemi_cat24m01': {
'vendor': 'ON Semiconductor',
'model': 'CAT24M01',
'size': 128 * 1024,
'page_size': 256,
'page_wraparound': True,
'addr_bytes': 2,
'addr_pins': 2, # Pin A0 not connected
'max_speed': 1000,
},
# Siemens
'siemens_slx_24c01': {
'vendor': 'Siemens',
'model': 'SLx 24C01',
'size': 128,
'page_size': 8,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 0, # Pins A0, A1, A2 are not connected (NC)
'max_speed': 400,
},
'siemens_slx_24c02': {
'vendor': 'Siemens',
'model': 'SLx 24C02',
'size': 256,
'page_size': 8,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 0, # Pins A0, A1, A2 are not connected (NC)
'max_speed': 400,
},
# ST
'st_m24c01': {
'vendor': 'ST',
'model': 'M24C01',
'size': 128,
'page_size': 16,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 3, # Called E0, E1, E2 on this chip.
'max_speed': 400,
},
'st_m24c02': {
'vendor': 'ST',
'model': 'M24C02',
'size': 256,
'page_size': 16,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 3, # Called E0, E1, E2 on this chip.
'max_speed': 400,
},
# Xicor
'xicor_x24c02': {
'vendor': 'Xicor',
'model': 'X24C02',
'size': 256,
'page_size': 4,
'page_wraparound': True,
'addr_bytes': 1,
'addr_pins': 3,
'max_speed': 100,
},
}

View File

@@ -0,0 +1,433 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2014 Uwe Hermann <uwe@hermann-uwe.de>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
from .lists import *
class Decoder(srd.Decoder):
api_version = 3
id = 'eeprom24xx'
name = '24xx EEPROM'
longname = '24xx I²C EEPROM'
desc = '24xx series I²C EEPROM protocol.'
license = 'gplv2+'
inputs = ['i2c']
outputs = []
tags = ['IC', 'Memory']
options = (
{'id': 'chip', 'desc': 'Chip', 'default': 'generic',
'values': tuple(chips.keys())},
{'id': 'addr_counter', 'desc': 'Initial address counter value',
'default': 0},
)
annotations = (
# Warnings
('warnings', 'Warnings'),
# Bits/bytes
('control-code', 'Control code'),
('address-pin', 'Address pin (A0/A1/A2)'),
('rw-bit', 'Read/write bit'),
('word-addr-byte', 'Word address byte'),
('data-byte', 'Data byte'),
# Fields
('control-word', 'Control word'),
('word-addr', 'Word address'),
('data', 'Data'),
# Operations
('byte-write', 'Byte write'),
('page-write', 'Page write'),
('cur-addr-read', 'Current address read'),
('random-read', 'Random read'),
('seq-random-read', 'Sequential random read'),
('seq-cur-addr-read', 'Sequential current address read'),
('ack-polling', 'Acknowledge polling'),
('set-bank-addr', 'Set bank address'), # SBA. Only 34AA04.
('read-bank-addr', 'Read bank address'), # RBA. Only 34AA04.
('set-wp', 'Set write protection'), # SWP
('clear-all-wp', 'Clear all write protection'), # CWP
('read-wp', 'Read write protection status'), # RPS
)
annotation_rows = (
('bits-bytes', 'Bits/bytes', (1, 2, 3, 4, 5)),
('fields', 'Fields', (6, 7, 8)),
('ops', 'Operations', tuple(range(9, 21))),
('warnings', 'Warnings', (0,)),
)
binary = (
('binary', 'Binary'),
)
def __init__(self):
self.reset()
def reset(self):
self.reset_variables()
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_binary = self.register(srd.OUTPUT_BINARY)
self.chip = chips[self.options['chip']]
self.addr_counter = self.options['addr_counter']
def putb(self, data):
self.put(self.ss_block, self.es_block, self.out_ann, data)
def putbin(self, data):
self.put(self.ss_block, self.es_block, self.out_binary, data)
def putbits(self, bit1, bit2, bits, data):
self.put(bits[bit1][1], bits[bit2][2], self.out_ann, data)
def reset_variables(self):
self.state = 'WAIT FOR START'
self.packets = []
self.bytebuf = []
self.is_cur_addr_read = False
self.is_random_access_read = False
self.is_seq_random_read = False
self.is_byte_write = False
self.is_page_write = False
def packet_append(self):
self.packets.append([self.ss, self.es, self.cmd, self.databyte, self.bits])
if self.cmd in ('DATA READ', 'DATA WRITE'):
self.bytebuf.append(self.databyte)
def hexbytes(self, idx):
return ' '.join(['%02X' % b for b in self.bytebuf[idx:]])
def put_control_word(self, bits):
s = ''.join(['%d' % b[0] for b in reversed(bits[4:])])
self.putbits(7, 4, bits, [1, ['Control code bits: ' + s,
'Control code: ' + s, 'Ctrl code: ' + s, 'Ctrl code', 'Ctrl', 'C']])
for i in reversed(range(self.chip['addr_pins'])):
self.putbits(i + 1, i + 1, bits,
[2, ['Address bit %d: %d' % (i, bits[i + 1][0]),
'Addr bit %d' % i, 'A%d' % i, 'A']])
s1 = 'read' if bits[0][0] == 1 else 'write'
s2 = 'R' if bits[0][0] == 1 else 'W'
self.putbits(0, 0, bits, [3, ['R/W bit: ' + s1, 'R/W', 'RW', s2]])
self.putbits(7, 0, bits, [6, ['Control word', 'Control', 'CW', 'C']])
def put_word_addr(self, p):
if self.chip['addr_bytes'] == 1:
a = p[1][3]
self.put(p[1][0], p[1][1], self.out_ann,
[4, ['Word address byte: %02X' % a, 'Word addr byte: %02X' % a,
'Addr: %02X' % a, 'A: %02X' % a, '%02X' % a]])
self.put(p[1][0], p[1][1], self.out_ann, [7, ['Word address',
'Word addr', 'Addr', 'A']])
self.addr_counter = a
else:
a = p[1][3]
self.put(p[1][0], p[1][1], self.out_ann,
[4, ['Word address high byte: %02X' % a,
'Word addr high byte: %02X' % a,
'Addr high: %02X' % a, 'AH: %02X' % a, '%02X' % a]])
a = p[2][3]
self.put(p[2][0], p[2][1], self.out_ann,
[4, ['Word address low byte: %02X' % a,
'Word addr low byte: %02X' % a,
'Addr low: %02X' % a, 'AL: %02X' % a, '%02X' % a]])
self.put(p[1][0], p[2][1], self.out_ann, [7, ['Word address',
'Word addr', 'Addr', 'A']])
self.addr_counter = (p[1][3] << 8) | p[2][3]
def put_data_byte(self, p):
if self.chip['addr_bytes'] == 1:
s = '%02X' % self.addr_counter
else:
s = '%04X' % self.addr_counter
self.put(p[0], p[1], self.out_ann, [5, ['Data byte %s: %02X' % \
(s, p[3]), 'Data byte: %02X' % p[3], \
'Byte: %02X' % p[3], 'DB: %02X' % p[3], '%02X' % p[3]]])
def put_data_bytes(self, idx, cls, s):
for p in self.packets[idx:]:
self.put_data_byte(p)
self.addr_counter += 1
self.put(self.packets[idx][0], self.packets[-1][1], self.out_ann,
[8, ['Data', 'D']])
a = ''.join(['%s' % c[0] for c in s.split()]).upper()
self.putb([cls, ['%s (%s): %s' % (s, self.addr_and_len(), \
self.hexbytes(self.chip['addr_bytes'])),
'%s (%s)' % (s, self.addr_and_len()), s, a, s[0]]])
self.putbin([0, bytes(self.bytebuf[self.chip['addr_bytes']:])])
def addr_and_len(self):
if self.chip['addr_bytes'] == 1:
a = '%02X' % self.bytebuf[0]
else:
a = '%02X%02X' % tuple(self.bytebuf[:2])
num_data_bytes = len(self.bytebuf) - self.chip['addr_bytes']
d = '%d bytes' % num_data_bytes
if num_data_bytes <= 1:
d = d[:-1]
return 'addr=%s, %s' % (a, d)
def decide_on_seq_or_rnd_read(self):
if len(self.bytebuf) < 2:
self.reset_variables()
return
if len(self.bytebuf) == 2:
self.is_random_access_read = True
else:
self.is_seq_random_read = True
def put_operation(self):
idx = 1 + self.chip['addr_bytes']
if self.is_byte_write:
# Byte write: word address, one data byte.
self.put_word_addr(self.packets)
self.put_data_bytes(idx, 9, 'Byte write')
elif self.is_page_write:
# Page write: word address, two or more data bytes.
self.put_word_addr(self.packets)
intitial_addr = self.addr_counter
self.put_data_bytes(idx, 10, 'Page write')
num_bytes_to_write = len(self.packets[idx:])
if num_bytes_to_write > self.chip['page_size']:
self.putb([0, ['Warning: Wrote %d bytes but page size is '
'only %d bytes!' % (num_bytes_to_write,
self.chip['page_size'])]])
page1 = int(intitial_addr / self.chip['page_size'])
page2 = int((self.addr_counter - 1) / self.chip['page_size'])
if page1 != page2:
self.putb([0, ['Warning: Page write crossed page boundary '
'from page %d to %d!' % (page1, page2)]])
elif self.is_cur_addr_read:
# Current address read: no word address, one data byte.
self.put_data_byte(self.packets[1])
self.put(self.packets[1][0], self.packets[-1][1], self.out_ann,
[8, ['Data', 'D']])
self.putb([11, ['Current address read: %02X' % self.bytebuf[0],
'Current address read', 'Cur addr read', 'CAR', 'C']])
self.putbin([0, bytes([self.bytebuf[0]])])
self.addr_counter += 1
elif self.is_random_access_read:
# Random access read: word address, one data byte.
self.put_control_word(self.packets[idx][4])
self.put_word_addr(self.packets)
self.put_data_bytes(idx + 1, 12, 'Random access read')
elif self.is_seq_random_read:
# Sequential random read: word address, two or more data bytes.
self.put_control_word(self.packets[idx][4])
self.put_word_addr(self.packets)
self.put_data_bytes(idx + 1, 13, 'Sequential random read')
def handle_wait_for_start(self):
# Wait for an I²C START condition.
if self.cmd not in ('START', 'START REPEAT'):
return
self.ss_block = self.ss
self.state = 'GET CONTROL WORD'
def handle_get_control_word(self):
# The packet after START must be an ADDRESS READ or ADDRESS WRITE.
if self.cmd not in ('ADDRESS READ', 'ADDRESS WRITE'):
self.reset_variables()
return
self.packet_append()
self.put_control_word(self.bits)
self.state = '%s GET ACK NACK AFTER CONTROL WORD' % self.cmd[8]
def handle_r_get_ack_nack_after_control_word(self):
if self.cmd == 'ACK':
self.state = 'R GET WORD ADDR OR BYTE'
elif self.cmd == 'NACK':
self.es_block = self.es
self.putb([0, ['Warning: No reply from slave!']])
self.reset_variables()
else:
self.reset_variables()
def handle_r_get_word_addr_or_byte(self):
if self.cmd == 'STOP':
self.es_block = self.es
self.putb([0, ['Warning: Slave replied, but master aborted!']])
self.reset_variables()
return
elif self.cmd != 'DATA READ':
self.reset_variables()
return
self.packet_append()
self.state = 'R GET ACK NACK AFTER WORD ADDR OR BYTE'
def handle_r_get_ack_nack_after_word_addr_or_byte(self):
if self.cmd == 'ACK':
self.state = 'R GET RESTART'
elif self.cmd == 'NACK':
self.is_cur_addr_read = True
self.state = 'GET STOP AFTER LAST BYTE'
else:
self.reset_variables()
def handle_r_get_restart(self):
if self.cmd == 'RESTART':
self.state = 'R READ BYTE'
else:
self.reset_variables()
def handle_r_read_byte(self):
if self.cmd == 'DATA READ':
self.packet_append()
self.state = 'R GET ACK NACK AFTER BYTE WAS READ'
else:
self.reset_variables()
def handle_r_get_ack_nack_after_byte_was_read(self):
if self.cmd == 'ACK':
self.state = 'R READ BYTE'
elif self.cmd == 'NACK':
# It's either a RANDOM READ or a SEQUENTIAL READ.
self.state = 'GET STOP AFTER LAST BYTE'
else:
self.reset_variables()
def handle_w_get_ack_nack_after_control_word(self):
if self.cmd == 'ACK':
self.state = 'W GET WORD ADDR'
elif self.cmd == 'NACK':
self.es_block = self.es
self.putb([0, ['Warning: No reply from slave!']])
self.reset_variables()
else:
self.reset_variables()
def handle_w_get_word_addr(self):
if self.cmd == 'STOP':
self.es_block = self.es
self.putb([0, ['Warning: Slave replied, but master aborted!']])
self.reset_variables()
return
elif self.cmd != 'DATA WRITE':
self.reset_variables()
return
self.packet_append()
self.state = 'W GET ACK AFTER WORD ADDR'
def handle_w_get_ack_after_word_addr(self):
if self.cmd == 'ACK':
self.state = 'W DETERMINE EEPROM READ OR WRITE'
else:
self.reset_variables()
def handle_w_determine_eeprom_read_or_write(self):
if self.cmd == 'START REPEAT':
# It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ.
self.state = 'R2 GET CONTROL WORD'
elif self.cmd == 'DATA WRITE':
self.packet_append()
self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
else:
self.reset_variables()
def handle_w_write_byte(self):
if self.cmd == 'DATA WRITE':
self.packet_append()
self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
elif self.cmd == 'STOP':
if len(self.bytebuf) < 2:
self.reset_variables()
return
self.es_block = self.es
if len(self.bytebuf) == 2:
self.is_byte_write = True
else:
self.is_page_write = True
self.put_operation()
self.reset_variables()
elif self.cmd == 'START REPEAT':
# It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ.
self.state = 'R2 GET CONTROL WORD'
else:
self.reset_variables()
def handle_w_get_ack_nack_after_byte_was_written(self):
if self.cmd == 'ACK':
self.state = 'W WRITE BYTE'
else:
self.reset_variables()
def handle_r2_get_control_word(self):
if self.cmd == 'ADDRESS READ':
self.packet_append()
self.state = 'R2 GET ACK AFTER ADDR READ'
else:
self.reset_variables()
def handle_r2_get_ack_after_addr_read(self):
if self.cmd == 'ACK':
self.state = 'R2 READ BYTE'
else:
self.reset_variables()
def handle_r2_read_byte(self):
if self.cmd == 'DATA READ':
self.packet_append()
self.state = 'R2 GET ACK NACK AFTER BYTE WAS READ'
elif self.cmd == 'STOP':
self.decide_on_seq_or_rnd_read()
self.es_block = self.es
self.putb([0, ['Warning: STOP expected after a NACK (not ACK)']])
self.put_operation()
self.reset_variables()
else:
self.reset_variables()
def handle_r2_get_ack_nack_after_byte_was_read(self):
if self.cmd == 'ACK':
self.state = 'R2 READ BYTE'
elif self.cmd == 'NACK':
self.decide_on_seq_or_rnd_read()
self.state = 'GET STOP AFTER LAST BYTE'
else:
self.reset_variables()
def handle_get_stop_after_last_byte(self):
if self.cmd == 'STOP':
self.es_block = self.es
self.put_operation()
self.reset_variables()
elif self.cmd == 'START REPEAT':
self.es_block = self.es
self.putb([0, ['Warning: STOP expected (not RESTART)']])
self.put_operation()
self.reset_variables()
self.ss_block = self.ss
self.state = 'GET CONTROL WORD'
else:
self.reset_variables()
def decode(self, ss, es, data):
self.cmd, self.databyte = data
# Collect the 'BITS' packet, then return. The next packet is
# guaranteed to belong to these bits we just stored.
if self.cmd == 'BITS':
self.bits = self.databyte
return
# Store the start/end samples of this I²C packet.
self.ss, self.es = ss, es
# State machine.
s = 'handle_%s' % self.state.lower().replace(' ', '_')
handle_state = getattr(self, s)
handle_state()

View File

@@ -0,0 +1,32 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
This decoder stacks on top of the 'microwire' PD and decodes the 93xx EEPROM
specific instructions.
The implemented instructions come from the STMicroelectronics M93Cx6 EEPROM
datasheet. They are compatible with the Atmel AT93Cxx EEPROM with slightly
different names.
Warning: Other EEPROMs using Microwire might have different operation codes
and instructions.
'''
from .pd import Decoder

View File

@@ -0,0 +1,141 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
##
## 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 <http://www.gnu.org/licenses/>.
##
import sigrokdecode as srd
class Decoder(srd.Decoder):
api_version = 3
id = 'eeprom93xx'
name = '93xx EEPROM'
longname = '93xx Microwire EEPROM'
desc = '93xx series Microwire EEPROM protocol.'
license = 'gplv2+'
inputs = ['microwire']
outputs = []
tags = ['IC', 'Memory']
options = (
{'id': 'addresssize', 'desc': 'Address size', 'default': 8},
{'id': 'wordsize', 'desc': 'Word size', 'default': 16},
)
annotations = (
('si-data', 'SI data'),
('so-data', 'SO data'),
('warning', 'Warning'),
)
annotation_rows = (
('data', 'Data', (0, 1)),
('warnings', 'Warnings', (2,)),
)
def __init__(self):
self.reset()
def reset(self):
self.frame = []
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.addresssize = self.options['addresssize']
self.wordsize = self.options['wordsize']
def put_address(self, data):
# Get address (MSb first).
a = 0
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]])
def put_word(self, si, data):
# Decode word (MSb first).
word = 0
for b in range(len(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]])
def decode(self, ss, es, data):
if len(data) < (2 + self.addresssize):
self.put(ss, es, self.out_ann, [2, ['Not enough packet bits']])
return
opcode = (data[0].si << 1) + (data[1].si << 0)
if opcode == 2:
# READ instruction.
self.put(data[0].ss, data[1].es,
self.out_ann, [0, ['Read word', 'READ']])
self.put_address(data[2:2 + self.addresssize])
# Get all words.
word_start = 2 + self.addresssize
while len(data) - word_start > 0:
# Check if there are enough bits for a word.
if len(data) - word_start < self.wordsize:
self.put(data[word_start].ss, data[len(data) - 1].es,
self.out_ann, [2, ['Not enough word bits']])
break
self.put_word(False, data[word_start:word_start + self.wordsize])
# Go to next word.
word_start += self.wordsize
elif opcode == 1:
# WRITE instruction.
self.put(data[0].ss, data[1].es,
self.out_ann, [0, ['Write word', 'WRITE']])
self.put_address(data[2:2 + self.addresssize])
# Get word.
if len(data) < 2 + self.addresssize + self.wordsize:
self.put(data[2 + self.addresssize].ss,
data[len(data) - 1].ss,
self.out_ann, [2, ['Not enough word bits']])
else:
self.put_word(True, data[2 + self.addresssize:2 + self.addresssize + self.wordsize])
elif opcode == 3:
# ERASE instruction.
self.put(data[0].ss, data[1].es,
self.out_ann, [0, ['Erase word', 'ERASE']])
self.put_address(data[2:2 + self.addresssize])
elif opcode == 0:
if data[2].si == 1 and data[3].si == 1:
# WEN instruction.
self.put(data[0].ss, data[2 + self.addresssize - 1].es,
self.out_ann, [0, ['Write enable', 'WEN']])
elif data[2].si == 0 and data[3].si == 0:
# WDS instruction.
self.put(data[0].ss, data[2 + self.addresssize - 1].es,
self.out_ann, [0, ['Write disable', 'WDS']])
elif data[2].si == 1 and data[3].si == 0:
# ERAL instruction.
self.put(data[0].ss, data[2 + self.addresssize - 1].es,
self.out_ann, [0, ['Erase all memory',
'Erase all', 'ERAL']])
elif data[2].si == 0 and data[3].si == 1:
# WRAL instruction.
self.put(data[0].ss, data[2 + self.addresssize - 1].es,
self.out_ann, [0, ['Write all memory',
'Write all', 'WRAL']])
# Get word.
if len(data) < 2 + self.addresssize + self.wordsize:
self.put(data[2 + self.addresssize].ss,
data[len(data) - 1].ss,
self.out_ann, [2, ['Not enough word bits']])
else:
self.put_word(True, data[2 + self.addresssize:2 + self.addresssize + self.wordsize])

View File

@@ -0,0 +1,24 @@
##
## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Benjamin Larsson <benjamin@southpole.se>
##
## 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 <http://www.gnu.org/licenses/>.
##
'''
EM4100 is a biphase/manchester/PSK based 100-150kHz RFID protocol.
'''
from .pd import Decoder

Some files were not shown because too many files have changed in this diff Show More