mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2024-12-29 17:36:26 -05:00
NVCM programmer: add 'verify' option
This commit is contained in:
parent
acdc900b3b
commit
49c3b35a4b
@ -1,5 +1,22 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
PYTHON_FILES = \
|
||||
usb_test.py \
|
||||
icenvcm.py \
|
||||
icebin2nvcm.py
|
||||
|
||||
lint:
|
||||
autopep8 --in-place --max-line-length 70 --aggressive --aggressive usb_test.py icenvcm.py
|
||||
mypy --disallow-untyped-defs usb_test.py icenvcm.py
|
||||
pycodestyle --max-line-length 70 usb_test.py icenvcm.py
|
||||
autopep8 --in-place --max-line-length 70 --aggressive --aggressive ${PYTHON_FILES}
|
||||
mypy --disallow-untyped-defs ${PYTHON_FILES}
|
||||
pycodestyle --max-line-length 70 ${PYTHON_FILES}
|
||||
|
||||
# Check that the NVCM generator gives a correct output for a known binary
|
||||
verify-nvcm:
|
||||
./icebin2nvcm.py nvcm_test/application_fpga.bin verify.nvcm
|
||||
cmp verify.nvcm nvcm_test/application_fpga.nvcm
|
||||
|
||||
verify:
|
||||
time ./icenvcm.py --verify nvcm_test/application_fpga.bin
|
||||
|
||||
program:
|
||||
time ./icenvcm.py --my-design-is-good-enough --write ../application_fpga/application_fpga.bin --ignore-blank
|
||||
|
81
hw/production_test/icebin2nvcm.py
Executable file
81
hw/production_test/icebin2nvcm.py
Executable file
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
#
|
||||
# bistream to NVCM command conversion is based on majbthrd's work in
|
||||
# https://github.com/YosysHQ/icestorm/pull/272
|
||||
#
|
||||
|
||||
|
||||
def icebin2nvcm(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'):
|
||||
break
|
||||
|
||||
if origin == len(bitstream):
|
||||
raise Exception("Preamble not found")
|
||||
|
||||
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
|
||||
|
||||
# todo: what is the correct size?
|
||||
print(len(bitstream))
|
||||
|
||||
rows = []
|
||||
|
||||
rows.append('06')
|
||||
|
||||
for pos in range(origin, len(bitstream), 8):
|
||||
row = bitstream[pos:pos + 8]
|
||||
|
||||
# pad out to 8-bytes
|
||||
row += b'\0' * (8 - len(row))
|
||||
|
||||
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)
|
||||
|
||||
row_str = "02%06x%s" % (nvcm_addr, row.hex())
|
||||
row_str = ' '.join([row_str[i:i + 2]
|
||||
for i in range(0, len(row_str), 2)]) + ' '
|
||||
|
||||
rows.append(row_str)
|
||||
|
||||
rows.append('04')
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('infile',
|
||||
type=str,
|
||||
help='input bin file')
|
||||
|
||||
parser.add_argument('outfile',
|
||||
type=str,
|
||||
help='output nvcm file')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.infile, 'rb') as f_in:
|
||||
bitstream = f_in.read()
|
||||
|
||||
cmds = icebin2nvcm(bitstream)
|
||||
|
||||
with open(args.outfile, 'w') as f_out:
|
||||
for cmd in cmds:
|
||||
f_out.write(cmd)
|
||||
f_out.write('\n')
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2021
|
||||
#
|
||||
@ -22,6 +22,7 @@
|
||||
#
|
||||
|
||||
import usb_test
|
||||
from icebin2nvcm import icebin2nvcm
|
||||
import sys
|
||||
from time import sleep
|
||||
import os
|
||||
@ -81,25 +82,13 @@ class Nvcm():
|
||||
self.flasher.gpio_put(self.pins['ss'], cs)
|
||||
self.flasher.gpio_put(self.pins['crst'], reset)
|
||||
|
||||
def sendhex(self, s: str) -> int:
|
||||
if self.debug and not s == "0500": # suppress status_wait
|
||||
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: str) -> bytes:
|
||||
def sendhex_cs(self, s: str, toggle_cs: bool = True) -> 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 = self.flasher.spi_bitbang(x)
|
||||
b = self.flasher.spi_bitbang(x, toggle_cs)
|
||||
|
||||
if self.debug and not s == "0500":
|
||||
print("RX", b.hex())
|
||||
@ -108,15 +97,10 @@ class Nvcm():
|
||||
def delay(self, count: int) -> None:
|
||||
# run the clock with no CS asserted
|
||||
# dev.exchange(b'\x00', duplex=True, readlen=count)
|
||||
self.sendhex('00' * count)
|
||||
self.sendhex_cs('00' * count, False)
|
||||
|
||||
def tck(self, count: int) -> None:
|
||||
self.delay(count >> 3)
|
||||
self.delay(count >> 3)
|
||||
self.delay(count >> 3)
|
||||
self.delay(count >> 3)
|
||||
self.delay(count >> 3)
|
||||
self.delay(count >> 3)
|
||||
self.delay((count >> 3) * 2)
|
||||
|
||||
def init(self) -> None:
|
||||
if self.debug:
|
||||
@ -161,39 +145,33 @@ class Nvcm():
|
||||
# SMCInstruction[1] = 0x70807E99557E;
|
||||
self.command("7eaa997e010e")
|
||||
|
||||
def read(
|
||||
def read_bytes(
|
||||
self,
|
||||
address: int,
|
||||
length: int = 8,
|
||||
cmd: int = 0x03) -> int:
|
||||
"""Returns a big integer"""
|
||||
# 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)
|
||||
|
||||
cmd: int = 0x03) -> bytes:
|
||||
"""Returns a byte array of the contents"""
|
||||
msg = ''
|
||||
msg += ("%02x%06x" % (cmd, address))
|
||||
msg += ("00" * 9) # dummy bytes
|
||||
msg += ("00" * length) # read
|
||||
ret = self.sendhex_cs(msg)
|
||||
|
||||
return ret[4 + 9:]
|
||||
|
||||
def read(
|
||||
self,
|
||||
address: int,
|
||||
length: int = 8,
|
||||
cmd: int = 0x03) -> int:
|
||||
"""Returns a big integer"""
|
||||
|
||||
val = self.read_bytes(address, length, cmd)
|
||||
x = 0
|
||||
for i in range(0, length):
|
||||
x = x << 8 | ret[i + 4 + 9]
|
||||
x = x << 8 | val[i]
|
||||
return x
|
||||
|
||||
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: int, data: str, cmd: int = 0x02) -> None:
|
||||
self.sendhex_cs("%02x%06x" % (cmd, address) + data)
|
||||
|
||||
@ -314,6 +292,7 @@ class Nvcm():
|
||||
|
||||
i = 0
|
||||
for row in rows:
|
||||
# print('data for row:',i, row)
|
||||
if i % 1024 == 0:
|
||||
print("%6d / %6d bytes" % (i, len(rows) * 8))
|
||||
i += 8
|
||||
@ -378,6 +357,7 @@ class Nvcm():
|
||||
self.write_trim_pages("0015f2f1c4000000")
|
||||
|
||||
def info(self) -> None:
|
||||
""" Print the contents of the configuration registers """
|
||||
self.select_sig()
|
||||
sig1 = self.read(0x000000, 8)
|
||||
|
||||
@ -388,7 +368,7 @@ class Nvcm():
|
||||
self.select_nvcm()
|
||||
trim = self.read_trim()
|
||||
|
||||
self.select_nvcm()
|
||||
# self.select_nvcm()
|
||||
|
||||
self.select_trim()
|
||||
trim0 = self.read(0x000020, 8)
|
||||
@ -421,17 +401,41 @@ class Nvcm():
|
||||
print("Trim 2: %016x" % (trim2))
|
||||
print("Trim 3: %016x" % (trim3))
|
||||
|
||||
def read_file(self, filename: str) -> None:
|
||||
def read_nvcm(self, length: int) -> bytes:
|
||||
""" Read out the contents of the NVCM fuses
|
||||
|
||||
Keyword arguments:
|
||||
length: Length of data to read
|
||||
"""
|
||||
|
||||
self.select_nvcm()
|
||||
|
||||
total_fuse = 104090
|
||||
contents = bytearray()
|
||||
|
||||
contents = b''
|
||||
|
||||
for offset in range(0, total_fuse, 8):
|
||||
for offset in range(0, length, 8):
|
||||
if offset % 1024 == 0:
|
||||
print("%6d / %6d bytes" % (offset, total_fuse))
|
||||
contents += self.read_bytes(offset, 8)
|
||||
print("%6d / %6d bytes" % (offset, length))
|
||||
|
||||
nvcm_addr = int(offset / 328) * 4096 + (offset % 328)
|
||||
contents += self.read_bytes(nvcm_addr, 8)
|
||||
self.tck(8)
|
||||
|
||||
return bytes(contents)
|
||||
|
||||
def read_file(self, filename: str, length: int) -> None:
|
||||
""" Read the contents of the NVCM to a file
|
||||
|
||||
Keyword arguments:
|
||||
filename -- File to write to, or '-' to write to stdout
|
||||
"""
|
||||
|
||||
contents = bytearray()
|
||||
|
||||
# prepend a header to the file, to identify it as an FPGA
|
||||
# bitstream
|
||||
contents += bytes([0xff, 0x00, 0x00, 0xff])
|
||||
|
||||
contents += self.read_nvcm(length)
|
||||
|
||||
if filename == '-':
|
||||
with os.fdopen(sys.stdout.fileno(),
|
||||
@ -444,45 +448,27 @@ class Nvcm():
|
||||
f.write(contents)
|
||||
f.flush()
|
||||
|
||||
def verify(self, filename: str) -> None:
|
||||
""" Verify that the contents of the NVCM match a file
|
||||
|
||||
#
|
||||
# bistream to NVCM command conversion is based on majbthrd's work in
|
||||
# https://github.com/YosysHQ/icestorm/pull/272
|
||||
#
|
||||
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'):
|
||||
break
|
||||
Keyword arguments:
|
||||
filename -- File to compare
|
||||
"""
|
||||
with open(filename, "rb") as f:
|
||||
compare = f.read()
|
||||
|
||||
if origin == len(bitstream):
|
||||
raise Exception("Preamble not found")
|
||||
assert (len(compare) > 0)
|
||||
|
||||
print("Found preamable at %08x" % (origin), file=sys.stderr)
|
||||
contents = bytearray()
|
||||
contents += bytes([0xff, 0x00, 0x00, 0xff])
|
||||
contents += self.read_nvcm(len(compare))
|
||||
|
||||
# there might be stuff in the header with vendor tools,
|
||||
# but not usually in icepack produced output, so ignore it for now
|
||||
# We might have read more than needed because of read
|
||||
# boundaries
|
||||
if len(contents) > len(compare):
|
||||
contents = contents[:len(compare)]
|
||||
|
||||
# todo: what is the correct size?
|
||||
|
||||
rows = []
|
||||
|
||||
for pos in range(origin, len(bitstream), 8):
|
||||
row = bitstream[pos:pos + 8]
|
||||
|
||||
# pad out to 8-bytes
|
||||
row += b'\0' * (8 - len(row))
|
||||
|
||||
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())]
|
||||
|
||||
return rows
|
||||
assert (compare == contents)
|
||||
|
||||
|
||||
def sleep_flash(pins: dict) -> None:
|
||||
@ -587,6 +573,12 @@ if __name__ == "__main__":
|
||||
default=None,
|
||||
help='Read contents of NVCM')
|
||||
|
||||
parser.add_argument('--verify',
|
||||
dest='verify_file',
|
||||
type=str,
|
||||
default=None,
|
||||
help='Verify the contents of NVCM')
|
||||
|
||||
parser.add_argument(
|
||||
'--write',
|
||||
dest='write_file',
|
||||
@ -649,7 +641,7 @@ if __name__ == "__main__":
|
||||
with open(args.write_file, "rb") as f:
|
||||
bitstream = f.read()
|
||||
print("read %d bytes" % (len(bitstream)))
|
||||
cmds = bitstream2nvcm(bitstream)
|
||||
cmds = icebin2nvcm(bitstream)
|
||||
if not cmds:
|
||||
exit(1)
|
||||
|
||||
@ -666,7 +658,11 @@ if __name__ == "__main__":
|
||||
|
||||
if args.read_file:
|
||||
# read back after writing to the NVCM
|
||||
nvcm.read_file(args.read_file)
|
||||
nvcm.read_file(args.read_file, 104090)
|
||||
|
||||
if args.verify_file:
|
||||
# read back after writing to the NVCM
|
||||
nvcm.verify(args.verify_file)
|
||||
|
||||
if args.set_secure:
|
||||
nvcm.trim_secure()
|
||||
|
BIN
hw/production_test/nvcm_test/application_fpga.bin
Normal file
BIN
hw/production_test/nvcm_test/application_fpga.bin
Normal file
Binary file not shown.
10973
hw/production_test/nvcm_test/application_fpga.nvcm
Normal file
10973
hw/production_test/nvcm_test/application_fpga.nvcm
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user