mirror of https://github.com/openXC7/prjxray.git
Merge pull request #159 from mcmasterg/db_bits
tilegrid multiple address support, misc cleanup
This commit is contained in:
commit
38207ffb84
|
|
@ -1,248 +1,569 @@
|
|||
#!/usr/bin/env python3
|
||||
'''
|
||||
Historically we grouped data into "segments"
|
||||
These were a region of the bitstream that encoded one or more tiles
|
||||
However, this didn't scale with certain tiles like BRAM
|
||||
Some sites had multiple bitstream areas and also occupied multiple tiles
|
||||
|
||||
Decoding was then shifted to instead describe how each title is encoded
|
||||
A post processing step verifies that two tiles don't reference the same bitstream area
|
||||
'''
|
||||
|
||||
import os, sys, json, re
|
||||
|
||||
#######################################
|
||||
# Read
|
||||
# matches lib/include/prjxray/xilinx/xc7series/block_type.h
|
||||
block_type_i2s = {
|
||||
0: 'CLB_IO_CLK',
|
||||
1: 'BLOCK_RAM',
|
||||
2: 'CFG_CLB',
|
||||
# special...maybe should error until we know what it is?
|
||||
# 3: 'RESERVED',
|
||||
}
|
||||
|
||||
tiles = list()
|
||||
site_baseaddr = dict()
|
||||
tile_baseaddr = dict()
|
||||
|
||||
with open("tiles.txt") as f:
|
||||
for line in f:
|
||||
tiles.append(line.split())
|
||||
def addr2btype(base_addr):
|
||||
'''
|
||||
Convert integer address to block type
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
with open(arg) as f:
|
||||
line = f.read().strip()
|
||||
site = arg[7:-6]
|
||||
frame = int(line[5:5 + 8], 16)
|
||||
site_baseaddr[site] = "0x%08x" % (frame & ~0x7f)
|
||||
Table 5-24: Frame Address Register Description
|
||||
Bit Index: [25:23]
|
||||
https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
|
||||
"Valid block types are CLB, I/O, CLK ( 000 ), block RAM content ( 001 ), and CFG_CLB ( 010 ). A normal bitstream does not include type 011 ."
|
||||
'''
|
||||
block_type_i = (base_addr >> 23) & 0x7
|
||||
return block_type_i2s[block_type_i]
|
||||
|
||||
#######################################
|
||||
# Create initial database
|
||||
|
||||
database = dict()
|
||||
database["tiles"] = dict()
|
||||
database["segments"] = dict()
|
||||
tiles_by_grid = dict()
|
||||
def nolr(tile_type):
|
||||
'''
|
||||
Remove _L or _R suffix tile_type suffix, if present
|
||||
Ex: BRAM_INT_INTERFACE_L => BRAM_INT_INTERFACE
|
||||
Ex: VBRK => VBRK
|
||||
'''
|
||||
postfix = tile_type[-2:]
|
||||
if postfix in ('_L', '_R'):
|
||||
return tile_type[:-2]
|
||||
else:
|
||||
return tile_type
|
||||
|
||||
for record in tiles:
|
||||
tile_type, tile_name, grid_x, grid_y = record[0:4]
|
||||
grid_x, grid_y = int(grid_x), int(grid_y)
|
||||
tiles_by_grid[(grid_x, grid_y)] = tile_name
|
||||
framebaseaddr = None
|
||||
|
||||
database["tiles"][tile_name] = {
|
||||
"type": tile_type,
|
||||
"sites": dict(),
|
||||
"grid_x": grid_x,
|
||||
"grid_y": grid_y
|
||||
}
|
||||
def load_tiles(tiles_fn):
|
||||
'''
|
||||
"$type $tile $grid_x $grid_y $typed_sites"
|
||||
typed_sites: foreach t $site_types s $sites
|
||||
'''
|
||||
tiles = list()
|
||||
|
||||
if len(record) > 4:
|
||||
for i in range(4, len(record), 2):
|
||||
site_type, site_name = record[i:i + 2]
|
||||
if site_name in site_baseaddr:
|
||||
framebaseaddr = site_baseaddr[site_name]
|
||||
database["tiles"][tile_name]["sites"][site_name] = site_type
|
||||
with open(tiles_fn) as f:
|
||||
for line in f:
|
||||
# CLBLM_L CLBLM_L_X10Y98 30 106 SLICEL SLICE_X13Y98 SLICEM SLICE_X12Y98
|
||||
record = line.split()
|
||||
tile_type, tile_name, grid_x, grid_y = record[0:4]
|
||||
grid_x, grid_y = int(grid_x), int(grid_y)
|
||||
sites = {}
|
||||
for i in range(4, len(record), 2):
|
||||
site_type, site_name = record[i:i + 2]
|
||||
sites[site_name] = site_type
|
||||
tile = {
|
||||
'type': tile_type,
|
||||
'name': tile_name,
|
||||
'grid_x': grid_x,
|
||||
'grid_y': grid_y,
|
||||
'sites': sites,
|
||||
}
|
||||
tiles.append(tile)
|
||||
|
||||
if framebaseaddr is not None:
|
||||
tile_baseaddr[tile_name] = [framebaseaddr, 0]
|
||||
return tiles
|
||||
|
||||
#######################################
|
||||
# Add Segments
|
||||
|
||||
for tile_name, tile_data in database["tiles"].items():
|
||||
tile_type = tile_data["type"]
|
||||
grid_x = tile_data["grid_x"]
|
||||
grid_y = tile_data["grid_y"]
|
||||
def load_baseaddrs(deltas_fns):
|
||||
site_baseaddr = dict()
|
||||
for arg in deltas_fns:
|
||||
with open(arg) as f:
|
||||
line = f.read().strip()
|
||||
site = arg[7:-6]
|
||||
frame = int(line[5:5 + 8], 16)
|
||||
# was "0x%08x"
|
||||
site_baseaddr[site] = frame & ~0x7f
|
||||
|
||||
if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R"]:
|
||||
if tile_type in ["CLBLL_L", "CLBLM_L"]:
|
||||
int_tile_name = tiles_by_grid[(grid_x + 1, grid_y)]
|
||||
else:
|
||||
int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)]
|
||||
return site_baseaddr
|
||||
|
||||
segment_name = "SEG_" + tile_name
|
||||
segtype = tile_type.lower()
|
||||
|
||||
database["segments"][segment_name] = dict()
|
||||
database["segments"][segment_name]["tiles"] = [
|
||||
tile_name, int_tile_name
|
||||
]
|
||||
database["segments"][segment_name]["type"] = segtype
|
||||
database["segments"][segment_name]["frames"] = 36
|
||||
database["segments"][segment_name]["words"] = 2
|
||||
def make_database(tiles):
|
||||
# tile database with X, Y, and list of sites
|
||||
# tile name as keys
|
||||
database = dict()
|
||||
|
||||
if tile_name in tile_baseaddr:
|
||||
database["segments"][segment_name]["baseaddr"] = tile_baseaddr[
|
||||
tile_name]
|
||||
for tile in tiles:
|
||||
database[tile["name"]] = {
|
||||
"type": tile["type"],
|
||||
"sites": tile["sites"],
|
||||
"grid_x": tile["grid_x"],
|
||||
"grid_y": tile["grid_y"],
|
||||
"bits": {},
|
||||
}
|
||||
|
||||
database["tiles"][tile_name]["segment"] = segment_name
|
||||
database["tiles"][int_tile_name]["segment"] = segment_name
|
||||
return database
|
||||
|
||||
if tile_type in ["HCLK_L", "HCLK_R"]:
|
||||
segment_name = "SEG_" + tile_name
|
||||
segtype = tile_type.lower()
|
||||
|
||||
database["segments"][segment_name] = dict()
|
||||
database["segments"][segment_name]["tiles"] = [tile_name]
|
||||
database["segments"][segment_name]["type"] = segtype
|
||||
database["segments"][segment_name]["frames"] = 26
|
||||
database["segments"][segment_name]["words"] = 1
|
||||
database["tiles"][tile_name]["segment"] = segment_name
|
||||
def make_tile_baseaddrs(tiles, site_baseaddr, verbose=False):
|
||||
# Look up a base address by tile name
|
||||
tile_baseaddrs = dict()
|
||||
|
||||
if tile_type in ["BRAM_L", "DSP_L", "BRAM_R", "DSP_R"]:
|
||||
for k in range(5):
|
||||
if tile_type in ["BRAM_L", "DSP_L"]:
|
||||
interface_tile_name = tiles_by_grid[(grid_x + 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)]
|
||||
verbose and print('')
|
||||
verbose and print('%u tiles' % len(tiles))
|
||||
added = 0
|
||||
for tile in tiles:
|
||||
for site_name in tile["sites"].keys():
|
||||
if site_name not in site_baseaddr:
|
||||
continue
|
||||
framebaseaddr = site_baseaddr[site_name]
|
||||
bt = addr2btype(framebaseaddr)
|
||||
tile_baseaddr = tile_baseaddrs.setdefault(tile["name"], {})
|
||||
if bt in tile_baseaddr:
|
||||
# actually lets just fail these, better to remove at tcl level to speed up processing
|
||||
assert 0, 'duplicate base address'
|
||||
assert tile_baseaddr[bt] == [framebaseaddr, 0]
|
||||
else:
|
||||
interface_tile_name = tiles_by_grid[(grid_x - 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
|
||||
tile_baseaddr[bt] = [framebaseaddr, 0]
|
||||
verbose and print(
|
||||
"baseaddr: %s.%s @ %s.0x%08x" %
|
||||
(tile["name"], site_name, bt, framebaseaddr))
|
||||
added += 1
|
||||
|
||||
segment_name = "SEG_" + tile_name.replace("_", "%d_" % k, 1)
|
||||
segtype = tile_type.lower().replace("_", "%d_" % k, 1)
|
||||
assert added
|
||||
return tile_baseaddrs
|
||||
|
||||
database["segments"][segment_name] = dict()
|
||||
database["segments"][segment_name]["type"] = segtype
|
||||
database["segments"][segment_name]["frames"] = 28
|
||||
database["segments"][segment_name]["words"] = 2
|
||||
|
||||
if k == 0:
|
||||
database["segments"][segment_name]["tiles"] = [
|
||||
tile_name, interface_tile_name, int_tile_name
|
||||
]
|
||||
database["tiles"][tile_name]["segment"] = segment_name
|
||||
database["tiles"][interface_tile_name][
|
||||
"segment"] = segment_name
|
||||
database["tiles"][int_tile_name]["segment"] = segment_name
|
||||
def make_tiles_by_grid(tiles):
|
||||
# lookup tile names by (X, Y)
|
||||
tiles_by_grid = dict()
|
||||
|
||||
for tile in tiles:
|
||||
tiles_by_grid[(tile["grid_x"], tile["grid_y"])] = tile["name"]
|
||||
|
||||
return tiles_by_grid
|
||||
|
||||
|
||||
def make_segments(database, tiles_by_grid, tile_baseaddrs, verbose=False):
|
||||
'''
|
||||
Create segments data structure
|
||||
Indicates how tiles are related to bitstream locations
|
||||
Also modify database to annotate which segment the tiles belong to
|
||||
|
||||
segments key examples:
|
||||
SEG_CLBLM_R_X13Y72
|
||||
SEG_BRAM3_L_X6Y85
|
||||
'''
|
||||
segments = dict()
|
||||
|
||||
verbose and print('')
|
||||
for tile_name, tile_data in database.items():
|
||||
tile_type = tile_data["type"]
|
||||
grid_x = tile_data["grid_x"]
|
||||
grid_y = tile_data["grid_y"]
|
||||
|
||||
def add_segment(name, tiles, segtype, baseaddr=None):
|
||||
assert name not in segments
|
||||
segment = segments.setdefault(name, {})
|
||||
segment["tiles"] = tiles
|
||||
segment["type"] = segtype
|
||||
if baseaddr:
|
||||
verbose and print(
|
||||
'make_segment: %s baseaddr %s' % (
|
||||
name,
|
||||
baseaddr,
|
||||
))
|
||||
segment["baseaddr"] = baseaddr
|
||||
|
||||
for tile_name in tiles:
|
||||
database[tile_name]["segment"] = name
|
||||
|
||||
def process_clb():
|
||||
if tile_type in ["CLBLL_L", "CLBLM_L"]:
|
||||
int_tile_name = tiles_by_grid[(grid_x + 1, grid_y)]
|
||||
else:
|
||||
database["segments"][segment_name]["tiles"] = [
|
||||
interface_tile_name, int_tile_name
|
||||
]
|
||||
database["tiles"][interface_tile_name][
|
||||
"segment"] = segment_name
|
||||
database["tiles"][int_tile_name]["segment"] = segment_name
|
||||
int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)]
|
||||
|
||||
#######################################
|
||||
# Populate segment base addresses: L/R along INT column
|
||||
add_segment(
|
||||
name="SEG_" + tile_name,
|
||||
tiles=[tile_name, int_tile_name],
|
||||
segtype=tile_type.lower(),
|
||||
baseaddr=tile_baseaddrs.get(tile_name, None))
|
||||
|
||||
for segment_name in database["segments"].keys():
|
||||
if "baseaddr" in database["segments"][segment_name]:
|
||||
framebase, wordbase = database["segments"][segment_name]["baseaddr"]
|
||||
inttile = [
|
||||
tile for tile in database["segments"][segment_name]["tiles"]
|
||||
if database["tiles"][tile]["type"] in ["INT_L", "INT_R"]
|
||||
][0]
|
||||
grid_x = database["tiles"][inttile]["grid_x"]
|
||||
grid_y = database["tiles"][inttile]["grid_y"]
|
||||
def process_hclk():
|
||||
add_segment(
|
||||
name="SEG_" + tile_name,
|
||||
tiles=[tile_name],
|
||||
segtype=tile_type.lower())
|
||||
|
||||
if database["tiles"][inttile]["type"] == "INT_L":
|
||||
grid_x += 1
|
||||
framebase = "0x%08x" % (int(framebase, 16) + 0x80)
|
||||
else:
|
||||
grid_x -= 1
|
||||
framebase = "0x%08x" % (int(framebase, 16) - 0x80)
|
||||
def process_bram_dsp():
|
||||
for k in range(5):
|
||||
if tile_type in ["BRAM_L", "DSP_L"]:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x + 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)]
|
||||
elif tile_type in ["BRAM_R", "DSP_R"]:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x - 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
|
||||
else:
|
||||
assert 0
|
||||
'''
|
||||
BRAM/DSP itself is at the base y address
|
||||
There is one huge switchbox on the right for the 5 tiles
|
||||
These fan into 5 BRAM_INT_INTERFACE tiles each which feed into their own CENTER_INTER (just like a CLB has)
|
||||
'''
|
||||
if k == 0:
|
||||
tiles = [tile_name, interface_tile_name, int_tile_name]
|
||||
baseaddr = tile_baseaddrs.get(tile_name, None)
|
||||
else:
|
||||
tiles = [interface_tile_name, int_tile_name]
|
||||
baseaddr = None
|
||||
|
||||
if (grid_x, grid_y) not in tiles_by_grid:
|
||||
add_segment(
|
||||
# BRAM_L_X6Y70 => SEG_BRAM4_L_X6Y70
|
||||
name="SEG_" + tile_name.replace("_", "%d_" % k, 1),
|
||||
tiles=tiles,
|
||||
# BRAM_L => bram4_l
|
||||
segtype=tile_type.lower().replace("_", "%d_" % k, 1),
|
||||
baseaddr=baseaddr)
|
||||
|
||||
def process_default():
|
||||
#verbose and nolr(tile_type) not in ('VBRK', 'INT', 'NULL') and print('make_segment: drop %s' % (tile_type,))
|
||||
pass
|
||||
|
||||
{
|
||||
"CLBLL": process_clb,
|
||||
"CLBLM": process_clb,
|
||||
"HCLK": process_hclk,
|
||||
"BRAM": process_bram_dsp,
|
||||
"DSP": process_bram_dsp,
|
||||
}.get(nolr(tile_type), process_default)()
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
def get_inttile(database, segment):
|
||||
'''Return interconnect tile for given segment'''
|
||||
inttiles = [
|
||||
tile for tile in segment["tiles"]
|
||||
if database[tile]["type"] in ["INT_L", "INT_R"]
|
||||
]
|
||||
assert len(inttiles) == 1
|
||||
return inttiles[0]
|
||||
|
||||
|
||||
def get_bramtile(database, segment):
|
||||
inttiles = [
|
||||
tile for tile in segment["tiles"]
|
||||
if database[tile]["type"] in ["BRAM_L", "BRAM_R"]
|
||||
]
|
||||
assert len(inttiles) == 1
|
||||
return inttiles[0]
|
||||
|
||||
|
||||
def seg_base_addr_lr_INT(database, segments, tiles_by_grid, verbose=False):
|
||||
'''Populate segment base addresses: L/R along INT column'''
|
||||
'''
|
||||
Create BRAM base addresses based on nearby CLBs
|
||||
ie if we have a BRAM_L, compute as nearby CLB_R base address + offset
|
||||
'''
|
||||
|
||||
verbose and print('')
|
||||
for segment_name in sorted(segments.keys()):
|
||||
segment = segments[segment_name]
|
||||
baseaddrs = segment.get("baseaddr", None)
|
||||
if not baseaddrs:
|
||||
continue
|
||||
|
||||
tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
for block_type, (framebase, wordbase) in sorted(baseaddrs.items()):
|
||||
verbose and print(
|
||||
'lr_INT: %s: %s.0x%08X:%u' %
|
||||
(segment_name, block_type, framebase, wordbase))
|
||||
if block_type != 'CLB_IO_CLK':
|
||||
verbose and print(' Skip non CLB')
|
||||
continue
|
||||
|
||||
if database["tiles"][inttile]["type"] == "INT_L":
|
||||
assert database["tiles"][tile]["type"] == "INT_R"
|
||||
elif database["tiles"][inttile]["type"] == "INT_R":
|
||||
assert database["tiles"][tile]["type"] == "INT_L"
|
||||
else:
|
||||
assert 0
|
||||
inttile = get_inttile(database, segment)
|
||||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
assert "segment" in database["tiles"][tile]
|
||||
if database[inttile]["type"] == "INT_L":
|
||||
grid_x += 1
|
||||
framebase = framebase + 0x80
|
||||
elif database[inttile]["type"] == "INT_R":
|
||||
grid_x -= 1
|
||||
framebase = framebase - 0x80
|
||||
else:
|
||||
assert 0
|
||||
|
||||
seg = database["tiles"][tile]["segment"]
|
||||
# ROI at edge?
|
||||
if (grid_x, grid_y) not in tiles_by_grid:
|
||||
verbose and print(' Skip edge')
|
||||
continue
|
||||
|
||||
if "baseaddr" in database["segments"][seg]:
|
||||
assert database["segments"][seg]["baseaddr"] == [
|
||||
framebase, wordbase
|
||||
]
|
||||
else:
|
||||
database["segments"][seg]["baseaddr"] = [framebase, wordbase]
|
||||
tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
|
||||
#######################################
|
||||
# Populate segment base addresses: Up along INT/HCLK columns
|
||||
if database[inttile]["type"] == "INT_L":
|
||||
assert database[tile]["type"] == "INT_R"
|
||||
elif database[inttile]["type"] == "INT_R":
|
||||
assert database[tile]["type"] == "INT_L"
|
||||
else:
|
||||
assert 0
|
||||
|
||||
start_segments = list()
|
||||
assert "segment" in database[tile]
|
||||
|
||||
for segment_name in database["segments"].keys():
|
||||
if "baseaddr" in database["segments"][segment_name]:
|
||||
start_segments.append(segment_name)
|
||||
seg = database[tile]["segment"]
|
||||
|
||||
for segment_name in start_segments:
|
||||
framebase, wordbase = database["segments"][segment_name]["baseaddr"]
|
||||
inttile = [
|
||||
tile for tile in database["segments"][segment_name]["tiles"]
|
||||
if database["tiles"][tile]["type"] in ["INT_L", "INT_R"]
|
||||
][0]
|
||||
grid_x = database["tiles"][inttile]["grid_x"]
|
||||
grid_y = database["tiles"][inttile]["grid_y"]
|
||||
seg_baseaddrs = segments[seg].setdefault("baseaddr", {})
|
||||
# At least one duplicate when we re-compute the entry for the base address
|
||||
# should give the same address
|
||||
if block_type in seg_baseaddrs:
|
||||
assert seg_baseaddrs[block_type] == [
|
||||
framebase, wordbase
|
||||
], (seg_baseaddrs[block_type], [framebase, wordbase])
|
||||
verbose and print(' Existing OK')
|
||||
else:
|
||||
seg_baseaddrs[block_type] = [framebase, wordbase]
|
||||
verbose and print(' Add new')
|
||||
|
||||
for i in range(50):
|
||||
grid_y -= 1
|
||||
|
||||
if wordbase == 50:
|
||||
wordbase += 1
|
||||
else:
|
||||
wordbase += 2
|
||||
def seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=False):
|
||||
'''Populate segment base addresses: Up along INT/HCLK columns'''
|
||||
|
||||
segname = database["tiles"][tiles_by_grid[(grid_x, grid_y)]]["segment"]
|
||||
database["segments"][segname]["baseaddr"] = [framebase, wordbase]
|
||||
verbose and print('')
|
||||
# Copy the initial list containing only base addresses
|
||||
# and soon to have derived addresses
|
||||
src_segment_names = list()
|
||||
for segment_name in segments.keys():
|
||||
if "baseaddr" in segments[segment_name]:
|
||||
src_segment_names.append(segment_name)
|
||||
|
||||
#######################################
|
||||
# Transfer segment data into tiles
|
||||
verbose and print('up_INT: %u base addresses' % len(src_segment_names))
|
||||
|
||||
for segment_name in database["segments"].keys():
|
||||
try:
|
||||
baseaddr, offset = database["segments"][segment_name]["baseaddr"]
|
||||
except:
|
||||
print('Failed on segment name %s' % segment_name)
|
||||
raise
|
||||
for tile_name in database["segments"][segment_name]["tiles"]:
|
||||
tile_type = database["tiles"][tile_name]["type"]
|
||||
if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R", "INT_L",
|
||||
"INT_R"]:
|
||||
database["tiles"][tile_name]["baseaddr"] = baseaddr
|
||||
database["tiles"][tile_name]["offset"] = offset
|
||||
database["tiles"][tile_name]["height"] = 2
|
||||
elif tile_type in ["HCLK_L", "HCLK_R"]:
|
||||
database["tiles"][tile_name]["baseaddr"] = baseaddr
|
||||
database["tiles"][tile_name]["offset"] = offset
|
||||
database["tiles"][tile_name]["height"] = 1
|
||||
elif tile_type in ["BRAM_L", "BRAM_R", "DSP_L", "DSP_R"]:
|
||||
database["tiles"][tile_name]["baseaddr"] = baseaddr
|
||||
database["tiles"][tile_name]["offset"] = offset
|
||||
database["tiles"][tile_name]["height"] = 10
|
||||
elif tile_type in ["INT_INTERFACE_L", "INT_INTERFACE_R",
|
||||
"BRAM_INT_INTERFACE_L", "BRAM_INT_INTERFACE_R"]:
|
||||
continue
|
||||
else:
|
||||
# print(tile_type, offset)
|
||||
assert False
|
||||
for src_segment_name in sorted(src_segment_names):
|
||||
src_segment = segments[src_segment_name]
|
||||
|
||||
# TODO: Migrate to new tilegrid format via library. This data is added for
|
||||
# compability with unconverted tools. Update tools then remove this data from
|
||||
# tilegrid.json.
|
||||
for tiledata in database['tiles'].values():
|
||||
if "segment" in tiledata:
|
||||
segment = tiledata['segment']
|
||||
tiledata['frames'] = database['segments'][segment]['frames']
|
||||
tiledata['words'] = database['segments'][segment]['words']
|
||||
tiledata['segment_type'] = database['segments'][segment]['type']
|
||||
for block_type, (framebase,
|
||||
wordbase) in sorted(src_segment["baseaddr"].items()):
|
||||
verbose and print(
|
||||
'up_INT: %s: %s.0x%08X:%u' %
|
||||
(src_segment_name, block_type, framebase, wordbase))
|
||||
|
||||
database = database["tiles"]
|
||||
def process_CLB_IO_CLK(wordbase):
|
||||
'''
|
||||
Lookup interconnect tile associated with this segment
|
||||
Use it to locate in the grid, and find other segments related by tile offset
|
||||
'''
|
||||
|
||||
#######################################
|
||||
# Write
|
||||
inttile = get_inttile(database, src_segment)
|
||||
verbose and print(
|
||||
' up_INT CLK_IO_CLK: %s => inttile %s' %
|
||||
(src_segment_name, inttile))
|
||||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
print(json.dumps(database, sort_keys=True, indent="\t"))
|
||||
for i in range(50):
|
||||
grid_y -= 1
|
||||
dst_tile = database[tiles_by_grid[(grid_x, grid_y)]]
|
||||
|
||||
if wordbase == 50:
|
||||
wordbase += 1
|
||||
else:
|
||||
wordbase += 2
|
||||
|
||||
#verbose and print(' dst_tile', dst_tile)
|
||||
dst_segment_name = dst_tile["segment"]
|
||||
#verbose and print('up_INT: %s => %s' % (src_segment_name, dst_segment_name))
|
||||
segments[dst_segment_name].setdefault(
|
||||
"baseaddr", {})[block_type] = [framebase, wordbase]
|
||||
|
||||
def process_BLOCK_RAM(wordbase):
|
||||
'''
|
||||
Lookup BRAM0 tile associated with this segment
|
||||
Use it to locate in the grid, and find other BRAM0 related by tile offset
|
||||
|
||||
|
||||
From minitest:
|
||||
|
||||
build/roi_bramd_bit01.diff (lowest BRAM coordinate)
|
||||
> bit_00c00000_000_00
|
||||
|
||||
build/roi_bramds_bit01.diff
|
||||
> bit_00c00000_000_00
|
||||
> bit_00c00000_010_00
|
||||
> bit_00c00000_020_00
|
||||
> bit_00c00000_030_00
|
||||
> bit_00c00000_040_00
|
||||
> bit_00c00000_051_00
|
||||
> bit_00c00000_061_00
|
||||
> bit_00c00000_071_00
|
||||
> bit_00c00000_081_00
|
||||
> bit_00c00000_091_00
|
||||
'''
|
||||
src_tile_name = get_bramtile(database, src_segment)
|
||||
verbose and print(
|
||||
' up_INT BLOCK_RAM: %s => %s' %
|
||||
(src_segment_name, src_tile_name))
|
||||
grid_x = database[src_tile_name]["grid_x"]
|
||||
grid_y = database[src_tile_name]["grid_y"]
|
||||
|
||||
for i in range(9):
|
||||
grid_y -= 5
|
||||
wordbase += 10
|
||||
# Skip HCLK
|
||||
if i == 4:
|
||||
grid_y -= 1
|
||||
wordbase += 1
|
||||
|
||||
dst_tile = database[tiles_by_grid[(grid_x, grid_y)]]
|
||||
assert nolr(dst_tile['type']) == 'BRAM', dst_tile
|
||||
|
||||
dst_segment_name = dst_tile["segment"]
|
||||
assert 'BRAM0' in dst_segment_name
|
||||
segments[dst_segment_name].setdefault(
|
||||
"baseaddr", {})[block_type] = [framebase, wordbase]
|
||||
|
||||
{
|
||||
'CLB_IO_CLK': process_CLB_IO_CLK,
|
||||
'BLOCK_RAM': process_BLOCK_RAM,
|
||||
}[block_type](
|
||||
wordbase)
|
||||
|
||||
|
||||
def add_tile_bits(tile_db, baseaddr, offset, frames, words, height=None):
|
||||
'''
|
||||
Record data structure geometry for the given tile baseaddr
|
||||
For most tiles there is only one baseaddr, but some like BRAM have multiple
|
||||
|
||||
Notes on multiple block types:
|
||||
https://github.com/SymbiFlow/prjxray/issues/145
|
||||
'''
|
||||
|
||||
bits = tile_db['bits']
|
||||
block_type = addr2btype(baseaddr)
|
||||
|
||||
assert 0 <= offset <= 100, offset
|
||||
assert 1 <= words <= 101
|
||||
assert offset + words <= 101, (
|
||||
tile_db, offset + words, offset, words, block_type)
|
||||
|
||||
assert block_type not in bits
|
||||
block = bits.setdefault(block_type, {})
|
||||
|
||||
# FDRI address
|
||||
block["baseaddr"] = '0x%08X' % baseaddr
|
||||
# Number of frames this entry is sretched across
|
||||
# that is the following FDRI addresses are used: range(baseaddr, baseaddr + frames)
|
||||
block["frames"] = frames
|
||||
|
||||
# Index of first word used within each frame
|
||||
block["offset"] = offset
|
||||
# Number of words consumed in each frame
|
||||
block["words"] = words
|
||||
|
||||
# related to words...
|
||||
# deprecated field? Don't worry about for now
|
||||
if height is not None:
|
||||
block["height"] = height
|
||||
|
||||
|
||||
def db_add_bits(database, segments):
|
||||
'''Transfer segment data into tiles'''
|
||||
for segment_name in segments.keys():
|
||||
for block_type, (baseaddr,
|
||||
offset) in segments[segment_name]["baseaddr"].items():
|
||||
for tile_name in segments[segment_name]["tiles"]:
|
||||
tile_type = database[tile_name]["type"]
|
||||
entry = {
|
||||
# (tile_type, block_type): (frames, words, height)
|
||||
("CLBLL", "CLB_IO_CLK"): (36, 2, 2),
|
||||
("CLBLM", "CLB_IO_CLK"): (36, 2, 2),
|
||||
("HCLK", "CLB_IO_CLK"): (26, 1, 1),
|
||||
("INT", "CLB_IO_CLK"): (28, 2, 2),
|
||||
("BRAM", "CLB_IO_CLK"): (28, 2, None),
|
||||
("BRAM", "BLOCK_RAM"): (128, 5, None),
|
||||
("DSP", "CLB_IO_CLK"): (28, 2, 10),
|
||||
("INT_INTERFACE", "CLB_IO_CLK"): (28, 2, None),
|
||||
("BRAM_INT_INTERFACE", "CLB_IO_CLK"): (28, 2, None),
|
||||
}.get((nolr(tile_type), block_type), None)
|
||||
if entry is None:
|
||||
# Other types are rare, not expected to have these
|
||||
if block_type == "CLB_IO_CLK":
|
||||
raise ValueError("Unknown tile type %s" % tile_type)
|
||||
continue
|
||||
|
||||
frames, words, height = entry
|
||||
if frames:
|
||||
# if we have a width, we should have a height
|
||||
assert frames and words
|
||||
add_tile_bits(
|
||||
database[tile_name], baseaddr, offset, frames, words,
|
||||
height)
|
||||
|
||||
|
||||
def db_add_segments(database, segments):
|
||||
# TODO: Migrate to new tilegrid format via library. This data is added for
|
||||
# compability with unconverted tools. Update tools then remove this data from
|
||||
# tilegrid.json.
|
||||
# looks like only htmlgen is using this?
|
||||
for tiledata in database.values():
|
||||
if "segment" in tiledata:
|
||||
segment = tiledata["segment"]
|
||||
tiledata["segment_type"] = segments[segment]["type"]
|
||||
|
||||
|
||||
def run(tiles_fn, json_fn, deltas_fns, verbose=False):
|
||||
# Load input files
|
||||
tiles = load_tiles(tiles_fn)
|
||||
site_baseaddr = load_baseaddrs(deltas_fns)
|
||||
|
||||
# Index input
|
||||
database = make_database(tiles)
|
||||
tile_baseaddrs = make_tile_baseaddrs(tiles, site_baseaddr, verbose=verbose)
|
||||
tiles_by_grid = make_tiles_by_grid(tiles)
|
||||
|
||||
segments = make_segments(
|
||||
database, tiles_by_grid, tile_baseaddrs, verbose=verbose)
|
||||
|
||||
# Reference adjacent CLBs to locate adjacent tiles by known offsets
|
||||
seg_base_addr_lr_INT(database, segments, tiles_by_grid, verbose=verbose)
|
||||
seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=verbose)
|
||||
|
||||
db_add_bits(database, segments)
|
||||
db_add_segments(database, segments)
|
||||
|
||||
# Save
|
||||
json.dump(
|
||||
database,
|
||||
open(json_fn, 'w'),
|
||||
sort_keys=True,
|
||||
indent=4,
|
||||
separators=(',', ': '))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
import glob
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate tilegrid.json from bitstream deltas')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--out', default='/dev/stdout', help='Output JSON')
|
||||
parser.add_argument(
|
||||
'--tiles', default='tiles.txt', help='Input tiles.txt tcl output')
|
||||
parser.add_argument(
|
||||
'deltas', nargs='*', help='.bit diffs to create base addresses from')
|
||||
args = parser.parse_args()
|
||||
|
||||
deltas = args.deltas
|
||||
if not args.deltas:
|
||||
deltas = glob.glob('*.delta')
|
||||
|
||||
run(args.tiles, args.out, deltas, verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,100 +1,152 @@
|
|||
create_project -force -part $::env(XRAY_PART) design design
|
||||
proc make_project {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
|
||||
read_verilog ../top.v
|
||||
synth_design -top top
|
||||
read_verilog ../top.v
|
||||
synth_design -top top
|
||||
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
|
||||
create_pblock roi
|
||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
||||
create_pblock roi
|
||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
set_param tcl.collectionResultDisplayLimit 0
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
set_param tcl.collectionResultDisplayLimit 0
|
||||
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
set luts [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT]
|
||||
set selected_luts {}
|
||||
set lut_index 0
|
||||
|
||||
if 0 {
|
||||
set grid_min_x -1
|
||||
set grid_max_x -1
|
||||
set grid_min_y -1
|
||||
set grid_max_y -1
|
||||
} {
|
||||
set grid_min_x $::env(XRAY_ROI_GRID_X1)
|
||||
set grid_max_x $::env(XRAY_ROI_GRID_X2)
|
||||
set grid_min_y $::env(XRAY_ROI_GRID_Y1)
|
||||
set grid_max_y $::env(XRAY_ROI_GRID_Y2)
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
}
|
||||
|
||||
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per column)
|
||||
# Also, if GRID_MIN/MAX is not defined, automatically create it based on used CLBs
|
||||
# See caveat in README on automatic creation
|
||||
foreach lut $luts {
|
||||
set tile [get_tile -of_objects $lut]
|
||||
set grid_x [get_property GRID_POINT_X $tile]
|
||||
set grid_y [get_property GRID_POINT_Y $tile]
|
||||
proc loc_luts {} {
|
||||
set luts [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT]
|
||||
set selected_luts {}
|
||||
set lut_index 0
|
||||
|
||||
if [expr $grid_min_x < 0 || $grid_x < $grid_min_x] {set grid_min_x $grid_x}
|
||||
if [expr $grid_max_x < 0 || $grid_x > $grid_max_x] {set grid_max_x $grid_x}
|
||||
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per CMT column)
|
||||
foreach lut $luts {
|
||||
set tile [get_tile -of_objects $lut]
|
||||
set grid_x [get_property GRID_POINT_X $tile]
|
||||
set grid_y [get_property GRID_POINT_Y $tile]
|
||||
|
||||
if [expr $grid_min_y < 0 || $grid_y < $grid_min_y] {set grid_min_y $grid_y}
|
||||
if [expr $grid_max_y < 0 || $grid_y > $grid_max_y] {set grid_max_y $grid_y}
|
||||
|
||||
# 50 per column => 50, 100, 150, etc
|
||||
if [regexp "Y(0|[0-9]*[05]0)/" $lut] {
|
||||
set cell [get_cells roi/is[$lut_index].lut]
|
||||
set_property LOC [get_sites -of_objects $lut] $cell
|
||||
set lut_index [expr $lut_index + 1]
|
||||
lappend selected_luts $lut
|
||||
}
|
||||
# 50 per column => 50, 100, 150, etc
|
||||
# ex: SLICE_X2Y50/A6LUT
|
||||
# Only take one of the CLBs within a slice
|
||||
if [regexp "X.*[02468]Y.*[05]0/" $lut] {
|
||||
set cell [get_cells roi/luts[$lut_index].lut]
|
||||
set_property LOC [get_sites -of_objects $lut] $cell
|
||||
set lut_index [expr $lut_index + 1]
|
||||
lappend selected_luts $lut
|
||||
}
|
||||
}
|
||||
return $selected_luts
|
||||
}
|
||||
|
||||
place_design
|
||||
route_design
|
||||
proc loc_brams {} {
|
||||
# XXX: for some reason this doesn't work if there is a cell already there
|
||||
# but LUTs don't have this issue
|
||||
set brams [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ RAMBFIFO36E1*}]
|
||||
set selected_brams {}
|
||||
set bram_index 0
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
# LOC one BRAM (a "selected_lut") into each BRAM segment configuration column (ie 10 per CMT column)
|
||||
foreach bram $brams {
|
||||
set tile [get_tile -of_objects $bram]
|
||||
set grid_x [get_property GRID_POINT_X $tile]
|
||||
set grid_y [get_property GRID_POINT_Y $tile]
|
||||
|
||||
# Get all tiles in ROI, ie not just the selected LUTs
|
||||
set tiles [get_tiles -filter "GRID_POINT_X >= $grid_min_x && GRID_POINT_X <= $grid_max_x && GRID_POINT_Y >= $grid_min_y && GRID_POINT_Y <= $grid_max_y"]
|
||||
|
||||
# Write tiles.txt with site metadata
|
||||
set fp [open "tiles.txt" w]
|
||||
foreach tile $tiles {
|
||||
set type [get_property TYPE $tile]
|
||||
set grid_x [get_property GRID_POINT_X $tile]
|
||||
set grid_y [get_property GRID_POINT_Y $tile]
|
||||
set sites [get_sites -quiet -of_objects $tile]
|
||||
set typed_sites {}
|
||||
|
||||
if [llength $sites] {
|
||||
set site_types [get_property SITE_TYPE $sites]
|
||||
foreach t $site_types s $sites {
|
||||
lappend typed_sites $t $s
|
||||
}
|
||||
}
|
||||
|
||||
puts $fp "$type $tile $grid_x $grid_y $typed_sites"
|
||||
}
|
||||
close $fp
|
||||
|
||||
# Toggle one bit in each selected LUT to generate base addresses
|
||||
for {set i 0} {$i < $lut_index} {incr i} {
|
||||
set cell [get_cells roi/is[$i].lut]
|
||||
set orig_init [get_property INIT $cell]
|
||||
# Flip a bit by changing MSB 0 => 1
|
||||
set new_init [regsub "h8" $orig_init "h0"]
|
||||
set_property INIT $new_init $cell
|
||||
write_bitstream -force design_[get_sites -of_objects [lindex $selected_luts $i]].bit
|
||||
set_property INIT $orig_init $cell
|
||||
# 10 per column => 10, 20, ,etc
|
||||
# ex: RAMB36_X0Y10/RAMBFIFO36E1
|
||||
if [regexp "Y.*0/" $bram] {
|
||||
set cell [get_cells roi/brams[$bram_index].bram]
|
||||
set_property LOC [get_sites -of_objects $bram] $cell
|
||||
set bram_index [expr $bram_index + 1]
|
||||
lappend selected_brams $bram
|
||||
}
|
||||
}
|
||||
return $selected_brams
|
||||
}
|
||||
|
||||
proc write_tiles_txt {} {
|
||||
# Get all tiles in ROI, ie not just the selected LUTs
|
||||
set grid_min_x $::env(XRAY_ROI_GRID_X1)
|
||||
set grid_max_x $::env(XRAY_ROI_GRID_X2)
|
||||
set grid_min_y $::env(XRAY_ROI_GRID_Y1)
|
||||
set grid_max_y $::env(XRAY_ROI_GRID_Y2)
|
||||
set tiles [get_tiles -filter "GRID_POINT_X >= $grid_min_x && GRID_POINT_X <= $grid_max_x && GRID_POINT_Y >= $grid_min_y && GRID_POINT_Y <= $grid_max_y"]
|
||||
|
||||
# Write tiles.txt with site metadata
|
||||
set fp [open "tiles.txt" w]
|
||||
foreach tile $tiles {
|
||||
set type [get_property TYPE $tile]
|
||||
set grid_x [get_property GRID_POINT_X $tile]
|
||||
set grid_y [get_property GRID_POINT_Y $tile]
|
||||
set sites [get_sites -quiet -of_objects $tile]
|
||||
set typed_sites {}
|
||||
|
||||
if [llength $sites] {
|
||||
set site_types [get_property SITE_TYPE $sites]
|
||||
foreach t $site_types s $sites {
|
||||
lappend typed_sites $t $s
|
||||
}
|
||||
}
|
||||
|
||||
puts $fp "$type $tile $grid_x $grid_y $typed_sites"
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
|
||||
proc write_clbs { selected_luts } {
|
||||
# Toggle one bit in each selected LUT to generate base addresses
|
||||
for {set i 0} {$i < [llength $selected_luts]} {incr i} {
|
||||
puts ""
|
||||
set cell [get_cells roi/luts[$i].lut]
|
||||
puts "LUT $cell"
|
||||
set orig_init [get_property INIT $cell]
|
||||
# Flip a bit by changing MSB 0 => 1
|
||||
set new_init [regsub "h8" $orig_init "h0"]
|
||||
puts "INIT $orig_init => $new_init"
|
||||
set_property INIT $new_init $cell
|
||||
write_bitstream -force design_[get_sites -of_objects [lindex $selected_luts $i]].bit
|
||||
set_property INIT $orig_init $cell
|
||||
}
|
||||
}
|
||||
|
||||
proc write_brams { selected_brams } {
|
||||
# Toggle one bit in each selected BRAM to generate base addresses
|
||||
for {set i 0} {$i < [llength $selected_brams]} {incr i} {
|
||||
puts ""
|
||||
set cell [get_cells roi/brams[$i].bram]
|
||||
puts "BRAM $cell"
|
||||
set orig_init [get_property INIT_00 $cell]
|
||||
# Flip a bit by changing MSB 0 => 1
|
||||
set new_init [regsub "h8" $orig_init "h0"]
|
||||
puts "INIT_00 $orig_init => $new_init"
|
||||
set_property INIT_00 $new_init $cell
|
||||
write_bitstream -force design_[get_sites -of_objects [lindex $selected_brams $i]].bit
|
||||
set_property INIT_00 $orig_init $cell
|
||||
}
|
||||
}
|
||||
|
||||
proc run {} {
|
||||
make_project
|
||||
set selected_luts [loc_luts]
|
||||
puts "Selected LUTs: [llength $selected_luts]"
|
||||
set selected_brams [loc_brams]
|
||||
puts "Selected LUTs: [llength $selected_brams]"
|
||||
|
||||
place_design
|
||||
route_design
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
|
||||
write_tiles_txt
|
||||
write_clbs $selected_luts
|
||||
write_brams $selected_brams
|
||||
}
|
||||
|
||||
run
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
//Need at least one LUT per frame base address we want
|
||||
`define N 100
|
||||
`define N_LUT 100
|
||||
`define N_BRAM 8
|
||||
|
||||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 6;
|
||||
localparam integer DOUT_N = `N;
|
||||
localparam integer DIN_N = 8;
|
||||
localparam integer DOUT_N = `N_LUT + `N_BRAM;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
|
@ -29,10 +30,10 @@ module top(input clk, stb, di, output do);
|
|||
);
|
||||
endmodule
|
||||
|
||||
module roi(input clk, input [5:0] din, output [`N-1:0] dout);
|
||||
module roi(input clk, input [7:0] din, output [`N_LUT + `N_BRAM-1:0] dout);
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < `N; i = i+1) begin:is
|
||||
for (i = 0; i < `N_LUT; i = i+1) begin:luts
|
||||
LUT6 #(
|
||||
.INIT(64'h8000_0000_0000_0001 + (i << 16))
|
||||
) lut (
|
||||
|
|
@ -46,4 +47,36 @@ module roi(input clk, input [5:0] din, output [`N-1:0] dout);
|
|||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
genvar j;
|
||||
generate
|
||||
for (j = 0; j < `N_BRAM; j = j+1) begin:brams
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
RAMB36E1 #(
|
||||
.INIT_00(256'h8000000000000000000000000000000000000000000000000000000000000000 + (j << 16))
|
||||
) bram (
|
||||
.CLKARDCLK(din[0]),
|
||||
.CLKBWRCLK(din[1]),
|
||||
.ENARDEN(din[2]),
|
||||
.ENBWREN(din[3]),
|
||||
.REGCEAREGCE(din[4]),
|
||||
.REGCEB(din[5]),
|
||||
.RSTRAMARSTRAM(din[6]),
|
||||
.RSTRAMB(din[7]),
|
||||
.RSTREGARSTREG(din[0]),
|
||||
.RSTREGB(din[1]),
|
||||
.ADDRARDADDR(din[2]),
|
||||
.ADDRBWRADDR(din[3]),
|
||||
.DIADI(din[4]),
|
||||
.DIBDI(din[5]),
|
||||
.DIPADIP(din[6]),
|
||||
.DIPBDIP(din[7]),
|
||||
.WEA(din[0]),
|
||||
.WEBWE(din[1]),
|
||||
.DOADO(dout[j + `N_LUT]),
|
||||
.DOBDO(),
|
||||
.DOPADOP(),
|
||||
.DOPBDOP());
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ with open("design_%s.txt" % sys.argv[1], "r") as f:
|
|||
for i in range(64):
|
||||
bitname = "%s.INIT[%02d]" % (bel, i)
|
||||
bitname = bitname.replace("6LUT", "LUT")
|
||||
segmk.addtag(site, bitname, ((init >> i) & 1) != 0)
|
||||
segmk.add_site_tag(site, bitname, ((init >> i) & 1) != 0)
|
||||
|
||||
segmk.compile()
|
||||
segmk.write(sys.argv[1])
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
/specimen_*/
|
||||
/tileconn.json
|
||||
/run.ok
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
N := 1
|
||||
SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
|
||||
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
|
||||
|
||||
database: $(SPECIMENS_OK)
|
||||
cp specimen_001/tileconn.json tileconn.json
|
||||
|
||||
pushdb:
|
||||
cp tileconn.json ${XRAY_DATABASE_DIR}/$(XRAY_DATABASE)/tileconn.json
|
||||
|
||||
$(SPECIMENS_OK):
|
||||
bash generate.sh $(subst /OK,,$@)
|
||||
touch $@
|
||||
|
||||
run:
|
||||
$(MAKE) clean
|
||||
$(MAKE) database
|
||||
$(MAKE) pushdb
|
||||
touch run.ok
|
||||
|
||||
clean:
|
||||
rm -rf specimen_[0-9][0-9][0-9]/ tileconn.json run.ok
|
||||
|
||||
.PHONY: database pushdb run clean
|
||||
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os, sys, json, re
|
||||
|
||||
tilenodes = dict()
|
||||
grid2tile = dict()
|
||||
database = dict()
|
||||
|
||||
print("Loading %s grid." % os.getenv("XRAY_DATABASE"))
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
grid = json.load(f)
|
||||
|
||||
for tile, tiledata in grid.items():
|
||||
grid_xy = (tiledata["grid_x"], tiledata["grid_y"])
|
||||
grid2tile[grid_xy] = tile
|
||||
|
||||
print("Loading nodewires.txt.")
|
||||
with open("nodewires.txt") as f:
|
||||
for line in f:
|
||||
node, *wires = line.split()
|
||||
for wire in wires:
|
||||
wire_tile, wire_name = wire.split("/")
|
||||
if wire_tile not in tilenodes:
|
||||
tilenodes[wire_tile] = dict()
|
||||
tilenodes[wire_tile][node] = wire_name
|
||||
|
||||
|
||||
def filter_pair(type1, type2, wire1, wire2, delta_x, delta_y):
|
||||
if type1 in ["HCLK_L", "HCLK_R"]:
|
||||
is_vertical_wire = False
|
||||
if wire1.startswith("HCLK_S"): is_vertical_wire = True
|
||||
if wire1.startswith("HCLK_N"): is_vertical_wire = True
|
||||
if wire1.startswith("HCLK_W"): is_vertical_wire = True
|
||||
if wire1.startswith("HCLK_E"): is_vertical_wire = True
|
||||
if wire1.startswith("HCLK_LV"): is_vertical_wire = True
|
||||
if wire1.startswith("HCLK_BYP"): is_vertical_wire = True
|
||||
if wire1.startswith("HCLK_FAN"): is_vertical_wire = True
|
||||
if wire1.startswith("HCLK_LEAF_CLK_"): is_vertical_wire = True
|
||||
|
||||
is_horizontal_wire = False
|
||||
if wire1.startswith("HCLK_CK_"): is_horizontal_wire = True
|
||||
if wire1.startswith("HCLK_INT_"): is_horizontal_wire = True
|
||||
|
||||
assert is_vertical_wire != is_horizontal_wire
|
||||
if is_vertical_wire and delta_y == 0: return True
|
||||
if is_horizontal_wire and delta_x == 0: return True
|
||||
|
||||
if type1 in ["INT_L", "INT_R"]:
|
||||
# the wires with underscore after BEG/END all connect vertically
|
||||
if (("BEG_" in wire1) or ("END_" in wire1)) and delta_y == 0:
|
||||
return True
|
||||
|
||||
if type1 in ["BRKH_INT", "BRKH_B_TERM_INT", "T_TERM_INT"]:
|
||||
if delta_y == 0: return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def handle_pair(tile1, tile2):
|
||||
if tile1 not in tilenodes: return
|
||||
if tile2 not in tilenodes: return
|
||||
|
||||
tile1data = grid[tile1]
|
||||
tile2data = grid[tile2]
|
||||
|
||||
grid1_xy = (tile1data["grid_x"], tile1data["grid_y"])
|
||||
grid2_xy = (tile2data["grid_x"], tile2data["grid_y"])
|
||||
|
||||
if grid1_xy > grid2_xy:
|
||||
return handle_pair(tile2, tile1)
|
||||
|
||||
key = (
|
||||
tile1data["type"], tile2data["type"], grid2_xy[0] - grid1_xy[0],
|
||||
grid2_xy[1] - grid1_xy[1])
|
||||
|
||||
wire_pairs = set()
|
||||
|
||||
for node, wire1 in tilenodes[tile1].items():
|
||||
if node in tilenodes[tile2]:
|
||||
wire2 = tilenodes[tile2][node]
|
||||
if filter_pair(key[0], key[1], wire1, wire2, key[2], key[3]):
|
||||
continue
|
||||
if filter_pair(key[1], key[0], wire2, wire1, -key[2], -key[3]):
|
||||
continue
|
||||
wire_pairs.add((wire1, wire2))
|
||||
|
||||
if key not in database:
|
||||
database[key] = wire_pairs
|
||||
else:
|
||||
database[key] &= wire_pairs
|
||||
|
||||
|
||||
for tile, tiledata in grid.items():
|
||||
grid_right_xy = (tiledata["grid_x"] + 1, tiledata["grid_y"])
|
||||
grid_below_xy = (tiledata["grid_x"], tiledata["grid_y"] + 1)
|
||||
|
||||
if grid_right_xy in grid2tile:
|
||||
handle_pair(tile, grid2tile[grid_right_xy])
|
||||
|
||||
if grid_below_xy in grid2tile:
|
||||
handle_pair(tile, grid2tile[grid_below_xy])
|
||||
|
||||
print("Converting database.")
|
||||
json_db = list()
|
||||
for key in sorted(database.keys()):
|
||||
(t1, t2, dx, dy) = key
|
||||
entry = dict()
|
||||
entry["tile_types"] = [t1, t2]
|
||||
entry["grid_deltas"] = [dx, dy]
|
||||
entry["wire_pairs"] = list(sorted(database[key]))
|
||||
if len(entry["wire_pairs"]):
|
||||
json_db.append(entry)
|
||||
|
||||
print("Writing tileconn.json.")
|
||||
with open("tileconn.json", "w") as f:
|
||||
print(json.dumps(json_db, sort_keys=True, indent="\t"), file=f)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/bin/bash -x
|
||||
|
||||
source ${XRAY_GENHEADER}
|
||||
|
||||
vivado -mode batch -source ../generate.tcl
|
||||
|
||||
python3 ../generate.py
|
||||
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
create_project -force -part $::env(XRAY_PART) design design
|
||||
|
||||
read_verilog ../top.v
|
||||
synth_design -top top
|
||||
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports a]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports y]
|
||||
|
||||
create_pblock roi
|
||||
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
set_param tcl.collectionResultDisplayLimit 0
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
# write_bitstream -force design.bit
|
||||
|
||||
source ../../../utils/utils.tcl
|
||||
|
||||
set fp [open "nodewires.txt" w]
|
||||
foreach node [get_nodes -of_objects [roi_tiles]] {
|
||||
puts $fp "$node [get_wires -of_objects $node]"
|
||||
}
|
||||
close $fp
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
module top(input a, output y);
|
||||
assign y = a;
|
||||
endmodule
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,139 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
|
||||
enumdb = dict()
|
||||
|
||||
|
||||
def get_enums(segtype):
|
||||
if segtype in enumdb:
|
||||
return enumdb[segtype]
|
||||
|
||||
enumdb[segtype] = {}
|
||||
|
||||
def process(l):
|
||||
l = l.strip()
|
||||
|
||||
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
||||
parts = line.split()
|
||||
name = parts[0]
|
||||
bit_vals = parts[1:]
|
||||
|
||||
# Assumption
|
||||
# only 1 bit => non-enumerated value
|
||||
enumdb[segtype][name] = len(bit_vals) != 1
|
||||
|
||||
with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE"), segtype),
|
||||
"r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
with open("%s/%s/segbits_int_%s.db" %
|
||||
(os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
segtype[-1]), "r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
return enumdb[segtype]
|
||||
|
||||
|
||||
def isenum(segtype, tag):
|
||||
return get_enums(segtype)[tag]
|
||||
|
||||
|
||||
def tag2fasm(grid, seg, tag):
|
||||
'''Given tilegrid, segment name and tag, return fasm directive'''
|
||||
segj = grid['segments'][seg]
|
||||
|
||||
m = re.match(r'([A-Za-z0-9_]+)[.](.*)', tag)
|
||||
tile_type = m.group(1)
|
||||
tag_post = m.group(2)
|
||||
|
||||
# Find associated tile
|
||||
for tile in segj['tiles']:
|
||||
if grid['tiles'][tile]['type'] == tile_type:
|
||||
break
|
||||
else:
|
||||
raise Exception("Couldn't find tile type %s" % tile_type)
|
||||
|
||||
if not isenum(segj['type'], tag):
|
||||
return '%s.%s 1' % (tile, tag_post)
|
||||
else:
|
||||
# Make the selection an argument of the configruation
|
||||
m = re.match(r'(.*)[.]([A-Za-z0-9_]+)', tag_post)
|
||||
which = m.group(1)
|
||||
value = m.group(2)
|
||||
return '%s.%s %s' % (tile, which, value)
|
||||
|
||||
|
||||
def run(f_in, f_out, sparse=False):
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
new_grid = json.load(f)
|
||||
|
||||
# TODO: Migrate to new tilegrid format via library.
|
||||
grid = {'tiles': new_grid, 'segments': {}}
|
||||
|
||||
for tilename, tile in grid['tiles'].items():
|
||||
if 'segment' in tile:
|
||||
segment = tile['segment']
|
||||
|
||||
if segment not in grid['segments']:
|
||||
grid['segments'][segment] = {
|
||||
'baseaddr': (
|
||||
tile['baseaddr'],
|
||||
tile['offset'],
|
||||
),
|
||||
'type': tile['segment_type'],
|
||||
'frames': tile['frames'],
|
||||
'words': tile['words'],
|
||||
'tiles': [tilename]
|
||||
}
|
||||
else:
|
||||
assert grid['segments'][segment]['baseaddr'] == (
|
||||
tile['baseaddr'],
|
||||
tile['offset'],
|
||||
)
|
||||
assert grid['segments'][segment]['type'] == tile[
|
||||
'segment_type']
|
||||
assert grid['segments'][segment]['frames'] == tile['frames']
|
||||
assert grid['segments'][segment]['words'] == tile['words']
|
||||
|
||||
grid['segments'][segment]['tiles'].append(tilename)
|
||||
|
||||
seg = None
|
||||
for l in f_in:
|
||||
l = l.strip()
|
||||
if not l:
|
||||
continue
|
||||
# seg SEG_CLBLM_L_X10Y102
|
||||
# tag CLBLM_L.SLICEM_X0.ALUT.INIT[00]
|
||||
m = re.match('(seg|tag) (.*)', l)
|
||||
if not m:
|
||||
raise Exception("Invalid line %s" % l)
|
||||
type = m.group(1)
|
||||
if type == 'seg':
|
||||
seg = m.group(2)
|
||||
elif type == 'tag':
|
||||
f_out.write(tag2fasm(grid, seg, m.group(2)) + '\n')
|
||||
else:
|
||||
raise Exception("Invalid type %s" % type)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Convert segprint -d output to .fasm file (FPGA assembly)')
|
||||
|
||||
parser.add_argument(
|
||||
'fn_in', default='/dev/stdin', nargs='?', help='Input segment file')
|
||||
parser.add_argument(
|
||||
'fn_out', default='/dev/stdout', nargs='?', help='Output .fasm file')
|
||||
|
||||
args = parser.parse_args()
|
||||
run(open(args.fn_in, 'r'), open(args.fn_out, 'w'))
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
seg SEG_HCLK_L_X31Y130
|
||||
tag HCLK_L.ENABLE_BUFFER.HCLK_CK_BUFHCLK8
|
||||
tag HCLK_L.HCLK_LEAF_CLK_B_BOTL5.HCLK_CK_BUFHCLK8
|
||||
|
||||
seg SEG_CLBLM_L_X10Y102
|
||||
tag CLBLM_L.SLICEM_X0.AFF.DMUX.AX
|
||||
tag CLBLM_L.SLICEM_X0.AFF.ZINI
|
||||
tag CLBLM_L.SLICEM_X0.AFF.ZRST
|
||||
tag CLBLM_L.SLICEM_X0.CEUSEDMUX
|
||||
tag CLBLM_L.SLICEM_X0.SRUSEDMUX
|
||||
tag INT_L.BYP_ALT0.EE2END0
|
||||
tag INT_L.BYP_ALT1.EL1END1
|
||||
tag INT_L.CLK_L1.GCLK_L_B11_WEST
|
||||
tag INT_L.CTRL_L1.ER1END2
|
||||
tag INT_L.FAN_ALT7.BYP_BOUNCE0
|
||||
tag INT_L.WW2BEG0.LOGIC_OUTS_L4
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
|
||||
seg SEG_CLBLM_L_X10Y102
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[00]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[08]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[10]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[11]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[13]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[14]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[15]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[41]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[43]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[44]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[46]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[47]
|
||||
tag CLBLM_L.SLICEM_X0.ALUT.INIT[63]
|
||||
tag INT_L.IMUX_L1.EE2END0
|
||||
tag INT_L.IMUX_L11.EL1END1
|
||||
tag INT_L.IMUX_L2.EE2END1
|
||||
tag INT_L.IMUX_L4.EE2END2
|
||||
tag INT_L.IMUX_L7.EE2END3
|
||||
tag INT_L.IMUX_L8.EL1END0
|
||||
tag INT_L.WW2BEG0.LOGIC_OUTS_L12
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import segprint2fasm
|
||||
|
||||
import unittest
|
||||
import StringIO
|
||||
import re
|
||||
|
||||
|
||||
class TestStringMethods(unittest.TestCase):
|
||||
def check_segprint_fasm_equiv(self, segp_fn, fasm_fn):
|
||||
fout = StringIO.StringIO()
|
||||
segprint2fasm.run(open(segp_fn, 'r'), fout)
|
||||
fasm_out = fout.getvalue()
|
||||
|
||||
fasm_ref = open(fasm_fn, 'r').read()
|
||||
|
||||
def normalize(fasm):
|
||||
'''Remove all comments and sort'''
|
||||
ret = []
|
||||
for l in fasm.split('\n'):
|
||||
# Remove comments
|
||||
i = l.rfind('#')
|
||||
if i >= 0:
|
||||
l = l[0:i]
|
||||
l = l.strip()
|
||||
if not l:
|
||||
continue
|
||||
ret.append(l)
|
||||
return sorted(ret)
|
||||
|
||||
fasm_out = normalize(fasm_out)
|
||||
fasm_ref = normalize(fasm_ref)
|
||||
self.assertEquals(fasm_ref, fasm_out)
|
||||
|
||||
def test_lut_int(self):
|
||||
self.check_segprint_fasm_equiv(
|
||||
'test_data/lut_int/design.segp', 'test_data/lut_int.fasm')
|
||||
|
||||
def test_ff_int(self):
|
||||
self.check_segprint_fasm_equiv(
|
||||
'test_data/ff_int/design.segp', 'test_data/ff_int.fasm')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
#!/usr/bin/env python3
|
||||
'''
|
||||
Take raw .bits files and decode them to higher level functionality
|
||||
This output is intended for debugging and not directly related to FASM
|
||||
However, as of 2018-10-16, the output is being parsed to create FASM,
|
||||
so be mindful when changing output format
|
||||
|
||||
TODO:
|
||||
'''
|
||||
|
||||
import sys, os, json, re
|
||||
|
||||
|
||||
class NoDB(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def line(s=''):
|
||||
print(s)
|
||||
|
||||
|
||||
def comment(s):
|
||||
print('# %s' % s)
|
||||
|
||||
|
||||
enumdb = dict()
|
||||
|
||||
|
||||
# TODO: migrate to library
|
||||
def process_db(tile_type, process):
|
||||
if tile_type in ('INT_L', 'INT_R'):
|
||||
# interconnect
|
||||
fn = "%s/%s/segbits_int_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type[-1].lower())
|
||||
else:
|
||||
# sites
|
||||
fn = "%s/%s/segbits_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type.lower())
|
||||
|
||||
if not os.path.exists(fn):
|
||||
raise NoDB(tile_type)
|
||||
|
||||
with open(fn, "r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
|
||||
def get_enums(tile_type):
|
||||
if tile_type in enumdb:
|
||||
return enumdb[tile_type]
|
||||
|
||||
enumdb[tile_type] = {}
|
||||
|
||||
def process(l):
|
||||
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
||||
parts = l.strip().split()
|
||||
name = parts[0]
|
||||
bit_vals = parts[1:]
|
||||
|
||||
# Assumption
|
||||
# only 1 bit => non-enumerated value
|
||||
enumdb[tile_type][name] = len(bit_vals) != 1
|
||||
|
||||
process_db(tile_type, process)
|
||||
|
||||
return enumdb[tile_type]
|
||||
|
||||
|
||||
def isenum(tilename, tag):
|
||||
return get_enums(tilename)[tag]
|
||||
|
||||
|
||||
# cache
|
||||
segbitsdb = dict()
|
||||
|
||||
|
||||
def get_database(tile_type):
|
||||
if tile_type in segbitsdb:
|
||||
return segbitsdb[tile_type]
|
||||
|
||||
ret = list()
|
||||
|
||||
def process(l):
|
||||
ret.append(l.split())
|
||||
|
||||
process_db(tile_type, process)
|
||||
|
||||
assert len(ret)
|
||||
segbitsdb[tile_type] = ret
|
||||
return ret
|
||||
|
||||
|
||||
def mk_fasm(segj, entry):
|
||||
tile_name = segj['tile_name']
|
||||
|
||||
# ex: CLBLL_L.SLICEL_X0.AFF.DMUX.O6
|
||||
tag = entry[0]
|
||||
m = re.match(r'([A-Za-z0-9_]+)[.](.*)', tag)
|
||||
# tile_type = m.group(1)
|
||||
# the postfix, O6 in the above example
|
||||
tag_post = m.group(2)
|
||||
|
||||
if not isenum(segj['type'], tag):
|
||||
return '%s.%s 1' % (tile_name, tag_post)
|
||||
else:
|
||||
# Make the selection an argument of the configruation
|
||||
m = re.match(r'(.*)[.]([A-Za-z0-9_]+)', tag_post)
|
||||
which = m.group(1)
|
||||
value = m.group(2)
|
||||
return '%s.%s %s' % (tile_name, which, value)
|
||||
|
||||
|
||||
def mk_segbits(seginfo, bitdata):
|
||||
baseframe = int(seginfo["baseaddr"][0], 16)
|
||||
basewordidx = int(seginfo["baseaddr"][1])
|
||||
numframes = int(seginfo["frames"])
|
||||
numwords = int(seginfo["words"])
|
||||
|
||||
segbits = set()
|
||||
for frame in range(baseframe, baseframe + numframes):
|
||||
if frame not in bitdata:
|
||||
continue
|
||||
for wordidx in range(basewordidx, basewordidx + numwords):
|
||||
if wordidx not in bitdata[frame]:
|
||||
continue
|
||||
for bitidx in bitdata[frame][wordidx]:
|
||||
segbits.add(
|
||||
"%02d_%02d" %
|
||||
(frame - baseframe, 32 * (wordidx - basewordidx) + bitidx))
|
||||
return segbits
|
||||
|
||||
|
||||
def tagmatch(entry, segbits):
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!" and bit not in segbits:
|
||||
return False
|
||||
if bit[0] == "!" and bit[1:] in segbits:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def tag_matched(entry, segbits):
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!":
|
||||
segbits.remove(bit)
|
||||
|
||||
|
||||
decode_warnings = set()
|
||||
|
||||
|
||||
def seg_decode(seginfo, segbits, verbose=False):
|
||||
fasms = set()
|
||||
|
||||
# already failed?
|
||||
if seginfo["type"] in decode_warnings:
|
||||
return fasms
|
||||
|
||||
try:
|
||||
db = get_database(seginfo["type"])
|
||||
except NoDB:
|
||||
verbose and comment(
|
||||
"WARNING: failed to load DB for %s" % seginfo["type"])
|
||||
decode_warnings.add(seginfo["type"])
|
||||
return fasms
|
||||
|
||||
for entry in db:
|
||||
if not tagmatch(entry, segbits):
|
||||
continue
|
||||
tag_matched(entry, segbits)
|
||||
#fasms.add('%s.%s 1' % (seginfo['tile_name'], entry[0]))
|
||||
fasm = mk_fasm(seginfo, entry)
|
||||
fasms.add(fasm)
|
||||
return fasms
|
||||
|
||||
|
||||
def handle_segment(segname, grid, bitdata, verbose=False):
|
||||
|
||||
assert segname
|
||||
|
||||
# only print bitstream tiles
|
||||
if segname not in grid["segments"]:
|
||||
return
|
||||
seginfo = grid["segments"][segname]
|
||||
|
||||
segbits = mk_segbits(seginfo, bitdata)
|
||||
|
||||
fasms = seg_decode(seginfo, segbits, verbose=verbose)
|
||||
|
||||
# Found something to print?
|
||||
if len(segbits) == 0 and len(fasms) == 0:
|
||||
return
|
||||
|
||||
line('')
|
||||
comment("seg %s" % (segname, ))
|
||||
|
||||
for fasm in sorted(fasms):
|
||||
line(fasm)
|
||||
|
||||
if verbose and len(segbits) > 0:
|
||||
comment('%u unknown bits' % len(segbits))
|
||||
for bit in sorted(segbits):
|
||||
comment("bit %s" % bit)
|
||||
|
||||
|
||||
def load_bitdata(bits_file):
|
||||
bitdata = dict()
|
||||
|
||||
with open(bits_file, "r") as f:
|
||||
for line in f:
|
||||
line = line.split("_")
|
||||
frame = int(line[1], 16)
|
||||
wordidx = int(line[2], 10)
|
||||
bitidx = int(line[3], 10)
|
||||
|
||||
if frame not in bitdata:
|
||||
bitdata[frame] = dict()
|
||||
|
||||
if wordidx not in bitdata[frame]:
|
||||
bitdata[frame][wordidx] = set()
|
||||
|
||||
bitdata[frame][wordidx].add(bitidx)
|
||||
return bitdata
|
||||
|
||||
|
||||
def mk_grid():
|
||||
'''Load tilegrid, flattening all blocks into one dictionary'''
|
||||
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
new_grid = json.load(f)
|
||||
|
||||
# TODO: Migrate to new tilegrid format via library.
|
||||
grid = {'tiles': new_grid, 'segments': {}}
|
||||
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
bits = tile.get('bits', None)
|
||||
if not bits:
|
||||
continue
|
||||
for block_name, block in bits.items():
|
||||
segname = mksegment(tile_name, block_name)
|
||||
grid['segments'][segname] = {
|
||||
'baseaddr': [
|
||||
block['baseaddr'],
|
||||
block['offset'],
|
||||
],
|
||||
'type': tile['type'],
|
||||
'frames': block['frames'],
|
||||
'words': block['words'],
|
||||
'tile_name': tile_name,
|
||||
'block_name': block_name,
|
||||
}
|
||||
return grid
|
||||
|
||||
|
||||
def mksegment(tile_name, block_name):
|
||||
'''Create a segment name'''
|
||||
return '%s:%s' % (tile_name, block_name)
|
||||
|
||||
|
||||
def tile_segnames(grid):
|
||||
ret = []
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
for block_name in tile['bits'].keys():
|
||||
ret.append(mksegment(tile_name, block_name))
|
||||
return ret
|
||||
|
||||
|
||||
def run(bits_file, segnames, verbose=False):
|
||||
grid = mk_grid()
|
||||
|
||||
bitdata = load_bitdata(bits_file)
|
||||
|
||||
# Default: print all
|
||||
if segnames:
|
||||
for i, segname in enumerate(segnames):
|
||||
# Default to common tile config area if tile given without explicit block
|
||||
if ':' not in segname:
|
||||
segnames[i] = mksegment(segname, 'CLB_IO_CLK')
|
||||
else:
|
||||
segnames = sorted(tile_segnames(grid))
|
||||
|
||||
comment('Segments: %u' % len(segnames))
|
||||
|
||||
# XXX: previously this was sorted by address, not name
|
||||
# revisit?
|
||||
for segname in segnames:
|
||||
handle_segment(segname, grid, bitdata, verbose=verbose)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
# XXX: tool still works, but not well
|
||||
# need to eliminate segments entirely
|
||||
parser = argparse.ArgumentParser(
|
||||
description='XXX: does not print all data?')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('bits_file', help='')
|
||||
parser.add_argument(
|
||||
'segnames', nargs='*', help='List of tile or tile:block to print')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(args.bits_file, args.segnames, verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -4,6 +4,7 @@ while [ -h "$XRAY_ENV_PATH" ]; do # resolve $XRAY_ENV_PATH until the file is no
|
|||
XRAY_ENV_PATH="$(readlink "$XRAY_ENV_PATH")"
|
||||
[[ $XRAY_ENV_PATH != /* ]] && XRAY_ENV_PATH="$XRAY_UTILS_DIR/$XRAY_ENV_PATH" # if $XRAY_ENV_PATH was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||
done
|
||||
export PYTHONPATH="${XRAY_DIR}:$PYTHONPATH"
|
||||
export XRAY_UTILS_DIR="$( cd -P "$( dirname "$XRAY_ENV_PATH" )" && pwd )"
|
||||
|
||||
export XRAY_DIR="$( dirname "$XRAY_UTILS_DIR" )"
|
||||
|
|
@ -21,3 +22,7 @@ export XRAY_DBCHECK="bash ${XRAY_UTILS_DIR}/dbcheck.sh"
|
|||
export XRAY_MASKMERGE="bash ${XRAY_UTILS_DIR}/maskmerge.sh"
|
||||
export XRAY_SEGMATCH="${XRAY_TOOLS_DIR}/segmatch"
|
||||
export XRAY_SEGPRINT="python3 ${XRAY_UTILS_DIR}/segprint.py"
|
||||
export XRAY_BITS2FASM="python3 ${XRAY_UTILS_DIR}/bits2fasm.py"
|
||||
export XRAY_FASM2FRAMES="python3 ${XRAY_UTILS_DIR}/fasm2frames.py"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,26 @@ def parsebit(val):
|
|||
return int(seg_word_column), int(word_bit_n), isset
|
||||
|
||||
|
||||
# TODO: migrate to library
|
||||
def process_db(tile_type, process):
|
||||
fns = [
|
||||
# sites
|
||||
"%s/%s/segbits_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type.lower()),
|
||||
# interconnect
|
||||
"%s/%s/segbits_int_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type[-1].lower()),
|
||||
]
|
||||
|
||||
for fn in fns:
|
||||
if os.path.exists(fn):
|
||||
with open(fn, "r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
|
||||
'''
|
||||
Loosely based on segprint function
|
||||
Maybe better to return as two distinct dictionaries?
|
||||
|
|
@ -37,17 +57,15 @@ Maybe better to return as two distinct dictionaries?
|
|||
segbitsdb = dict()
|
||||
|
||||
|
||||
def get_database(segtype):
|
||||
if segtype in segbitsdb:
|
||||
return segbitsdb[segtype]
|
||||
def get_database(tile_type):
|
||||
if tile_type in segbitsdb:
|
||||
return segbitsdb[tile_type]
|
||||
|
||||
segbitsdb[segtype] = {}
|
||||
segbitsdb[tile_type] = {}
|
||||
|
||||
def process(l):
|
||||
l = l.strip()
|
||||
|
||||
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
||||
parts = line.split()
|
||||
parts = l.strip().split()
|
||||
name = parts[0]
|
||||
bit_vals = parts[1:]
|
||||
|
||||
|
|
@ -59,7 +77,8 @@ def get_database(segtype):
|
|||
raise Exception(
|
||||
"Expect single bit DB entries to be set, got %s" % l)
|
||||
# Treat like an enumerated value with keys 0 or 1
|
||||
segbitsdb[segtype][name] = {
|
||||
assert name not in segbitsdb[tile_type]
|
||||
segbitsdb[tile_type][name] = {
|
||||
'0': [(seg_word_column, word_bit_n, 0)],
|
||||
'1': [(seg_word_column, word_bit_n, 1)],
|
||||
}
|
||||
|
|
@ -67,26 +86,16 @@ def get_database(segtype):
|
|||
# An enumerated value
|
||||
# Split the base name and selected key
|
||||
m = re.match(r'(.+)[.](.+)', name)
|
||||
name = m.group(1)
|
||||
namepart = m.group(1)
|
||||
key = m.group(2)
|
||||
|
||||
# May or may not be the first key encountered
|
||||
bits_map = segbitsdb[segtype].setdefault(name, {})
|
||||
bits_map = segbitsdb[tile_type].setdefault(namepart, {})
|
||||
bits_map[key] = [parsebit(x) for x in bit_vals]
|
||||
|
||||
with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE"), segtype),
|
||||
"r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
process_db(tile_type, process)
|
||||
|
||||
with open("%s/%s/segbits_int_%s.db" %
|
||||
(os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
segtype[-1]), "r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
return segbitsdb[segtype]
|
||||
return segbitsdb[tile_type]
|
||||
|
||||
|
||||
def dump_frames_verbose(frames):
|
||||
|
|
@ -126,34 +135,13 @@ def dump_frm(f, frames):
|
|||
'0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n')
|
||||
|
||||
|
||||
def run(f_in, f_out, sparse=False, debug=False):
|
||||
# address to array of 101 32 bit words
|
||||
frames = {}
|
||||
# Directives we've seen so far
|
||||
# Complain if there is a duplicate
|
||||
# Contains line number of last entry
|
||||
used_names = {}
|
||||
def mksegment(tile_name, block_name):
|
||||
'''Create a segment name'''
|
||||
return '%s:%s' % (tile_name, block_name)
|
||||
|
||||
def frames_init():
|
||||
'''Set all frames to 0'''
|
||||
for segj in grid['segments'].values():
|
||||
seg_baseaddr, seg_word_base = segj['baseaddr']
|
||||
seg_baseaddr = int(seg_baseaddr, 0)
|
||||
for coli in range(segj['frames']):
|
||||
frame_init(seg_baseaddr + coli)
|
||||
|
||||
def frame_init(addr):
|
||||
'''Set given frame to 0'''
|
||||
if not addr in frames:
|
||||
frames[addr] = [0 for _i in range(101)]
|
||||
|
||||
def frame_set(frame_addr, word_addr, bit_index):
|
||||
'''Set given bit in given frame address and word'''
|
||||
frames[frame_addr][word_addr] |= 1 << bit_index
|
||||
|
||||
def frame_clear(frame_addr, word_addr, bit_index):
|
||||
'''Set given bit in given frame address and word'''
|
||||
frames[frame_addr][word_addr] &= 0xFFFFFFFF ^ (1 << bit_index)
|
||||
def mk_grid():
|
||||
'''Load tilegrid, flattening all blocks into one dictionary'''
|
||||
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
|
|
@ -162,76 +150,129 @@ def run(f_in, f_out, sparse=False, debug=False):
|
|||
# TODO: Migrate to new tilegrid format via library.
|
||||
grid = {'tiles': new_grid, 'segments': {}}
|
||||
|
||||
for tile in grid['tiles'].values():
|
||||
if 'segment' in tile:
|
||||
segment = tile['segment']
|
||||
grid['segments'][segment] = {
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
for block_name, block in tile['bits'].items():
|
||||
segname = mksegment(tile_name, block_name)
|
||||
grid['segments'][segname] = {
|
||||
'baseaddr': [
|
||||
tile['baseaddr'],
|
||||
tile['offset'],
|
||||
block['baseaddr'],
|
||||
block['offset'],
|
||||
],
|
||||
'type': tile['segment_type'],
|
||||
'frames': tile['frames'],
|
||||
'words': tile['words'],
|
||||
'type': tile['type'],
|
||||
'frames': block['frames'],
|
||||
'words': block['words'],
|
||||
'tile_name': tile_name,
|
||||
'block_name': block_name,
|
||||
}
|
||||
return grid
|
||||
|
||||
if not sparse:
|
||||
# Initiaize bitstream to 0
|
||||
frames_init()
|
||||
|
||||
for line_number, l in enumerate(f_in, 1):
|
||||
# Comment
|
||||
# Remove all text including and after #
|
||||
i = l.rfind('#')
|
||||
if i >= 0:
|
||||
l = l[0:i]
|
||||
l = l.strip()
|
||||
def frame_init(frames, addr):
|
||||
'''Set given frame to 0'''
|
||||
if not addr in frames:
|
||||
frames[addr] = [0 for _i in range(101)]
|
||||
|
||||
# Ignore blank lines
|
||||
if not l:
|
||||
continue
|
||||
|
||||
# tile.site.stuff value
|
||||
# INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0
|
||||
# Optional value
|
||||
m = re.match(r'([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_.\[\]]+)([ ](.+))?', l)
|
||||
if not m:
|
||||
raise FASMSyntaxError("Bad line: %s" % l)
|
||||
tile = m.group(1)
|
||||
name = m.group(2)
|
||||
value = m.group(4)
|
||||
def frames_init(frames, grid):
|
||||
'''Set all frames to 0'''
|
||||
for segj in grid['segments'].values():
|
||||
seg_baseaddr, seg_word_base = segj['baseaddr']
|
||||
seg_baseaddr = int(seg_baseaddr, 0)
|
||||
for coli in range(segj['frames']):
|
||||
frame_init(frames, seg_baseaddr + coli)
|
||||
|
||||
used_name = (tile, name)
|
||||
old_line_number = used_names.get(used_name, None)
|
||||
if old_line_number:
|
||||
raise FASMSyntaxError(
|
||||
"Duplicate name lines %d and %d, second line: %s" %
|
||||
(old_line_number, line_number, l))
|
||||
used_names[used_name] = line_number
|
||||
|
||||
tilej = grid['tiles'][tile]
|
||||
seg = tilej['segment']
|
||||
segj = grid['segments'][seg]
|
||||
def frame_set(frames, frame_addr, word_addr, bit_index):
|
||||
'''Set given bit in given frame address and word'''
|
||||
frames[frame_addr][word_addr] |= 1 << bit_index
|
||||
|
||||
|
||||
def frame_clear(frames, frame_addr, word_addr, bit_index):
|
||||
'''Set given bit in given frame address and word'''
|
||||
frames[frame_addr][word_addr] &= 0xFFFFFFFF ^ (1 << bit_index)
|
||||
|
||||
|
||||
def parse_line(l):
|
||||
# Comment
|
||||
# Remove all text including and after #
|
||||
i = l.rfind('#')
|
||||
if i >= 0:
|
||||
l = l[0:i]
|
||||
l = l.strip()
|
||||
|
||||
# Ignore blank lines
|
||||
if not l:
|
||||
return
|
||||
|
||||
# tile.site.stuff value
|
||||
# INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0
|
||||
# Optional value
|
||||
m = re.match(r'([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_.\[\]]+)([ ](.+))?', l)
|
||||
if not m:
|
||||
raise FASMSyntaxError("Bad line: %s" % l)
|
||||
tile = m.group(1)
|
||||
name = m.group(2)
|
||||
value = m.group(4)
|
||||
|
||||
return tile, name, value
|
||||
|
||||
|
||||
def check_duplicate(used_names, tile, name, l, line_number):
|
||||
'''Throw an exception if a conflicting FASM directive was given'''
|
||||
used_name = (tile, name)
|
||||
old_line_number = used_names.get(used_name, None)
|
||||
if old_line_number:
|
||||
raise FASMSyntaxError(
|
||||
"Duplicate name lines %d and %d, second line: %s" %
|
||||
(old_line_number, line_number, l))
|
||||
used_names[used_name] = line_number
|
||||
|
||||
|
||||
def update_segbit(
|
||||
frames, seg_word_column, word_bit_n, isset, seg_baseaddr,
|
||||
seg_word_base):
|
||||
'''Set or clear a single bit in a segment at the given word column and word bit position'''
|
||||
# Now we have the word column and word bit index
|
||||
# Combine with the segments relative frame position to fully get the position
|
||||
frame_addr = seg_baseaddr + seg_word_column
|
||||
# 2 words per segment
|
||||
word_addr = seg_word_base + word_bit_n // 32
|
||||
bit_index = word_bit_n % 32
|
||||
if isset:
|
||||
frame_set(frames, frame_addr, word_addr, bit_index)
|
||||
else:
|
||||
frame_clear(frames, frame_addr, word_addr, bit_index)
|
||||
|
||||
|
||||
def default_value(db_vals, name):
|
||||
# If its binary, allow omitted value default to 1
|
||||
if tuple(sorted(db_vals.keys())) == ('0', '1'):
|
||||
return '1'
|
||||
else:
|
||||
raise FASMSyntaxError(
|
||||
"Enumerable entry %s must have explicit value" % name)
|
||||
|
||||
|
||||
def process_line(line_number, l, grid, frames, used_names):
|
||||
parsed = parse_line(l)
|
||||
# empty line
|
||||
if not parsed:
|
||||
return
|
||||
tile_name, name, value = parsed
|
||||
check_duplicate(used_names, tile_name, name, l, line_number)
|
||||
|
||||
tilej = grid['tiles'][tile_name]
|
||||
for block_name, block in tilej['bits'].items():
|
||||
segname = mksegment(tile_name, block_name)
|
||||
|
||||
segj = grid['segments'][segname]
|
||||
seg_baseaddr, seg_word_base = segj['baseaddr']
|
||||
seg_baseaddr = int(seg_baseaddr, 0)
|
||||
|
||||
# Ensure that all frames exist for this segment
|
||||
# FIXME: type dependent
|
||||
for coli in range(segj['frames']):
|
||||
frame_init(seg_baseaddr + coli)
|
||||
|
||||
def update_segbit(seg_word_column, word_bit_n, isset):
|
||||
'''Set or clear a single bit in a segment at the given word column and word bit position'''
|
||||
# Now we have the word column and word bit index
|
||||
# Combine with the segments relative frame position to fully get the position
|
||||
frame_addr = seg_baseaddr + seg_word_column
|
||||
# 2 words per segment
|
||||
word_addr = seg_word_base + word_bit_n // 32
|
||||
bit_index = word_bit_n % 32
|
||||
if isset:
|
||||
frame_set(frame_addr, word_addr, bit_index)
|
||||
else:
|
||||
frame_clear(frame_addr, word_addr, bit_index)
|
||||
frame_init(frames, seg_baseaddr + coli)
|
||||
|
||||
# Now lets look up the bits we need frames for
|
||||
segdb = get_database(segj['type'])
|
||||
|
|
@ -245,12 +286,8 @@ def run(f_in, f_out, sparse=False, debug=False):
|
|||
(segj['type'], db_k, l)) from None
|
||||
|
||||
if not value:
|
||||
# If its binary, allow omitted value default to 1
|
||||
if tuple(sorted(db_vals.keys())) == ('0', '1'):
|
||||
value = '1'
|
||||
else:
|
||||
raise FASMSyntaxError(
|
||||
"Enumerable entry %s must have explicit value" % name)
|
||||
value = default_value(db_vals, name)
|
||||
|
||||
# Get the specific entry we need
|
||||
try:
|
||||
db_vals = db_vals[value]
|
||||
|
|
@ -258,8 +295,29 @@ def run(f_in, f_out, sparse=False, debug=False):
|
|||
raise FASMSyntaxError(
|
||||
"Invalid entry %s. Valid entries are %s" %
|
||||
(value, db_vals.keys()))
|
||||
|
||||
for seg_word_column, word_bit_n, isset in db_vals:
|
||||
update_segbit(seg_word_column, word_bit_n, isset)
|
||||
update_segbit(
|
||||
frames, seg_word_column, word_bit_n, isset, seg_baseaddr,
|
||||
seg_word_base)
|
||||
|
||||
|
||||
def run(f_in, f_out, sparse=False, debug=False):
|
||||
# address to array of 101 32 bit words
|
||||
frames = {}
|
||||
# Directives we've seen so far
|
||||
# Complain if there is a duplicate
|
||||
# Contains line number of last entry
|
||||
used_names = {}
|
||||
|
||||
grid = mk_grid()
|
||||
|
||||
if not sparse:
|
||||
# Initiaize bitstream to 0
|
||||
frames_init(frames, grid)
|
||||
|
||||
for line_number, l in enumerate(f_in, 1):
|
||||
process_line(line_number, l, grid, frames, used_names)
|
||||
|
||||
if debug:
|
||||
#dump_frames_verbose(frames)
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
'''
|
||||
NOTE: "segments" as used in this file is mostly unrelated to tilegrid.json usage
|
||||
ie tilegrid.json has names like SEG_CLBLL_L_X2Y50 where as here they are tile based and named like seg_00400100_02
|
||||
Instead of using tilegrid.json "segments, segments are formed by looking for tiles that use the same address + offset
|
||||
|
||||
Sample segdata.txt output (from 015-clbnffmux/specimen_001/segdata_clbll_r.txt):
|
||||
seg 00020880_048
|
||||
|
|
@ -15,6 +18,8 @@ import os, json, re
|
|||
XRAY_DATABASE = os.getenv("XRAY_DATABASE")
|
||||
XRAY_DIR = os.getenv("XRAY_DIR")
|
||||
|
||||
BLOCK_TYPES = set(('CLB_IO_CLK', 'BLOCK_RAM', 'CFG_CLB'))
|
||||
|
||||
|
||||
def recurse_sum(x):
|
||||
'''Count number of nested iterable occurances'''
|
||||
|
|
@ -36,8 +41,9 @@ def json_hex2i(s):
|
|||
|
||||
|
||||
class segmaker:
|
||||
def __init__(self, bitsfile, verbose=False):
|
||||
self.verbose = verbose
|
||||
def __init__(self, bitsfile, verbose=None):
|
||||
self.verbose = verbose if verbose is not None else os.getenv(
|
||||
'VERBOSE', 'N') == 'Y'
|
||||
self.load_grid()
|
||||
self.load_bits(bitsfile)
|
||||
'''
|
||||
|
|
@ -46,7 +52,8 @@ class segmaker:
|
|||
-site: ex 'SLICE_X13Y101'
|
||||
-name: ex 'CLB.SLICE_X0.AFF.DMUX.CY'
|
||||
'''
|
||||
self.tags = dict()
|
||||
self.site_tags = dict()
|
||||
self.tile_tags = dict()
|
||||
|
||||
# output after compiling
|
||||
self.segments_by_type = None
|
||||
|
|
@ -94,7 +101,7 @@ class segmaker:
|
|||
'Loaded bits: %u bits in %u base frames' %
|
||||
(recurse_sum(self.bits), len(self.bits)))
|
||||
|
||||
def addtag(self, site_tile, name, value):
|
||||
def add_site_tag(self, site, name, value):
|
||||
'''
|
||||
XXX: can add tags in two ways:
|
||||
-By site name
|
||||
|
|
@ -107,7 +114,10 @@ class segmaker:
|
|||
self.addtag('SLICE_X13Y101', 'CLB.SLICE_X0.AFF.DMUX.CY', 1)
|
||||
Indicates that the SLICE_X13Y101 site has an element called 'CLB.SLICE_X0.AFF.DMUX.CY'
|
||||
'''
|
||||
self.tags.setdefault(site_tile, dict())[name] = value
|
||||
self.site_tags.setdefault(site, dict())[name] = value
|
||||
|
||||
def add_tile_tag(self, tile, name, value):
|
||||
self.tile_tags.setdefault(tile, dict())[name] = value
|
||||
|
||||
def compile(self, bitfilter=None):
|
||||
print("Compiling segment data.")
|
||||
|
|
@ -131,11 +141,20 @@ class segmaker:
|
|||
tiledata: tilegrid info for this tile
|
||||
'''
|
||||
assert segname not in segments
|
||||
segments[segname] = {"bits": set(), "tags": dict()}
|
||||
segment = segments.setdefault(
|
||||
segname,
|
||||
{
|
||||
"bits": set(),
|
||||
"tags": dict(),
|
||||
# verify new entries match this
|
||||
"offset": bitj["offset"],
|
||||
"words": bitj["words"],
|
||||
"frames": bitj["frames"],
|
||||
})
|
||||
|
||||
base_frame = json_hex2i(tiledata["baseaddr"])
|
||||
for wordidx in range(tiledata["offset"],
|
||||
tiledata["offset"] + tiledata["height"]):
|
||||
base_frame = json_hex2i(bitj["baseaddr"])
|
||||
for wordidx in range(bitj["offset"],
|
||||
bitj["offset"] + bitj["height"]):
|
||||
if base_frame not in self.bits:
|
||||
continue
|
||||
if wordidx not in self.bits[base_frame]:
|
||||
|
|
@ -144,13 +163,14 @@ class segmaker:
|
|||
base_frame][wordidx]:
|
||||
bitname_frame = bit_frame - base_frame
|
||||
bitname_bit = 32 * (
|
||||
bit_wordidx - tiledata["offset"]) + bit_bitidx
|
||||
bit_wordidx - bitj["offset"]) + bit_bitidx
|
||||
# some bits are hard to de-correlate
|
||||
# allow force dropping some bits from search space for practicality
|
||||
if bitfilter is None or bitfilter(bitname_frame,
|
||||
bitname_bit):
|
||||
bitname = "%02d_%02d" % (bitname_frame, bitname_bit)
|
||||
segments[segname]["bits"].add(bitname)
|
||||
segment["bits"].add(bitname)
|
||||
return segment
|
||||
|
||||
'''
|
||||
XXX: wouldn't it be better to iterate over tags? Easy to drop tags
|
||||
|
|
@ -158,20 +178,27 @@ class segmaker:
|
|||
'''
|
||||
for tilename, tiledata in self.grid.items():
|
||||
|
||||
def add_tilename_tags():
|
||||
def getseg(segname):
|
||||
if not segname in segments:
|
||||
add_segbits(
|
||||
return add_segbits(
|
||||
segments, segname, tiledata, bitfilter=bitfilter)
|
||||
else:
|
||||
segment = segments[segname]
|
||||
assert segment["offset"] == bitj["offset"]
|
||||
assert segment["words"] == bitj["words"]
|
||||
assert segment["frames"] == bitj["frames"]
|
||||
return segment
|
||||
|
||||
for name, value in self.tags[tilename].items():
|
||||
def add_tilename_tags():
|
||||
segment = getseg(segname)
|
||||
|
||||
for name, value in self.tile_tags[tilename].items():
|
||||
tags_used.add((tilename, name))
|
||||
tag = "%s.%s" % (tile_type_norm, name)
|
||||
segments[segname]["tags"][tag] = value
|
||||
segment["tags"][tag] = value
|
||||
|
||||
def add_site_tags():
|
||||
if not segname in segments:
|
||||
add_segbits(
|
||||
segments, segname, tiledata, bitfilter=bitfilter)
|
||||
segment = getseg(segname)
|
||||
|
||||
if 'SLICE_' in site:
|
||||
'''
|
||||
|
|
@ -188,7 +215,7 @@ class segmaker:
|
|||
else:
|
||||
assert 0, 'Unhandled site type'
|
||||
|
||||
for name, value in self.tags[site].items():
|
||||
for name, value in self.site_tags[site].items():
|
||||
tags_used.add((site, name))
|
||||
tag = "%s.%s.%s" % (tile_type_norm, sitekey, name)
|
||||
# XXX: does this come from name?
|
||||
|
|
@ -197,7 +224,7 @@ class segmaker:
|
|||
segments[segname]["tags"][tag] = value
|
||||
|
||||
# ignore dummy tiles (ex: VBRK)
|
||||
if "baseaddr" not in tiledata:
|
||||
if "bits" not in tiledata:
|
||||
continue
|
||||
|
||||
tile_type = tiledata["type"]
|
||||
|
|
@ -210,23 +237,25 @@ class segmaker:
|
|||
'''
|
||||
tile_type_norm = re.sub("(LL|LM)?_[LR]$", "", tile_type)
|
||||
|
||||
segname = "%s_%03d" % (
|
||||
# truncate 0x to leave hex string
|
||||
tiledata["baseaddr"][2:],
|
||||
tiledata["offset"])
|
||||
for block_type, bitj in tiledata['bits'].items():
|
||||
# NOTE: multiple tiles may have the same base addr + offset
|
||||
segname = "%s_%03d" % (
|
||||
# truncate 0x to leave hex string
|
||||
bitj["baseaddr"][2:],
|
||||
bitj["offset"])
|
||||
|
||||
# process tile name tags
|
||||
if tilename in self.tags:
|
||||
add_tilename_tags()
|
||||
# process tile name tags
|
||||
if tilename in self.tile_tags:
|
||||
add_tilename_tags()
|
||||
|
||||
# process site name tags
|
||||
for site in tiledata["sites"]:
|
||||
if site not in self.tags:
|
||||
continue
|
||||
add_site_tags()
|
||||
# process site name tags
|
||||
for site in tiledata["sites"]:
|
||||
if site not in self.site_tags:
|
||||
continue
|
||||
add_site_tags()
|
||||
|
||||
if self.verbose:
|
||||
ntags = recurse_sum(self.tags)
|
||||
ntags = recurse_sum(self.site_tags) + recurse_sum(self.tile_tags)
|
||||
print("Used %u / %u tags" % (len(tags_used), ntags))
|
||||
print("Grid DB had %u tile types" % len(tile_types_found))
|
||||
assert ntags and ntags == len(tags_used)
|
||||
|
|
@ -234,6 +263,10 @@ class segmaker:
|
|||
def write(self, suffix=None, roi=False):
|
||||
assert self.segments_by_type, 'No data to write'
|
||||
|
||||
assert sum(
|
||||
[len(segments) for segments in self.segments_by_type.values()
|
||||
]) != 0
|
||||
|
||||
for segtype in self.segments_by_type.keys():
|
||||
if suffix is not None:
|
||||
filename = "segdata_%s_%s.txt" % (segtype.lower(), suffix)
|
||||
|
|
@ -244,19 +277,9 @@ class segmaker:
|
|||
|
||||
with open(filename, "w") as f:
|
||||
segments = self.segments_by_type[segtype]
|
||||
if True:
|
||||
for segname, segdata in sorted(segments.items()):
|
||||
print("seg %s" % segname, file=f)
|
||||
for bitname in sorted(segdata["bits"]):
|
||||
print("bit %s" % bitname, file=f)
|
||||
for tagname, tagval in sorted(segdata["tags"].items()):
|
||||
print("tag %s %d" % (tagname, tagval), file=f)
|
||||
else:
|
||||
print("seg roi", file=f)
|
||||
for segname, segdata in sorted(segments.items()):
|
||||
for bitname in sorted(segdata["bits"]):
|
||||
print("bit %s_%s" % (segname, bitname), file=f)
|
||||
for tagname, tagval in sorted(segdata["tags"].items()):
|
||||
print(
|
||||
"tag %s_%s %d" % (segname, tagname, tagval),
|
||||
file=f)
|
||||
for segname, segdata in sorted(segments.items()):
|
||||
print("seg %s" % segname, file=f)
|
||||
for bitname in sorted(segdata["bits"]):
|
||||
print("bit %s" % bitname, file=f)
|
||||
for tagname, tagval in sorted(segdata["tags"].items()):
|
||||
print("tag %s %d" % (tagname, tagval), file=f)
|
||||
|
|
|
|||
|
|
@ -1,182 +1,66 @@
|
|||
#!/usr/bin/env python3
|
||||
'''
|
||||
Take raw .bits files and decode them to higher level functionality
|
||||
This output is intended for debugging and not directly related to FASM
|
||||
'''
|
||||
|
||||
import getopt, sys, os, json, re
|
||||
|
||||
flag_z = False
|
||||
flag_b = False
|
||||
flag_d = False
|
||||
flag_D = False
|
||||
import sys, os, json, re
|
||||
|
||||
|
||||
def usage():
|
||||
print("Usage: %s [options] <bits_file> [segments/tiles]" % sys.argv[0])
|
||||
print("")
|
||||
print(" -z")
|
||||
print(" do not print a 'seg' header for empty segments")
|
||||
print("")
|
||||
print(" -b")
|
||||
print(" print bits outside of known segments")
|
||||
print("")
|
||||
print(" -d")
|
||||
print(" decode known segment bits and write them as tags")
|
||||
print("")
|
||||
print(" -D")
|
||||
print(" decode known segment bits and omit them in the output")
|
||||
print("")
|
||||
sys.exit(0)
|
||||
class NoDB(Exception):
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "zbdD")
|
||||
except:
|
||||
usage()
|
||||
|
||||
if len(args) == 0:
|
||||
usage()
|
||||
|
||||
for o, a in opts:
|
||||
if o == "-z":
|
||||
flag_z = True
|
||||
elif o == "-b":
|
||||
flag_b = True
|
||||
elif o == "-d":
|
||||
flag_d = True
|
||||
elif o == "-D":
|
||||
flag_D = True
|
||||
else:
|
||||
usage()
|
||||
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
new_grid = json.load(f)
|
||||
|
||||
# TODO: Migrate to new tilegrid format via library.
|
||||
grid = {'tiles': new_grid, 'segments': {}}
|
||||
|
||||
for tile in grid['tiles'].values():
|
||||
if 'segment' in tile:
|
||||
segment = tile['segment']
|
||||
grid['segments'][segment] = {
|
||||
'baseaddr': [
|
||||
tile['baseaddr'],
|
||||
tile['offset'],
|
||||
],
|
||||
'type': tile['segment_type'],
|
||||
'frames': tile['frames'],
|
||||
'words': tile['words'],
|
||||
}
|
||||
|
||||
bitdata = dict()
|
||||
|
||||
# print("Loading %s." % sys.argv[1])
|
||||
with open(args[0], "r") as f:
|
||||
for line in f:
|
||||
line = line.split("_")
|
||||
frame = int(line[1], 16)
|
||||
wordidx = int(line[2], 10)
|
||||
bitidx = int(line[3], 10)
|
||||
|
||||
if frame not in bitdata:
|
||||
bitdata[frame] = dict()
|
||||
|
||||
if wordidx not in bitdata[frame]:
|
||||
bitdata[frame][wordidx] = set()
|
||||
|
||||
bitdata[frame][wordidx].add(bitidx)
|
||||
|
||||
# cache
|
||||
segbitsdb = dict()
|
||||
|
||||
|
||||
def get_database(segtype):
|
||||
if segtype in segbitsdb:
|
||||
return segbitsdb[segtype]
|
||||
# int and sites are loaded together so that bit coverage can be checked together
|
||||
# however, as currently written, each segment is essentially printed twice
|
||||
def process_db(tile_type, process):
|
||||
fns = [
|
||||
# sites
|
||||
"%s/%s/segbits_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type.lower()),
|
||||
# interconnect
|
||||
"%s/%s/segbits_int_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type[-1].lower()),
|
||||
]
|
||||
|
||||
segbitsdb[segtype] = list()
|
||||
|
||||
with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE"), segtype),
|
||||
"r") as f:
|
||||
for line in f:
|
||||
line = line.split()
|
||||
segbitsdb[segtype].append(line)
|
||||
|
||||
with open("%s/%s/segbits_int_%s.db" %
|
||||
(os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
segtype[-1]), "r") as f:
|
||||
for line in f:
|
||||
line = line.split()
|
||||
segbitsdb[segtype].append(line)
|
||||
|
||||
return segbitsdb[segtype]
|
||||
for fn in fns:
|
||||
if os.path.exists(fn):
|
||||
with open(fn, "r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
|
||||
def handle_segment(segname):
|
||||
if segname is None:
|
||||
segframes = dict()
|
||||
for segname, segdata in grid["segments"].items():
|
||||
framebase = int(segdata["baseaddr"][0], 16)
|
||||
for i in range(segdata["frames"]):
|
||||
if (framebase + i) not in segframes:
|
||||
segframes[framebase + i] = set()
|
||||
for j in range(segdata["baseaddr"][1],
|
||||
segdata["baseaddr"][1] + segdata["words"]):
|
||||
segframes[framebase + i].add(j)
|
||||
for frame in sorted(bitdata.keys()):
|
||||
for wordidx in sorted(bitdata[frame].keys()):
|
||||
if frame in segframes and wordidx in segframes[frame]:
|
||||
continue
|
||||
for bitidx in sorted(bitdata[frame][wordidx]):
|
||||
print("bit_%08x_%03d_%02d" % (frame, wordidx, bitidx))
|
||||
return
|
||||
if ":" in segname:
|
||||
seg1, seg2 = segname.split(":")
|
||||
def get_database(tile_type):
|
||||
tags = list()
|
||||
|
||||
if seg1 in grid["tiles"]:
|
||||
seg1 = grid["tiles"][seg1]["segment"]
|
||||
if tile_type in segbitsdb:
|
||||
return segbitsdb[tile_type]
|
||||
|
||||
if seg2 in grid["tiles"]:
|
||||
seg2 = grid["tiles"][seg2]["segment"]
|
||||
def process(l):
|
||||
tags.append(l.split())
|
||||
|
||||
seginfo1 = grid["segments"][seg1]
|
||||
seginfo2 = grid["segments"][seg2]
|
||||
process_db(tile_type, process)
|
||||
|
||||
frame1 = int(seginfo1["baseaddr"][0], 16)
|
||||
word1 = int(seginfo1["baseaddr"][1])
|
||||
if len(tags) == 0:
|
||||
raise NoDB(tile_type)
|
||||
|
||||
frame2 = int(seginfo2["baseaddr"][0], 16)
|
||||
word2 = int(seginfo2["baseaddr"][1])
|
||||
segbitsdb[tile_type] = tags
|
||||
return tags
|
||||
|
||||
if frame1 > frame2:
|
||||
frame1, frame2 = frame2, frame1
|
||||
|
||||
if word1 > word2:
|
||||
word1, word2 = word2, word1
|
||||
|
||||
segs = list()
|
||||
|
||||
for seg, seginfo in sorted(grid["segments"].items()):
|
||||
frame = int(seginfo["baseaddr"][0], 16)
|
||||
word = int(seginfo["baseaddr"][1])
|
||||
if frame1 <= frame <= frame2 and word1 <= word <= word2:
|
||||
segs.append((frame, word, seg))
|
||||
|
||||
for _, _, seg in sorted(segs):
|
||||
handle_segment(seg)
|
||||
return
|
||||
|
||||
if segname in grid["tiles"]:
|
||||
segname = grid["tiles"][segname]["segment"]
|
||||
|
||||
seginfo = grid["segments"][segname]
|
||||
|
||||
def mk_segbits(seginfo, bitdata):
|
||||
baseframe = int(seginfo["baseaddr"][0], 16)
|
||||
basewordidx = int(seginfo["baseaddr"][1])
|
||||
numframes = int(seginfo["frames"])
|
||||
numwords = int(seginfo["words"])
|
||||
|
||||
segbits = set()
|
||||
segtags = set()
|
||||
|
||||
for frame in range(baseframe, baseframe + numframes):
|
||||
if frame not in bitdata:
|
||||
continue
|
||||
|
|
@ -187,25 +71,105 @@ def handle_segment(segname):
|
|||
segbits.add(
|
||||
"%02d_%02d" %
|
||||
(frame - baseframe, 32 * (wordidx - basewordidx) + bitidx))
|
||||
return segbits
|
||||
|
||||
if flag_d or flag_D:
|
||||
|
||||
def print_unknown_bits(grid, bitdata):
|
||||
'''
|
||||
Print bits not covered by known tiles
|
||||
'''
|
||||
|
||||
# Index all known locations
|
||||
# seggrames[address] = set()
|
||||
# where set contains word numbers
|
||||
segframes = dict()
|
||||
for segname, segdata in grid["segments"].items():
|
||||
framebase = int(segdata["baseaddr"][0], 16)
|
||||
for i in range(segdata["frames"]):
|
||||
words = segframes.setdefault(framebase + i, set())
|
||||
for j in range(segdata["baseaddr"][1],
|
||||
segdata["baseaddr"][1] + segdata["words"]):
|
||||
words.add(j)
|
||||
|
||||
# print uncovered locations
|
||||
print('Non-database bits:')
|
||||
for frame in sorted(bitdata.keys()):
|
||||
for wordidx in sorted(bitdata[frame].keys()):
|
||||
if frame in segframes and wordidx in segframes[frame]:
|
||||
continue
|
||||
for bitidx in sorted(bitdata[frame][wordidx]):
|
||||
print("bit_%08x_%03d_%02d" % (frame, wordidx, bitidx))
|
||||
|
||||
|
||||
def tagmatch(entry, segbits):
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!" and bit not in segbits:
|
||||
return False
|
||||
if bit[0] == "!" and bit[1:] in segbits:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def tag_matched(entry, segbits):
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!":
|
||||
segbits.remove(bit)
|
||||
|
||||
|
||||
decode_warnings = set()
|
||||
|
||||
|
||||
def seg_decode(flag_decode_emit, seginfo, segbits, verbose=False):
|
||||
segtags = set()
|
||||
|
||||
# already failed?
|
||||
if seginfo["type"] in decode_warnings:
|
||||
return segtags
|
||||
|
||||
try:
|
||||
for entry in get_database(seginfo["type"]):
|
||||
match_entry = True
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!" and bit not in segbits:
|
||||
match_entry = False
|
||||
if bit[0] == "!" and bit[1:] in segbits:
|
||||
match_entry = False
|
||||
if match_entry:
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!":
|
||||
segbits.remove(bit)
|
||||
if flag_d:
|
||||
segtags.add(entry[0])
|
||||
if not tagmatch(entry, segbits):
|
||||
continue
|
||||
tag_matched(entry, segbits)
|
||||
if flag_decode_emit:
|
||||
segtags.add(entry[0])
|
||||
except NoDB:
|
||||
verbose and print(
|
||||
"WARNING: failed to load DB for %s" % seginfo["type"])
|
||||
decode_warnings.add(seginfo["type"])
|
||||
return segtags
|
||||
|
||||
if not flag_z or len(segbits) > 0 or len(segtags) > 0:
|
||||
print()
|
||||
print("seg %s" % segname)
|
||||
|
||||
def handle_segment(
|
||||
segname,
|
||||
grid,
|
||||
bitdata,
|
||||
flag_decode_emit,
|
||||
flag_decode_omit,
|
||||
omit_empty_segs,
|
||||
verbose=False):
|
||||
|
||||
assert segname
|
||||
|
||||
# only print bitstream tiles
|
||||
if segname not in grid["segments"]:
|
||||
return
|
||||
seginfo = grid["segments"][segname]
|
||||
|
||||
segbits = mk_segbits(seginfo, bitdata)
|
||||
|
||||
if flag_decode_emit or flag_decode_omit:
|
||||
segtags = seg_decode(
|
||||
flag_decode_emit, seginfo, segbits, verbose=verbose)
|
||||
else:
|
||||
segtags = set()
|
||||
|
||||
# Found something to print?
|
||||
if not (not omit_empty_segs or len(segbits) > 0 or len(segtags) > 0):
|
||||
return
|
||||
|
||||
print()
|
||||
print("seg %s" % (segname, ))
|
||||
|
||||
for bit in sorted(segbits):
|
||||
print("bit %s" % bit)
|
||||
|
|
@ -214,15 +178,140 @@ def handle_segment(segname):
|
|||
print("tag %s" % tag)
|
||||
|
||||
|
||||
if flag_b:
|
||||
handle_segment(None)
|
||||
def load_bitdata(bits_file):
|
||||
bitdata = dict()
|
||||
|
||||
if len(args) == 1:
|
||||
seglist = list()
|
||||
for seg, seginfo in grid["segments"].items():
|
||||
seglist.append((seginfo["baseaddr"][0], -seginfo["baseaddr"][1], seg))
|
||||
for _, _, seg in sorted(seglist):
|
||||
handle_segment(seg)
|
||||
else:
|
||||
for arg in args[1:]:
|
||||
handle_segment(arg)
|
||||
with open(bits_file, "r") as f:
|
||||
for line in f:
|
||||
line = line.split("_")
|
||||
frame = int(line[1], 16)
|
||||
wordidx = int(line[2], 10)
|
||||
bitidx = int(line[3], 10)
|
||||
|
||||
if frame not in bitdata:
|
||||
bitdata[frame] = dict()
|
||||
|
||||
if wordidx not in bitdata[frame]:
|
||||
bitdata[frame][wordidx] = set()
|
||||
|
||||
bitdata[frame][wordidx].add(bitidx)
|
||||
return bitdata
|
||||
|
||||
|
||||
def mk_grid():
|
||||
'''Load tilegrid, flattening all blocks into one dictionary'''
|
||||
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
new_grid = json.load(f)
|
||||
|
||||
# TODO: Migrate to new tilegrid format via library.
|
||||
grid = {'tiles': new_grid, 'segments': {}}
|
||||
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
bits = tile.get('bits', None)
|
||||
if not bits:
|
||||
continue
|
||||
for block_name, block in bits.items():
|
||||
segname = mksegment(tile_name, block_name)
|
||||
grid['segments'][segname] = {
|
||||
'baseaddr': [
|
||||
block['baseaddr'],
|
||||
block['offset'],
|
||||
],
|
||||
'type': tile['type'],
|
||||
'frames': block['frames'],
|
||||
'words': block['words'],
|
||||
'tile_name': tile_name,
|
||||
'block_name': block_name,
|
||||
}
|
||||
return grid
|
||||
|
||||
|
||||
def mksegment(tile_name, block_name):
|
||||
'''Create a segment name'''
|
||||
return '%s:%s' % (tile_name, block_name)
|
||||
|
||||
|
||||
def tile_segnames(grid):
|
||||
ret = []
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
for block_name in tile['bits'].keys():
|
||||
ret.append(mksegment(tile_name, block_name))
|
||||
return ret
|
||||
|
||||
|
||||
def run(
|
||||
bits_file,
|
||||
segnames,
|
||||
omit_empty_segs=False,
|
||||
flag_unknown_bits=False,
|
||||
flag_decode_emit=False,
|
||||
flag_decode_omit=False,
|
||||
verbose=False):
|
||||
grid = mk_grid()
|
||||
|
||||
bitdata = load_bitdata(bits_file)
|
||||
|
||||
if flag_unknown_bits:
|
||||
print_unknown_bits(grid, bitdata)
|
||||
|
||||
# Default: print all
|
||||
if segnames:
|
||||
for i, segname in enumerate(segnames):
|
||||
# Default to common tile config area if tile given without explicit block
|
||||
if ':' not in segname:
|
||||
segnames[i] = mksegment(segname, 'CLB_IO_CLK')
|
||||
else:
|
||||
segnames = sorted(tile_segnames(grid))
|
||||
print('Segments: %u' % len(segnames))
|
||||
|
||||
# XXX: previously this was sorted by address, not name
|
||||
# revisit?
|
||||
for segname in segnames:
|
||||
handle_segment(
|
||||
segname,
|
||||
grid,
|
||||
bitdata,
|
||||
flag_decode_emit,
|
||||
flag_decode_omit,
|
||||
omit_empty_segs,
|
||||
verbose=verbose)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
# XXX: tool still works, but not well
|
||||
# need to eliminate segments entirely
|
||||
parser = argparse.ArgumentParser(
|
||||
description='XXX: does not print all data?')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument(
|
||||
'-z',
|
||||
action='store_true',
|
||||
help="do not print a 'seg' header for empty segments")
|
||||
parser.add_argument(
|
||||
'-b', action='store_true', help='print bits outside of known segments')
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
action='store_true',
|
||||
help='decode known segment bits and write them as tags')
|
||||
# XXX: possibly broken, or we have missing DB data
|
||||
parser.add_argument(
|
||||
'-D',
|
||||
action='store_true',
|
||||
help='decode known segment bits and omit them in the output')
|
||||
parser.add_argument('bits_file', help='')
|
||||
parser.add_argument(
|
||||
'segnames', nargs='*', help='List of tile or tile:block to print')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(
|
||||
args.bits_file, args.segnames, args.z, args.b, args.d, args.D,
|
||||
args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
# TODO: need better coverage for different tile types
|
||||
|
||||
import fasm2frame
|
||||
import fasm2frames
|
||||
|
||||
import unittest
|
||||
import StringIO
|
||||
from io import StringIO
|
||||
import re
|
||||
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ def frm2bits(txt):
|
|||
assert (101 == len(words))
|
||||
for wordi, word in enumerate(words):
|
||||
word = int(word, 0)
|
||||
for biti in xrange(32):
|
||||
for biti in range(32):
|
||||
val = word & (1 << biti)
|
||||
if val:
|
||||
bits_out.add((addr, wordi, biti))
|
||||
|
|
@ -51,12 +52,12 @@ def bitread2bits(txt):
|
|||
class TestStringMethods(unittest.TestCase):
|
||||
def test_lut(self):
|
||||
'''Simple smoke test on just the LUTs'''
|
||||
fout = StringIO.StringIO()
|
||||
fasm2frame.run(open('test_data/lut.fasm', 'r'), fout)
|
||||
fout = StringIO()
|
||||
fasm2frames.run(open('test_data/lut.fasm', 'r'), fout)
|
||||
|
||||
def bitread_frm_equals(self, frm_fn, bitread_fn):
|
||||
fout = StringIO.StringIO()
|
||||
fasm2frame.run(open(frm_fn, 'r'), fout)
|
||||
fout = StringIO()
|
||||
fasm2frames.run(open(frm_fn, 'r'), fout)
|
||||
|
||||
# Build a list of output used bits
|
||||
bits_out = frm2bits(fout.getvalue())
|
||||
|
|
@ -84,24 +85,24 @@ class TestStringMethods(unittest.TestCase):
|
|||
# Same check as above, but isolated test case
|
||||
def test_opkey_01_default(self):
|
||||
'''Optional key with binary omitted value should produce valid result'''
|
||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX")
|
||||
fout = StringIO.StringIO()
|
||||
fasm2frame.run(fin, fout)
|
||||
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX")
|
||||
fout = StringIO()
|
||||
fasm2frames.run(fin, fout)
|
||||
|
||||
def test_opkey_01_1(self):
|
||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1")
|
||||
fout = StringIO.StringIO()
|
||||
fasm2frame.run(fin, fout)
|
||||
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1")
|
||||
fout = StringIO()
|
||||
fasm2frames.run(fin, fout)
|
||||
|
||||
def test_opkey_enum(self):
|
||||
'''Optional key with enumerated value should produce syntax error'''
|
||||
# CLBLM_L.SLICEM_X0.AMUX.O6 !30_06 !30_07 !30_08 30_11
|
||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.AMUX.O6")
|
||||
fout = StringIO.StringIO()
|
||||
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.AMUX.O6")
|
||||
fout = StringIO()
|
||||
try:
|
||||
fasm2frame.run(fin, fout)
|
||||
fasm2frames.run(fin, fout)
|
||||
self.fail("Expected syntax error")
|
||||
except fasm2frame.FASMSyntaxError:
|
||||
except fasm2frames.FASMSyntaxError:
|
||||
pass
|
||||
|
||||
def test_ff_int_0s(self):
|
||||
|
|
@ -111,39 +112,39 @@ class TestStringMethods(unittest.TestCase):
|
|||
|
||||
def test_badkey(self):
|
||||
'''Bad key should throw syntax error'''
|
||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 2")
|
||||
fout = StringIO.StringIO()
|
||||
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 2")
|
||||
fout = StringIO()
|
||||
try:
|
||||
fasm2frame.run(fin, fout)
|
||||
fasm2frames.run(fin, fout)
|
||||
self.fail("Expected syntax error")
|
||||
except fasm2frame.FASMSyntaxError:
|
||||
except fasm2frames.FASMSyntaxError:
|
||||
pass
|
||||
|
||||
def test_dupkey(self):
|
||||
'''Duplicate key should throw syntax error'''
|
||||
fin = StringIO.StringIO(
|
||||
fin = StringIO(
|
||||
"""\
|
||||
CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 0
|
||||
CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1
|
||||
""")
|
||||
fout = StringIO.StringIO()
|
||||
fout = StringIO()
|
||||
try:
|
||||
fasm2frame.run(fin, fout)
|
||||
fasm2frames.run(fin, fout)
|
||||
self.fail("Expected syntax error")
|
||||
except fasm2frame.FASMSyntaxError:
|
||||
except fasm2frames.FASMSyntaxError:
|
||||
pass
|
||||
|
||||
def test_sparse(self):
|
||||
'''Verify sparse equivilent to normal encoding'''
|
||||
frm_fn = 'test_data/lut_int.fasm'
|
||||
|
||||
fout_sparse = StringIO.StringIO()
|
||||
fasm2frame.run(open(frm_fn, 'r'), fout_sparse, sparse=True)
|
||||
fout_sparse = StringIO()
|
||||
fasm2frames.run(open(frm_fn, 'r'), fout_sparse, sparse=True)
|
||||
fout_sparse_txt = fout_sparse.getvalue()
|
||||
bits_sparse = frm2bits(fout_sparse_txt)
|
||||
|
||||
fout_full = StringIO.StringIO()
|
||||
fasm2frame.run(open(frm_fn, 'r'), fout_full, sparse=False)
|
||||
fout_full = StringIO()
|
||||
fasm2frames.run(open(frm_fn, 'r'), fout_full, sparse=False)
|
||||
fout_full_txt = fout_full.getvalue()
|
||||
bits_full = frm2bits(fout_full_txt)
|
||||
|
||||
Loading…
Reference in New Issue