mirror of https://github.com/openXC7/prjxray.git
tilegrid: break into functions, add main()
Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
parent
63d872d539
commit
875081dbc2
|
|
@ -1,136 +1,201 @@
|
|||
#!/usr/bin/env python3
|
||||
'''
|
||||
Historically we grouped data into "segments"
|
||||
These were a region of the bitstream that encoded one or more tiles
|
||||
However, this didn't scale with certain tiles like BRAM
|
||||
Some sites had multiple bitstream areas and also occupied multiple tiles
|
||||
|
||||
Decoding was then shifted to instead describe how each title is encoded
|
||||
A post processing step verifies that two tiles don't reference the same bitstream area
|
||||
'''
|
||||
|
||||
import os, sys, json, re
|
||||
|
||||
#######################################
|
||||
# Read
|
||||
# matches lib/include/prjxray/xilinx/xc7series/block_type.h
|
||||
block_type_i2s = {
|
||||
0: 'CLB_IO_CLK',
|
||||
1: 'BLOCK_RAM',
|
||||
2: 'CFG_CLB',
|
||||
# special...maybe should error until we know what it is?
|
||||
# 3: 'RESERVED',
|
||||
}
|
||||
|
||||
tiles = list()
|
||||
site_baseaddr = dict()
|
||||
tile_baseaddr = dict()
|
||||
|
||||
with open("tiles.txt") as f:
|
||||
for line in f:
|
||||
tiles.append(line.split())
|
||||
def load_tiles():
|
||||
tiles = list()
|
||||
site_baseaddr = dict()
|
||||
tile_baseaddr = dict()
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
with open(arg) as f:
|
||||
line = f.read().strip()
|
||||
site = arg[7:-6]
|
||||
frame = int(line[5:5 + 8], 16)
|
||||
site_baseaddr[site] = "0x%08x" % (frame & ~0x7f)
|
||||
with open("tiles.txt") as f:
|
||||
for line in f:
|
||||
tiles.append(line.split())
|
||||
|
||||
#######################################
|
||||
# Create initial database
|
||||
for arg in sys.argv[1:]:
|
||||
with open(arg) as f:
|
||||
line = f.read().strip()
|
||||
site = arg[7:-6]
|
||||
frame = int(line[5:5 + 8], 16)
|
||||
site_baseaddr[site] = "0x%08x" % (frame & ~0x7f)
|
||||
|
||||
database = dict()
|
||||
segments = dict()
|
||||
tiles_by_grid = dict()
|
||||
return tiles, site_baseaddr, tile_baseaddr
|
||||
|
||||
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[tile_name] = {
|
||||
"type": tile_type,
|
||||
"sites": dict(),
|
||||
"grid_x": grid_x,
|
||||
"grid_y": grid_y
|
||||
}
|
||||
def make_database(tiles, site_baseaddr, tile_baseaddr):
|
||||
database = dict()
|
||||
tiles_by_grid = dict()
|
||||
|
||||
if len(record) > 4:
|
||||
for i in range(4, len(record), 2):
|
||||
site_type, site_name = record[i:i + 2]
|
||||
if site_name in site_baseaddr:
|
||||
framebaseaddr = site_baseaddr[site_name]
|
||||
database[tile_name]["sites"][site_name] = site_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
|
||||
|
||||
if framebaseaddr is not None:
|
||||
tile_baseaddr[tile_name] = [framebaseaddr, 0]
|
||||
database[tile_name] = {
|
||||
"type": tile_type,
|
||||
"sites": dict(),
|
||||
"grid_x": grid_x,
|
||||
"grid_y": grid_y
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Add Segments
|
||||
if len(record) > 4:
|
||||
for i in range(4, len(record), 2):
|
||||
site_type, site_name = record[i:i + 2]
|
||||
if site_name in site_baseaddr:
|
||||
framebaseaddr = site_baseaddr[site_name]
|
||||
database[tile_name]["sites"][site_name] = site_type
|
||||
|
||||
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"]
|
||||
if framebaseaddr is not None:
|
||||
tile_baseaddr[tile_name] = [framebaseaddr, 0]
|
||||
|
||||
if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R"]:
|
||||
if tile_type in ["CLBLL_L", "CLBLM_L"]:
|
||||
int_tile_name = tiles_by_grid[(grid_x + 1, grid_y)]
|
||||
else:
|
||||
int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)]
|
||||
return database, tiles_by_grid
|
||||
|
||||
segment_name = "SEG_" + tile_name
|
||||
segtype = tile_type.lower()
|
||||
|
||||
segments[segment_name] = dict()
|
||||
segments[segment_name]["tiles"] = [
|
||||
tile_name, int_tile_name
|
||||
]
|
||||
segments[segment_name]["type"] = segtype
|
||||
segments[segment_name]["frames"] = 36
|
||||
segments[segment_name]["words"] = 2
|
||||
def make_segments(database, tiles_by_grid, tile_baseaddr):
|
||||
segments = dict()
|
||||
|
||||
if tile_name in tile_baseaddr:
|
||||
segments[segment_name]["baseaddr"] = tile_baseaddr[
|
||||
tile_name]
|
||||
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"]
|
||||
|
||||
database[tile_name]["segment"] = segment_name
|
||||
database[int_tile_name]["segment"] = segment_name
|
||||
|
||||
if tile_type in ["HCLK_L", "HCLK_R"]:
|
||||
segment_name = "SEG_" + tile_name
|
||||
segtype = tile_type.lower()
|
||||
|
||||
segments[segment_name] = dict()
|
||||
segments[segment_name]["tiles"] = [tile_name]
|
||||
segments[segment_name]["type"] = segtype
|
||||
segments[segment_name]["frames"] = 26
|
||||
segments[segment_name]["words"] = 1
|
||||
database[tile_name]["segment"] = segment_name
|
||||
|
||||
if tile_type in ["BRAM_L", "DSP_L", "BRAM_R", "DSP_R"]:
|
||||
for k in range(5):
|
||||
if tile_type in ["BRAM_L", "DSP_L"]:
|
||||
interface_tile_name = tiles_by_grid[(grid_x + 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)]
|
||||
if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R"]:
|
||||
if tile_type in ["CLBLL_L", "CLBLM_L"]:
|
||||
int_tile_name = tiles_by_grid[(grid_x + 1, grid_y)]
|
||||
else:
|
||||
interface_tile_name = tiles_by_grid[(grid_x - 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)]
|
||||
|
||||
segment_name = "SEG_" + tile_name.replace("_", "%d_" % k, 1)
|
||||
segtype = tile_type.lower().replace("_", "%d_" % k, 1)
|
||||
segment_name = "SEG_" + tile_name
|
||||
segtype = tile_type.lower()
|
||||
|
||||
segments[segment_name] = dict()
|
||||
segments[segment_name]["tiles"] = [tile_name, int_tile_name]
|
||||
segments[segment_name]["type"] = segtype
|
||||
segments[segment_name]["frames"] = 28
|
||||
segments[segment_name]["frames"] = 36
|
||||
segments[segment_name]["words"] = 2
|
||||
|
||||
if k == 0:
|
||||
segments[segment_name]["tiles"] = [
|
||||
tile_name, interface_tile_name, int_tile_name
|
||||
]
|
||||
database[tile_name]["segment"] = segment_name
|
||||
database[interface_tile_name][
|
||||
"segment"] = segment_name
|
||||
database[int_tile_name]["segment"] = segment_name
|
||||
if tile_name in tile_baseaddr:
|
||||
segments[segment_name]["baseaddr"] = tile_baseaddr[tile_name]
|
||||
|
||||
database[tile_name]["segment"] = segment_name
|
||||
database[int_tile_name]["segment"] = segment_name
|
||||
|
||||
if tile_type in ["HCLK_L", "HCLK_R"]:
|
||||
segment_name = "SEG_" + tile_name
|
||||
segtype = tile_type.lower()
|
||||
|
||||
segments[segment_name] = dict()
|
||||
segments[segment_name]["tiles"] = [tile_name]
|
||||
segments[segment_name]["type"] = segtype
|
||||
segments[segment_name]["frames"] = 26
|
||||
segments[segment_name]["words"] = 1
|
||||
database[tile_name]["segment"] = segment_name
|
||||
|
||||
if tile_type in ["BRAM_L", "DSP_L", "BRAM_R", "DSP_R"]:
|
||||
for k in range(5):
|
||||
if tile_type in ["BRAM_L", "DSP_L"]:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x + 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)]
|
||||
else:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x - 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
|
||||
|
||||
segment_name = "SEG_" + tile_name.replace("_", "%d_" % k, 1)
|
||||
segtype = tile_type.lower().replace("_", "%d_" % k, 1)
|
||||
|
||||
segments[segment_name] = dict()
|
||||
segments[segment_name]["type"] = segtype
|
||||
segments[segment_name]["frames"] = 28
|
||||
segments[segment_name]["words"] = 2
|
||||
|
||||
if k == 0:
|
||||
segments[segment_name]["tiles"] = [
|
||||
tile_name, interface_tile_name, int_tile_name
|
||||
]
|
||||
database[tile_name]["segment"] = segment_name
|
||||
database[interface_tile_name]["segment"] = segment_name
|
||||
database[int_tile_name]["segment"] = segment_name
|
||||
else:
|
||||
segments[segment_name]["tiles"] = [
|
||||
interface_tile_name, int_tile_name
|
||||
]
|
||||
database[interface_tile_name]["segment"] = segment_name
|
||||
database[int_tile_name]["segment"] = segment_name
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
def seg_base_addr_lr_INT(database, segments, tiles_by_grid):
|
||||
'''Populate segment base addresses: L/R along INT column'''
|
||||
for segment_name in segments.keys():
|
||||
if "baseaddr" in segments[segment_name]:
|
||||
framebase, wordbase = segments[segment_name]["baseaddr"]
|
||||
inttile = [
|
||||
tile for tile in segments[segment_name]["tiles"]
|
||||
if database[tile]["type"] in ["INT_L", "INT_R"]
|
||||
][0]
|
||||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
if database[inttile]["type"] == "INT_L":
|
||||
grid_x += 1
|
||||
framebase = "0x%08x" % (int(framebase, 16) + 0x80)
|
||||
else:
|
||||
segments[segment_name]["tiles"] = [
|
||||
interface_tile_name, int_tile_name
|
||||
]
|
||||
database[interface_tile_name][
|
||||
"segment"] = segment_name
|
||||
database[int_tile_name]["segment"] = segment_name
|
||||
grid_x -= 1
|
||||
framebase = "0x%08x" % (int(framebase, 16) - 0x80)
|
||||
|
||||
#######################################
|
||||
# Populate segment base addresses: L/R along INT column
|
||||
if (grid_x, grid_y) not in tiles_by_grid:
|
||||
continue
|
||||
|
||||
for segment_name in segments.keys():
|
||||
if "baseaddr" in segments[segment_name]:
|
||||
tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
|
||||
if database[inttile]["type"] == "INT_L":
|
||||
assert database[tile]["type"] == "INT_R"
|
||||
elif database[inttile]["type"] == "INT_R":
|
||||
assert database[tile]["type"] == "INT_L"
|
||||
else:
|
||||
assert 0
|
||||
|
||||
assert "segment" in database[tile]
|
||||
|
||||
seg = database[tile]["segment"]
|
||||
|
||||
if "baseaddr" in segments[seg]:
|
||||
assert segments[seg]["baseaddr"] == [framebase, wordbase]
|
||||
else:
|
||||
segments[seg]["baseaddr"] = [framebase, wordbase]
|
||||
|
||||
|
||||
def seg_base_addr_up_INT(database, segments, tiles_by_grid):
|
||||
'''Populate segment base addresses: Up along INT/HCLK columns'''
|
||||
start_segments = list()
|
||||
|
||||
for segment_name in segments.keys():
|
||||
if "baseaddr" in segments[segment_name]:
|
||||
start_segments.append(segment_name)
|
||||
|
||||
for segment_name in start_segments:
|
||||
framebase, wordbase = segments[segment_name]["baseaddr"]
|
||||
inttile = [
|
||||
tile for tile in segments[segment_name]["tiles"]
|
||||
|
|
@ -139,108 +204,128 @@ for segment_name in segments.keys():
|
|||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
if database[inttile]["type"] == "INT_L":
|
||||
grid_x += 1
|
||||
framebase = "0x%08x" % (int(framebase, 16) + 0x80)
|
||||
else:
|
||||
grid_x -= 1
|
||||
framebase = "0x%08x" % (int(framebase, 16) - 0x80)
|
||||
for i in range(50):
|
||||
grid_y -= 1
|
||||
|
||||
if (grid_x, grid_y) not in tiles_by_grid:
|
||||
continue
|
||||
if wordbase == 50:
|
||||
wordbase += 1
|
||||
else:
|
||||
wordbase += 2
|
||||
|
||||
tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
|
||||
if database[inttile]["type"] == "INT_L":
|
||||
assert database[tile]["type"] == "INT_R"
|
||||
elif database[inttile]["type"] == "INT_R":
|
||||
assert database[tile]["type"] == "INT_L"
|
||||
else:
|
||||
assert 0
|
||||
|
||||
assert "segment" in database[tile]
|
||||
|
||||
seg = database[tile]["segment"]
|
||||
|
||||
if "baseaddr" in segments[seg]:
|
||||
assert segments[seg]["baseaddr"] == [
|
||||
framebase, wordbase
|
||||
]
|
||||
else:
|
||||
segments[seg]["baseaddr"] = [framebase, wordbase]
|
||||
|
||||
#######################################
|
||||
# Populate segment base addresses: Up along INT/HCLK columns
|
||||
|
||||
start_segments = list()
|
||||
|
||||
for segment_name in segments.keys():
|
||||
if "baseaddr" in segments[segment_name]:
|
||||
start_segments.append(segment_name)
|
||||
|
||||
for segment_name in start_segments:
|
||||
framebase, wordbase = segments[segment_name]["baseaddr"]
|
||||
inttile = [
|
||||
tile for tile in segments[segment_name]["tiles"]
|
||||
if database[tile]["type"] in ["INT_L", "INT_R"]
|
||||
][0]
|
||||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
for i in range(50):
|
||||
grid_y -= 1
|
||||
|
||||
if wordbase == 50:
|
||||
wordbase += 1
|
||||
else:
|
||||
wordbase += 2
|
||||
|
||||
segname = database[tiles_by_grid[(grid_x, grid_y)]]["segment"]
|
||||
segments[segname]["baseaddr"] = [framebase, wordbase]
|
||||
|
||||
#######################################
|
||||
# Transfer segment data into tiles
|
||||
|
||||
for segment_name in segments.keys():
|
||||
try:
|
||||
baseaddr, offset = segments[segment_name]["baseaddr"]
|
||||
except:
|
||||
print('Failed on segment name %s' % segment_name)
|
||||
raise
|
||||
for tile_name in segments[segment_name]["tiles"]:
|
||||
tile_type = database[tile_name]["type"]
|
||||
if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R", "INT_L",
|
||||
"INT_R"]:
|
||||
database[tile_name]["baseaddr"] = baseaddr
|
||||
database[tile_name]["offset"] = offset
|
||||
database[tile_name]["height"] = 2
|
||||
elif tile_type in ["HCLK_L", "HCLK_R"]:
|
||||
database[tile_name]["baseaddr"] = baseaddr
|
||||
database[tile_name]["offset"] = offset
|
||||
database[tile_name]["height"] = 1
|
||||
elif tile_type in ["BRAM_L", "BRAM_R", "DSP_L", "DSP_R"]:
|
||||
database[tile_name]["baseaddr"] = baseaddr
|
||||
database[tile_name]["offset"] = offset
|
||||
database[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
|
||||
# compability with unconverted tools. Update tools then remove this data from
|
||||
# tilegrid.json.
|
||||
for tiledata in database.values():
|
||||
if "segment" in tiledata:
|
||||
segment = tiledata["segment"]
|
||||
tiledata["frames"] = segments[segment]["frames"]
|
||||
tiledata["words"] = segments[segment]["words"]
|
||||
tiledata["segment_type"] = segments[segment]["type"]
|
||||
segname = database[tiles_by_grid[(grid_x, grid_y)]]["segment"]
|
||||
segments[segname]["baseaddr"] = [framebase, wordbase]
|
||||
|
||||
|
||||
#######################################
|
||||
# Write
|
||||
def base_addr_2_block_type(base_addr):
|
||||
'''
|
||||
Table 5-24: Frame Address Register Description
|
||||
Bit Index: [25:23]
|
||||
https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
|
||||
"Valid block types are CLB, I/O, CLK ( 000 ), block RAM content ( 001 ), and CFG_CLB ( 010 ). A normal bitstream does not include type 011 ."
|
||||
'''
|
||||
block_type_i = (base_addr >> 23) & 0x7
|
||||
return block_type_i2s[block_type_i]
|
||||
|
||||
print(json.dumps(database, sort_keys=True, indent="\t"))
|
||||
|
||||
def add_tile_bits(tile_db, baseaddr, offset, height):
|
||||
'''
|
||||
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.setdefault('bits', {})
|
||||
block_type = base_addr_2_block_type(int(baseaddr, 0))
|
||||
|
||||
assert block_type not in bits
|
||||
block = bits.setdefault(block_type, {})
|
||||
'''
|
||||
# TODO: remove, just make compatible for initial cleanup commit
|
||||
block = tile_db
|
||||
|
||||
block["baseaddr"] = baseaddr
|
||||
block["offset"] = offset
|
||||
block["height"] = height
|
||||
|
||||
|
||||
def add_bits(database, segments):
|
||||
'''Transfer segment data into tiles'''
|
||||
for segment_name in segments.keys():
|
||||
try:
|
||||
baseaddr, offset = segments[segment_name]["baseaddr"]
|
||||
except:
|
||||
print('Failed on segment name %s' % segment_name)
|
||||
raise
|
||||
|
||||
for tile_name in segments[segment_name]["tiles"]:
|
||||
tile_type = database[tile_name]["type"]
|
||||
if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R",
|
||||
"INT_L", "INT_R"]:
|
||||
add_tile_bits(database[tile_name], baseaddr, offset, 2)
|
||||
elif tile_type in ["HCLK_L", "HCLK_R"]:
|
||||
add_tile_bits(database[tile_name], baseaddr, offset, 1)
|
||||
elif tile_type in ["BRAM_L", "BRAM_R", "DSP_L", "DSP_R"]:
|
||||
add_tile_bits(database[tile_name], baseaddr, offset, 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
|
||||
|
||||
|
||||
def annotate_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.
|
||||
for tiledata in database.values():
|
||||
if "segment" in tiledata:
|
||||
segment = tiledata["segment"]
|
||||
tiledata["frames"] = segments[segment]["frames"]
|
||||
tiledata["words"] = segments[segment]["words"]
|
||||
tiledata["segment_type"] = segments[segment]["type"]
|
||||
|
||||
|
||||
def run(tiles_fn, json_fn, deltas_fns):
|
||||
tiles, site_baseaddr, tile_baseaddr = load_tiles()
|
||||
database, tiles_by_grid = make_database(
|
||||
tiles, site_baseaddr, tile_baseaddr)
|
||||
segments = make_segments(database, tiles_by_grid, tile_baseaddr)
|
||||
seg_base_addr_lr_INT(database, segments, tiles_by_grid)
|
||||
seg_base_addr_up_INT(database, segments, tiles_by_grid)
|
||||
add_bits(database, segments)
|
||||
annotate_segments(database, segments)
|
||||
json.dump(
|
||||
database,
|
||||
open(json_fn, 'w'),
|
||||
sort_keys=True,
|
||||
indent=4,
|
||||
separators=(',', ': '))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Generate a simple wrapper to test synthesizing an arbitrary verilog module'
|
||||
)
|
||||
|
||||
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',
|
||||
nargs='?',
|
||||
help='Input tiles.txt tcl output')
|
||||
parser.add_argument(
|
||||
'deltas', nargs='+', help='.bit diffs to create base addresses from')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(args.tiles, args.out, args.deltas)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
Loading…
Reference in New Issue