2023-03-02 17:43:26 -05:00
|
|
|
#!/usr/bin/env python
|
2023-03-06 06:41:21 -05:00
|
|
|
""" bistream to NVCM command conversion is based on majbthrd's work
|
|
|
|
in https://github.com/YosysHQ/icestorm/pull/272
|
|
|
|
"""
|
2023-03-02 17:43:26 -05:00
|
|
|
|
|
|
|
|
2023-03-06 06:41:21 -05:00
|
|
|
def icebin2nvcm(bitstream: bytes) -> list[str]:
|
|
|
|
"""Convert an ice40 bitstream into an NVCM program
|
2023-03-02 17:43:26 -05:00
|
|
|
|
2023-03-06 06:41:21 -05:00
|
|
|
The NVCM format is a set of commands that are run against the
|
|
|
|
NVCM state machine, which instruct the state machine to write
|
|
|
|
the bitstream into the NVCM. It's somewhat convoluted!
|
|
|
|
|
|
|
|
Keyword arguments:
|
|
|
|
bitstream -- Bitstream to convert into NVCM format
|
|
|
|
"""
|
2023-03-02 17:43:26 -05:00
|
|
|
|
|
|
|
# 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):
|
2023-03-06 06:41:21 -05:00
|
|
|
raise ValueError('Preamble not found')
|
2023-03-02 17:43:26 -05:00
|
|
|
|
2023-03-06 06:41:21 -05:00
|
|
|
print(f'Found preamable at {origin:08x}')
|
2023-03-02 17:43:26 -05:00
|
|
|
|
|
|
|
# there might be stuff in the header with vendor tools,
|
|
|
|
# but not usually in icepack produced output, so ignore it for now
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2023-03-06 06:41:21 -05:00
|
|
|
row_str = f'02{nvcm_addr:06x}{row.hex()}'
|
2023-03-02 17:43:26 -05:00
|
|
|
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:
|
2023-03-06 06:41:21 -05:00
|
|
|
data = f_in.read()
|
2023-03-02 17:43:26 -05:00
|
|
|
|
2023-03-06 06:41:21 -05:00
|
|
|
cmds = icebin2nvcm(data)
|
2023-03-02 17:43:26 -05:00
|
|
|
|
2023-03-06 06:41:21 -05:00
|
|
|
with open(args.outfile, 'w', encoding='utf-8') as f_out:
|
2023-03-02 17:43:26 -05:00
|
|
|
for cmd in cmds:
|
|
|
|
f_out.write(cmd)
|
|
|
|
f_out.write('\n')
|