From 04be27f6a70748f7231816f57d0f8b602d8963dc Mon Sep 17 00:00:00 2001 From: Matthew Mets Date: Thu, 23 Feb 2023 14:47:44 +0000 Subject: [PATCH] Make the flash sleep function toggleable --- hw/production_test/icenvcm.py | 935 +++++++++++++++++----------------- 1 file changed, 466 insertions(+), 469 deletions(-) diff --git a/hw/production_test/icenvcm.py b/hw/production_test/icenvcm.py index de5256b..444326a 100644 --- a/hw/production_test/icenvcm.py +++ b/hw/production_test/icenvcm.py @@ -30,390 +30,390 @@ debug = False # todo: add expected bitstream sizes nvcm_id_table = { - 0x06: "ICE40LP8K / ICE40HX8K", - 0x07: "ICE40LP4K / ICE40HX4K", - 0x08: "ICE40LP1K / ICE40HX1K", - 0x09: "ICE40LP384", - 0x0E: "ICE40LP1K_SWG16", - 0x0F: "ICE40LP640_SWG16", - 0x10: "ICE5LP1K", - 0x11: "ICE5LP2K", - 0x12: "ICE5LP4K", - 0x14: "ICE40UL1K", - 0x15: "ICE40UL640", - 0x20: "ICE40UP5K", - 0x21: "ICE40UP3K", + 0x06: "ICE40LP8K / ICE40HX8K", + 0x07: "ICE40LP4K / ICE40HX4K", + 0x08: "ICE40LP1K / ICE40HX1K", + 0x09: "ICE40LP384", + 0x0E: "ICE40LP1K_SWG16", + 0x0F: "ICE40LP640_SWG16", + 0x10: "ICE5LP1K", + 0x11: "ICE5LP2K", + 0x12: "ICE5LP4K", + 0x14: "ICE40UL1K", + 0x15: "ICE40UL640", + 0x20: "ICE40UP5K", + 0x21: "ICE40UP3K", } def die(s): - print(s, file=sys.stderr) - exit(1) + print(s, file=sys.stderr) + exit(1) def enable(cs,reset=1): - #gpio.write(cs << cs_pin | reset << reset_pin) - flasher.gpio_put(tp1_pins['ss'], cs) - flasher.gpio_put(tp1_pins['crst'], reset) + #gpio.write(cs << cs_pin | reset << reset_pin) + flasher.gpio_put(tp1_pins['ss'], cs) + flasher.gpio_put(tp1_pins['crst'], reset) def sendhex(s): - if debug and not s == "0500": - print("TX", s) - x = bytes.fromhex(s) + if debug and not s == "0500": + print("TX", s) + x = bytes.fromhex(s) - #b = dev.exchange(x, duplex=True, readlen=len(x)) - b = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['mosi'], miso_pin=tp1_pins['miso'], buf=x) + #b = dev.exchange(x, duplex=True, readlen=len(x)) + b = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['mosi'], miso_pin=tp1_pins['miso'], buf=x) - if debug and not s == "0500": - print("RX", b.hex()) - return int.from_bytes(b, byteorder='big') + if debug and not s == "0500": + print("RX", b.hex()) + return int.from_bytes(b, byteorder='big') def delay(count: int): - # run the clock with no CS asserted - #dev.exchange(b'\x00', duplex=True, readlen=count) - sendhex('00' * count) + # run the clock with no CS asserted + #dev.exchange(b'\x00', duplex=True, readlen=count) + sendhex('00' * count) def tck(count: int, dly: int = 0): - delay(count >> 3) - delay(count >> 3) - delay(count >> 3) - delay(count >> 3) - delay(count >> 3) - delay(count >> 3) + delay(count >> 3) + delay(count >> 3) + delay(count >> 3) + delay(count >> 3) + delay(count >> 3) + delay(count >> 3) def init(): - if debug: - print("init") - enable(1, 1) - enable(1, 0) # reset high - sleep(0.15) + if debug: + print("init") + enable(1, 1) + enable(1, 0) # reset high + sleep(0.15) - enable(0, 0) # enable and reset high - sleep(0.12) - enable(0, 1) # enable low, reset high - sleep(0.12) - enable(1, 1) # enable and reset low - sleep(0.12) - return True + enable(0, 0) # enable and reset high + sleep(0.12) + enable(0, 1) # enable low, reset high + sleep(0.12) + enable(1, 1) # enable and reset low + sleep(0.12) + return True def status_wait(count=1000): - for i in range(0,count): - tck(5000) - enable(0) - x = sendhex("0500") - enable(1) + for i in range(0,count): + tck(5000) + enable(0) + x = sendhex("0500") + enable(1) - #print("x=%04x" %(x)) + #print("x=%04x" %(x)) - if (x & 0x00c1) == 0: - return True + if (x & 0x00c1) == 0: + return True - print("status failed to clear", file=sys.stdout) - return False + print("status failed to clear", file=sys.stdout) + return False def nvcm_command(cmd): - enable(0) - sendhex(cmd) - enable(1) - if not status_wait(): - return False - tck(8) - return True + enable(0) + sendhex(cmd) + enable(1) + if not status_wait(): + return False + tck(8) + return True def nvcm_pgm_enable(): - return nvcm_command("06") + return nvcm_command("06") def nvcm_pgm_disable(): - return nvcm_command("04") + return nvcm_command("04") def nvcm_enable_access(): - # ! Shift in Access-NVCM instruction; - # SMCInstruction[1] = 0x70807E99557E; - return nvcm_command("7eaa997e010e") + # ! Shift in Access-NVCM instruction; + # SMCInstruction[1] = 0x70807E99557E; + return nvcm_command("7eaa997e010e") # Returns a big integer def nvcm_read(address, length=8, cmd=0x03): - enable(0) - sendhex("%02x%06x" % (cmd, address)) - sendhex("00" * 9) # dummy bytes - x = 0 - for i in range(0,length): - x = x << 8 | sendhex("00") - enable(1) - return x + enable(0) + sendhex("%02x%06x" % (cmd, address)) + sendhex("00" * 9) # dummy bytes + x = 0 + for i in range(0,length): + x = x << 8 | sendhex("00") + enable(1) + return x # Returns a byte array of the contents def nvcm_read_bytes(address, length=8): - return nvcm_read(address, length).to_bytes(length, byteorder="big") + return nvcm_read(address, length).to_bytes(length, byteorder="big") def nvcm_write(address, data, cmd=0x02): - enable(0) - sendhex("%02x%06x" % (cmd, address)) - sendhex(data) - enable(1) + enable(0) + sendhex("%02x%06x" % (cmd, address)) + sendhex(data) + enable(1) - if not status_wait(): - print("WRITE FAILED: cmd=%02x address=%06x data=%s" % (cmd, address, data.hex()), file=sys.stderr) - return False + if not status_wait(): + print("WRITE FAILED: cmd=%02x address=%06x data=%s" % (cmd, address, data.hex()), file=sys.stderr) + return False - tck(8) - return True + tck(8) + return True def nvcm_bank_select(bank): - return nvcm_write(cmd=0x83, address=0x000025, data="%02x" % (bank)) + return nvcm_write(cmd=0x83, address=0x000025, data="%02x" % (bank)) def nvcm_select_nvcm(): - # ! Shift in Restore Access-NVCM instruction; - # SDR 40 TDI(0x00A40000C1); - return nvcm_bank_select(0x00) + # ! Shift in Restore Access-NVCM instruction; + # SDR 40 TDI(0x00A40000C1); + return nvcm_bank_select(0x00) def nvcm_select_trim(): - # ! Shift in Trim setup-NVCM instruction; - # SDR 40 TDI(0x08A40000C1); - return nvcm_bank_select(0x10) + # ! Shift in Trim setup-NVCM instruction; + # SDR 40 TDI(0x08A40000C1); + return nvcm_bank_select(0x10) def nvcm_select_sig(): - # ! Shift in Access Silicon Signature instruction; - # IDInstruction[1] = 0x04A40000C1; - # SDR 40 TDI(IDInstruction[1]); - return nvcm_bank_select(0x20) + # ! Shift in Access Silicon Signature instruction; + # IDInstruction[1] = 0x04A40000C1; + # SDR 40 TDI(IDInstruction[1]); + return nvcm_bank_select(0x20) def nvcm_read_trim(): - # ! Shift in Access-NVCM instruction; - # SMCInstruction[1] = 0x70807E99557E; - if not nvcm_enable_access(): - return + # ! Shift in Access-NVCM instruction; + # SMCInstruction[1] = 0x70807E99557E; + if not nvcm_enable_access(): + return - # ! Shift in READ_RF(0x84) instruction; - # SDR 104 TDI(0x00000000000000000004000021); - x = nvcm_read(cmd=0x84, address=0x000020, length=8) - tck(8) + # ! Shift in READ_RF(0x84) instruction; + # SDR 104 TDI(0x00000000000000000004000021); + x = nvcm_read(cmd=0x84, address=0x000020, length=8) + tck(8) - #print("FSM Trim Register %x" % (x)) + #print("FSM Trim Register %x" % (x)) - nvcm_select_nvcm() - return x + nvcm_select_nvcm() + return x def nvcm_write_trim(data): - # ! Setup Programming Parameter in Trim Registers; - # ! Shift in Trim setup-NVCM instruction; - # TRIMInstruction[1] = 0x000000430F4FA80004000041; - return nvcm_write(cmd=0x82, address=0x000020, data=data) + # ! Setup Programming Parameter in Trim Registers; + # ! Shift in Trim setup-NVCM instruction; + # TRIMInstruction[1] = 0x000000430F4FA80004000041; + return nvcm_write(cmd=0x82, address=0x000020, data=data) def nvcm_enable(): - if debug: - print("nvcm_enable") - # ! Shift in Access-NVCM instruction; - # SMCInstruction[1] = 0x70807E99557E; - if not nvcm_enable_access(): - return + if debug: + print("nvcm_enable") + # ! Shift in Access-NVCM instruction; + # SMCInstruction[1] = 0x70807E99557E; + if not nvcm_enable_access(): + return - # ! Setup Reading Parameter in Trim Registers; - # ! Shift in Trim setup-NVCM instruction; - # TRIMInstruction[1] = 0x000000230000000004000041; - if debug: - print("setup_nvcm") - return nvcm_write_trim("00000000c4000000") + # ! Setup Reading Parameter in Trim Registers; + # ! Shift in Trim setup-NVCM instruction; + # TRIMInstruction[1] = 0x000000230000000004000041; + if debug: + print("setup_nvcm") + return nvcm_write_trim("00000000c4000000") def nvcm_enable_trim(): - # ! Setup Programming Parameter in Trim Registers; - # ! Shift in Trim setup-NVCM instruction; - # TRIMInstruction[1] = 0x000000430F4FA80004000041; - return nvcm_write_trim("0015f2f0c2000000") + # ! Setup Programming Parameter in Trim Registers; + # ! Shift in Trim setup-NVCM instruction; + # TRIMInstruction[1] = 0x000000430F4FA80004000041; + return nvcm_write_trim("0015f2f0c2000000") def nvcm_disable(): - if not nvcm_select_nvcm(): - return + if not nvcm_select_nvcm(): + return - reset(1) - tck(8, 1) - rest(0) - tck(8, 2000) + reset(1) + tck(8, 1) + rest(0) + tck(8, 2000) def nvcm_trim_blank_check(): - print ("NVCM Trim_Parameter_OTP blank check"); + print ("NVCM Trim_Parameter_OTP blank check"); - if not nvcm_select_trim(): - return + if not nvcm_select_trim(): + return - x = nvcm_read(0x000020, 1) - nvcm_select_nvcm() + x = nvcm_read(0x000020, 1) + nvcm_select_nvcm() - if x != 0: - die ("NVCM Trim_Parameter_OTP Block is not blank. (%02x)" % x); + if x != 0: + die ("NVCM Trim_Parameter_OTP Block is not blank. (%02x)" % x); - return True + return True def nvcm_blank_check(total_fuse): - nvcm_select_nvcm() + nvcm_select_nvcm() - status = True - print ("NVCM main memory blank check"); - contents = nvcm_read_bytes(0x000000, total_fuse) + status = True + print ("NVCM main memory blank check"); + contents = nvcm_read_bytes(0x000000, total_fuse) - for i in range(0,total_fuse): - x = contents[i] - if debug: - print("%08x: %02x" % (i, x)) - if x != 0: - print ("%08x: NVCM Main Memory Block is not blank." % (i), file=sys.stderr) - status = False - #break + for i in range(0,total_fuse): + x = contents[i] + if debug: + print("%08x: %02x" % (i, x)) + if x != 0: + print ("%08x: NVCM Main Memory Block is not blank." % (i), file=sys.stderr) + status = False + #break - nvcm_select_nvcm() - return status + nvcm_select_nvcm() + return status def nvcm_program(rows): - nvcm_select_nvcm() + nvcm_select_nvcm() - if not nvcm_enable_trim(): - return False + if not nvcm_enable_trim(): + return False - print ("NVCM Program main memory") + print ("NVCM Program main memory") - if not nvcm_pgm_enable(): - return False + if not nvcm_pgm_enable(): + return False - status = True + status = True - i = 0 - for row in rows: - if i % 1024 == 0: - print("%6d / %6d bytes" % (i, len(rows) * 8)) - i += 8 - if not nvcm_command(row): - status = False - break + i = 0 + for row in rows: + if i % 1024 == 0: + print("%6d / %6d bytes" % (i, len(rows) * 8)) + i += 8 + if not nvcm_command(row): + status = False + break - nvcm_pgm_disable() + nvcm_pgm_disable() - if not status: - print("PROGRAMMING FAILED", file=sys.stderr) - return status + if not status: + print("PROGRAMMING FAILED", file=sys.stderr) + return status def nvcm_write_trim_pages(lock_bits): - if not nvcm_select_nvcm(): - die("select trim failed") + if not nvcm_select_nvcm(): + die("select trim failed") - if not nvcm_enable_trim(): - die("write trim command failed") + if not nvcm_enable_trim(): + die("write trim command failed") - if not nvcm_select_trim(): - die("select trim failed") + if not nvcm_select_trim(): + die("select trim failed") - if not nvcm_pgm_enable(): - die("write enable failed") + if not nvcm_pgm_enable(): + die("write enable failed") - # ! Program Security Bit row 1; - # ! Shift in PAGEPGM instruction; - # SDR 96 TDI(0x000000008000000C04000040); - # ! Program Security Bit row 2; - # SDR 96 TDI(0x000000008000000C06000040); - # ! Program Security Bit row 3; - # SDR 96 TDI(0x000000008000000C05000040); - # ! Program Security Bit row 4; - # SDR 96 TDI(0x00000000800000C07000040); - if not nvcm_write(0x000020, lock_bits): - die("trim write 0x20 failed") - if not nvcm_write(0x000060, lock_bits): - die("trim write 0x60 failed") - if not nvcm_write(0x0000a0, lock_bits): - die("trim write 0xa0 failed") - if not nvcm_write(0x0000e0, lock_bits): - die("trim write 0xe0 failed") + # ! Program Security Bit row 1; + # ! Shift in PAGEPGM instruction; + # SDR 96 TDI(0x000000008000000C04000040); + # ! Program Security Bit row 2; + # SDR 96 TDI(0x000000008000000C06000040); + # ! Program Security Bit row 3; + # SDR 96 TDI(0x000000008000000C05000040); + # ! Program Security Bit row 4; + # SDR 96 TDI(0x00000000800000C07000040); + if not nvcm_write(0x000020, lock_bits): + die("trim write 0x20 failed") + if not nvcm_write(0x000060, lock_bits): + die("trim write 0x60 failed") + if not nvcm_write(0x0000a0, lock_bits): + die("trim write 0xa0 failed") + if not nvcm_write(0x0000e0, lock_bits): + die("trim write 0xe0 failed") - nvcm_pgm_disable() + nvcm_pgm_disable() - # verify a read back - x = nvcm_read(0x000020, 8) + # verify a read back + x = nvcm_read(0x000020, 8) - nvcm_select_nvcm() + nvcm_select_nvcm() - lock_bits = int(lock_bits,16) - if x & lock_bits != lock_bits: - die("Failed to write trim lock bits: %016x != expected %016x" % (x,lock_bits)) + lock_bits = int(lock_bits,16) + if x & lock_bits != lock_bits: + die("Failed to write trim lock bits: %016x != expected %016x" % (x,lock_bits)) - print("New state %016x" % (x)) - return True + print("New state %016x" % (x)) + return True def nvcm_trim_secure(): - print ("NVCM Secure") - trim = nvcm_read_trim() - if (trim >> 60) & 0x3 != 0: - print("NVCM already secure? trim=%016x" % (trim), file=sys.stderr) + print ("NVCM Secure") + trim = nvcm_read_trim() + if (trim >> 60) & 0x3 != 0: + print("NVCM already secure? trim=%016x" % (trim), file=sys.stderr) - return nvcm_write_trim_pages("3000000100000000") + return nvcm_write_trim_pages("3000000100000000") def nvcm_trim_program(): - print ("NVCM Program Trim_Parameter_OTP"); - return nvcm_write_trim_pages("0015f2f1c4000000") + print ("NVCM Program Trim_Parameter_OTP"); + return nvcm_write_trim_pages("0015f2f1c4000000") def nvcm_info(): - nvcm_select_sig() - sig1 = nvcm_read(0x000000, 8) + nvcm_select_sig() + sig1 = nvcm_read(0x000000, 8) - nvcm_select_sig() - sig2 = nvcm_read(0x000008, 8) + nvcm_select_sig() + sig2 = nvcm_read(0x000008, 8) - # have to switch back to nvcm bank before switching to trim? - nvcm_select_nvcm() - trim = nvcm_read_trim() + # have to switch back to nvcm bank before switching to trim? + nvcm_select_nvcm() + trim = nvcm_read_trim() - nvcm_select_nvcm() + nvcm_select_nvcm() - nvcm_select_trim() - trim0 = nvcm_read(0x000020, 8) + nvcm_select_trim() + trim0 = nvcm_read(0x000020, 8) - nvcm_select_trim() - trim1 = nvcm_read(0x000060, 8) + nvcm_select_trim() + trim1 = nvcm_read(0x000060, 8) - nvcm_select_trim() - trim2 = nvcm_read(0x0000a0, 8) + nvcm_select_trim() + trim2 = nvcm_read(0x0000a0, 8) - nvcm_select_trim() - trim3 = nvcm_read(0x0000e0, 8) + nvcm_select_trim() + trim3 = nvcm_read(0x0000e0, 8) - nvcm_select_nvcm() + nvcm_select_nvcm() - secured = ((trim >> 60) & 0x3) - device_id = (sig1 >> 56) & 0xFF + secured = ((trim >> 60) & 0x3) + device_id = (sig1 >> 56) & 0xFF - print("Device: %s (%02x) secure=%d" % ( - nvcm_id_table.get(device_id, "Unknown"), - device_id, - secured - )) - print("Sig 0: %016x" % (sig1)) - print("Sig 1: %016x" % (sig2)) + print("Device: %s (%02x) secure=%d" % ( + nvcm_id_table.get(device_id, "Unknown"), + device_id, + secured + )) + print("Sig 0: %016x" % (sig1)) + print("Sig 1: %016x" % (sig2)) - print("TrimRF: %016x" % (trim)) - print("Trim 0: %016x" % (trim0)) - print("Trim 1: %016x" % (trim1)) - print("Trim 2: %016x" % (trim2)) - print("Trim 3: %016x" % (trim3)) + print("TrimRF: %016x" % (trim)) + print("Trim 0: %016x" % (trim0)) + print("Trim 1: %016x" % (trim1)) + print("Trim 2: %016x" % (trim2)) + print("Trim 3: %016x" % (trim3)) - return True + return True def nvcm_read_file(filename): - nvcm_select_nvcm() + nvcm_select_nvcm() - total_fuse = 104090 + total_fuse = 104090 - contents = b'' + contents = b'' - for offset in range(0,total_fuse,8): - if offset % 1024 == 0: - print("%6d / %6d bytes" % (offset, total_fuse)) - contents += nvcm_read_bytes(offset, 8) + for offset in range(0,total_fuse,8): + if offset % 1024 == 0: + print("%6d / %6d bytes" % (offset, total_fuse)) + contents += nvcm_read_bytes(offset, 8) - if filename == '-': - with os.fdopen(sys.stdout.fileno(), "wb", closefd=False) as f: - f.write(contents) - f.flush() - else: - with open(filename, "wb") as f: - f.write(contents) - f.flush() + if filename == '-': + with os.fdopen(sys.stdout.fileno(), "wb", closefd=False) as f: + f.write(contents) + f.flush() + else: + with open(filename, "wb") as f: + f.write(contents) + f.flush() # @@ -421,251 +421,248 @@ def nvcm_read_file(filename): # https://github.com/YosysHQ/icestorm/pull/272 # def bitstream2nvcm(bitstream): - # ensure that the file starts with the correct bistream preamble - for origin in range(0,len(bitstream)): - if bitstream[origin:origin+4] == bytes.fromhex('7EAA997E'): - break + # ensure that the file starts with the correct bistream preamble + for origin in range(0,len(bitstream)): + if bitstream[origin:origin+4] == bytes.fromhex('7EAA997E'): + break - if origin == len(bitstream): - print("Preamble not found", file=sys.stderr) - return False + if origin == len(bitstream): + print("Preamble not found", file=sys.stderr) + return False - print("Found preamable at %08x" % (origin), file=sys.stderr) + print("Found preamable at %08x" % (origin), file=sys.stderr) - # there might be stuff in the header with vendor tools, - # but not usually in icepack produced output, so ignore it for now + # there might be stuff in the header with vendor tools, + # but not usually in icepack produced output, so ignore it for now - # todo: what is the correct size? + # todo: what is the correct size? - rows = [] + rows = [] - for pos in range(origin, len(bitstream), 8): - row = bitstream[pos:pos+8] + for pos in range(origin, len(bitstream), 8): + row = bitstream[pos:pos+8] - # pad out to 8-bytes - row += b'\0' * (8 - len(row)) + # pad out to 8-bytes + row += b'\0' * (8 - len(row)) - if row == bytes(8): - # skip any all-zero entries in the bistream - continue + if row == bytes(8): + # skip any all-zero entries in the bistream + continue - # NVCM addressing is very weird - addr = pos - origin - nvcm_addr = int(addr / 328) * 4096 + (addr % 328) - rows += [ "02 %06x %s" % (nvcm_addr, row.hex()) ] + # NVCM addressing is very weird + addr = pos - origin + nvcm_addr = int(addr / 328) * 4096 + (addr % 328) + rows += [ "02 %06x %s" % (nvcm_addr, row.hex()) ] + + return rows + +def sleep_flash(): + # Disable board power + flasher.gpio_put(tp1_pins['5v_en'], False) + flasher.gpio_set_direction(tp1_pins['5v_en'], True) + + # Pull CRST low to prevent FPGA from starting + flasher.gpio_set_direction(tp1_pins['crst'], True) + flasher.gpio_put(tp1_pins['crst'], False) + + # Enable board power + flasher.gpio_put(tp1_pins['5v_en'], True) + + # Configure pins for talking to flash + flasher.gpio_set_direction(tp1_pins['ss'], True) + flasher.gpio_set_direction(tp1_pins['mosi'], False) + flasher.gpio_set_direction(tp1_pins['sck'], True) + flasher.gpio_set_direction(tp1_pins['miso'], True) + + flasher.gpio_put(tp1_pins['ss'], False) + flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0xAB]) + flasher.gpio_put(tp1_pins['ss'], True) + + # Confirm we can talk to flash + flasher.gpio_put(tp1_pins['ss'], False) + data = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0x9f, 0,0]) + flasher.gpio_put(tp1_pins['ss'], True) + + print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data])) + assert(data == bytes([0xff, 0xef, 0x40])) + + # Test that the flash will ignore a sleep command that doesn't start on the first byte + flasher.gpio_put(tp1_pins['ss'], False) + flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0, 0xb9]) + flasher.gpio_put(tp1_pins['ss'], True) + + # Confirm we can talk to flash + flasher.gpio_put(tp1_pins['ss'], False) + data = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0x9f, 0,0]) + flasher.gpio_put(tp1_pins['ss'], True) + + print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data])) + assert(data == bytes([0xff, 0xef, 0x40])) + + # put the flash to sleep + flasher.gpio_put(tp1_pins['ss'], False) + flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0xb9]) + flasher.gpio_put(tp1_pins['ss'], True) + + # Confirm flash is asleep + flasher.gpio_put(tp1_pins['ss'], False) + data = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0x9f, 0,0]) + flasher.gpio_put(tp1_pins['ss'], True) + + print('flash ID while asleep:', ' '.join(['{:02x}'.format(b) for b in data])) + assert(data == bytes([0xff, 0xff, 0xff])) - return rows if __name__ == "__main__": - import argparse + import argparse - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser() - parser.add_argument( '--port', - type=str, - default='ftdi://::/1', - help='FTDI port of the form ftdi://::/1') + parser.add_argument( '--port', + type=str, + default='ftdi://::/1', + help='FTDI port of the form ftdi://::/1') - parser.add_argument( '-v', '--verbose', - dest='verbose', - action='store_true', - help='Show debug information and serial read/writes') + parser.add_argument( '-v', '--verbose', + dest='verbose', + action='store_true', + help='Show debug information and serial read/writes') - parser.add_argument('-b', '--boot', - dest='do_boot', - action='store_true', - help='Deassert the reset line to allow the FPGA to boot') + parser.add_argument('-f', '--sleep_flash', + dest='sleep_flash', + action='store_true', + help='Put an attached SPI flash chip in deep sleep before programming FPGA') - parser.add_argument('-i', '--info', - dest='read_info', - action='store_true', - help='Read chip ID, trim and other info') + parser.add_argument('-b', '--boot', + dest='do_boot', + action='store_true', + help='Deassert the reset line to allow the FPGA to boot') - parser.add_argument('--read', - dest='read_file', - type=str, - default=None, - help='Read contents of NVCM') + parser.add_argument('-i', '--info', + dest='read_info', + action='store_true', + help='Read chip ID, trim and other info') - parser.add_argument('--write', - dest='write_file', - type=str, - default=None, - help='bitstream file to write to NVCM (warning: not reversable!)') + parser.add_argument('--read', + dest='read_file', + type=str, + default=None, + help='Read contents of NVCM') - parser.add_argument('--ignore-blank', - dest='ignore_blank', - action='store_true', - help='Proceed even if the chip is not blank') + parser.add_argument('--write', + dest='write_file', + type=str, + default=None, + help='bitstream file to write to NVCM (warning: not reversable!)') - parser.add_argument('--secure', - dest='set_secure', - action='store_true', - help='Set security bits to prevent modification (warning: not reversable!') + parser.add_argument('--ignore-blank', + dest='ignore_blank', + action='store_true', + help='Proceed even if the chip is not blank') - parser.add_argument('--my-design-is-good-enough', - dest='good_enough', - action='store_true', - help='Enable the dangerous commands --write and --secure') + parser.add_argument('--secure', + dest='set_secure', + action='store_true', + help='Set security bits to prevent modification (warning: not reversable!') - args = parser.parse_args() + parser.add_argument('--my-design-is-good-enough', + dest='good_enough', + action='store_true', + help='Enable the dangerous commands --write and --secure') - debug = args.verbose + args = parser.parse_args() - if not args.good_enough \ - and (args.write_file or args.set_secure): - print("Are you sure your design is good enough?", file=sys.stderr) - exit(1) + debug = args.verbose - # Instantiate a SPI controller, with separately managed CS line - #spi = SpiController() - flasher = usb_test.ice40_flasher() - - # Configure the first interface (IF/1) of the FTDI device as a SPI controller - #spi.configure(args.port) + if not args.good_enough \ + and (args.write_file or args.set_secure): + print("Are you sure your design is good enough?", file=sys.stderr) + exit(1) - - # Get a port to a SPI device w/ /CS on A*BUS3 and SPI mode 0 @ 12MHz - # the CS line is not used in this case - #dev = spi.get_port(cs=0, freq=12E6, mode=0) - - #reset_pin = 7 - #cs_pin = 4 - - # Get GPIO port to manage the CS and RESET pins - #gpio = spi.get_gpio() - #gpio.set_direction(1 << reset_pin | 1 << cs_pin, 1 << reset_pin | 1 << cs_pin) + # Instantiate a SPI controller, with separately managed CS line + #spi = SpiController() + flasher = usb_test.ice40_flasher() + + # Configure the first interface (IF/1) of the FTDI device as a SPI controller + #spi.configure(args.port) - # Enable power to the FPGA, then set both reset and CS pins high - tp1_pins = { - '5v_en' : 7, - 'sck' : 10, - 'mosi' : 11, - 'ss' : 12, - 'miso' : 13, - 'crst' : 14, - 'cdne' : 15 - } + + # Get a port to a SPI device w/ /CS on A*BUS3 and SPI mode 0 @ 12MHz + # the CS line is not used in this case + #dev = spi.get_port(cs=0, freq=12E6, mode=0) + + #reset_pin = 7 + #cs_pin = 4 + + # Get GPIO port to manage the CS and RESET pins + #gpio = spi.get_gpio() + #gpio.set_direction(1 << reset_pin | 1 << cs_pin, 1 << reset_pin | 1 << cs_pin) - # # Reset pin values - # for pin in tp1_pins: - # flasher.gpio_set_direction(tp1_pins[pin], False) + # Enable power to the FPGA, then set both reset and CS pins high + tp1_pins = { + '5v_en' : 7, + 'sck' : 10, + 'mosi' : 11, + 'ss' : 12, + 'miso' : 13, + 'crst' : 14, + 'cdne' : 15 + } - # Disable board power - flasher.gpio_put(tp1_pins['5v_en'], False) - flasher.gpio_set_direction(tp1_pins['5v_en'], True) + # # Reset pin values + # for pin in tp1_pins: + # flasher.gpio_set_direction(tp1_pins[pin], False) - # Pull CRST low to prevent FPGA from starting - flasher.gpio_set_direction(tp1_pins['crst'], True) - flasher.gpio_put(tp1_pins['crst'], False) + if args.sleep_flash: + sleep_flash() - sleep(1) - # Enable board power - flasher.gpio_put(tp1_pins['5v_en'], True) + # Configure pins for talking to ice40 + flasher.gpio_set_direction(tp1_pins['ss'], True) + flasher.gpio_set_direction(tp1_pins['mosi'], True) + flasher.gpio_set_direction(tp1_pins['sck'], True) + flasher.gpio_set_direction(tp1_pins['miso'], False) - # Configure pins for talking to flash - flasher.gpio_set_direction(tp1_pins['ss'], True) - flasher.gpio_set_direction(tp1_pins['mosi'], False) - flasher.gpio_set_direction(tp1_pins['sck'], True) - flasher.gpio_set_direction(tp1_pins['miso'], True) + # # Turn on ICE40 in CRAM boot mode - flasher.gpio_put(tp1_pins['ss'], False) - flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0xAB]) - flasher.gpio_put(tp1_pins['ss'], True) + enable(1, 0) # enable and reset high + sleep(0.2) + enable(1, 1) # enable low, reset high + + init() or exit(1) + nvcm_enable() or exit(1) - # Confirm we can talk to flash - flasher.gpio_put(tp1_pins['ss'], False) - data = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0x9f, 0,0]) - flasher.gpio_put(tp1_pins['ss'], True) + if args.read_info: + nvcm_info() or exit(1) - print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data])) - assert(data == bytes([0xff, 0xef, 0x40])) + if args.write_file: + with open(args.write_file, "rb") as f: + bitstream = f.read() + print("read %d bytes" % (len(bitstream))) + cmds = bitstream2nvcm(bitstream) + if not cmds: + exit(1) - # Test that the flash will ignore a sleep command that doesn't start on the first byte - flasher.gpio_put(tp1_pins['ss'], False) - flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0, 0xb9]) - flasher.gpio_put(tp1_pins['ss'], True) + if not args.ignore_blank: + nvcm_trim_blank_check() or exit(1) + # how much should we check? + nvcm_blank_check(0x100) or exit(1) - # Confirm we can talk to flash - flasher.gpio_put(tp1_pins['ss'], False) - data = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0x9f, 0,0]) - flasher.gpio_put(tp1_pins['ss'], True) + # this is it! + nvcm_program(cmds) or exit(1) - print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data])) - assert(data == bytes([0xff, 0xef, 0x40])) + # update the trim to boot from nvcm + nvcm_trim_program() or exit(1) - # put the flash to sleep - flasher.gpio_put(tp1_pins['ss'], False) - flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0xb9]) - flasher.gpio_put(tp1_pins['ss'], True) + if args.read_file: + # read back after writing to the NVCM + nvcm_read_file(args.read_file) or exit(1) - # Confirm flash is asleep - flasher.gpio_put(tp1_pins['ss'], False) - data = flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0x9f, 0,0]) - flasher.gpio_put(tp1_pins['ss'], True) + if args.set_secure: + nvcm_trim_secure() or exit(1) - print('flash ID while asleep:', ' '.join(['{:02x}'.format(b) for b in data])) - assert(data == bytes([0xff, 0xff, 0xff])) - - - # Configure pins for talking to ice40 - flasher.gpio_set_direction(tp1_pins['ss'], True) - flasher.gpio_set_direction(tp1_pins['mosi'], True) - flasher.gpio_set_direction(tp1_pins['sck'], True) - flasher.gpio_set_direction(tp1_pins['miso'], False) - - # # Turn on ICE40 in CRAM boot mode - - # init() - # nvcm_enable() - # nvcm_info() - - - # release power down: 0xAB - # sleep: 0xB9 - ## Request the JEDEC ID from the SPI device - #jedec_id = dev.exchange([0x9f], 3) - - - enable(1, 0) # enable and reset high - sleep(0.2) - enable(1, 1) # enable low, reset high - - init() or exit(1) - nvcm_enable() or exit(1) - - if args.read_info: - nvcm_info() or exit(1) - - if args.write_file: - with open(args.write_file, "rb") as f: - bitstream = f.read() - print("read %d bytes" % (len(bitstream))) - cmds = bitstream2nvcm(bitstream) - if not cmds: - exit(1) - - if not args.ignore_blank: - nvcm_trim_blank_check() or exit(1) - # how much should we check? - nvcm_blank_check(0x100) or exit(1) - - # this is it! - nvcm_program(cmds) or exit(1) - - # update the trim to boot from nvcm - nvcm_trim_program() or exit(1) - - if args.read_file: - # read back after writing to the NVCM - nvcm_read_file(args.read_file) or exit(1) - - if args.set_secure: - nvcm_trim_secure() or exit(1) - - if args.do_boot: - # hold reset low for half a second - enable(1,0) - sleep(0.5) - enable(1,1) + if args.do_boot: + # hold reset low for half a second + enable(1,0) + sleep(0.5) + enable(1,1)