tilegrid: break into functions, add main()

Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
John McMaster 2018-10-09 15:23:51 -07:00
parent 63d872d539
commit 875081dbc2
1 changed files with 288 additions and 203 deletions

View File

@ -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()