fpga: Fix Xilinx bitfile parser for Python 3

The script was previously only Python 2 compatible.

Python 2 support is now removed, so we don't maintain backwards
compatibility with Python 2.

This also fixes all linter warnings.


Original-commit: 6046e1a812ba2065d6ae13b4c4afdce8abc99d4f
This commit is contained in:
Martin Braun
2021-08-24 09:45:36 -05:00
committed by Wade Fife
parent 5983927439
commit 13633dd543
+54 -31
View File
@@ -1,4 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
Parser for Xilinx bitfiles.
Reads metadata from Xilinx bitfile headers.
"""
import argparse import argparse
import os import os
@@ -7,7 +12,8 @@ import struct
import re import re
# Parse command line options # Parse command line options
def get_options(): def get_args():
"""Run argparser"""
parser = argparse.ArgumentParser(description='Parser for the Xilinx FPGA Bitfile') parser = argparse.ArgumentParser(description='Parser for the Xilinx FPGA Bitfile')
parser.add_argument("bitfile", help="Input bitfile path") parser.add_argument("bitfile", help="Input bitfile path")
parser.add_argument("--bin_out", help="Output bin file path") parser.add_argument("--bin_out", help="Output bin file path")
@@ -20,60 +26,77 @@ def get_options():
sys.exit(1) sys.exit(1)
return args return args
short = struct.Struct('>H')
ulong = struct.Struct('>I')
KEYNAMES = {'a':'design_name', 'b':'part_name', 'c':'date', 'd':'time'}
# Parse bitfile
def parse_bitfile(bitfile_bytes): def parse_bitfile(bitfile_bytes):
"""
Parse bitfile
"""
short = struct.Struct('>H')
ulong = struct.Struct('>I')
keynames = {'a': 'design_name', 'b': 'part_name', 'c': 'date', 'd': 'time'}
header = dict() header = dict()
ptr = 0 ptr = 0
#Field 1 #Field 1
if short.unpack(bitfile_bytes[ptr:ptr+2])[0] == 9 and ulong.unpack(bitfile_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0: if short.unpack(bitfile_bytes[ptr:ptr+2])[0] == 9 and \
ulong.unpack(bitfile_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0:
#Headers #Headers
ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 2 ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 2
ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 1 ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 1
#Fields a-d #Fields a-d
for keynum in range(0, 4): for _ in range(0, 4):
key = bitfile_bytes[ptr]; ptr += 1 key = chr(bitfile_bytes[ptr])
val_len = short.unpack(bitfile_bytes[ptr:ptr+2])[0]; ptr += 2 ptr += 1
val = bitfile_bytes[ptr:ptr+val_len]; ptr += val_len val_len = short.unpack(bitfile_bytes[ptr:ptr+2])[0]
header[KEYNAMES[key]] = str(val).rstrip('\0') ptr += 2
val = bitfile_bytes[ptr:ptr+val_len]
ptr += val_len
header[keynames[key]] = val.decode('ascii').rstrip('\0')
#Field e #Field e
ptr += 1 ptr += 1
length = ulong.unpack(bitfile_bytes[ptr:ptr+4])[0]; ptr += 4 length = ulong.unpack(bitfile_bytes[ptr:ptr+4])[0]
ptr += 4
header['bitstream_len'] = length header['bitstream_len'] = length
header['header_len'] = ptr header['header_len'] = ptr
data = bitfile_bytes[ptr:ptr+length] data = bitfile_bytes[ptr:ptr+length]
return (header, data) return (header, data)
else: raise Exception('Bitfile header validation failed!')
raise Exception('Bitfile header validation failed!')
# Flip 32-bit endianess
def flip32(data): def flip32(data):
sl = struct.Struct('<I') """
sb = struct.Struct('>I') Flip 32-bit endianness
b = memoryview(data) """
d = bytearray(len(data)) le32_struct = struct.Struct('<I')
be32_struct = struct.Struct('>I')
data_flipped = bytearray(len(data))
for offset in range(0, len(data), 4): for offset in range(0, len(data), 4):
sb.pack_into(d, offset, sl.unpack_from(b, offset)[0]) be32_struct.pack_into(
return d data_flipped,
offset,
le32_struct.unpack_from(memoryview(data), offset)[0])
return data_flipped
def main(): def main():
args = get_options(); """GoGoGo"""
args = get_args()
with open(args.bitfile, 'rb') as bit_file: with open(args.bitfile, 'rb') as bit_file:
# Parse bytes into a header map and data buffer # Parse bytes into a header map and data buffer
(header, data) = parse_bitfile(bit_file.read()) (header, data) = parse_bitfile(bit_file.read())
# Print bitfile info # Print bitfile info
if args.info: if args.info:
m = re.search('(.+);UserID=(.+);COMPRESS=(.+);Version=(.+)', header['design_name']) keynames = {
if m: 'COMPRESS': 'Compression: ',
print('Design Name: ' + m.group(1)) 'UserID': 'User ID: ',
print('User ID: ' + m.group(2)) 'Version': 'Vivado Version: '}
print('Compression: ' + m.group(3)) name_fields = header['design_name'].split(';')
print('Vivado Version: ' + m.group(4)) for field in name_fields:
else: mobj = re.search('(.+)=(.+)', field)
print('Design Name: ' + header['design_name']) if mobj:
if mobj.group(1) in keynames:
print(keynames[mobj.group(1)] + mobj.group(2))
else:
print(mobj.group(1) + ': ' + mobj.group(2))
else:
print('Design Name: ' + field)
print('Part Name: ' + header['part_name']) print('Part Name: ' + header['part_name'])
print('Datestamp: ' + header['date'] + ' ' + header['time']) print('Datestamp: ' + header['date'] + ' ' + header['time'])
print('Bitstream Size: ' + str(header['bitstream_len'])) print('Bitstream Size: ' + str(header['bitstream_len']))