mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-01-01 10:56:27 -05:00
Use streamlined USB interface, make NVCM a class
This commit is contained in:
parent
4eb025a978
commit
ac174afb8f
@ -26,394 +26,440 @@ from time import sleep
|
|||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
|
|
||||||
def die(s):
|
def die(s):
|
||||||
print(s, file=sys.stderr)
|
print(s, file=sys.stderr)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
def enable(cs,reset=1):
|
class Nvcm():
|
||||||
#gpio.write(cs << cs_pin | reset << reset_pin)
|
# todo: add expected bitstream sizes
|
||||||
flasher.gpio_put(tp1_pins['ss'], cs)
|
id_table = {
|
||||||
flasher.gpio_put(tp1_pins['crst'], reset)
|
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 sendhex(s):
|
def __init__(self, pins, debug=False):
|
||||||
if debug and not s == "0500":
|
self.pins = pins
|
||||||
print("TX", s)
|
self.debug = debug
|
||||||
x = bytes.fromhex(s)
|
|
||||||
|
|
||||||
#b = dev.exchange(x, duplex=True, readlen=len(x))
|
self.flasher = usb_test.ice40_flasher()
|
||||||
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":
|
self.flasher.gpio_put(self.pins['5v_en'], False)
|
||||||
print("RX", b.hex())
|
self.flasher.gpio_put(self.pins['crst'], False)
|
||||||
return int.from_bytes(b, byteorder='big')
|
|
||||||
|
|
||||||
def delay(count: int):
|
# Configure pins for talking to ice40
|
||||||
# run the clock with no CS asserted
|
self.flasher.gpio_set_direction(pins['ss'], True)
|
||||||
#dev.exchange(b'\x00', duplex=True, readlen=count)
|
self.flasher.gpio_set_direction(pins['mosi'], True)
|
||||||
sendhex('00' * count)
|
self.flasher.gpio_set_direction(pins['sck'], True)
|
||||||
|
self.flasher.gpio_set_direction(pins['miso'], False)
|
||||||
|
self.flasher.gpio_set_direction(pins['5v_en'], True)
|
||||||
|
self.flasher.gpio_set_direction(pins['crst'], True)
|
||||||
|
self.flasher.gpio_set_direction(pins['cdne'], False)
|
||||||
|
|
||||||
def tck(count: int, dly: int = 0):
|
self.flasher.spi_pins_set(
|
||||||
delay(count >> 3)
|
pins['sck'],
|
||||||
delay(count >> 3)
|
pins['ss'],
|
||||||
delay(count >> 3)
|
pins['mosi'],
|
||||||
delay(count >> 3)
|
pins['miso']
|
||||||
delay(count >> 3)
|
)
|
||||||
delay(count >> 3)
|
|
||||||
|
|
||||||
def init():
|
def power_on(self):
|
||||||
if debug:
|
self.flasher.gpio_put(self.pins['5v_en'], True)
|
||||||
print("init")
|
|
||||||
enable(1, 1)
|
|
||||||
enable(1, 0) # reset high
|
|
||||||
sleep(0.15)
|
|
||||||
|
|
||||||
enable(0, 0) # enable and reset high
|
def power_off(self):
|
||||||
sleep(0.12)
|
self.flasher.gpio_put(self.pins['5v_en'], False)
|
||||||
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):
|
def enable(self,cs,reset=1):
|
||||||
for i in range(0,count):
|
#gpio.write(cs << cs_pin | reset << reset_pin)
|
||||||
tck(5000)
|
self.flasher.gpio_put(self.pins['ss'], cs)
|
||||||
enable(0)
|
self.flasher.gpio_put(self.pins['crst'], reset)
|
||||||
x = sendhex("0500")
|
|
||||||
enable(1)
|
|
||||||
|
|
||||||
#print("x=%04x" %(x))
|
def sendhex(self,s):
|
||||||
|
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 = 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):
|
||||||
|
if self.debug and not s == "0500":
|
||||||
|
print("TX", s)
|
||||||
|
x = bytes.fromhex(s)
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
if (x & 0x00c1) == 0:
|
def delay(self,count: int):
|
||||||
return True
|
# run the clock with no CS asserted
|
||||||
|
#dev.exchange(b'\x00', duplex=True, readlen=count)
|
||||||
|
self.sendhex('00' * count)
|
||||||
|
|
||||||
print("status failed to clear", file=sys.stdout)
|
def tck(self,count: int):
|
||||||
return False
|
self.delay(count >> 3)
|
||||||
|
self.delay(count >> 3)
|
||||||
|
self.delay(count >> 3)
|
||||||
|
self.delay(count >> 3)
|
||||||
|
self.delay(count >> 3)
|
||||||
|
self.delay(count >> 3)
|
||||||
|
|
||||||
def nvcm_command(cmd):
|
def init(self):
|
||||||
enable(0)
|
if self.debug:
|
||||||
sendhex(cmd)
|
print("init")
|
||||||
enable(1)
|
self.enable(1, 1)
|
||||||
if not status_wait():
|
self.enable(1, 0) # reset high
|
||||||
return False
|
sleep(0.15)
|
||||||
tck(8)
|
|
||||||
return True
|
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 nvcm_pgm_enable():
|
def status_wait(self,count=1000):
|
||||||
return nvcm_command("06")
|
for i in range(0,count):
|
||||||
def nvcm_pgm_disable():
|
self.tck(5000)
|
||||||
return nvcm_command("04")
|
ret = self.sendhex_cs("0500")
|
||||||
|
x = int.from_bytes(ret, byteorder='big')
|
||||||
def nvcm_enable_access():
|
|
||||||
# ! Shift in Access-NVCM instruction;
|
#print("x=%04x" %(x))
|
||||||
# SMCInstruction[1] = 0x70807E99557E;
|
|
||||||
return nvcm_command("7eaa997e010e")
|
if (x & 0x00c1) == 0:
|
||||||
|
return True
|
||||||
# Returns a big integer
|
|
||||||
def nvcm_read(address, length=8, cmd=0x03):
|
print("status failed to clear", file=sys.stdout)
|
||||||
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")
|
|
||||||
|
|
||||||
def nvcm_write(address, data, cmd=0x02):
|
|
||||||
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
|
return False
|
||||||
|
|
||||||
tck(8)
|
def command(self,cmd):
|
||||||
return True
|
self.sendhex_cs(cmd)
|
||||||
|
if not self.status_wait():
|
||||||
|
return False
|
||||||
|
self.tck(8)
|
||||||
|
return True
|
||||||
|
|
||||||
def nvcm_bank_select(bank):
|
def pgm_enable(self):
|
||||||
return nvcm_write(cmd=0x83, address=0x000025, data="%02x" % (bank))
|
return self.command("06")
|
||||||
|
|
||||||
def nvcm_select_nvcm():
|
def pgm_disable(self):
|
||||||
# ! Shift in Restore Access-NVCM instruction;
|
return self.command("04")
|
||||||
# SDR 40 TDI(0x00A40000C1);
|
|
||||||
return nvcm_bank_select(0x00)
|
|
||||||
|
|
||||||
def nvcm_select_trim():
|
def enable_access(self):
|
||||||
# ! Shift in Trim setup-NVCM instruction;
|
# ! Shift in Access-NVCM instruction;
|
||||||
# SDR 40 TDI(0x08A40000C1);
|
# SMCInstruction[1] = 0x70807E99557E;
|
||||||
return nvcm_bank_select(0x10)
|
return self.command("7eaa997e010e")
|
||||||
|
|
||||||
def nvcm_select_sig():
|
def read(self, address, length=8, cmd=0x03):
|
||||||
# ! Shift in Access Silicon Signature instruction;
|
"""Returns a big integer"""
|
||||||
# IDInstruction[1] = 0x04A40000C1;
|
# enable(0)
|
||||||
# SDR 40 TDI(IDInstruction[1]);
|
# sendhex("%02x%06x" % (cmd, address))
|
||||||
return nvcm_bank_select(0x20)
|
# sendhex("00" * 9) # dummy bytes
|
||||||
|
# x = 0
|
||||||
|
# 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
|
||||||
|
ret = self.sendhex_cs(msg)
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
for i in range(0,length):
|
||||||
|
x = x << 8 | ret[i + 4+9]
|
||||||
|
return x
|
||||||
|
|
||||||
def nvcm_read_trim():
|
def read_bytes(self, address, length=8):
|
||||||
# ! Shift in Access-NVCM instruction;
|
"""Returns a byte array of the contents"""
|
||||||
# SMCInstruction[1] = 0x70807E99557E;
|
return self.read(address, length).to_bytes(length, byteorder="big")
|
||||||
if not nvcm_enable_access():
|
|
||||||
return
|
|
||||||
|
|
||||||
# ! Shift in READ_RF(0x84) instruction;
|
def write(self,address, data, cmd=0x02):
|
||||||
# SDR 104 TDI(0x00000000000000000004000021);
|
self.sendhex_cs("%02x%06x" % (cmd, address) + data)
|
||||||
x = nvcm_read(cmd=0x84, address=0x000020, length=8)
|
|
||||||
tck(8)
|
if not self.status_wait():
|
||||||
|
print("WRITE FAILED: cmd=%02x address=%06x data=%s" % (cmd, address, data.hex()), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.tck(8)
|
||||||
|
return True
|
||||||
|
|
||||||
#print("FSM Trim Register %x" % (x))
|
def bank_select(self,bank):
|
||||||
|
return self.write(cmd=0x83, address=0x000025, data="%02x" % (bank))
|
||||||
|
|
||||||
nvcm_select_nvcm()
|
def select_nvcm(self):
|
||||||
return x
|
# ! Shift in Restore Access-NVCM instruction;
|
||||||
|
# SDR 40 TDI(0x00A40000C1);
|
||||||
|
return self.bank_select(0x00)
|
||||||
|
|
||||||
|
def select_trim(self):
|
||||||
|
# ! Shift in Trim setup-NVCM instruction;
|
||||||
|
# SDR 40 TDI(0x08A40000C1);
|
||||||
|
return self.bank_select(0x10)
|
||||||
|
|
||||||
|
def select_sig(self):
|
||||||
|
# ! Shift in Access Silicon Signature instruction;
|
||||||
|
# IDInstruction[1] = 0x04A40000C1;
|
||||||
|
# SDR 40 TDI(IDInstruction[1]);
|
||||||
|
return self.bank_select(0x20)
|
||||||
|
|
||||||
def nvcm_write_trim(data):
|
def read_trim(self):
|
||||||
# ! Setup Programming Parameter in Trim Registers;
|
# ! Shift in Access-NVCM instruction;
|
||||||
# ! Shift in Trim setup-NVCM instruction;
|
# SMCInstruction[1] = 0x70807E99557E;
|
||||||
# TRIMInstruction[1] = 0x000000430F4FA80004000041;
|
if not self.enable_access():
|
||||||
return nvcm_write(cmd=0x82, address=0x000020, data=data)
|
return
|
||||||
|
|
||||||
|
# ! 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))
|
||||||
|
|
||||||
|
self.select_nvcm()
|
||||||
|
return x
|
||||||
|
|
||||||
|
def write_trim(self,data):
|
||||||
|
# ! Setup Programming Parameter in Trim Registers;
|
||||||
|
# ! Shift in Trim setup-NVCM instruction;
|
||||||
|
# TRIMInstruction[1] = 0x000000430F4FA80004000041;
|
||||||
|
return self.write(cmd=0x82, address=0x000020, data=data)
|
||||||
|
|
||||||
def nvcm_enable():
|
def nvcm_enable(self):
|
||||||
if debug:
|
if self.debug:
|
||||||
print("nvcm_enable")
|
print("enable")
|
||||||
# ! Shift in Access-NVCM instruction;
|
# ! Shift in Access-NVCM instruction;
|
||||||
# SMCInstruction[1] = 0x70807E99557E;
|
# SMCInstruction[1] = 0x70807E99557E;
|
||||||
if not nvcm_enable_access():
|
if not self.enable_access():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# ! 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):
|
||||||
|
# ! 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)
|
||||||
|
|
||||||
# ! Setup Reading Parameter in Trim Registers;
|
def trim_blank_check(self):
|
||||||
# ! Shift in Trim setup-NVCM instruction;
|
print ("NVCM Trim_Parameter_OTP blank check");
|
||||||
# TRIMInstruction[1] = 0x000000230000000004000041;
|
|
||||||
if debug:
|
if not self.select_trim():
|
||||||
print("setup_nvcm")
|
return
|
||||||
return nvcm_write_trim("00000000c4000000")
|
|
||||||
|
x = self.read(0x000020, 1)
|
||||||
def nvcm_enable_trim():
|
self.select_nvcm()
|
||||||
# ! 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
|
|
||||||
|
|
||||||
reset(1)
|
|
||||||
tck(8, 1)
|
|
||||||
rest(0)
|
|
||||||
tck(8, 2000)
|
|
||||||
|
|
||||||
def nvcm_trim_blank_check():
|
|
||||||
print ("NVCM Trim_Parameter_OTP blank check");
|
|
||||||
|
|
||||||
if not nvcm_select_trim():
|
|
||||||
return
|
|
||||||
|
|
||||||
x = nvcm_read(0x000020, 1)
|
|
||||||
nvcm_select_nvcm()
|
|
||||||
|
|
||||||
if x != 0:
|
|
||||||
die ("NVCM Trim_Parameter_OTP Block is not blank. (%02x)" % x);
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def nvcm_blank_check(total_fuse):
|
|
||||||
nvcm_select_nvcm()
|
|
||||||
|
|
||||||
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:
|
if x != 0:
|
||||||
print ("%08x: NVCM Main Memory Block is not blank." % (i), file=sys.stderr)
|
die ("NVCM Trim_Parameter_OTP Block is not blank. (%02x)" % x);
|
||||||
status = False
|
|
||||||
#break
|
return True
|
||||||
|
|
||||||
|
def blank_check(self,total_fuse):
|
||||||
|
self.select_nvcm()
|
||||||
|
|
||||||
|
status = True
|
||||||
|
print ("NVCM main memory blank check");
|
||||||
|
contents = self.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
|
||||||
|
|
||||||
|
self.select_nvcm()
|
||||||
|
return status
|
||||||
|
|
||||||
nvcm_select_nvcm()
|
def program(self,rows):
|
||||||
return status
|
self.select_nvcm()
|
||||||
|
|
||||||
|
if not self.enable_trim():
|
||||||
|
return False
|
||||||
|
|
||||||
|
print ("NVCM Program main memory")
|
||||||
|
|
||||||
|
if not self.pgm_enable():
|
||||||
|
return False
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
def nvcm_program(rows):
|
def write_trim_pages(self,lock_bits):
|
||||||
nvcm_select_nvcm()
|
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);
|
||||||
|
# ! 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 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.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
|
||||||
|
|
||||||
if not nvcm_enable_trim():
|
def trim_secure(self):
|
||||||
return False
|
print ("NVCM Secure")
|
||||||
|
trim = self.read_trim()
|
||||||
print ("NVCM Program main memory")
|
if (trim >> 60) & 0x3 != 0:
|
||||||
|
print("NVCM already secure? trim=%016x" % (trim), file=sys.stderr)
|
||||||
if not nvcm_pgm_enable():
|
|
||||||
return False
|
return self.write_trim_pages("3000000100000000")
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
nvcm_pgm_disable()
|
|
||||||
|
|
||||||
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_enable_trim():
|
|
||||||
die("write trim command failed")
|
|
||||||
|
|
||||||
if not nvcm_select_trim():
|
|
||||||
die("select trim 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")
|
|
||||||
|
|
||||||
nvcm_pgm_disable()
|
|
||||||
|
|
||||||
# verify a read back
|
|
||||||
x = nvcm_read(0x000020, 8)
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
return nvcm_write_trim_pages("3000000100000000")
|
|
||||||
|
|
||||||
|
|
||||||
def nvcm_trim_program():
|
def trim_program(self):
|
||||||
print ("NVCM Program Trim_Parameter_OTP");
|
print ("NVCM Program Trim_Parameter_OTP");
|
||||||
return nvcm_write_trim_pages("0015f2f1c4000000")
|
return self.write_trim_pages("0015f2f1c4000000")
|
||||||
|
|
||||||
def nvcm_info():
|
def info(self):
|
||||||
nvcm_select_sig()
|
self.select_sig()
|
||||||
sig1 = nvcm_read(0x000000, 8)
|
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,
|
||||||
|
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))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
nvcm_select_sig()
|
def read_file(self,filename):
|
||||||
sig2 = nvcm_read(0x000008, 8)
|
self.select_nvcm()
|
||||||
|
|
||||||
# have to switch back to nvcm bank before switching to trim?
|
total_fuse = 104090
|
||||||
nvcm_select_nvcm()
|
|
||||||
trim = nvcm_read_trim()
|
contents = b''
|
||||||
|
|
||||||
nvcm_select_nvcm()
|
for offset in range(0,total_fuse,8):
|
||||||
|
if offset % 1024 == 0:
|
||||||
nvcm_select_trim()
|
print("%6d / %6d bytes" % (offset, total_fuse))
|
||||||
trim0 = nvcm_read(0x000020, 8)
|
contents += self.read_bytes(offset, 8)
|
||||||
|
|
||||||
nvcm_select_trim()
|
if filename == '-':
|
||||||
trim1 = nvcm_read(0x000060, 8)
|
with os.fdopen(sys.stdout.fileno(), "wb", closefd=False) as f:
|
||||||
|
f.write(contents)
|
||||||
nvcm_select_trim()
|
f.flush()
|
||||||
trim2 = nvcm_read(0x0000a0, 8)
|
else:
|
||||||
|
with open(filename, "wb") as f:
|
||||||
nvcm_select_trim()
|
f.write(contents)
|
||||||
trim3 = nvcm_read(0x0000e0, 8)
|
f.flush()
|
||||||
|
|
||||||
nvcm_select_nvcm()
|
|
||||||
|
|
||||||
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("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 nvcm_read_file(filename):
|
|
||||||
nvcm_select_nvcm()
|
|
||||||
|
|
||||||
total_fuse = 104090
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -456,58 +502,55 @@ def bitstream2nvcm(bitstream):
|
|||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
def sleep_flash():
|
def sleep_flash(pins):
|
||||||
|
flasher = usb_test.ice40_flasher()
|
||||||
|
|
||||||
# Disable board power
|
# Disable board power
|
||||||
flasher.gpio_put(tp1_pins['5v_en'], False)
|
flasher.gpio_put(pins['5v_en'], False)
|
||||||
flasher.gpio_set_direction(tp1_pins['5v_en'], True)
|
flasher.gpio_set_direction(pins['5v_en'], True)
|
||||||
|
|
||||||
# Pull CRST low to prevent FPGA from starting
|
# Pull CRST low to prevent FPGA from starting
|
||||||
flasher.gpio_set_direction(tp1_pins['crst'], True)
|
flasher.gpio_set_direction(pins['crst'], True)
|
||||||
flasher.gpio_put(tp1_pins['crst'], False)
|
flasher.gpio_put(pins['crst'], False)
|
||||||
|
|
||||||
# Enable board power
|
# Enable board power
|
||||||
flasher.gpio_put(tp1_pins['5v_en'], True)
|
flasher.gpio_put(pins['5v_en'], True)
|
||||||
|
|
||||||
# Configure pins for talking to flash
|
# Configure pins for talking to flash
|
||||||
flasher.gpio_set_direction(tp1_pins['ss'], True)
|
flasher.gpio_set_direction(pins['ss'], True)
|
||||||
flasher.gpio_set_direction(tp1_pins['mosi'], False)
|
flasher.gpio_set_direction(pins['mosi'], False)
|
||||||
flasher.gpio_set_direction(tp1_pins['sck'], True)
|
flasher.gpio_set_direction(pins['sck'], True)
|
||||||
flasher.gpio_set_direction(tp1_pins['miso'], True)
|
flasher.gpio_set_direction(pins['miso'], True)
|
||||||
|
|
||||||
flasher.gpio_put(tp1_pins['ss'], False)
|
flasher.spi_pins_set(
|
||||||
flasher.spi_bitbang(sck_pin=tp1_pins['sck'], mosi_pin=tp1_pins['miso'], miso_pin=tp1_pins['mosi'], buf=[0xAB])
|
pins['sck'],
|
||||||
flasher.gpio_put(tp1_pins['ss'], True)
|
pins['ss'],
|
||||||
|
pins['miso'],
|
||||||
|
pins['mosi']
|
||||||
|
)
|
||||||
|
|
||||||
|
flasher.spi_bitbang([0xAB])
|
||||||
|
|
||||||
# Confirm we can talk to flash
|
# Confirm we can talk to flash
|
||||||
flasher.gpio_put(tp1_pins['ss'], False)
|
data = flasher.spi_bitbang([0x9f, 0,0])
|
||||||
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]))
|
print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data]))
|
||||||
assert(data == bytes([0xff, 0xef, 0x40]))
|
assert(data == bytes([0xff, 0xef, 0x40]))
|
||||||
|
|
||||||
# Test that the flash will ignore a sleep command that doesn't start on the first byte
|
# 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([0, 0xb9])
|
||||||
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
|
# Confirm we can talk to flash
|
||||||
flasher.gpio_put(tp1_pins['ss'], False)
|
data = flasher.spi_bitbang([0x9f, 0,0])
|
||||||
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]))
|
print('flash ID while awake:', ' '.join(['{:02x}'.format(b) for b in data]))
|
||||||
assert(data == bytes([0xff, 0xef, 0x40]))
|
assert(data == bytes([0xff, 0xef, 0x40]))
|
||||||
|
|
||||||
# put the flash to sleep
|
# put the flash to sleep
|
||||||
flasher.gpio_put(tp1_pins['ss'], False)
|
flasher.spi_bitbang([0xb9])
|
||||||
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
|
# Confirm flash is asleep
|
||||||
flasher.gpio_put(tp1_pins['ss'], False)
|
data = flasher.spi_bitbang(buf=[0x9f, 0,0])
|
||||||
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]))
|
print('flash ID while asleep:', ' '.join(['{:02x}'.format(b) for b in data]))
|
||||||
assert(data == bytes([0xff, 0xff, 0xff]))
|
assert(data == bytes([0xff, 0xff, 0xff]))
|
||||||
@ -573,7 +616,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
debug = args.verbose
|
|
||||||
|
|
||||||
if not args.good_enough \
|
if not args.good_enough \
|
||||||
and (args.write_file or args.set_secure):
|
and (args.write_file or args.set_secure):
|
||||||
@ -582,7 +624,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# Instantiate a SPI controller, with separately managed CS line
|
# Instantiate a SPI controller, with separately managed CS line
|
||||||
#spi = SpiController()
|
#spi = SpiController()
|
||||||
flasher = usb_test.ice40_flasher()
|
|
||||||
|
|
||||||
# Configure the first interface (IF/1) of the FTDI device as a SPI controller
|
# Configure the first interface (IF/1) of the FTDI device as a SPI controller
|
||||||
#spi.configure(args.port)
|
#spi.configure(args.port)
|
||||||
@ -600,6 +641,11 @@ if __name__ == "__main__":
|
|||||||
#gpio.set_direction(1 << reset_pin | 1 << cs_pin, 1 << reset_pin | 1 << cs_pin)
|
#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
|
# Enable power to the FPGA, then set both reset and CS pins high
|
||||||
|
|
||||||
|
# # Reset pin values
|
||||||
|
# for pin in tp1_pins:
|
||||||
|
# flasher.gpio_set_direction(tp1_pins[pin], False)
|
||||||
|
|
||||||
tp1_pins = {
|
tp1_pins = {
|
||||||
'5v_en' : 7,
|
'5v_en' : 7,
|
||||||
'sck' : 10,
|
'sck' : 10,
|
||||||
@ -610,30 +656,18 @@ if __name__ == "__main__":
|
|||||||
'cdne' : 15
|
'cdne' : 15
|
||||||
}
|
}
|
||||||
|
|
||||||
# # Reset pin values
|
|
||||||
# for pin in tp1_pins:
|
|
||||||
# flasher.gpio_set_direction(tp1_pins[pin], False)
|
|
||||||
|
|
||||||
if args.sleep_flash:
|
if args.sleep_flash:
|
||||||
sleep_flash()
|
sleep_flash(tp1_pins)
|
||||||
|
|
||||||
# Configure pins for talking to ice40
|
nvcm = Nvcm(tp1_pins, debug=args.verbose)
|
||||||
flasher.gpio_set_direction(tp1_pins['ss'], True)
|
nvcm.power_on()
|
||||||
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
|
# # Turn on ICE40 in CRAM boot mode
|
||||||
|
nvcm.init() or exit(1)
|
||||||
enable(1, 0) # enable and reset high
|
nvcm.nvcm_enable() or exit(1)
|
||||||
sleep(0.2)
|
|
||||||
enable(1, 1) # enable low, reset high
|
|
||||||
|
|
||||||
init() or exit(1)
|
|
||||||
nvcm_enable() or exit(1)
|
|
||||||
|
|
||||||
if args.read_info:
|
if args.read_info:
|
||||||
nvcm_info() or exit(1)
|
nvcm.info() or exit(1)
|
||||||
|
|
||||||
if args.write_file:
|
if args.write_file:
|
||||||
with open(args.write_file, "rb") as f:
|
with open(args.write_file, "rb") as f:
|
||||||
@ -644,25 +678,25 @@ if __name__ == "__main__":
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
if not args.ignore_blank:
|
if not args.ignore_blank:
|
||||||
nvcm_trim_blank_check() or exit(1)
|
nvcm.trim_blank_check() or exit(1)
|
||||||
# how much should we check?
|
# how much should we check?
|
||||||
nvcm_blank_check(0x100) or exit(1)
|
nvcm.blank_check(0x100) or exit(1)
|
||||||
|
|
||||||
# this is it!
|
# this is it!
|
||||||
nvcm_program(cmds) or exit(1)
|
nvcm.program(cmds) or exit(1)
|
||||||
|
|
||||||
# update the trim to boot from nvcm
|
# update the trim to boot from nvcm
|
||||||
nvcm_trim_program() or exit(1)
|
nvcm.trim_program() or exit(1)
|
||||||
|
|
||||||
if args.read_file:
|
if args.read_file:
|
||||||
# read back after writing to the NVCM
|
# read back after writing to the NVCM
|
||||||
nvcm_read_file(args.read_file) or exit(1)
|
nvcm.read_file(args.read_file) or exit(1)
|
||||||
|
|
||||||
if args.set_secure:
|
if args.set_secure:
|
||||||
nvcm_trim_secure() or exit(1)
|
nvcm.trim_secure() or exit(1)
|
||||||
|
|
||||||
if args.do_boot:
|
if args.do_boot:
|
||||||
# hold reset low for half a second
|
# hold reset low for half a second
|
||||||
enable(1,0)
|
nvcm.enable(1,0)
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
enable(1,1)
|
nvcm.enable(1,1)
|
||||||
|
@ -9,8 +9,11 @@ class ice40_flasher:
|
|||||||
FLASHER_REQUEST_PULLUPS_SET = 0x12
|
FLASHER_REQUEST_PULLUPS_SET = 0x12
|
||||||
FLASHER_REQUEST_PIN_VALUES_SET = 0x20
|
FLASHER_REQUEST_PIN_VALUES_SET = 0x20
|
||||||
FLASHER_REQUEST_PIN_VALUES_GET = 0x30
|
FLASHER_REQUEST_PIN_VALUES_GET = 0x30
|
||||||
FLASHER_REQUEST_SPI_BITBANG = 0x40
|
FLASHER_REQUEST_SPI_BITBANG_CS = 0x41
|
||||||
|
FLASHER_REQUEST_SPI_BITBANG_NO_CS = 0x42
|
||||||
|
FLASHER_REQUEST_SPI_PINS_SET = 0x43
|
||||||
FLASHER_REQUEST_ADC_READ = 0x50
|
FLASHER_REQUEST_ADC_READ = 0x50
|
||||||
|
FLASHER_REQUEST_BOOTLOADRE = 0xFF
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# self.dev = None
|
# self.dev = None
|
||||||
@ -32,10 +35,16 @@ class ice40_flasher:
|
|||||||
#self.dev.close()
|
#self.dev.close()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _write(self, request_id, data):
|
def _write(self, request_id: int, data: bytes):
|
||||||
self.dev.ctrl_transfer(0x40, request_id,0,0,data)
|
self.dev.ctrl_transfer(0x40, request_id,0,0,data)
|
||||||
|
|
||||||
def _read(self, request_id, length):
|
def _write_bulk(self, request_id: int, data: bytes):
|
||||||
|
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:
|
# Request type:
|
||||||
# bit 7: direction 0:host to device (OUT), 1: device to host (IN)
|
# bit 7: direction 0:host to device (OUT), 1: device to host (IN)
|
||||||
@ -44,21 +53,6 @@ class ice40_flasher:
|
|||||||
ret = self.dev.ctrl_transfer(0xC0, request_id,0,0,length)
|
ret = self.dev.ctrl_transfer(0xC0, request_id,0,0,length)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
# def led_set(self, value: bool) -> None:
|
|
||||||
# """Set the state of the onboard LED
|
|
||||||
|
|
||||||
# Keyword arguments:
|
|
||||||
# value -- True: On, False: Off
|
|
||||||
# """
|
|
||||||
# msg = struct.pack('>BBB',
|
|
||||||
# 0x0,
|
|
||||||
# 0x00,
|
|
||||||
# (1 if value else 0)
|
|
||||||
# )
|
|
||||||
|
|
||||||
# #print(['{:02x}'.format(b) for b in msg])
|
|
||||||
# self.dev.write(msg)
|
|
||||||
|
|
||||||
def gpio_set_direction(self, pin: int, direction: bool) -> None:
|
def gpio_set_direction(self, pin: int, direction: bool) -> None:
|
||||||
"""Set the direction of a single GPIO pin
|
"""Set the direction of a single GPIO pin
|
||||||
|
|
||||||
@ -71,6 +65,7 @@ class ice40_flasher:
|
|||||||
((1 if direction else 0)<<pin),
|
((1 if direction else 0)<<pin),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#self._write_bulk(self.FLASHER_REQUEST_PIN_DIRECTION_SET, msg)
|
||||||
self._write(self.FLASHER_REQUEST_PIN_DIRECTION_SET, msg)
|
self._write(self.FLASHER_REQUEST_PIN_DIRECTION_SET, msg)
|
||||||
|
|
||||||
def gpio_set_pulls(self, pin: int, pullup: bool, pulldown: bool) -> None:
|
def gpio_set_pulls(self, pin: int, pullup: bool, pulldown: bool) -> None:
|
||||||
@ -121,23 +116,39 @@ class ice40_flasher:
|
|||||||
|
|
||||||
return ((gpio_states >> pin) & 0x01) == 0x01
|
return ((gpio_states >> pin) & 0x01) == 0x01
|
||||||
|
|
||||||
def spi_bitbang(
|
def spi_pins_set(
|
||||||
self,
|
self,
|
||||||
sck_pin: int,
|
sck_pin: int,
|
||||||
|
cs_pin: int,
|
||||||
mosi_pin: int,
|
mosi_pin: int,
|
||||||
miso_pin: int,
|
miso_pin: int) -> None:
|
||||||
buf: bytearray) -> bytearray:
|
"""Set the pins to use for SPI transfers
|
||||||
"""Bitbang a SPI transfer using the specificed GPIO pins
|
|
||||||
|
|
||||||
Note that this command does not handle setting a CS pin, that must be accomplished
|
|
||||||
separately, for instance by calling gpio_set() on the pin controlling the CS line.
|
|
||||||
|
|
||||||
Keyword arguments:
|
Keyword arguments:
|
||||||
sck_pin -- GPIO pin number to use as the SCK signal
|
sck_pin -- GPIO pin number to use as the SCK signal
|
||||||
|
cs_pin -- GPIO pin number to use as the CS signal
|
||||||
mosi_pin -- GPIO pin number to use as the MOSI signal
|
mosi_pin -- GPIO pin number to use as the MOSI signal
|
||||||
miso_pin -- GPIO pin number to use as the MISO signal
|
miso_pin -- GPIO pin number to use as the MISO signal
|
||||||
|
"""
|
||||||
|
header = struct.pack('>BBBB',
|
||||||
|
sck_pin,
|
||||||
|
cs_pin,
|
||||||
|
mosi_pin,
|
||||||
|
miso_pin)
|
||||||
|
msg = bytearray()
|
||||||
|
msg.extend(header)
|
||||||
|
|
||||||
|
self._write(self.FLASHER_REQUEST_SPI_PINS_SET,msg)
|
||||||
|
|
||||||
|
def spi_bitbang(
|
||||||
|
self,
|
||||||
|
buf: bytearray,
|
||||||
|
toggle_cs: bool = True) -> bytearray:
|
||||||
|
"""Bitbang a SPI transfer
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
buf -- Byte buffer to send. If the bit_count is smaller than the buffer size, some data will not be sent.
|
buf -- Byte buffer to send. If the bit_count is smaller than the buffer size, some data will not be sent.
|
||||||
bit_count -- (Optional) Number of bits (not bytes) to bitbang. If left unspecificed, defaults to the size of buf.
|
toggle_cs: (Optional) If true, toggle the CS line
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ret = bytearray()
|
ret = bytearray()
|
||||||
@ -147,31 +158,25 @@ class ice40_flasher:
|
|||||||
chunk = buf[i:i + max_chunk_size]
|
chunk = buf[i:i + max_chunk_size]
|
||||||
ret.extend(
|
ret.extend(
|
||||||
self.spi_bitbang_inner(
|
self.spi_bitbang_inner(
|
||||||
sck_pin=sck_pin,
|
buf=chunk,
|
||||||
mosi_pin=mosi_pin,
|
toggle_cs=toggle_cs))
|
||||||
miso_pin=miso_pin,
|
|
||||||
buf=chunk))
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def spi_bitbang_inner(
|
def spi_bitbang_inner(
|
||||||
self,
|
self,
|
||||||
sck_pin: int,
|
|
||||||
mosi_pin: int,
|
|
||||||
miso_pin: int,
|
|
||||||
buf: bytearray,
|
buf: bytearray,
|
||||||
bit_count: int = -1) -> bytearray:
|
bit_count: int = -1,
|
||||||
|
toggle_cs: bool = True) -> bytearray:
|
||||||
"""Bitbang a SPI transfer using the specificed GPIO pins
|
"""Bitbang a SPI transfer using the specificed GPIO pins
|
||||||
|
|
||||||
Note that this command does not handle setting a CS pin, that must be accomplished
|
Note that this command does not handle setting a CS pin, that must be accomplished
|
||||||
separately, for instance by calling gpio_set() on the pin controlling the CS line.
|
separately, for instance by calling gpio_set() on the pin controlling the CS line.
|
||||||
|
|
||||||
Keyword arguments:
|
Keyword arguments:
|
||||||
sck_pin -- GPIO pin number to use as the SCK signal
|
|
||||||
mosi_pin -- GPIO pin number to use as the MOSI signal
|
|
||||||
miso_pin -- GPIO pin number to use as the MISO signal
|
|
||||||
buf -- Byte buffer to send. If the bit_count is smaller than the buffer size, some data will not be sent.
|
buf -- Byte buffer to send. If the bit_count is smaller than the buffer size, some data will not be sent.
|
||||||
bit_count -- (Optional) Number of bits (not bytes) to bitbang. If left unspecificed, defaults to the size of buf.
|
bit_count -- (Optional) Number of bits (not bytes) to bitbang. If left unspecificed, defaults to the size of buf.
|
||||||
|
toggle_cs: (Optional) If true, toggle the CS line
|
||||||
"""
|
"""
|
||||||
if bit_count == -1:
|
if bit_count == -1:
|
||||||
bit_count = len(buf) * 8
|
bit_count = len(buf) * 8
|
||||||
@ -188,18 +193,18 @@ class ice40_flasher:
|
|||||||
len(buf) * 8)
|
len(buf) * 8)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
header = struct.pack('>BBBI',
|
header = struct.pack('>I',
|
||||||
sck_pin,
|
|
||||||
mosi_pin,
|
|
||||||
miso_pin,
|
|
||||||
bit_count)
|
bit_count)
|
||||||
msg = bytearray()
|
msg = bytearray()
|
||||||
msg.extend(header)
|
msg.extend(header)
|
||||||
msg.extend(buf)
|
msg.extend(buf)
|
||||||
|
|
||||||
self._write(self.FLASHER_REQUEST_SPI_BITBANG,msg)
|
if toggle_cs:
|
||||||
|
self._write(self.FLASHER_REQUEST_SPI_BITBANG_CS,msg)
|
||||||
msg_in = self._read(self.FLASHER_REQUEST_SPI_BITBANG, byte_length)
|
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)
|
||||||
|
|
||||||
return msg_in
|
return msg_in
|
||||||
|
|
||||||
@ -232,15 +237,6 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
|
|
||||||
buf = [0x01,0x02,0x03, 0xFE]
|
buf = [0x01,0x02,0x03, 0xFE]
|
||||||
header = struct.pack('>BBBI',
|
|
||||||
10,
|
|
||||||
11,
|
|
||||||
13,
|
|
||||||
len(buf)*8)
|
|
||||||
msg = bytearray()
|
|
||||||
msg.extend(header)
|
|
||||||
msg.extend(buf)
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
flasher.spi_bitbang_inner(sck_pin=10, mosi_pin=11, miso_pin=13, buf=buf)
|
flasher.spi_bitbang_inner(sck_pin=10, mosi_pin=11, miso_pin=13, buf=buf)
|
||||||
|
Loading…
Reference in New Issue
Block a user