From 84d020e3c0ee2c204a65aca360376010b7a912a8 Mon Sep 17 00:00:00 2001 From: Matt Mets Date: Thu, 2 Mar 2023 18:17:11 +0100 Subject: [PATCH] Add type annotations, lint for pep8 --- hw/production_test/Makefile | 8 + hw/production_test/icenvcm.py | 551 ++++++++++++++++----------------- hw/production_test/usb_test.py | 119 +++---- 3 files changed, 319 insertions(+), 359 deletions(-) create mode 100644 hw/production_test/Makefile mode change 100644 => 100755 hw/production_test/icenvcm.py diff --git a/hw/production_test/Makefile b/hw/production_test/Makefile new file mode 100644 index 0000000..877c61a --- /dev/null +++ b/hw/production_test/Makefile @@ -0,0 +1,8 @@ + +lint: + autopep8 --in-place --aggressive --aggressive usb_test.py + mypy --disallow-untyped-defs usb_test.py +#pycodestyle usb_test.py + autopep8 --in-place --aggressive --aggressive icenvcm.py + mypy --disallow-untyped-defs icenvcm.py +#pycodestyle icenvcm.py diff --git a/hw/production_test/icenvcm.py b/hw/production_test/icenvcm.py old mode 100644 new mode 100755 index b2f7493..51309b0 --- a/hw/production_test/icenvcm.py +++ b/hw/production_test/icenvcm.py @@ -20,15 +20,10 @@ # import usb_test -from binascii import unhexlify, hexlify import sys from time import sleep -import re import os -def die(s): - print(s, file=sys.stderr) - exit(1) class Nvcm(): # todo: add expected bitstream sizes @@ -48,7 +43,7 @@ class Nvcm(): 0x21: "ICE40UP3K", } - def __init__(self, pins, debug=False): + def __init__(self, pins: dict, debug: bool = False) -> None: self.pins = pins self.debug = debug @@ -67,53 +62,53 @@ class Nvcm(): self.flasher.gpio_set_direction(pins['cdne'], False) self.flasher.spi_pins_set( - pins['sck'], - pins['ss'], - pins['mosi'], - pins['miso'] - ) + pins['sck'], + pins['ss'], + pins['mosi'], + pins['miso'] + ) - def power_on(self): + def power_on(self) -> None: self.flasher.gpio_put(self.pins['5v_en'], True) - def power_off(self): + def power_off(self) -> None: self.flasher.gpio_put(self.pins['5v_en'], False) - def enable(self,cs,reset=1): - #gpio.write(cs << cs_pin | reset << reset_pin) + def enable(self, cs: bool, reset: bool = True) -> None: + # gpio.write(cs << cs_pin | reset << reset_pin) self.flasher.gpio_put(self.pins['ss'], cs) self.flasher.gpio_put(self.pins['crst'], reset) - def sendhex(self,s): - if self.debug and not s == "0500": # supress status check messages + def sendhex(self, s: str) -> int: + if self.debug and not s == "0500": # supress status check messages print("TX", s) x = bytes.fromhex(s) - - #b = dev.exchange(x, duplex=True, readlen=len(x)) + + # b = dev.exchange(x, duplex=True, readlen=len(x)) b = self.flasher.spi_bitbang(x, toggle_cs=False) - + if self.debug and not s == "0500": print("RX", b.hex()) return int.from_bytes(b, byteorder='big') - - def sendhex_cs(self,s): + + def sendhex_cs(self, s: str) -> bytes: if self.debug and not s == "0500": print("TX", s) x = bytes.fromhex(s) - - #b = dev.exchange(x, duplex=True, readlen=len(x)) + + # b = dev.exchange(x, duplex=True, readlen=len(x)) b = self.flasher.spi_bitbang(x) - + if self.debug and not s == "0500": print("RX", b.hex()) return b - def delay(self,count: int): + def delay(self, count: int) -> None: # run the clock with no CS asserted - #dev.exchange(b'\x00', duplex=True, readlen=count) + # dev.exchange(b'\x00', duplex=True, readlen=count) self.sendhex('00' * count) - def tck(self,count: int): + def tck(self, count: int) -> None: self.delay(count >> 3) self.delay(count >> 3) self.delay(count >> 3) @@ -121,54 +116,50 @@ class Nvcm(): self.delay(count >> 3) self.delay(count >> 3) - def init(self): + def init(self) -> None: if self.debug: print("init") - self.enable(1, 1) - self.enable(1, 0) # reset high + self.enable(True, True) + self.enable(True, False) # reset high sleep(0.15) - - self.enable(0, 0) # enable and reset high - sleep(0.12) - self.enable(0, 1) # enable low, reset high - sleep(0.12) - self.enable(1, 1) # enable and reset low - sleep(0.12) - return True - def status_wait(self,count=1000): - for i in range(0,count): + self.enable(False, False) # enable and reset high + sleep(0.12) + self.enable(False, True) # enable low, reset high + sleep(0.12) + self.enable(True, True) # enable and reset low + sleep(0.12) + + def status_wait(self, count: int = 1000) -> None: + for i in range(0, count): self.tck(5000) ret = self.sendhex_cs("0500") x = int.from_bytes(ret, byteorder='big') - - #print("x=%04x" %(x)) - + + # print("x=%04x" %(x)) + if (x & 0x00c1) == 0: - return True - - print("status failed to clear", file=sys.stdout) - return False + return - def command(self,cmd): + raise Exception("status failed to clear") + + def command(self, cmd: str) -> None: self.sendhex_cs(cmd) - if not self.status_wait(): - return False + self.status_wait() self.tck(8) - return True - def pgm_enable(self): - return self.command("06") + def pgm_enable(self) -> None: + self.command("06") - def pgm_disable(self): - return self.command("04") + def pgm_disable(self) -> None: + self.command("04") - def enable_access(self): + def enable_access(self) -> None: # ! Shift in Access-NVCM instruction; # SMCInstruction[1] = 0x70807E99557E; - return self.command("7eaa997e010e") + self.command("7eaa997e010e") - def read(self, address, length=8, cmd=0x03): + def read(self, address: int, length: int = 8, cmd: int = 0x03) -> int: """Returns a big integer""" # enable(0) # sendhex("%02x%06x" % (cmd, address)) @@ -177,177 +168,158 @@ class Nvcm(): # for i in range(0,length): # x = x << 8 | sendhex("00") # enable(1) - + msg = '' msg += ("%02x%06x" % (cmd, address)) - msg += ("00" * 9) # dummy bytes - msg += ("00" * length) # read + msg += ("00" * 9) # dummy bytes + msg += ("00" * length) # read ret = self.sendhex_cs(msg) - + x = 0 - for i in range(0,length): - x = x << 8 | ret[i + 4+9] + for i in range(0, length): + x = x << 8 | ret[i + 4 + 9] return x - def read_bytes(self, address, length=8): + def read_bytes(self, address: int, length: int = 8) -> bytes: """Returns a byte array of the contents""" return self.read(address, length).to_bytes(length, byteorder="big") - def write(self,address, data, cmd=0x02): + def write(self, address: int, data: str, cmd: int = 0x02) -> None: self.sendhex_cs("%02x%06x" % (cmd, address) + data) - - if not self.status_wait(): - print("WRITE FAILED: cmd=%02x address=%06x data=%s" % (cmd, address, data.hex()), file=sys.stderr) - return False - + + try: + self.status_wait() + except Exception as e: + raise Exception("WRITE FAILED: cmd=%02x address=%06x data=%s" % + (cmd, address, data)) + self.tck(8) - return True - def bank_select(self,bank): - return self.write(cmd=0x83, address=0x000025, data="%02x" % (bank)) + def bank_select(self, bank: int) -> None: + self.write(cmd=0x83, address=0x000025, data="%02x" % (bank)) - def select_nvcm(self): + def select_nvcm(self) -> None: # ! Shift in Restore Access-NVCM instruction; # SDR 40 TDI(0x00A40000C1); - return self.bank_select(0x00) - - def select_trim(self): + self.bank_select(0x00) + + def select_trim(self) -> None: # ! Shift in Trim setup-NVCM instruction; # SDR 40 TDI(0x08A40000C1); - return self.bank_select(0x10) - - def select_sig(self): + self.bank_select(0x10) + + def select_sig(self) -> None: # ! Shift in Access Silicon Signature instruction; # IDInstruction[1] = 0x04A40000C1; # SDR 40 TDI(IDInstruction[1]); - return self.bank_select(0x20) + self.bank_select(0x20) - def read_trim(self): + def read_trim(self) -> int: # ! Shift in Access-NVCM instruction; # SMCInstruction[1] = 0x70807E99557E; - if not self.enable_access(): - return - + self.enable_access() + # ! Shift in READ_RF(0x84) instruction; # SDR 104 TDI(0x00000000000000000004000021); x = self.read(cmd=0x84, address=0x000020, length=8) self.tck(8) - - #print("FSM Trim Register %x" % (x)) - + + # print("FSM Trim Register %x" % (x)) + self.select_nvcm() return x - - def write_trim(self,data): + + def write_trim(self, data: str) -> None: # ! Setup Programming Parameter in Trim Registers; # ! Shift in Trim setup-NVCM instruction; # TRIMInstruction[1] = 0x000000430F4FA80004000041; - return self.write(cmd=0x82, address=0x000020, data=data) + self.write(cmd=0x82, address=0x000020, data=data) - def nvcm_enable(self): + def nvcm_enable(self) -> None: if self.debug: print("enable") # ! Shift in Access-NVCM instruction; # SMCInstruction[1] = 0x70807E99557E; - if not self.enable_access(): - return - + self.enable_access() + # ! Setup Reading Parameter in Trim Registers; # ! Shift in Trim setup-NVCM instruction; # TRIMInstruction[1] = 0x000000230000000004000041; if self.debug: print("setup_nvcm") - return self.write_trim("00000000c4000000") - - def enable_trim(self): + self.write_trim("00000000c4000000") + + def enable_trim(self) -> None: # ! Setup Programming Parameter in Trim Registers; # ! Shift in Trim setup-NVCM instruction; # TRIMInstruction[1] = 0x000000430F4FA80004000041; - return self.write_trim("0015f2f0c2000000") - - def disable(self): - if not self.select_nvcm(): - return - - self.reset(1) - self.tck(8) - self.reset(0) - self.tck(8) + self.write_trim("0015f2f0c2000000") + + def trim_blank_check(self) -> bool: + print("NVCM Trim_Parameter_OTP blank check") + + self.select_trim() - def trim_blank_check(self): - print ("NVCM Trim_Parameter_OTP blank check"); - - if not self.select_trim(): - return - x = self.read(0x000020, 1) self.select_nvcm() - + if x != 0: - die ("NVCM Trim_Parameter_OTP Block is not blank. (%02x)" % x); - + print("NVCM Trim_Parameter_OTP Block is not blank. (%02x)" % x) + return False return True - - def blank_check(self,total_fuse): + + def blank_check(self, total_fuse: int) -> bool: self.select_nvcm() - + status = True - print ("NVCM main memory blank check"); + print("NVCM main memory blank check") contents = self.read_bytes(0x000000, total_fuse) - - for i in range(0,total_fuse): + + for i in range(0, total_fuse): x = contents[i] - if debug: + if self.debug: print("%08x: %02x" % (i, x)) if x != 0: - print ("%08x: NVCM Main Memory Block is not blank." % (i), file=sys.stderr) + print( + "%08x: NVCM Main Memory Block is not blank." % + (i), file=sys.stderr) status = False - #break - + self.select_nvcm() return status - def program(self,rows): + def program(self, rows: list[str]) -> None: + print("NVCM Program main memory") + self.select_nvcm() - - if not self.enable_trim(): - return False - - print ("NVCM Program main memory") - - if not self.pgm_enable(): - return False - + + self.enable_trim() + + self.pgm_enable() + status = True - + i = 0 for row in rows: if i % 1024 == 0: print("%6d / %6d bytes" % (i, len(rows) * 8)) i += 8 - if not self.command(row): - status = False - break - - self.pgm_disable() - - if not status: - print("PROGRAMMING FAILED", file=sys.stderr) - return status + try: + self.command(row) + except Exception as e: + raise Exception("programming failed, row:", row) + + self.pgm_disable() + + def write_trim_pages(self, lock_bits: str) -> None: + self.select_nvcm() + + self.enable_trim() + + self.select_trim() + + self.pgm_enable() - def write_trim_pages(self,lock_bits): - if not self.select_nvcm(): - die("select trim failed") - - if not self.enable_trim(): - die("write trim command failed") - - if not self.select_trim(): - die("select trim failed") - - if not self.pgm_enable(): - die("write enable failed") - # ! Program Security Bit row 1; # ! Shift in PAGEPGM instruction; # SDR 96 TDI(0x000000008000000C04000040); @@ -357,72 +329,68 @@ class Nvcm(): # SDR 96 TDI(0x000000008000000C05000040); # ! Program Security Bit row 4; # SDR 96 TDI(0x00000000800000C07000040); - if not self.write(0x000020, lock_bits): - die("trim write 0x20 failed") - if not self.write(0x000060, lock_bits): - die("trim write 0x60 failed") - if not self.write(0x0000a0, lock_bits): - die("trim write 0xa0 failed") - if not self.write(0x0000e0, lock_bits): - die("trim write 0xe0 failed") - + self.write(0x000020, lock_bits) + self.write(0x000060, lock_bits) + self.write(0x0000a0, lock_bits) + self.write(0x0000e0, lock_bits) + self.pgm_disable() - + # verify a read back x = self.read(0x000020, 8) - - self.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)) - - print("New state %016x" % (x)) - return True - def trim_secure(self): - print ("NVCM Secure") + self.select_nvcm() + + lock_bits_int = int(lock_bits, 16) + if x & lock_bits_int != lock_bits_int: + raise Exception( + "Failed to write trim lock bits: %016x != expected %016x" % + (x, lock_bits_int)) + + print("New state %016x" % (x)) + + def trim_secure(self) -> None: + print("NVCM Secure") trim = self.read_trim() if (trim >> 60) & 0x3 != 0: print("NVCM already secure? trim=%016x" % (trim), file=sys.stderr) - - return self.write_trim_pages("3000000100000000") + self.write_trim_pages("3000000100000000") - def trim_program(self): - print ("NVCM Program Trim_Parameter_OTP"); - return self.write_trim_pages("0015f2f1c4000000") + def trim_program(self) -> None: + print("NVCM Program Trim_Parameter_OTP") + self.write_trim_pages("0015f2f1c4000000") - def info(self): + def info(self) -> None: self.select_sig() sig1 = self.read(0x000000, 8) - + self.select_sig() sig2 = self.read(0x000008, 8) - + # have to switch back to nvcm bank before switching to trim? self.select_nvcm() trim = self.read_trim() - + self.select_nvcm() - + self.select_trim() trim0 = self.read(0x000020, 8) - + self.select_trim() trim1 = self.read(0x000060, 8) - + self.select_trim() trim2 = self.read(0x0000a0, 8) - + self.select_trim() trim3 = self.read(0x0000e0, 8) - + self.select_nvcm() - + secured = ((trim >> 60) & 0x3) device_id = (sig1 >> 56) & 0xFF - + print("Device: %s (%02x) secure=%d" % ( self.id_table.get(device_id, "Unknown"), device_id, @@ -430,28 +398,25 @@ class Nvcm(): )) 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)) - - return True - def read_file(self,filename): + def read_file(self, filename: str) -> None: self.select_nvcm() - + total_fuse = 104090 - + contents = b'' - - for offset in range(0,total_fuse,8): + + for offset in range(0, total_fuse, 8): if offset % 1024 == 0: print("%6d / %6d bytes" % (offset, total_fuse)) contents += self.read_bytes(offset, 8) - + if filename == '-': with os.fdopen(sys.stdout.fileno(), "wb", closefd=False) as f: f.write(contents) @@ -466,15 +431,14 @@ class Nvcm(): # bistream to NVCM command conversion is based on majbthrd's work in # https://github.com/YosysHQ/icestorm/pull/272 # -def bitstream2nvcm(bitstream): +def bitstream2nvcm(bitstream: bytes) -> list[str]: # 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'): + 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 + raise Exception("Preamble not found") print("Found preamable at %08x" % (origin), file=sys.stderr) @@ -486,7 +450,7 @@ def bitstream2nvcm(bitstream): rows = [] for pos in range(origin, len(bitstream), 8): - row = bitstream[pos:pos+8] + row = bitstream[pos:pos + 8] # pad out to 8-bytes row += b'\0' * (8 - len(row)) @@ -498,11 +462,13 @@ def bitstream2nvcm(bitstream): # NVCM addressing is very weird addr = pos - origin nvcm_addr = int(addr / 328) * 4096 + (addr % 328) - rows += [ "02 %06x %s" % (nvcm_addr, row.hex()) ] + rows += ["02 %06x %s" % (nvcm_addr, row.hex())] return rows -def sleep_flash(pins): + +def sleep_flash(pins: dict) -> None: + """ Put the SPI bootloader flash in deep sleep mode""" flasher = usb_test.ice40_flasher() # Disable board power @@ -523,37 +489,41 @@ def sleep_flash(pins): flasher.gpio_set_direction(pins['miso'], True) flasher.spi_pins_set( - pins['sck'], - pins['ss'], - pins['miso'], - pins['mosi'] - ) + pins['sck'], + pins['ss'], + pins['miso'], + pins['mosi'] + ) - flasher.spi_bitbang([0xAB]) + flasher.spi_bitbang(bytes([0xAB])) # Confirm we can talk to flash - data = flasher.spi_bitbang([0x9f, 0,0]) + data = flasher.spi_bitbang(bytes([0x9f, 0, 0])) - print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data])) - assert(data == bytes([0xff, 0xef, 0x40])) + 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.spi_bitbang([0, 0xb9]) + # Test that the flash will ignore a sleep command that doesn't start on + # the first byte + flasher.spi_bitbang(bytes([0, 0xb9])) # Confirm we can talk to flash - data = flasher.spi_bitbang([0x9f, 0,0]) + data = flasher.spi_bitbang(bytes([0x9f, 0, 0])) - print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data])) - assert(data == bytes([0xff, 0xef, 0x40])) + 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.spi_bitbang([0xb9]) + flasher.spi_bitbang(bytes([0xb9])) # Confirm flash is asleep - data = flasher.spi_bitbang(buf=[0x9f, 0,0]) + data = flasher.spi_bitbang(bytes([0x9f, 0, 0])) - print('flash ID while asleep:', ' '.join(['{:02x}'.format(b) for b in data])) - assert(data == bytes([0xff, 0xff, 0xff])) + print('flash ID while asleep:', ' '.join( + ['{:02x}'.format(b) for b in data])) + assert (data == bytes([0xff, 0xff, 0xff])) if __name__ == "__main__": @@ -562,83 +532,88 @@ if __name__ == "__main__": 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('-f', '--sleep_flash', + 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('-b', '--boot', + 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('-i', '--info', - dest='read_info', - action='store_true', - help='Read chip ID, trim and other info') + dest='read_info', + action='store_true', + help='Read chip ID, trim and other info') parser.add_argument('--read', - dest='read_file', - type=str, - default=None, - help='Read contents of NVCM') + dest='read_file', + type=str, + default=None, + help='Read contents of NVCM') - parser.add_argument('--write', + parser.add_argument( + '--write', dest='write_file', type=str, default=None, help='bitstream file to write to NVCM (warning: not reversable!)') parser.add_argument('--ignore-blank', - dest='ignore_blank', - action='store_true', - help='Proceed even if the chip is not blank') + dest='ignore_blank', + action='store_true', + help='Proceed even if the chip is not blank') - parser.add_argument('--secure', + parser.add_argument( + '--secure', dest='set_secure', action='store_true', help='Set security bits to prevent modification (warning: not reversable!') - parser.add_argument('--my-design-is-good-enough', + parser.add_argument( + '--my-design-is-good-enough', dest='good_enough', action='store_true', help='Enable the dangerous commands --write and --secure') args = parser.parse_args() - if not args.good_enough \ - and (args.write_file or args.set_secure): + and (args.write_file or args.set_secure): print("Are you sure your design is good enough?", file=sys.stderr) exit(1) # Instantiate a SPI controller, with separately managed CS line - #spi = SpiController() - - # Configure the first interface (IF/1) of the FTDI device as a SPI controller - #spi.configure(args.port) + # spi = SpiController() + + # Configure the first interface (IF/1) of the FTDI device as a SPI controller + # spi.configure(args.port) - # 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 - + # 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) + # gpio = spi.get_gpio() + # gpio.set_direction(1 << reset_pin | 1 << cs_pin, 1 << reset_pin | 1 << cs_pin) # Enable power to the FPGA, then set both reset and CS pins high @@ -647,13 +622,13 @@ if __name__ == "__main__": # flasher.gpio_set_direction(tp1_pins[pin], False) tp1_pins = { - '5v_en' : 7, - 'sck' : 10, - 'mosi' : 11, - 'ss' : 12, - 'miso' : 13, - 'crst' : 14, - 'cdne' : 15 + '5v_en': 7, + 'sck': 10, + 'mosi': 11, + 'ss': 12, + 'miso': 13, + 'crst': 14, + 'cdne': 15 } if args.sleep_flash: @@ -663,11 +638,11 @@ if __name__ == "__main__": nvcm.power_on() # # Turn on ICE40 in CRAM boot mode - nvcm.init() or exit(1) - nvcm.nvcm_enable() or exit(1) + nvcm.init() + nvcm.nvcm_enable() if args.read_info: - nvcm.info() or exit(1) + nvcm.info() if args.write_file: with open(args.write_file, "rb") as f: @@ -683,20 +658,20 @@ if __name__ == "__main__": nvcm.blank_check(0x100) or exit(1) # this is it! - nvcm.program(cmds) or exit(1) + nvcm.program(cmds) # update the trim to boot from nvcm - nvcm.trim_program() or exit(1) + nvcm.trim_program() if args.read_file: # read back after writing to the NVCM - nvcm.read_file(args.read_file) or exit(1) + nvcm.read_file(args.read_file) if args.set_secure: - nvcm.trim_secure() or exit(1) + nvcm.trim_secure() if args.do_boot: # hold reset low for half a second - nvcm.enable(1,0) + nvcm.enable(True, False) sleep(0.5) - nvcm.enable(1,1) + nvcm.enable(True, True) diff --git a/hw/production_test/usb_test.py b/hw/production_test/usb_test.py index c72e373..10535a3 100755 --- a/hw/production_test/usb_test.py +++ b/hw/production_test/usb_test.py @@ -1,8 +1,9 @@ #!/usr/bin/env python -import usb.core -import usb.util +import usb.core # type: ignore +import usb.util # type: ignore import struct + class ice40_flasher: FLASHER_REQUEST_LED_SET = 0x00, FLASHER_REQUEST_PIN_DIRECTION_SET = 0x10 @@ -15,42 +16,31 @@ class ice40_flasher: FLASHER_REQUEST_ADC_READ = 0x50 FLASHER_REQUEST_BOOTLOADRE = 0xFF - def __init__(self): -# self.dev = None -# for dict in hid.enumerate(USB_VID): -# self.dev = hid.Device(dict['vendor_id'], dict['product_id']) -# if self.dev is None: -# raise IOError("Couldn't find any hid device with vendor id 0x%x" % (USB_VID)) - + def __init__(self) -> None: # See: https://github.com/pyusb/pyusb/blob/master/docs/tutorial.rst self.dev = usb.core.find(idVendor=0xcafe, idProduct=0x4010) - + if self.dev is None: raise ValueError('Device not found') - + self.dev.set_configuration() - def close(self): - """Close the HID device""" - #self.dev.close() - pass + def _write(self, request_id: int, data: bytes) -> None: + self.dev.ctrl_transfer(0x40, request_id, 0, 0, data) - def _write(self, request_id: int, data: bytes): - self.dev.ctrl_transfer(0x40, request_id,0,0,data) - - def _write_bulk(self, request_id: int, data: bytes): + def _write_bulk(self, request_id: int, data: bytes) -> None: msg = bytearray() msg.append(request_id) msg.extend(data) self.dev.write(0x01, data) def _read(self, request_id: int, length: int) -> bytes: - #ctrl_transfer(self, bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength = None, timeout = None): + # ctrl_transfer(self, bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength = None, timeout = None): # Request type: # bit 7: direction 0:host to device (OUT), 1: device to host (IN) # bits 5-6: type: 0:standard 1:class 2:vendor 3:reserved # bits 0-4: recipient: 0:device 1:interface 2:endpoint 3:other - ret = self.dev.ctrl_transfer(0xC0, request_id,0,0,length) + ret = self.dev.ctrl_transfer(0xC0, request_id, 0, 0, length) return ret def gpio_set_direction(self, pin: int, direction: bool) -> None: @@ -61,11 +51,11 @@ class ice40_flasher: value -- True: Set pin as output, False: set pin as input """ msg = struct.pack('>II', - (1< None: @@ -78,10 +68,10 @@ class ice40_flasher: pulldown -- True: Enable pulldown, False: Disable pulldown """ msg = struct.pack('>III', - (1<II', - 1 << pin, - (1 if val else 0) << pin, - ) + 1 << pin, + (1 if val else 0) << pin, + ) self._write(self.FLASHER_REQUEST_PIN_VALUES_SET, msg) def gpio_get_all(self) -> int: """Read the input levels of all GPIO pins""" - msg_in = self._read(self.FLASHER_REQUEST_PIN_VALUES_GET,4) + msg_in = self._read(self.FLASHER_REQUEST_PIN_VALUES_GET, 4) [gpio_states] = struct.unpack('>I', msg_in) return gpio_states @@ -112,7 +102,7 @@ class ice40_flasher: Keyword arguments: pin -- GPIO pin number """ - gpio_states = gpio_get_all() + gpio_states = self.gpio_get_all() return ((gpio_states >> pin) & 0x01) == 0x01 @@ -138,12 +128,12 @@ class ice40_flasher: msg = bytearray() msg.extend(header) - self._write(self.FLASHER_REQUEST_SPI_PINS_SET,msg) + self._write(self.FLASHER_REQUEST_SPI_PINS_SET, msg) def spi_bitbang( self, - buf: bytearray, - toggle_cs: bool = True) -> bytearray: + buf: bytes, + toggle_cs: bool = True) -> bytes: """Bitbang a SPI transfer Keyword arguments: @@ -153,7 +143,7 @@ class ice40_flasher: ret = bytearray() - max_chunk_size = (1024-8) + max_chunk_size = (1024 - 8) for i in range(0, len(buf), max_chunk_size): chunk = buf[i:i + max_chunk_size] ret.extend( @@ -161,13 +151,13 @@ class ice40_flasher: buf=chunk, toggle_cs=toggle_cs)) - return ret + return bytes(ret) def spi_bitbang_inner( self, - buf: bytearray, + buf: bytes, bit_count: int = -1, - toggle_cs: bool = True) -> bytearray: + toggle_cs: bool = True) -> bytes: """Bitbang a SPI transfer using the specificed GPIO pins Note that this command does not handle setting a CS pin, that must be accomplished @@ -181,16 +171,16 @@ class ice40_flasher: if bit_count == -1: bit_count = len(buf) * 8 - byte_length = (bit_count+7)//8 + byte_length = (bit_count + 7) // 8 - if byte_length > (1024-8): + if byte_length > (1024 - 8): print('Message too large, bit_count:{:}'.format(bit_count)) exit(1) - + if byte_length != len(buf): print( - 'Bit count size mismatch, bit_count:{:} len(buf):{:}'.format(bit_count), - len(buf) * 8) + 'Bit count size mismatch, bit_count:{:} len(buf):{:}'.format( + bit_count, len(buf) * 8)) exit(1) header = struct.pack('>I', @@ -200,43 +190,30 @@ class ice40_flasher: msg.extend(buf) if toggle_cs: - self._write(self.FLASHER_REQUEST_SPI_BITBANG_CS,msg) - msg_in = self._read(self.FLASHER_REQUEST_SPI_BITBANG_CS, byte_length) + self._write(self.FLASHER_REQUEST_SPI_BITBANG_CS, msg) + msg_in = self._read( + self.FLASHER_REQUEST_SPI_BITBANG_CS, + byte_length) else: - self._write(self.FLASHER_REQUEST_SPI_BITBANG_NO_CS,msg) - msg_in = self._read(self.FLASHER_REQUEST_SPI_BITBANG_NO_CS, byte_length) + self._write(self.FLASHER_REQUEST_SPI_BITBANG_NO_CS, msg) + msg_in = self._read( + self.FLASHER_REQUEST_SPI_BITBANG_NO_CS, + byte_length) return msg_in - def adc_read_all(self) -> list[float]: + def adc_read_all(self) -> tuple[float, float, float]: """Read the voltage values of ADC 0, 1, and 2 The firmware will read the values for each input multiple times, and return averaged values for each input. """ - msg_in = self._read(self.FLASHER_REQUEST_ADC_READ,3*4) + msg_in = self._read(self.FLASHER_REQUEST_ADC_READ, 3 * 4) [ch0, ch1, ch2] = struct.unpack('>III', msg_in) - return ch0/1000000, ch1/1000000, ch2/1000000 + return ch0 / 1000000, ch1 / 1000000, ch2 / 1000000 + if __name__ == '__main__': flasher = ice40_flasher() print(flasher.gpio_get_all()) print(flasher.adc_read_all()) - - # for pin in range(10,13): - # flasher.gpio_set_direction(pin, True) - - # while True: - # for pin in range(10,13): - # flasher.gpio_put(pin, True) - # flasher.gpio_put(pin, False) - - flasher.gpio_set_direction(10, True) - flasher.gpio_set_direction(11, True) - flasher.gpio_set_direction(13, False) - - - buf = [0x01,0x02,0x03, 0xFE] - - while True: - flasher.spi_bitbang_inner(sck_pin=10, mosi_pin=11, miso_pin=13, buf=buf)