1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
#!/usr/bin/env python3
"""
Parser for Xilinx bitfiles.
Reads metadata from Xilinx bitfile headers.
"""
import argparse
import os
import sys
import struct
import re
# Parse command line options
def get_args():
"""Run argparser"""
parser = argparse.ArgumentParser(description='Parser for the Xilinx FPGA Bitfile')
parser.add_argument("bitfile", help="Input bitfile path")
parser.add_argument("--bin_out", help="Output bin file path")
parser.add_argument('--flip', action='store_true', default=False, help='Flip 32-bit endianess')
parser.add_argument('--info', action='store_true', default=False, help='Print bitfile info')
args = parser.parse_args()
if not os.path.isfile(args.bitfile):
print('ERROR: Bitfile ' + args.bitfile + ' could not be accessed or is not a file.\n')
parser.print_help()
sys.exit(1)
return args
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()
ptr = 0
#Field 1
if short.unpack(bitfile_bytes[ptr:ptr+2])[0] == 9 and \
ulong.unpack(bitfile_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0:
#Headers
ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 2
ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 1
#Fields a-d
for _ in range(0, 4):
key = chr(bitfile_bytes[ptr])
ptr += 1
val_len = short.unpack(bitfile_bytes[ptr:ptr+2])[0]
ptr += 2
val = bitfile_bytes[ptr:ptr+val_len]
ptr += val_len
header[keynames[key]] = val.decode('ascii').rstrip('\0')
#Field e
ptr += 1
length = ulong.unpack(bitfile_bytes[ptr:ptr+4])[0]
ptr += 4
header['bitstream_len'] = length
header['header_len'] = ptr
data = bitfile_bytes[ptr:ptr+length]
return (header, data)
raise Exception('Bitfile header validation failed!')
def flip32(data):
"""
Flip 32-bit endianness
"""
le32_struct = struct.Struct('<I')
be32_struct = struct.Struct('>I')
data_flipped = bytearray(len(data))
for offset in range(0, len(data), 4):
be32_struct.pack_into(
data_flipped,
offset,
le32_struct.unpack_from(memoryview(data), offset)[0])
return data_flipped
def main():
"""GoGoGo"""
args = get_args()
with open(args.bitfile, 'rb') as bit_file:
# Parse bytes into a header map and data buffer
(header, data) = parse_bitfile(bit_file.read())
# Print bitfile info
if args.info:
keynames = {
'COMPRESS': 'Compression: ',
'UserID': 'User ID: ',
'Version': 'Vivado Version: '}
name_fields = header['design_name'].split(';')
for field in name_fields:
mobj = re.search('(.+)=(.+)', field)
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('Datestamp: ' + header['date'] + ' ' + header['time'])
print('Bitstream Size: ' + str(header['bitstream_len']))
# Write a bin file
if args.bin_out:
open(args.bin_out, 'wb').write(flip32(data) if args.flip else data)
if __name__ == '__main__':
main()
|