diff --git a/software/glasgow/applet/interface/issp_host/__init__.py b/software/glasgow/applet/interface/issp_host/__init__.py new file mode 100644 index 000000000..1f2a3ff76 --- /dev/null +++ b/software/glasgow/applet/interface/issp_host/__init__.py @@ -0,0 +1,599 @@ +import argparse +import logging +import math +import asyncio +from amaranth import * +from amaranth.lib import io, cdc, enum, data +from ... import * + +MAX_SEND_BITS = 2**16 +assert MAX_SEND_BITS <= 2**16, "must update state machine to be able to send more" + +ZERO_BITS_AFTER_POLL = 40 +# Number of zero bits defined in ISSP programming specification differs. +# revK says 40 zero bits +# revI says 30 zero bits +# Applying more zero bits then necessary is fine. (The spec explicity says that 0 padding after any 22-bit mnemonic is permitted) + +class _Command(data.Struct): + class Kind(enum.Enum): + ASSERT_RESET = 0x1 + DEASSERT_RESET = 0x2 + FLOAT_RESET = 0x3 + SEND_BITS = 0x4 + READ_BYTE = 0x5 + WAIT_PENDING = 0x6 + FLOAT_SCLK = 0x7 + LOW_SCLK = 0x8 + + kind: Kind + params: data.UnionLayout({ + "send_bits": data.StructLayout({ + "do_poll": unsigned(1), + "needs_single_clock_pulse_for_poll": unsigned(1), + "needs_arbitrary_clocks_for_poll": unsigned(1), + }), + "read_byte": data.StructLayout({ + "reg_not_mem": unsigned(1), + }), + }) + +assert _Command.as_shape().size <= 8, "Command must fit in a byte" + +class PinDriver(): + def __init__(self, m, ports, name, synchronize_input=False, edge_detect_input=False): + self._name = name + self.m = m + self.buffer = io.FFBuffer("io", ports[name]) + m.submodules[name] = self.buffer + self.o = Signal(name=name + "_o") + self.o_nxt = Signal(name=name + "_o_nxt") + self.oe = Signal(name=name + "_oe") + self.oe_nxt = Signal(name=name + "_oe_nxt") + + # Here are the flipflops that mirror the state of what's in the IO buffer: + m.d.sync += (self.o.eq(self.o_nxt), + self.oe.eq(self.oe_nxt)) + + # This is the deafult case, the pin will always keep it's previous state, unless modified + m.d.comb += (self.o_nxt.eq(self.o), + self.oe_nxt.eq(self.oe)) + + # Here we connect up to the buffer: + m.d.comb += self.buffer.oe.eq(self.oe_nxt) + m.d.comb += self.buffer.o.eq(self.o_nxt) + + if synchronize_input: + self.i_sync = Signal(name=name + "_i_sync") + synchronizer = cdc.FFSynchronizer(self.buffer.i, self.i_sync) + m.submodules[name + "_synchronizer"] = synchronizer + + if edge_detect_input: + self.i_sync_ff = Signal(name=name + "i_sync_ff") + m.d.sync += self.i_sync_ff.eq(self.i_sync) + + + def negedge(self): + return (self.i_sync == 0) & (self.i_sync_ff == 1) + + def posedge(self): + return (self.i_sync == 1) & (self.i_sync_ff == 0) + + def drive(self, value): + self.m.d.comb += (self.o_nxt.eq(value), + self.oe_nxt.eq(1)) + + def release(self): + self.m.d.comb += self.oe_nxt.eq(0) + +class ISSPHostSubtarget(Elaboratable): + def __init__(self, ports, out_fifo, in_fifo, period_cyc, io_decay_wait_cyc, after_reset_wait_cyc, sample_before_falling_edge_cyc=1): + """ + sample_before_falling_edge_cyc: Specifies how many system clock cycles before the falling edge + we should be sampling the data signal at. (as seen from outside of the FPGA). + If you set it to 3, while running at 8MHz, the signal will be sampled at exactly the rising + edge, like the ISSP spec specifies. Setting it to a lower number we can further optimize timing. + """ + self._ports = ports + self._out_fifo = out_fifo + self._in_fifo = in_fifo + self._period_cyc = period_cyc + self._clock_high_cyc = period_cyc // 2 + self._clock_low_cyc = period_cyc - self._clock_high_cyc + self._io_decay_plus_sample_wait_cyc = io_decay_wait_cyc + 3 + self._after_reset_wait_cyc = after_reset_wait_cyc + self._timer_max_cyc = max(self._clock_high_cyc, self._clock_low_cyc, self._io_decay_plus_sample_wait_cyc) + assert sample_before_falling_edge_cyc <= 3 + self._sample_data_delayed = 3 - sample_before_falling_edge_cyc + assert self._sample_data_delayed < self._period_cyc + self._clock_low_cyc + + def elaborate(self, platform): + m = Module() + + i_fifo = self._in_fifo.stream + o_fifo = self._out_fifo.stream + + total_bits_counter = Signal(range(max(MAX_SEND_BITS, 8))) + + sclk = PinDriver(m, self._ports, "sclk") + sdata = PinDriver(m, self._ports, "sdata", synchronize_input=True, edge_detect_input=True) + + sdata_negedge_seen = Signal() + m.d.sync += sdata_negedge_seen.eq(sdata_negedge_seen | sdata.negedge()) + + xres_pin_exists = self._ports.xres is not None + if xres_pin_exists: + xres = PinDriver(m, self._ports, "xres", synchronize_input=True) + else: + may_be_after_power_cycle = Signal(init=1) + + # Use a single timer for various waits + timer = Signal(range(self._timer_max_cyc)) + timer_running = Signal() + timer_mode_clock = Signal() + timer_done_oneshot = Signal() + timer_done = Signal() + m.d.comb += timer_done.eq(timer_done_oneshot | ~timer_running) + + with m.If(timer_running): + with m.If(timer == 0): + with m.If(sclk.o & timer_mode_clock): + m.d.sync += timer.eq(self._clock_low_cyc - 1) + sclk.drive(0) + with m.Else(): + m.d.sync += timer_running.eq(0) + m.d.comb += timer_done_oneshot.eq(1) + with m.Else(): + m.d.sync += timer.eq(timer - 1) + + def start_clock_cycle(): + """ + This will start an sclk clock cycle, first a rising edge is sent, + then, when the timer runs out, it will send a falling edge and it + will auto-restart itself. When the clock cycle is complete timer_done + will go high + """ + m.d.sync += timer.eq(self._clock_high_cyc - 1) + m.d.sync += timer_running.eq(1) + m.d.sync += timer_mode_clock.eq(1) + sclk.drive(1) + + def start_simple_timer(cycles): + """ + This is used to just implement delays without any action being taken. + When the timer expires, it won't restart, and timer_done will go high. + """ + assert cycles >= 1 + assert cycles - 1 < self._timer_max_cyc + m.d.sync += timer.eq(cycles - 1) + m.d.sync += timer_running.eq(1) + m.d.sync += timer_mode_clock.eq(0) + + cmd = Signal(_Command) + byte = Signal(8) + bit_in_byte_counter = Signal(3) + + do_sample_data = Signal() + do_sample_data_delayed = do_sample_data + for i in range(self._sample_data_delayed): + do_sample_data_delayed_new = Signal(name=f"do_sample_data_delayed_{i}") + m.d.sync += do_sample_data_delayed_new.eq(do_sample_data_delayed) + do_sample_data_delayed = do_sample_data_delayed_new + with m.If(do_sample_data_delayed): + m.d.sync += byte.eq((byte << 1) | sdata.i_sync) + + with m.FSM(): + with m.State("IDLE"): + m.d.comb += o_fifo.ready.eq(1) + with m.If(o_fifo.valid): + m.d.sync += cmd.eq(o_fifo.payload) + m.next = "COMMAND" + with m.State("COMMAND"): + with m.Switch(cmd.kind): + with m.Case(_Command.Kind.ASSERT_RESET): + if xres_pin_exists: + xres.drive(1) + else: + m.d.sync += may_be_after_power_cycle.eq(1) + m.next = "IDLE" + with m.Case(_Command.Kind.DEASSERT_RESET): + if xres_pin_exists: + xres.drive(0) + m.next = "IDLE" + with m.Case(_Command.Kind.FLOAT_RESET): + if xres_pin_exists: + xres.release() + m.next = "IDLE" + with m.Case(_Command.Kind.SEND_BITS): + m.d.comb += o_fifo.ready.eq(1) + with m.If(o_fifo.valid): + m.d.sync += total_bits_counter[8:].eq(o_fifo.payload) + m.next = "SEND_BITS_WAIT_CNT_LSB" + with m.Case(_Command.Kind.READ_BYTE): + m.d.comb += o_fifo.ready.eq(1) + with m.If(o_fifo.valid): + m.d.sync += byte.eq(o_fifo.payload) + sdata.drive(1) + start_clock_cycle() + m.next = "READ_BYTE_SEND_CMD_BIT_0" + with m.Case(_Command.Kind.WAIT_PENDING): + m.d.comb += i_fifo.payload.eq(0) + m.d.comb += i_fifo.valid.eq(1) + with m.If(i_fifo.ready): + m.next = "IDLE" + with m.Case(_Command.Kind.FLOAT_SCLK): + sclk.release() + m.next = "IDLE" + with m.Case(_Command.Kind.LOW_SCLK): + sclk.drive(0) + m.next = "IDLE" + with m.Default(): + m.next = "IDLE" + with m.State("SEND_BITS_WAIT_CNT_LSB"): + m.d.comb += o_fifo.ready.eq(1) + with m.If(o_fifo.valid): + m.d.sync += total_bits_counter[:8].eq(o_fifo.payload) + + if xres_pin_exists: + xres.drive(0) + + with m.If(((xres.oe == 1) & (xres.o == 1)) | + ((xres.oe == 0) & (xres.i_sync == 1))): + start_simple_timer(self._after_reset_wait_cyc - 1) + # ^^ - 1 to account for the additional cycle to remove the first data byte from the FIFO + m.next = "SEND_BITS_WAIT_AFTER_RESET" + with m.Else(): + m.next = "SEND_BITS_WAIT_DATA" + else: + # LIMITED support for power-cycle mode: + with m.If(may_be_after_power_cycle & sdata.i_sync): + m.d.sync += may_be_after_power_cycle.eq(0) + start_simple_timer(self._after_reset_wait_cyc - 1) + m.next = "PWR_CYCLE_MODE_WAIT_SDATA_LOW_PLUS_TIME" + with m.Else(): + m.next = "SEND_BITS_WAIT_DATA" + with m.State("PWR_CYCLE_MODE_WAIT_SDATA_LOW_PLUS_TIME"): + with m.If(sdata.i_sync): + start_simple_timer(self._after_reset_wait_cyc - 1) + with m.Else(): + with m.If(timer_done): + m.next = "SEND_BITS_WAIT_DATA" + with m.State("SEND_BITS_WAIT_AFTER_RESET"): + with m.If(timer_done_oneshot): + m.next = "SEND_BITS_WAIT_DATA" + with m.State("SEND_BITS_WAIT_DATA"): + m.d.comb += o_fifo.ready.eq(1) + with m.If(o_fifo.valid): + m.d.sync += bit_in_byte_counter.eq(7) + m.d.sync += byte.eq(o_fifo.payload[:7]) + sdata.drive(o_fifo.payload[7]) + start_clock_cycle() + m.next = "SEND_BITS_SHIFT" + with m.State("SEND_BITS_SHIFT"): + with m.If(timer_done_oneshot): + m.d.sync += byte.eq(byte << 1) + sdata.drive(byte[6]) + m.d.sync += bit_in_byte_counter.eq(bit_in_byte_counter - 1) + m.d.sync += total_bits_counter.eq(total_bits_counter - 1) + + with m.If(total_bits_counter == 0): + sdata.release() + with m.If(cmd.params.send_bits.do_poll): + start_simple_timer(self._io_decay_plus_sample_wait_cyc) + m.next = "SEND_BITS_WAIT_IO_DECAY" + with m.Else(): + m.next = "IDLE" + with m.Elif(bit_in_byte_counter == 0): + m.d.comb += o_fifo.ready.eq(1) + with m.If(o_fifo.valid): + m.d.sync += byte.eq(o_fifo.payload[:7]) + sdata.drive(o_fifo.payload[7]) + start_clock_cycle() + with m.Else(): + m.next = "SEND_BITS_WAIT_DATA" + with m.Else(): + start_clock_cycle() + with m.State("SEND_BITS_WAIT_IO_DECAY"): + with m.If(timer_done_oneshot): + with m.If(cmd.params.send_bits.needs_single_clock_pulse_for_poll): + start_clock_cycle() + m.d.sync += sdata_negedge_seen.eq(0) + m.next = "SEND_BITS_WAIT_POLL" + with m.State("SEND_BITS_WAIT_POLL"): + # Some versions of the ISSP spec say that the chip can be in this + # state with SDATA high for 100ms, other versions say 200ms, For + # now we don't implement a time-out. + with m.If(sdata_negedge_seen & ~sdata.i_sync & ~timer_running): + m.next = "IDLE" + with m.Elif(cmd.params.send_bits.needs_arbitrary_clocks_for_poll & ~sdata_negedge_seen & ~sdata.i_sync & timer_done): + start_clock_cycle() + with m.State("SEND_BITS_ZERO_AFTER_POLL"): + with m.If(timer_done_oneshot): + m.d.sync += total_bits_counter.eq(total_bits_counter - 1) + with m.If(total_bits_counter == 0): + sdata.release() + m.next = "IDLE" + with m.Else(): + start_clock_cycle() + with m.State("READ_BYTE_SEND_CMD_BIT_0"): + with m.If(timer_done_oneshot): + sdata.drive(cmd.params.read_byte.reg_not_mem) + start_clock_cycle() + m.next = "READ_BYTE_SEND_CMD_BIT_1" + with m.State("READ_BYTE_SEND_CMD_BIT_1"): + with m.If(timer_done_oneshot): + sdata.drive(1) + start_clock_cycle() + m.next = "READ_BYTE_SEND_CMD_BIT_2" + with m.State("READ_BYTE_SEND_CMD_BIT_2"): + with m.If(timer_done_oneshot): + sdata.drive(byte[7]) + m.d.sync += bit_in_byte_counter.eq(7) + start_clock_cycle() + m.next = "READ_BYTE_SHIFT_ADDRESS" + with m.State("READ_BYTE_SHIFT_ADDRESS"): + with m.If(timer_done_oneshot): + sdata.drive(byte[6]) + m.d.sync += byte.eq(byte << 1) + m.d.sync += bit_in_byte_counter.eq(bit_in_byte_counter - 1) + start_clock_cycle() + with m.If(bit_in_byte_counter == 0): + sdata.release() + m.next = "READ_BYTE_WAIT_TURNAROUND_1" + with m.State("READ_BYTE_WAIT_TURNAROUND_1"): + with m.If(timer_done_oneshot): + start_clock_cycle() + m.d.sync += bit_in_byte_counter.eq(7) + m.next = "READ_BYTE_SHIFT" + with m.State("READ_BYTE_SHIFT"): + with m.If((timer == 0) & (sclk.o) & (bit_in_byte_counter != 7)): + m.d.comb += do_sample_data.eq(1) + with m.If(timer_done_oneshot): + start_clock_cycle() + m.d.sync += bit_in_byte_counter.eq(bit_in_byte_counter - 1) + with m.If(bit_in_byte_counter == 0): + start_clock_cycle() + m.next = "READ_BYTE_WAIT_TURNAROUND_2" + with m.State("READ_BYTE_WAIT_TURNAROUND_2"): + with m.If((timer == 0) & (sclk.o)): + m.d.comb += do_sample_data.eq(1) + with m.If(timer_done_oneshot): + sdata.drive(1) + # Note: comparing to the ISSP specification it might seem that + # we are missing a high-z clock cycle here, however this is + # documented incorrectly: final bus turnaround happens between + # falling-edge and rising-edge, there's only half a cycle for bus + # turnaround, rather then the full 1.5 cycles shown on the example + # waveform. + start_clock_cycle() + m.next = "READ_BYTE_WAIT_TURNAROUND_3" + with m.State("READ_BYTE_WAIT_TURNAROUND_3"): + with m.If(timer_done): + m.d.comb += i_fifo.payload.eq(byte) + m.d.comb += i_fifo.valid.eq(1) + sdata.release() + with m.If(i_fifo.ready): + m.next = "IDLE" + + return m + +assert Const({"kind": 1}, _Command).as_value().value == 1, "Code below assumes the command kind is at offset zero" + +DO_POLL_OFFSET = 4 +NEEDS_SINGLE_CLOCK_PULSE_FOR_POLL_OFFSET = 5 +NEEDS_ARBITRARY_CLOCKS_FOR_POLL_OFFSET = 6 +REG_NOT_MEM_OFFSET = 4 + +assert Const({"params": {"send_bits": {"do_poll": 1}}}, _Command).as_value().value == 1 << DO_POLL_OFFSET, "Please update the above constants" +assert Const({"params": {"send_bits": {"needs_single_clock_pulse_for_poll": 1}}}, _Command).as_value().value == 1 << NEEDS_SINGLE_CLOCK_PULSE_FOR_POLL_OFFSET, "Please update the above constants" +assert Const({"params": {"send_bits": {"needs_arbitrary_clocks_for_poll": 1}}}, _Command).as_value().value == 1 << NEEDS_ARBITRARY_CLOCKS_FOR_POLL_OFFSET, "Please update the above constants" +assert Const({"params": {"read_byte": {"reg_not_mem": 1}}}, _Command).as_value().value == 1 << REG_NOT_MEM_OFFSET, "Please update the above constants" + +class ISSPHostInterface: + def __init__(self, iface, logger): + self.lower = iface + self._logger = logger + self._level = logging.DEBUG if self._logger.name == __name__ else logging.TRACE + + async def wait_pending(self): + """ + Make sure that pending commands are completed. + + This command causes a "0" byte to be returned to the USB host, + and we wait to receive that byte. After we've received it, we know + that all state-machine commands sent before have finished. + """ + await self.lower.write([_Command.Kind.WAIT_PENDING.value]) + await self.lower.flush() + assert 0 == (await self.lower.read(1))[0] + + async def assert_xres(self): + """ + Assert xres (set to 1) (Also, in case we're in power-cycle mode, tell the state machine that we're about to power-cycle) + """ + await self.lower.write([_Command.Kind.ASSERT_RESET.value]) + await self.wait_pending() + + async def deassert_xres(self): + """ + Deassert xres (set to 0) + """ + await self.lower.write([_Command.Kind.DEASSERT_RESET.value]) + await self.wait_pending() + + async def float_xres(self): + """ + Float xres if it was previously driven + """ + await self.lower.write([_Command.Kind.FLOAT_RESET.value]) + await self.wait_pending() + + async def float_sclk(self): + """ + Float sclk if it was previously driven + """ + await self.lower.write([_Command.Kind.FLOAT_SCLK.value]) + + async def low_sclk(self): + """ + Drive sclk with a strong low value + """ + await self.lower.write([_Command.Kind.LOW_SCLK.value]) + + async def _send_zero_bits(self, cnt_bits = ZERO_BITS_AFTER_POLL): + cnt_enc = cnt_bits - 1 + cnt_bytes = (cnt_bits + 7)//8 + zero_blist = [0] * cnt_bytes + await self.lower.write([ + _Command.Kind.SEND_BITS.value, cnt_enc >> 8, cnt_enc & 0xff, *zero_blist]) + + async def send_bits(self, cnt_bits, value, do_poll=1, do_zero_bits=1, needs_single_clock_pulse_for_poll=1, needs_arbitrary_clocks_for_poll=0): + """ + Send a vector of up to MAX_SEND_BITS bits, while also optionally performing + "Wait and Poll", and optionally send the 40 terminating zero-bits. + + The bits are specified as an integer, and number of bits to send. + The value is sent MSB-first. + """ + assert cnt_bits <= MAX_SEND_BITS + assert (value >> cnt_bits) == 0 + cnt_enc = cnt_bits - 1 + cnt_enc_msb = cnt_enc >> 8 + cnt_enc_lsb = cnt_enc & 0xff + bits_left = cnt_bits + blist = [] + while bits_left > 0: + blist.append(((value << 8) >> bits_left) & 0xff) + bits_left -= 8 + await self.lower.write([ + (_Command.Kind.SEND_BITS.value | + (do_poll << DO_POLL_OFFSET) | + (needs_single_clock_pulse_for_poll << NEEDS_SINGLE_CLOCK_PULSE_FOR_POLL_OFFSET) | + (needs_arbitrary_clocks_for_poll << NEEDS_ARBITRARY_CLOCKS_FOR_POLL_OFFSET) + ), cnt_enc_msb, cnt_enc_lsb, *blist]) + if do_zero_bits: + await self._send_zero_bits() + + async def send_bitstring(self, bitstring, do_poll=1, do_zero_bits=1, needs_single_clock_pulse_for_poll=1, needs_arbitrary_clocks_for_poll=0): + """ + Send a vector of up to MAX_SEND_BITS bits, while also optionally performing + "Wait and Poll", and optionally send the 40 terminating zero-bits. + + The bits are specified as a string of "1" and "0", and the bits are sent in the specified order. + """ + cnt_enc = len(bitstring) - 1 + cnt_enc_msb = cnt_enc >> 8 + cnt_enc_lsb = cnt_enc & 0xff + blist = [] + for index in range(0, len(bitstring), 8): + piece = bitstring[index:index+8] + if len(piece) < 8: + piece = piece + ("0" * (8 - len(piece))) + blist.append(int(piece, 2)) + await self.lower.write([ + (_Command.Kind.SEND_BITS.value | + (do_poll << DO_POLL_OFFSET) | + (needs_single_clock_pulse_for_poll << NEEDS_SINGLE_CLOCK_PULSE_FOR_POLL_OFFSET) | + (needs_arbitrary_clocks_for_poll << NEEDS_ARBITRARY_CLOCKS_FOR_POLL_OFFSET) + ), cnt_enc_msb, cnt_enc_lsb, *blist]) + if do_zero_bits: + await self._send_zero_bits() + + async def read_bytes(self, address, cnt_bytes=1, reg_not_mem=0): + """ + Executes one or more READ-BYTE sequences, as per ISSP sepcification. + Can be used for "Verify Silicon ID Procedure", and for "Verify Procedure". + Note that we deviate from the specification, which incorrectly shows + 1.5 cycles of bus turnaround time in the waveforms, when going from the target + driving the data line to the host driving the data line. Real chips have half + a cycle of turnaround time instead, and inserting the extra clock cycles causes + a lock-up. (The first bus turnaround between address and data bits of 1.5 cycles + is correctly described) + """ + send_bytes = [] + for offset in range(cnt_bytes): + send_bytes.append(_Command.Kind.READ_BYTE.value | (reg_not_mem << REG_NOT_MEM_OFFSET)) + send_bytes.append(address + offset) + await self.lower.write(send_bytes) + return list(await self.lower.read(cnt_bytes)) + + async def write_bytes(self, address, bytes, reg_not_mem=0): + """ + Executes one or more WRITE-BYTE sequences, as per ISSP specification. + """ + for byte in bytes: + if reg_not_mem: + first_three_bits = 0b110 + else: + first_three_bits = 0b100 + encoded_bits = (((((first_three_bits << 8) | address) << 8) | byte) << 3) | 0b111 + encoded_bits_count = 3 + 8 + 8 + 3 + await self.send_bits(encoded_bits_count, encoded_bits, do_poll=0, do_zero_bits=0) + address += 1 + +class ISSPHostApplet(GlasgowApplet): + logger = logging.getLogger(__name__) + help = "initiate ISSP transactions" + description = """ + Initiate ISSP transactions. + + ISSP stands for In-system serial programming protocol, + used for programming Cypress PSoC 1 devices. + """ + required_revision = "C0" + + @classmethod + def add_build_arguments(cls, parser, access): + super().add_build_arguments(parser, access) + + access.add_pin_argument(parser, "sclk", default=True) + access.add_pin_argument(parser, "sdata", default=True) + access.add_pin_argument(parser, "xres") + + parser.add_argument( + "-f", "--frequency", metavar="FREQ", type=float, default=1000, + help="set SCLK frequency to FREQ kHz (default: %(default)s)") + + parser.add_argument( + "--io-decay-wait-time", metavar="IODECAY", type=float, default=1000, + help="specify the time it takes for sdata to settle to 0 through " + + "the weak pull-down in the target, as IODECAY ns (default: %(default)s)") + + parser.add_argument( + "--xres-to-sclk-time", metavar="XRESTOSCLK", type=float, default=1000, + help="specify the minimum time after xres is deasserted until the first " + + "sclk rising edge as XRESTOSCLK ns (default: %(default)s)") + # Note: the above constraint is not specified by Cypress. The author has measured + # this to be between 312ns and 333 ns on a particular CY8C21434. The default value + # is a guess. + + + def build(self, target, args): + self.mux_interface = iface = target.multiplexer.claim_interface(self, args) + io_decay_wait_us = 1.0 + if args.frequency > 8000.001: + self.logger.warn(f"Frequency set to {args.frequency} kHz. According to the ISSP specification this may be too high!") + period_cyc = math.ceil(target.sys_clk_freq / (args.frequency * 1000)) + after_reset_wait_cyc=math.ceil(target.sys_clk_freq * args.xres_to_sclk_time / 1000_000_000.) + txresacq_cyc = 98 * target.sys_clk_freq / 1000. / 1000. + if after_reset_wait_cyc + period_cyc * 8.5 > txresacq_cyc: + if hasattr(args, "pin_xres") and args.pin_xres is not None: + self.logger.warn(f"Frequency set to {args.frequency} kHz. This may be too slow to satisfy tXREQACQ/tXRESINI, so it's possible the device might not enter programming mode.") + iface.add_subtarget(ISSPHostSubtarget( + ports=iface.get_port_group( + sclk=args.pin_sclk, + sdata=args.pin_sdata, + xres=args.pin_xres, + ), + out_fifo=iface.get_out_fifo(), + in_fifo=iface.get_in_fifo(), + period_cyc=period_cyc, + io_decay_wait_cyc=math.ceil(target.sys_clk_freq * args.io_decay_wait_time / 1000_000_000.), + after_reset_wait_cyc=after_reset_wait_cyc, + )) + + async def run(self, device, args): + iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args) + issp_iface = ISSPHostInterface(iface, self.logger) + return issp_iface + diff --git a/software/glasgow/applet/program/psoc1/__init__.py b/software/glasgow/applet/program/psoc1/__init__.py new file mode 100644 index 000000000..4ce14dbf8 --- /dev/null +++ b/software/glasgow/applet/program/psoc1/__init__.py @@ -0,0 +1,875 @@ +import logging +import argparse +import asyncio +import textwrap +import os + +from ....support.aobject import * +from ... import * +from ...interface.issp_host import ISSPHostApplet +from fx2.format import input_data, output_data +from . import vectors + +class Psoc1Error(GlasgowAppletError): + pass + +class SyncEnabledContext: + def __init__(self, fconfig, iface): + self.fconfig = fconfig + self.iface = iface + + async def __aenter__(self): + if self.fconfig.has_sync_en_dis_cmd: + await self.iface.lower.send_bitstring(vectors.SYNC_ENABLE, do_poll=0, do_zero_bits=0) + + async def __aexit__(self, exc_type, exc, tb): + if exc is None and self.fconfig.has_sync_en_dis_cmd: + await self.iface.lower.send_bitstring(vectors.SYNC_DISABLE, do_poll=0, do_zero_bits=0) + +class Psoc1Interface(aobject): + async def __init__(self, interface, logger, voltage, has_xres): + self.lower = interface + self._logger = logger + self._level = logging.DEBUG if self._logger.name == __name__ else logging.TRACE + self._voltage = voltage + self._has_xres = has_xres + + def _log(self, message, *args): + self._logger.log(self._level, "PSoC1: " + message, *args) + + async def _power_cycle(self): + device = self.lower.lower.device + VA = await device.get_voltage('A') + VB = await device.get_voltage('A') + await device.reset_alert("AB") + await device.set_voltage("AB", 0.0) + await device.poll_alert() + await asyncio.sleep(0.100) + await device.set_voltage("A", VA) + await device.set_voltage("B", VB) + await asyncio.sleep(0.001) # TVDDWait, but it's hard to control this precisely + + async def initialize_and_get_silicon_id(self, fconfig): + await self.lower.low_sclk() + await self.lower.assert_xres() + await asyncio.sleep(0.001) + if not self._has_xres: + self._logger.warn("Warning: Using power-cycle method, because no xres pin has been specified. "+ + "The power-cycle method is experimental, and doesn't tightly control timing") + await self._power_cycle() + if fconfig.init_type == 0: + await self.lower.send_bitstring(vectors.INITIALIZE_1, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + await self.lower.send_bitstring(vectors.INITIALIZE_2, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + if self._voltage > 3.6: + await self.lower.send_bitstring(vectors.INITIALIZE_3_5V, do_poll=0) + else: + await self.lower.send_bitstring(vectors.INITIALIZE_3_3V, do_poll=0) + await self.lower.send_bitstring(vectors.ID_SETUP, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + silicon_id = await self.lower.read_bytes(0b11111000, 2) + return vectors.SiliconId(*silicon_id) + elif fconfig.init_type == 1: + await self.lower.send_bitstring(vectors.ID_SETUP_1, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + await self.lower.send_bitstring(vectors.ID_SETUP_2, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + async with SyncEnabledContext(fconfig, self): + silicon_id = (*await self.lower.read_bytes(0b1111_1000, 2), + *await self.lower.read_bytes(0b1111_0011, 1, reg_not_mem=1), + *await self.lower.read_bytes(0b1111_0000, 1, reg_not_mem=1)) + return vectors.SiliconId(*silicon_id) + + async def release_bus(self): + await self.lower.float_sclk() + await self.lower.float_xres() + + async def set_block_num(self, number): + if number < 0 or number > 255: + raise Psoc1Error("Block number out of range\n") + await self.lower.write_bytes(0b11111010, (number,)) + + async def set_bank_num(self, number): + """ + Only supported by RevL-covered devices + """ + assert number >= 0 + assert number <= 3 + await self.lower.send_bitstring( \ + ("1101111011100010000111" + \ + "11011111010000000dd111" + \ + "1101111011100000000111").replace( \ + "dd", f"{number:02b}"), do_poll=0, do_zero_bits=0) + + @staticmethod + def _slowed_down_bitstring(bitstring, padding_between_mnemonics=100): + """Adds 0-bit padding between 22-bit mnemonics. The purpose is to space out the mnemonics in time. + Doing it this way is nicer then implementing another counter in hardware, and also better (quicker) than + waiting for a delay on the host. + """ + return ("0" * padding_between_mnemonics).join([bitstring[i:i+22] for i in range(0, len(bitstring), 22)]) + + async def get_security(self, fconfig): + if fconfig.read_security_type in (1, 2): + await self.lower.send_bitstring(vectors.GET_SECURITY, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + return await self.lower.read_bytes(0b1000_0000, fconfig.secure_bytes_per_bank) + elif fconfig.read_security_type in (3, 4): + await self.lower.send_bitstring(vectors.READ_SECURITY_SETUP, do_poll=0, do_zero_bits=0) + assert fconfig.secure_bytes_per_bank <= (2 ** 7) + for address in range(fconfig.secure_bytes_per_bank): + address_bitstr = f"{address:07b}" + assert len(address_bitstr) == 7 + async with SyncEnabledContext(fconfig, self): + read_security_1_modified = self._slowed_down_bitstring(vectors.READ_SECURITY_1.replace("aaaaaaa", address_bitstr), 100) + # Note: it has been observed that for part CY8C24493, when running at 8MHz, this vector is sensitive to how spaced-out + # its mnemonics are. At 1MHz it runs fine, but at some point above that it starts returning corrupt data. For this reason + # we space it out, so it works correctly at all supported frequencies. Note that a spacing of 15 "0" clock cycles has + # been observed as sufficient (while 14 wasn't), however we've chosen to employ 100 for paranoia reasons. + await self.lower.send_bitstring(read_security_1_modified, do_poll=0, do_zero_bits=0) + if fconfig.read_security_type == 3: + await self.lower.send_bitstring(vectors.READ_SECURITY_2, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + await self.lower.send_bitstring(vectors.READ_SECURITY_3.replace("aaaaaaa", address_bitstr), + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + elif fconfig.read_security_type == 4: + await self.lower.send_bitstring(vectors.READ_SECURITY_2, do_poll=0) + await self.lower.send_bitstring(vectors.READ_SECURITY_3.replace("aaaaaaa", address_bitstr), do_poll=0) + async with SyncEnabledContext(fconfig, self): + return await self.lower.read_bytes(0b1000_0000, fconfig.secure_bytes_per_bank) + else: + raise Psoc1Error("This device doesn't support reading security information") + + async def set_security(self, fconfig, data): + async with SyncEnabledContext(fconfig, self): + if fconfig.has_read_write_setup: + await self.lower.send_bitstring(vectors.READ_WRITE_SETUP, do_poll=0, do_zero_bits=0) + await self.lower.write_bytes(0b1000_0000, data) + await self.lower.send_bitstring(vectors.SET_SECURITY[fconfig.set_security_type], + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + await self.lower.wait_pending() + + async def reset_run(self): + if self._has_xres: + await self.lower.assert_xres() + await asyncio.sleep(0.001) + await self.lower.deassert_xres() + await self.lower.float_xres() + else: + self._logger.info("Power-cycling instead of resetting device because not xres pin has been specified") + await self._power_cycle() + + async def bulk_erase(self, fconfig): + if fconfig.erase_type == 0: + await self.lower.send_bitstring(vectors.BULK_ERASE, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + elif fconfig.erase_type == 1: + await self.lower.send_bitstring(vectors.ERASE, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + await self.lower.wait_pending() + + async def block_erase(self, fconfig, block_num): + async with SyncEnabledContext(fconfig, self): # TODO: keep if I can implement for revI? + await self.set_block_num(block_num) + + await self.lower.send_bitstring(vectors.ERASE_BLOCK, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + await self.lower.wait_pending() + + async def read_block(self, fconfig, block_num): + if fconfig.has_read_write_setup: + await self.lower.send_bitstring(vectors.READ_WRITE_SETUP, do_poll=0, do_zero_bits=0) + + async with SyncEnabledContext(fconfig, self): + await self.set_block_num(block_num) + + await self.lower.send_bitstring(vectors.VERIFY_SETUP[fconfig.verify_setup_type], + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + + # It's been observed that verify_setup sometimes needs a little bit of + # additional time, especially when running the interface at 8MHz, otherwise + # the first byte read will be corrupt: + await self.lower.wait_pending() + await asyncio.sleep(0.001) + + async with SyncEnabledContext(fconfig, self): + if fconfig.has_read_status: + status = (await self.lower.read_bytes(0b1111_1000, 1))[0] + if status == 0x01: + raise Psoc1Error(f"While reading flash block {block_num} has been reported as secured!") + + if fconfig.has_read_write_setup: + await self.lower.send_bitstring(vectors.READ_WRITE_SETUP, do_poll=0, do_zero_bits=0) + + return await self.lower.read_bytes(0b1000_0000, fconfig.bytes_per_block) + + async def write_block(self, fconfig, block_num, data, wait_pending=True): + program_block = vectors.PROGRAM_BLOCK[fconfig.program_block_type] + + if fconfig.has_sync_en_dis_cmd: + await self.lower.send_bitstring(vectors.SYNC_ENABLE, do_poll=0, do_zero_bits=0) + + if fconfig.has_read_write_setup: + await self.lower.send_bitstring(vectors.READ_WRITE_SETUP, do_poll=0, do_zero_bits=0) + + await self.lower.write_bytes(0b1000_0000, data) + + async with SyncEnabledContext(fconfig, self): + await self.set_block_num(block_num) + + await self.lower.send_bitstring(program_block, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + + if fconfig.has_read_status: + async with SyncEnabledContext(fconfig, self): + status = (await self.lower.read_bytes(0b1111_1000, 1))[0] + if status == 0x04: + raise Psoc1Error("Programming failure reported!") + elif status != 0x00: + raise Psoc1Error(f"Unknown programming status code reported: 0x{status:02x}!") + elif wait_pending: + await self.lower.wait_pending() + + async def get_checksum(self, fconfig): + checksum_setup = vectors.CHECKSUM_SETUP[fconfig.checksum_setup_type] + if checksum_setup is None: + return None + await self.lower.send_bitstring(checksum_setup, + needs_single_clock_pulse_for_poll=fconfig.needs_single_clock_pulse_for_poll, + needs_arbitrary_clocks_for_poll=fconfig.needs_arbitrary_clocks_for_poll) + async with SyncEnabledContext(fconfig, self): + return [(await self.lower.read_bytes(0b1111_1001, 1))[0], + (await self.lower.read_bytes(0b1111_1000, 1))[0]] + +class ProgramPsoc1Applet(ISSPHostApplet): + logger = logging.getLogger(__name__) + help = "program Cypress PSoC1 microcontrollers via ISSP" + description = """ + Read and write Cypress PSoC1 microcontrollers via the ISSP interface. + + This applet has been tested and works correctly on the following devices (as of Aug 2024): + + * CY8C24493 + * CY8C24894 + * CY8C21434 + * CY8C27643 + * CY8C29666 + + Per the ISSP spec, the maximum frequency should be 8MHz. + All commands except for reset-run must receive a valid part number to check against. + When connecting to an unknown chip, please be aware that there are two groups of chips supported + by this applet, and their initialization sequence, up to the point where silicon ID can be read + differs. One group is chips described by ISSP specs RevK, RevL (aka AN2026a and AN2026b), + and the other group is described by RevI (aka AN2026c). If you want to use the "get-silicon-id" + command to find out what chip you're connected to you may pass a different part number to that + command as long as it is withing the same group of chips, it should be able to read the Silicon ID. + If reading the silicon ID succeeds, the console log messages will report the correct part number. + """ + + async def run(self, device, args): + issp_iface = await super().run(device, args) + return await Psoc1Interface(issp_iface, self.logger, args.voltage, has_xres = hasattr(args, "pin_xres") and args.pin_xres is not None) + + @classmethod + def add_interact_arguments(cls, parser): + p_operation = parser.add_subparsers(dest="operation", metavar="OPERATION", required=True) + + p_get_silicon_id = p_operation.add_parser( + "get-silicon-id", help="read package-specific silicon ID from the target") + p_get_silicon_id.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number to get silicon ID from") + + p_reset_run = p_operation.add_parser( + "reset-run", help="assert and deassert reset, allowing the chip to boot") + + p_bulk_erase = p_operation.add_parser( + "bulk-erase", help="bulk erase flash memory") + p_bulk_erase.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number to bulk erase") + p_bulk_erase.add_argument( + "-f", "--force", action='store_true', + help="ignore some errors that can be ignored") + + p_block_erase = p_operation.add_parser( + "block-erase", help="erase only some blocks of flash memory") + p_block_erase.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number to bulk erase") + p_block_erase.add_argument( + "--first-bank", metavar="FIRST_BANK", type=int, required=False, + help="First bank index to erase, if the device has banks. (default: %(default)s)") + p_block_erase.add_argument( + "--first-block", metavar="FIRST_BLOCK", type=int, required=True, + help="First block index to erase") + p_block_erase.add_argument( + "--block-count", metavar="BLOCK_COUNT", type=int, required=True, + help="Block count to erase") + + p_read_flash = p_operation.add_parser( + "read-flash", help="read flash memory and save it to a binary file") + p_read_flash.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number we're reading flash from") + p_read_flash.add_argument( + "-f", "--force", action='store_true', + help="ignore some errors that can be ignored") + p_read_flash.add_argument( + "--first-bank", metavar="FIRST_BANK", type=int, default=0, + help="First bank index to read, if the device has banks. (default: %(default)s)") + p_read_flash.add_argument( + "--first-block", metavar="FIRST_BLOCK", type=int, default=0, + help="First block index to read. (default: %(default)s)") + p_read_flash.add_argument( + "--block-count", metavar="BLOCK_COUNT", type=int, required=False, + help="Block count to read (default: read entire device)") + p_read_flash.add_argument( + "file", metavar="FILE", type=argparse.FileType("wb"), + help="save flash binary image to FILE") + + p_get_security = p_operation.add_parser( + "get-security", help="get security status of the device") + p_get_security.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number we're getting security information from") + p_get_security.add_argument( + "-f", "--force", action='store_true', + help="ignore some errors that can be ignored") + + p_program = p_operation.add_parser( + "program", help="bulk erase and program the flash memory") + p_program.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number we're programming") + p_program.add_argument( + "-f", "--force", action='store_true', + help="ignore some errors that can be ignored") + p_program.add_argument( + "--ihex", action='store_true', + help="use intel hex file format, instead of trying to auto-detect it") + p_program.add_argument( + "--bin", action='store_true', + help="use binary file format, instead of trying to auto-detect it") + p_program.add_argument( + "--no-verify", action='store_true', + help="skip verification of data bytes written") + p_program.add_argument( + "--no-write-security", action='store_true', + help="skip writing security information") + p_program.add_argument( + "--no-verify-security", action='store_true', + help="don't verify written security information (security is verified by default only on devices on which we're sure security verification is supported)") + p_program.add_argument( + "--verify-security", action='store_true', + help="verify written security information (security is verified by default only on devices on which we're sure security verification is supported)") + p_program.add_argument( + "--no-check-checksum", action='store_true', + help="skip checking device-calculated checksum") + p_program.add_argument( + "file", metavar="FILE", type=argparse.FileType("rb"), + help="read flash binary image from FILE") + + p_partial_program = p_operation.add_parser( + "partial-program", help="block erase and program parts of the flash memory") + p_partial_program.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number we're programming") + p_partial_program.add_argument( + "--no-block-erase", action='store_true', + help="skip performing of block erase") + p_partial_program.add_argument( + "--no-verify", action='store_true', + help="skip verification of data bytes written") + p_partial_program.add_argument( + "--first-bank", metavar="FIRST_BANK", type=int, required=False, + help="First bank index to program, if the device has banks. (default: %(default)s)") + p_partial_program.add_argument( + "--first-block", metavar="FIRST_BLOCK", type=int, required=True, + help="First block index to program. (default: %(default)s)") + p_partial_program.add_argument( + "file", metavar="FILE", type=argparse.FileType("rb"), + help="read flash binary image from FILE") + + p_verify_full = p_operation.add_parser( + "verify-full", help="fully verify the flash memory content matches given file (only works if security doesn't prevent flash read)") + p_verify_full.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number we're verifying") + p_verify_full.add_argument( + "-f", "--force", action='store_true', + help="ignore some errors that can be ignored") + p_verify_full.add_argument( + "--ihex", action='store_true', + help="use intel hex file format, instead of trying to auto-detect it") + p_verify_full.add_argument( + "--bin", action='store_true', + help="use binary file format, instead of trying to auto-detect it") + p_verify_full.add_argument( + "--no-check-checksum", action='store_true', + help="skip checking device-calculated checksum") + p_verify_full.add_argument( + "file", metavar="FILE", type=argparse.FileType("rb"), + help="read flash binary image from FILE") + + p_verify_checksum = p_operation.add_parser( + "verify-checksum", help="Compare the checksum calculated by the device against the checksum found in the input file.") + p_verify_checksum.add_argument( + "-p", "--part", metavar="PART_NUMBER", type=str, required=True, + help="specify part number we're verifying") + p_verify_checksum.add_argument( + "-f", "--force", action='store_true', + help="ignore some errors that can be ignored") + p_verify_checksum.add_argument( + "--ihex", action='store_true', + help="use intel hex file format, instead of trying to auto-detect it") + p_verify_checksum.add_argument( + "--bin", action='store_true', + help="use binary file format, instead of trying to auto-detect it") + p_verify_checksum.add_argument( + "--no-verify-security", action='store_true', + help="don't verify written security information (security is verified by default only on devices on which we're sure security verification is supported)") + p_verify_checksum.add_argument( + "--verify-security", action='store_true', + help="verify written security information (security is verified by default only on devices on which we're sure security verification is supported)") + p_verify_checksum.add_argument( + "file", metavar="FILE", type=argparse.FileType("rb"), + help="read flash binary image from FILE") + + @staticmethod + def count_flash_bytes(chunks): + cnt_bytes = 0 + for address, chunk in chunks: + if address <= 0x100000: + if address + len(chunk) <= 0x100000: + cnt_bytes += len(chunk) + else: + cnt_bytes += 0x100000 - address + return cnt_bytes + + @staticmethod + def get_bytes(chunks, starting_address, length): + bytes = [] + for address, chunk in chunks: + if chunk: + if (address < starting_address + length) and \ + (address + len(chunk) > starting_address): + if address < starting_address: + chunk = chunk[starting_address - address:] + address = starting_address + if address - starting_address + len(chunk) > length: + chunk = chunk[:length - (address - starting_address)] + if address - starting_address > len(bytes): + bytes.extend([0] * (address - starting_address - len(bytes))) + bytes[address - starting_address: address - starting_address + len(chunk)] = chunk + return bytes + + @staticmethod + def get_flash_bytes(chunks, bytes_per_block): + """ Return contiguous flash bytes derived from chunks returned by input_data() """ + bytes = ProgramPsoc1Applet.get_bytes(chunks, 0, 0x100000) + if len(bytes) % bytes_per_block: + bytes.extend([0x0] * (bytes_per_block - (len(bytes) % bytes_per_block))) + return bytes + + @staticmethod + def get_security_bytes(chunks, secure_bytes_per_bank): + """ Get security bytes from ihex chunks """ + bytes = ProgramPsoc1Applet.get_bytes(chunks, 0x100000, 128) + if len(bytes) % secure_bytes_per_bank: + bytes.extend([0x0] * (secure_bytes_per_bank - (len(bytes) % secure_bytes_per_bank))) + return bytes + + @staticmethod + def get_checksum_bytes(chunks): + """ Get security bytes from ihex chunks """ + bytes = ProgramPsoc1Applet.get_bytes(chunks, 0x200000, 64) + return bytes + + def error_or_warning(self, only_warn, message): + if only_warn: + self.logger.warn(message) + else: + raise Psoc1Error(message + " (You may skip over this error by using -f/--force)") + + async def _get_all_security(self, iface, fconfig): + if fconfig.banks: + block = [] + for current_bank in range(fconfig.banks): + await iface.set_bank_num(current_bank) + block.extend(await iface.get_security(fconfig)) + else: + block = await iface.get_security(fconfig) + return block + + def _check_silicon_id_is_as_expected(self, args, silicon_id): + expected_silicon_id = vectors.get_expected_silicon_id(args.part) + if hasattr(args, 'force'): + force = args.force + else: + force = True + if silicon_id == expected_silicon_id: + self.logger.info(f"Verify Silicon ID Procedure succeeded. Silicon ID is: {str(silicon_id)}") + elif expected_silicon_id is None: + self.error_or_warning(force, f"Unable to Verify Silicon ID {str(silicon_id)}. We don't know what the silicon ID of {args.part} is supposed to be. Please consider contributing the silicon ID.") + else: + self.error_or_warning(force, f"Unexpected silicon ID: got {str(silicon_id)} instead of {str(expected_silicon_id)}") + + async def interact(self, device, args, iface): + if hasattr(args, 'part'): + fconfig = vectors.flash_config[args.part] + if fconfig is None: + raise Psoc1Error(f"Part number {args.part} not recognized.") + if fconfig.banks: + fconfig_bytes = fconfig.bytes_per_block * fconfig.blocks * fconfig.banks + self.logger.info(f"Part number {args.part} has {fconfig_bytes/1024:.1f} KiB flash ({fconfig.bytes_per_block} bytes/block, {fconfig.blocks} blocks/bank, {fconfig.banks} banks).") + else: + fconfig_bytes = fconfig.bytes_per_block * fconfig.blocks + self.logger.info(f"Part number {args.part} has {fconfig_bytes/1024:.1f} KiB flash ({fconfig.bytes_per_block} bytes/block, {fconfig.blocks} blocks).") + else: + assert args.operation in ("reset-run",) # only command that doesn't require part number to be specified + + if args.operation in ('block-erase', 'read-flash', 'partial-program'): + if args.first_block >= fconfig.blocks: + raise Psoc1Error(f"Starting block number is out of range. Maximum is: {fconfig.blocks - 1}") + if fconfig.banks != 0 and args.first_bank is not None and args.first_bank >= fconfig.banks: + raise Psoc1Error(f"Starting bank number is out of range. Maximum is: {fconfig.banks - 1}") + + if args.operation in ('block-erase', 'read-flash') and args.block_count is not None: + if fconfig.banks == 0: + if args.first_block + args.block_count > fconfig.blocks: + raise Psoc1Error(f"Last block of read is out of range, this device only has {fconfig.blocks} blocks") + elif args.first_bank is not None: + last_block_bank = args.first_bank + (args.first_block + args.block_count - 1) // fconfig.blocks + if last_block_bank >= fconfig.banks: + raise Psoc1Error(f"Last block of read is out of range, this device only has {fconfig.banks} banks, but the last block would be in bank {last_block_bank}") + + if args.operation in ('block-erase', 'partial-program'): + if fconfig.banks == 0: + if args.first_bank is not None: + raise Psoc1Error("The specified device doesn't support banks, please leave off --first-bank!") + else: + if args.first_bank is None: + raise Psoc1Error("The specified device supports banks, you must specify --first-bank!") + + if args.operation == "get-silicon-id": + silicon_id = await iface.initialize_and_get_silicon_id(fconfig) + await iface.release_bus() + matching = [] + for sid, part in vectors.silicon_ids: + if sid == silicon_id: + matching.append(part) + if matching: + strmatching = " / ".join([str(m) for m in matching]) + self.logger.info(f"Found known chip: {strmatching} {str(silicon_id)}") + else: + self.logger.warn(f"Found unknown chip: {str(silicon_id)}") + self._check_silicon_id_is_as_expected(args, silicon_id) + elif args.operation == "reset-run": + await iface.reset_run() + elif args.operation == "bulk-erase": + silicon_id = await iface.initialize_and_get_silicon_id(fconfig) + self._check_silicon_id_is_as_expected(args, silicon_id) + await iface.bulk_erase(fconfig) + self.logger.info("Executed Bulk Erase") + await iface.release_bus() + elif args.operation in ("block-erase", "partial-program"): + if args.operation == "partial-program": + databytes = args.file.read() + if len(databytes) % fconfig.bytes_per_block != 0: + databytes += b'\x00' * (fconfig.bytes_per_block - (len(databytes) % fconfig.bytes_per_block)) + self.logger.warn(f"Input file is not aligned to a block size of {fconfig.bytes_per_block} bytes. Padding with zeros...") + block_count = len(databytes) // fconfig.bytes_per_block + + if fconfig.banks == 0: + if args.first_block + block_count > fconfig.blocks: + raise Psoc1Error(f"Trying to program blocks from {args.first_block} to {args.first_block + block_count - 1}, this device only has {fconfig.blocks} blocks!") + elif args.first_bank is not None: + last_block_bank = args.first_bank + (args.first_block + block_count - 1) // fconfig.blocks + if last_block_bank >= fconfig.banks: + raise Psoc1Error(f"Last block we're trying to program is out of range, this device only has {fconfig.banks} banks, but the last block would be in bank {last_block_bank}") + else: + block_count = args.block_count + + silicon_id = await iface.initialize_and_get_silicon_id(fconfig) + self._check_silicon_id_is_as_expected(args, silicon_id) + if args.operation == "partial-program" and args.no_block_erase: + self.logger.info("Skipping block erase, as requested.") + else: + if fconfig.erase_block_type == 0: + if args.operation == "block-erase": + raise Psoc1Error("Specified part number does not support block erase, or block erase is not implemented.") + else: + self.logger.warn("Specified part number does not support block erase, or block erase is not implemented. Continue to attempt programming anyway...") + else: + current_bank = args.first_bank if fconfig.banks != 0 else 0 + current_block = args.first_block + first = True + for block_ofs in range(block_count): + if fconfig.banks != 0: + if current_block == 0 or first: + first = False + await iface.set_bank_num(current_bank) + else: + assert current_bank == 0 + + await iface.block_erase(fconfig, current_block) + + current_block += 1 + if current_block >= fconfig.blocks: + current_block = 0 + current_bank += 1 + self.logger.info("Executed Block Erase") + if args.operation == "partial-program": + current_bank = args.first_bank if fconfig.banks != 0 else 0 + current_block = args.first_block + lbytes = block_count * fconfig.bytes_per_block + first = True + for sbyte in range(0, lbytes, fconfig.bytes_per_block): + print(f"\rProgramming [{sbyte * 100 // lbytes}%]", end="") + if fconfig.banks != 0: + if current_block == 0 or first: + first = False + await iface.set_bank_num(current_bank) + else: + assert current_bank == 0 + block = databytes[sbyte: sbyte + fconfig.bytes_per_block] + await iface.write_block(fconfig, current_block, block) + current_block += 1 + if current_block >= fconfig.blocks: + current_block = 0 + current_bank += 1 + print("\rProgramming [100%]\n", end="") + self.logger.info(f"Programmed {lbytes} bytes.") + + if args.operation == "partial-program" and not args.no_verify: + current_bank = args.first_bank if fconfig.banks != 0 else 0 + current_block = args.first_block + first = True + for sbyte in range(0, lbytes, fconfig.bytes_per_block): + print(f"\rVerifying [{sbyte * 100 // lbytes}%]", end="") + if fconfig.banks != 0 and (current_block == 0 or first): + first = False + await iface.set_bank_num(current_bank) + block = await iface.read_block(fconfig, current_block) + for i in range(len(block)): + if block[i] != databytes[sbyte + i]: + print("") + self.logger.error(f"Error at offset 0x{sbyte+i:04x}, expected: 0x{databytes[sbyte+i]:02x} got: 0x{block[i]:02x}") + raise Psoc1Error(f"Verification failed!") + current_block += 1 + if current_block >= fconfig.blocks: + current_block = 0 + current_bank += 1 + print("\rVerifying [100%]\n", end="") + self.logger.info(f"Verified {lbytes} bytes successfully.") + await iface.release_bus() + elif args.operation == "get-security": + silicon_id = await iface.initialize_and_get_silicon_id(fconfig) + self._check_silicon_id_is_as_expected(args, silicon_id) + if fconfig.read_security_type == 1: + self.logger.warn(f"This part number may or may not support the vectors for reading security information. The matching ISSP specs don't document it, but at least some devices have been found supporting it. Use your own judgement whether you want to trust these results.") + block = await self._get_all_security(iface, fconfig) + await iface.release_bus() + self.logger.info(f"The following security bytes have been read: {', '.join([hex(b) for b in block])}") + self.logger.info("Interpretation:") + + def get_desc_encoded(bank, blockno): + i = bank * fconfig.blocks + blockno + if blockno < fconfig.blocks: + return (block[i // 4] >> ((i % 4) * 2)) & 3 + return None + + def human_readable_desc(enc): + lookup = { + 0: "U = Unprotected", + 1: "F = Factory upgrade (external and internal writes are permitted)", + 2: "R = Field upgrade (internal writes permitted)", + 3: "W = Full protection", + } + return f"{enc:02b}b: {lookup[enc]}" + + def explain_bank(prefix_string, bank): + last_explained = -1 + for i in range(fconfig.blocks): + desc = get_desc_encoded(bank, i) + next_desc = get_desc_encoded(bank, i+1) + if desc != next_desc: + self.logger.info(f" {prefix_string}Blocks {last_explained+1}..{i}: {human_readable_desc(desc)}") + last_explained = i + + if fconfig.banks: + for i in range(fconfig.banks): + explain_bank(f"Bank {i} ", i) + else: + explain_bank("", 0) + + elif args.operation == "read-flash": + silicon_id = await iface.initialize_and_get_silicon_id(fconfig) + self._check_silicon_id_is_as_expected(args, silicon_id) + + current_bank = args.first_bank if fconfig.banks != 0 else 0 + current_block = args.first_block + if args.block_count is None: + total_bytes = fconfig_bytes + else: + total_bytes = args.block_count * fconfig.bytes_per_block + first = True + for sbyte in range(0, total_bytes, fconfig.bytes_per_block): + print(f"\rReading [{sbyte * 100 // total_bytes}%]", end="") + if fconfig.banks != 0: + if current_block == 0 or first: + first = False + await iface.set_bank_num(current_bank) + else: + assert current_bank == 0 + block = await iface.read_block(fconfig, current_block) + args.file.write(bytes(block)) + current_block += 1 + if current_block >= fconfig.blocks: + current_block = 0 + current_bank += 1 + print("\rReading [100%]\n", end="") + self.logger.info(f"Saved {total_bytes} bytes to {args.file.name}.") + self.logger.warning(f"Warning: this only returned valid data, if security was disabled!") + await iface.release_bus() + elif args.operation in ("program", "verify-full", "verify-checksum"): + ext = os.path.splitext(args.file.name)[1].lower() + if args.bin: + format = "bin" + elif args.ihex: + format = "ihex" + else: + if ext in (".hex", ".ihex", ".ihx"): + format = "ihex" + elif ext in (".bin",): + format = "bin" + else: + raise Psoc1Error("Cannot autodetect file format based on file extension. Please specify --ihex or --bin.") + data = input_data(args.file, format) + databytes = self.get_flash_bytes(data, fconfig.bytes_per_block) + lbytes = len(databytes) + count_nonpad_bytes = self.count_flash_bytes(data) + assert count_nonpad_bytes <= lbytes + if lbytes > fconfig_bytes: + self.error_or_warning(args.force, f"Input file contains {lbytes} bytes, which is more than the flash size.") + if count_nonpad_bytes < fconfig_bytes: + self.logger.warn(f"Input file contains {count_nonpad_bytes} bytes (not including padding), which is less than the flash size. This operation will erase the whole flash, so all data will be overwritten") + + silicon_id = await iface.initialize_and_get_silicon_id(fconfig) + self._check_silicon_id_is_as_expected(args, silicon_id) + + if args.operation in ("program",): + self.logger.info("Performing bulk erase.") + await iface.bulk_erase(fconfig) + current_block = 0 + current_bank = 0 + for sbyte in range(0, lbytes, fconfig.bytes_per_block): + print(f"\rProgramming [{sbyte * 100 // lbytes}%]", end="") + if fconfig.banks != 0: + if current_block == 0: + await iface.set_bank_num(current_bank) + else: + assert current_bank == 0 + block = databytes[sbyte: sbyte + fconfig.bytes_per_block] + await iface.write_block(fconfig, current_block, block) + current_block += 1 + if current_block >= fconfig.blocks: + current_block = 0 + current_bank += 1 + print("\rProgramming [100%]\n", end="") + self.logger.info(f"Programmed {lbytes} bytes.") + + if args.operation == "verify-full" or (args.operation == "program" and not args.no_verify): + current_block = 0 + current_bank = 0 + for sbyte in range(0, lbytes, fconfig.bytes_per_block): + print(f"\rVerifying [{sbyte * 100 // lbytes}%]", end="") + if fconfig.banks != 0 and current_block == 0: + await iface.set_bank_num(current_bank) + block = await iface.read_block(fconfig, current_block) + for i in range(len(block)): + if block[i] != databytes[sbyte + i]: + print("") + self.logger.error(f"Error at offset 0x{sbyte+i:04x}, expected: 0x{databytes[sbyte+i]:02x} got: 0x{block[i]:02x}") + if args.operation == "verify-full": + self.logger.info("Note that verification is only expected to work when security is not enabled. Consider running only verify-checksum.") + raise Psoc1Error(f"Verification failed!") + current_block += 1 + if current_block >= fconfig.blocks: + current_block = 0 + current_bank += 1 + print("\rVerifying [100%]\n", end="") + self.logger.info(f"Verified {lbytes} bytes successfully.") + + if args.operation == "program" and not args.no_write_security: + security_bytes = self.get_security_bytes(data, fconfig.secure_bytes_per_bank) + if security_bytes: + self.logger.info(f"Programming security bits.") + if fconfig.banks: + for current_bank in range(fconfig.banks): + await iface.set_bank_num(current_bank) + await iface.set_security(fconfig, security_bytes[current_bank * fconfig.secure_bytes_per_bank: (current_bank + 1) * fconfig.secure_bytes_per_bank]) + else: + await iface.set_security(fconfig, security_bytes) + else: + self.logger.warn(f"No security information found in input file. Not programming security bits.") + + get_security_guaranteed_supported = fconfig.read_security_type >= 2 + if ((args.operation == "program" and not args.no_write_security and (args.verify_security or get_security_guaranteed_supported)) or + (args.operation == "verify-checksum" and (args.verify_security or get_security_guaranteed_supported))): + if not args.no_verify_security: + security_bytes = self.get_security_bytes(data, fconfig.secure_bytes_per_bank) + if security_bytes: + self.logger.info(f"Verifying security bits.") + block = await self._get_all_security(iface, fconfig) + if block == security_bytes[:len(block)]: + self.logger.info(f"Verified {len(block)} bytes of security data.") + else: + raise Psoc1Error(f"Verification of security data failed.") + else: + self.logger.warn(f"No security information found in input file. Not checking security bits.") + + if args.operation == "verify-checksum" or (args.operation in ("program", "verify-full") and not args.no_check_checksum): + checksum_bytes = self.get_checksum_bytes(data) + if checksum_bytes: + if fconfig.banks: + r_checksums = [] + for current_bank in range(fconfig.banks): + await iface.set_bank_num(current_bank) + single_bank_checksum = await iface.get_checksum(fconfig) + r_checksums.append((single_bank_checksum[0] << 8) | single_bank_checksum[1]) + r_checksum = sum(r_checksums) + r_checksum = [(r_checksum >> 8) & 0xff, r_checksum & 0xff] + else: + r_checksum = await iface.get_checksum(fconfig) + if r_checksum is None: + self.logger.warn("Unable to read checksum.") + else: + if r_checksum == checksum_bytes: + self.logger.info(f"Checksum verification successful, checksum is as expected: {checksum_bytes}!") + else: + raise Psoc1Error(f"Checksum error, expected: {checksum_bytes}, got: {r_checksum}") + else: + self.logger.warn(f"No checksum bytes found in input file. Not checking checksum.") + + await iface.release_bus() + + + + diff --git a/software/glasgow/applet/program/psoc1/vectors.py b/software/glasgow/applet/program/psoc1/vectors.py new file mode 100644 index 000000000..81852eca2 --- /dev/null +++ b/software/glasgow/applet/program/psoc1/vectors.py @@ -0,0 +1,756 @@ +# Ref: CY8C21x12, CY8C21x23, CY8C21x34, CY8C23x33, CY8C24x23A, CY8C27x43, CY8CTMG110, CY8CTST110 PSoCĀ® 1 ISSP Programming Specifications Rev. K +# Document Number: 001-13617 (Also known using its old name as "AN2026a") +# Accession: G00090 + +# Ref: CY8C21x45, CY8C22x45, CY8C24x94, CY8C28xxx, CY8C29x66, CY8CTST120, CY8CTMA120, CY8CTMG120, CY7C64215 PSoCĀ® 1 ISSP Programming Specifications Rev. L +# Document Number: 001-15239 (Also known using its old name as "AN2026b") +# Accession: G00091 + +# Ref: CY8C20045, CY8C20055, CY8C20xx6, CY8C20xx6A, CY8C20xx6AS, CY8C20xx6AN, CY8C20xx6L, CY8C20xx6H, CY7C643xx, CY7C604xx, CY8CTST2xx, CY8CTMG2xx, CY8C20xx7, CY8C20xx7S, and CY8C20xx7AN ISSP Programming Specifications Rev. I +# Document Number: 001-57631 (Also known using its old name as "AN2026c") +# Accession: G00091 + +import re +from collections import namedtuple + +INITIALIZE_1 = ( # Matches ISSP Specs: RevK, RevL + "11001010000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000" + + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111101111000000001001111" + + "11011111000000000001111101111111100010010111") + +INITIALIZE_2 = ( # Matches ISSP Specs: RevK, RevL + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111001111101000000001111" + + "11011110000000001101111101111100000000000111" + + "1101111111100010010111") + +INITIALIZE_3_3V = ( # Matches ISSP Specs: RevK, RevL + "11011110111000000001111101111010000000011111" + + "11011110101000000001111101111011000001000111" + + "11011111000010100011111101111100111111000111" + + "11011111010001100001111101111111100010010111" + + "00000000000000000000001101111011100000000111" + + "11011110100000000111111101111010100000000111" + + "11011110110000010001111101111100001100000111" + + "11011111001111010101111101111101000110000111" + + "11011110111000100001111101111111100010010111" + + "00000000000000000000001101111011100000000111" + + "11011110100000000111111101111010100000000111" + + "11011110110000010001111101111100001010001111" + + "11011111001111110011111101111101000110000111" + + "11011111111000100101110000000000000000000000" + + "11011110111000000001111101111010000000011111" + + "11011110101000000001111101111011000001000111" + + "11011111000011000001111101111100111101000111" + + "11011111010001100001111101111011100010000111" + + "11011111111000100101110000000000000000000000") + +INITIALIZE_3_5V = ( # Matches ISSP Specs: RevK, RevL + "11011110111000000001111101111010000000011111" + + "11011110101000000001111101111011000001000111" + + "11011111000010100011111101111100111111100111" + + "11011111010001100001111101111111100010010111" + + "00000000000000000000001101111011100000000111" + + "11011110100000000111111101111010100000000111" + + "11011110110000010001111101111100001100000111" + + "11011111001111010101111101111101000110000111" + + "11011110111000100001111101111111100010010111" + + "00000000000000000000001101111011100000000111" + + "11011110100000000111111101111010100000000111" + + "11011110110000010001111101111100001010001111" + + "11011111001111111011111101111101000110000111" + + "11011111111000100101110000000000000000000000" + + "11011110111000000001111101111010000000011111" + + "11011110101000000001111101111011000001000111" + + "11011111000011000001111101111100111101000111" + + "11011111010001100001111101111011100010000111" + + "11011111111000100101110000000000000000000000") + +ID_SETUP = ( # Matches ISSP Specs: RevK, RevL + "11011110111000100001111101110000000000010111" + + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111001111101000000000111" + + "11011110000000001101111101111100000000000111" + + "1101111111100010010111") + +ID_SETUP_1 = ( # Matches ISSP Specs: RevI (in this code used by init_type=1) + "110010100000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000000000" + + "110111101110001000011111011111110000001001111101110001000000100111" + + "110111000000000001111111011110111000000001111001111100000111010111" + + "100111110010000001111110011111011000100001111101111011100010000111" + + "110111111100000000011111011100010000000001111101110000000000011111" + + "110111101110000000011111011110100000000111111101111010100000000111" + + "110111101100000000011111011110000000010011111101111100000000000111" + + "110111110010100000011111011111010001100001111101111111100010010110") + +SYNC_ENABLE = ( # Matches ISSP Specs: RevI (in this code used by init_type=1) + "110111101110001000011111011111110000001001111101110001000000100111" + + "11011100000000000111111101111011100000000111") + +SYNC_DISABLE = ( # Matches ISSP Specs: RevI (in this code used by init_type=1) + "110111101110001000011111011100010000000001111101111111000000000111" + + "11011100000000000111111101111011100000000111") + +ID_SETUP_2 = ( # Matches ISSP Specs: RevI (in this code used by init_type=1) + "110111101110001000011111011111110000001001111101110001000000100111" + + "110111000000000001111110011111000001110101111001111100100000011111" + + "100111110100000000011111011111110000000001111101110001000000000111" + + "110111000000000001111111011110111000000001111101111010000000011111" + + "110111101010000000011111011110110000000001111101111000000000110111" + + "110111110000000000011111011111001010000001111101111101000110000111" + + "1101111111100010010110") + + +BULK_ERASE = ( # Matches ISSP Specs: RevK, RevL + "10011111100000101011111001111111001010110111" + + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111101111000000000101111" + + "11011111000000000001111101111111100010010111") + +ERASE = ( # Matches ISSP Specs: RevI (in this code used by erase_type=1) + "110111101110001000011111011111110000001001111101110001000000100111" + + "110111000000000001111110011111000001110101111001111100100001011111" + + "110111111100000000011111011100010000000001111101110000000000011111" + + "110111101110000000011111011110000000001011111101111010000000011111" + + "110111101010000000011111011110110000010001111101111100000000000111" + + "110111110010011000011111011111010010000001111101111111100010010110") + +class PartNumberMatcher: + def __init__(self, *descriptors): + assert len(descriptors) >= 1 + self.descriptors = tuple(descriptors) + sub_regexes = [] + for descriptor in descriptors: + rstr = descriptor.replace("x", ".") + if "-" not in rstr: + rstr += "(?:-.*)?" + else: + rstr += ".*" + rstr = "(?:" + rstr + ")" + sub_regexes.append(rstr) + self.regex = re.compile("|".join(sub_regexes)) + + def __str__(self): + return " / ".join(self.descriptors) + + def __repr__(self): + args = ", ".join([repr(descriptor) for descriptor in self.descriptors]) + return f"PartNumberMatcher({args})" + + def __eq__(self, other): + return self.descriptors == other.descriptors + + def __ne__(self, other): + return self.descriptors != other.descriptors + + def __hash__(self): + return hash(("PartNumberMatcher", self.descriptors)) + + def fullmatch(self, part_to_match): + return self.regex.fullmatch(part_to_match) + +class SiliconId: + def __init__(self, *id_bytes): + assert len(id_bytes) >= 1 + self.id_bytes = id_bytes + + def __str__(self): + return "(" + (", ".join([f"0x{idbyte:02x}" for idbyte in self.id_bytes])) + ")" + + def __repr__(self): + return "SiliconId(" + (", ".join([f"0x{idbyte:02x}" for idbyte in self.id_bytes])) + ")" + + def __eq__(self, other): + if isinstance(other, SiliconId): + return self.id_bytes == other.id_bytes + return self.id_bytes == other + + def __ne__(self, other): + if isinstance(other, SiliconId): + return self.id_bytes != other.id_bytes + return self.id_bytes != other + + def __hash__(self): + return hash(("SiliconId", self.id_bytes)) + +silicon_ids = { + (SiliconId(0b0000_0000, 0b0000_1001), PartNumberMatcher("CY8C27143")), # RevK + (SiliconId(0b0000_0000, 0b0000_1010), PartNumberMatcher("CY8C27243")), # RevK + (SiliconId(0b0000_0000, 0b0000_1011), PartNumberMatcher("CY8C27443")), # RevK + (SiliconId(0b0000_0000, 0b0000_1100), PartNumberMatcher("CY8C27543")), # RevK + (SiliconId(0b0000_0000, 0b0000_1101), PartNumberMatcher("CY8C27643")), # RevK + (SiliconId(0b0000_0000, 0b0011_0010), PartNumberMatcher("CY8C24123A")), # RevK + (SiliconId(0b0000_0000, 0b0011_0011), PartNumberMatcher("CY8C24223A")), # RevK + (SiliconId(0b0000_0000, 0b0011_0100), PartNumberMatcher("CY8C24423A")), # RevK + (SiliconId(0b0000_1000, 0b1011_0001), PartNumberMatcher("CY8C23533")), # RevK + (SiliconId(0b0000_1000, 0b1011_0000), PartNumberMatcher("CY8C23433")), # RevK + (SiliconId(0b0000_1000, 0b1011_0010), PartNumberMatcher("CY8C23033")), # RevK + (SiliconId(0b0000_0000, 0b0001_0111), PartNumberMatcher("CY8C21123")), # RevK + (SiliconId(0b0000_0000, 0b0001_1000), PartNumberMatcher("CY8C21223")), # RevK + (SiliconId(0b0000_0000, 0b0001_1001), PartNumberMatcher("CY8C21323")), # RevK + (SiliconId(0b0000_0000, 0b0011_0110), PartNumberMatcher("CY8C21234")), # RevK + (SiliconId(0b0000_1000, 0b0011_0111), PartNumberMatcher("CY8C21312")), # RevK + (SiliconId(0b0000_0000, 0b0011_0111), PartNumberMatcher("CY8C21334")), # RevK + (SiliconId(0b0000_0000, 0b0011_0111), PartNumberMatcher("CY8C21334W")), # RevK + (SiliconId(0b0000_0000, 0b0011_1000), PartNumberMatcher("CY8C21434")), # RevK + (SiliconId(0b0000_1000, 0b0100_0000), PartNumberMatcher("CY8C21512")), # RevK + (SiliconId(0b0000_0000, 0b0100_0000), PartNumberMatcher("CY8C21534")), # RevK + (SiliconId(0b0000_0000, 0b0100_0000), PartNumberMatcher("CY8C21534W")), # RevK + (SiliconId(0b0000_0000, 0b0100_1001), PartNumberMatcher("CY8C21634")), # RevK + (SiliconId(0b0000_0111, 0b0011_1000), PartNumberMatcher("CY8CTMG110-32LTXI")), # RevK + (SiliconId(0b0000_0111, 0b0011_1001), PartNumberMatcher("CY8CTMG110-00PVXI")), # RevK + (SiliconId(0b0000_0110, 0b0011_1000), PartNumberMatcher("CY8CTST110-32LTXI")), # RevK + (SiliconId(0b0000_0110, 0b0011_1001), PartNumberMatcher("CY8CTST110-00PVXI")), # RevK + + (SiliconId(0b0000_0000, 0b1101_0011), PartNumberMatcher("CY8C21345")), # RevL + (SiliconId(0b0000_1000, 0b1101_1010), PartNumberMatcher("CY8C21645-24xxXA")), # RevL + (SiliconId(0b0000_1000, 0b1101_1001), PartNumberMatcher("CY8C21645-12xxXE")), # RevL + (SiliconId(0b0000_0000, 0b1101_0001), PartNumberMatcher("CY8C22345")), # RevL + (SiliconId(0b0000_1100, 0b1101_0001), PartNumberMatcher("CY8C22345H-24xxXA")), # RevL + (SiliconId(0b0000_0000, 0b1101_0010), PartNumberMatcher("CY8C22545-24xxXI")), # RevL + (SiliconId(0b0000_0000, 0b1101_1010), PartNumberMatcher("CY8C22645-24xxXA")), # RevL + (SiliconId(0b0000_0000, 0b1101_1001), PartNumberMatcher("CY8C22645-12xxXE")), # RevL + (SiliconId(0b0000_0000, 0b0001_1101), PartNumberMatcher("CY8C24794")), # RevL + (SiliconId(0b0000_0000, 0b0001_1111), PartNumberMatcher("CY8C24894")), # RevL + (SiliconId(0b0000_0000, 0b0101_1001), PartNumberMatcher("CY8C24994")), # RevL + (SiliconId(0b0000_0000, 0b1110_0000), PartNumberMatcher("CY8C28000")), # RevL + (SiliconId(0b0000_0000, 0b1110_0001), PartNumberMatcher("CY8C28445")), # RevL + (SiliconId(0b0000_0000, 0b1110_0010), PartNumberMatcher("CY8C28545")), # RevL + (SiliconId(0b0000_0000, 0b1110_0011), PartNumberMatcher("CY8C28645")), # RevL + (SiliconId(0b0000_0000, 0b1110_0100), PartNumberMatcher("CY8C28243")), # RevL + (SiliconId(0b0000_0000, 0b1110_1010), PartNumberMatcher("CY8C28643")), # RevL + (SiliconId(0b0000_0000, 0b1110_0101), PartNumberMatcher("CY8C28452")), # RevL + (SiliconId(0b0000_0000, 0b1110_0110), PartNumberMatcher("CY8C28413")), # RevL + (SiliconId(0b0000_0000, 0b1110_1011), PartNumberMatcher("CY8C28513")), # RevL + (SiliconId(0b0000_0000, 0b1110_0111), PartNumberMatcher("CY8C28433")), # RevL + (SiliconId(0b0000_0000, 0b1110_1100), PartNumberMatcher("CY8C28533")), # RevL + (SiliconId(0b0000_0000, 0b1110_1000), PartNumberMatcher("CY8C28403")), # RevL + (SiliconId(0b0000_0000, 0b1110_1001), PartNumberMatcher("CY8C28623")), # RevL + (SiliconId(0b0000_0000, 0b0010_1010), PartNumberMatcher("CY8C29466")), # RevL + (SiliconId(0b0000_0000, 0b0010_1011), PartNumberMatcher("CY8C29566")), # RevL + (SiliconId(0b0000_0000, 0b0010_1100), PartNumberMatcher("CY8C29666")), # RevL + (SiliconId(0b0000_0000, 0b0010_1101), PartNumberMatcher("CY8C29866")), # RevL + (SiliconId(0b0000_0110, 0b0001_1111), PartNumberMatcher("CY8CTST120-56")), # RevL + (SiliconId(0b0000_0110, 0b0001_1011), PartNumberMatcher("CY8CTST120-00")), # RevL + (SiliconId(0b0000_0101, 0b0001_1111), PartNumberMatcher("CY8CTMA120-56")), # RevL + (SiliconId(0b0000_0101, 0b0001_1011), PartNumberMatcher("CY8CTMA120-00")), # RevL + (SiliconId(0b0000_0101, 0b0101_1001), PartNumberMatcher("CY8CTMA120-100")), # RevL + (SiliconId(0b0000_0111, 0b0001_1111), PartNumberMatcher("CY8CTMG120-56")), # RevL + (SiliconId(0b0000_0111, 0b0001_1011), PartNumberMatcher("CY8CTMG120-00")), # RevL + (SiliconId(0b0000_0000, 0b0001_1110), PartNumberMatcher("CY7C64215-28")), # RevL + (SiliconId(0b0000_0000, 0b0101_0011), PartNumberMatcher("CY7C64215-56")), # RevL + + (SiliconId(0b0000_0000, 0b1001_1010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20066", "CY8C20066A")), # RevI + (SiliconId(0b0000_0000, 0b1011_0011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20236", "CY8C20236A")), # RevI + (SiliconId(0b0000_0000, 0b1010_1010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20246", "CY8C20246A")), # RevI + (SiliconId(0b0000_0000, 0b1001_0110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20266", "CY8C20266A")), # RevI + (SiliconId(0b0000_0000, 0b1011_0100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20336", "CY8C20336A")), # RevI + (SiliconId(0b0000_1100, 0b1011_0100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20336H-24LQXI")), # RevI + (SiliconId(0b0000_0000, 0b1010_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20346", "CY8C20346A")), # RevI + (SiliconId(0b0000_1100, 0b1010_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20346H-24LQXI")), # RevI + (SiliconId(0b0000_0000, 0b1001_0111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20366", "CY8C20366A")), # RevI + (SiliconId(0b0000_0000, 0b1010_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20396", "CY8C20396A")), # RevI + (SiliconId(0b0000_0000, 0b1011_0101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20436", "CY8C20436A")), # RevI + (SiliconId(0b0000_0000, 0b1010_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20446 CY8C20446A")), # RevI + (SiliconId(0b0000_1100, 0b1010_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20446H-24LQXI")), # RevI + (SiliconId(0b0000_0000, 0b1001_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20466", "CY8C20466A")), # RevI + (SiliconId(0b0000_0000, 0b1011_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20496", "CY8C20496A")), # RevI + (SiliconId(0b0000_0000, 0b1011_1001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20536", "CY8C20536A")), # RevI + (SiliconId(0b0000_0000, 0b1010_1110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20546", "CY8C20546A")), # RevI + (SiliconId(0b0000_0000, 0b1001_1001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20566", "CY8C20566A")), # RevI + (SiliconId(0b0000_0000, 0b1011_1010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20636", "CY8C20636A")), # RevI + (SiliconId(0b0000_0000, 0b1011_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20646", "CY8C20646A")), # RevI + (SiliconId(0b0000_0000, 0b1001_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20666", "CY8C20666A")), # RevI + (SiliconId(0b0000_0000, 0b1011_1110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20746A")), # RevI + (SiliconId(0b0000_0000, 0b1011_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20766A")), # RevI + (SiliconId(0b0000_0000, 0b1010_1011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C60400")), # RevI + (SiliconId(0b0000_0000, 0b1011_0110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C60413")), # RevI + (SiliconId(0b0000_0000, 0b1010_0111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C60445")), # RevI + (SiliconId(0b0000_0000, 0b1010_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C60455")), # RevI + (SiliconId(0b0000_0000, 0b1010_1001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C60456")), # RevI + (SiliconId(0b0000_0000, 0b1010_0110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C64300")), # RevI + (SiliconId(0b0000_0000, 0b1010_0000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C64315")), # RevI + (SiliconId(0b0000_0000, 0b1010_0001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C64316")), # RevI + (SiliconId(0b0000_0000, 0b1011_0111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C64343")), # RevI + (SiliconId(0b0000_0000, 0b1010_0010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C64345")), # RevI + (SiliconId(0b0000_0000, 0b1010_0011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C64355")), # RevI + (SiliconId(0b0000_0000, 0b1010_0100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY7C64356")), # RevI + (SiliconId(0b0000_0111, 0b1001_1010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG200-00LTXI", "CY8CTMG200A-00LTXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG200-16LGXI", "CY8CTMG200A-16LGXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG200-24LQXI", "CY8CTMG200A-24LQXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_1110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG200-32LQXI", "CY8CTMG200A-32LQXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG200-48LTXI", "CY8CTMG200A-48LTXI")), # RevI + (SiliconId(0b0000_1111, 0b0110_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG200AH-48LTXI")), # RevI + (SiliconId(0b0000_0111, 0b1101_0100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG240-LQI-01")), # RevI + (SiliconId(0b0000_0111, 0b1101_0101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG240-LTI-01")), # RevI + (SiliconId(0b0000_0111, 0b1011_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CP8CTMG240-FNC-01")), # RevI + (SiliconId(0b0000_0111, 0b0001_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG200-48PVXI", "CY8CTMG200A-48PVXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST200-16LGXI", "CY8CTST200A-16LGXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST200-24LQXI", "CY8CTST200A-24LQXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_1110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST200-32LQXI", "CY8CTST200A-32LQXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST200-48LTXI", "CY8CTST200A-48LTXI")), # RevI + (SiliconId(0b0000_0110, 0b0001_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST200-48PVXI", "CY8CTST200A-48PVXI")), # RevI + (SiliconId(0b0000_0110, 0b1101_0110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST241-LQI-01")), # RevI + (SiliconId(0b0000_0110, 0b1101_0111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST241-LTI-01")), # RevI + (SiliconId(0b0000_0110, 0b1011_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CP8CTST241-FNC-01")), # RevI + (SiliconId(0b0000_0111, 0b0110_0001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG201-16LQXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_0010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG201-24LQXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_0011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG201-32LQXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_0100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG201-48LTXI", "CY8CTMG201A-48LTXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_0101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG201-48PVXI", "CY8CTMG201A-48PVXI")), # RevI + (SiliconId(0b0000_0110, 0b1011_1110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CP8CTST242-FNC-01")), # RevI + (SiliconId(0b0000_1011, 0b1010_1010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20246AS-24LKXI")), # RevI + (SiliconId(0b0000_0000, 0b1101_1011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20336AN-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b1010_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20346AS-24LQXI")), # RevI + (SiliconId(0b0000_0000, 0b1101_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20436AN-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b1010_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20446AS-24LQXI")), # RevI + (SiliconId(0b0000_0011, 0b1010_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20446L-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b1001_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20466AS-24LQXI")), # RevI + (SiliconId(0b0000_0011, 0b1001_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20466L-24LQXI")), # RevI + (SiliconId(0b0000_0011, 0b1011_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20496L-24LQXI")), # RevI + (SiliconId(0b0000_0011, 0b1010_1110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20546L-24PVXI")), # RevI + (SiliconId(0b0000_0011, 0b1001_1001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20566L-24PVXI")), # RevI + (SiliconId(0b0000_0000, 0b1101_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20636AN-24LTXI")), # RevI + (SiliconId(0b0000_1011, 0b1011_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20646AS-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b1011_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20646AS-24LTXI")), # RevI + (SiliconId(0b0000_0011, 0b1011_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20646L-24LTXI", "CY8C20646L-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b1001_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20666AS-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b1001_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20666AS-24LTXI")), # RevI + (SiliconId(0b0000_0011, 0b1001_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20666L-24LTXI CY8C20666L-24LQXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_0001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG201-16LGXI")), # RevI + (SiliconId(0b0000_0111, 0b0110_0010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTMG201-24LQXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_0001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST201-16LGXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_0010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST201-24LQXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_0011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST201-32LQXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_0100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST201-48LTXI")), # RevI + (SiliconId(0b0000_0110, 0b0110_0101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST201-48PVXI")), # RevI + (SiliconId(0b0000_0110, 0b1001_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST242-LQI-01")), # RevI + (SiliconId(0b0000_0110, 0b1001_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8CTST242-LTI-01")), # RevI + (SiliconId(0b0000_0001, 0b0100_0010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20237-24LKXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_0000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20237-24SXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_0011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20247-24LKXI")), # RevI + (SiliconId(0b0000_1011, 0b0100_0011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20247S-24LKXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_0001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20247-24SXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_0100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20337-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0101_0000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20337AN-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_0101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20347-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b0100_0101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20347S-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_0110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20437-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0101_0001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20437AN-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_0111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20447-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b0100_0111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20447S-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20467-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b0100_1000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20467S-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_1001, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20637-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_1111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20637AN-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_1010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20647-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b0100_1010, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20647S-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_1011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20667-24LQXI")), # RevI + (SiliconId(0b0000_1011, 0b0100_1011, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20667S-24LQXI")), # RevI + (SiliconId(0b0000_0001, 0b0100_1100, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20747-24FDXC")), # RevI + (SiliconId(0b0000_0001, 0b0100_1101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20767-24FDXC")), # RevI + (SiliconId(0b0000_0001, 0b1010_0101, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20045-24LKXI")), # RevI + (SiliconId(0b0000_0001, 0b0111_0000, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20055-24LKXI")), # RevI + (SiliconId(0b0000_0001, 0b1100_1110, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C20055-24SXI")), # RevI + + (SiliconId(0b0000_0000, 0b1111_0111, 0b0101_0010, 0b0010_0001), PartNumberMatcher("CY8C24493")), # Not documented, but found experimentally +} + +def get_expected_silicon_id(lookup_part_number): + for sid, part in silicon_ids: + if part.fullmatch(lookup_part_number): + return sid + return None + +class RegexLookup: + def __init__(self, dictionary): + self.dictionary = dictionary + + def __getitem__(self, item): + for r in self.dictionary: + if r.fullmatch(item): + return self.dictionary[r] + return None + +PROGRAM_BLOCK = { + 0: # RevK, RevL + "10011111100010101001111001111111001010110111" + + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111101111000000000010111" + + "11011111000000000001111101111111100010010111", + 1: # RevK + "10011111100000101011111001111111001010110111" + + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111101111000000000010111" + + "11011111000000000001111101111111100010010111", + 2: # PROGRAM-AND-VERIFY , matches ISSP Specs: RevI + "110111101110001000011111011111110000001001111101110001000000100111" + + "110111000000000001111110011111000001110101111001111100100000011111" + + "100111110111000000011111011111110000000001111101110001000000000111" + + "110111000000000001111111011110111000000001111101101010000000001111" + + "110111101000000001111111011110101000000001111101111011000000000111" + + "110111100000000101011111011111000000000001111101111100101000000111" + + "11011111010001100001111101111111100010010110", +} + +# Notes: +# * banks=0 implies it isn't documented as having the concept of banks, so don't try to set bank number +# * read_security_type=0 implies that reading security is definitely not supported so print an error +# * read_security_type=1 implies that reading security (with same as type 2 commands) may or may not be +# supported, so perhaps print a warning. Some devices documented in the RevK ISSP spec have +# been found as supporting this command, even though it's not documented there. +# * read_security_type=2 use the VERIFY-SECURE-SETUP vectors documented in RevL ISSP spec. +# * read_security_type=3 use the various security-reading vectors documented in RevI ISSP specs. +# * read_security_type=4 uses the same security-reading vectors documented in RevI ISSP specs, +# however it doesn't do wait-and-poll after READ-SECURITY-2/READ-SECURITY-3, like Figure 2-12 +# describes. Doing a WAIT-AND_POLL has been observed to not work correctly on a CY8C24493 device. +# However reading security values does work correctly if we skip polling for SDATA. We're going +# to assume that the spec has a mistake in it in this regard, and all devices covered by the +# spec behave like this. If you find a device that is covered by the RevI spec, that has issues +# reading the security, bytes, please consider trying read_security_type=3, and update this +# comment. in the meantime all RevI devices will be configured with trying read_security_type=4 +# * needs_single_clock_pulse_for_poll = 1 means exactly 1 SCLK cycle is needed after a vector that requires +# polling (and after SDATA has settled low), to allow SDATA to go high. +# * needs_arbitrary_clocks_for_poll = 1 means we are following the textual specification of WAIT-AND-POLL +# from RevI specs, which says clocking is necessary for SDATA to go high. Setting this bit high +# will generate as many clock pulses as necessary, and it will stop generating clock pulses as soon +# as SDATA goes high. +# * needs_single_clock_pulse_for_poll = 0, and needs_arbitrary_clocks_for_poll = 0 at the same time appears +# to match Figure 2-4 from RevI specs, however this combination has been observed to not work +# correctly on a CY8C24493 device. Since the spec is self-contradictory we are assuming there is a +# mistake in the spec, and we are configuring all RevI-covered devices with +# needs_arbitrary_clocks_for_poll = 1. If you are having initialization issues with RevI +# devices, please consider playing with these settings. +# * secure_bytes_per_bank refers to the number of bytes we should read/write, not to the number of bytes +# containing useful security data. +# * erase_block_type=0 means not supported/not implemented +# * erase_block_type=1 means use the vector described in RevK/RevL specs + +FlashConfig = namedtuple("FlashConfig", + " ".join(reversed([ + 'erase_block_type', #----------------------------------------------------------------------------------+ + 'read_security_type', #-----------------------------------------------------------------------------+ | + 'set_security_type', #---------------------------------------------------------------------------+ | | + 'verify_setup_type', #------------------------------------------------------------------------+ | | | + 'has_read_status', #-----------------------------------------------------------------------+ | | | | + 'has_read_write_setup', #---------------------------------------------------------------+ | | | | | + 'erase_type', #----------------------------------------------------------------------+ | | | | | | + 'has_sync_en_dis_cmd', #----------------------------------------------------------+ | | | | | | | + 'init_type', #-----------------------------------------------------------------+ | | | | | | | | + 'needs_arbitrary_clocks_for_poll', #----------------------------------------+ | | | | | | | | | + 'needs_single_clock_pulse_for_poll', #-----------------------------------+ | | | | | | | | | | + 'checksum_setup_type', #----------------------------------------------+ | | | | | | | | | | | + 'program_block_type', #--------------------------------------------+ | | | | | | | | | | | | + 'secure_bytes_per_bank', #--------------------------------------+ | | | | | | | | | | | | | + 'banks', #--------------------------------------------------+ | | | | | | | | | | | | | | + 'blocks', #----------------------------------------------+ | | | | | | | | | | | | | | | + 'bytes_per_block', #--------------------------------+ | | | | | | | | | | | | | | | | + ]))) # | | | | | | | | | | | | | | | | | + # | | | | | | | | | | | | | | | | | +flash_config = RegexLookup({ # | | | | | | | V | | | | | | | | | + PartNumberMatcher("CY8C21x12"): FlashConfig( 64, 128, 0, 64, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 512 bytes SRAM + PartNumberMatcher("CY8C21x23"): FlashConfig( 64, 64, 0, 64, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 256 bytes SRAM + PartNumberMatcher("CY8C21x34"): FlashConfig( 64, 128, 0, 64, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 512 bytes SRAM + PartNumberMatcher("CY8C23x33"): FlashConfig( 64, 128, 0, 64, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 256 bytes SRAM + + PartNumberMatcher("CY8C24x23A"): FlashConfig( 64, 64, 0, 64, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 256 bytes SRAM + PartNumberMatcher("CY8C27x43"): FlashConfig( 64, 256, 0, 64, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 256 bytes SRAM + # TODO the assimetry between program_block_type/checksum_setup_type for the above two devices is strange. I think I have one of + # these two devices, check if it works okay. If it does, remove comment. + + PartNumberMatcher("CY8CTMG110"): FlashConfig( 64, 128, 0, 64, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 512 bytes SRAM + PartNumberMatcher("CY8CTST110"): FlashConfig( 64, 128, 0, 64, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1), # RevK # 512 bytes SRAM + + PartNumberMatcher("CY8C22x45"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY8C22x45H"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY8C24x94"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY8C28xxx"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY8CTST120"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY8CTMA120"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY8CTMG120"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY7C64215"): FlashConfig( 64, 128, 2, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 1K SRAM + PartNumberMatcher("CY8C29x66"): FlashConfig( 64, 128, 4, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 2K SRAM + PartNumberMatcher("CY8C21x45"): FlashConfig( 64, 128, 1, 32, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1), # RevL # 512 bytes SRAM + + PartNumberMatcher("CY7C60400"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C60413"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C60445"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C60455"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C60456"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64300"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64313"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64315"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64316"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64343"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64345"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64355"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY7C64356"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20045-24LKXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20055-24LKXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20055-24SXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20066"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20236-24LKXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20246-24LKXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20266-24LKXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20336-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20336H-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20346-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20346H-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20366-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20396-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20436-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20446-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20446H-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20466-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20496-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20536-24PVXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20546-24PVXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20566-24PVXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20636-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20646-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20666-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200-16LGXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200-32LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200-48LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200-48PVXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200-00LGXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200-16LGXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200-32LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200-48LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200-48PVXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG201-32LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG201-48LTXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG201-48PVXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20066A"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20236A-24LKXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20246A-24LKXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20246AS-24LKXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20266A-24LKXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20336A-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20336AN-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20346A-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20346AS-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20366A-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20396A-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20436A-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20436AN-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20446A-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20446AS-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20446L-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20466A-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20466AS-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20466L-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20496A-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20496L-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20536A-24PVXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20546A-24PVXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20546L-24PVXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20566A-24PVXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20566L-24PVXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20636A-24LTXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20636A-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20636AN-24LTXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20646A-24LTXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20646A-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20646AS-24LTXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20646L-24LTXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20646L-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20666A-24LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20666A-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20666AS-24LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20666L-24LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20666L-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20746A-24FDXC"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20766A-24FDXC"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200A-16LGXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200A-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200A-32LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200A-48LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST200A-48PVXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST241-LQI-01"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTST241-LTI-01"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CP8CTST241-FNC-01"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200A-00LGXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200A-16LGXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200A-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200A-32LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200A-48LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200AH-48LTXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG200A-48PVXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG201A-32LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG201A-48LTXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG201A-48PVXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG240-LQI-01"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8CTMG240-LTI-01"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CP8CTMG240-FNC-01"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20237-24SXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20247-24SXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20237-24LKXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20247-24LKXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20247S-24LKXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20337-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20337AN-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20347-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20347S-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20437-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20437AN-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20447-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20447S-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20467-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20467S-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20637-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20637AN-24LQXI"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20647-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20647S-24LQXI"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20667-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20667S-24LQXI"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20747-24FDXC"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C20767-24FDXC"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + + # The CY8C24X93 datasheet points towards the AN2026C document, aka RevI, so this device must use the same + # vectors as other devices from RevI. Flash configuration has been deduced from the TRM: + PartNumberMatcher("CY8C24493"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C24693"): FlashConfig(128, 256, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C24593"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C24393"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C24293"): FlashConfig(128, 128, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C24193"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI + PartNumberMatcher("CY8C24093"): FlashConfig(128, 64, 0, 64, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0), # RevI +}) + +VERIFY_SETUP = { + 0: ( # Matches ISSP Specs: RevK, RevL + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111101111000000000001111" + + "11011111000000000001111101111111100010010111"), + 1: ( # RevI + "110111101110001000011111011111110000001001111101110001000000100111" + + "110111000000000001111110011111000001110101111001111100100000011111" + + "100111110111000000011111011111110000000001111101110001000000000111" + + "110111000000000001111111011110111000000001111101101010100000001111" + + "110111101000000001111111011110101000000001111101111011000000000111" + + "110111100000000000111111011111000000000001111101111100101000000111" + + "11011111010001100001111101111111100010010110"), +} + +# Documented in RevL ISSP Specification v13_00 only, +# not clear if all devices support it: +GET_SECURITY = ( # a.k.a. "VERIFY-SECURE-SETUP" + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "10011111101000000001111001111111100000000111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111101111000000010000111" + + "11011111000000000001111101111111100010010111") + +READ_SECURITY_SETUP = ( # RevI + "110111101110001000011111011000001000100001111101100001000010000111" + + "1101111011100000000111") + +READ_SECURITY_1 = ( # RevI + "110111101110001000011111011100101000011101111101110010100000000111" + + "110111001010aaaaaaa1111101110010100000000111") + +READ_SECURITY_2 = ( # RevI + "110111101110000000011111011110100000000111111101111010100000000111" + + "110111101100000000011111011111000010111011111101111100111100101111" + + "110111110100011000011111011110111000100001111101111111100010010110") + +READ_SECURITY_3 = ( # RevI + "110111101110000000011111011110100000000111111101111010100000000111" + + "11011110110000000001111101111100001010011111110111110011aaaaaaa111" + + "11011111010001100001111101111111100010010110") + +SET_SECURITY = { # a.k.a. "SECURE" + 0: ( # Matches ISSP Specs: RevK, RevL + "10011111100010101001111001111111001010110111" + + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111101111000000000100111" + + "11011111000000000001111101111111100010010111"), + 1: ( # RevI + "110111101110001000011111011111110000001001111101110001000000100111" + + "110111000000000001111110011111000001110101111001111100100000011111" + + "100111110111000000011111011111110000000001111101110001000000000111" + + "110111000000000001111111011110111000000001111101101010000000001111" + + "110111101000000001111111011110101000000001111101111011000000000111" + + "110111100000000010011111011111000000000001111101111100101000000111" + + "11011111010001100001111101111111100010010110"), +} + +CHECKSUM_SETUP = { + 0: # RevK + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111001111101000000000111" + + "11011110000000001111111101111100000000000111" + + "1101111111100010010111", + 1: # RevK + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011100000001111101111100100110000111" + + "11011111010010000001111001111101001000000111" + + "11011110000000001111111101111100000000000111" + + "1101111111100010010111", + 2: # RevL + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "10011111011000000001111101111100100110000111" + + "11011111010010000001111001111101010000000111" + + "11011110000000001111111101111100000000000111" + + "1101111111100010010111", + 3: # RevI + "110111101110001000011111011111110000001001111101110001000000100111" + + "110111000000000001111110011111000001110101111001111100100000011111" + + "100111110100000000011111011111110000000001111101110001000000000111" + + "110111000000000001111111011110111000000001111101111010000000011111" + + "110111101010000000011111011110110000000001111101111000000000111111" + + "110111110000000000011111011111001010000001111101111101000110000111" + + "1101111111100010010110", +} + +READ_WRITE_SETUP = ( # RevI + "110111101111000000011111011110000000000001111101101000000000001111") + +ERASE_BLOCK = ( # RevK, RevL + "10011111100010101001111001111111001010110111" + + "11011110111000000001111101111011000000000111" + + "10011111000001110101111001111100100000011111" + + "11011110101000000001111101111010000000011111" + + "11011111001001100001111101111101001000000111" + + "11011110000000000111111101111100000000000111" + "1101111111100010010111") diff --git a/software/pyproject.toml b/software/pyproject.toml index 5a7bec9e2..e42bc35d6 100644 --- a/software/pyproject.toml +++ b/software/pyproject.toml @@ -85,6 +85,7 @@ spi-controller = "glasgow.applet.interface.spi_controller:SPIControllerApplet" spi-flashrom = "glasgow.applet.interface.spi_flashrom:SPIFlashromApplet" i2c-initiator = "glasgow.applet.interface.i2c_initiator:I2CInitiatorApplet" i2c-target = "glasgow.applet.interface.i2c_target:I2CTargetApplet" +issp-host = "glasgow.applet.interface.issp_host:ISSPHostApplet" jtag-pinout = "glasgow.applet.interface.jtag_pinout:JTAGPinoutApplet" jtag-probe = "glasgow.applet.interface.jtag_probe:JTAGProbeApplet" jtag-openocd = "glasgow.applet.interface.jtag_openocd:JTAGOpenOCDApplet" @@ -110,6 +111,7 @@ program-ice40-sram = "glasgow.applet.program.ice40_sram:ProgramICE40SRAMApplet" program-m16c = "glasgow.applet.program.m16c:ProgramM16CApplet" program-mec16xx = "glasgow.applet.program.mec16xx:ProgramMEC16xxApplet" program-nrf24lx1 = "glasgow.applet.program.nrf24lx1:ProgramNRF24Lx1Applet" +program-psoc1 = "glasgow.applet.program.psoc1:ProgramPsoc1Applet" program-stusb4500-nvm = "glasgow.applet.program.stusb4500_nvm:StUsb4500NvmApplet" program-xc6s = "glasgow.applet.program.xc6s:ProgramXC6SApplet" program-xc9500 = "glasgow.applet.program.xc9500:ProgramXC9500Applet"