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
|
#!/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
|
import os, sys, json, re
|
||||||
|
|
||||||
#######################################
|
# matches lib/include/prjxray/xilinx/xc7series/block_type.h
|
||||||
# Read
|
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:
|
def addr2btype(base_addr):
|
||||||
for line in f:
|
'''
|
||||||
tiles.append(line.split())
|
Convert integer address to block type
|
||||||
|
|
||||||
for arg in sys.argv[1:]:
|
Table 5-24: Frame Address Register Description
|
||||||
with open(arg) as f:
|
Bit Index: [25:23]
|
||||||
line = f.read().strip()
|
https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
|
||||||
site = arg[7:-6]
|
"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 ."
|
||||||
frame = int(line[5:5 + 8], 16)
|
'''
|
||||||
site_baseaddr[site] = "0x%08x" % (frame & ~0x7f)
|
block_type_i = (base_addr >> 23) & 0x7
|
||||||
|
return block_type_i2s[block_type_i]
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Create initial database
|
|
||||||
|
|
||||||
database = dict()
|
def nolr(tile_type):
|
||||||
database["tiles"] = dict()
|
'''
|
||||||
database["segments"] = dict()
|
Remove _L or _R suffix tile_type suffix, if present
|
||||||
tiles_by_grid = dict()
|
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] = {
|
def load_tiles(tiles_fn):
|
||||||
"type": tile_type,
|
'''
|
||||||
"sites": dict(),
|
"$type $tile $grid_x $grid_y $typed_sites"
|
||||||
"grid_x": grid_x,
|
typed_sites: foreach t $site_types s $sites
|
||||||
"grid_y": grid_y
|
'''
|
||||||
}
|
tiles = list()
|
||||||
|
|
||||||
if len(record) > 4:
|
with open(tiles_fn) as f:
|
||||||
for i in range(4, len(record), 2):
|
for line in f:
|
||||||
site_type, site_name = record[i:i + 2]
|
# CLBLM_L CLBLM_L_X10Y98 30 106 SLICEL SLICE_X13Y98 SLICEM SLICE_X12Y98
|
||||||
if site_name in site_baseaddr:
|
record = line.split()
|
||||||
framebaseaddr = site_baseaddr[site_name]
|
tile_type, tile_name, grid_x, grid_y = record[0:4]
|
||||||
database["tiles"][tile_name]["sites"][site_name] = site_type
|
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:
|
return tiles
|
||||||
tile_baseaddr[tile_name] = [framebaseaddr, 0]
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Add Segments
|
|
||||||
|
|
||||||
for tile_name, tile_data in database["tiles"].items():
|
def load_baseaddrs(deltas_fns):
|
||||||
tile_type = tile_data["type"]
|
site_baseaddr = dict()
|
||||||
grid_x = tile_data["grid_x"]
|
for arg in deltas_fns:
|
||||||
grid_y = tile_data["grid_y"]
|
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"]:
|
return site_baseaddr
|
||||||
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)]
|
|
||||||
|
|
||||||
segment_name = "SEG_" + tile_name
|
|
||||||
segtype = tile_type.lower()
|
|
||||||
|
|
||||||
database["segments"][segment_name] = dict()
|
def make_database(tiles):
|
||||||
database["segments"][segment_name]["tiles"] = [
|
# tile database with X, Y, and list of sites
|
||||||
tile_name, int_tile_name
|
# tile name as keys
|
||||||
]
|
database = dict()
|
||||||
database["segments"][segment_name]["type"] = segtype
|
|
||||||
database["segments"][segment_name]["frames"] = 36
|
|
||||||
database["segments"][segment_name]["words"] = 2
|
|
||||||
|
|
||||||
if tile_name in tile_baseaddr:
|
for tile in tiles:
|
||||||
database["segments"][segment_name]["baseaddr"] = tile_baseaddr[
|
database[tile["name"]] = {
|
||||||
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
|
return database
|
||||||
database["tiles"][int_tile_name]["segment"] = segment_name
|
|
||||||
|
|
||||||
if tile_type in ["HCLK_L", "HCLK_R"]:
|
|
||||||
segment_name = "SEG_" + tile_name
|
|
||||||
segtype = tile_type.lower()
|
|
||||||
|
|
||||||
database["segments"][segment_name] = dict()
|
def make_tile_baseaddrs(tiles, site_baseaddr, verbose=False):
|
||||||
database["segments"][segment_name]["tiles"] = [tile_name]
|
# Look up a base address by tile name
|
||||||
database["segments"][segment_name]["type"] = segtype
|
tile_baseaddrs = dict()
|
||||||
database["segments"][segment_name]["frames"] = 26
|
|
||||||
database["segments"][segment_name]["words"] = 1
|
|
||||||
database["tiles"][tile_name]["segment"] = segment_name
|
|
||||||
|
|
||||||
if tile_type in ["BRAM_L", "DSP_L", "BRAM_R", "DSP_R"]:
|
verbose and print('')
|
||||||
for k in range(5):
|
verbose and print('%u tiles' % len(tiles))
|
||||||
if tile_type in ["BRAM_L", "DSP_L"]:
|
added = 0
|
||||||
interface_tile_name = tiles_by_grid[(grid_x + 1, grid_y - k)]
|
for tile in tiles:
|
||||||
int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)]
|
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:
|
else:
|
||||||
interface_tile_name = tiles_by_grid[(grid_x - 1, grid_y - k)]
|
tile_baseaddr[bt] = [framebaseaddr, 0]
|
||||||
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
|
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)
|
assert added
|
||||||
segtype = tile_type.lower().replace("_", "%d_" % k, 1)
|
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:
|
def make_tiles_by_grid(tiles):
|
||||||
database["segments"][segment_name]["tiles"] = [
|
# lookup tile names by (X, Y)
|
||||||
tile_name, interface_tile_name, int_tile_name
|
tiles_by_grid = dict()
|
||||||
]
|
|
||||||
database["tiles"][tile_name]["segment"] = segment_name
|
for tile in tiles:
|
||||||
database["tiles"][interface_tile_name][
|
tiles_by_grid[(tile["grid_x"], tile["grid_y"])] = tile["name"]
|
||||||
"segment"] = segment_name
|
|
||||||
database["tiles"][int_tile_name]["segment"] = segment_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:
|
else:
|
||||||
database["segments"][segment_name]["tiles"] = [
|
int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)]
|
||||||
interface_tile_name, int_tile_name
|
|
||||||
]
|
|
||||||
database["tiles"][interface_tile_name][
|
|
||||||
"segment"] = segment_name
|
|
||||||
database["tiles"][int_tile_name]["segment"] = segment_name
|
|
||||||
|
|
||||||
#######################################
|
add_segment(
|
||||||
# Populate segment base addresses: L/R along INT column
|
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():
|
def process_hclk():
|
||||||
if "baseaddr" in database["segments"][segment_name]:
|
add_segment(
|
||||||
framebase, wordbase = database["segments"][segment_name]["baseaddr"]
|
name="SEG_" + tile_name,
|
||||||
inttile = [
|
tiles=[tile_name],
|
||||||
tile for tile in database["segments"][segment_name]["tiles"]
|
segtype=tile_type.lower())
|
||||||
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"]
|
|
||||||
|
|
||||||
if database["tiles"][inttile]["type"] == "INT_L":
|
def process_bram_dsp():
|
||||||
grid_x += 1
|
for k in range(5):
|
||||||
framebase = "0x%08x" % (int(framebase, 16) + 0x80)
|
if tile_type in ["BRAM_L", "DSP_L"]:
|
||||||
else:
|
interface_tile_name = tiles_by_grid[(
|
||||||
grid_x -= 1
|
grid_x + 1, grid_y - k)]
|
||||||
framebase = "0x%08x" % (int(framebase, 16) - 0x80)
|
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
|
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":
|
inttile = get_inttile(database, segment)
|
||||||
assert database["tiles"][tile]["type"] == "INT_R"
|
grid_x = database[inttile]["grid_x"]
|
||||||
elif database["tiles"][inttile]["type"] == "INT_R":
|
grid_y = database[inttile]["grid_y"]
|
||||||
assert database["tiles"][tile]["type"] == "INT_L"
|
|
||||||
else:
|
|
||||||
assert 0
|
|
||||||
|
|
||||||
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]:
|
tile = tiles_by_grid[(grid_x, grid_y)]
|
||||||
assert database["segments"][seg]["baseaddr"] == [
|
|
||||||
framebase, wordbase
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
database["segments"][seg]["baseaddr"] = [framebase, wordbase]
|
|
||||||
|
|
||||||
#######################################
|
if database[inttile]["type"] == "INT_L":
|
||||||
# Populate segment base addresses: Up along INT/HCLK columns
|
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():
|
seg = database[tile]["segment"]
|
||||||
if "baseaddr" in database["segments"][segment_name]:
|
|
||||||
start_segments.append(segment_name)
|
|
||||||
|
|
||||||
for segment_name in start_segments:
|
seg_baseaddrs = segments[seg].setdefault("baseaddr", {})
|
||||||
framebase, wordbase = database["segments"][segment_name]["baseaddr"]
|
# At least one duplicate when we re-compute the entry for the base address
|
||||||
inttile = [
|
# should give the same address
|
||||||
tile for tile in database["segments"][segment_name]["tiles"]
|
if block_type in seg_baseaddrs:
|
||||||
if database["tiles"][tile]["type"] in ["INT_L", "INT_R"]
|
assert seg_baseaddrs[block_type] == [
|
||||||
][0]
|
framebase, wordbase
|
||||||
grid_x = database["tiles"][inttile]["grid_x"]
|
], (seg_baseaddrs[block_type], [framebase, wordbase])
|
||||||
grid_y = database["tiles"][inttile]["grid_y"]
|
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:
|
def seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=False):
|
||||||
wordbase += 1
|
'''Populate segment base addresses: Up along INT/HCLK columns'''
|
||||||
else:
|
|
||||||
wordbase += 2
|
|
||||||
|
|
||||||
segname = database["tiles"][tiles_by_grid[(grid_x, grid_y)]]["segment"]
|
verbose and print('')
|
||||||
database["segments"][segname]["baseaddr"] = [framebase, wordbase]
|
# 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)
|
||||||
|
|
||||||
#######################################
|
verbose and print('up_INT: %u base addresses' % len(src_segment_names))
|
||||||
# Transfer segment data into tiles
|
|
||||||
|
|
||||||
for segment_name in database["segments"].keys():
|
for src_segment_name in sorted(src_segment_names):
|
||||||
try:
|
src_segment = segments[src_segment_name]
|
||||||
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
|
|
||||||
|
|
||||||
# TODO: Migrate to new tilegrid format via library. This data is added for
|
for block_type, (framebase,
|
||||||
# compability with unconverted tools. Update tools then remove this data from
|
wordbase) in sorted(src_segment["baseaddr"].items()):
|
||||||
# tilegrid.json.
|
verbose and print(
|
||||||
for tiledata in database['tiles'].values():
|
'up_INT: %s: %s.0x%08X:%u' %
|
||||||
if "segment" in tiledata:
|
(src_segment_name, block_type, framebase, wordbase))
|
||||||
segment = tiledata['segment']
|
|
||||||
tiledata['frames'] = database['segments'][segment]['frames']
|
|
||||||
tiledata['words'] = database['segments'][segment]['words']
|
|
||||||
tiledata['segment_type'] = database['segments'][segment]['type']
|
|
||||||
|
|
||||||
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
|
||||||
|
'''
|
||||||
|
|
||||||
#######################################
|
inttile = get_inttile(database, src_segment)
|
||||||
# Write
|
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
|
read_verilog ../top.v
|
||||||
synth_design -top top
|
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_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_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_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_03) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||||
|
|
||||||
create_pblock roi
|
create_pblock roi
|
||||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||||
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
||||||
|
|
||||||
set_property CFGBVS VCCO [current_design]
|
set_property CFGBVS VCCO [current_design]
|
||||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||||
set_param tcl.collectionResultDisplayLimit 0
|
set_param tcl.collectionResultDisplayLimit 0
|
||||||
|
|
||||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per column)
|
proc loc_luts {} {
|
||||||
# Also, if GRID_MIN/MAX is not defined, automatically create it based on used CLBs
|
set luts [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT]
|
||||||
# See caveat in README on automatic creation
|
set selected_luts {}
|
||||||
foreach lut $luts {
|
set lut_index 0
|
||||||
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_x < 0 || $grid_x < $grid_min_x] {set grid_min_x $grid_x}
|
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per CMT column)
|
||||||
if [expr $grid_max_x < 0 || $grid_x > $grid_max_x] {set grid_max_x $grid_x}
|
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}
|
# 50 per column => 50, 100, 150, etc
|
||||||
if [expr $grid_max_y < 0 || $grid_y > $grid_max_y] {set grid_max_y $grid_y}
|
# ex: SLICE_X2Y50/A6LUT
|
||||||
|
# Only take one of the CLBs within a slice
|
||||||
# 50 per column => 50, 100, 150, etc
|
if [regexp "X.*[02468]Y.*[05]0/" $lut] {
|
||||||
if [regexp "Y(0|[0-9]*[05]0)/" $lut] {
|
set cell [get_cells roi/luts[$lut_index].lut]
|
||||||
set cell [get_cells roi/is[$lut_index].lut]
|
set_property LOC [get_sites -of_objects $lut] $cell
|
||||||
set_property LOC [get_sites -of_objects $lut] $cell
|
set lut_index [expr $lut_index + 1]
|
||||||
set lut_index [expr $lut_index + 1]
|
lappend selected_luts $lut
|
||||||
lappend selected_luts $lut
|
}
|
||||||
}
|
}
|
||||||
|
return $selected_luts
|
||||||
}
|
}
|
||||||
|
|
||||||
place_design
|
proc loc_brams {} {
|
||||||
route_design
|
# 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
|
# LOC one BRAM (a "selected_lut") into each BRAM segment configuration column (ie 10 per CMT column)
|
||||||
write_bitstream -force design.bit
|
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
|
# 10 per column => 10, 20, ,etc
|
||||||
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"]
|
# ex: RAMB36_X0Y10/RAMBFIFO36E1
|
||||||
|
if [regexp "Y.*0/" $bram] {
|
||||||
# Write tiles.txt with site metadata
|
set cell [get_cells roi/brams[$bram_index].bram]
|
||||||
set fp [open "tiles.txt" w]
|
set_property LOC [get_sites -of_objects $bram] $cell
|
||||||
foreach tile $tiles {
|
set bram_index [expr $bram_index + 1]
|
||||||
set type [get_property TYPE $tile]
|
lappend selected_brams $bram
|
||||||
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]
|
return $selected_brams
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
//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);
|
module top(input clk, stb, di, output do);
|
||||||
localparam integer DIN_N = 6;
|
localparam integer DIN_N = 8;
|
||||||
localparam integer DOUT_N = `N;
|
localparam integer DOUT_N = `N_LUT + `N_BRAM;
|
||||||
|
|
||||||
reg [DIN_N-1:0] din;
|
reg [DIN_N-1:0] din;
|
||||||
wire [DOUT_N-1:0] dout;
|
wire [DOUT_N-1:0] dout;
|
||||||
|
|
@ -29,10 +30,10 @@ module top(input clk, stb, di, output do);
|
||||||
);
|
);
|
||||||
endmodule
|
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;
|
genvar i;
|
||||||
generate
|
generate
|
||||||
for (i = 0; i < `N; i = i+1) begin:is
|
for (i = 0; i < `N_LUT; i = i+1) begin:luts
|
||||||
LUT6 #(
|
LUT6 #(
|
||||||
.INIT(64'h8000_0000_0000_0001 + (i << 16))
|
.INIT(64'h8000_0000_0000_0001 + (i << 16))
|
||||||
) lut (
|
) lut (
|
||||||
|
|
@ -46,4 +47,36 @@ module roi(input clk, input [5:0] din, output [`N-1:0] dout);
|
||||||
);
|
);
|
||||||
end
|
end
|
||||||
endgenerate
|
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
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ with open("design_%s.txt" % sys.argv[1], "r") as f:
|
||||||
for i in range(64):
|
for i in range(64):
|
||||||
bitname = "%s.INIT[%02d]" % (bel, i)
|
bitname = "%s.INIT[%02d]" % (bel, i)
|
||||||
bitname = bitname.replace("6LUT", "LUT")
|
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.compile()
|
||||||
segmk.write(sys.argv[1])
|
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="$(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
|
[[ $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
|
done
|
||||||
|
export PYTHONPATH="${XRAY_DIR}:$PYTHONPATH"
|
||||||
export XRAY_UTILS_DIR="$( cd -P "$( dirname "$XRAY_ENV_PATH" )" && pwd )"
|
export XRAY_UTILS_DIR="$( cd -P "$( dirname "$XRAY_ENV_PATH" )" && pwd )"
|
||||||
|
|
||||||
export XRAY_DIR="$( dirname "$XRAY_UTILS_DIR" )"
|
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_MASKMERGE="bash ${XRAY_UTILS_DIR}/maskmerge.sh"
|
||||||
export XRAY_SEGMATCH="${XRAY_TOOLS_DIR}/segmatch"
|
export XRAY_SEGMATCH="${XRAY_TOOLS_DIR}/segmatch"
|
||||||
export XRAY_SEGPRINT="python3 ${XRAY_UTILS_DIR}/segprint.py"
|
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
|
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
|
Loosely based on segprint function
|
||||||
Maybe better to return as two distinct dictionaries?
|
Maybe better to return as two distinct dictionaries?
|
||||||
|
|
@ -37,17 +57,15 @@ Maybe better to return as two distinct dictionaries?
|
||||||
segbitsdb = dict()
|
segbitsdb = dict()
|
||||||
|
|
||||||
|
|
||||||
def get_database(segtype):
|
def get_database(tile_type):
|
||||||
if segtype in segbitsdb:
|
if tile_type in segbitsdb:
|
||||||
return segbitsdb[segtype]
|
return segbitsdb[tile_type]
|
||||||
|
|
||||||
segbitsdb[segtype] = {}
|
segbitsdb[tile_type] = {}
|
||||||
|
|
||||||
def process(l):
|
def process(l):
|
||||||
l = l.strip()
|
|
||||||
|
|
||||||
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
||||||
parts = line.split()
|
parts = l.strip().split()
|
||||||
name = parts[0]
|
name = parts[0]
|
||||||
bit_vals = parts[1:]
|
bit_vals = parts[1:]
|
||||||
|
|
||||||
|
|
@ -59,7 +77,8 @@ def get_database(segtype):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Expect single bit DB entries to be set, got %s" % l)
|
"Expect single bit DB entries to be set, got %s" % l)
|
||||||
# Treat like an enumerated value with keys 0 or 1
|
# 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)],
|
'0': [(seg_word_column, word_bit_n, 0)],
|
||||||
'1': [(seg_word_column, word_bit_n, 1)],
|
'1': [(seg_word_column, word_bit_n, 1)],
|
||||||
}
|
}
|
||||||
|
|
@ -67,26 +86,16 @@ def get_database(segtype):
|
||||||
# An enumerated value
|
# An enumerated value
|
||||||
# Split the base name and selected key
|
# Split the base name and selected key
|
||||||
m = re.match(r'(.+)[.](.+)', name)
|
m = re.match(r'(.+)[.](.+)', name)
|
||||||
name = m.group(1)
|
namepart = m.group(1)
|
||||||
key = m.group(2)
|
key = m.group(2)
|
||||||
|
|
||||||
# May or may not be the first key encountered
|
# 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]
|
bits_map[key] = [parsebit(x) for x in bit_vals]
|
||||||
|
|
||||||
with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"),
|
process_db(tile_type, process)
|
||||||
os.getenv("XRAY_DATABASE"), segtype),
|
|
||||||
"r") as f:
|
|
||||||
for line in f:
|
|
||||||
process(line)
|
|
||||||
|
|
||||||
with open("%s/%s/segbits_int_%s.db" %
|
return segbitsdb[tile_type]
|
||||||
(os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
|
||||||
segtype[-1]), "r") as f:
|
|
||||||
for line in f:
|
|
||||||
process(line)
|
|
||||||
|
|
||||||
return segbitsdb[segtype]
|
|
||||||
|
|
||||||
|
|
||||||
def dump_frames_verbose(frames):
|
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')
|
'0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n')
|
||||||
|
|
||||||
|
|
||||||
def run(f_in, f_out, sparse=False, debug=False):
|
def mksegment(tile_name, block_name):
|
||||||
# address to array of 101 32 bit words
|
'''Create a segment name'''
|
||||||
frames = {}
|
return '%s:%s' % (tile_name, block_name)
|
||||||
# Directives we've seen so far
|
|
||||||
# Complain if there is a duplicate
|
|
||||||
# Contains line number of last entry
|
|
||||||
used_names = {}
|
|
||||||
|
|
||||||
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):
|
def mk_grid():
|
||||||
'''Set given frame to 0'''
|
'''Load tilegrid, flattening all blocks into one dictionary'''
|
||||||
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)
|
|
||||||
|
|
||||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
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.
|
# TODO: Migrate to new tilegrid format via library.
|
||||||
grid = {'tiles': new_grid, 'segments': {}}
|
grid = {'tiles': new_grid, 'segments': {}}
|
||||||
|
|
||||||
for tile in grid['tiles'].values():
|
for tile_name, tile in grid['tiles'].items():
|
||||||
if 'segment' in tile:
|
for block_name, block in tile['bits'].items():
|
||||||
segment = tile['segment']
|
segname = mksegment(tile_name, block_name)
|
||||||
grid['segments'][segment] = {
|
grid['segments'][segname] = {
|
||||||
'baseaddr': [
|
'baseaddr': [
|
||||||
tile['baseaddr'],
|
block['baseaddr'],
|
||||||
tile['offset'],
|
block['offset'],
|
||||||
],
|
],
|
||||||
'type': tile['segment_type'],
|
'type': tile['type'],
|
||||||
'frames': tile['frames'],
|
'frames': block['frames'],
|
||||||
'words': tile['words'],
|
'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):
|
def frame_init(frames, addr):
|
||||||
# Comment
|
'''Set given frame to 0'''
|
||||||
# Remove all text including and after #
|
if not addr in frames:
|
||||||
i = l.rfind('#')
|
frames[addr] = [0 for _i in range(101)]
|
||||||
if i >= 0:
|
|
||||||
l = l[0:i]
|
|
||||||
l = l.strip()
|
|
||||||
|
|
||||||
# Ignore blank lines
|
|
||||||
if not l:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# tile.site.stuff value
|
def frames_init(frames, grid):
|
||||||
# INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0
|
'''Set all frames to 0'''
|
||||||
# Optional value
|
for segj in grid['segments'].values():
|
||||||
m = re.match(r'([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_.\[\]]+)([ ](.+))?', l)
|
seg_baseaddr, seg_word_base = segj['baseaddr']
|
||||||
if not m:
|
seg_baseaddr = int(seg_baseaddr, 0)
|
||||||
raise FASMSyntaxError("Bad line: %s" % l)
|
for coli in range(segj['frames']):
|
||||||
tile = m.group(1)
|
frame_init(frames, seg_baseaddr + coli)
|
||||||
name = m.group(2)
|
|
||||||
value = m.group(4)
|
|
||||||
|
|
||||||
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]
|
def frame_set(frames, frame_addr, word_addr, bit_index):
|
||||||
seg = tilej['segment']
|
'''Set given bit in given frame address and word'''
|
||||||
segj = grid['segments'][seg]
|
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, seg_word_base = segj['baseaddr']
|
||||||
seg_baseaddr = int(seg_baseaddr, 0)
|
seg_baseaddr = int(seg_baseaddr, 0)
|
||||||
|
|
||||||
# Ensure that all frames exist for this segment
|
# Ensure that all frames exist for this segment
|
||||||
# FIXME: type dependent
|
# FIXME: type dependent
|
||||||
for coli in range(segj['frames']):
|
for coli in range(segj['frames']):
|
||||||
frame_init(seg_baseaddr + coli)
|
frame_init(frames, 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)
|
|
||||||
|
|
||||||
# Now lets look up the bits we need frames for
|
# Now lets look up the bits we need frames for
|
||||||
segdb = get_database(segj['type'])
|
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
|
(segj['type'], db_k, l)) from None
|
||||||
|
|
||||||
if not value:
|
if not value:
|
||||||
# If its binary, allow omitted value default to 1
|
value = default_value(db_vals, name)
|
||||||
if tuple(sorted(db_vals.keys())) == ('0', '1'):
|
|
||||||
value = '1'
|
|
||||||
else:
|
|
||||||
raise FASMSyntaxError(
|
|
||||||
"Enumerable entry %s must have explicit value" % name)
|
|
||||||
# Get the specific entry we need
|
# Get the specific entry we need
|
||||||
try:
|
try:
|
||||||
db_vals = db_vals[value]
|
db_vals = db_vals[value]
|
||||||
|
|
@ -258,8 +295,29 @@ def run(f_in, f_out, sparse=False, debug=False):
|
||||||
raise FASMSyntaxError(
|
raise FASMSyntaxError(
|
||||||
"Invalid entry %s. Valid entries are %s" %
|
"Invalid entry %s. Valid entries are %s" %
|
||||||
(value, db_vals.keys()))
|
(value, db_vals.keys()))
|
||||||
|
|
||||||
for seg_word_column, word_bit_n, isset in db_vals:
|
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:
|
if debug:
|
||||||
#dump_frames_verbose(frames)
|
#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):
|
Sample segdata.txt output (from 015-clbnffmux/specimen_001/segdata_clbll_r.txt):
|
||||||
seg 00020880_048
|
seg 00020880_048
|
||||||
|
|
@ -15,6 +18,8 @@ import os, json, re
|
||||||
XRAY_DATABASE = os.getenv("XRAY_DATABASE")
|
XRAY_DATABASE = os.getenv("XRAY_DATABASE")
|
||||||
XRAY_DIR = os.getenv("XRAY_DIR")
|
XRAY_DIR = os.getenv("XRAY_DIR")
|
||||||
|
|
||||||
|
BLOCK_TYPES = set(('CLB_IO_CLK', 'BLOCK_RAM', 'CFG_CLB'))
|
||||||
|
|
||||||
|
|
||||||
def recurse_sum(x):
|
def recurse_sum(x):
|
||||||
'''Count number of nested iterable occurances'''
|
'''Count number of nested iterable occurances'''
|
||||||
|
|
@ -36,8 +41,9 @@ def json_hex2i(s):
|
||||||
|
|
||||||
|
|
||||||
class segmaker:
|
class segmaker:
|
||||||
def __init__(self, bitsfile, verbose=False):
|
def __init__(self, bitsfile, verbose=None):
|
||||||
self.verbose = verbose
|
self.verbose = verbose if verbose is not None else os.getenv(
|
||||||
|
'VERBOSE', 'N') == 'Y'
|
||||||
self.load_grid()
|
self.load_grid()
|
||||||
self.load_bits(bitsfile)
|
self.load_bits(bitsfile)
|
||||||
'''
|
'''
|
||||||
|
|
@ -46,7 +52,8 @@ class segmaker:
|
||||||
-site: ex 'SLICE_X13Y101'
|
-site: ex 'SLICE_X13Y101'
|
||||||
-name: ex 'CLB.SLICE_X0.AFF.DMUX.CY'
|
-name: ex 'CLB.SLICE_X0.AFF.DMUX.CY'
|
||||||
'''
|
'''
|
||||||
self.tags = dict()
|
self.site_tags = dict()
|
||||||
|
self.tile_tags = dict()
|
||||||
|
|
||||||
# output after compiling
|
# output after compiling
|
||||||
self.segments_by_type = None
|
self.segments_by_type = None
|
||||||
|
|
@ -94,7 +101,7 @@ class segmaker:
|
||||||
'Loaded bits: %u bits in %u base frames' %
|
'Loaded bits: %u bits in %u base frames' %
|
||||||
(recurse_sum(self.bits), len(self.bits)))
|
(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:
|
XXX: can add tags in two ways:
|
||||||
-By site name
|
-By site name
|
||||||
|
|
@ -107,7 +114,10 @@ class segmaker:
|
||||||
self.addtag('SLICE_X13Y101', 'CLB.SLICE_X0.AFF.DMUX.CY', 1)
|
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'
|
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):
|
def compile(self, bitfilter=None):
|
||||||
print("Compiling segment data.")
|
print("Compiling segment data.")
|
||||||
|
|
@ -131,11 +141,20 @@ class segmaker:
|
||||||
tiledata: tilegrid info for this tile
|
tiledata: tilegrid info for this tile
|
||||||
'''
|
'''
|
||||||
assert segname not in segments
|
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"])
|
base_frame = json_hex2i(bitj["baseaddr"])
|
||||||
for wordidx in range(tiledata["offset"],
|
for wordidx in range(bitj["offset"],
|
||||||
tiledata["offset"] + tiledata["height"]):
|
bitj["offset"] + bitj["height"]):
|
||||||
if base_frame not in self.bits:
|
if base_frame not in self.bits:
|
||||||
continue
|
continue
|
||||||
if wordidx not in self.bits[base_frame]:
|
if wordidx not in self.bits[base_frame]:
|
||||||
|
|
@ -144,13 +163,14 @@ class segmaker:
|
||||||
base_frame][wordidx]:
|
base_frame][wordidx]:
|
||||||
bitname_frame = bit_frame - base_frame
|
bitname_frame = bit_frame - base_frame
|
||||||
bitname_bit = 32 * (
|
bitname_bit = 32 * (
|
||||||
bit_wordidx - tiledata["offset"]) + bit_bitidx
|
bit_wordidx - bitj["offset"]) + bit_bitidx
|
||||||
# some bits are hard to de-correlate
|
# some bits are hard to de-correlate
|
||||||
# allow force dropping some bits from search space for practicality
|
# allow force dropping some bits from search space for practicality
|
||||||
if bitfilter is None or bitfilter(bitname_frame,
|
if bitfilter is None or bitfilter(bitname_frame,
|
||||||
bitname_bit):
|
bitname_bit):
|
||||||
bitname = "%02d_%02d" % (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
|
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():
|
for tilename, tiledata in self.grid.items():
|
||||||
|
|
||||||
def add_tilename_tags():
|
def getseg(segname):
|
||||||
if not segname in segments:
|
if not segname in segments:
|
||||||
add_segbits(
|
return add_segbits(
|
||||||
segments, segname, tiledata, bitfilter=bitfilter)
|
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))
|
tags_used.add((tilename, name))
|
||||||
tag = "%s.%s" % (tile_type_norm, name)
|
tag = "%s.%s" % (tile_type_norm, name)
|
||||||
segments[segname]["tags"][tag] = value
|
segment["tags"][tag] = value
|
||||||
|
|
||||||
def add_site_tags():
|
def add_site_tags():
|
||||||
if not segname in segments:
|
segment = getseg(segname)
|
||||||
add_segbits(
|
|
||||||
segments, segname, tiledata, bitfilter=bitfilter)
|
|
||||||
|
|
||||||
if 'SLICE_' in site:
|
if 'SLICE_' in site:
|
||||||
'''
|
'''
|
||||||
|
|
@ -188,7 +215,7 @@ class segmaker:
|
||||||
else:
|
else:
|
||||||
assert 0, 'Unhandled site type'
|
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))
|
tags_used.add((site, name))
|
||||||
tag = "%s.%s.%s" % (tile_type_norm, sitekey, name)
|
tag = "%s.%s.%s" % (tile_type_norm, sitekey, name)
|
||||||
# XXX: does this come from name?
|
# XXX: does this come from name?
|
||||||
|
|
@ -197,7 +224,7 @@ class segmaker:
|
||||||
segments[segname]["tags"][tag] = value
|
segments[segname]["tags"][tag] = value
|
||||||
|
|
||||||
# ignore dummy tiles (ex: VBRK)
|
# ignore dummy tiles (ex: VBRK)
|
||||||
if "baseaddr" not in tiledata:
|
if "bits" not in tiledata:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tile_type = tiledata["type"]
|
tile_type = tiledata["type"]
|
||||||
|
|
@ -210,23 +237,25 @@ class segmaker:
|
||||||
'''
|
'''
|
||||||
tile_type_norm = re.sub("(LL|LM)?_[LR]$", "", tile_type)
|
tile_type_norm = re.sub("(LL|LM)?_[LR]$", "", tile_type)
|
||||||
|
|
||||||
segname = "%s_%03d" % (
|
for block_type, bitj in tiledata['bits'].items():
|
||||||
# truncate 0x to leave hex string
|
# NOTE: multiple tiles may have the same base addr + offset
|
||||||
tiledata["baseaddr"][2:],
|
segname = "%s_%03d" % (
|
||||||
tiledata["offset"])
|
# truncate 0x to leave hex string
|
||||||
|
bitj["baseaddr"][2:],
|
||||||
|
bitj["offset"])
|
||||||
|
|
||||||
# process tile name tags
|
# process tile name tags
|
||||||
if tilename in self.tags:
|
if tilename in self.tile_tags:
|
||||||
add_tilename_tags()
|
add_tilename_tags()
|
||||||
|
|
||||||
# process site name tags
|
# process site name tags
|
||||||
for site in tiledata["sites"]:
|
for site in tiledata["sites"]:
|
||||||
if site not in self.tags:
|
if site not in self.site_tags:
|
||||||
continue
|
continue
|
||||||
add_site_tags()
|
add_site_tags()
|
||||||
|
|
||||||
if self.verbose:
|
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("Used %u / %u tags" % (len(tags_used), ntags))
|
||||||
print("Grid DB had %u tile types" % len(tile_types_found))
|
print("Grid DB had %u tile types" % len(tile_types_found))
|
||||||
assert ntags and ntags == len(tags_used)
|
assert ntags and ntags == len(tags_used)
|
||||||
|
|
@ -234,6 +263,10 @@ class segmaker:
|
||||||
def write(self, suffix=None, roi=False):
|
def write(self, suffix=None, roi=False):
|
||||||
assert self.segments_by_type, 'No data to write'
|
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():
|
for segtype in self.segments_by_type.keys():
|
||||||
if suffix is not None:
|
if suffix is not None:
|
||||||
filename = "segdata_%s_%s.txt" % (segtype.lower(), suffix)
|
filename = "segdata_%s_%s.txt" % (segtype.lower(), suffix)
|
||||||
|
|
@ -244,19 +277,9 @@ class segmaker:
|
||||||
|
|
||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
segments = self.segments_by_type[segtype]
|
segments = self.segments_by_type[segtype]
|
||||||
if True:
|
for segname, segdata in sorted(segments.items()):
|
||||||
for segname, segdata in sorted(segments.items()):
|
print("seg %s" % segname, file=f)
|
||||||
print("seg %s" % segname, file=f)
|
for bitname in sorted(segdata["bits"]):
|
||||||
for bitname in sorted(segdata["bits"]):
|
print("bit %s" % bitname, file=f)
|
||||||
print("bit %s" % bitname, file=f)
|
for tagname, tagval in sorted(segdata["tags"].items()):
|
||||||
for tagname, tagval in sorted(segdata["tags"].items()):
|
print("tag %s %d" % (tagname, tagval), file=f)
|
||||||
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)
|
|
||||||
|
|
|
||||||
|
|
@ -1,182 +1,66 @@
|
||||||
#!/usr/bin/env python3
|
#!/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
|
import sys, os, json, re
|
||||||
|
|
||||||
flag_z = False
|
|
||||||
flag_b = False
|
|
||||||
flag_d = False
|
|
||||||
flag_D = False
|
|
||||||
|
|
||||||
|
|
||||||
def usage():
|
class NoDB(Exception):
|
||||||
print("Usage: %s [options] <bits_file> [segments/tiles]" % sys.argv[0])
|
pass
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
# cache
|
||||||
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)
|
|
||||||
|
|
||||||
segbitsdb = dict()
|
segbitsdb = dict()
|
||||||
|
|
||||||
|
|
||||||
def get_database(segtype):
|
# int and sites are loaded together so that bit coverage can be checked together
|
||||||
if segtype in segbitsdb:
|
# however, as currently written, each segment is essentially printed twice
|
||||||
return segbitsdb[segtype]
|
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()
|
for fn in fns:
|
||||||
|
if os.path.exists(fn):
|
||||||
with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"),
|
with open(fn, "r") as f:
|
||||||
os.getenv("XRAY_DATABASE"), segtype),
|
for line in f:
|
||||||
"r") as f:
|
process(line)
|
||||||
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]
|
|
||||||
|
|
||||||
|
|
||||||
def handle_segment(segname):
|
def get_database(tile_type):
|
||||||
if segname is None:
|
tags = list()
|
||||||
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(":")
|
|
||||||
|
|
||||||
if seg1 in grid["tiles"]:
|
if tile_type in segbitsdb:
|
||||||
seg1 = grid["tiles"][seg1]["segment"]
|
return segbitsdb[tile_type]
|
||||||
|
|
||||||
if seg2 in grid["tiles"]:
|
def process(l):
|
||||||
seg2 = grid["tiles"][seg2]["segment"]
|
tags.append(l.split())
|
||||||
|
|
||||||
seginfo1 = grid["segments"][seg1]
|
process_db(tile_type, process)
|
||||||
seginfo2 = grid["segments"][seg2]
|
|
||||||
|
|
||||||
frame1 = int(seginfo1["baseaddr"][0], 16)
|
if len(tags) == 0:
|
||||||
word1 = int(seginfo1["baseaddr"][1])
|
raise NoDB(tile_type)
|
||||||
|
|
||||||
frame2 = int(seginfo2["baseaddr"][0], 16)
|
segbitsdb[tile_type] = tags
|
||||||
word2 = int(seginfo2["baseaddr"][1])
|
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)
|
baseframe = int(seginfo["baseaddr"][0], 16)
|
||||||
basewordidx = int(seginfo["baseaddr"][1])
|
basewordidx = int(seginfo["baseaddr"][1])
|
||||||
numframes = int(seginfo["frames"])
|
numframes = int(seginfo["frames"])
|
||||||
numwords = int(seginfo["words"])
|
numwords = int(seginfo["words"])
|
||||||
|
|
||||||
segbits = set()
|
segbits = set()
|
||||||
segtags = set()
|
|
||||||
|
|
||||||
for frame in range(baseframe, baseframe + numframes):
|
for frame in range(baseframe, baseframe + numframes):
|
||||||
if frame not in bitdata:
|
if frame not in bitdata:
|
||||||
continue
|
continue
|
||||||
|
|
@ -187,25 +71,105 @@ def handle_segment(segname):
|
||||||
segbits.add(
|
segbits.add(
|
||||||
"%02d_%02d" %
|
"%02d_%02d" %
|
||||||
(frame - baseframe, 32 * (wordidx - basewordidx) + bitidx))
|
(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"]):
|
for entry in get_database(seginfo["type"]):
|
||||||
match_entry = True
|
if not tagmatch(entry, segbits):
|
||||||
for bit in entry[1:]:
|
continue
|
||||||
if bit[0] != "!" and bit not in segbits:
|
tag_matched(entry, segbits)
|
||||||
match_entry = False
|
if flag_decode_emit:
|
||||||
if bit[0] == "!" and bit[1:] in segbits:
|
segtags.add(entry[0])
|
||||||
match_entry = False
|
except NoDB:
|
||||||
if match_entry:
|
verbose and print(
|
||||||
for bit in entry[1:]:
|
"WARNING: failed to load DB for %s" % seginfo["type"])
|
||||||
if bit[0] != "!":
|
decode_warnings.add(seginfo["type"])
|
||||||
segbits.remove(bit)
|
return segtags
|
||||||
if flag_d:
|
|
||||||
segtags.add(entry[0])
|
|
||||||
|
|
||||||
if not flag_z or len(segbits) > 0 or len(segtags) > 0:
|
|
||||||
print()
|
def handle_segment(
|
||||||
print("seg %s" % segname)
|
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):
|
for bit in sorted(segbits):
|
||||||
print("bit %s" % bit)
|
print("bit %s" % bit)
|
||||||
|
|
@ -214,15 +178,140 @@ def handle_segment(segname):
|
||||||
print("tag %s" % tag)
|
print("tag %s" % tag)
|
||||||
|
|
||||||
|
|
||||||
if flag_b:
|
def load_bitdata(bits_file):
|
||||||
handle_segment(None)
|
bitdata = dict()
|
||||||
|
|
||||||
if len(args) == 1:
|
with open(bits_file, "r") as f:
|
||||||
seglist = list()
|
for line in f:
|
||||||
for seg, seginfo in grid["segments"].items():
|
line = line.split("_")
|
||||||
seglist.append((seginfo["baseaddr"][0], -seginfo["baseaddr"][1], seg))
|
frame = int(line[1], 16)
|
||||||
for _, _, seg in sorted(seglist):
|
wordidx = int(line[2], 10)
|
||||||
handle_segment(seg)
|
bitidx = int(line[3], 10)
|
||||||
else:
|
|
||||||
for arg in args[1:]:
|
if frame not in bitdata:
|
||||||
handle_segment(arg)
|
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
|
# TODO: need better coverage for different tile types
|
||||||
|
|
||||||
import fasm2frame
|
import fasm2frames
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
import StringIO
|
from io import StringIO
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,7 +24,7 @@ def frm2bits(txt):
|
||||||
assert (101 == len(words))
|
assert (101 == len(words))
|
||||||
for wordi, word in enumerate(words):
|
for wordi, word in enumerate(words):
|
||||||
word = int(word, 0)
|
word = int(word, 0)
|
||||||
for biti in xrange(32):
|
for biti in range(32):
|
||||||
val = word & (1 << biti)
|
val = word & (1 << biti)
|
||||||
if val:
|
if val:
|
||||||
bits_out.add((addr, wordi, biti))
|
bits_out.add((addr, wordi, biti))
|
||||||
|
|
@ -51,12 +52,12 @@ def bitread2bits(txt):
|
||||||
class TestStringMethods(unittest.TestCase):
|
class TestStringMethods(unittest.TestCase):
|
||||||
def test_lut(self):
|
def test_lut(self):
|
||||||
'''Simple smoke test on just the LUTs'''
|
'''Simple smoke test on just the LUTs'''
|
||||||
fout = StringIO.StringIO()
|
fout = StringIO()
|
||||||
fasm2frame.run(open('test_data/lut.fasm', 'r'), fout)
|
fasm2frames.run(open('test_data/lut.fasm', 'r'), fout)
|
||||||
|
|
||||||
def bitread_frm_equals(self, frm_fn, bitread_fn):
|
def bitread_frm_equals(self, frm_fn, bitread_fn):
|
||||||
fout = StringIO.StringIO()
|
fout = StringIO()
|
||||||
fasm2frame.run(open(frm_fn, 'r'), fout)
|
fasm2frames.run(open(frm_fn, 'r'), fout)
|
||||||
|
|
||||||
# Build a list of output used bits
|
# Build a list of output used bits
|
||||||
bits_out = frm2bits(fout.getvalue())
|
bits_out = frm2bits(fout.getvalue())
|
||||||
|
|
@ -84,24 +85,24 @@ class TestStringMethods(unittest.TestCase):
|
||||||
# Same check as above, but isolated test case
|
# Same check as above, but isolated test case
|
||||||
def test_opkey_01_default(self):
|
def test_opkey_01_default(self):
|
||||||
'''Optional key with binary omitted value should produce valid result'''
|
'''Optional key with binary omitted value should produce valid result'''
|
||||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX")
|
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX")
|
||||||
fout = StringIO.StringIO()
|
fout = StringIO()
|
||||||
fasm2frame.run(fin, fout)
|
fasm2frames.run(fin, fout)
|
||||||
|
|
||||||
def test_opkey_01_1(self):
|
def test_opkey_01_1(self):
|
||||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1")
|
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1")
|
||||||
fout = StringIO.StringIO()
|
fout = StringIO()
|
||||||
fasm2frame.run(fin, fout)
|
fasm2frames.run(fin, fout)
|
||||||
|
|
||||||
def test_opkey_enum(self):
|
def test_opkey_enum(self):
|
||||||
'''Optional key with enumerated value should produce syntax error'''
|
'''Optional key with enumerated value should produce syntax error'''
|
||||||
# CLBLM_L.SLICEM_X0.AMUX.O6 !30_06 !30_07 !30_08 30_11
|
# CLBLM_L.SLICEM_X0.AMUX.O6 !30_06 !30_07 !30_08 30_11
|
||||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.AMUX.O6")
|
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.AMUX.O6")
|
||||||
fout = StringIO.StringIO()
|
fout = StringIO()
|
||||||
try:
|
try:
|
||||||
fasm2frame.run(fin, fout)
|
fasm2frames.run(fin, fout)
|
||||||
self.fail("Expected syntax error")
|
self.fail("Expected syntax error")
|
||||||
except fasm2frame.FASMSyntaxError:
|
except fasm2frames.FASMSyntaxError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_ff_int_0s(self):
|
def test_ff_int_0s(self):
|
||||||
|
|
@ -111,39 +112,39 @@ class TestStringMethods(unittest.TestCase):
|
||||||
|
|
||||||
def test_badkey(self):
|
def test_badkey(self):
|
||||||
'''Bad key should throw syntax error'''
|
'''Bad key should throw syntax error'''
|
||||||
fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 2")
|
fin = StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 2")
|
||||||
fout = StringIO.StringIO()
|
fout = StringIO()
|
||||||
try:
|
try:
|
||||||
fasm2frame.run(fin, fout)
|
fasm2frames.run(fin, fout)
|
||||||
self.fail("Expected syntax error")
|
self.fail("Expected syntax error")
|
||||||
except fasm2frame.FASMSyntaxError:
|
except fasm2frames.FASMSyntaxError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_dupkey(self):
|
def test_dupkey(self):
|
||||||
'''Duplicate key should throw syntax error'''
|
'''Duplicate key should throw syntax error'''
|
||||||
fin = StringIO.StringIO(
|
fin = StringIO(
|
||||||
"""\
|
"""\
|
||||||
CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 0
|
CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 0
|
||||||
CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1
|
CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1
|
||||||
""")
|
""")
|
||||||
fout = StringIO.StringIO()
|
fout = StringIO()
|
||||||
try:
|
try:
|
||||||
fasm2frame.run(fin, fout)
|
fasm2frames.run(fin, fout)
|
||||||
self.fail("Expected syntax error")
|
self.fail("Expected syntax error")
|
||||||
except fasm2frame.FASMSyntaxError:
|
except fasm2frames.FASMSyntaxError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_sparse(self):
|
def test_sparse(self):
|
||||||
'''Verify sparse equivilent to normal encoding'''
|
'''Verify sparse equivilent to normal encoding'''
|
||||||
frm_fn = 'test_data/lut_int.fasm'
|
frm_fn = 'test_data/lut_int.fasm'
|
||||||
|
|
||||||
fout_sparse = StringIO.StringIO()
|
fout_sparse = StringIO()
|
||||||
fasm2frame.run(open(frm_fn, 'r'), fout_sparse, sparse=True)
|
fasm2frames.run(open(frm_fn, 'r'), fout_sparse, sparse=True)
|
||||||
fout_sparse_txt = fout_sparse.getvalue()
|
fout_sparse_txt = fout_sparse.getvalue()
|
||||||
bits_sparse = frm2bits(fout_sparse_txt)
|
bits_sparse = frm2bits(fout_sparse_txt)
|
||||||
|
|
||||||
fout_full = StringIO.StringIO()
|
fout_full = StringIO()
|
||||||
fasm2frame.run(open(frm_fn, 'r'), fout_full, sparse=False)
|
fasm2frames.run(open(frm_fn, 'r'), fout_full, sparse=False)
|
||||||
fout_full_txt = fout_full.getvalue()
|
fout_full_txt = fout_full.getvalue()
|
||||||
bits_full = frm2bits(fout_full_txt)
|
bits_full = frm2bits(fout_full_txt)
|
||||||
|
|
||||||
Loading…
Reference in New Issue