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