name = 'ADNS-5020'
longname = 'Avago ADNS-5020 optical mouse sensor'
desc = 'Bidirectional command and data over an SPI-like protocol.'
- license = 'gplv2'
+ license = 'gplv2+'
inputs = ['spi']
outputs = ['adns5020']
annotations = (
('warnings', 'Warnings', (2,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
checksum += self.bits2num(bitlist[i-8:i])
return checksum % 256
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.reset()
'default': 'alternative', 'values': ('alternative', 'original')},
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.buf = []
self.syncbuf = []
self.prevsample = 0
('function', 'Current function', (11,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.buf = []
self.syncbuf = []
self.swpackets = {}
('data', 'Stream data', (1,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.buf = []
self.syncbuf = []
self.prevsample = 0
self.stream = 0
- self.stream_ss = None
+ self.ss_stream = None
self.bytenum = 0
def start(self):
def stream_changed(self, ss, stream):
if self.stream != stream:
if self.stream != 0:
- self.put(self.stream_ss, ss, self.out_ann,
- [0, ["Stream %d" % self.stream, "S%d" % self.stream]])
+ self.put(self.ss_stream, ss, self.out_ann,
+ [0, ['Stream %d' % self.stream, 'S%d' % self.stream]])
self.stream = stream
- self.stream_ss = ss
+ 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_ann, [1, ['0x%02x' % byte]])
self.put(ss, es, self.out_python, ['DATA', 0, (byte, [])])
def process_frame(self, buf):
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+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
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+# 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 = 2
+ id = 'aud'
+ name = 'AUD'
+ longname = 'Advanced User Debugger'
+ desc = 'Renesas/Hitachi Advanced User Debugger (AUD) protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['aud']
+ 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.ncnt = 0
+ self.nmax = 0
+ self.addr = 0
+ self.lastaddr = 0
+ self.samplenum = 0
+ self.oldclk = 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 find_clk_edge(self, clk, sync, datapins):
+ # Ignore sample if there's no edge.
+ if clk == self.oldclk:
+ return
+ self.oldclk = clk
+ # Ignore falling edges.
+ if clk == 0:
+ return
+
+ # 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, ss, es, data):
+ for (self.samplenum, pins) in data:
+ clk = pins[0]
+ sync = pins[1]
+ d = pins[2:]
+ self.find_clk_edge(clk, sync, d)
('dev', 'Device', (9,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.mosi_bytes, self.miso_bytes = [], []
self.ss_cmd, self.es_cmd = 0, 0
--- /dev/null
+##
+## 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
--- /dev/null
+##
+## 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>
+##
+## This program is free software; you can 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 = 2
+ id = 'avr_pdi'
+ name = 'AVR PDI'
+ longname = 'Atmel Program and Debug Interface'
+ desc = 'Atmel proprietary interface for the ATxmega MCU.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['pdi']
+ 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.samplerate = None
+ # Detect input changes and clock edges.
+ self.prev_pins = None
+ self.prev_clock = 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 find_clk_edge(self, samplenum, clock_pin, data_pin):
+ # Ignore the sample if the clock pin has not changed.
+ if clock_pin == self.prev_clock:
+ return
+ self.prev_clock = clock_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 = 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, ss, es, data):
+ for samplenum, pins in data:
+
+ # Ignore identical samples.
+ if self.prev_pins == pins:
+ continue
+ self.prev_pins = pins
+
+ # Have DATA processed at appropriate clock edges.
+ clock_pin, data_pin = pins[0], pins[1]
+ self.find_clk_edge(samplenum, clock_pin, data_pin)
('fields', 'Fields', tuple(range(15)) + (16,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.reset_variables()
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
--- /dev/null
+##
+## 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 *
--- /dev/null
+##
+## 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',
+]
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+from .mod import *
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+# 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".
+}
+
+card_status = {
+ 0: 'Reserved for manufacturer test mode',
+ 1: 'Reserved for manufacturer test mode',
+ 2: 'Reserved for application specific commands',
+ 3: 'AKE_SEQ_ERROR',
+ 4: 'Reserved for SDIO card',
+ 5: 'APP_CMD',
+ 6: 'Unknown',
+ 7: 'Unknown',
+ 8: 'READY_FOR_DATA',
+ 9: 'CURRENT_STATE', # CURRENT_STATE is a 4-bit value (decimal: 0..15).
+ 10: 'CURRENT_STATE',
+ 11: 'CURRENT_STATE',
+ 12: 'CURRENT_STATE',
+ 13: 'ERASE_RESET',
+ 14: 'CARD_ECC_DISABLED',
+ 15: 'WP_ERASE_SKIP',
+ 16: 'CSD_OVERWRITE',
+ 17: 'Reserved for DEFERRED_RESPONSE', # See eSD addendum
+ 18: 'Reserved',
+ 19: 'ERROR',
+ 20: 'CC_ERROR',
+ 21: 'CARD_ECC_FAILED',
+ 22: 'ILLEGAL_COMMAND',
+ 23: 'COM_CRC_ERROR',
+ 24: 'LOCK_UNLOCK_FAILED',
+ 25: 'CARD_IS_LOCKED',
+ 26: 'WP_VIOLATION',
+ 27: 'ERASE_PARAM',
+ 28: 'ERASE_SEQ_ERROR',
+ 29: 'BLOCK_LEN_ERROR',
+ 30: 'ADDRESS_ERROR',
+ 31: 'OUT_OF_RANGE',
+}
+
+sd_status = {
+ # 311:0: Reserved for manufacturer
+ # 391:312: Reserved
+}
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+from .mod import *
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+# Return the specified BCD number (max. 8 bits) as integer.
+def bcd2int(b):
+ return (b & 0x0f) + ((b >> 4) * 10)
import sigrokdecode as srd
import calendar
-
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
- return (b & 0x0f) + ((b >> 4) * 10)
+from common.srdhelper import bcd2int
class SamplerateError(Exception):
pass
('warnings', 'Warnings', (19,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.state = 'WAIT FOR RISING EDGE'
self.oldpins = None
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+DMX512 (Digital MultipleX 512) is a protocol based on RS485, used to control
+professional lighting fixtures.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 'dmx512'
+ name = 'DMX512'
+ longname = 'Digital MultipleX 512'
+ desc = 'Professional lighting control protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['dmx512']
+ channels = (
+ {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'},
+ )
+ 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.samplerate = None
+ self.sample_usec = None
+ self.samplenum = -1
+ self.run_start = -1
+ self.run_bit = 0
+ 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, ss, es, data):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ for (self.samplenum, pins) in data:
+ # Seek for an interval with no state change with a length between
+ # 88 and 1000000 us (BREAK).
+ if self.state == 'FIND BREAK':
+ if self.run_bit == pins[0]:
+ continue
+ runlen = (self.samplenum - self.run_start) * self.sample_usec
+ if runlen > 88 and runlen < 1000000:
+ self.putr([1, ['Break']])
+ self.bit_break = self.run_bit
+ self.state = 'MARK MAB'
+ self.channel = 0
+ elif runlen >= 1000000:
+ # Error condition.
+ self.putr([10, ['Invalid break length']])
+ self.run_bit = pins[0]
+ self.run_start = self.samplenum
+ # Directly following the BREAK is the MARK AFTER BREAK.
+ elif self.state == 'MARK MAB':
+ if self.run_bit == pins[0]:
+ continue
+ self.putr([2, ['MAB']])
+ self.state = 'READ BYTE'
+ self.channel = 0
+ self.bit = 0
+ self.aggreg = pins[0]
+ 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':
+ self.next_sample = self.run_start + (self.bit + 1) * self.skip_per_bit
+ self.aggreg += pins[0]
+ if self.samplenum != self.next_sample:
+ continue
+ bit_value = 0 if round(self.aggreg/self.skip_per_bit) == self.bit_break else 1
+
+ if self.bit == 0:
+ self.byte = 0
+ self.putr([3, ['Start bit']])
+ if bit_value != 0:
+ # (Possibly) invalid start bit, mark but don't fail.
+ self.put(self.samplenum, self.samplenum,
+ self.out_ann, [10, ['Invalid start bit']])
+ elif self.bit >= 9:
+ self.put(self.samplenum - self.skip_per_bit,
+ self.samplenum, self.out_ann, [4, ['Stop bit']])
+ if bit_value != 1:
+ # Invalid stop bit, mark.
+ self.put(self.samplenum, self.samplenum,
+ self.out_ann, [10, ['Invalid stop bit']])
+ if self.bit == 10:
+ # On invalid 2nd stop bit, search for new break.
+ self.run_bit = pins[0]
+ self.state = 'FIND BREAK'
+ else:
+ # Label and process one bit.
+ self.put(self.samplenum - self.skip_per_bit,
+ self.samplenum, self.out_ann, [0, [str(bit_value)]])
+ self.byte |= bit_value << (self.bit - 1)
+
+ # Label a complete byte.
+ if self.bit == 10:
+ if self.channel == 0:
+ d = [5, ['Start code']]
+ else:
+ d = [6, ['Channel ' + str(self.channel)]]
+ self.put(self.run_start, self.next_sample, self.out_ann, d)
+ self.put(self.run_start + self.skip_per_bit,
+ self.next_sample - 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.run_bit = pins[0]
+ self.state = 'MARK IFT'
+
+ self.aggreg = pins[0]
+ self.bit += 1
+ # Mark the INTERFRAME-TIME between bytes / INTERPACKET-TIME between packets.
+ elif self.state == 'MARK IFT':
+ if self.run_bit == pins[0]:
+ continue
+ if self.channel > 512:
+ self.putr([8, ['Interpacket']])
+ self.state = 'FIND BREAK'
+ self.run_bit = pins[0]
+ self.run_start = self.samplenum
+ else:
+ self.putr([7, ['Interframe']])
+ self.state = 'READ BYTE'
+ self.bit = 0
+ self.run_start = self.samplenum
import re
import sigrokdecode as srd
+from common.srdhelper import bcd2int
days_of_week = (
'Sunday', 'Monday', 'Tuesday', 'Wednesday',
l += [('bit-' + re.sub('\/| ', '-', b).lower(), b + ' bit') for b in bits]
return tuple(l)
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
- return (b & 0x0f) + ((b >> 4) * 10)
-
class Decoder(srd.Decoder):
api_version = 2
id = 'ds1307'
('warnings', 'Warnings', (28,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.hours = -1
self.minutes = -1
('fields', 'Fields', (0,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = None
# Received data items, used as an index into samplenum/data
self.cnt = 0
[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]]])
+ ['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]]])
+ ['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']])
('binary', 'Binary'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.reset()
def start(self):
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either data 2 of the License, or
-## (at your option) any later data.
+## 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
('tags', 'Tags', (9,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.oldpin = None
self.last_samplenum = None
self.oldpl = 0
self.oldsamplenum = 0
self.last_bit_pos = 0
- self.first_ss = 0
+ self.ss_first = 0
self.first_one = 0
self.state = 'HEADER'
self.data = 0
self.data_bits = 0
- self.data_ss = 0
+ self.ss_data = 0
self.data_parity = 0
self.payload_cnt = 0
self.data_col_parity = [0, 0, 0, 0, 0, 0]
if self.first_one > 0:
self.first_one += 1
if self.first_one == 9:
- self.put(self.first_ss, es, self.out_ann,
+ self.put(self.ss_first, es, self.out_ann,
[1, ['Header', 'Head', 'He', 'H']])
self.first_one = 0
self.state = 'PAYLOAD'
return
if self.first_one == 0:
self.first_one = 1
- self.first_ss = ss
+ self.ss_first = ss
if bit == 0:
self.first_one = 0
if self.state == 'PAYLOAD':
self.payload_cnt += 1
if self.data_bits == 0:
- self.data_ss = ss
+ self.ss_data = ss
self.data = 0
self.data_parity = 0
self.data_bits += 1
if self.data_bits == 5:
s = 'Version/customer' if self.payload_cnt <= 10 else 'Data'
c = 2 if self.payload_cnt <= 10 else 3
- self.put(self.data_ss, ss, self.out_ann,
+ self.put(self.ss_data, ss, self.out_ann,
[c, [s + ': %X' % self.data, '%X' % self.data]])
s = 'OK' if self.data_parity == bit else 'ERROR'
c = 4 if s == 'OK' else 5
if self.state == 'TRAILER':
self.payload_cnt += 1
if self.data_bits == 0:
- self.data_ss = ss
+ self.ss_data = ss
self.data = 0
self.data_parity = 0
self.data_bits += 1
# Emit an annotation for valid-looking tags.
all_col_parity_ok = (self.data_col_parity[1:5] == self.col_parity[1:5])
if all_col_parity_ok and self.all_row_parity_ok:
- self.put(self.first_ss, es, self.out_ann,
+ self.put(self.ss_first, es, self.out_ann,
[9, ['Tag: %010X' % self.tag, 'Tag', 'T']])
self.tag = 0
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+EM4305 is a 100-150kHz RFID protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 'em4305'
+ name = 'EM4305'
+ longname = 'RFID EM4205/EM4305'
+ desc = 'EM4205/EM4305 100-150kHz RFID protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['em4305']
+ channels = (
+ {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+ )
+ options = (
+ {'id': 'coilfreq', 'desc': 'Coil frequency', 'default': 125000},
+ {'id': 'first_field_stop', 'desc': 'First field stop min', 'default': 40},
+ {'id': 'w_gap', 'desc': 'Write gap min', 'default': 12},
+ {'id': 'w_one_max', 'desc': 'Write one max', 'default': 32},
+ {'id': 'w_zero_on_min', 'desc': 'Write zero on min', 'default': 15},
+ {'id': 'w_zero_off_max', 'desc': 'Write zero off max', 'default': 27},
+ {'id': 'em4100_decode', 'desc': 'EM4100 decode', 'default': 'on',
+ 'values': ('on', 'off')},
+ )
+ annotations = (
+ ('bit_value', 'Bit value'),
+ ('first_field_stop', 'First field stop'),
+ ('write_gap', 'Write gap'),
+ ('write_mode_exit', 'Write mode exit'),
+ ('bit', 'Bit'),
+ ('opcode', 'Opcode'),
+ ('lock', 'Lock'),
+ ('data', 'Data'),
+ ('password', 'Password'),
+ ('address', 'Address'),
+ ('bitrate', 'Bitrate'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0,)),
+ ('structure', 'Structure', (1, 2, 3, 4)),
+ ('fields', 'Fields', (5, 6, 7, 8, 9)),
+ ('decode', 'Decode', (10,)),
+ )
+
+ def __init__(self):
+ self.samplerate = None
+ self.oldpin = None
+ self.last_samplenum = None
+ self.state = 'FFS_SEARCH'
+ self.bits_pos = [[0 for col in range(3)] for row in range(70)]
+ self.br_string = ['RF/8', 'RF/16', 'Unused', 'RF/32', 'RF/40',
+ 'Unused', 'Unused', 'RF/64',]
+ self.encoder = ['not used', 'Manchester', 'Bi-phase', 'not used']
+ self.delayed_on = ['No delay', 'Delayed on - BP/8', 'Delayed on - BP/4', 'No delay']
+ self.em4100_decode1_partial = 0
+ self.cmds = ['Invalid', 'Login', 'Write word', 'Invalid', 'Read word', 'Disable', 'Protect', 'Invalid']
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+ self.field_clock = self.samplerate / self.options['coilfreq']
+ self.wzmax = self.options['w_zero_off_max'] * self.field_clock
+ self.wzmin = self.options['w_zero_on_min'] * self.field_clock
+ self.womax = self.options['w_one_max'] * self.field_clock
+ self.ffs = self.options['first_field_stop'] * self.field_clock
+ self.writegap = self.options['w_gap'] * self.field_clock
+ self.nogap = 300 * self.field_clock
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def decode_config(self, idx):
+ bitrate = self.get_3_bits(idx+2)
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx+5][2],
+ self.out_ann, [10, ['Data rate: ' + \
+ self.br_string[bitrate], self.br_string[bitrate]]])
+ encoding = self.bits_pos[idx+6][0]<<0 | self.bits_pos[idx+7][0]<<1
+ self.put(self.bits_pos[idx+6][1], self.bits_pos[idx+10][2],
+ self.out_ann, [10, ['Encoder: ' + \
+ self.encoder[encoding], self.encoder[encoding]]])
+ self.put(self.bits_pos[idx+11][1], self.bits_pos[idx+12][2], self.out_ann,
+ [10, ['Zero bits', 'ZB']])
+ delay_on = self.bits_pos[idx+13][0]<<0 | self.bits_pos[idx+14][0]<<1
+ self.put(self.bits_pos[idx+13][1], self.bits_pos[idx+14][2],
+ self.out_ann, [10, ['Delayed on: ' + \
+ self.delayed_on[delay_on], self.delayed_on[delay_on]]])
+ lwr = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \
+ self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0
+ self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2],
+ self.out_ann, [10, ['Last default read word: %d' % lwr, 'LWR: %d' % lwr, '%d' % lwr]])
+ self.put(self.bits_pos[idx+20][1], self.bits_pos[idx+20][2],
+ self.out_ann, [10, ['Read login: %d' % self.bits_pos[idx+20][0], '%d' % self.bits_pos[idx+20][0]]])
+ self.put(self.bits_pos[idx+21][1], self.bits_pos[idx+21][2], self.out_ann,
+ [10, ['Zero bits', 'ZB']])
+ self.put(self.bits_pos[idx+22][1], self.bits_pos[idx+22][2],
+ self.out_ann, [10, ['Write login: %d' % self.bits_pos[idx+22][0], '%d' % self.bits_pos[idx+22][0]]])
+ self.put(self.bits_pos[idx+23][1], self.bits_pos[idx+24][2], self.out_ann,
+ [10, ['Zero bits', 'ZB']])
+ self.put(self.bits_pos[idx+25][1], self.bits_pos[idx+25][2],
+ self.out_ann, [10, ['Disable: %d' % self.bits_pos[idx+25][0], '%d' % self.bits_pos[idx+25][0]]])
+ self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+27][2],
+ self.out_ann, [10, ['Reader talk first: %d' % self.bits_pos[idx+27][0], 'RTF: %d' % self.bits_pos[idx+27][0]]])
+ self.put(self.bits_pos[idx+28][1], self.bits_pos[idx+28][2], self.out_ann,
+ [10, ['Zero bits', 'ZB']])
+ self.put(self.bits_pos[idx+29][1], self.bits_pos[idx+29][2],
+ self.out_ann, [10, ['Pigeon mode: %d' % self.bits_pos[idx+29][0], '%d' % self.bits_pos[idx+29][0]]])
+ self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2],
+ self.out_ann, [10, ['Reserved', 'Res', 'R']])
+
+ def put4bits(self, idx):
+ bits = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \
+ self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0]
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann,
+ [10, ['%X' % bits]])
+
+ def em4100_decode1(self, idx):
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx+9][2], self.out_ann,
+ [10, ['EM4100 header', 'EM header', 'Header', 'H']])
+ self.put4bits(idx+10)
+ bits = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \
+ self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0
+ self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2], self.out_ann,
+ [10, ['%X' % bits]])
+ self.put4bits(idx+21)
+ self.put4bits(idx+27)
+ self.em4100_decode1_partial = self.bits_pos[idx+32][0]<<3 | \
+ self.bits_pos[idx+33][0]<<2 | self.bits_pos[idx+34][0]<<1
+ self.put(self.bits_pos[idx+32][1], self.bits_pos[idx+34][2],
+ self.out_ann, [10, ['Partial nibble']])
+
+ def em4100_decode2(self, idx):
+ if self.em4100_decode1_partial != 0:
+ bits = self.em4100_decode1_partial + self.bits_pos[idx][0]
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+ self.out_ann, [10, ['%X' % bits]])
+ self.em4100_decode1_partial = 0
+ else:
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+ self.out_ann, [10, ['Partial nibble']])
+
+ self.put4bits(idx+2)
+ bits = self.bits_pos[idx+7][0]<<3 | self.bits_pos[idx+9][0]<<2 | \
+ self.bits_pos[idx+10][0]<<1 | self.bits_pos[idx+11][0]<<0
+ self.put(self.bits_pos[idx+7][1], self.bits_pos[idx+11][2], self.out_ann,
+ [10, ['%X' % bits]])
+ self.put4bits(idx+13)
+ self.put4bits(idx+19)
+ bits = self.bits_pos[idx+24][0]<<3 | self.bits_pos[idx+25][0]<<2 | \
+ self.bits_pos[idx+27][0]<<1 | self.bits_pos[idx+28][0]<<0
+ self.put(self.bits_pos[idx+24][1], self.bits_pos[idx+28][2], self.out_ann,
+ [10, ['%X' % bits]])
+ self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2],
+ self.out_ann, [10, ['EM4100 trailer']])
+
+ def get_32_bits(self, idx):
+ return self.get_8_bits(idx+27)<<24 | self.get_8_bits(idx+18)<<16 | \
+ self.get_8_bits(idx+9)<<8 | self.get_8_bits(idx)
+
+ def get_8_bits(self, idx):
+ retval = 0
+ for i in range(0, 8):
+ retval <<= 1
+ retval |= self.bits_pos[i+idx][0]
+ return retval
+
+ def get_3_bits(self, idx):
+ return self.bits_pos[idx][0]<<2 | self.bits_pos[idx+1][0]<<1 | \
+ self.bits_pos[idx+2][0]
+
+ def get_4_bits(self, idx):
+ return self.bits_pos[idx][0]<<0 | self.bits_pos[idx+1][0]<<1 | \
+ self.bits_pos[idx+2][0]<<2 | self.bits_pos[idx+3][0]<<3
+
+ def print_row_parity(self, idx, length):
+ parity = 0
+ for i in range(0, length):
+ parity += self.bits_pos[i+idx][0]
+ parity = parity & 0x1
+ if parity == self.bits_pos[idx+length][0]:
+ self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann,
+ [5, ['Row parity OK', 'Parity OK', 'OK']])
+ else:
+ self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann,
+ [5, ['Row parity failed', 'Parity failed', 'Fail']])
+
+ def print_col_parity(self, idx):
+ data_1 = self.get_8_bits(idx)
+ data_2 = self.get_8_bits(idx+9)
+ data_3 = self.get_8_bits(idx+9+9)
+ data_4 = self.get_8_bits(idx+9+9+9)
+ col_par = self.get_8_bits(idx+9+9+9+9)
+ col_par_calc = data_1^data_2^data_3^data_4
+
+ if col_par == col_par_calc:
+ self.put(self.bits_pos[idx+9+9+9+9][1], self.bits_pos[idx+9+9+9+9+7][2], self.out_ann,
+ [5, ['Column parity OK', 'Parity OK', 'OK']])
+ else:
+ self.put(self.bits_pos[idx+9+9+9+9][1], self.bits_pos[idx+9+9+9+9+7][2], self.out_ann,
+ [5, ['Column parity failed', 'Parity failed', 'Fail']])
+
+ def print_8bit_data(self, idx):
+ data = self.get_8_bits(idx)
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx+7][2], self.out_ann,
+ [9, ['Data' + ': %X' % data, '%X' % data]])
+
+ def put_fields(self):
+ if self.bit_nr == 50:
+ self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann,
+ [4, ['Logic zero']])
+ self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann,
+ [4, ['Command', 'Cmd', 'C']])
+ self.put(self.bits_pos[5][1], self.bits_pos[49][2], self.out_ann,
+ [4, ['Password', 'Passwd', 'Pass', 'P']])
+ # Get command.
+ cmd = self.get_3_bits(1)
+ self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann,
+ [5, [self.cmds[cmd]]])
+ self.print_row_parity(1, 3)
+
+ # Print data.
+ self.print_8bit_data(5)
+ self.print_row_parity(5, 8)
+ self.print_8bit_data(14)
+ self.print_row_parity(14, 8)
+ self.print_8bit_data(23)
+ self.print_row_parity(23, 8)
+ self.print_8bit_data(32)
+ self.print_row_parity(32, 8)
+ self.print_col_parity(5)
+ if self.bits_pos[49][0] == 0:
+ self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann,
+ [5, ['Stop bit', 'Stop', 'SB']])
+ else:
+ self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann,
+ [5, ['Stop bit error', 'Error']])
+
+ if cmd == 1:
+ password = self.get_32_bits(5)
+ self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann,
+ [10, ['Login password: %X' % password]])
+
+ if self.bit_nr == 57:
+ self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann,
+ [4, ['Logic zero', 'LZ']])
+ self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann,
+ [4, ['Command', 'Cmd', 'C']])
+ self.put(self.bits_pos[5][1], self.bits_pos[11][2], self.out_ann,
+ [4, ['Address', 'Addr', 'A']])
+ self.put(self.bits_pos[12][1], self.bits_pos[56][2], self.out_ann,
+ [4, ['Data', 'Da', 'D']])
+
+ # Get command.
+ cmd = self.get_3_bits(1)
+ self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann,
+ [5, [self.cmds[cmd]]])
+ self.print_row_parity(1, 3)
+
+ # Get address.
+ addr = self.get_4_bits(5)
+ self.put(self.bits_pos[5][1], self.bits_pos[8][2], self.out_ann,
+ [9, ['Addr' + ': %d' % addr, '%d' % addr]])
+ self.put(self.bits_pos[9][1], self.bits_pos[10][2], self.out_ann,
+ [5, ['Zero bits', 'ZB']])
+ self.print_row_parity(5, 6)
+ # Print data.
+ self.print_8bit_data(12)
+ self.print_row_parity(12, 8)
+ self.print_8bit_data(21)
+ self.print_row_parity(21, 8)
+ self.print_8bit_data(30)
+ self.print_row_parity(30, 8)
+ self.print_8bit_data(39)
+ self.print_row_parity(39, 8)
+ self.print_col_parity(12)
+ if self.bits_pos[56][0] == 0:
+ self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann,
+ [5, ['Stop bit', 'Stop', 'SB']])
+ else:
+ self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann,
+ [5, ['Stop bit error', 'Error']])
+
+ if addr == 4:
+ self.decode_config(12)
+
+ if addr == 2:
+ password = self.get_32_bits(12)
+ self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann,
+ [10, ['Write password: %X' % password]])
+
+ # If we are programming EM4100 data we can decode it halfway.
+ if addr == 5 and self.options['em4100_decode'] == 'on':
+ self.em4100_decode1(12)
+ if addr == 6 and self.options['em4100_decode'] == 'on':
+ self.em4100_decode2(12)
+
+ self.bit_nr = 0
+
+ def add_bits_pos(self, bit, ss_bit, es_bit):
+ if self.bit_nr < 70:
+ self.bits_pos[self.bit_nr][0] = bit
+ self.bits_pos[self.bit_nr][1] = ss_bit
+ self.bits_pos[self.bit_nr][2] = es_bit
+ self.bit_nr += 1
+
+ def decode(self, ss, es, data):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ for (self.samplenum, (pin,)) in data:
+ # Ignore identical samples early on (for performance reasons).
+ if self.oldpin == pin:
+ continue
+
+ if self.oldpin is None:
+ self.oldpin = pin
+ self.last_samplenum = self.samplenum
+ self.oldsamplenum = 0
+ self.old_gap_end = 0
+ self.gap_detected = 0
+ self.bit_nr = 0
+ continue
+
+ if self.oldpin != pin:
+ pl = self.samplenum - self.oldsamplenum
+ pp = pin
+ samples = self.samplenum - self.last_samplenum
+
+ if self.state == 'FFS_DETECTED':
+ if pl > self.writegap:
+ self.gap_detected = 1
+ if (self.last_samplenum - self.old_gap_end) > self.nogap:
+ self.gap_detected = 0
+ self.state = 'FFS_SEARCH'
+ self.put(self.old_gap_end, self.last_samplenum,
+ self.out_ann, [3, ['Write mode exit']])
+ self.put_fields()
+
+ if self.state == 'FFS_SEARCH':
+ if pl > self.ffs:
+ self.gap_detected = 1
+ self.put(self.last_samplenum, self.samplenum,
+ self.out_ann, [1, ['First field stop', 'Field stop', 'FFS']])
+ self.state = 'FFS_DETECTED'
+
+ if self.gap_detected == 1:
+ self.gap_detected = 0
+ if (self.last_samplenum - self.old_gap_end) > self.wzmin \
+ and (self.last_samplenum - self.old_gap_end) < self.wzmax:
+ self.put(self.old_gap_end, self.samplenum,
+ self.out_ann, [0, ['0']])
+ self.add_bits_pos(0, self.old_gap_end, self.samplenum)
+ if (self.last_samplenum - self.old_gap_end) > self.womax \
+ and (self.last_samplenum-self.old_gap_end) < self.nogap:
+ # One or more 1 bits
+ one_bits = (int)((self.last_samplenum - self.old_gap_end) / self.womax)
+ for ox in range(0, one_bits):
+ bs = (int)(self.old_gap_end+ox*self.womax)
+ be = (int)(self.old_gap_end+ox*self.womax + self.womax)
+ self.put(bs, be, self.out_ann, [0, ['1']])
+ self.add_bits_pos(1, bs, be)
+ if (self.samplenum - self.last_samplenum) > self.wzmin \
+ and (self.samplenum - self.last_samplenum) < self.wzmax:
+ bs = (int)(self.old_gap_end+one_bits*self.womax)
+ self.put(bs, self.samplenum, self.out_ann, [0, ['0']])
+ self.add_bits_pos(0, bs, self.samplenum)
+
+ self.old_gap_end = self.samplenum
+
+ if self.state == 'SKIP':
+ self.state = 'FFS_SEARCH'
+
+ self.oldsamplenum = self.samplenum
+ self.last_samplenum = self.samplenum
+ self.oldpin = pin
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+This protocol decoder can decode the GPIB (IEEE-488) protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 'gpib'
+ name = 'GPIB'
+ longname = 'General Purpose Interface Bus'
+ desc = 'IEEE-488 GPIB / HPIB protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['gpib']
+ channels = (
+ {'id': 'dio1' , 'name': 'DIO1', 'desc': 'Data I/O bit 1'},
+ {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'},
+ {'id': 'dio3' , 'name': 'DIO3', 'desc': 'Data I/O bit 3'},
+ {'id': 'dio4' , 'name': 'DIO4', 'desc': 'Data I/O bit 4'},
+ {'id': 'dio5' , 'name': 'DIO5', 'desc': 'Data I/O bit 5'},
+ {'id': 'dio6' , 'name': 'DIO6', 'desc': 'Data I/O bit 6'},
+ {'id': 'dio7' , 'name': 'DIO7', 'desc': 'Data I/O bit 7'},
+ {'id': 'dio8' , 'name': 'DIO8', 'desc': 'Data I/O bit 8'},
+ {'id': 'eoi', 'name': 'EOI', 'desc': 'End or identify'},
+ {'id': 'dav', 'name': 'DAV', 'desc': 'Data valid'},
+ {'id': 'nrfd', 'name': 'NRFD', 'desc': 'Not ready for data'},
+ {'id': 'ndac', 'name': 'NDAC', 'desc': 'Not data accepted'},
+ {'id': 'ifc', 'name': 'IFC', 'desc': 'Interface clear'},
+ {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
+ {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
+ {'id': 'ren', 'name': 'REN', 'desc': 'Remote enable'},
+ )
+ options = (
+ {'id': 'sample_total', 'desc': 'Total number of samples', 'default': 0},
+ )
+ annotations = (
+ ('items', 'Items'),
+ ('gpib', 'DAT/CMD'),
+ ('eoi', 'EOI'),
+ )
+ annotation_rows = (
+ ('bytes', 'Bytes', (0,)),
+ ('gpib', 'DAT/CMD', (1,)),
+ ('eoi', 'EOI', (2,)),
+ )
+
+ def __init__(self):
+ self.olddav = None
+ self.items = []
+ self.itemcount = 0
+ self.saved_item = None
+ self.saved_ATN = False
+ self.saved_EOI = False
+ self.samplenum = 0
+ self.oldpins = None
+ self.ss_item = self.es_item = None
+ self.first = True
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putb(self, data):
+ self.put(self.ss_item, self.es_item, self.out_ann, data)
+
+ def handle_bits(self, datapins):
+ dbyte = 0x20
+ dATN = False
+ item2 = False
+ dEOI = False
+ item3 = False
+ # If this is the first item in a word, save its sample number.
+ if self.itemcount == 0:
+ self.ss_word = self.samplenum
+
+ # Get the bits for this item.
+ item = 0
+ for i in range(8):
+ item |= datapins[i] << i
+
+ item = item ^ 0xff # Invert data byte.
+ self.items.append(item)
+ self.itemcount += 1
+
+ if datapins[14] == 0:
+ item2 = True
+ if datapins[8] == 0:
+ item3 = True
+
+ if self.first:
+ # Save the start sample and item for later (no output yet).
+ self.ss_item = self.samplenum
+ self.first = False
+ self.saved_item = item
+ self.saved_ATN = item2
+ self.saved_EOI = item3
+ else:
+ # Output the saved item.
+ dbyte = self.saved_item
+ dATN = self.saved_ATN
+ dEOI = self.saved_EOI
+ self.es_item = self.samplenum
+ self.putb([0, ['%02X' % self.saved_item]])
+
+ # Encode item byte to GPIB convention.
+ self.strgpib = ' '
+ if dATN: # ATN, decode commands.
+ if dbyte == 0x01: self.strgpib = 'GTL'
+ if dbyte == 0x04: self.strgpib = 'SDC'
+ if dbyte == 0x05: self.strgpib = 'PPC'
+ if dbyte == 0x08: self.strgpib = 'GET'
+ if dbyte == 0x09: self.strgpib = 'TCT'
+ if dbyte == 0x11: self.strgpib = 'LLO'
+ if dbyte == 0x14: self.strgpib = 'DCL'
+ if dbyte == 0x15: self.strgpib = 'PPU'
+ if dbyte == 0x18: self.strgpib = 'SPE'
+ if dbyte == 0x19: self.strgpib = 'SPD'
+ if dbyte == 0x3f: self.strgpib = 'UNL'
+ if dbyte == 0x5f: self.strgpib = 'UNT'
+ if dbyte > 0x1f and dbyte < 0x3f: # Address Listener.
+ self.strgpib = 'L' + chr(dbyte + 0x10)
+ if dbyte > 0x3f and dbyte < 0x5f: # Address Talker
+ self.strgpib = 'T' + chr(dbyte - 0x10)
+ else:
+ if dbyte > 0x1f and dbyte < 0x7f:
+ self.strgpib = chr(dbyte)
+ if dbyte == 0x0a:
+ self.strgpib = 'LF'
+ if dbyte == 0x0d:
+ self.strgpib = 'CR'
+
+ self.putb([1, [self.strgpib]])
+ self.strEOI = ' '
+ if dEOI:
+ self.strEOI = 'EOI'
+ self.putb([2, [self.strEOI]])
+
+ self.ss_item = self.samplenum
+ self.saved_item = item
+ self.saved_ATN = item2
+ self.saved_EOI = item3
+
+ if self.itemcount < 16:
+ return
+
+ self.itemcount, self.items = 0, []
+
+ def find_falling_dav_edge(self, dav, datapins):
+ # Ignore sample if the DAV pin hasn't changed.
+ if dav == self.olddav:
+ return
+ self.olddav = dav
+ # Sample on falling DAV edge.
+ if dav == 1:
+ return
+
+ # Found the correct DAV edge, now get the bits.
+ self.handle_bits(datapins)
+
+ def decode(self, ss, es, data):
+ lsn = self.options['sample_total']
+
+ for (self.samplenum, pins) in data:
+ if lsn > 0:
+ if (lsn - self.samplenum) == 1: # Show the last data word.
+ self.handle_bits(pins)
+
+ # Ignore identical samples early on (for performance reasons).
+ if self.oldpins == pins:
+ continue
+ self.oldpins = pins
+
+ self.find_falling_dav_edge(pins[9], pins)
def putx(self, data):
self.put(self.ss_edge, self.samplenum, self.out_ann, data)
- def __init__(self, **kwargs):
+ def __init__(self):
self.olddata = None
self.ss_edge = None
self.first_transition = True
('data-write', 'Data write'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.ss = self.es = self.ss_byte = -1
self.samplenum = None
inputs = ['i2c']
outputs = [] # TODO: Only known at run-time.
- def __init__(self, **kwargs):
+ def __init__(self):
self.packets = [] # Local cache of I²C packets
self.slaves = [] # List of known slave addresses
self.stream = -1 # Current output stream
'values': ('read', 'write', 'both')}
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.curslave = -1
self.curdirection = None
self.packets = [] # Local cache of I²C packets
('wav', 'WAV file'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.oldsck = 1
self.oldws = 1
[11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
'%s' % btn[1]]])
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
self.data = self.count = self.active = self.old_ir = None
('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.samplenum = None
self.edges, self.bits, self.ss_es_bits = [], [], []
('ascii-float', 'Jitter values as newline-separated ASCII floats'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'CLK'
self.samplerate = None
self.oldpin = None
def putx(self, delta):
# Adjust granularity.
if delta == 0 or delta >= 1:
- delta_s = "%.1fs" % (delta)
+ delta_s = '%.1fs' % (delta)
elif delta <= 1e-12:
- delta_s = "%.1ffs" % (delta * 1e15)
+ delta_s = '%.1ffs' % (delta * 1e15)
elif delta <= 1e-9:
- delta_s = "%.1fps" % (delta * 1e12)
+ delta_s = '%.1fps' % (delta * 1e12)
elif delta <= 1e-6:
- delta_s = "%.1fns" % (delta * 1e9)
+ delta_s = '%.1fns' % (delta * 1e9)
elif delta <= 1e-3:
- delta_s = "%.1fμs" % (delta * 1e6)
+ delta_s = '%.1fμs' % (delta * 1e6)
else:
- delta_s = "%.1fms" % (delta * 1e3)
+ delta_s = '%.1fms' % (delta * 1e3)
self.put(self.clk_start, self.sig_start, self.out_ann, [0, [delta_s]])
('states', 'States', tuple(range(15 + 1))),
)
- def __init__(self, **kwargs):
+ def __init__(self):
# self.state = 'TEST-LOGIC-RESET'
self.state = 'RUN-TEST/IDLE'
self.oldstate = None
('warnings', 'Warnings', (3,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.samplenums = None
('warnings', 'Human-readable warnings'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.reg = 0x00 # Currently selected register
self.databytes = []
('warnings', 'Warnings', (0,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.oldlclk = -1
self.samplenum = 0
('text', 'Human-readable text'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.trn_beg = 0
self.trn_end = 0
self.state = 'ROM'
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+## Copyright (C) 2016 Elias Oenal <sigrok@eliasoenal.com>
+## All rights reserved.
##
-## This program is free software; you can 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.
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are met:
##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
+## 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.
##
-## 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 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.
##
'''
The MDIO (Management Data Input/Output) protocol decoder supports the
-MII Management serial bus, with a clock line (MDC) and a bi-directional
-data line (MDIO).
+MII Management serial bus (a bidirectional bus between the PHY and the STA),
+with a clock line (MDC) and a bi-directional data line (MDIO).
MDIO is also known as SMI (Serial Management Interface).
+
+It's part of the Ethernet standard.
'''
from .pd import Decoder
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+## Copyright (C) 2016 Elias Oenal <sigrok@eliasoenal.com>
+## All rights reserved.
##
-## This program is free software; you can 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.
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are met:
##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
+## 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.
##
-## 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 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
name = 'MDIO'
longname = 'Management Data Input/Output'
desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.'
- license = 'gplv2+'
+ license = 'bsd'
inputs = ['logic']
outputs = ['mdio']
channels = (
{'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
{'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
)
+ options = (
+ {'id': 'show_debug_bits', 'desc': 'Show debug bits',
+ 'default': 'no', 'values': ('yes', 'no')},
+ )
annotations = (
- ('mdio-data', 'MDIO data'),
- ('mdio-bits', 'MDIO bits'),
- ('errors', 'Human-readable errors'),
+ ('bit-val', 'Bit value'),
+ ('bit-num', 'Bit number'),
+ ('frame', 'Frame'),
+ ('frame-idle', 'Bus idle state'),
+ ('frame-error', 'Frame error'),
+ ('decode', 'Decode'),
)
annotation_rows = (
- ('mdio-data', 'MDIO data', (0,)),
- ('mdio-bits', 'MDIO bits', (1,)),
- ('other', 'Other', (2,)),
+ ('bit-val', 'Bit value', (0,)),
+ ('bit-num', 'Bit number', (1,)),
+ ('frame', 'Frame', (2, 3)),
+ ('frame-error', 'Frame error', (4,)),
+ ('decode', 'Decode', (5,)),
)
def __init__(self):
- self.oldmdc = 0
- self.ss_block = -1
+ self.last_mdc = 1
+ self.illegal_bus = 0
self.samplenum = -1
- self.oldpins = None
+ self.clause45_addr = -1 # Clause 45 is context sensitive.
self.reset_decoder_state()
def start(self):
self.out_python = self.register(srd.OUTPUT_PYTHON)
self.out_ann = self.register(srd.OUTPUT_ANN)
- def putw(self, data):
- self.put(self.ss_block, self.samplenum, self.out_ann, data)
+ def putbit(self, mdio, ss, es):
+ self.put(ss, es, self.out_ann, [0, ['%d' % mdio]])
+ if self.options['show_debug_bits'] == 'yes':
+ self.put(ss, es, self.out_ann, [1, ['%d' % (self.bitcount - 1), '%d' % ((self.bitcount - 1) % 10)]])
- def putbit(self, mdio, start, stop):
- # Bit annotations.
- self.put(start, stop, self.out_ann, [1, ['%d' % mdio]])
+ def putff(self, data):
+ self.put(self.ss_frame_field, self.samplenum, self.out_ann, data)
def putdata(self):
- # FIXME: Only pass data, no bits.
- # Pass MDIO bits and then data to the next PD up the stack.
- ss, es = self.mdiobits[-1][1], self.mdiobits[0][2]
-
- # self.put(ss, es, self.out_python, ['BITS', self.mdiobits])
- self.put(ss, es, self.out_python, ['DATA', self.mdiodata])
-
- # Bit annotations.
- for bit in self.mdiobits:
- self.put(bit[1], bit[2], self.out_ann, [1, ['%d' % bit[0]]])
-
- # Error annotation if an error happened.
- if self.error:
- self.put(self.ss_bit, self.es_error, self.out_ann, [2, [self.error]])
- return
-
- op = 'READ' if self.operation else 'WRITE'
-
- # Dataword annotations.
- if self.ss_preamble != -1:
- self.put(self.ss_preamble, self.ss_start, self.out_ann, [0, ['PREAMBLE']])
- self.put(self.ss_start, self.ss_operation, self.out_ann, [0, ['START']])
- self.put(self.ss_operation, self.ss_phy, self.out_ann, [0, [op]])
- self.put(self.ss_phy, self.ss_reg, self.out_ann, [0, ['PHY: %d' % self.phy]])
- self.put(self.ss_reg, self.ss_turnaround, self.out_ann, [0, ['REG: %d' % self.reg]])
- self.put(self.ss_turnaround, self.ss_data, self.out_ann, [0, ['TURNAROUND']])
- self.put(self.ss_data, self.es_data, self.out_ann, [0, ['DATA: %04X' % self.data]])
+ self.put(self.ss_frame_field, self.mdiobits[0][2], self.out_ann,
+ [2, ['DATA: %04X' % self.data, 'DATA', 'D']])
+
+ if self.clause45 and self.opcode == 0:
+ self.clause45_addr = self.data
+
+ # Decode data.
+ if self.opcode > 0 or not self.clause45:
+ decoded_min = ''
+ if self.clause45 and self.clause45_addr != -1:
+ decoded_min += str.format('ADDR: %04X ' % self.clause45_addr)
+ elif self.clause45:
+ decoded_min += str.format('ADDR: UKWN ' % self.clause45_addr)
+
+ if self.clause45 and self.opcode > 1 \
+ or (not self.clause45 and self.opcode):
+ decoded_min += str.format('READ: %04X' % self.data)
+ is_read = 1
+ else:
+ decoded_min += str.format('WRITE: %04X' % self.data)
+ is_read = 0
+ decoded_ext = str.format(' %s: %02d' % \
+ ('PRTAD' if self.clause45 else 'PHYAD', self.portad))
+ decoded_ext += str.format(' %s: %02d' % \
+ ('DEVAD' if self.clause45 else 'REGAD', self.devad))
+ if self.ta_invalid or self.op_invalid:
+ decoded_ext += ' ERROR'
+ self.put(self.ss_frame, self.mdiobits[0][2], self.out_ann,
+ [5, [decoded_min + decoded_ext, decoded_min]])
+
+ self.put(self.ss_frame, self.mdiobits[0][2], self.out_python,
+ [(bool(self.clause45), int(self.clause45_addr), \
+ bool(is_read), int(self.portad), int(self.devad), \
+ int(self.data))])
+
+ # Post read increment address.
+ if self.clause45 and self.opcode == 2 and self.clause45_addr != -1:
+ self.clause45_addr += 1
def reset_decoder_state(self):
- self.mdiodata = 0
self.mdiobits = []
- self.bitcount = 0
- self.ss_preamble = -1
- self.ss_start = -1
- self.ss_operation = -1
- self.ss_phy = -1
- self.ss_reg = -1
- self.ss_turnaround = -1
- self.ss_data = -1
- self.phy = 0
- self.phy_bits = 0
- self.reg = 0
- self.reg_bits = 0
- self.data = 0
- self.data_bits = 0
- self.state = 'PREAMBLE'
- self.error = None
-
- def parse_preamble(self, mdio):
- if self.ss_preamble == -1:
- self.ss_preamble = self.samplenum
- if mdio != 1:
- self.error = 'Invalid preamble: could not find 32 consecutive bits set to 1'
- self.state = 'ERROR'
- elif self.bitcount == 31:
- self.state = 'START'
+ self.bitcount = -1
+ self.opcode = -1
+ self.clause45 = 0
+ self.ss_frame = -1
+ self.ss_frame_field = -1
+ self.preamble_len = 0
+ self.ta_invalid = -1
+ self.op_invalid = ''
+ self.portad = -1
+ self.portad_bits = 5
+ self.devad = -1
+ self.devad_bits = 5
+ self.data = -1
+ self.data_bits = 16
+ self.state = 'PRE'
+
+ def state_PRE(self, mdio):
+ if self.illegal_bus:
+ if mdio == 0: # Stay in illegal bus state.
+ return
+ else: # Leave and continue parsing.
+ self.illegal_bus = 0
+ self.put(self.ss_illegal, self.samplenum, self.out_ann,
+ [4, ['ILLEGAL BUS STATE', 'ILL']])
+ self.ss_frame = self.samplenum
+
+ if self.ss_frame == -1:
+ self.ss_frame = self.samplenum
+
+ if mdio == 1:
+ self.preamble_len += 1
+
+ # Valid MDIO can't clock more than 16 succeeding ones without being
+ # in either IDLE or PRE.
+ if self.preamble_len > 16:
+ if self.preamble_len >= 10000 + 32:
+ self.put(self.ss_frame, self.mdiobits[32][1], self.out_ann,
+ [3, ['IDLE #%d' % (self.preamble_len - 32), 'IDLE', 'I']])
+ self.ss_frame = self.mdiobits[32][1]
+ self.preamble_len = 32
+ # This is getting out of hand, free some memory.
+ del self.mdiobits[33:-1]
+ if mdio == 0:
+ if self.preamble_len < 32:
+ self.ss_frame = self.mdiobits[self.preamble_len][1]
+ self.put(self.ss_frame, self.samplenum, self.out_ann,
+ [4, ['SHORT PREAMBLE', 'SHRT PRE']])
+ elif self.preamble_len > 32:
+ self.ss_frame = self.mdiobits[32][1]
+ self.put(self.mdiobits[self.preamble_len][1],
+ self.mdiobits[32][1], self.out_ann,
+ [3, ['IDLE #%d' % (self.preamble_len - 32),
+ 'IDLE', 'I']])
+ self.preamble_len = 32
+ else:
+ self.ss_frame = self.mdiobits[32][1]
+ self.put(self.ss_frame, self.samplenum, self.out_ann,
+ [2, ['PRE #%d' % self.preamble_len, 'PRE', 'P']])
+ self.ss_frame_field = self.samplenum
+ self.state = 'ST'
+ elif mdio == 0:
+ self.ss_illegal = self.ss_frame
+ self.illegal_bus = 1
+
+ def state_ST(self, mdio):
+ if mdio == 0:
+ self.clause45 = 1
+ self.state = 'OP'
+
+ def state_OP(self, mdio):
+ if self.opcode == -1:
+ if self.clause45:
+ st = ['ST (Clause 45)', 'ST 45']
+ else:
+ st = ['ST (Clause 22)', 'ST 22']
+ self.putff([2, st + ['ST', 'S']])
+ self.ss_frame_field = self.samplenum
- def parse_start(self, mdio):
- if self.ss_start == -1:
- if mdio != 0:
- self.error = 'Invalid start bits: should be 01'
- self.state = 'ERROR'
+ if mdio:
+ self.opcode = 2
else:
- self.ss_start = self.samplenum
+ self.opcode = 0
else:
- if mdio != 1:
- self.error = 'Invalid start bits: should be 01'
- self.state = 'ERROR'
+ if self.clause45:
+ self.state = 'PRTAD'
+ self.opcode += mdio
else:
- self.state = 'OPERATION'
-
- def parse_operation(self, mdio):
- if self.ss_operation == -1:
- self.ss_operation = self.samplenum
- self.operation = mdio
- else:
- if mdio == self.operation:
- self.error = 'Invalid operation bits'
- self.state = 'ERROR'
+ if mdio == self.opcode:
+ self.op_invalid = 'invalid for Clause 22'
+ self.state = 'PRTAD'
+
+ def state_PRTAD(self, mdio):
+ if self.portad == -1:
+ self.portad = 0
+ if self.clause45:
+ if self.opcode == 0:
+ op = ['OP: ADDR', 'OP: A']
+ elif self.opcode == 1:
+ op = ['OP: WRITE', 'OP: W']
+ elif self.opcode == 2:
+ op = ['OP: READINC', 'OP: RI']
+ elif self.opcode == 3:
+ op = ['OP: READ', 'OP: R']
else:
- self.state = 'PHY'
-
- def parse_phy(self, mdio):
- if self.ss_phy == -1:
- self.ss_phy = self.samplenum
- self.phy_bits += 1
- self.phy |= mdio << (5 - self.phy_bits)
- if self.phy_bits == 5:
- self.state = 'REG'
-
- def parse_reg(self, mdio):
- if self.ss_reg == -1:
- self.ss_reg = self.samplenum
- self.reg_bits += 1
- self.reg |= mdio << (5 - self.reg_bits)
- if self.reg_bits == 5:
- self.state = 'TURNAROUND'
-
- def parse_turnaround(self, mdio):
- if self.ss_turnaround == -1:
- if self.operation == 0 and mdio != 1:
- self.error = 'Invalid turnaround bits'
- self.state = 'ERROR'
+ op = ['OP: READ', 'OP: R'] if self.opcode else ['OP: WRITE', 'OP: W']
+ self.putff([2, op + ['OP', 'O']])
+ if self.op_invalid:
+ self.putff([4, ['OP %s' % self.op_invalid, 'OP', 'O']])
+ self.ss_frame_field = self.samplenum
+ self.portad_bits -= 1
+ self.portad |= mdio << self.portad_bits
+ if not self.portad_bits:
+ self.state = 'DEVAD'
+
+ def state_DEVAD(self, mdio):
+ if self.devad == -1:
+ self.devad = 0
+ if self.clause45:
+ prtad = ['PRTAD: %02d' % self.portad, 'PRT', 'P']
+ else:
+ prtad = ['PHYAD: %02d' % self.portad, 'PHY', 'P']
+ self.putff([2, prtad])
+ self.ss_frame_field = self.samplenum
+ self.devad_bits -= 1
+ self.devad |= mdio << self.devad_bits
+ if not self.devad_bits:
+ self.state = 'TA'
+
+ def state_TA(self, mdio):
+ if self.ta_invalid == -1:
+ self.ta_invalid = ''
+ if self.clause45:
+ regad = ['DEVAD: %02d' % self.devad, 'DEV', 'D']
else:
- self.ss_turnaround = self.samplenum
+ regad = ['REGAD: %02d' % self.devad, 'REG', 'R']
+ self.putff([2, regad])
+ self.ss_frame_field = self.samplenum
+ if mdio != 1 and ((self.clause45 and self.opcode < 2)
+ or (not self.clause45 and self.opcode == 0)):
+ self.ta_invalid = ' invalid (bit1)'
else:
if mdio != 0:
- self.error = 'Invalid turnaround bits'
- self.state = 'ERROR'
- else:
- self.state = 'DATA'
-
- def parse_data(self, mdio):
- if self.ss_data == -1:
- self.ss_data = self.samplenum
- self.data_bits += 1
- self.data |= mdio << (16 - self.data_bits)
- if self.data_bits == 16:
- self.es_data = self.samplenum + int((self.samplenum - self.ss_data) / 15)
- self.state = 'DONE'
-
- def parse_error(self, mdio):
- if self.bitcount == 63:
- self.es_error = self.samplenum + int((self.samplenum - self.ss_bit) / 63)
- self.state = 'DONE'
-
- def handle_bit(self, mdio):
- # If this is the first bit of a command, save its sample number.
- if self.bitcount == 0:
- self.ss_bit = self.samplenum
- # No preamble?
- if mdio == 0:
- self.state = 'START'
-
- # Guesstimate the endsample for this bit (can be overridden below).
- es = self.samplenum
- if self.bitcount > 0:
- es += self.samplenum - self.mdiobits[0][1]
-
- self.mdiobits.insert(0, [mdio, self.samplenum, es])
-
- if self.bitcount > 0:
- self.bitsamples = (self.samplenum - self.ss_bit) / self.bitcount
- self.mdiobits[1][2] = self.samplenum
-
- if self.state == 'PREAMBLE':
- self.parse_preamble(mdio)
- elif self.state == 'START':
- self.parse_start(mdio)
- elif self.state == 'OPERATION':
- self.parse_operation(mdio)
- elif self.state == 'PHY':
- self.parse_phy(mdio)
- elif self.state == 'REG':
- self.parse_reg(mdio)
- elif self.state == 'TURNAROUND':
- self.parse_turnaround(mdio)
- elif self.state == 'DATA':
- self.parse_data(mdio)
- elif self.state == 'ERROR':
- self.parse_error(mdio)
-
- self.bitcount += 1
- if self.state == 'DONE':
- self.putdata()
- self.reset_decoder_state()
-
- def find_mdc_edge(self, mdc, mdio):
- # Output the current error annotation if the clock stopped running
- if self.state == 'ERROR' and self.samplenum - self.clocksample > (1.5 * self.bitsamples):
- self.es_error = self.clocksample + int((self.clocksample - self.ss_bit) / self.bitcount)
+ if self.ta_invalid:
+ self.ta_invalid = ' invalid (bit1 and bit2)'
+ else:
+ self.ta_invalid = ' invalid (bit2)'
+ self.state = 'DATA'
+
+ def state_DATA(self, mdio):
+ if self.data == -1:
+ self.data = 0
+ self.putff([2, ['TURNAROUND', 'TA', 'T']])
+ if self.ta_invalid:
+ self.putff([4, ['TURNAROUND%s' % self.ta_invalid,
+ 'TA%s' % self.ta_invalid, 'TA', 'T']])
+ self.ss_frame_field = self.samplenum
+ self.data_bits -= 1
+ self.data |= mdio << self.data_bits
+ if not self.data_bits:
+ # Output final bit.
+ self.mdiobits[0][2] = self.mdiobits[0][1] + self.quartile_cycle_length()
+ self.bitcount += 1
+ self.putbit(self.mdiobits[0][0], self.mdiobits[0][1], self.mdiobits[0][2])
self.putdata()
self.reset_decoder_state()
- # Ignore sample if the clock pin hasn't changed.
- if mdc == self.oldmdc:
- return
+ def process_state(self, argument, mdio):
+ method_name = 'state_' + str(argument)
+ method = getattr(self, method_name)
+ return method(mdio)
+
+ # Returns the first quartile point of the frames cycle lengths. This is a
+ # conservative guess for the end of the last cycle. On average it will be
+ # more likely to fall short, than being too long, which makes for better
+ # readability in GUIs.
+ def quartile_cycle_length(self):
+ # 48 is the minimum number of samples we have to have at the end of a
+ # frame. The last sample only has a leading clock edge and is ignored.
+ bitlen = []
+ for i in range(1, 49):
+ bitlen.append(self.mdiobits[i][2] - self.mdiobits[i][1])
+ bitlen = sorted(bitlen)
+ return bitlen[12]
- self.oldmdc = mdc
+ def handle_bit(self, mdio):
+ self.bitcount += 1
+ self.mdiobits.insert(0, [mdio, self.samplenum, -1])
- if mdc == 0: # Sample on rising clock edge.
- return
+ if self.bitcount > 0:
+ self.mdiobits[1][2] = self.samplenum # Note end of last cycle.
+ # Output the last bit we processed.
+ self.putbit(self.mdiobits[1][0], self.mdiobits[1][1], self.mdiobits[1][2])
- # Found the correct clock edge, now get/handle the bit(s).
- self.clocksample = self.samplenum
- self.handle_bit(mdio)
+ self.process_state(self.state, mdio)
def decode(self, ss, es, data):
for (self.samplenum, pins) in data:
# Ignore identical samples early on (for performance reasons).
- if self.oldpins == pins:
+ if self.last_mdc == pins[0]:
+ continue
+ self.last_mdc = pins[0]
+ if pins[0] == 0: # Check for rising edge.
continue
- self.oldpins, (mdc, mdio) = pins, pins
- self.find_mdc_edge(mdc, mdio)
+ # Found the correct clock edge, now get/handle the bit(s).
+ self.handle_bit(pins[1])
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2013-2016 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2016 Chris Dreher <chrisdreher@hotmail.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
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
+# Each status byte has 3 string names, each shorter than the previous
status_bytes = {
# Channel voice messages
- 0x80: 'note off',
- 0x90: 'note on', # However, velocity = 0 means "note off".
- 0xa0: 'polyphonic key pressure / aftertouch',
- 0xb0: 'control change',
- 0xc0: 'program change',
- 0xd0: 'channel pressure / aftertouch',
- 0xe0: 'pitch bend change',
+ 0x80: ['note off', 'note off', 'N off'],
+ 0x90: ['note on', 'note on', 'N on'], # However, velocity = 0 means "note off".
+ 0xa0: ['polyphonic key pressure / aftertouch', 'key pressure', 'KP' ],
+ 0xb0: ['control change', 'ctrl chg', 'CC'],
+ 0xc0: ['program change', 'prgm chg', 'PC'],
+ 0xd0: ['channel pressure / aftertouch', 'channel pressure', 'CP'],
+ 0xe0: ['pitch bend change', 'pitch bend', 'PB'],
# Channel mode messages
# 0xb0: 'select channel mode', # Note: Same as 'control change'.
# System exclusive messages
- 0xf0: 'system exclusive (SysEx)',
+ 0xf0: ['system exclusive', 'SysEx', 'SE'],
# System common messages
- 0xf1: 'MIDI time code quarter frame',
- 0xf2: 'song position pointer',
- 0xf3: 'song select',
- 0xf4: 'undefined',
- 0xf5: 'undefined',
- 0xf6: 'tune request',
- 0xf7: 'end of system exclusive (EOX)',
+ 0xf1: ['MIDI time code quarter frame', 'MIDI time code', 'MIDI time'],
+ 0xf2: ['song position pointer', 'song position', 'song pos'],
+ 0xf3: ['song select', 'song select', 'song sel'],
+ 0xf4: ['undefined 0xf4', 'undef 0xf4', 'undef'],
+ 0xf5: ['undefined 0xf5', 'undef 0xf5', 'undef'],
+ 0xf6: ['tune request', 'tune request', 'tune req'],
+ 0xf7: ['end of system exclusive (EOX)', 'end of SysEx', 'EOX'],
# System real time messages
- 0xf8: 'timing clock',
- 0xf9: 'undefined',
- 0xfa: 'start',
- 0xfb: 'continue',
- 0xfc: 'stop',
- 0xfd: 'undefined',
- 0xfe: 'active sensing',
- 0xff: 'system reset',
+ 0xf8: ['timing clock', 'timing clock', 'clock'],
+ 0xf9: ['undefined 0xf9', 'undef 0xf9', 'undef'],
+ 0xfa: ['start', 'start', 's'],
+ 0xfb: ['continue', 'continue', 'cont'],
+ 0xfc: ['stop', 'stop', 'st'],
+ 0xfd: ['undefined 0xfd', 'undef 0xfd', 'undef'],
+ 0xfe: ['active sensing', 'active sensing', 'sensing'],
+ 0xff: ['system reset', 'reset', 'rst'],
}
# Universal system exclusive (SysEx) messages, non-realtime (0x7e)
# Note: Not all IDs are used/listed, i.e. there are some "holes".
sysex_manufacturer_ids = {
- # American group
- (0x01): 'Sequential',
- (0x02): 'IDP',
- (0x03): 'Voyetra/Octave-Plateau',
- (0x04): 'Moog',
- (0x05): 'Passport Designs',
- (0x06): 'Lexicon',
- (0x07): 'Kurzweil',
- (0x08): 'Fender',
- (0x09): 'Gulbransen',
- (0x0a): 'AKG Acoustics',
- (0x0b): 'Voyce Music',
- (0x0c): 'Waveframe Corp',
- (0x0d): 'ADA Signal Processors',
- (0x0e): 'Garfield Electronics',
- (0x0f): 'Ensoniq',
- (0x10): 'Oberheim',
- (0x11): 'Apple Computer',
- (0x12): 'Grey Matter Response',
- (0x13): 'Digidesign',
- (0x14): 'Palm Tree Instruments',
- (0x15): 'JLCooper Electronics',
- (0x16): 'Lowrey',
- (0x17): 'Adams-Smith',
- (0x18): 'Emu Systems',
- (0x19): 'Harmony Systems',
- (0x1a): 'ART',
- (0x1b): 'Baldwin',
- (0x1c): 'Eventide',
- (0x1d): 'Inventronics',
- (0x1f): 'Clarity',
+ # American group (range 01-1f, 000001-001f7f)
+ (0x01,): 'Sequential',
+ (0x02,): 'IDP',
+ (0x03,): 'Voyetra/Octave-Plateau',
+ (0x04,): 'Moog',
+ (0x05,): 'Passport Designs',
+ (0x06,): 'Lexicon',
+ (0x07,): 'Kurzweil',
+ (0x08,): 'Fender',
+ (0x09,): 'Gulbransen',
+ (0x0a,): 'AKG Acoustics',
+ (0x0b,): 'Voyce Music',
+ (0x0c,): 'Waveframe Corp',
+ (0x0d,): 'ADA Signal Processors',
+ (0x0e,): 'Garfield Electronics',
+ (0x0f,): 'Ensoniq',
+ (0x10,): 'Oberheim',
+ (0x11,): 'Apple Computer',
+ (0x12,): 'Grey Matter Response',
+ (0x13,): 'Digidesign',
+ (0x14,): 'Palm Tree Instruments',
+ (0x15,): 'JLCooper Electronics',
+ (0x16,): 'Lowrey',
+ (0x17,): 'Adams-Smith',
+ (0x18,): 'Emu Systems',
+ (0x19,): 'Harmony Systems',
+ (0x1a,): 'ART',
+ (0x1b,): 'Baldwin',
+ (0x1c,): 'Eventide',
+ (0x1d,): 'Inventronics',
+ (0x1f,): 'Clarity',
(0x00, 0x00, 0x01): 'Time Warner Interactive',
(0x00, 0x00, 0x07): 'Digital Music Corp.',
(0x00, 0x01, 0x02): 'Crystal Semiconductor',
(0x00, 0x01, 0x03): 'Rockwell Semiconductor',
- # European group
- (0x20): 'Passac',
- (0x21): 'SIEL',
- (0x22): 'Synthaxe',
- (0x24): 'Hohner',
- (0x25): 'Twister',
- (0x26): 'Solton',
- (0x27): 'Jellinghaus MS',
- (0x28): 'Southworth Music Systems',
- (0x29): 'PPG',
- (0x2a): 'JEN',
- (0x2b): 'SSL Limited',
- (0x2c): 'Audio Veritrieb',
- (0x2f): 'Elka',
-
- (0x30): 'Dynacord',
- (0x31): 'Viscount',
- (0x33): 'Clavia Digital Instruments',
- (0x34): 'Audio Architecture',
- (0x35): 'GeneralMusic Corp.',
- (0x39): 'Soundcraft Electronics',
- (0x3b): 'Wersi',
- (0x3c): 'Avab Elektronik Ab',
- (0x3d): 'Digigram',
- (0x3e): 'Waldorf Electronics',
- (0x3f): 'Quasimidi',
+ # European group (range 20-3f, 002000-003f7f)
+ (0x20,): 'Passac',
+ (0x21,): 'SIEL',
+ (0x22,): 'Synthaxe',
+ (0x24,): 'Hohner',
+ (0x25,): 'Twister',
+ (0x26,): 'Solton',
+ (0x27,): 'Jellinghaus MS',
+ (0x28,): 'Southworth Music Systems',
+ (0x29,): 'PPG',
+ (0x2a,): 'JEN',
+ (0x2b,): 'SSL Limited',
+ (0x2c,): 'Audio Veritrieb',
+ (0x2f,): 'Elka',
+
+ (0x30,): 'Dynacord',
+ (0x31,): 'Viscount',
+ (0x33,): 'Clavia Digital Instruments',
+ (0x34,): 'Audio Architecture',
+ (0x35,): 'GeneralMusic Corp.',
+ (0x39,): 'Soundcraft Electronics',
+ (0x3b,): 'Wersi',
+ (0x3c,): 'Avab Elektronik Ab',
+ (0x3d,): 'Digigram',
+ (0x3e,): 'Waldorf Electronics',
+ (0x3f,): 'Quasimidi',
(0x00, 0x20, 0x00): 'Dream',
(0x00, 0x20, 0x01): 'Strand Lighting',
(0x00, 0x20, 0x2d): 'Blue Chip Music Tech',
(0x00, 0x20, 0x2e): 'BEE OH Corp',
- # Japanese group
- (0x40): 'Kawai',
- (0x41): 'Roland',
- (0x42): 'Korg',
- (0x43): 'Yamaha',
- (0x44): 'Casio',
- (0x46): 'Kamiya Studio',
- (0x47): 'Akai',
- (0x48): 'Japan Victor',
- (0x49): 'Mesosha',
- (0x4a): 'Hoshino Gakki',
- (0x4b): 'Fujitsu Elect',
- (0x4c): 'Sony',
- (0x4d): 'Nisshin Onpa',
- (0x4e): 'TEAC',
- (0x50): 'Matsushita Electric',
- (0x51): 'Fostex',
- (0x52): 'Zoom',
- (0x53): 'Midori Electronics',
- (0x54): 'Matsushita Communication Industrial',
- (0x55): 'Suzuki Musical Inst. Mfg.',
+ # Japanese group (range 40-5f, 004000-005f7f)
+ (0x40,): 'Kawai',
+ (0x41,): 'Roland',
+ (0x42,): 'Korg',
+ (0x43,): 'Yamaha',
+ (0x44,): 'Casio',
+ (0x46,): 'Kamiya Studio',
+ (0x47,): 'Akai',
+ (0x48,): 'Japan Victor',
+ (0x49,): 'Mesosha',
+ (0x4a,): 'Hoshino Gakki',
+ (0x4b,): 'Fujitsu Elect',
+ (0x4c,): 'Sony',
+ (0x4d,): 'Nisshin Onpa',
+ (0x4e,): 'TEAC',
+ (0x50,): 'Matsushita Electric',
+ (0x51,): 'Fostex',
+ (0x52,): 'Zoom',
+ (0x53,): 'Midori Electronics',
+ (0x54,): 'Matsushita Communication Industrial',
+ (0x55,): 'Suzuki Musical Inst. Mfg.',
+
+ # Other (range 60-7c, 006000-007f7f)
+
+ # Special (7d-7f)
+ (0x7d,): 'Non-Commercial',
+ (0x7e,): 'Universal Non-Realtime',
+ (0x7f,): 'Universal Realtime',
}
control_functions = {
- 0x00: 'bank select',
- 0x01: 'modulation wheel/lever',
- 0x02: 'breath controller',
- # 0x03: undefined
- 0x04: 'foot controller',
- 0x05: 'portamento time',
- 0x06: 'data entry MSB',
- 0x07: 'channel volume (formerly main volume)',
- 0x08: 'balance',
- # 0x09: undefined
- 0x0a: 'pan',
- 0x0b: 'expression controller',
- 0x0c: 'effect control 1',
- 0x0d: 'effect control 2',
- # 0x0e-0x0f: undefined
- 0x10: 'general purpose controller 1',
- 0x11: 'general purpose controller 2',
- 0x12: 'general purpose controller 3',
- 0x13: 'general purpose controller 4',
- # 0x14-0x1f: undefined
- # 0x20-0x3f: LSB for values 0x00-0x1f
- 0x40: 'damper pedal (sustain)',
- 0x41: 'portamento on/off',
- 0x42: 'sostenuto',
- 0x43: 'soft pedal',
- 0x44: 'legato footswitch', # vv: 00-3f = normal, 40-7f = legato
- 0x45: 'hold 2',
- 0x46: 'sound controller 1 (default: sound variation)',
- 0x47: 'sound controller 2 (default: timbre / harmonic intensity)',
- 0x48: 'sound controller 3 (default: release time)',
- 0x49: 'sound controller 4 (default: attack time)',
- 0x4a: 'sound controller 5 (default: brightness)',
- 0x4b: 'sound controller 6 (GM2 default: decay time)',
- 0x4c: 'sound controller 7 (GM2 default: vibrato rate)',
- 0x4d: 'sound controller 8 (GM2 default: vibrato depth)',
- 0x4e: 'sound controller 9 (GM2 default: vibrato delay)',
- 0x4f: 'sound controller 10',
- 0x50: 'general purpose controller 5',
- 0x51: 'general purpose controller 6',
- 0x52: 'general purpose controller 7',
- 0x53: 'general purpose controller 8',
- 0x54: 'portamento control',
+ 0x00: ['bank select MSB', 'bank MSB', 'bank-M'],
+ 0x01: ['modulation wheel/lever MSB', 'modulation MSB', 'mod-M'],
+ 0x02: ['breath controller MSB', 'breath MSB', 'breath-M'],
+ # 0x03: undefined MSB
+ 0x04: ['foot controller MSB', 'foot MSB', 'foot-M'],
+ 0x05: ['portamento time MSB', 'portamento MSB', 'porta-M'],
+ 0x06: ['data entry MSB', 'data entry MSB', 'data-M'],
+ 0x07: ['channel volume MSB (formerly main volume)', 'channel volume MSB', 'ch vol-M'],
+ 0x08: ['balance MSB', 'bal MSB', 'bal-M'],
+ # 0x09: undefined MSB
+ 0x0a: ['pan MSB', 'pan MSB', 'pan-M'],
+ 0x0b: ['expression controller MSB', 'expression MSB', 'expr-M'],
+ 0x0c: ['effect control 1 MSB', 'effect 1 MSB', 'eff-1-M'],
+ 0x0d: ['effect control 2 MSB', 'effect 2 MSB', 'eff-2-M'],
+ # 0x0e-0x0f: undefined MSB
+ 0x10: ['general purpose controller 1 MSB', 'GP ctrl 1 MSB', 'GPC-1-M'],
+ 0x11: ['general purpose controller 2 MSB', 'GP ctrl 2 MSB', 'GPC-2-M'],
+ 0x12: ['general purpose controller 3 MSB', 'GP ctrl 3 MSB', 'GPC-3-M'],
+ 0x13: ['general purpose controller 4 MSB', 'GP ctrl 4 MSB', 'GPC-4-M'],
+ # 0x14-0x1f: undefined MSB
+ 0x20: ['bank select LSB', 'bank LSB', 'bank-L'],
+ 0x21: ['modulation wheel/lever LSB', 'modulation LSB', 'mod-L'],
+ 0x22: ['breath controller LSB', 'breath LSB', 'breath-L'],
+ # 0x23: undefined LSB
+ 0x24: ['foot controller LSB', 'foot LSB', 'foot-L'],
+ 0x25: ['portamento time LSB', 'portamento LSB', 'porta-L'],
+ 0x26: ['data entry LSB', 'data entry LSB', 'data-L'],
+ 0x27: ['channel volume LSB (formerly main volume)', 'channel volume LSB', 'ch vol-L'],
+ 0x28: ['balance LSB', 'bal LSB', 'bal-L'],
+ # 0x29: undefined LSB
+ 0x2a: ['pan LSB', 'pan LSB', 'pan-L'],
+ 0x2b: ['expression controller LSB', 'expression LSB', 'expr-L'],
+ 0x2c: ['effect control 1 LSB', 'effect 1 LSB', 'eff-1-L'],
+ 0x2d: ['effect control 2 LSB', 'effect 2 LSB', 'eff-2-L'],
+ # 0x2e-0x2f: undefined LSB
+ 0x30: ['general purpose controller 1 LSB', 'GP ctrl 1 LSB', 'GPC-1-L'],
+ 0x31: ['general purpose controller 2 LSB', 'GP ctrl 2 LSB', 'GPC-2-L'],
+ 0x32: ['general purpose controller 3 LSB', 'GP ctrl 3 LSB', 'GPC-3-L'],
+ 0x33: ['general purpose controller 4 LSB', 'GP ctrl 4 LSB', 'GPC-4-L'],
+ # 0x34-0x3f: undefined LSB
+ 0x40: ['damper pedal (sustain)', 'sustain', 'sust'],
+ 0x41: ['portamento on/off', 'porta on/off', 'porta on/off'],
+ 0x42: ['sostenuto', 'sostenuto', 'sostenuto'],
+ 0x43: ['soft pedal', 'soft pedal', 'soft pedal'],
+ 0x44: ['legato footswitch', 'legato switch', 'legato'], # vv: 00-3f = normal, 40-7f = legato
+ 0x45: ['hold 2', 'hold 2', 'hold 2'],
+ 0x46: ['sound controller 1 (default: sound variation)', 'sound ctrl 1', 'snd ctrl 1'],
+ 0x47: ['sound controller 2 (default: timbre / harmonic intensity)', 'sound ctrl 2', 'snd ctrl 2'],
+ 0x48: ['sound controller 3 (default: release time)', 'sound ctrl 3', 'snd ctrl 3'],
+ 0x49: ['sound controller 4 (default: attack time)', 'sound ctrl 4', 'snd ctrl 4'],
+ 0x4a: ['sound controller 5 (default: brightness)', 'sound ctrl 5', 'snd ctrl 5'],
+ 0x4b: ['sound controller 6 (GM2 default: decay time)', 'sound ctrl 6', 'snd ctrl 6'],
+ 0x4c: ['sound controller 7 (GM2 default: vibrato rate)', 'sound ctrl 7', 'snd ctrl 7'],
+ 0x4d: ['sound controller 8 (GM2 default: vibrato depth)', 'sound ctrl 8', 'snd ctrl 8'],
+ 0x4e: ['sound controller 9 (GM2 default: vibrato delay)', 'sound ctrl 9', 'snd ctrl 9'],
+ 0x4f: ['sound controller 10', 'sound ctrl 10', 'snd ctrl 10'],
+ 0x50: ['general purpose controller 5', 'GP controller 5', 'GPC-5'],
+ 0x51: ['general purpose controller 6', 'GP controller 6', 'GPC-6'],
+ 0x52: ['general purpose controller 7', 'GP controller 7', 'GPC-7'],
+ 0x53: ['general purpose controller 8', 'GP controller 8', 'GPC-8'],
+ 0x54: ['portamento control', 'portamento ctrl', 'porta ctrl'],
# 0x55-0x5a: undefined
- 0x5b: 'effects 1 depth (formerly external effects depth)',
- 0x5c: 'effects 2 depth (formerly tremolo depth)',
- 0x5d: 'effects 3 depth (formerly chorus depth)',
- 0x5e: 'effects 4 depth (formerly celeste/detune depth)',
- 0x5f: 'effects 5 depth (formerly phaser depth)',
- 0x60: 'data increment',
- 0x61: 'data decrement',
- 0x62: 'non-registered parameter number LSB',
- 0x63: 'non-registered parameter number MSB',
- 0x64: 'registered parameter number LSB',
- 0x65: 'registered parameter number MSB',
+ 0x5b: ['effects 1 depth (formerly external effects depth)', 'effects 1 depth', 'eff 1 depth'],
+ 0x5c: ['effects 2 depth (formerly tremolo depth)', 'effects 2 depth', 'eff 2 depth'],
+ 0x5d: ['effects 3 depth (formerly chorus depth)', 'effects 3 depth', 'eff 3 depth'],
+ 0x5e: ['effects 4 depth (formerly celeste/detune depth)', 'effects 4 depth', 'eff 4 depth'],
+ 0x5f: ['effects 5 depth (formerly phaser depth)', 'effects 5 depth', 'eff 5 depth'],
+ 0x60: ['data increment', 'data inc', 'data++'],
+ 0x61: ['data decrement', 'data dec', 'data--'],
+ 0x62: ['Non-Registered Parameter Number LSB', 'NRPN LSB', 'NRPN-L'],
+ 0x63: ['Non-Registered Parameter Number MSB', 'NRPN MSB', 'NRPN-M'],
+ 0x64: ['Registered Parameter Number LSB', 'RPN LSB', 'RPN-L'],
+ 0x65: ['Registered Parameter Number MSB', 'RPN MSB', 'RPN-M'],
# 0x66-0x77: undefined
# 0x78-0x7f: reserved for channel mode messages
- 0x78: 'all sound off',
- 0x79: 'reset all controllers',
- 0x7a: 'local control on/off',
- 0x7b: 'all notes off',
- 0x7c: 'omni mode off', # all notes off
- 0x7d: 'omni mode on', # all notes off
- 0x7e: 'poly mode off', # mono mode on, all notes off
- 0x7f: 'poly mode on', # mono mode off, all notes off
+ 0x78: ['all sound off', 'all snd off', 'snd off'],
+ 0x79: ['reset all controllers', 'reset all ctrls', 'reset ctrls'],
+ 0x7a: ['local control', 'local ctrl', 'local ctrl'],
+ 0x7b: ['all notes off', 'notes off', 'notes off'],
+ 0x7c: ['omni mode off', 'omni off', 'omni off'], # all notes off
+ 0x7d: ['omni mode on', 'omni on', 'omni on'], # all notes off
+ 0x7e: ['mono mode on', 'mono on', 'mono'], # mono mode on, all notes off
+ 0x7f: ['poly mode on', 'poly on', 'poly'], # mono mode off, all notes off
+}
+
+gm_instruments = {
+ 1: 'Acoustic Grand Piano',
+ 2: 'Bright Acoustic Piano',
+ 3: 'Electric Grand Piano',
+ 4: 'Honky-tonk Piano',
+ 5: 'Electric Piano 1',
+ 6: 'Electric Piano 2',
+ 7: 'Harpsichord',
+ 8: 'Clavi',
+ 9: 'Celesta',
+ 10: 'Glockenspiel',
+ 11: 'Music Box',
+ 12: 'Vibraphone',
+ 13: 'Marimba',
+ 14: 'Xylophone',
+ 15: 'Tubular Bells',
+ 16: 'Dulcimer',
+ 17: 'Drawbar Organ',
+ 18: 'Percussive Organ',
+ 19: 'Rock Organ',
+ 20: 'Church Organ',
+ 21: 'Reed Organ',
+ 22: 'Accordion',
+ 23: 'Harmonica',
+ 24: 'Tango Accordion',
+ 25: 'Acoustic Guitar (nylon)',
+ 26: 'Acoustic Guitar (steel)',
+ 27: 'Electric Guitar (jazz)',
+ 28: 'Electric Guitar (clean)',
+ 29: 'Electric Guitar (muted)',
+ 30: 'Overdriven Guitar',
+ 31: 'Distortion Guitar',
+ 32: 'Guitar harmonics',
+ 33: 'Acoustic Bass',
+ 34: 'Electric Bass (finger)',
+ 35: 'Electric Bass (pick)',
+ 36: 'Fretless Bass',
+ 37: 'Slap Bass 1',
+ 38: 'Slap Bass 2',
+ 39: 'Synth Bass 1',
+ 40: 'Synth Bass 2',
+ 41: 'Violin',
+ 42: 'Viola',
+ 43: 'Cello',
+ 44: 'Contrabass',
+ 45: 'Tremolo Strings',
+ 46: 'Pizzicato Strings',
+ 47: 'Orchestral Harp',
+ 48: 'Timpani',
+ 49: 'String Ensemble 1',
+ 50: 'String Ensemble 2',
+ 51: 'SynthStrings 1',
+ 52: 'SynthStrings 2',
+ 53: 'Choir Aahs',
+ 54: 'Voice Oohs',
+ 55: 'Synth Voice',
+ 56: 'Orchestra Hit',
+ 57: 'Trumpet',
+ 58: 'Trombone',
+ 59: 'Tuba',
+ 60: 'Muted Trumpet',
+ 61: 'French Horn',
+ 62: 'Brass Section',
+ 63: 'SynthBrass 1',
+ 64: 'SynthBrass 2',
+ 65: 'Soprano Sax',
+ 66: 'Alto Sax',
+ 67: 'Tenor Sax',
+ 68: 'Baritone Sax',
+ 69: 'Oboe',
+ 70: 'English Horn',
+ 71: 'Bassoon',
+ 72: 'Clarinet',
+ 73: 'Piccolo',
+ 74: 'Flute',
+ 75: 'Recorder',
+ 76: 'Pan Flute',
+ 77: 'Blown Bottle',
+ 78: 'Shakuhachi',
+ 79: 'Whistle',
+ 80: 'Ocarina',
+ 81: 'Lead 1 (square)',
+ 82: 'Lead 2 (sawtooth)',
+ 83: 'Lead 3 (calliope)',
+ 84: 'Lead 4 (chiff)',
+ 85: 'Lead 5 (charang)',
+ 86: 'Lead 6 (voice)',
+ 87: 'Lead 7 (fifths)',
+ 88: 'Lead 8 (bass + lead)',
+ 89: 'Pad 1 (new age)',
+ 90: 'Pad 2 (warm)',
+ 91: 'Pad 3 (polysynth)',
+ 92: 'Pad 4 (choir)',
+ 93: 'Pad 5 (bowed)',
+ 94: 'Pad 6 (metallic)',
+ 95: 'Pad 7 (halo)',
+ 96: 'Pad 8 (sweep)',
+ 97: 'FX 1 (rain)',
+ 98: 'FX 2 (soundtrack)',
+ 99: 'FX 3 (crystal)',
+ 100: 'FX 4 (atmosphere)',
+ 101: 'FX 5 (brightness)',
+ 102: 'FX 6 (goblins)',
+ 103: 'FX 7 (echoes)',
+ 104: 'FX 8 (sci-fi)',
+ 105: 'Sitar',
+ 106: 'Banjo',
+ 107: 'Shamisen',
+ 108: 'Koto',
+ 109: 'Kalimba',
+ 110: 'Bag pipe',
+ 111: 'Fiddle',
+ 112: 'Shanai',
+ 113: 'Tinkle Bell',
+ 114: 'Agogo',
+ 115: 'Steel Drums',
+ 116: 'Woodblock',
+ 117: 'Taiko Drum',
+ 118: 'Melodic Tom',
+ 119: 'Synth Drum',
+ 120: 'Reverse Cymbal',
+ 121: 'Guitar Fret Noise',
+ 122: 'Breath Noise',
+ 123: 'Seashore',
+ 124: 'Bird Tweet',
+ 125: 'Telephone Ring',
+ 126: 'Helicopter',
+ 127: 'Applause',
+ 128: 'Gunshot',
+}
+
+drum_kit = {
+ 1: 'GM Standard Kit',
+ 9: 'GS Room Kit',
+ 17: 'GS Power Kit',
+ 25: 'GS Power Kit',
+ 26: 'GS TR-808 Kit',
+ 33: 'GS Jazz Kit',
+ 41: 'GS Brush Kit',
+ 49: 'GS Orchestra Kit',
+ 57: 'GS Sound FX Kit',
+ 128: 'GS CM-64/CM-32 Kit',
+}
+
+# Each quarter frame type has 2 string names, each shorter than the previous
+quarter_frame_type = {
+ 0: ['frame count LS nibble', 'frame LSN'],
+ 1: ['frame count MS nibble', 'frame MSN'],
+ 2: ['seconds LS nibble', 'sec LSN'],
+ 3: ['seconds MS nibble', 'sec MSN'],
+ 4: ['minutes LS nibble', 'min LSN'],
+ 5: ['minutes MS nibble', 'min MSN'],
+ 6: ['hours LS nibble', 'hrs LSN'],
+ 7: ['hours MS nibble and SMPTE type', 'hrs MSN'],
+}
+
+smpte_type = {
+ 0: '24 fps',
+ 1: '25 fps',
+ 2: '30 fps (drop-frame)',
+ 3: '30 fps (non-drop)',
+}
+
+chromatic_notes = {
+ 0: 'C-1',
+ 1: 'C#-1',
+ 2: 'D-1',
+ 3: 'D#-1',
+ 4: 'E-1',
+ 5: 'F-1',
+ 6: 'F#-1',
+ 7: 'G-1',
+ 8: 'G#-1',
+ 9: 'A-1',
+ 10: 'A#-1',
+ 11: 'B-1',
+ 12: 'C0',
+ 13: 'C#0',
+ 14: 'D0',
+ 15: 'D#0',
+ 16: 'E0',
+ 17: 'F0',
+ 18: 'F#0',
+ 19: 'G0',
+ 20: 'G#0',
+ 21: 'A0',
+ 22: 'A#0',
+ 23: 'B0',
+ 24: 'C1',
+ 25: 'C#1',
+ 26: 'D1',
+ 27: 'D#1',
+ 28: 'E1',
+ 29: 'F1',
+ 30: 'F#1',
+ 31: 'G1',
+ 32: 'G#1',
+ 33: 'A1',
+ 34: 'A#1',
+ 35: 'B1',
+ 36: 'C2',
+ 37: 'C#2',
+ 38: 'D2',
+ 39: 'D#2',
+ 40: 'E2',
+ 41: 'F2',
+ 42: 'F#2',
+ 43: 'G2',
+ 44: 'G#2',
+ 45: 'A2',
+ 46: 'A#2',
+ 47: 'B2',
+ 48: 'C3',
+ 49: 'C#3',
+ 50: 'D3',
+ 51: 'D#3',
+ 52: 'E3',
+ 53: 'F3',
+ 54: 'F#3',
+ 55: 'G3',
+ 56: 'G#3',
+ 57: 'A3',
+ 58: 'A#3',
+ 59: 'B3',
+ 60: 'C4',
+ 61: 'C#4',
+ 62: 'D4',
+ 63: 'D#4',
+ 64: 'E4',
+ 65: 'F4',
+ 66: 'F#4',
+ 67: 'G4',
+ 68: 'G#4',
+ 69: 'A4',
+ 70: 'A#4',
+ 71: 'B4',
+ 72: 'C5',
+ 73: 'C#5',
+ 74: 'D5',
+ 75: 'D#5',
+ 76: 'E5',
+ 77: 'F5',
+ 78: 'F#5',
+ 79: 'G5',
+ 80: 'G#5',
+ 81: 'A5',
+ 82: 'A#5',
+ 83: 'B5',
+ 84: 'C6',
+ 85: 'C#6',
+ 86: 'D6',
+ 87: 'D#6',
+ 88: 'E6',
+ 89: 'F6',
+ 90: 'F#6',
+ 91: 'G6',
+ 92: 'G#6',
+ 93: 'A6',
+ 94: 'A#6',
+ 95: 'B6',
+ 96: 'C7',
+ 97: 'C#7',
+ 98: 'D7',
+ 99: 'D#7',
+ 100: 'E7',
+ 101: 'F7',
+ 102: 'F#7',
+ 103: 'G7',
+ 104: 'G#7',
+ 105: 'A7',
+ 106: 'A#7',
+ 107: 'B7',
+ 108: 'C8',
+ 109: 'C#8',
+ 110: 'D8',
+ 111: 'D#8',
+ 112: 'E8',
+ 113: 'F8',
+ 114: 'F#8',
+ 115: 'G8',
+ 116: 'G#8',
+ 117: 'A8',
+ 118: 'A#8',
+ 119: 'B8',
+ 120: 'C9',
+ 121: 'C#9',
+ 122: 'D9',
+ 123: 'D#9',
+ 124: 'E9',
+ 125: 'F9',
+ 126: 'F#9',
+ 127: 'G9',
+}
+
+percussion_notes = {
+ 35: 'Acoustic Bass Drum',
+ 36: 'Bass Drum 1',
+ 37: 'Side Stick',
+ 38: 'Acoustic Snare',
+ 39: 'Hand Clap',
+ 40: 'Electric Snare',
+ 41: 'Low Floor Tom',
+ 42: 'Closed Hi Hat',
+ 43: 'High Floor Tom',
+ 44: 'Pedal Hi-Hat',
+ 45: 'Low Tom',
+ 46: 'Open Hi-Hat',
+ 47: 'Low-Mid Tom',
+ 48: 'Hi Mid Tom',
+ 49: 'Crash Cymbal 1',
+ 50: 'High Tom',
+ 51: 'Ride Cymbal 1',
+ 52: 'Chinese Cymbal',
+ 53: 'Ride Bell',
+ 54: 'Tambourine',
+ 55: 'Splash Cymbal',
+ 56: 'Cowbell',
+ 57: 'Crash Cymbal 2',
+ 58: 'Vibraslap',
+ 59: 'Ride Cymbal 2',
+ 60: 'Hi Bongo',
+ 61: 'Low Bongo',
+ 62: 'Mute Hi Conga',
+ 63: 'Open Hi Conga',
+ 64: 'Low Conga',
+ 65: 'High Timbale',
+ 66: 'Low Timbale',
+ 67: 'High Agogo',
+ 68: 'Low Agogo',
+ 69: 'Cabasa',
+ 70: 'Maracas',
+ 71: 'Short Whistle',
+ 72: 'Long Whistle',
+ 73: 'Short Guiro',
+ 74: 'Long Guiro',
+ 75: 'Claves',
+ 76: 'Hi Wood Block',
+ 77: 'Low Wood Block',
+ 78: 'Mute Cuica',
+ 79: 'Open Cuica',
+ 80: 'Mute Triangle',
+ 81: 'Open Triangle',
}
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2013-2016 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2016 Chris Dreher <chrisdreher@hotmail.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
outputs = ['midi']
annotations = (
('text-verbose', 'Human-readable text (verbose)'),
+ ('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'),
+ ('text-error', 'Human-readable Error text'),
+ )
+ annotation_rows = (
+ ('normal', 'Normal', (0, 2)),
+ ('sys-real', 'SysReal', (1,)),
)
- def __init__(self, **kwargs):
- self.cmd = []
+ def __init__(self):
self.state = 'IDLE'
+ self.status_byte = 0
+ self.explicit_status_byte = False
+ self.cmd = []
self.ss = None
self.es = None
self.ss_block = None
def putx(self, data):
self.put(self.ss_block, self.es_block, self.out_ann, data)
- def handle_channel_msg_0x80(self):
+ def get_note_name(self, channel, note):
+ if channel != 10:
+ return chromatic_notes[note]
+ else:
+ return 'assuming ' + percussion_notes.get(note, 'undefined')
+
+ def check_for_garbage_flush(self, is_flushed):
+ if is_flushed:
+ if self.explicit_status_byte:
+ self.cmd.insert(0, self.status_byte)
+ self.handle_garbage_msg(None)
+
+ def soft_clear_status_byte(self):
+ self.explicit_status_byte = False
+
+ def hard_clear_status_byte(self):
+ self.status_byte = 0
+ self.explicit_status_byte = False
+
+ def set_status_byte(self, newbyte):
+ self.status_byte = newbyte
+ self.explicit_status_byte = True
+
+ def handle_channel_msg_0x80(self, is_flushed):
# Note off: 8n kk vv
# n = channel, kk = note, vv = velocity
c = self.cmd
- if len(c) < 3:
+ if len(c) < 2:
+ self.check_for_garbage_flush(is_flushed)
return
self.es_block = self.es
- msg, chan, note, velocity = c[0] & 0xf0, (c[0] & 0x0f) + 1, c[1], c[2]
- self.putx([0, ['Channel %d: %s (note = %d, velocity = %d)' % \
- (chan, status_bytes[msg], note, velocity)]])
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ note, velocity = c[0], c[1]
+ note_name = self.get_note_name(chan, note)
+ self.putx([0, ['Channel %d: %s (note = %d \'%s\', velocity = %d)' % \
+ (chan, status_bytes[msg][0], note, note_name, velocity),
+ 'ch %d: %s %d, velocity = %d' % \
+ (chan, status_bytes[msg][1], note, velocity),
+ '%d: %s %d, vel %d' % \
+ (chan, status_bytes[msg][2], note, velocity)]])
self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
- def handle_channel_msg_0x90(self):
+ def handle_channel_msg_0x90(self, is_flushed):
# Note on: 9n kk vv
# n = channel, kk = note, vv = velocity
# If velocity == 0 that actually means 'note off', though.
c = self.cmd
- if len(c) < 3:
+ if len(c) < 2:
+ self.check_for_garbage_flush(is_flushed)
return
self.es_block = self.es
- msg, chan, note, velocity = c[0] & 0xf0, (c[0] & 0x0f) + 1, c[1], c[2]
- s = 'note off' if (velocity == 0) else status_bytes[msg]
- self.putx([0, ['Channel %d: %s (note = %d, velocity = %d)' % \
- (chan, s, note, velocity)]])
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ note, velocity = c[0], c[1]
+ s = status_bytes[0x80] if (velocity == 0) else status_bytes[msg]
+ note_name = self.get_note_name(chan, note)
+ self.putx([0, ['Channel %d: %s (note = %d \'%s\', velocity = %d)' % \
+ (chan, s[0], note, note_name, velocity),
+ 'ch %d: %s %d, velocity = %d' % \
+ (chan, s[1], note, velocity),
+ '%d: %s %d, vel %d' % \
+ (chan, s[2], note, velocity)]])
self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
- def handle_channel_msg_0xa0(self):
+ def handle_channel_msg_0xa0(self, is_flushed):
# Polyphonic key pressure / aftertouch: An kk vv
# n = channel, kk = polyphonic key pressure, vv = pressure value
- pass # TODO
+ c = self.cmd
+ if len(c) < 2:
+ self.check_for_garbage_flush(is_flushed)
+ return
+ self.es_block = self.es
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ note, pressure = c[0], c[1]
+ note_name = self.get_note_name(chan, note)
+ self.putx([0, ['Channel %d: %s of %d for note = %d \'%s\'' % \
+ (chan, status_bytes[msg][0], pressure, note, note_name),
+ 'ch %d: %s %d for note %d' % \
+ (chan, status_bytes[msg][1], pressure, note),
+ '%d: %s %d, N %d' % \
+ (chan, status_bytes[msg][2], pressure, note)]])
+ self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
def handle_controller_0x44(self):
# Legato footswitch: Bn 44 vv
# n = channel, vv = value (<= 0x3f: normal, > 0x3f: legato)
- chan, vv = (self.cmd[0] & 0x0f) + 1, self.cmd[2]
- t = 'normal' if vv <= 0x3f else 'legato'
- self.putx([0, ['Channel %d: control function \'%s\' = %s' % \
- (chan, control_functions[0x44], t)]])
+ c = self.cmd
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ vv = c[1]
+ t = ('normal', 'no') if vv <= 0x3f else ('legato', 'yes')
+ self.putx([0, ['Channel %d: %s \'%s\' = %s' % \
+ (chan, status_bytes[msg][0],
+ control_functions[0x44][0], t[0]),
+ 'ch %d: %s \'%s\' = %s' % \
+ (chan, status_bytes[msg][1],
+ control_functions[0x44][1], t[0]),
+ '%d: %s \'%s\' = %s' % \
+ (chan, status_bytes[msg][2],
+ control_functions[0x44][2], t[1])]])
def handle_controller_0x54(self):
# Portamento control (PTC): Bn 54 kk
# n = channel, kk = source note for pitch reference
- chan, kk = (self.cmd[0] & 0x0f) + 1, self.cmd[2]
- self.putx([0, ['Channel %d: control function \'%s\' (source note ' \
- '= %d)' % (chan, control_functions[0x54], kk)]])
+ c = self.cmd
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ kk = c[1]
+ kk_name = self.get_note_name(chan, kk)
+ self.putx([0, ['Channel %d: %s \'%s\' (source note = %d / %s)' % \
+ (chan, status_bytes[msg][0],
+ control_functions[0x54][0], kk, kk_name),
+ 'ch %d: %s \'%s\' (source note = %d)' % \
+ (chan, status_bytes[msg][1],
+ control_functions[0x54][1], kk),
+ '%d: %s \'%s\' (src N %d)' % \
+ (chan, status_bytes[msg][2],
+ control_functions[0x54][2], kk)]])
def handle_controller_generic(self):
c = self.cmd
- chan, fn, param = (c[0] & 0x0f) + 1, c[1], c[2]
- ctrl_fn = control_functions.get(fn, 'undefined')
- self.putx([0, ['Channel %d: control change to function \'%s\' ' \
- '(param = 0x%02x)' % (chan, ctrl_fn, param)]])
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ fn, param = c[0], c[1]
+ default_name = 'undefined'
+ ctrl_fn = control_functions.get(fn, default_name)
+ if ctrl_fn == default_name:
+ ctrl_fn = ('undefined 0x%02x' % fn, 'undef 0x%02x' % fn, '0x%02x' % fn)
+ self.putx([0, ['Channel %d: %s \'%s\' (param = 0x%02x)' % \
+ (chan, status_bytes[msg][0], ctrl_fn[0], param),
+ 'ch %d: %s \'%s\' (param = 0x%02x)' % \
+ (chan, status_bytes[msg][1], ctrl_fn[1], param),
+ '%d: %s \'%s\' is 0x%02x' % \
+ (chan, status_bytes[msg][2], ctrl_fn[2], param)]])
+
+ def handle_channel_mode(self):
+ # Channel Mode: Bn mm vv
+ # n = channel, mm = mode number (120 - 127), vv = value
+ c = self.cmd
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ mm, vv = c[0], c[1]
+ mode_fn = control_functions.get(mm, ('undefined', 'undef', 'undef'))
+ # Decode the value based on the mode number.
+ vv_string = ('', '')
+ if mm == 122: # mode = local control?
+ if vv == 0:
+ vv_string = ('off', 'off')
+ elif vv == 127: # mode = poly mode on?
+ vv_string = ('on', 'on')
+ else:
+ vv_string = ('(non-standard param value of 0x%02x)' % vv,
+ '0x%02x' % vv)
+ elif mm == 126: # mode = mono mode on?
+ if vv != 0:
+ vv_string = ('(%d channels)' % vv, '(%d ch)' % vv)
+ else:
+ vv_string = ('(channels \'basic\' through 16)',
+ '(ch \'basic\' thru 16)')
+ elif vv != 0: # All other channel mode messages expect vv == 0.
+ vv_string = ('(non-standard param value of 0x%02x)' % vv,
+ '0x%02x' % vv)
+ self.putx([0, ['Channel %d: %s \'%s\' %s' % \
+ (chan, status_bytes[msg][0], mode_fn[0], vv_string[0]),
+ 'ch %d: %s \'%s\' %s' % \
+ (chan, status_bytes[msg][1], mode_fn[1], vv_string[1]),
+ '%d: %s \'%s\' %s' % \
+ (chan, status_bytes[msg][2], mode_fn[2], vv_string[1])]])
+ self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
- def handle_channel_msg_0xb0(self):
+ def handle_channel_msg_0xb0(self, is_flushed):
# Control change (or channel mode messages): Bn cc vv
# n = channel, cc = control number (0 - 119), vv = control value
c = self.cmd
- if (len(c) >= 2) and (c[1] in range(0x78, 0x7f + 1)):
- # This is not a control change, but rather a channel mode message.
- # TODO: Handle channel mode messages.
- return
- if len(c) < 3:
+ if len(c) < 2:
+ self.check_for_garbage_flush(is_flushed)
return
self.es_block = self.es
- handle_ctrl = getattr(self, 'handle_controller_0x%02x' % c[1],
+ if c[0] in range(0x78, 0x7f + 1):
+ self.handle_channel_mode()
+ return
+ handle_ctrl = getattr(self, 'handle_controller_0x%02x' % c[0],
self.handle_controller_generic)
handle_ctrl()
self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
- def handle_channel_msg_0xc0(self):
+ def handle_channel_msg_0xc0(self, is_flushed):
# Program change: Cn pp
# n = channel, pp = program number (0 - 127)
- pass # TODO
+ c = self.cmd
+ if len(c) < 1:
+ self.check_for_garbage_flush(is_flushed)
+ return
+ self.es_block = self.es
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ pp = self.cmd[0] + 1
+ change_type = 'instrument'
+ name = ''
+ if chan != 10: # channel != percussion
+ name = gm_instruments.get(pp, 'undefined')
+ else:
+ change_type = 'drum kit'
+ name = drum_kit.get(pp, 'undefined')
+ self.putx([0, ['Channel %d: %s to %s %d (assuming %s)' % \
+ (chan, status_bytes[msg][0], change_type, pp, name),
+ 'ch %d: %s to %s %d' % \
+ (chan, status_bytes[msg][1], change_type, pp),
+ '%d: %s %d' % \
+ (chan, status_bytes[msg][2], pp)]])
+ self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
- def handle_channel_msg_0xd0(self):
+ def handle_channel_msg_0xd0(self, is_flushed):
# Channel pressure / aftertouch: Dn vv
# n = channel, vv = pressure value
- pass # TODO
+ c = self.cmd
+ if len(c) < 1:
+ self.check_for_garbage_flush(is_flushed)
+ return
+ self.es_block = self.es
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ vv = self.cmd[0]
+ self.putx([0, ['Channel %d: %s %d' % (chan, status_bytes[msg][0], vv),
+ 'ch %d: %s %d' % (chan, status_bytes[msg][1], vv),
+ '%d: %s %d' % (chan, status_bytes[msg][2], vv)]])
+ self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
- def handle_channel_msg_0xe0(self):
+ def handle_channel_msg_0xe0(self, is_flushed):
# Pitch bend change: En ll mm
# n = channel, ll = pitch bend change LSB, mm = pitch bend change MSB
- pass # TODO
+ c = self.cmd
+ if len(c) < 2:
+ self.check_for_garbage_flush(is_flushed)
+ return
+ self.es_block = self.es
+ msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+ ll, mm = self.cmd[0], self.cmd[1]
+ decimal = (mm << 7) + ll
+ self.putx([0, ['Channel %d: %s 0x%02x 0x%02x (%d)' % \
+ (chan, status_bytes[msg][0], ll, mm, decimal),
+ 'ch %d: %s 0x%02x 0x%02x (%d)' % \
+ (chan, status_bytes[msg][1], ll, mm, decimal),
+ '%d: %s (%d)' % \
+ (chan, status_bytes[msg][2], decimal)]])
+ self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
- def handle_channel_msg_generic(self):
- msg_type = self.cmd[0] & 0xf0
- self.putx([0, ['Unknown channel message type: 0x%02x' % msg_type]])
- # TODO: Handle properly.
+ def handle_channel_msg_generic(self, is_flushed):
+ # TODO: It should not be possible to hit this code.
+ # It currently can not be unit tested.
+ msg_type = self.status_byte & 0xf0
+ self.es_block = self.es
+ self.putx([2, ['Unknown channel message type: 0x%02x' % msg_type]])
+ self.cmd, self.state = [], 'IDLE'
+ self.soft_clear_status_byte()
def handle_channel_msg(self, newbyte):
- self.cmd.append(newbyte)
- msg_type = self.cmd[0] & 0xf0
+ if newbyte is not None:
+ if newbyte >= 0x80:
+ self.set_status_byte(newbyte)
+ else:
+ self.cmd.append(newbyte)
+ msg_type = self.status_byte & 0xf0
handle_msg = getattr(self, 'handle_channel_msg_0x%02x' % msg_type,
self.handle_channel_msg_generic)
- handle_msg()
+ handle_msg(newbyte is None)
def handle_sysex_msg(self, newbyte):
- # SysEx message: 1 status byte, x data bytes, EOX byte
- self.cmd.append(newbyte)
- if newbyte != 0xf7: # EOX
+ # SysEx message: 1 status byte, 1-3 manuf. bytes, x data bytes, EOX byte
+ #
+ # SysEx messages are variable length, can be terminated by EOX or
+ # by any non-SysReal status byte, and it clears self.status_byte.
+ #
+ # Note: All System message codes don't utilize self.status_byte.
+ self.hard_clear_status_byte()
+ if newbyte != 0xf7 and newbyte is not None: # EOX
+ self.cmd.append(newbyte)
+ return
+ self.es_block = self.es
+ # Note: Unlike other methods, this code pops bytes out of self.cmd
+ # to isolate the data.
+ msg = self.cmd.pop(0)
+ if len(self.cmd) < 1:
+ self.putx([2, ['%s: truncated manufacturer code (<1 bytes)' % \
+ status_bytes[msg][0],
+ '%s: truncated manufacturer (<1 bytes)' % \
+ status_bytes[msg][1],
+ '%s: trunc. manu.' % status_bytes[msg][2]]])
+ self.cmd, self.state = [], 'IDLE'
+ return
+ # Extract the manufacturer name (or SysEx realtime or non-realtime).
+ m1 = self.cmd.pop(0)
+ manu = (m1,)
+ if m1 == 0x00: # If byte == 0, then 2 more manufacturer bytes follow.
+ if len(self.cmd) < 2:
+ self.putx([2, ['%s: truncated manufacturer code (<3 bytes)' % \
+ status_bytes[msg][0],
+ '%s: truncated manufacturer (<3 bytes)' % \
+ status_bytes[msg][1],
+ '%s: trunc. manu.' % status_bytes[msg][2]]])
+ self.cmd, self.state = [], 'IDLE'
+ return
+ manu = (m1, self.cmd.pop(0), self.cmd.pop(0))
+ default_name = 'undefined'
+ manu_name = sysex_manufacturer_ids.get(manu, default_name)
+ if manu_name == default_name:
+ if len(manu) == 3:
+ manu_name = ('%s (0x%02x 0x%02x 0x%02x)' % \
+ (default_name, manu[0], manu[1], manu[2]),
+ default_name)
+ else:
+ manu_name = ('%s (0x%02x)' % (default_name, manu[0]),
+ default_name)
+ else:
+ manu_name = (manu_name, manu_name)
+ # Extract the payload, display in 1 of 2 formats
+ # TODO: Write methods to decode SysEx realtime & non-realtime payloads.
+ payload0 = ''
+ payload1 = ''
+ while len(self.cmd) > 0:
+ byte = self.cmd.pop(0)
+ payload0 += '0x%02x ' % (byte)
+ payload1 += '%02x ' % (byte)
+ if payload0 == '':
+ payload0 = '<empty>'
+ payload1 = '<>'
+ payload = (payload0, payload1)
+ self.putx([0, ['%s: for \'%s\' with payload %s' % \
+ (status_bytes[msg][0], manu_name[0], payload[0]),
+ '%s: \'%s\', payload %s' % \
+ (status_bytes[msg][1], manu_name[1], payload[1]),
+ '%s: \'%s\', payload %s' % \
+ (status_bytes[msg][2], manu_name[1], payload[1])]])
+ self.cmd, self.state = [], 'IDLE'
+
+ def handle_syscommon_midi_time_code_quarter_frame_msg(self, newbyte):
+ # MIDI time code quarter frame: F1 nd
+ # n = message type
+ # d = values
+ #
+ # Note: All System message codes don't utilize self.status_byte,
+ # and System Exclusive and System Common clear it.
+ c = self.cmd
+ if len(c) < 2:
+ if newbyte is None:
+ self.handle_garbage_msg(None)
return
+ msg = c[0]
+ nn, dd = (c[1] & 0x70) >> 4, c[1] & 0x0f
+ group = ('System Common', 'SysCom', 'SC')
self.es_block = self.es
- # TODO: Get message ID, vendor ID, message contents, etc.
- self.putx([0, ['SysEx message']])
+ if nn != 7: # If message type does not contain SMPTE type.
+ self.putx([0, ['%s: %s of %s, value 0x%01x' % \
+ (group[0], status_bytes[msg][0],
+ quarter_frame_type[nn][0], dd),
+ '%s: %s of %s, value 0x%01x' % \
+ (group[1], status_bytes[msg][1],
+ quarter_frame_type[nn][1], dd),
+ '%s: %s of %s, value 0x%01x' % \
+ (group[2], status_bytes[msg][2],
+ quarter_frame_type[nn][1], dd)]])
+ self.cmd, self.state = [], 'IDLE'
+ return
+ tt = (dd & 0x6) >> 1
+ self.putx([0, ['%s: %s of %s, value 0x%01x for %s' % \
+ (group[0], status_bytes[msg][0], \
+ quarter_frame_type[nn][0], dd, smpte_type[tt]),
+ '%s: %s of %s, value 0x%01x for %s' % \
+ (group[1], status_bytes[msg][1], \
+ quarter_frame_type[nn][1], dd, smpte_type[tt]),
+ '%s: %s of %s, value 0x%01x for %s' % \
+ (group[2], status_bytes[msg][2], \
+ quarter_frame_type[nn][1], dd, smpte_type[tt])]])
self.cmd, self.state = [], 'IDLE'
def handle_syscommon_msg(self, newbyte):
- pass # TODO
+ # System common messages
+ #
+ # There are 5 simple formats (which are directly handled here) and
+ # 1 complex one called MIDI time code quarter frame.
+ #
+ # Note: While the MIDI lists 0xf7 as a "system common" message, it
+ # is actually only used with SysEx messages so it is processed there.
+ #
+ # Note: All System message codes don't utilize self.status_byte.
+ self.hard_clear_status_byte()
+ if newbyte is not None:
+ self.cmd.append(newbyte)
+ c = self.cmd
+ msg = c[0]
+ group = ('System Common', 'SysCom', 'SC')
+ if msg == 0xf1:
+ # MIDI time code quarter frame
+ self.handle_syscommon_midi_time_code_quarter_frame_msg(newbyte)
+ return
+ elif msg == 0xf2:
+ # Song position pointer: F2 ll mm
+ # ll = LSB position, mm = MSB position
+ if len(c) < 3:
+ if newbyte is None:
+ self.handle_garbage_msg(None)
+ return
+ ll, mm = c[1], c[2]
+ decimal = (mm << 7) + ll
+ self.es_block = self.es
+ self.putx([0, ['%s: %s 0x%02x 0x%02x (%d)' % \
+ (group[0], status_bytes[msg][0], ll, mm, decimal),
+ '%s: %s 0x%02x 0x%02x (%d)' % \
+ (group[1], status_bytes[msg][1], ll, mm, decimal),
+ '%s: %s (%d)' % \
+ (group[2], status_bytes[msg][2], decimal)]])
+ elif msg == 0xf3:
+ # Song select: F3 ss
+ # ss = song selection number
+ if len(c) < 2:
+ if newbyte is None:
+ self.handle_garbage_msg(None)
+ return
+ ss = c[1]
+ self.es_block = self.es
+ self.putx([0, ['%s: %s number %d' % \
+ (group[0], status_bytes[msg][0], ss),
+ '%s: %s number %d' % \
+ (group[1], status_bytes[msg][1], ss),
+ '%s: %s # %d' % \
+ (group[2], status_bytes[msg][2], ss)]])
+ elif msg == 0xf4 or msg == 0xf5 or msg == 0xf6:
+ # Undefined 0xf4, Undefined 0xf5, and Tune Request (respectively).
+ # All are only 1 byte long with no data bytes.
+ self.es_block = self.es
+ self.putx([0, ['%s: %s' % (group[0], status_bytes[msg][0]),
+ '%s: %s' % (group[1], status_bytes[msg][1]),
+ '%s: %s' % (group[2], status_bytes[msg][2])]])
+ self.cmd, self.state = [], 'IDLE'
def handle_sysrealtime_msg(self, newbyte):
# System realtime message: 0b11111ttt (t = message type)
+ #
+ # Important: These messages are handled differently from all others
+ # because they are allowed to temporarily interrupt other messages.
+ # The interrupted messages resume after the realtime message is done.
+ # Thus, they mostly leave 'self' the way it was found.
+ #
+ # Note: All System message codes don't utilize self.status_byte.
+ old_ss_block, old_es_block = self.ss_block, self.es_block
+ self.ss_block, self.es_block = self.ss, self.es
+ group = ('System Realtime', 'SysReal', 'SR')
+ self.putx([1, ['%s: %s' % (group[0], status_bytes[newbyte][0]),
+ '%s: %s' % (group[1], status_bytes[newbyte][1]),
+ '%s: %s' % (group[2], status_bytes[newbyte][2])]])
+ self.ss_block, self.es_block = old_ss_block, old_es_block
+ # Deliberately not resetting self.cmd or self.state.
+
+ def handle_garbage_msg(self, newbyte):
+ # Handle messages that are either not handled or are corrupt.
self.es_block = self.es
- self.putx([0, ['System realtime message: %s' % status_bytes[newbyte]]])
+ if newbyte is not None:
+ self.cmd.append(newbyte)
+ return
+ payload = '<empty>'
+ max_bytes = 16 # Put a limit on the length on the hex dump.
+ for index in range(len(self.cmd)):
+ if index == max_bytes:
+ payload += ' ...'
+ break
+ if index == 0:
+ payload = '0x%02x' % self.cmd[index]
+ else:
+ payload += ' 0x%02x' % self.cmd[index]
+ self.putx([2, ['UNHANDLED DATA: %s' % payload,
+ 'UNHANDLED', '???', '?']])
self.cmd, self.state = [], 'IDLE'
+ self.hard_clear_status_byte()
+
+ def handle_state(self, state, newbyte):
+ # 'newbyte' can either be:
+ # 1. Value between 0x00-0xff, deal with the byte normally.
+ # 2. Value of 'None' which means "flush any buffered data".
+ if state == 'HANDLE CHANNEL MSG':
+ self.handle_channel_msg(newbyte)
+ elif state == 'HANDLE SYSEX MSG':
+ self.handle_sysex_msg(newbyte)
+ elif state == 'HANDLE SYSCOMMON MSG':
+ self.handle_syscommon_msg(newbyte)
+ elif state == 'HANDLE SYSREALTIME MSG':
+ self.handle_sysrealtime_msg(newbyte)
+ elif state == 'BUFFER GARBAGE MSG':
+ self.handle_garbage_msg(newbyte)
+
+ def get_next_state(self, newbyte):
+ # 'newbyte' must be a valid byte between 0x00 and 0xff.
+ #
+ # Try to determine the state based off of the 'newbyte' parameter.
+ if newbyte in range(0x80, 0xef + 1):
+ return 'HANDLE CHANNEL MSG'
+ if newbyte == 0xf0:
+ return 'HANDLE SYSEX MSG'
+ if newbyte in range(0xf1, 0xf7):
+ return'HANDLE SYSCOMMON MSG'
+ if newbyte in range(0xf8, 0xff + 1):
+ return 'HANDLE SYSREALTIME MSG'
+ # Passing 0xf7 is an error; messages don't start with 0xf7.
+ if newbyte == 0xf7:
+ return 'BUFFER GARBAGE MSG'
+ # Next, base the state off of self.status_byte.
+ if self.status_byte < 0x80:
+ return 'BUFFER GARBAGE MSG'
+ return self.get_next_state(self.status_byte)
def decode(self, ss, es, data):
ptype, rxtx, pdata = data
+ state = 'IDLE'
# For now, ignore all UART packets except the actual data packets.
if ptype != 'DATA':
return
- self.ss, self.es = ss, es
-
# We're only interested in the byte value (not individual bits).
pdata = pdata[0]
# - Most messages: 1 status byte, 1-2 data bytes.
# - Real-time system messages: always 1 byte.
# - SysEx messages: 1 status byte, n data bytes, EOX byte.
+ #
+ # Aspects of the MIDI protocol that complicate decoding:
+ # - MIDI System Realtime messages can briefly interrupt other
+ # messages already in progress.
+ # - "Running Status" allows for omitting the status byte in most
+ # scenarios if sequential messages have the same status byte.
+ # - System Exclusive (SysEx) messages can be terminated by ANY
+ # status byte (not limited to EOX byte).
# State machine.
- if self.state == 'IDLE':
- # Wait until we see a status byte (bit 7 must be set).
- if pdata < 0x80:
- return # TODO: How to handle? Ignore?
+ if pdata >= 0x80 and pdata != 0xf7:
+ state = self.get_next_state(pdata)
+ if state != 'HANDLE SYSREALTIME MSG' and self.state != 'IDLE':
+ # Flush the previous data since a new message is starting.
+ self.handle_state(self.state, None)
+ # Cache ss and es -after- flushing previous data.
+ self.ss, self.es = ss, es
# This is a status byte, remember the start sample.
- self.ss_block = ss
- if pdata in range(0x80, 0xef + 1):
- self.state = 'HANDLE CHANNEL MSG'
- elif pdata == 0xf0:
- self.state = 'HANDLE SYSEX MSG'
- elif pdata in range(0xf1, 0xf7 + 1):
- self.state = 'HANDLE SYSCOMMON MSG'
- elif pdata in range(0xf8, 0xff + 1):
- self.state = 'HANDLE SYSREALTIME MSG'
+ if state != 'HANDLE SYSREALTIME MSG':
+ self.ss_block = ss
+ elif self.state == 'IDLE' or self.state == 'BUFFER GARBAGE MSG':
+ # Deal with "running status" or that we're buffering garbage.
+ self.ss, self.es = ss, es
+ if self.state == 'IDLE':
+ self.ss_block = ss
+ state = self.get_next_state(pdata)
+ else:
+ self.ss, self.es = ss, es
+ state = self.state
# Yes, this is intentionally _not_ an 'elif' here.
- if self.state == 'HANDLE CHANNEL MSG':
- self.handle_channel_msg(pdata)
- elif self.state == 'HANDLE SYSEX MSG':
- self.handle_sysex_msg(pdata)
- elif self.state == 'HANDLE SYSCOMMON MSG':
- self.handle_syscommon_msg(pdata)
- elif self.state == 'HANDLE SYSREALTIME MSG':
- self.handle_sysrealtime_msg(pdata)
+ if state != 'HANDLE SYSREALTIME MSG':
+ self.state = state
+ if state == 'BUFFER GARBAGE MSG':
+ self.status_byte = 0
+ self.handle_state(state, pdata)
('kelvin', 'Temperature in Kelvin'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IGNORE START REPEAT'
self.data = []
name = 'Modbus'
longname = 'Modbus RTU over RS232/RS485'
desc = 'Modbus RTU protocol for industrial applications.'
- license = 'gplv2+'
+ license = 'gplv3+'
inputs = ['uart']
outputs = ['modbus']
annotations = (
'values': ('RX', 'TX')},
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.ADUSc = None # Start off with empty slave -> client ADU.
self.ADUCs = None # Start off with empty client -> slave ADU.
name = 'MRF24J40'
longname = 'Microchip MRF24J40'
desc = 'IEEE 802.15.4 2.4 GHz RF tranceiver chip.'
- license = 'gplv2'
+ license = 'gplv2+'
inputs = ['spi']
outputs = ['mrf24j40']
annotations = (
('warnings', 'Warnings', (4,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
self.miso_bytes = []
('text', 'Human-readable text'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
def start(self):
('warnings', 'Warnings', (ann_warn,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.next()
self.requirements_met = True
self.cs_was_released = False
('warnings', 'Warnings', (14,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1
self.databytecount = 0
def putrs(self, data):
self.put(self.rise, self.samplenum, self.out_ann, data)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.samplenum = 0
self.state = 'WAIT FOR FALLING EDGE'
('text', 'Human-readable text'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.ss_block = 0
self.es_block = 0
self.state = 'COMMAND'
('warnings', 'Human-readable warnings'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.cmd = ['', '']
self.ss_block = None
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Daniel Schulte <trilader@schroedingers-bit.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 can decode PS/2 device -> host communication.
+
+Host -> device communication is currently not supported.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Daniel Schulte <trilader@schroedingers-bit.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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+from collections import namedtuple
+
+class Ann:
+ BIT, START, STOP, PARITY_OK, PARITY_ERR, DATA, WORD = range(7)
+
+Bit = namedtuple('Bit', 'val ss es')
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 'ps2'
+ name = 'PS/2'
+ longname = 'PS/2'
+ desc = 'PS/2 keyboard/mouse interface.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['ps2']
+ channels = (
+ {'id': 'clk', 'name': 'Clock', 'desc': 'Clock line'},
+ {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+ )
+ annotations = (
+ ('bit', 'Bit'),
+ ('start-bit', 'Start bit'),
+ ('stop-bit', 'Stop bit'),
+ ('parity-ok', 'Parity OK bit'),
+ ('parity-err', 'Parity error bit'),
+ ('data-bit', 'Data bit'),
+ ('word', 'Word'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0,)),
+ ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
+ )
+
+ def __init__(self):
+ self.bits = []
+ self.prev_pins = None
+ self.prev_clock = None
+ self.samplenum = 0
+ self.clock_was_high = False
+ self.bitcount = 0
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def putb(self, bit, ann_idx):
+ b = self.bits[bit]
+ self.put(b.ss, b.es, self.out_ann, [ann_idx, [str(b.val)]])
+
+ def putx(self, bit, ann):
+ self.put(self.bits[bit].ss, self.bits[bit].es, self.out_ann, ann)
+
+ def handle_bits(self, datapin):
+ # Ignore non start condition bits (useful during keyboard init).
+ if self.bitcount == 0 and datapin == 1:
+ return
+
+ # Store individual bits and their start/end samplenumbers.
+ self.bits.append(Bit(datapin, self.samplenum, self.samplenum))
+
+ # Fix up end sample numbers of the bits.
+ if self.bitcount > 0:
+ b = self.bits[self.bitcount - 1]
+ self.bits[self.bitcount - 1] = Bit(b.val, b.ss, self.samplenum)
+ if self.bitcount == 11:
+ self.bitwidth = self.bits[1].es - self.bits[2].es
+ b = self.bits[-1]
+ self.bits[-1] = Bit(b.val, b.ss, b.es + self.bitwidth)
+
+ # Find all 11 bits. Start + 8 data + odd parity + stop.
+ if self.bitcount < 11:
+ self.bitcount += 1
+ return
+
+ # Extract data word.
+ word = 0
+ for i in range(8):
+ word |= (self.bits[i + 1].val << i)
+
+ # Calculate parity.
+ parity_ok = (bin(word).count('1') + self.bits[9].val) % 2 == 1
+
+ # Emit annotations.
+ for i in range(11):
+ self.putb(i, Ann.BIT)
+ self.putx(0, [Ann.START, ['Start bit', 'Start', 'S']])
+ self.put(self.bits[1].ss, self.bits[8].es, self.out_ann, [Ann.WORD,
+ ['Data: %02x' % word, 'D: %02x' % word, '%02x' % word]])
+ if parity_ok:
+ self.putx(9, [Ann.PARITY_OK, ['Parity OK', 'Par OK', 'P']])
+ else:
+ self.putx(9, [Ann.PARITY_ERR, ['Parity error', 'Par err', 'PE']])
+ self.putx(10, [Ann.STOP, ['Stop bit', 'Stop', 'St', 'T']])
+
+ self.bits, self.bitcount = [], 0
+
+ def find_clk_edge(self, clock_pin, data_pin):
+ # Ignore sample if the clock pin hasn't changed.
+ if clock_pin == self.prev_clock:
+ return
+ self.prev_clock = clock_pin
+
+ # Sample on falling clock edge.
+ if clock_pin == 1:
+ return
+
+ # Found the correct clock edge, now get the bits.
+ self.handle_bits(data_pin)
+
+ def decode(self, ss, es, data):
+ for (self.samplenum, pins) in data:
+ clock_pin, data_pin = pins[0], pins[1]
+
+ # Ignore identical samples.
+ if self.prev_pins == pins:
+ continue
+ self.prev_pins = pins
+
+ if clock_pin == 0 and not self.clock_was_high:
+ continue
+ self.clock_was_high = True
+
+ self.find_clk_edge(clock_pin, data_pin)
('raw', 'RAW file'),
)
- def __init__(self, **kwargs):
- self.ss = self.es = None
+ def __init__(self):
+ self.ss_block = self.es_block = None
self.first_transition = True
self.first_samplenum = None
self.start_samplenum = None
meta=(float, 'Average', 'PWM base (cycle) frequency'))
def putx(self, data):
- self.put(self.ss, self.es, self.out_ann, data)
+ self.put(self.ss_block, self.es_block, self.out_ann, data)
def putp(self, period_t):
# Adjust granularity.
else:
period_s = '%.1f ms' % (period_t * 1e3)
- self.put(self.ss, self.es, self.out_ann, [1, [period_s]])
+ self.put(self.ss_block, self.es_block, self.out_ann, [1, [period_s]])
def putb(self, data):
self.put(self.num_cycles, self.num_cycles, self.out_binary, data)
ratio = float(duty / period)
# This interval starts at this edge.
- self.ss = self.start_samplenum
+ self.ss_block = self.start_samplenum
# Store the new rising edge position and the ending
# edge interval.
- self.start_samplenum = self.es = self.samplenum
+ self.start_samplenum = self.es_block = self.samplenum
# Report the duty cycle in percent.
percent = float(ratio * 100)
# Update and report the new duty cycle average.
self.num_cycles += 1
self.average += percent
- self.put(self.first_samplenum, self.es, self.out_average,
+ self.put(self.first_samplenum, self.es_block, self.out_average,
float(self.average / self.num_cycles))
else:
# Falling edge
- self.end_samplenum = self.ss = self.samplenum
+ self.end_samplenum = self.ss_block = self.samplenum
self.oldpin = pins[0]
('packets', 'Packets', (5, 6, 7)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.reset_variables()
('interpretation', 'Interpretation', (5,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.mosi_bytes, self.miso_bytes = [], []
self.mosi_bits, self.miso_bits = [], []
self.row_pos = [0, 0, 0]
name = 'RGB LED (SPI)'
longname = 'RGB LED string decoder (SPI)'
desc = 'RGB LED string protocol (RGB values clocked over SPI).'
- license = 'gplv2'
+ license = 'gplv2+'
inputs = ['spi']
outputs = ['rgb_led_spi']
annotations = (
('rgb', 'RGB values'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.ss_cmd, self.es_cmd = 0, 0
self.mosi_bytes = []
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Vladimir Ermakov <vooon341@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 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+WS281x RGB LED protocol decoder.
+
+Details:
+https://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Vladimir Ermakov <vooon341@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 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+from functools import reduce
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 'rgb_led_ws281x'
+ name = 'RGB LED (WS281x)'
+ longname = 'RGB LED string decoder (WS281x)'
+ desc = 'RGB LED string protocol (WS281x).'
+ license = 'gplv3+'
+ inputs = ['logic']
+ outputs = ['rgb_led_ws281x']
+ channels = (
+ {'id': 'din', 'name': 'DIN', 'desc': 'DIN data line'},
+ )
+ annotations = (
+ ('bit', 'Bit'),
+ ('reset', 'RESET'),
+ ('rgb', 'RGB'),
+ )
+ annotation_rows = (
+ ('bit', 'Bits', (0, 1)),
+ ('rgb', 'RGB', (2,)),
+ )
+
+ def __init__(self):
+ self.samplerate = None
+ self.oldpin = None
+ self.ss_packet = None
+ self.ss = None
+ self.es = None
+ self.bits = []
+ self.inreset = False
+
+ 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 handle_bits(self, samplenum):
+ if len(self.bits) == 24:
+ grb = reduce(lambda a, b: (a << 1) | b, self.bits)
+ rgb = (grb & 0xff0000) >> 8 | (grb & 0x00ff00) << 8 | (grb & 0x0000ff)
+ self.put(self.ss_packet, samplenum, self.out_ann,
+ [2, ['#%06x' % rgb]])
+ self.bits = []
+ self.ss_packet = None
+
+ def decode(self, ss, es, data):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+
+ for (samplenum, (pin, )) in data:
+ if self.oldpin is None:
+ self.oldpin = pin
+ continue
+
+ # Check RESET condition (manufacturer recommends 50 usec minimal,
+ # but real minimum is ~10 usec).
+ if not self.inreset and not pin and self.es is not None and \
+ (samplenum - self.es) / self.samplerate > 50e-6:
+
+ # Decode last bit value.
+ tH = (self.es - self.ss) / self.samplerate
+ bit_ = True if tH >= 625e-9 else False
+
+ self.bits.append(bit_)
+ self.handle_bits(self.es)
+
+ self.put(self.ss, self.es, self.out_ann, [0, ['%d' % bit_]])
+ self.put(self.es, samplenum, self.out_ann,
+ [1, ['RESET', 'RST', 'R']])
+
+ self.inreset = True
+ self.bits = []
+ self.ss_packet = None
+ self.ss = None
+
+ if not self.oldpin and pin:
+ # Rising edge.
+ if self.ss and self.es:
+ period = samplenum - self.ss
+ duty = self.es - self.ss
+ # Ideal duty for T0H: 33%, T1H: 66%.
+ bit_ = (duty / period) > 0.5
+
+ self.put(self.ss, samplenum, self.out_ann,
+ [0, ['%d' % bit_]])
+
+ self.bits.append(bit_)
+ self.handle_bits(samplenum)
+
+ if self.ss_packet is None:
+ self.ss_packet = samplenum
+
+ self.ss = samplenum
+
+ elif self.oldpin and not pin:
+ # Falling edge.
+ self.inreset = False
+ self.es = samplenum
+
+ self.oldpin = pin
##
import sigrokdecode as srd
-
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
- return (b & 0x0f) + ((b >> 4) * 10)
+from common.srdhelper import bcd2int
def reg_list():
l = []
('date-time', 'Date/time', (9, 10)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.hours = -1
self.minutes = -1
##
-## This file is part of the sigrok project.
+## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
##
##
-## This file is part of the sigrok project.
+## This file is part of the libsigrokdecode project.
##
## Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
##
##
import sigrokdecode as srd
-from .lists import *
+from common.sdcard import (cmd_names, acmd_names, accepted_voltages, card_status, sd_status)
class Decoder(srd.Decoder):
api_version = 2
('cmd', 'Commands', tuple(range(128))),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'GET COMMAND TOKEN'
self.token = []
self.oldpins = None
##
import sigrokdecode as srd
-
-# Normal commands (CMD)
-cmd_names = {
- 0: 'GO_IDLE_STATE',
- 1: 'SEND_OP_COND',
- 6: 'SWITCH_FUNC',
- 8: 'SEND_IF_COND',
- 9: 'SEND_CSD',
- 10: 'SEND_CID',
- 12: 'STOP_TRANSMISSION',
- 13: 'SEND_STATUS',
- 16: 'SET_BLOCKLEN',
- 17: 'READ_SINGLE_BLOCK',
- 18: 'READ_MULTIPLE_BLOCK',
- 24: 'WRITE_BLOCK',
- 25: 'WRITE_MULTIPLE_BLOCK',
- 27: 'PROGRAM_CSD',
- 28: 'SET_WRITE_PROT',
- 29: 'CLR_WRITE_PROT',
- 30: 'SEND_WRITE_PROT',
- 32: 'ERASE_WR_BLK_START_ADDR',
- 33: 'ERASE_WR_BLK_END_ADDR',
- 38: 'ERASE',
- 42: 'LOCK_UNLOCK',
- 55: 'APP_CMD',
- 56: 'GEN_CMD',
- 58: 'READ_OCR',
- 59: 'CRC_ON_OFF',
- # CMD60-63: Reserved for manufacturer
-}
-
-# Application-specific commands (ACMD)
-acmd_names = {
- 13: 'SD_STATUS',
- 18: 'Reserved for SD security applications',
- 22: 'SEND_NUM_WR_BLOCKS',
- 23: 'SET_WR_BLK_ERASE_COUNT',
- 25: 'Reserved for SD security applications',
- 26: 'Reserved for SD security applications',
- 38: 'Reserved for SD security applications',
- 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',
- 51: 'SEND_SCR',
-}
+from common.sdcard import (cmd_names, acmd_names)
class Decoder(srd.Decoder):
api_version = 2
('cmd-reply', 'Commands/replies', tuple(range(134))),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.ss, self.es = 0, 0
self.ss_bit, self.es_bit = 0, 0
def cmd_name(self, cmd):
c = acmd_names if self.is_acmd else cmd_names
- return c.get(cmd, 'Unknown')
+ s = c.get(cmd, 'Unknown')
+ # SD mode names for CMD32/33: ERASE_WR_BLK_{START,END}.
+ # SPI mode names for CMD32/33: ERASE_WR_BLK_{START,END}_ADDR.
+ if cmd in (32, 33):
+ s += '_ADDR'
+ return s
def handle_command_token(self, mosi, miso):
# Command tokens (6 bytes) are sent (MSB-first) by the host.
def puty(self, data):
self.put(self.ss_edge, self.samplenum, self.out_ann, data)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'GET FIRST PULSE WIDTH'
self.olddata = None
self.ss_edge = None
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 putw(self, data):
self.put(self.ss_block, self.samplenum, self.out_ann, data)
if self.have_miso:
ss, es = self.misobits[-1][1], self.misobits[0][2]
- self.put(ss, es, self.out_binary, [0, bytes([so])])
+ 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]
- self.put(ss, es, self.out_binary, [1, bytes([si])])
+ 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])
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
-# Dict which maps command IDs to their names and descriptions.
-cmds = {
- 0x06: ('WREN', 'Write enable'),
- 0x04: ('WRDI', 'Write disable'),
- 0x9f: ('RDID', 'Read identification'),
- 0x05: ('RDSR', 'Read status register'),
- 0x01: ('WRSR', 'Write status register'),
- 0x03: ('READ', 'Read data'),
- 0x0b: ('FAST/READ', 'Fast read data'),
- 0xbb: ('2READ', '2x I/O read'),
- 0x20: ('SE', 'Sector erase'),
- 0xd8: ('BE', 'Block erase'),
- 0x60: ('CE', 'Chip erase'),
- 0xc7: ('CE2', 'Chip erase'), # Alternative command ID
- 0x02: ('PP', 'Page program'),
- 0xad: ('CP', 'Continuously program mode'),
- 0xb9: ('DP', 'Deep power down'),
- 0xab: ('RDP/RES', 'Release from deep powerdown / Read electronic ID'),
- 0x90: ('REMS', 'Read electronic manufacturer & device ID'),
- 0xef: ('REMS2', 'Read ID for 2x I/O mode'),
- 0xb1: ('ENSO', 'Enter secured OTP'),
- 0xc1: ('EXSO', 'Exit secured OTP'),
- 0x2b: ('RDSCUR', 'Read security register'),
- 0x2f: ('WRSCUR', 'Write security register'),
- 0x70: ('ESRY', 'Enable SO to output RY/BY#'),
- 0x80: ('DSRY', 'Disable SO to output RY/BY#'),
-}
+from collections import OrderedDict
+
+# OrderedDict which maps command IDs to their names and descriptions.
+# Please keep this sorted by command ID.
+# Don't forget to update 'Ann' in pd.py if you add/remove items here.
+cmds = OrderedDict([
+ (0x01, ('WRSR', 'Write status register')),
+ (0x02, ('PP', 'Page program')),
+ (0x03, ('READ', 'Read data')),
+ (0x04, ('WRDI', 'Write disable')),
+ (0x05, ('RDSR', 'Read status register')),
+ (0x06, ('WREN', 'Write enable')),
+ (0x0b, ('FAST/READ', 'Fast read data')),
+ (0x20, ('SE', 'Sector erase')),
+ (0x2b, ('RDSCUR', 'Read security register')),
+ (0x2f, ('WRSCUR', 'Write security register')),
+ (0x35, ('RDSR2', 'Read status register 2')),
+ (0x60, ('CE', 'Chip erase')),
+ (0x70, ('ESRY', 'Enable SO to output RY/BY#')),
+ (0x80, ('DSRY', 'Disable SO to output RY/BY#')),
+ (0x90, ('REMS', 'Read electronic manufacturer & device ID')),
+ (0x9f, ('RDID', 'Read identification')),
+ (0xab, ('RDP/RES', 'Release from deep powerdown / Read electronic ID')),
+ (0xad, ('CP', 'Continuously program mode')),
+ (0xb1, ('ENSO', 'Enter secured OTP')),
+ (0xb9, ('DP', 'Deep power down')),
+ (0xbb, ('2READ', '2x I/O read')), # a.k.a. "Fast read dual I/O".
+ (0xc1, ('EXSO', 'Exit secured OTP')),
+ (0xc7, ('CE2', 'Chip erase')), # Alternative command ID
+ (0xd8, ('BE', 'Block erase')),
+ (0xef, ('REMS2', 'Read ID for 2x I/O mode')),
+])
device_name = {
- 0x14: 'MX25L1605D',
- 0x15: 'MX25L3205D',
- 0x16: 'MX25L6405D',
+ 'fidelix': {
+ 0x15: 'FM25Q32',
+ },
+ 'macronix': {
+ 0x14: 'MX25L1605D',
+ 0x15: 'MX25L3205D',
+ 0x16: 'MX25L6405D',
+ },
}
chips = {
+ # FIDELIX
+ 'fidelix_fm25q32': {
+ 'vendor': 'FIDELIX',
+ 'model': 'FM25Q32',
+ 'res_id': 0x15,
+ 'rems_id': 0xa115,
+ 'rems2_id': 0xa115,
+ 'rdid_id': 0xa14016,
+ 'page_size': 256,
+ 'sector_size': 4 * 1024,
+ 'block_size': 64 * 1024,
+ },
# Macronix
'macronix_mx25l1605d': {
'vendor': 'Macronix',
##
## This file is part of the libsigrokdecode project.
##
-## Copyright (C) 2011-2015 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2011-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
import sigrokdecode as srd
from .lists import *
+L = len(cmds)
+
+# Don't forget to keep this in sync with 'cmds' is lists.py.
+class Ann:
+ WRSR, PP, READ, WRDI, RDSR, WREN, FAST_READ, SE, RDSCUR, WRSCUR, \
+ RDSR2, CE, ESRY, DSRY, REMS, RDID, RDP_RES, CP, ENSO, DP, READ2X, \
+ EXSO, CE2, BE, REMS2, \
+ BIT, FIELD, WARN = range(L + 3)
+
def cmd_annotation_classes():
return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
+def decode_dual_bytes(sio0, sio1):
+ # Given a byte in SIO0 (MOSI) of even bits and a byte in
+ # SIO1 (MISO) of odd bits, return a tuple of two bytes.
+ def combine_byte(even, odd):
+ result = 0
+ for bit in range(4):
+ if even & (1 << bit):
+ result |= 1 << (bit*2)
+ if odd & (1 << bit):
+ result |= 1 << ((bit*2) + 1)
+ return result
+ return (combine_byte(sio0 >> 4, sio1 >> 4), combine_byte(sio0, sio1))
+
def decode_status_reg(data):
# TODO: Additional per-bit(s) self.put() calls with correct start/end.
inputs = ['spi']
outputs = ['spiflash']
annotations = cmd_annotation_classes() + (
- ('bits', 'Bits'),
- ('bits2', 'Bits2'),
- ('warnings', 'Warnings'),
+ ('bit', 'Bit'),
+ ('field', 'Field'),
+ ('warning', 'Warning'),
)
annotation_rows = (
- ('bits', 'Bits', (24, 25)),
- ('commands', 'Commands', tuple(range(23 + 1))),
- ('warnings', 'Warnings', (26,)),
+ ('bits', 'Bits', (L + 0,)),
+ ('fields', 'Fields', (L + 1,)),
+ ('commands', 'Commands', tuple(range(len(cmds)))),
+ ('warnings', 'Warnings', (L + 2,)),
)
options = (
{'id': 'chip', 'desc': 'Chip', 'default': tuple(chips.keys())[0],
'values': tuple(chips.keys())},
+ {'id': 'format', 'desc': 'Data format', 'default': 'hex',
+ 'values': ('hex', 'ascii')},
)
- def __init__(self, **kwargs):
+ def __init__(self):
+ self.device_id = -1
+ self.on_end_transaction = None
+ self.end_current_transaction()
+
+ # 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 end_current_transaction(self):
+ if self.on_end_transaction is not None: # Callback for CS# transition.
+ self.on_end_transaction()
+ self.on_end_transaction = None
self.state = None
self.cmdstate = 1
self.addr = 0
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.chip = chips[self.options['chip']]
+ self.vendor = self.options['chip'].split('_')[0]
def putx(self, data):
# Simplification, most annotations span exactly one SPI byte/packet.
self.put(self.ss, self.es, self.out_ann, data)
- def putb(self, data):
- self.put(self.block_ss, self.block_es, self.out_ann, data)
+ def 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 device(self):
+ return device_name[self.vendor].get(self.device_id, 'Unknown')
+
+ def vendor_device(self):
+ return '%s %s' % (self.chip['vendor'], self.device())
+
+ 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 cmd_vendor_dev_list(self):
+ c, d = cmds[self.state], 'Device = %s' % self.vendor_device()
+ return ['%s (%s): %s' % (c[1], c[0], d), '%s: %s' % (c[1], d),
+ '%s: %s' % (c[0], d), d, self.vendor_device()]
+
+ def emit_cmd_byte(self):
+ self.ss_cmd = self.ss
+ self.putx([Ann.FIELD, self.cmd_ann_list()])
+ self.addr = 0
+
+ def emit_addr_bytes(self, mosi):
+ self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+ b = ((3 - (self.cmdstate - 2)) * 8) - 1
+ self.putx([Ann.BIT,
+ ['Address bits %d..%d: 0x%02x' % (b, b - 7, mosi),
+ 'Addr bits %d..%d: 0x%02x' % (b, b - 7, mosi),
+ 'Addr bits %d..%d' % (b, b - 7), 'A%d..A%d' % (b, b - 7)]])
+ if self.cmdstate == 2:
+ self.ss_field = self.ss
+ if self.cmdstate == 4:
+ self.es_field = self.es
+ self.putf([Ann.FIELD, ['Address: 0x%06x' % self.addr,
+ 'Addr: 0x%06x' % self.addr, '0x%06x' % self.addr]])
def handle_wren(self, mosi, miso):
- self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+ self.putx([Ann.WREN, self.cmd_ann_list()])
self.state = None
def handle_wrdi(self, mosi, miso):
pass # TODO
- # TODO: Check/display device ID / name
def handle_rdid(self, mosi, miso):
if self.cmdstate == 1:
# Byte 1: Master sends command ID.
- self.ss_block = self.ss
- self.putx([2, ['Command: %s' % cmds[self.state][1]]])
+ self.emit_cmd_byte()
elif self.cmdstate == 2:
# Byte 2: Slave sends the JEDEC manufacturer ID.
- self.putx([2, ['Manufacturer ID: 0x%02x' % miso]])
+ self.putx([Ann.FIELD, ['Manufacturer ID: 0x%02x' % miso]])
elif self.cmdstate == 3:
- # Byte 3: Slave sends the memory type (0x20 for this chip).
- self.putx([2, ['Memory type: 0x%02x' % miso]])
+ # Byte 3: Slave sends the memory type.
+ self.putx([Ann.FIELD, ['Memory type: 0x%02x' % miso]])
elif self.cmdstate == 4:
# Byte 4: Slave sends the device ID.
self.device_id = miso
- self.putx([2, ['Device ID: 0x%02x' % miso]])
+ self.putx([Ann.FIELD, ['Device ID: 0x%02x' % miso]])
if self.cmdstate == 4:
- # TODO: Check self.device_id is valid & exists in device_names.
- # TODO: Same device ID? Check!
- d = 'Device: Macronix %s' % device_name[self.device_id]
- self.put(self.ss_block, self.es, self.out_ann, [0, [d]])
+ self.es_cmd = self.es
+ self.putc([Ann.RDID, self.cmd_vendor_dev_list()])
self.state = None
else:
self.cmdstate += 1
# When done, the master de-asserts CS# again.
if self.cmdstate == 1:
# Byte 1: Master sends command ID.
- self.putx([3, ['Command: %s' % cmds[self.state][1]]])
+ self.emit_cmd_byte()
elif self.cmdstate >= 2:
# Bytes 2-x: Slave sends status register as long as master clocks.
- if self.cmdstate <= 3: # TODO: While CS# asserted.
- self.putx([24, ['Status register: 0x%02x' % miso]])
- self.putx([25, [decode_status_reg(miso)]])
-
- if self.cmdstate == 3: # TODO: If CS# got de-asserted.
- self.state = None
- return
+ self.es_cmd = self.es
+ self.putx([Ann.BIT, [decode_status_reg(miso)]])
+ self.putx([Ann.FIELD, ['Status register']])
+ self.putc([Ann.RDSR, self.cmd_ann_list()])
+ self.cmdstate += 1
+ def handle_rdsr2(self, mosi, miso):
+ # Read status register 2: Master asserts CS#, sends RDSR2 command,
+ # reads status register 2 byte. If CS# is kept asserted, the status
+ # register 2 can be read continuously / multiple times in a row.
+ # When done, the master de-asserts CS# again.
+ if self.cmdstate == 1:
+ # Byte 1: Master sends command ID.
+ self.emit_cmd_byte()
+ elif self.cmdstate >= 2:
+ # Bytes 2-x: Slave sends status register 2 as long as master clocks.
+ self.es_cmd = self.es
+ # TODO: Decode status register 2 correctly.
+ self.putx([Ann.BIT, [decode_status_reg(miso)]])
+ self.putx([Ann.FIELD, ['Status register 2']])
+ self.putc([Ann.RDSR2, self.cmd_ann_list()])
self.cmdstate += 1
def handle_wrsr(self, mosi, miso):
- pass # TODO
+ # Write status register: Master asserts CS#, sends WRSR command,
+ # writes 1 or 2 status register byte(s).
+ # When done, the master de-asserts CS# again. If this doesn't happen
+ # the WRSR command will not be executed.
+ if self.cmdstate == 1:
+ # Byte 1: Master sends command ID.
+ self.emit_cmd_byte()
+ elif self.cmdstate == 2:
+ # Byte 2: Master sends status register 1.
+ self.putx([Ann.BIT, [decode_status_reg(miso)]])
+ self.putx([Ann.FIELD, ['Status register 1']])
+ elif self.cmdstate == 3:
+ # Byte 3: Master sends status register 2.
+ # TODO: Decode status register 2 correctly.
+ self.putx([Ann.BIT, [decode_status_reg(miso)]])
+ self.putx([Ann.FIELD, ['Status register 2']])
+ self.es_cmd = self.es
+ self.putc([Ann.WRSR, self.cmd_ann_list()])
+ self.cmdstate += 1
def handle_read(self, mosi, miso):
# Read data bytes: Master asserts CS#, sends READ command, sends
# 3-byte address, reads >= 1 data bytes, de-asserts CS#.
if self.cmdstate == 1:
# Byte 1: Master sends command ID.
- self.putx([5, ['Command: %s' % cmds[self.state][1]]])
+ self.emit_cmd_byte()
elif self.cmdstate in (2, 3, 4):
# Bytes 2/3/4: Master sends read address (24bits, MSB-first).
- self.addr |= (mosi << ((4 - self.cmdstate) * 8))
- # self.putx([0, ['Read address, byte %d: 0x%02x' % \
- # (4 - self.cmdstate, mosi)]])
- if self.cmdstate == 4:
- self.putx([24, ['Read address: 0x%06x' % self.addr]])
- self.addr = 0
+ self.emit_addr_bytes(mosi)
elif self.cmdstate >= 5:
# Bytes 5-x: Master reads data bytes (until CS# de-asserted).
- # TODO: For now we hardcode 256 bytes per READ command.
- if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
- self.data.append(miso)
- # self.putx([0, ['New read byte: 0x%02x' % miso]])
-
- if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
- # s = ', '.join(map(hex, self.data))
- s = ''.join(map(chr, self.data))
- self.putx([24, ['Read data']])
- self.putx([25, ['Read data: %s' % s]])
- self.data = []
- self.state = None
- return
-
+ self.es_field = self.es # Will be overwritten for each byte.
+ if self.cmdstate == 5:
+ self.ss_field = self.ss
+ self.on_end_transaction = lambda: self.output_data_block('Data', Ann.READ)
+ self.data.append(miso)
self.cmdstate += 1
def handle_fast_read(self, mosi, miso):
# 3-byte address + 1 dummy byte, reads >= 1 data bytes, de-asserts CS#.
if self.cmdstate == 1:
# Byte 1: Master sends command ID.
- self.putx([5, ['Command: %s' % cmds[self.state][1]]])
+ self.emit_cmd_byte()
elif self.cmdstate in (2, 3, 4):
# Bytes 2/3/4: Master sends read address (24bits, MSB-first).
- self.putx([24, ['AD%d: 0x%02x' % (self.cmdstate - 1, mosi)]])
- if self.cmdstate == 2:
- self.block_ss = self.ss
- self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+ self.emit_addr_bytes(mosi)
elif self.cmdstate == 5:
- self.putx([24, ['Dummy byte: 0x%02x' % mosi]])
- self.block_es = self.es
- self.putb([5, ['Read address: 0x%06x' % self.addr]])
- self.addr = 0
+ self.putx([Ann.BIT, ['Dummy byte: 0x%02x' % mosi]])
elif self.cmdstate >= 6:
# Bytes 6-x: Master reads data bytes (until CS# de-asserted).
- # TODO: For now we hardcode 32 bytes per FAST READ command.
+ self.es_field = self.es # Will be overwritten for each byte.
if self.cmdstate == 6:
- self.block_ss = self.ss
- if self.cmdstate <= 32 + 5: # TODO: While CS# asserted.
- self.data.append(miso)
- if self.cmdstate == 32 + 5: # TODO: If CS# got de-asserted.
- self.block_es = self.es
- s = ' '.join([hex(b)[2:] for b in self.data])
- self.putb([25, ['Read data: %s' % s]])
- self.data = []
- self.state = None
- return
-
+ self.ss_field = self.ss
+ self.on_end_transaction = lambda: self.output_data_block('Data', Ann.FAST_READ)
+ self.data.append(miso)
self.cmdstate += 1
def handle_2read(self, mosi, miso):
- pass # TODO
+ # 2x I/O read (fast read dual I/O): Master asserts CS#, sends 2READ
+ # command, sends 3-byte address + 1 dummy byte, reads >= 1 data bytes,
+ # de-asserts CS#. All data after the command is sent via two I/O pins.
+ # MOSI = SIO0 = even bits, MISO = SIO1 = odd bits.
+ if self.cmdstate != 1:
+ b1, b2 = decode_dual_bytes(mosi, miso)
+ if self.cmdstate == 1:
+ # Byte 1: Master sends command ID.
+ self.emit_cmd_byte()
+ elif self.cmdstate == 2:
+ # Bytes 2/3(/4): Master sends read address (24bits, MSB-first).
+ # Handle bytes 2 and 3 here.
+ self.emit_addr_bytes(b1)
+ self.cmdstate = 3
+ self.emit_addr_bytes(b2)
+ elif self.cmdstate == 4:
+ # Byte 5: Dummy byte. Also handle byte 4 (address LSB) here.
+ self.emit_addr_bytes(b1)
+ self.cmdstate = 5
+ self.putx([Ann.BIT, ['Dummy byte: 0x%02x' % b2]])
+ elif self.cmdstate >= 6:
+ # Bytes 6-x: Master reads data bytes (until CS# de-asserted).
+ self.es_field = self.es # Will be overwritten for each byte.
+ if self.cmdstate == 6:
+ self.ss_field = self.ss
+ self.on_end_transaction = lambda: self.output_data_block('Data', Ann.READ2X)
+ self.data.append(b1)
+ self.data.append(b2)
+ self.cmdstate += 1
# TODO: Warn/abort if we don't see the necessary amount of bytes.
# TODO: Warn if WREN was not seen before.
def handle_se(self, mosi, miso):
if self.cmdstate == 1:
# Byte 1: Master sends command ID.
- self.addr = 0
- self.ss_block = self.ss
- self.putx([8, ['Command: %s' % cmds[self.state][1]]])
+ self.emit_cmd_byte()
elif self.cmdstate in (2, 3, 4):
# Bytes 2/3/4: Master sends sector address (24bits, MSB-first).
- self.addr |= (mosi << ((4 - self.cmdstate) * 8))
- # self.putx([0, ['Sector address, byte %d: 0x%02x' % \
- # (4 - self.cmdstate, mosi)]])
+ self.emit_addr_bytes(mosi)
if self.cmdstate == 4:
+ self.es_cmd = self.es
d = 'Erase sector %d (0x%06x)' % (self.addr, self.addr)
- self.put(self.ss_block, self.es, self.out_ann, [24, [d]])
+ self.putc([Ann.SE, [d]])
# TODO: Max. size depends on chip, check that too if possible.
if self.addr % 4096 != 0:
# Sector addresses must be 4K-aligned (same for all 3 chips).
- d = 'Warning: Invalid sector address!'
- self.put(self.ss_block, self.es, self.out_ann, [101, [d]])
+ self.putc([Ann.WARN, ['Warning: Invalid sector address!']])
self.state = None
else:
self.cmdstate += 1
# page address, sends >= 1 data bytes, de-asserts CS#.
if self.cmdstate == 1:
# Byte 1: Master sends command ID.
- self.putx([12, ['Command: %s' % cmds[self.state][1]]])
+ self.emit_cmd_byte()
elif self.cmdstate in (2, 3, 4):
# Bytes 2/3/4: Master sends page address (24bits, MSB-first).
- self.addr |= (mosi << ((4 - self.cmdstate) * 8))
- # self.putx([0, ['Page address, byte %d: 0x%02x' % \
- # (4 - self.cmdstate, mosi)]])
- if self.cmdstate == 4:
- self.putx([24, ['Page address: 0x%06x' % self.addr]])
- self.addr = 0
+ self.emit_addr_bytes(mosi)
elif self.cmdstate >= 5:
# Bytes 5-x: Master sends data bytes (until CS# de-asserted).
- # TODO: For now we hardcode 256 bytes per page / PP command.
- if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
- self.data.append(mosi)
- # self.putx([0, ['New data byte: 0x%02x' % mosi]])
-
- if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
- # s = ', '.join(map(hex, self.data))
- s = ''.join(map(chr, self.data))
- self.putx([24, ['Page data']])
- self.putx([25, ['Page data: %s' % s]])
- self.data = []
- self.state = None
- return
-
+ self.es_field = self.es # Will be overwritten for each byte.
+ if self.cmdstate == 5:
+ self.ss_field = self.ss
+ self.on_end_transaction = lambda: self.output_data_block('Data', Ann.PP)
+ self.data.append(mosi)
self.cmdstate += 1
def handle_cp(self, mosi, miso):
pass # TODO
def handle_rdp_res(self, mosi, miso):
- pass # TODO
+ if self.cmdstate == 1:
+ # Byte 1: Master sends command ID.
+ self.emit_cmd_byte()
+ elif self.cmdstate in (2, 3, 4):
+ # Bytes 2/3/4: Master sends three dummy bytes.
+ self.putx([Ann.FIELD, ['Dummy byte: %02x' % mosi]])
+ elif self.cmdstate == 5:
+ # Byte 5: Slave sends device ID.
+ self.es_cmd = self.es
+ self.device_id = miso
+ self.putx([Ann.FIELD, ['Device ID: %s' % self.device()]])
+ d = 'Device = %s' % self.vendor_device()
+ self.putc([Ann.RDP_RES, self.cmd_vendor_dev_list()])
+ self.state = None
+ self.cmdstate += 1
def handle_rems(self, mosi, miso):
if self.cmdstate == 1:
# Byte 1: Master sends command ID.
- self.ss_block = self.ss
- self.putx([16, ['Command: %s' % cmds[self.state][1]]])
+ self.emit_cmd_byte()
elif self.cmdstate in (2, 3):
# Bytes 2/3: Master sends two dummy bytes.
- # TODO: Check dummy bytes? Check reply from device?
- self.putx([24, ['Dummy byte: %s' % mosi]])
+ self.putx([Ann.FIELD, ['Dummy byte: 0x%02x' % mosi]])
elif self.cmdstate == 4:
# Byte 4: Master sends 0x00 or 0x01.
# 0x00: Master wants manufacturer ID as first reply byte.
# 0x01: Master wants device ID as first reply byte.
self.manufacturer_id_first = True if (mosi == 0x00) else False
d = 'manufacturer' if (mosi == 0x00) else 'device'
- self.putx([24, ['Master wants %s ID first' % d]])
+ self.putx([Ann.FIELD, ['Master wants %s ID first' % d]])
elif self.cmdstate == 5:
# Byte 5: Slave sends manufacturer ID (or device ID).
self.ids = [miso]
d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
- self.putx([24, ['%s ID' % d]])
+ self.putx([Ann.FIELD, ['%s ID: 0x%02x' % (d, miso)]])
elif self.cmdstate == 6:
# Byte 6: Slave sends device ID (or manufacturer ID).
self.ids.append(miso)
- d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
- self.putx([24, ['%s ID' % d]])
+ d = 'Device' if self.manufacturer_id_first else 'Manufacturer'
+ self.putx([Ann.FIELD, ['%s ID: 0x%02x' % (d, miso)]])
if self.cmdstate == 6:
id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
- self.putx([24, ['Device: Macronix %s' % device_name[id]]])
+ self.device_id = id
+ self.es_cmd = self.es
+ self.putc([Ann.REMS, self.cmd_vendor_dev_list()])
self.state = None
else:
self.cmdstate += 1
def handle_dsry(self, mosi, miso):
pass # TODO
- def decode(self, ss, es, data):
+ def output_data_block(self, label, idx):
+ # Print accumulated block of data
+ # (called on CS# de-assert via self.on_end_transaction callback).
+ self.es_cmd = self.es # End on the CS# de-assert sample.
+ if self.options['format'] == 'hex':
+ s = ' '.join([('%02x' % b) for b in self.data])
+ else:
+ s = ''.join(map(chr, self.data))
+ self.putf([Ann.FIELD, ['%s (%d bytes)' % (label, len(self.data))]])
+ self.putc([idx, ['%s (addr 0x%06x, %d bytes): %s' % \
+ (cmds[self.state][1], self.addr, len(self.data), s)]])
+ def decode(self, ss, es, data):
ptype, mosi, miso = data
- # if ptype == 'DATA':
- # self.putx([0, ['MOSI: 0x%02x, MISO: 0x%02x' % (mosi, miso)]])
+ self.ss, self.es = ss, es
- # if ptype == 'CS-CHANGE':
- # if mosi == 1 and miso == 0:
- # self.putx([0, ['Asserting CS#']])
- # elif mosi == 0 and miso == 1:
- # self.putx([0, ['De-asserting CS#']])
+ if ptype == 'CS-CHANGE':
+ self.end_current_transaction()
if ptype != 'DATA':
return
- self.ss, self.es = ss, es
-
# If we encountered a known chip command, enter the resp. state.
if self.state is None:
self.state = mosi
self.cmdstate = 1
# Handle commands.
- if self.state in cmds:
- s = 'handle_%s' % cmds[self.state][0].lower().replace('/', '_')
- handle_reg = getattr(self, s)
- handle_reg(mosi, miso)
- else:
- self.putx([24, ['Unknown command: 0x%02x' % mosi]])
+ try:
+ self.cmd_handlers[self.state](mosi, miso)
+ except KeyError:
+ self.putx([Ann.BIT, ['Unknown command: 0x%02x' % mosi]])
self.state = None
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+## Authors: Oleksij Rempel
+## <fixed-term.Oleksij.Rempel@de.bosch.com>
+## <linux@rempel-privat.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the Bosch
+SSI32 protocol.
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+## Authors: Oleksij Rempel
+## <fixed-term.Oleksij.Rempel@de.bosch.com>
+## <linux@rempel-privat.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 'ssi32'
+ name = 'SSI32'
+ longname = 'Synchronous Serial Interface (32bit)'
+ desc = 'Synchronous Serial Interface (32bit) protocol.'
+ license = 'gplv2+'
+ inputs = ['spi']
+ outputs = ['ssi32']
+ options = (
+ {'id': 'msgsize', 'desc': 'Message size', 'default': 64},
+ )
+ annotations = (
+ ('ctrl-tx', 'CTRL TX'),
+ ('ack-tx', 'ACK TX'),
+ ('ctrl-rx', 'CTRL RX'),
+ ('ack-rx', 'ACK RX'),
+ )
+ annotation_rows = (
+ ('tx', 'TX', (0, 1)),
+ ('rx', 'RX', (2, 3)),
+ )
+
+ def __init__(self):
+ self.ss_cmd, self.es_cmd = 0, 0
+ self.mosi_bytes = []
+ self.miso_bytes = []
+ self.es_array = []
+ self.rx_size = 0
+ self.tx_size = 0
+
+ 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 reset(self):
+ self.mosi_bytes = []
+ self.miso_bytes = []
+ self.es_array = []
+
+ def handle_ack(self):
+ # Only first byte should have ACK data, other 3 bytes are reserved.
+ self.es_cmd = self.es_array[0]
+ self.putx([1, ['> ACK:0x%02x' % (self.mosi_bytes[0])]])
+ self.putx([3, ['< ACK:0x%02x' % (self.miso_bytes[0])]])
+
+ def handle_ctrl(self):
+ mosi = miso = ''
+ self.tx_size = self.mosi_bytes[2]
+ self.rx_size = self.miso_bytes[2]
+
+ if self.tx_size > 0:
+ mosi = ', DATA:0x' + ''.join(format(x, '02x') for x in self.mosi_bytes[4:self.tx_size + 4])
+ if self.rx_size > 0:
+ miso = ', DATA:0x' + ''.join(format(x, '02x') for x in self.miso_bytes[4:self.rx_size + 4])
+
+ self.es_cmd = self.es_array[self.tx_size + 3]
+ self.putx([0, ['> CTRL:0x%02x, LUN:0x%02x, SIZE:0x%02x, CRC:0x%02x%s'
+ % (self.mosi_bytes[0], self.mosi_bytes[1],
+ self.mosi_bytes[2], self.mosi_bytes[3], mosi)]])
+
+ self.es_cmd = self.es_array[self.rx_size + 3]
+ self.putx([2, ['< CTRL:0x%02x, LUN:0x%02x, SIZE:0x%02x, CRC:0x%02x%s'
+ % (self.miso_bytes[0], self.miso_bytes[1],
+ self.miso_bytes[2], self.miso_bytes[3], miso)]])
+
+ def decode(self, ss, es, data):
+ ptype = data[0]
+ if ptype == 'CS-CHANGE':
+ self.reset()
+ return
+
+ # Don't care about anything else.
+ if ptype != 'DATA':
+ return
+ mosi, miso = data[1:]
+
+ self.ss, self.es = ss, es
+
+ if len(self.mosi_bytes) == 0:
+ self.ss_cmd = ss
+ self.mosi_bytes.append(mosi)
+ self.miso_bytes.append(miso)
+ self.es_array.append(es)
+
+ if self.mosi_bytes[0] & 0x80:
+ if len(self.mosi_bytes) < 4:
+ return
+
+ self.handle_ack()
+ self.reset()
+ else:
+ if len(self.mosi_bytes) < self.options['msgsize']:
+ return
+
+ self.handle_ctrl()
+ self.reset()
('position', 'Position', (1,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.oldstep = None
- self.prev_step_ss = None
+ self.ss_prev_step = None
self.pos = 0
self.prev_speed = None
self.prev_pos = None
self.unit = 'mm'
def step(self, ss, direction):
- if self.prev_step_ss is not None:
- delta = ss - self.prev_step_ss
+ if self.ss_prev_step is not None:
+ delta = ss - self.ss_prev_step
speed = self.samplerate / delta / self.scale
speed_txt = self.format % speed
pos_txt = self.format % (self.pos / self.scale)
- self.put(self.prev_step_ss, ss, self.out_ann,
+ self.put(self.ss_prev_step, ss, self.out_ann,
[0, [speed_txt + ' ' + self.unit + '/s', speed_txt]])
- self.put(self.prev_step_ss, ss, self.out_ann,
+ self.put(self.ss_prev_step, ss, self.out_ann,
[1, [pos_txt + ' ' + self.unit, pos_txt]])
self.pos += (1 if direction else -1)
- self.prev_step_ss = ss
+ self.ss_prev_step = ss
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
('parity', 'PARITY'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
# SWD data/clock state
self.state = 'UNKNOWN'
self.oldclk = -1
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+'''
+T55xx is a 100-150kHz RFID protocol according to the Atmel e555x
+downlink/write protocol (pulse interval coding).
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+ pass
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 't55xx'
+ name = 'T55xx'
+ longname = 'RFID T55xx'
+ desc = 'T55xx 100-150kHz RFID protocol.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['t55xx']
+ channels = (
+ {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+ )
+ options = (
+ {'id': 'coilfreq', 'desc': 'Coil frequency', 'default': 125000},
+ {'id': 'start_gap', 'desc': 'Start gap min', 'default': 20},
+ {'id': 'w_gap', 'desc': 'Write gap min', 'default': 20},
+ {'id': 'w_one_min', 'desc': 'Write one min', 'default': 48},
+ {'id': 'w_one_max', 'desc': 'Write one max', 'default': 63},
+ {'id': 'w_zero_min', 'desc': 'Write zero min', 'default': 16},
+ {'id': 'w_zero_max', 'desc': 'Write zero max', 'default': 31},
+ {'id': 'em4100_decode', 'desc': 'EM4100 decode', 'default': 'on',
+ 'values': ('on', 'off')},
+ )
+ annotations = (
+ ('bit_value', 'Bit value'),
+ ('start_gap', 'Start gap'),
+ ('write_gap', 'Write gap'),
+ ('write_mode_exit', 'Write mode exit'),
+ ('bit', 'Bit'),
+ ('opcode', 'Opcode'),
+ ('lock', 'Lock'),
+ ('data', 'Data'),
+ ('password', 'Password'),
+ ('address', 'Address'),
+ ('bitrate', 'Bitrate'),
+ )
+ annotation_rows = (
+ ('bits', 'Bits', (0,)),
+ ('structure', 'Structure', (1, 2, 3, 4)),
+ ('fields', 'Fields', (5, 6, 7, 8, 9)),
+ ('decode', 'Decode', (10,)),
+ )
+
+ def __init__(self):
+ self.samplerate = None
+ self.oldpin = None
+ self.last_samplenum = None
+ self.lastlast_samplenum = None
+ self.state = 'START_GAP'
+ self.bits_pos = [[0 for col in range(3)] for row in range(70)]
+ self.br_string = ['RF/8', 'RF/16', 'RF/32', 'RF/40',
+ 'RF/50', 'RF/64', 'RF/100', 'RF/128']
+ self.mod_str1 = ['Direct', 'Manchester', 'Biphase', 'Reserved']
+ self.mod_str2 = ['Direct', 'PSK1', 'PSK2', 'PSK3', 'FSK1', 'FSK2',
+ 'FSK1a', 'FSK2a']
+ self.pskcf_str = ['RF/2', 'RF/4', 'RF/8', 'Reserved']
+ self.em4100_decode1_partial = 0
+
+ def metadata(self, key, value):
+ if key == srd.SRD_CONF_SAMPLERATE:
+ self.samplerate = value
+ self.field_clock = self.samplerate / self.options['coilfreq']
+ self.wzmax = self.options['w_zero_max'] * self.field_clock
+ self.wzmin = self.options['w_zero_min'] * self.field_clock
+ self.womax = self.options['w_one_max'] * self.field_clock
+ self.womin = self.options['w_one_min'] * self.field_clock
+ self.startgap = self.options['start_gap'] * self.field_clock
+ self.writegap = self.options['w_gap'] * self.field_clock
+ self.nogap = 64 * self.field_clock
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def decode_config(self, idx):
+ safer_key = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \
+ self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0]
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann,
+ [10, ['Safer Key' + ': %X' % safer_key,'%X' % safer_key]])
+ bitrate = self.bits_pos[idx+11][0]<<2 | self.bits_pos[idx+12][0]<<1 | \
+ self.bits_pos[idx+13][0]
+ self.put(self.bits_pos[idx+11][1], self.bits_pos[idx+13][2],
+ self.out_ann, [10, ['Data Bit Rate: ' + \
+ self.br_string[bitrate], self.br_string[bitrate]]])
+ modulation1 = self.bits_pos[idx+15][0]<<1 | self.bits_pos[idx+16][0]
+ modulation2 = self.bits_pos[idx+17][0]<<2 | \
+ self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]
+ if modulation1 == 0:
+ mod_string = self.mod_str2[modulation2]
+ else:
+ mod_string = self.mod_str1[modulation1]
+ self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2],
+ self.out_ann, [10, ['Modulation: ' + mod_string, mod_string]])
+ psk_cf = self.bits_pos[idx+20][0]<<1 | self.bits_pos[idx+21][0]
+ self.put(self.bits_pos[idx+20][1], self.bits_pos[idx+21][2],
+ self.out_ann, [10, ['PSK-CF: ' + self.pskcf_str[psk_cf],
+ self.pskcf_str[psk_cf]]])
+ self.put(self.bits_pos[idx+22][1], self.bits_pos[idx+22][2],
+ self.out_ann, [10, ['AOR' + ': %d' % \
+ (self.bits_pos[idx+22][0]),'%d' % (self.bits_pos[idx+22][0])]])
+ maxblock = self.bits_pos[idx+24][0]<<2 | self.bits_pos[idx+25][0]<<1 | \
+ self.bits_pos[idx+26][0]
+ self.put(self.bits_pos[idx+24][1], self.bits_pos[idx+26][2],
+ self.out_ann, [10, ['Max-Block' + ': %d' % maxblock,
+ '%d' % maxblock]])
+ self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+27][2],
+ self.out_ann, [10, ['PWD' + ': %d' % \
+ (self.bits_pos[idx+27][0]),'%d' % (self.bits_pos[idx+27][0])]])
+ self.put(self.bits_pos[idx+28][1], self.bits_pos[idx+28][2],
+ self.out_ann, [10, ['ST-sequence terminator' + ': %d' % \
+ (self.bits_pos[idx+28][0]),'%d' % (self.bits_pos[idx+28][0])]])
+ self.put(self.bits_pos[idx+31][1], self.bits_pos[idx+31][2],
+ self.out_ann, [10, ['POR delay' + ': %d' % \
+ (self.bits_pos[idx+31][0]),'%d' % (self.bits_pos[idx+31][0])]])
+
+ def put4bits(self, idx):
+ bits = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \
+ self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0]
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann,
+ [10, ['%X' % bits]])
+
+ def em4100_decode1(self, idx):
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx+8][2], self.out_ann,
+ [10, ['EM4100 header', 'EM header', 'Header', 'H']])
+ self.put4bits(idx+9)
+ self.put4bits(idx+14)
+ self.put4bits(idx+19)
+ self.put4bits(idx+24)
+ self.em4100_decode1_partial = self.bits_pos[idx+29][0]<<3 | \
+ self.bits_pos[idx+30][0]<<2 | self.bits_pos[idx+31][0]<<1
+ self.put(self.bits_pos[idx+29][1], self.bits_pos[idx+31][2],
+ self.out_ann, [10, ['Partial nibble']])
+
+ def em4100_decode2(self, idx):
+ if self.em4100_decode1_partial != 0:
+ bits = self.em4100_decode1_partial + self.bits_pos[idx][0]
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+ self.out_ann, [10, ['%X' % bits]])
+ self.em4100_decode1_partial = 0
+ else:
+ self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+ self.out_ann, [10, ['Partial nibble']])
+
+ self.put4bits(idx+2)
+ self.put4bits(idx+7)
+ self.put4bits(idx+12)
+ self.put4bits(idx+17)
+ self.put4bits(idx+22)
+ self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+31][2],
+ self.out_ann, [10, ['EM4100 trailer']])
+
+ def get_32_bits(self, idx):
+ retval = 0
+ for i in range(0, 32):
+ retval <<= 1
+ retval |= self.bits_pos[i+idx][0]
+ return retval
+
+ def get_3_bits(self, idx):
+ retval = self.bits_pos[idx][0]<<2 | self.bits_pos[idx+1][0]<<1 | \
+ self.bits_pos[idx+2][0]
+ return retval
+
+ def put_fields(self):
+ if (self.bit_nr == 70):
+ self.put(self.bits_pos[0][1], self.bits_pos[1][2], self.out_ann,
+ [5, ['Opcode' + ': %d%d' % (self.bits_pos[0][0],
+ self.bits_pos[1][0]), '%d%d' % (self.bits_pos[0][0],
+ self.bits_pos[1][0])]])
+ password = self.get_32_bits(2)
+ self.put(self.bits_pos[2][1], self.bits_pos[33][2], self.out_ann,
+ [8, ['Password' + ': %X' % password, '%X' % password]])
+ self.put(self.bits_pos[34][1], self.bits_pos[34][2], self.out_ann,
+ [6, ['Lock' + ': %X' % self.bits_pos[34][0],
+ '%X' % self.bits_pos[34][0]]])
+ data = self.get_32_bits(35)
+ self.put(self.bits_pos[35][1], self.bits_pos[66][2], self.out_ann,
+ [7, ['Data' + ': %X' % data, '%X' % data]])
+ addr = self.get_3_bits(67)
+ self.put(self.bits_pos[67][1], self.bits_pos[69][2], self.out_ann,
+ [9, ['Addr' + ': %X' % addr, '%X' % addr]])
+ if addr == 0:
+ self.decode_config(35)
+ if addr == 7:
+ self.put(self.bits_pos[35][1], self.bits_pos[66][2],
+ self.out_ann, [10, ['Password' + ': %X' % data,
+ '%X' % data]])
+ # If we are programming EM4100 data we can decode it halfway.
+ if addr == 1 and self.options['em4100_decode'] == 'on':
+ self.em4100_decode1(35)
+ if addr == 2 and self.options['em4100_decode'] == 'on':
+ self.em4100_decode2(35)
+
+ if (self.bit_nr == 38):
+ self.put(self.bits_pos[0][1], self.bits_pos[1][2], self.out_ann,
+ [5, ['Opcode' + ': %d%d' % (self.bits_pos[0][0],
+ self.bits_pos[1][0]), '%d%d' % (self.bits_pos[0][0],
+ self.bits_pos[1][0])]])
+ self.put(self.bits_pos[2][1], self.bits_pos[2][2], self.out_ann,
+ [6, ['Lock' + ': %X' % self.bits_pos[2][0],
+ '%X' % self.bits_pos[2][0]]])
+ data = self.get_32_bits(3)
+ self.put(self.bits_pos[3][1], self.bits_pos[34][2], self.out_ann,
+ [7, ['Data' + ': %X' % data, '%X' % data]])
+ addr = self.get_3_bits(35)
+ self.put(self.bits_pos[35][1], self.bits_pos[37][2], self.out_ann,
+ [9, ['Addr' + ': %X' % addr, '%X' % addr]])
+ if addr == 0:
+ self.decode_config(3)
+ if addr == 7:
+ self.put(self.bits_pos[3][1], self.bits_pos[34][2],
+ self.out_ann, [10, ['Password' + ': %X' % data,
+ '%X' % data]])
+ # If we are programming EM4100 data we can decode it halfway.
+ if addr == 1 and self.options['em4100_decode'] == 'on':
+ self.em4100_decode1(3)
+ if addr == 2 and self.options['em4100_decode'] == 'on':
+ self.em4100_decode2(3)
+
+ if (self.bit_nr == 2):
+ self.put(self.bits_pos[0][1], self.bits_pos[1][2], self.out_ann,
+ [5, ['Opcode' + ': %d%d' % (self.bits_pos[0][0],
+ self.bits_pos[1][0]), '%d%d' % (self.bits_pos[0][0],
+ self.bits_pos[1][0])]])
+ self.bit_nr = 0
+
+ def add_bits_pos(self, bit, bit_start, bit_end):
+ if self.bit_nr < 70:
+ self.bits_pos[self.bit_nr][0] = bit
+ self.bits_pos[self.bit_nr][1] = bit_start
+ self.bits_pos[self.bit_nr][2] = bit_end
+ self.bit_nr += 1
+
+ def decode(self, ss, es, data):
+ if not self.samplerate:
+ raise SamplerateError('Cannot decode without samplerate.')
+ for (self.samplenum, (pin,)) in data:
+ # Ignore identical samples early on (for performance reasons).
+ if self.oldpin == pin:
+ continue
+
+ if self.oldpin is None:
+ self.oldpin = pin
+ self.last_samplenum = self.samplenum
+ self.lastlast_samplenum = self.samplenum
+ self.last_edge = self.samplenum
+ self.oldpl = 0
+ self.oldpp = 0
+ self.oldsamplenum = 0
+ self.last_bit_pos = 0
+
+ self.old_gap_start = 0
+ self.old_gap_end = 0
+ self.gap_detected = 0
+ self.bit_nr = 0
+ continue
+
+ if self.oldpin != pin:
+ pl = self.samplenum - self.oldsamplenum
+ pp = pin
+ samples = self.samplenum - self.last_samplenum
+
+ if self.state == 'WRITE_GAP':
+ if pl > self.writegap:
+ self.gap_detected = 1
+ self.put(self.last_samplenum, self.samplenum,
+ self.out_ann, [2, ['Write gap']])
+ if (self.last_samplenum-self.old_gap_end) > self.nogap:
+ self.gap_detected = 0
+ self.state = 'START_GAP'
+ self.put(self.old_gap_end, self.last_samplenum,
+ self.out_ann, [3, ['Write mode exit']])
+ self.put_fields()
+
+ if self.state == 'START_GAP':
+ if pl > self.startgap:
+ self.gap_detected = 1
+ self.put(self.last_samplenum, self.samplenum,
+ self.out_ann, [1, ['Start gap']])
+ self.state = 'WRITE_GAP'
+
+ if self.gap_detected == 1:
+ self.gap_detected = 0
+ if (self.last_samplenum - self.old_gap_end) > self.wzmin \
+ and (self.last_samplenum - self.old_gap_end) < self.wzmax:
+ self.put(self.old_gap_end, self.last_samplenum,
+ self.out_ann, [0, ['0']])
+ self.put(self.old_gap_end, self.last_samplenum,
+ self.out_ann, [4, ['Bit']])
+ self.add_bits_pos(0, self.old_gap_end,
+ self.last_samplenum)
+ if (self.last_samplenum - self.old_gap_end) > self.womin \
+ and (self.last_samplenum - self.old_gap_end) < self.womax:
+ self.put(self.old_gap_end, self.last_samplenum,
+ self.out_ann, [0, ['1']])
+ self.put(self.old_gap_end, self.last_samplenum,
+ self.out_ann, [4, ['Bit']])
+ self.add_bits_pos(1, self.old_gap_end, self.last_samplenum)
+
+ self.old_gap_start = self.last_samplenum
+ self.old_gap_end = self.samplenum
+
+ self.oldpl = pl
+ self.oldpp = pp
+ self.oldsamplenum = self.samplenum
+ self.last_samplenum = self.samplenum
+ self.oldpin = pin
('warnings', 'Warnings', (2,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.state = 'IDLE'
self.chip = -1
##
import sigrokdecode as srd
+from collections import deque
class SamplerateError(Exception):
pass
def normalize_time(t):
if t >= 1.0:
- return '%.3f s' % t
+ return '%.3f s (%.3f Hz)' % (t, (1/t))
elif t >= 0.001:
- return '%.3f ms' % (t * 1000.0)
+ if 1/t/1000 < 1:
+ return '%.3f ms (%.3f Hz)' % (t * 1000.0, (1/t))
+ else:
+ return '%.3f ms (%.3f kHz)' % (t * 1000.0, (1/t)/1000)
elif t >= 0.000001:
- return '%.3f μs' % (t * 1000.0 * 1000.0)
+ if 1/t/1000/1000 < 1:
+ return '%.3f μs (%.3f kHz)' % (t * 1000.0 * 1000.0, (1/t)/1000)
+ else:
+ return '%.3f μs (%.3f MHz)' % (t * 1000.0 * 1000.0, (1/t)/1000/1000)
elif t >= 0.000000001:
- return '%.3f ns' % (t * 1000.0 * 1000.0 * 1000.0)
+ if 1/t/1000/1000/1000:
+ return '%.3f ns (%.3f MHz)' % (t * 1000.0 * 1000.0 * 1000.0, (1/t)/1000/1000)
+ else:
+ return '%.3f ns (%.3f GHz)' % (t * 1000.0 * 1000.0 * 1000.0, (1/t)/1000/1000/1000)
else:
return '%f' % t
api_version = 2
id = 'timing'
name = 'Timing'
- longname = 'Timing calculation'
+ longname = 'Timing calculation with frequency and averaging'
desc = 'Calculate time between edges.'
license = 'gplv2+'
inputs = ['logic']
)
annotations = (
('time', 'Time'),
+ ('average', 'Average'),
)
annotation_rows = (
('time', 'Time', (0,)),
+ ('average', 'Average', (1,)),
+ )
+ options = (
+ { 'id': 'avg_period', 'desc': 'Averaging period', 'default': 100 },
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.oldpin = None
self.last_samplenum = None
+ self.last_n = deque()
+ self.chunks = 0
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
- for (samplenum, (pin,)) in data:
- # Ignore identical samples early on (for performance reasons).
- if self.oldpin == pin:
- continue
-
+ for (self.samplenum, (pin,)) in data:
if self.oldpin is None:
self.oldpin = pin
- self.last_samplenum = samplenum
+ self.last_samplenum = self.samplenum
continue
if self.oldpin != pin:
- samples = samplenum - self.last_samplenum
+ samples = self.samplenum - self.last_samplenum
t = samples / self.samplerate
+ self.chunks += 1
+
+ # Don't insert the first chunk into the averaging as it is
+ # not complete probably.
+ if self.last_samplenum is None or self.chunks < 2:
+ # Report the timing normalized.
+ self.put(self.last_samplenum, self.samplenum, self.out_ann,
+ [0, [normalize_time(t)]])
+ else:
+ if t > 0:
+ self.last_n.append(t)
+
+ if len(self.last_n) > self.options['avg_period']:
+ self.last_n.popleft()
- # Report the timing normalized.
- self.put(self.last_samplenum, samplenum, self.out_ann,
- [0, [normalize_time(t)]])
+ # Report the timing normalized.
+ self.put(self.last_samplenum, self.samplenum, self.out_ann,
+ [0, [normalize_time(t)]])
+ self.put(self.last_samplenum, self.samplenum, self.out_ann,
+ [1, [normalize_time(sum(self.last_n) / len(self.last_n))]])
# Store data for next round.
- self.last_samplenum = samplenum
+ self.last_samplenum = self.samplenum
self.oldpin = pin
('errors', 'Errors', (9,)),
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.oldpins = self.oldclk = self.oldload = self.oldldac = None
self.bits = []
self.ss_dac_first = None
- '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 512 (as the data can be up to 9 bits in size).
+ 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).
'values': (0.0, 0.5, 1.0, 1.5)},
{'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
'values': ('lsb-first', 'msb-first')},
- {'id': 'format', 'desc': 'Data format', 'default': 'ascii',
+ {'id': 'format', 'desc': 'Data format', 'default': 'hex',
'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
{'id': 'invert_rx', 'desc': 'Invert RX?', 'default': 'no',
'values': ('yes', 'no')},
s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.samplenum = 0
self.frame_start = [-1, -1]
self.startbit = [-1, -1]
self.cur_data_bit = [0, 0]
- self.databyte = [0, 0]
+ self.datavalue = [0, 0]
self.paritybit = [-1, -1]
self.stopbit1 = [-1, -1]
self.startsample = [-1, -1]
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.startbit[rxtx] = signal
- # The startbit must be 0. If not, we report an error.
+ # 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[rxtx] != 0:
self.putp(['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
- # TODO: Abort? Ignore rest of the frame?
+ self.state[rxtx] = 'WAIT FOR START BIT'
+ return
self.cur_data_bit[rxtx] = 0
- self.databyte[rxtx] = 0
+ self.datavalue[rxtx] = 0
self.startsample[rxtx] = -1
self.state[rxtx] = 'GET DATA BITS'
# Get the next data bit in LSB-first or MSB-first fashion.
if self.options['bit_order'] == 'lsb-first':
- self.databyte[rxtx] >>= 1
- self.databyte[rxtx] |= \
+ self.datavalue[rxtx] >>= 1
+ self.datavalue[rxtx] |= \
(signal << (self.options['num_data_bits'] - 1))
else:
- self.databyte[rxtx] <<= 1
- self.databyte[rxtx] |= (signal << 0)
+ self.datavalue[rxtx] <<= 1
+ self.datavalue[rxtx] |= (signal << 0)
self.putg([rxtx + 12, ['%d' % signal]])
self.state[rxtx] = 'GET PARITY BIT'
self.putpx(rxtx, ['DATA', rxtx,
- (self.databyte[rxtx], self.databits[rxtx])])
-
- b, f = self.databyte[rxtx], self.options['format']
- if f == 'ascii':
- c = chr(b) if b in range(30, 126 + 1) else '[%02X]' % b
- self.putx(rxtx, [rxtx, [c]])
- elif f == 'dec':
- self.putx(rxtx, [rxtx, [str(b)]])
- elif f == 'hex':
- self.putx(rxtx, [rxtx, [hex(b)[2:].zfill(2).upper()]])
- elif f == 'oct':
- self.putx(rxtx, [rxtx, [oct(b)[2:].zfill(3)]])
- elif f == 'bin':
- self.putx(rxtx, [rxtx, [bin(b)[2:].zfill(8)]])
-
- self.putbin(rxtx, [rxtx, bytes([b])])
- self.putbin(rxtx, [2, bytes([b])])
+ (self.datavalue[rxtx], self.databits[rxtx])])
+
+ b = self.datavalue[rxtx]
+ formatted = self.format_value(b)
+ if formatted is not None:
+ self.putx(rxtx, [rxtx, [formatted]])
+
+ bdata = b.to_bytes(self.bw, byteorder='big')
+ self.putbin(rxtx, [rxtx, bdata])
+ self.putbin(rxtx, [2, bdata])
+
+ self.databits[rxtx] = []
+
+ 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)
- self.databits = [[], []]
+ return None
def get_parity_bit(self, rxtx, signal):
# If no parity is used/configured, skip to the next state immediately.
self.state[rxtx] = 'GET STOP BITS'
if parity_ok(self.options['parity_type'], self.paritybit[rxtx],
- self.databyte[rxtx], self.options['num_data_bits']):
+ self.datavalue[rxtx], self.options['num_data_bits']):
self.putp(['PARITYBIT', rxtx, self.paritybit[rxtx]])
self.putg([rxtx + 4, ['Parity bit', 'Parity', 'P']])
else:
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
-"""
-USB Power Delivery - baseband protocol decoder / checker
-"""
+'''
+USB Power Delivery - baseband protocol decoder / checker.
+'''
from .pd import *
self.putwarn('No start of packet found', 'XXX')
return -1 # No Start Of Packet
- def __init__(self, **kwargs):
+ def __init__(self):
self.samplerate = None
self.idx = 0
self.packet_seq = 0
)
def us2samples(self, us):
- if not self.samplerate:
- raise SamplerateError('Need the samplerate.')
return int(us * self.samplerate / 1000000)
def decode_packet(self):
)
def __init__(self):
- self.samplerate = 8e6 # None
- self.secs_per_sample = float(1) / float(self.samplerate)
+ self.samplerate = None
self.request = {}
self.request_id = 0
self.transaction_state = 'IDLE'
- self.transaction_ss = None
- self.transaction_es = None
+ self.ss_transaction = None
+ self.es_transaction = None
self.transaction_ep = None
self.transaction_addr = None
self.wrote_pcap_header = False
addr = self.transaction_addr
if not (addr, ep) in self.request:
self.request[(addr, ep)] = {'setup_data': [], 'data': [],
- 'type': None, 'ss': self.transaction_ss, 'es': None,
+ 'type': None, 'ss': self.ss_transaction, 'es': None,
'id': self.request_id, 'addr': addr, 'ep': ep}
self.request_id += 1
request_started = 1
if request['type'] in (None, 'BULK IN') and self.transaction_type == 'IN':
request['type'] = 'BULK IN'
request['data'] += self.transaction_data
- request['es'] = self.transaction_es
+ request['es'] = self.es_transaction
self.handle_request(request_started, request_end)
elif request['type'] in (None, 'BULK OUT') and self.transaction_type == 'OUT':
request['type'] = 'BULK OUT'
request['data'] += self.transaction_data
- request['es'] = self.transaction_es
+ request['es'] = self.es_transaction
self.handle_request(request_started, request_end)
# CONTROL, SETUP stage
- elif request['type'] == None and self.transaction_type == 'SETUP':
+ elif request['type'] is None and self.transaction_type == 'SETUP':
request['setup_data'] = self.transaction_data
request['wLength'] = struct.unpack('<H',
bytes(self.transaction_data[6:8]))[0]
# CONTROL, STATUS stage
elif request['type'] == 'SETUP IN' and self.transaction_type == 'OUT':
- request['es'] = self.transaction_es
+ request['es'] = self.es_transaction
self.handle_request(0, request_end)
elif request['type'] == 'SETUP OUT' and self.transaction_type == 'IN':
- request['es'] = self.transaction_es
+ request['es'] = self.es_transaction
self.handle_request(0, request_end)
else:
if pname == 'SOF':
return
if self.transaction_state == 'TOKEN RECEIVED':
- transaction_timeout = self.transaction_es
+ transaction_timeout = self.es_transaction
# Token length is 35 bits, timeout is 16..18 bit times
# (USB 2.0 7.1.19.1).
- transaction_timeout += int((self.transaction_es - self.transaction_ss) / 2)
+ transaction_timeout += int((self.es_transaction - self.ss_transaction) / 2)
if ss > transaction_timeout:
- self.transaction_es = transaction_timeout
+ self.es_transaction = transaction_timeout
self.handshake = 'timeout'
self.handle_transfer()
self.transaction_state = 'IDLE'
sync, pid, addr, ep, crc5 = pinfo
self.transaction_data = []
- self.transaction_ss = ss
- self.transaction_es = es
+ self.ss_transaction = ss
+ self.es_transaction = es
self.transaction_state = 'TOKEN RECEIVED'
self.transaction_ep = ep
self.transaction_addr = addr
self.handshake = pname
self.transaction_state = 'IDLE'
- self.transaction_es = es
+ self.es_transaction = es
self.handle_transfer()
elif pname == 'PRE':
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Sean Burford <sburford@google.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
+##
+
+'''
+The Wiegand interface is a de facto wiring standard commonly used to connect
+a card swipe mechanism to the rest of an electronic entry system.
+
+Details:
+https://en.wikipedia.org/wiki/Wiegand_interface
+'''
+
+from .pd import Decoder
--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Sean Burford <sburford@google.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+ api_version = 2
+ id = 'wiegand'
+ name = 'Wiegand'
+ longname = 'Wiegand interface'
+ desc = 'Wiegand interface for electronic entry systems.'
+ license = 'gplv2+'
+ inputs = ['logic']
+ outputs = ['wiegand']
+ channels = (
+ {'id': 'd0', 'name': 'D0', 'desc': 'Data 0 line'},
+ {'id': 'd1', 'name': 'D1', 'desc': 'Data 1 line'},
+ )
+ options = (
+ {'id': 'active', 'desc': 'Data lines active level',
+ 'default': 'low', 'values': ('low', 'high')},
+ {'id': 'bitwidth_ms', 'desc': 'Single bit width in milliseconds',
+ 'default': 4, 'values': (1, 2, 4, 8, 16, 32)},
+ )
+ annotations = (
+ ('bits', 'Bits'),
+ ('state', 'State'),
+ )
+ annotation_rows = (
+ ('bits', 'Binary value', (0,)),
+ ('state', 'Stream state', (1,)),
+ )
+
+ def __init__(self):
+ self._samples_per_bit = 10
+
+ self._d0_prev = None
+ self._d1_prev = None
+
+ self._state = None
+ self.ss_state = None
+
+ self.ss_bit = None
+ self.es_bit = None
+ self._bit = None
+ self._bits = []
+
+ def start(self):
+ 'Register output types and verify user supplied decoder values.'
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+ self._active = self.options['active'] == 'high' and 1 or 0
+ self._inactive = 1 - self._active
+
+ def metadata(self, key, value):
+ 'Receive decoder metadata about the data stream.'
+ if key == srd.SRD_CONF_SAMPLERATE:
+ ms_per_sample = 1000 * (1.0 / value)
+ ms_per_bit = float(self.options['bitwidth_ms'])
+ self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample)))
+
+ def _update_state(self, state, bit=None):
+ 'Update state and bit values when they change.'
+ if self._bit is not None:
+ self._bits.append(self._bit)
+ self.put(self.ss_bit, self.samplenum, self.out_ann,
+ [0, [str(self._bit)]])
+ self._bit = bit
+ self.ss_bit = self.samplenum
+ if bit is not None:
+ # Set a timeout so that the final bit ends.
+ self.es_bit = self.samplenum + self._samples_per_bit
+ else:
+ self.es_bit = None
+
+ if state != self._state:
+ ann = None
+ if self._state == 'data':
+ accum_bits = ''.join(str(x) for x in self._bits)
+ ann = [1, ['%d bits %s' % (len(self._bits), accum_bits),
+ '%d bits' % len(self._bits)]]
+ elif self._state == 'invalid':
+ ann = [1, [self._state]]
+ if ann:
+ self.put(self.ss_state, self.samplenum, self.out_ann, ann)
+ self.ss_state = self.samplenum
+ self._state = state
+ self._bits = []
+
+ def decode(self, ss, es, data):
+ for self.samplenum, (d0, d1) in data:
+ if d0 == self._d0_prev and d1 == self._d1_prev:
+ if self.es_bit and self.samplenum >= self.es_bit:
+ if (d0, d1) == (self._inactive, self._inactive):
+ self._update_state('idle')
+ else:
+ self._update_state('invalid')
+ continue
+
+ if self._state in (None, 'idle', 'data'):
+ if (d0, d1) == (self._active, self._inactive):
+ self._update_state('data', 0)
+ elif (d0, d1) == (self._inactive, self._active):
+ self._update_state('data', 1)
+ elif (d0, d1) == (self._active, self._active):
+ self._update_state('invalid')
+ elif self._state == 'invalid':
+ # Wait until we see an idle state before leaving invalid.
+ # This prevents inverted lines from being misread.
+ if (d0, d1) == (self._inactive, self._inactive):
+ self._update_state('idle')
+
+ self._d0_prev, self._d1_prev = d0, d1
+
+ def report(self):
+ return '%s: %s D0 %d D1 %d (active on %d), %d samples per bit' % (
+ self.name, self._state, self._d0_prev, self._d1_prev,
+ self._active, self._samples_per_bit)
##
import sigrokdecode as srd
-
-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",
-]
+from common.plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS,
+ CONNECTOR, TRANSCEIVER, SERIAL_ENCODING, XMIT_TECH, CDR, DEVICE_TECH,
+ ENHANCED_OPTS, AUX_TYPES)
class Decoder(srd.Decoder):
api_version = 2
('fields', 'XFP structure fields'),
)
- def __init__(self, **kwargs):
+ def __init__(self):
# Received data items, used as an index into samplenum/data
self.cnt = -1
# Start/end sample numbers per data item
('warnings', 'Warnings', (Ann.WARN,))
)
- def __init__(self, **kwargs):
+ def __init__(self):
self.prev_cycle = Cycle.NONE
self.op_state = self.state_IDLE