forked from Ivasoft/DSView
move decoders
This commit is contained in:
25
libsigrokdecode4DSL/decoders/0-i2c/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/0-i2c/__init__.py
Normal 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
|
||||
259
libsigrokdecode4DSL/decoders/0-i2c/pd.py
Normal file
259
libsigrokdecode4DSL/decoders/0-i2c/pd.py
Normal 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()
|
||||
|
||||
32
libsigrokdecode4DSL/decoders/0-spi/__init__.py
Normal file
32
libsigrokdecode4DSL/decoders/0-spi/__init__.py
Normal 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
|
||||
275
libsigrokdecode4DSL/decoders/0-spi/pd.py
Normal file
275
libsigrokdecode4DSL/decoders/0-spi/pd.py
Normal 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)
|
||||
40
libsigrokdecode4DSL/decoders/0-uart/__init__.py
Normal file
40
libsigrokdecode4DSL/decoders/0-uart/__init__.py
Normal 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
|
||||
353
libsigrokdecode4DSL/decoders/0-uart/pd.py
Normal file
353
libsigrokdecode4DSL/decoders/0-uart/pd.py
Normal 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)
|
||||
25
libsigrokdecode4DSL/decoders/1-i2c/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/1-i2c/__init__.py
Normal 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
|
||||
295
libsigrokdecode4DSL/decoders/1-i2c/pd.py
Normal file
295
libsigrokdecode4DSL/decoders/1-i2c/pd.py
Normal 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()
|
||||
|
||||
32
libsigrokdecode4DSL/decoders/1-spi/__init__.py
Normal file
32
libsigrokdecode4DSL/decoders/1-spi/__init__.py
Normal 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
|
||||
352
libsigrokdecode4DSL/decoders/1-spi/pd.py
Normal file
352
libsigrokdecode4DSL/decoders/1-spi/pd.py
Normal 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)
|
||||
40
libsigrokdecode4DSL/decoders/1-uart/__init__.py
Normal file
40
libsigrokdecode4DSL/decoders/1-uart/__init__.py
Normal 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
|
||||
441
libsigrokdecode4DSL/decoders/1-uart/pd.py
Normal file
441
libsigrokdecode4DSL/decoders/1-uart/pd.py
Normal 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)
|
||||
25
libsigrokdecode4DSL/decoders/a7105/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/a7105/__init__.py
Normal 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
|
||||
356
libsigrokdecode4DSL/decoders/a7105/pd.py
Normal file
356
libsigrokdecode4DSL/decoders/a7105/pd.py
Normal 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))
|
||||
36
libsigrokdecode4DSL/decoders/ac97/__init__.py
Normal file
36
libsigrokdecode4DSL/decoders/ac97/__init__.py
Normal 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
|
||||
505
libsigrokdecode4DSL/decoders/ac97/pd.py
Normal file
505
libsigrokdecode4DSL/decoders/ac97/pd.py
Normal 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
|
||||
25
libsigrokdecode4DSL/decoders/ad5626/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/ad5626/__init__.py
Normal 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
|
||||
62
libsigrokdecode4DSL/decoders/ad5626/pd.py
Normal file
62
libsigrokdecode4DSL/decoders/ad5626/pd.py
Normal 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
|
||||
25
libsigrokdecode4DSL/decoders/ad79x0/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/ad79x0/__init__.py
Normal 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
|
||||
137
libsigrokdecode4DSL/decoders/ad79x0/pd.py
Normal file
137
libsigrokdecode4DSL/decoders/ad79x0/pd.py
Normal 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
|
||||
32
libsigrokdecode4DSL/decoders/ade77xx/__init__.py
Normal file
32
libsigrokdecode4DSL/decoders/ade77xx/__init__.py
Normal 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
|
||||
102
libsigrokdecode4DSL/decoders/ade77xx/lists.py
Normal file
102
libsigrokdecode4DSL/decoders/ade77xx/lists.py
Normal 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)),
|
||||
])
|
||||
131
libsigrokdecode4DSL/decoders/ade77xx/pd.py
Normal file
131
libsigrokdecode4DSL/decoders/ade77xx/pd.py
Normal 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()
|
||||
29
libsigrokdecode4DSL/decoders/adf435x/__init__.py
Normal file
29
libsigrokdecode4DSL/decoders/adf435x/__init__.py
Normal 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
|
||||
144
libsigrokdecode4DSL/decoders/adf435x/pd.py
Normal file
144
libsigrokdecode4DSL/decoders/adf435x/pd.py
Normal 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
|
||||
27
libsigrokdecode4DSL/decoders/adns5020/__init__.py
Normal file
27
libsigrokdecode4DSL/decoders/adns5020/__init__.py
Normal 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
|
||||
116
libsigrokdecode4DSL/decoders/adns5020/pd.py
Normal file
116
libsigrokdecode4DSL/decoders/adns5020/pd.py
Normal 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 = []
|
||||
26
libsigrokdecode4DSL/decoders/adxl345/__init__.py
Normal file
26
libsigrokdecode4DSL/decoders/adxl345/__init__.py
Normal 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
|
||||
96
libsigrokdecode4DSL/decoders/adxl345/lists.py
Normal file
96
libsigrokdecode4DSL/decoders/adxl345/lists.py
Normal 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'],
|
||||
}
|
||||
453
libsigrokdecode4DSL/decoders/adxl345/pd.py
Normal file
453
libsigrokdecode4DSL/decoders/adxl345/pd.py
Normal 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
|
||||
36
libsigrokdecode4DSL/decoders/am230x/__init__.py
Normal file
36
libsigrokdecode4DSL/decoders/am230x/__init__.py
Normal 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
|
||||
229
libsigrokdecode4DSL/decoders/am230x/pd.py
Normal file
229
libsigrokdecode4DSL/decoders/am230x/pd.py
Normal 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()
|
||||
28
libsigrokdecode4DSL/decoders/amulet_ascii/__init__.py
Normal file
28
libsigrokdecode4DSL/decoders/amulet_ascii/__init__.py
Normal 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
|
||||
73
libsigrokdecode4DSL/decoders/amulet_ascii/lists.py
Normal file
73
libsigrokdecode4DSL/decoders/amulet_ascii/lists.py
Normal 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
|
||||
]
|
||||
699
libsigrokdecode4DSL/decoders/amulet_ascii/pd.py
Normal file
699
libsigrokdecode4DSL/decoders/amulet_ascii/pd.py
Normal 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
|
||||
25
libsigrokdecode4DSL/decoders/arm_etmv3/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/arm_etmv3/__init__.py
Normal 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
|
||||
567
libsigrokdecode4DSL/decoders/arm_etmv3/pd.py
Normal file
567
libsigrokdecode4DSL/decoders/arm_etmv3/pd.py
Normal 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 = []
|
||||
25
libsigrokdecode4DSL/decoders/arm_itm/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/arm_itm/__init__.py
Normal 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
|
||||
373
libsigrokdecode4DSL/decoders/arm_itm/pd.py
Normal file
373
libsigrokdecode4DSL/decoders/arm_itm/pd.py
Normal 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 = []
|
||||
28
libsigrokdecode4DSL/decoders/arm_tpiu/__init__.py
Normal file
28
libsigrokdecode4DSL/decoders/arm_tpiu/__init__.py
Normal 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
|
||||
131
libsigrokdecode4DSL/decoders/arm_tpiu/pd.py
Normal file
131
libsigrokdecode4DSL/decoders/arm_tpiu/pd.py
Normal 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 = []
|
||||
30
libsigrokdecode4DSL/decoders/atsha204a/__init__.py
Normal file
30
libsigrokdecode4DSL/decoders/atsha204a/__init__.py
Normal 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
|
||||
323
libsigrokdecode4DSL/decoders/atsha204a/pd.py
Normal file
323
libsigrokdecode4DSL/decoders/atsha204a/pd.py
Normal 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'
|
||||
31
libsigrokdecode4DSL/decoders/aud/__init__.py
Normal file
31
libsigrokdecode4DSL/decoders/aud/__init__.py
Normal 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
|
||||
107
libsigrokdecode4DSL/decoders/aud/pd.py
Normal file
107
libsigrokdecode4DSL/decoders/aud/pd.py
Normal 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)
|
||||
25
libsigrokdecode4DSL/decoders/avr_isp/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/avr_isp/__init__.py
Normal 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
|
||||
41
libsigrokdecode4DSL/decoders/avr_isp/parts.py
Normal file
41
libsigrokdecode4DSL/decoders/avr_isp/parts.py
Normal 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.
|
||||
}
|
||||
213
libsigrokdecode4DSL/decoders/avr_isp/pd.py
Normal file
213
libsigrokdecode4DSL/decoders/avr_isp/pd.py
Normal 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 = []
|
||||
42
libsigrokdecode4DSL/decoders/avr_pdi/__init__.py
Normal file
42
libsigrokdecode4DSL/decoders/avr_pdi/__init__.py
Normal 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
|
||||
576
libsigrokdecode4DSL/decoders/avr_pdi/pd.py
Normal file
576
libsigrokdecode4DSL/decoders/avr_pdi/pd.py
Normal 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)
|
||||
36
libsigrokdecode4DSL/decoders/caliper/__init__.py
Normal file
36
libsigrokdecode4DSL/decoders/caliper/__init__.py
Normal 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
|
||||
146
libsigrokdecode4DSL/decoders/caliper/pd.py
Normal file
146
libsigrokdecode4DSL/decoders/caliper/pd.py
Normal 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()
|
||||
29
libsigrokdecode4DSL/decoders/can/__init__.py
Normal file
29
libsigrokdecode4DSL/decoders/can/__init__.py
Normal 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
|
||||
415
libsigrokdecode4DSL/decoders/can/pd.py
Normal file
415
libsigrokdecode4DSL/decoders/can/pd.py
Normal 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()
|
||||
28
libsigrokdecode4DSL/decoders/cc1101/__init__.py
Normal file
28
libsigrokdecode4DSL/decoders/cc1101/__init__.py
Normal 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
|
||||
115
libsigrokdecode4DSL/decoders/cc1101/lists.py
Normal file
115
libsigrokdecode4DSL/decoders/cc1101/lists.py
Normal 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'
|
||||
}
|
||||
296
libsigrokdecode4DSL/decoders/cc1101/pd.py
Normal file
296
libsigrokdecode4DSL/decoders/cc1101/pd.py
Normal 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))
|
||||
25
libsigrokdecode4DSL/decoders/cec/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/cec/__init__.py
Normal 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
|
||||
312
libsigrokdecode4DSL/decoders/cec/pd.py
Normal file
312
libsigrokdecode4DSL/decoders/cec/pd.py
Normal 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
|
||||
117
libsigrokdecode4DSL/decoders/cec/protocoldata.py
Normal file
117
libsigrokdecode4DSL/decoders/cec/protocoldata.py
Normal 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))
|
||||
34
libsigrokdecode4DSL/decoders/cfp/__init__.py
Normal file
34
libsigrokdecode4DSL/decoders/cfp/__init__.py
Normal 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
|
||||
110
libsigrokdecode4DSL/decoders/cfp/pd.py
Normal file
110
libsigrokdecode4DSL/decoders/cfp/pd.py
Normal 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']])
|
||||
37
libsigrokdecode4DSL/decoders/cjtag-oscan0/__init__.py
Normal file
37
libsigrokdecode4DSL/decoders/cjtag-oscan0/__init__.py
Normal 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
|
||||
390
libsigrokdecode4DSL/decoders/cjtag-oscan0/pd.py
Normal file
390
libsigrokdecode4DSL/decoders/cjtag-oscan0/pd.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
19
libsigrokdecode4DSL/decoders/common/__init__.py
Normal file
19
libsigrokdecode4DSL/decoders/common/__init__.py
Normal 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/>.
|
||||
##
|
||||
|
||||
20
libsigrokdecode4DSL/decoders/common/plugtrx/__init__.py
Normal file
20
libsigrokdecode4DSL/decoders/common/plugtrx/__init__.py
Normal 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 *
|
||||
192
libsigrokdecode4DSL/decoders/common/plugtrx/mod.py
Normal file
192
libsigrokdecode4DSL/decoders/common/plugtrx/mod.py
Normal 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',
|
||||
]
|
||||
20
libsigrokdecode4DSL/decoders/common/sdcard/__init__.py
Normal file
20
libsigrokdecode4DSL/decoders/common/sdcard/__init__.py
Normal 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 *
|
||||
151
libsigrokdecode4DSL/decoders/common/sdcard/mod.py
Normal file
151
libsigrokdecode4DSL/decoders/common/sdcard/mod.py
Normal 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
|
||||
}
|
||||
|
||||
20
libsigrokdecode4DSL/decoders/common/srdhelper/__init__.py
Normal file
20
libsigrokdecode4DSL/decoders/common/srdhelper/__init__.py
Normal 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 *
|
||||
84
libsigrokdecode4DSL/decoders/common/srdhelper/mod.py
Normal file
84
libsigrokdecode4DSL/decoders/common/srdhelper/mod.py
Normal 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())
|
||||
28
libsigrokdecode4DSL/decoders/counter/__init__.py
Normal file
28
libsigrokdecode4DSL/decoders/counter/__init__.py
Normal 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
|
||||
145
libsigrokdecode4DSL/decoders/counter/pd.py
Normal file
145
libsigrokdecode4DSL/decoders/counter/pd.py
Normal 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
|
||||
24
libsigrokdecode4DSL/decoders/dali/__init__.py
Normal file
24
libsigrokdecode4DSL/decoders/dali/__init__.py
Normal 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
|
||||
98
libsigrokdecode4DSL/decoders/dali/lists.py
Normal file
98
libsigrokdecode4DSL/decoders/dali/lists.py
Normal 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'],
|
||||
}
|
||||
245
libsigrokdecode4DSL/decoders/dali/pd.py
Normal file
245
libsigrokdecode4DSL/decoders/dali/pd.py
Normal 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
|
||||
28
libsigrokdecode4DSL/decoders/dcf77/__init__.py
Normal file
28
libsigrokdecode4DSL/decoders/dcf77/__init__.py
Normal 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
|
||||
311
libsigrokdecode4DSL/decoders/dcf77/pd.py
Normal file
311
libsigrokdecode4DSL/decoders/dcf77/pd.py
Normal 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'
|
||||
25
libsigrokdecode4DSL/decoders/dmx512/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/dmx512/__init__.py
Normal 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
|
||||
179
libsigrokdecode4DSL/decoders/dmx512/pd.py
Normal file
179
libsigrokdecode4DSL/decoders/dmx512/pd.py
Normal 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
|
||||
25
libsigrokdecode4DSL/decoders/ds1307/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/ds1307/__init__.py
Normal 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
|
||||
264
libsigrokdecode4DSL/decoders/ds1307/pd.py
Normal file
264
libsigrokdecode4DSL/decoders/ds1307/pd.py
Normal 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'
|
||||
25
libsigrokdecode4DSL/decoders/ds2408/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/ds2408/__init__.py
Normal 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
|
||||
129
libsigrokdecode4DSL/decoders/ds2408/pd.py
Normal file
129
libsigrokdecode4DSL/decoders/ds2408/pd.py
Normal 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']])
|
||||
25
libsigrokdecode4DSL/decoders/ds243x/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/ds243x/__init__.py
Normal 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
|
||||
270
libsigrokdecode4DSL/decoders/ds243x/pd.py
Normal file
270
libsigrokdecode4DSL/decoders/ds243x/pd.py
Normal 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])
|
||||
25
libsigrokdecode4DSL/decoders/ds28ea00/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/ds28ea00/__init__.py
Normal 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
|
||||
93
libsigrokdecode4DSL/decoders/ds28ea00/pd.py
Normal file
93
libsigrokdecode4DSL/decoders/ds28ea00/pd.py
Normal 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)]])
|
||||
24
libsigrokdecode4DSL/decoders/dsi/__init__.py
Normal file
24
libsigrokdecode4DSL/decoders/dsi/__init__.py
Normal 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
|
||||
157
libsigrokdecode4DSL/decoders/dsi/pd.py
Normal file
157
libsigrokdecode4DSL/decoders/dsi/pd.py
Normal 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
|
||||
35
libsigrokdecode4DSL/decoders/edid/__init__.py
Normal file
35
libsigrokdecode4DSL/decoders/edid/__init__.py
Normal 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
|
||||
1
libsigrokdecode4DSL/decoders/edid/config
Normal file
1
libsigrokdecode4DSL/decoders/edid/config
Normal file
@@ -0,0 +1 @@
|
||||
extra-install pnpids.txt
|
||||
668
libsigrokdecode4DSL/decoders/edid/pd.py
Normal file
668
libsigrokdecode4DSL/decoders/edid/pd.py
Normal 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
|
||||
2135
libsigrokdecode4DSL/decoders/edid/pnpids.txt
Normal file
2135
libsigrokdecode4DSL/decoders/edid/pnpids.txt
Normal file
File diff suppressed because it is too large
Load Diff
25
libsigrokdecode4DSL/decoders/eeprom24xx/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/eeprom24xx/__init__.py
Normal 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
|
||||
204
libsigrokdecode4DSL/decoders/eeprom24xx/lists.py
Normal file
204
libsigrokdecode4DSL/decoders/eeprom24xx/lists.py
Normal 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,
|
||||
},
|
||||
}
|
||||
433
libsigrokdecode4DSL/decoders/eeprom24xx/pd.py
Normal file
433
libsigrokdecode4DSL/decoders/eeprom24xx/pd.py
Normal 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()
|
||||
32
libsigrokdecode4DSL/decoders/eeprom93xx/__init__.py
Normal file
32
libsigrokdecode4DSL/decoders/eeprom93xx/__init__.py
Normal 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
|
||||
141
libsigrokdecode4DSL/decoders/eeprom93xx/pd.py
Normal file
141
libsigrokdecode4DSL/decoders/eeprom93xx/pd.py
Normal 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])
|
||||
24
libsigrokdecode4DSL/decoders/em4100/__init__.py
Normal file
24
libsigrokdecode4DSL/decoders/em4100/__init__.py
Normal 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
Reference in New Issue
Block a user