forked from jrrk2/prjxray
-
Notifications
You must be signed in to change notification settings - Fork 2
/
fasm2frames.py
executable file
·316 lines (246 loc) · 9.44 KB
/
fasm2frames.py
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017-2020 The Project X-Ray Authors.
#
# Use of this source code is governed by a ISC-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/ISC
#
# SPDX-License-Identifier: ISC
from __future__ import print_function
import fasm
import argparse
import json
import os
import os.path
import csv
from collections import defaultdict
from prjxray import fasm_assembler, util
from prjxray.db import Database
from prjxray.roi import Roi
from prjxray.util import OpenSafeFile
import sys
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
class FASMSyntaxError(SyntaxError):
pass
def dump_frames_verbose(frames):
print()
print("Frames: %d" % len(frames))
for addr in sorted(frames.keys()):
words = frames[addr]
print(
'0x%08X ' % addr + ', '.join(['0x%08X' % w for w in words]) +
'...')
def dump_frames_sparse(frames):
print()
print("Frames: %d" % len(frames))
for addr in sorted(frames.keys()):
words = frames[addr]
# Skip frames without filled words
for w in words:
if w:
break
else:
continue
print('Frame @ 0x%08X' % addr)
for i, w in enumerate(words):
if w:
print(' % 3d: 0x%08X' % (i, w))
def dump_frm(f, frames):
'''Write a .frm file given a list of frames, each containing a list of 101 32 bit words'''
for addr in sorted(frames.keys()):
words = frames[addr]
f.write(
'0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n')
def find_pudc_b(db):
""" Find PUDC_B pin func in grid, and return the tile and site prefix.
The PUDC_B pin is a special 7-series pin that controls unused pin pullup.
If the PUDC_B is unused, it is configured as an input with a PULLUP.
"""
grid = db.grid()
pudc_b_tile_site = None
for tile in grid.tiles():
gridinfo = grid.gridinfo_at_tilename(tile)
for site, pin_function in gridinfo.pin_functions.items():
if 'PUDC_B' in pin_function:
assert pudc_b_tile_site == None, (
pudc_b_tile_site, (tile, site))
iob_y = int(site[-1]) % 2
pudc_b_tile_site = (tile, 'IOB_Y{}'.format(iob_y))
return pudc_b_tile_site
def get_iob_sites(db, tile_name):
"""
Yields prjxray site names for given IOB tile name
"""
grid = db.grid()
gridinfo = grid.gridinfo_at_tilename(tile_name)
for site in gridinfo.sites:
site_y = int(site[-1]) % 2
yield "IOB_Y{}".format(site_y)
def run(
db_root,
part,
filename_in,
f_out,
sparse=False,
roi=None,
debug=False,
emit_pudc_b_pullup=False):
db = Database(db_root, part)
assembler = fasm_assembler.FasmAssembler(db)
set_features = set()
def feature_callback(feature):
set_features.add(feature)
assembler.set_feature_callback(feature_callback)
# Build mapping of tile to IO bank
tile_to_bank = {}
bank_to_tile = defaultdict(lambda: set())
if part is not None:
with OpenSafeFile(os.path.join(db_root, part, "package_pins.csv"), "r") as fp:
reader = csv.DictReader(fp)
package_pins = [l for l in reader]
with OpenSafeFile(os.path.join(db_root, part, "part.json"), "r") as fp:
part_data = json.load(fp)
for bank, loc in part_data["iobanks"].items():
tile = "HCLK_IOI3_" + loc
bank_to_tile[bank].add(tile)
tile_to_bank[tile] = bank
for pin in package_pins:
bank_to_tile[pin["bank"]].add(pin["tile"])
tile_to_bank[pin["tile"]] = pin["bank"]
if emit_pudc_b_pullup:
pudc_b_in_use = False
pudc_b_tile_site = find_pudc_b(db)
def check_for_pudc_b(set_feature):
feature_callback(set_feature)
parts = set_feature.feature.split('.')
if parts[0] == pudc_b_tile_site[0] and parts[
1] == pudc_b_tile_site[1]:
nonlocal pudc_b_in_use
pudc_b_in_use = True
if pudc_b_tile_site is not None:
assembler.set_feature_callback(check_for_pudc_b)
extra_features = []
if roi:
with OpenSafeFile(roi) as f:
roi_j = json.load(f)
x1 = roi_j['info']['GRID_X_MIN']
x2 = roi_j['info']['GRID_X_MAX']
y1 = roi_j['info']['GRID_Y_MIN']
y2 = roi_j['info']['GRID_Y_MAX']
assembler.mark_roi_frames(Roi(db=db, x1=x1, x2=x2, y1=y1, y2=y2))
if 'required_features' in roi_j:
extra_features = list(
fasm.parse_fasm_string('\n'.join(roi_j['required_features'])))
# Get required extra features for the part
required_features = db.get_required_fasm_features(part)
extra_features += list(
fasm.parse_fasm_string('\n'.join(required_features)))
assembler.parse_fasm_filename(filename_in, extra_features=extra_features)
if emit_pudc_b_pullup and not pudc_b_in_use and pudc_b_tile_site is not None:
# Enable IN-only and PULLUP on PUDC_B IOB.
#
# TODO: The following FASM string only works on Artix 50T and Zynq 10
# fabrics. It is known to be wrong for the K70T fabric, but it is
# unclear how to know which IOSTANDARD to use.
missing_features = []
for line in fasm.parse_fasm_string("""
{tile}.{site}.LVCMOS12_LVCMOS15_LVCMOS18_LVCMOS25_LVCMOS33_LVTTL_SSTL135_SSTL15.IN_ONLY
{tile}.{site}.LVCMOS25_LVCMOS33_LVTTL.IN
{tile}.{site}.PULLTYPE.PULLUP
""".format(
tile=pudc_b_tile_site[0],
site=pudc_b_tile_site[1],
)):
assembler.add_fasm_line(line, missing_features)
if missing_features:
raise fasm_assembler.FasmLookupError('\n'.join(missing_features))
if part is not None:
# Make a set of all used IOB tiles and sites. Look for the "STEPDOWN"
# feature. If one is set for an IOB then set it for all other IOBs of
# the same bank.
stepdown_tags = defaultdict(lambda: set())
stepdown_banks = set()
used_iob_sites = set()
for set_feature in set_features:
if set_feature.value == 0:
continue
feature = set_feature.feature
parts = feature.split(".")
if len(parts) >= 3:
tile, site, tag = feature.split(".", maxsplit=2)
if "IOB33" in tile:
used_iob_sites.add((
tile,
site,
))
# Store STEPDOWN related tags.
if "STEPDOWN" in tag:
bank = tile_to_bank[tile]
stepdown_banks.add(bank)
stepdown_tags[bank].add(tag)
# Set the feature for unused IOBs, loop over all banks which were
# observed to have the STEPDOWN feature set.
missing_features = []
for bank in stepdown_banks:
for tile in bank_to_tile[bank]:
# This is an IOB33 tile. Set the STEPDOWN feature in it but
# only if it is unused.
if "IOB33" in tile:
for site in get_iob_sites(db, tile):
if (tile, site) in used_iob_sites:
continue
for tag in stepdown_tags[bank]:
feature = "{}.{}.{}".format(tile, site, tag)
for line in fasm.parse_fasm_string(feature):
assembler.add_fasm_line(line, missing_features)
# This is a HCLK_IOI3 tile, set the stepdown feature for it
# too.
if "HCLK_IOI3" in tile:
feature = "{}.STEPDOWN".format(tile)
for line in fasm.parse_fasm_string(feature):
assembler.add_fasm_line(line, missing_features)
if missing_features:
raise fasm_assembler.FasmLookupError('\n'.join(missing_features))
frames = assembler.get_frames(sparse=sparse)
if debug:
dump_frames_sparse(frames)
dump_frm(f_out, frames)
def main():
parser = argparse.ArgumentParser(
description=
'Convert FPGA configuration description ("FPGA assembly") into binary frame equivalent'
)
util.db_root_arg(parser)
util.part_arg(parser)
parser.add_argument(
'--sparse', action='store_true', help="Don't zero fill all frames")
parser.add_argument(
'--roi',
help="ROI design.json file defining which tiles are within the ROI.")
parser.add_argument(
'--emit_pudc_b_pullup',
help="Emit an IBUF and PULLUP on the PUDC_B pin if unused",
action='store_true')
parser.add_argument(
'--debug', action='store_true', help="Print debug dump")
parser.add_argument('fn_in', help='Input FPGA assembly (.fasm) file')
parser.add_argument(
'fn_out',
default='/dev/stdout',
nargs='?',
help='Output FPGA frame (.frm) file')
args = parser.parse_args()
run(
db_root=args.db_root,
part=args.part,
filename_in=args.fn_in,
f_out=open(args.fn_out, 'w'),
sparse=args.sparse,
roi=args.roi,
debug=args.debug,
emit_pudc_b_pullup=args.emit_pudc_b_pullup)
if __name__ == '__main__':
main()