mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2024-12-20 13:24:24 -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:
|
lint:
|
||||||
autopep8 --in-place --max-line-length 70 --aggressive --aggressive usb_test.py icenvcm.py
|
autopep8 --in-place --max-line-length 70 --aggressive --aggressive ${PYTHON_FILES}
|
||||||
mypy --disallow-untyped-defs usb_test.py icenvcm.py
|
mypy --disallow-untyped-defs ${PYTHON_FILES}
|
||||||
pycodestyle --max-line-length 70 usb_test.py icenvcm.py
|
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
|
# Copyright (C) 2021
|
||||||
#
|
#
|
||||||
@ -22,6 +22,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import usb_test
|
import usb_test
|
||||||
|
from icebin2nvcm import icebin2nvcm
|
||||||
import sys
|
import sys
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import os
|
import os
|
||||||
@ -81,25 +82,13 @@ class Nvcm():
|
|||||||
self.flasher.gpio_put(self.pins['ss'], cs)
|
self.flasher.gpio_put(self.pins['ss'], cs)
|
||||||
self.flasher.gpio_put(self.pins['crst'], reset)
|
self.flasher.gpio_put(self.pins['crst'], reset)
|
||||||
|
|
||||||
def sendhex(self, s: str) -> int:
|
def sendhex_cs(self, s: str, toggle_cs: bool = True) -> bytes:
|
||||||
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:
|
|
||||||
if self.debug and not s == "0500":
|
if self.debug and not s == "0500":
|
||||||
print("TX", s)
|
print("TX", s)
|
||||||
x = bytes.fromhex(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)
|
b = self.flasher.spi_bitbang(x, toggle_cs)
|
||||||
|
|
||||||
if self.debug and not s == "0500":
|
if self.debug and not s == "0500":
|
||||||
print("RX", b.hex())
|
print("RX", b.hex())
|
||||||
@ -108,15 +97,10 @@ class Nvcm():
|
|||||||
def delay(self, count: int) -> None:
|
def delay(self, count: int) -> None:
|
||||||
# run the clock with no CS asserted
|
# 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)
|
self.sendhex_cs('00' * count, False)
|
||||||
|
|
||||||
def tck(self, count: int) -> None:
|
def tck(self, count: int) -> None:
|
||||||
self.delay(count >> 3)
|
self.delay((count >> 3) * 2)
|
||||||
self.delay(count >> 3)
|
|
||||||
self.delay(count >> 3)
|
|
||||||
self.delay(count >> 3)
|
|
||||||
self.delay(count >> 3)
|
|
||||||
self.delay(count >> 3)
|
|
||||||
|
|
||||||
def init(self) -> None:
|
def init(self) -> None:
|
||||||
if self.debug:
|
if self.debug:
|
||||||
@ -161,39 +145,33 @@ class Nvcm():
|
|||||||
# SMCInstruction[1] = 0x70807E99557E;
|
# SMCInstruction[1] = 0x70807E99557E;
|
||||||
self.command("7eaa997e010e")
|
self.command("7eaa997e010e")
|
||||||
|
|
||||||
def read(
|
def read_bytes(
|
||||||
self,
|
self,
|
||||||
address: int,
|
address: int,
|
||||||
length: int = 8,
|
length: int = 8,
|
||||||
cmd: int = 0x03) -> int:
|
cmd: int = 0x03) -> bytes:
|
||||||
"""Returns a big integer"""
|
"""Returns a byte array of the contents"""
|
||||||
# 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)
|
|
||||||
|
|
||||||
msg = ''
|
msg = ''
|
||||||
msg += ("%02x%06x" % (cmd, address))
|
msg += ("%02x%06x" % (cmd, address))
|
||||||
msg += ("00" * 9) # dummy bytes
|
msg += ("00" * 9) # dummy bytes
|
||||||
msg += ("00" * length) # read
|
msg += ("00" * length) # read
|
||||||
ret = self.sendhex_cs(msg)
|
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
|
x = 0
|
||||||
for i in range(0, length):
|
for i in range(0, length):
|
||||||
x = x << 8 | ret[i + 4 + 9]
|
x = x << 8 | val[i]
|
||||||
return x
|
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:
|
def write(self, address: int, data: str, cmd: int = 0x02) -> None:
|
||||||
self.sendhex_cs("%02x%06x" % (cmd, address) + data)
|
self.sendhex_cs("%02x%06x" % (cmd, address) + data)
|
||||||
|
|
||||||
@ -314,6 +292,7 @@ class Nvcm():
|
|||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
# print('data for row:',i, row)
|
||||||
if i % 1024 == 0:
|
if i % 1024 == 0:
|
||||||
print("%6d / %6d bytes" % (i, len(rows) * 8))
|
print("%6d / %6d bytes" % (i, len(rows) * 8))
|
||||||
i += 8
|
i += 8
|
||||||
@ -378,6 +357,7 @@ class Nvcm():
|
|||||||
self.write_trim_pages("0015f2f1c4000000")
|
self.write_trim_pages("0015f2f1c4000000")
|
||||||
|
|
||||||
def info(self) -> None:
|
def info(self) -> None:
|
||||||
|
""" Print the contents of the configuration registers """
|
||||||
self.select_sig()
|
self.select_sig()
|
||||||
sig1 = self.read(0x000000, 8)
|
sig1 = self.read(0x000000, 8)
|
||||||
|
|
||||||
@ -388,7 +368,7 @@ class Nvcm():
|
|||||||
self.select_nvcm()
|
self.select_nvcm()
|
||||||
trim = self.read_trim()
|
trim = self.read_trim()
|
||||||
|
|
||||||
self.select_nvcm()
|
# self.select_nvcm()
|
||||||
|
|
||||||
self.select_trim()
|
self.select_trim()
|
||||||
trim0 = self.read(0x000020, 8)
|
trim0 = self.read(0x000020, 8)
|
||||||
@ -421,17 +401,41 @@ class Nvcm():
|
|||||||
print("Trim 2: %016x" % (trim2))
|
print("Trim 2: %016x" % (trim2))
|
||||||
print("Trim 3: %016x" % (trim3))
|
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()
|
self.select_nvcm()
|
||||||
|
|
||||||
total_fuse = 104090
|
contents = bytearray()
|
||||||
|
|
||||||
contents = b''
|
for offset in range(0, length, 8):
|
||||||
|
|
||||||
for offset in range(0, total_fuse, 8):
|
|
||||||
if offset % 1024 == 0:
|
if offset % 1024 == 0:
|
||||||
print("%6d / %6d bytes" % (offset, total_fuse))
|
print("%6d / %6d bytes" % (offset, length))
|
||||||
contents += self.read_bytes(offset, 8)
|
|
||||||
|
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 == '-':
|
if filename == '-':
|
||||||
with os.fdopen(sys.stdout.fileno(),
|
with os.fdopen(sys.stdout.fileno(),
|
||||||
@ -444,45 +448,27 @@ class Nvcm():
|
|||||||
f.write(contents)
|
f.write(contents)
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
|
def verify(self, filename: str) -> None:
|
||||||
|
""" Verify that the contents of the NVCM match a file
|
||||||
|
|
||||||
#
|
Keyword arguments:
|
||||||
# bistream to NVCM command conversion is based on majbthrd's work in
|
filename -- File to compare
|
||||||
# https://github.com/YosysHQ/icestorm/pull/272
|
"""
|
||||||
#
|
with open(filename, "rb") as f:
|
||||||
def bitstream2nvcm(bitstream: bytes) -> list[str]:
|
compare = f.read()
|
||||||
# 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):
|
assert (len(compare) > 0)
|
||||||
raise Exception("Preamble not found")
|
|
||||||
|
|
||||||
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,
|
# We might have read more than needed because of read
|
||||||
# but not usually in icepack produced output, so ignore it for now
|
# boundaries
|
||||||
|
if len(contents) > len(compare):
|
||||||
|
contents = contents[:len(compare)]
|
||||||
|
|
||||||
# todo: what is the correct size?
|
assert (compare == contents)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def sleep_flash(pins: dict) -> None:
|
def sleep_flash(pins: dict) -> None:
|
||||||
@ -587,6 +573,12 @@ if __name__ == "__main__":
|
|||||||
default=None,
|
default=None,
|
||||||
help='Read contents of NVCM')
|
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(
|
parser.add_argument(
|
||||||
'--write',
|
'--write',
|
||||||
dest='write_file',
|
dest='write_file',
|
||||||
@ -649,7 +641,7 @@ if __name__ == "__main__":
|
|||||||
with open(args.write_file, "rb") as f:
|
with open(args.write_file, "rb") as f:
|
||||||
bitstream = f.read()
|
bitstream = f.read()
|
||||||
print("read %d bytes" % (len(bitstream)))
|
print("read %d bytes" % (len(bitstream)))
|
||||||
cmds = bitstream2nvcm(bitstream)
|
cmds = icebin2nvcm(bitstream)
|
||||||
if not cmds:
|
if not cmds:
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
@ -666,7 +658,11 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
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)
|
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:
|
if args.set_secure:
|
||||||
nvcm.trim_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