X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=decoders%2Fspiflash%2Fpd.py;h=4c5c06b232011d3121668406274b1bc9d91ef10f;hb=6ccb64feb20dcb7833b2452cdfface4d9aa2bd72;hp=17ebae305216f6ffe89c8f23ce8e7aece0f2c70b;hpb=5b0b88ced37e8fbe3031867255412f449245ca26;p=libsigrokdecode.git diff --git a/decoders/spiflash/pd.py b/decoders/spiflash/pd.py index 17ebae3..4c5c06b 100644 --- a/decoders/spiflash/pd.py +++ b/decoders/spiflash/pd.py @@ -24,6 +24,19 @@ from .lists import * def cmd_annotation_classes(): return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()]) +def decode_dual_bytes(sio0, sio1): + # Given a byte in SIO0 (MOSI) of even bits and a byte in + # SIO1 (MISO) of odd bits, return a tuple of two bytes. + def combine_byte(even, odd): + result = 0 + for bit in range(4): + if even & (1 << bit): + result |= 1 << (bit*2) + if odd & (1 << bit): + result |= 1 << ((bit*2) + 1) + return result + return (combine_byte(sio0 >> 4, sio1 >> 4), combine_byte(sio0, sio1)) + def decode_status_reg(data): # TODO: Additional per-bit(s) self.put() calls with correct start/end. @@ -71,9 +84,27 @@ class Decoder(srd.Decoder): options = ( {'id': 'chip', 'desc': 'Chip', 'default': tuple(chips.keys())[0], 'values': tuple(chips.keys())}, + {'id': 'format', 'desc': 'Data format', 'default': 'hex', + 'values': ('hex', 'ascii')}, ) def __init__(self): + self.device_id = -1 + self.on_end_transaction = None + self.end_current_transaction() + + # Build dict mapping command keys to handler functions. Each + # command in 'cmds' (defined in lists.py) has a matching + # handler self.handle_. + def get_handler(cmd): + s = 'handle_%s' % cmds[cmd][0].lower().replace('/', '_') + return getattr(self, s) + self.cmd_handlers = dict((cmd, get_handler(cmd)) for cmd in cmds.keys()) + + def end_current_transaction(self): + if self.on_end_transaction is not None: # Callback for CS# transition. + self.on_end_transaction() + self.on_end_transaction = None self.state = None self.cmdstate = 1 self.addr = 0 @@ -82,6 +113,7 @@ class Decoder(srd.Decoder): def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.chip = chips[self.options['chip']] + self.vendor = self.options['chip'].split('_')[0] def putx(self, data): # Simplification, most annotations span exactly one SPI byte/packet. @@ -90,6 +122,10 @@ class Decoder(srd.Decoder): def putb(self, data): self.put(self.ss_block, self.es_block, self.out_ann, data) + def vendor_device(self): + dev = device_name[self.vendor].get(self.device_id, 'Unknown') + return '%s %s' % (self.chip['vendor'], dev) + def handle_wren(self, mosi, miso): self.putx([0, ['Command: %s' % cmds[self.state][1]]]) self.state = None @@ -115,9 +151,8 @@ class Decoder(srd.Decoder): 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] + d = 'Device: %s' % self.vendor_device() self.put(self.ss_block, self.es, self.out_ann, [0, [d]]) self.state = None else: @@ -133,18 +168,42 @@ class Decoder(srd.Decoder): 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)]]) + 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_rdsr2(self, mosi, miso): + # Read status register 2: Master asserts CS#, sends RDSR2 command, + # reads status register 2 byte. If CS# is kept asserted, the status + # register 2 can be read continuously / multiple times in a row. + # When done, the master de-asserts CS# again. + if self.cmdstate == 1: + # Byte 1: Master sends command ID. + self.putx([3, ['Command: %s' % cmds[self.state][1]]]) + elif self.cmdstate >= 2: + # Bytes 2-x: Slave sends status register 2 as long as master clocks. + self.putx([24, ['Status register 2: 0x%02x' % miso]]) + self.putx([25, [decode_status_reg(miso)]]) + # TODO: Handle status register 2 correctly. self.cmdstate += 1 def handle_wrsr(self, mosi, miso): - pass # TODO + # Write status register: Master asserts CS#, sends WRSR command, + # writes 1 or 2 status register byte(s). + # When done, the master de-asserts CS# again. If this doesn't happen + # the WRSR command will not be executed. + if self.cmdstate == 1: + # Byte 1: Master sends command ID. + self.putx([3, ['Command: %s' % cmds[self.state][1]]]) + elif self.cmdstate in (2, 3): + # Bytes 2 and/or 3: Master sends status register byte(s). + self.putx([24, ['Status register: 0x%02x' % miso]]) + self.putx([25, [decode_status_reg(miso)]]) + # TODO: Handle status register 2 correctly. + + self.cmdstate += 1 def handle_read(self, mosi, miso): # Read data bytes: Master asserts CS#, sends READ command, sends @@ -162,19 +221,10 @@ class Decoder(srd.Decoder): 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 + if self.cmdstate == 5: + self.ss_block = self.ss + self.on_end_transaction = lambda: self.output_data_block('Read') + self.data.append(miso) self.cmdstate += 1 @@ -197,23 +247,29 @@ class Decoder(srd.Decoder): 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.ss_block = 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.es_block = 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.on_end_transaction = lambda: self.output_data_block('Read') + self.data.append(miso) self.cmdstate += 1 def handle_2read(self, mosi, miso): - pass # TODO + # Fast read dual I/O: Same as fast read, but all data + # after the command is sent via two I/O pins. + # MOSI = SIO0 = even bits, MISO = SIO1 = odd bits. + # Recombine the bytes and pass them up to the handle_fast_read command. + if self.cmdstate == 1: + # Byte 1: Master sends command ID. + self.putx([5, ['Command: %s' % cmds[self.state][1]]]) + self.cmdstate = 2 + else: + # Dual I/O mode. + a, b = decode_dual_bytes(mosi, miso) + # Pass same byte in as both MISO & MOSI, parser state determines + # which one it cares about. + self.handle_fast_read(a, a) + self.handle_fast_read(b, b) # TODO: Warn/abort if we don't see the necessary amount of bytes. # TODO: Warn if WREN was not seen before. @@ -266,19 +322,10 @@ class Decoder(srd.Decoder): 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 + if self.cmdstate == 5: + self.ss_block = self.ss + self.on_end_transaction = lambda: self.output_data_block('Page data') + self.data.append(mosi) self.cmdstate += 1 @@ -289,7 +336,20 @@ class Decoder(srd.Decoder): pass # TODO def handle_rdp_res(self, mosi, miso): - pass # TODO + 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, 4): + # Bytes 2/3/4: Master sends three dummy bytes. + self.putx([24, ['Dummy byte: %02x' % mosi]]) + elif self.cmdstate == 5: + # Byte 5: Slave sends device ID. + self.device_id = miso + self.putx([24, ['Device: %s' % self.vendor_device()]]) + self.state = None + + self.cmdstate += 1 def handle_rems(self, mosi, miso): if self.cmdstate == 1: @@ -320,7 +380,8 @@ class Decoder(srd.Decoder): if self.cmdstate == 6: id = self.ids[1] if self.manufacturer_id_first else self.ids[0] - self.putx([24, ['Device: Macronix %s' % device_name[id]]]) + self.device_id = id + self.putx([24, ['Device: %s' % self.vendor_device()]]) self.state = None else: self.cmdstate += 1 @@ -346,34 +407,35 @@ class Decoder(srd.Decoder): def handle_dsry(self, mosi, miso): pass # TODO - def decode(self, ss, es, data): + def output_data_block(self, label): + # Print accumulated block of data + # (called on CS# de-assert via self.on_end_transaction callback). + self.es_block = self.es # Ends on the CS# de-assert sample. + if self.options['format'] == 'hex': + s = ' '.join([('%02x' % b) for b in self.data]) + else: + s = ''.join(map(chr, self.data)) + self.putb([25, ['%s %d bytes: %s' % (label, len(self.data), s)]]) + def decode(self, ss, es, data): ptype, mosi, miso = data - # if ptype == 'DATA': - # self.putx([0, ['MOSI: 0x%02x, MISO: 0x%02x' % (mosi, miso)]]) + self.ss, self.es = ss, es - # if ptype == 'CS-CHANGE': - # if mosi == 1 and miso == 0: - # self.putx([0, ['Asserting CS#']]) - # elif mosi == 0 and miso == 1: - # self.putx([0, ['De-asserting CS#']]) + if ptype == 'CS-CHANGE': + self.end_current_transaction() if ptype != 'DATA': return - self.ss, self.es = ss, es - # If we encountered a known chip command, enter the resp. state. if self.state is None: self.state = mosi self.cmdstate = 1 # Handle commands. - if self.state in cmds: - s = 'handle_%s' % cmds[self.state][0].lower().replace('/', '_') - handle_reg = getattr(self, s) - handle_reg(mosi, miso) - else: + try: + self.cmd_handlers[self.state](mosi, miso) + except KeyError: self.putx([24, ['Unknown command: 0x%02x' % mosi]]) self.state = None