tilegrid: misc cleanup

Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
John McMaster 2018-10-10 16:40:41 -07:00
parent e62d722003
commit 9f3443a8a0
2 changed files with 196 additions and 109 deletions

View File

@ -21,6 +21,19 @@ block_type_i2s = {
} }
def addr2btype(base_addr):
'''
Convert integer address to block type
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]
def nolr(tile_type): def nolr(tile_type):
''' '''
Remove _L or _R suffix tile_type suffix, if present Remove _L or _R suffix tile_type suffix, if present
@ -70,7 +83,8 @@ def load_baseaddrs(deltas_fns):
line = f.read().strip() line = f.read().strip()
site = arg[7:-6] site = arg[7:-6]
frame = int(line[5:5 + 8], 16) frame = int(line[5:5 + 8], 16)
site_baseaddr[site] = "0x%08x" % (frame & ~0x7f) # was "0x%08x"
site_baseaddr[site] = frame & ~0x7f
return site_baseaddr return site_baseaddr
@ -81,27 +95,39 @@ def make_database(tiles):
database = dict() database = dict()
for tile in tiles: for tile in tiles:
database[tile['name']] = { database[tile["name"]] = {
"type": tile['type'], "type": tile["type"],
"sites": tile['sites'], "sites": tile["sites"],
"grid_x": tile['grid_x'], "grid_x": tile["grid_x"],
"grid_y": tile['grid_y'], "grid_y": tile["grid_y"],
} }
return database return database
def make_tile_baseaddr(tiles, site_baseaddr): def make_tile_baseaddrs(tiles, site_baseaddr, verbose=False):
# Look up a base address by tile name # Look up a base address by tile name
tile_baseaddr = dict() tile_baseaddrs = dict()
verbose and print('')
for tile in tiles: for tile in tiles:
for site_name in tile['sites'].keys(): for site_name in tile["sites"].keys():
if site_name in site_baseaddr: if site_name not in site_baseaddr:
framebaseaddr = site_baseaddr[site_name] continue
tile_baseaddr[tile['name']] = [framebaseaddr, 0] 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:
tile_baseaddr[bt] = [framebaseaddr, 0]
verbose and print(
"baseaddr: %s.%s @ %s.0x%08x" %
(tile["name"], site_name, bt, framebaseaddr))
return tile_baseaddr return tile_baseaddrs
def make_tiles_by_grid(tiles): def make_tiles_by_grid(tiles):
@ -109,12 +135,12 @@ def make_tiles_by_grid(tiles):
tiles_by_grid = dict() tiles_by_grid = dict()
for tile in tiles: for tile in tiles:
tiles_by_grid[(tile['grid_x'], tile['grid_y'])] = tile["name"] tiles_by_grid[(tile["grid_x"], tile["grid_y"])] = tile["name"]
return tiles_by_grid return tiles_by_grid
def make_segments(database, tiles_by_grid, tile_baseaddr): def make_segments(database, tiles_by_grid, tile_baseaddrs, verbose=False):
''' '''
Create segments data structure Create segments data structure
Indicates how tiles are related to bitstream locations Indicates how tiles are related to bitstream locations
@ -126,6 +152,7 @@ def make_segments(database, tiles_by_grid, tile_baseaddr):
''' '''
segments = dict() segments = dict()
verbose and print('')
for tile_name, tile_data in database.items(): for tile_name, tile_data in database.items():
tile_type = tile_data["type"] tile_type = tile_data["type"]
grid_x = tile_data["grid_x"] grid_x = tile_data["grid_x"]
@ -139,6 +166,11 @@ def make_segments(database, tiles_by_grid, tile_baseaddr):
segment["frames"] = frames segment["frames"] = frames
segment["words"] = words segment["words"] = words
if baseaddr: if baseaddr:
verbose and print(
'make_segment: %s baseaddr %s' % (
name,
baseaddr,
))
segment["baseaddr"] = baseaddr segment["baseaddr"] = baseaddr
for tile_name in tiles: for tile_name in tiles:
@ -156,7 +188,7 @@ def make_segments(database, tiles_by_grid, tile_baseaddr):
segtype=tile_type.lower(), segtype=tile_type.lower(),
frames=36, frames=36,
words=2, words=2,
baseaddr=tile_baseaddr.get(tile_name, None)) baseaddr=tile_baseaddrs.get(tile_name, None))
def process_hclk(): def process_hclk():
add_segment( add_segment(
@ -178,7 +210,11 @@ def make_segments(database, tiles_by_grid, tile_baseaddr):
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)] int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
else: else:
assert 0 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: if k == 0:
tiles = [tile_name, interface_tile_name, int_tile_name] tiles = [tile_name, interface_tile_name, int_tile_name]
else: else:
@ -193,99 +229,148 @@ def make_segments(database, tiles_by_grid, tile_baseaddr):
frames=28, frames=28,
words=2) words=2)
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, "CLBLL": process_clb,
"CLBLM": process_clb, "CLBLM": process_clb,
"HCLK": process_hclk, "HCLK": process_hclk,
"BRAM": process_bram_dsp, "BRAM": process_bram_dsp,
"DSP": process_bram_dsp, "DSP": process_bram_dsp,
}.get(nolr(tile_type), lambda: None)() }.get(nolr(tile_type), process_default)()
return segments return segments
def seg_base_addr_lr_INT(database, segments, tiles_by_grid): 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 seg_base_addr_lr_INT(database, segments, tiles_by_grid, verbose=False):
'''Populate segment base addresses: L/R along INT column''' '''Populate segment base addresses: L/R along INT column'''
for segment_name in segments.keys(): '''
# As of this writing only CLBs, but soon to be BRAM data Create BRAM base addresses based on nearby CLBs
if "baseaddr" not in segments[segment_name]: 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
framebase, wordbase = segments[segment_name]["baseaddr"] for block_type, (framebase, wordbase) in sorted(baseaddrs.items()):
inttile = [ verbose and print(
tile for tile in segments[segment_name]["tiles"] 'lr_INT: %s: %s.0x%08X:%u' %
if database[tile]["type"] in ["INT_L", "INT_R"] (segment_name, block_type, framebase, wordbase))
][0] if block_type != 'CLB_IO_CLK':
grid_x = database[inttile]["grid_x"] verbose and print(' Skip non CLB')
grid_y = database[inttile]["grid_y"] continue
if database[inttile]["type"] == "INT_L": inttile = get_inttile(database, segment)
grid_x += 1 grid_x = database[inttile]["grid_x"]
framebase = "0x%08x" % (int(framebase, 16) + 0x80) grid_y = database[inttile]["grid_y"]
else:
grid_x -= 1
framebase = "0x%08x" % (int(framebase, 16) - 0x80)
if (grid_x, grid_y) not in tiles_by_grid: if database[inttile]["type"] == "INT_L":
continue grid_x += 1
framebase = framebase + 0x80
elif database[inttile]["type"] == "INT_R":
grid_x -= 1
framebase = framebase - 0x80
else:
assert 0
tile = tiles_by_grid[(grid_x, grid_y)] # ROI at edge?
if (grid_x, grid_y) not in tiles_by_grid:
verbose and print(' Skip edge')
continue
if database[inttile]["type"] == "INT_L": tile = tiles_by_grid[(grid_x, grid_y)]
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] 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
seg = database[tile]["segment"] assert "segment" in database[tile]
if "baseaddr" in segments[seg]: seg = database[tile]["segment"]
assert segments[seg]["baseaddr"] == [framebase, wordbase]
else: seg_baseaddrs = segments[seg].setdefault("baseaddr", {})
segments[seg]["baseaddr"] = [framebase, wordbase] # At least one duplicate when we re-compute the entry for the base address
# should give the same address
if block_type in seg_baseaddrs:
assert seg_baseaddrs[block_type] == [
framebase, wordbase
], (seg_baseaddrs[block_type], [framebase, wordbase])
verbose and print(' Existing OK')
else:
seg_baseaddrs[block_type] = [framebase, wordbase]
verbose and print(' Add new')
def seg_base_addr_up_INT(database, segments, tiles_by_grid): def seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=False):
'''Populate segment base addresses: Up along INT/HCLK columns''' '''Populate segment base addresses: Up along INT/HCLK columns'''
start_segments = list()
verbose and print('')
'''
All baseaddrs so far have 50 tiles above them to be derived
However, once we start deriving, this is no longer true
Copy the initial list so that any baseaddr encountered can safely be swept up
'''
src_segment_names = list()
for segment_name in segments.keys(): for segment_name in segments.keys():
if "baseaddr" in segments[segment_name]: if "baseaddr" in segments[segment_name]:
start_segments.append(segment_name) src_segment_names.append(segment_name)
for segment_name in start_segments: verbose and print('up_INT: %u base addresses' % len(src_segment_names))
framebase, wordbase = segments[segment_name]["baseaddr"] #verbose and print('\n'.join(sorted(src_segment_names)))
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): for src_segment_name in sorted(src_segment_names):
grid_y -= 1 src_segment = segments[src_segment_name]
if wordbase == 50: for block_type, (framebase,
wordbase += 1 wordbase) in sorted(src_segment["baseaddr"].items()):
else: verbose and print(
wordbase += 2 'up_INT: %s: %s.0x%08X:%u' %
(src_segment_name, block_type, framebase, wordbase))
# Ignore BRAM base addresses
# TODO: BRAM data needs to be populated in its own special way
if block_type != 'CLB_IO_CLK':
verbose and print(' Skip non CLB')
continue
segname = database[tiles_by_grid[(grid_x, grid_y)]]["segment"] inttile = get_inttile(database, src_segment)
segments[segname]["baseaddr"] = [framebase, wordbase] verbose and print(
' up_INT: %s => inttile %s' % (src_segment_name, inttile))
grid_x = database[inttile]["grid_x"]
grid_y = database[inttile]["grid_y"]
for i in range(50):
grid_y -= 1
dst_tile = database[tiles_by_grid[(grid_x, grid_y)]]
def base_addr_2_block_type(base_addr): if wordbase == 50:
''' wordbase += 1
Table 5-24: Frame Address Register Description else:
Bit Index: [25:23] wordbase += 2
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 ." #verbose and print(' dst_tile', dst_tile)
''' dst_segment_name = dst_tile["segment"]
block_type_i = (base_addr >> 23) & 0x7 #verbose and print('up_INT: %s => %s' % (src_segment_name, dst_segment_name))
return block_type_i2s[block_type_i] segments[dst_segment_name].setdefault(
"baseaddr", {})[block_type] = [framebase, wordbase]
def add_tile_bits(tile_db, baseaddr, offset, height): def add_tile_bits(tile_db, baseaddr, offset, height):
@ -297,12 +382,12 @@ def add_tile_bits(tile_db, baseaddr, offset, height):
https://github.com/SymbiFlow/prjxray/issues/145 https://github.com/SymbiFlow/prjxray/issues/145
''' '''
bits = tile_db.setdefault('bits', {}) bits = tile_db.setdefault('bits', {})
block_type = base_addr_2_block_type(int(baseaddr, 0)) block_type = addr2btype(baseaddr)
assert block_type not in bits assert block_type not in bits
block = bits.setdefault(block_type, {}) block = bits.setdefault(block_type, {})
block["baseaddr"] = baseaddr block["baseaddr"] = '0x%08X' % baseaddr
block["offset"] = offset block["offset"] = offset
block["height"] = height block["height"] = height
@ -310,28 +395,26 @@ def add_tile_bits(tile_db, baseaddr, offset, height):
def add_bits(database, segments): def add_bits(database, segments):
'''Transfer segment data into tiles''' '''Transfer segment data into tiles'''
for segment_name in segments.keys(): for segment_name in segments.keys():
try: for _block_type, (
baseaddr, offset = segments[segment_name]["baseaddr"] baseaddr,
except: offset) in segments[segment_name]["baseaddr"].items():
print('Failed on segment name %s' % segment_name) for tile_name in segments[segment_name]["tiles"]:
raise tile_type = database[tile_name]["type"]
height = {
for tile_name in segments[segment_name]["tiles"]: "CLBLL": 2,
tile_type = database[tile_name]["type"] "CLBLM": 2,
height = { "INT": 2,
"CLBLL": 2, "HCLK": 1,
"CLBLM": 2, "BRAM": 10,
"INT": 2, "DSP": 10,
"HCLK": 1, "INT_INTERFACE": 0,
"BRAM": 10, "BRAM_INT_INTERFACE": 0,
"DSP": 10, }.get(nolr(tile_type), None)
"INT_INTERFACE": 0, if height is None:
"BRAM_INT_INTERFACE": 0, raise ValueError("Unknown tile type %s" % tile_type)
}.get(nolr(tile_type), None) if height:
if height is None: add_tile_bits(
raise ValueError("Unknown tile type %s" % tile_type) database[tile_name], baseaddr, offset, height)
if height:
add_tile_bits(database[tile_name], baseaddr, offset, height)
def annotate_segments(database, segments): def annotate_segments(database, segments):
@ -346,25 +429,28 @@ def annotate_segments(database, segments):
tiledata["segment_type"] = segments[segment]["type"] tiledata["segment_type"] = segments[segment]["type"]
def run(tiles_fn, json_fn, deltas_fns): def run(tiles_fn, json_fn, deltas_fns, verbose=False):
# Load input files # Load input files
tiles = load_tiles(tiles_fn) tiles = load_tiles(tiles_fn)
site_baseaddr = load_baseaddrs(deltas_fns) site_baseaddr = load_baseaddrs(deltas_fns)
# Index input # Index input
database = make_database(tiles) database = make_database(tiles)
tile_baseaddr = make_tile_baseaddr(tiles, site_baseaddr) tile_baseaddrs = make_tile_baseaddrs(tiles, site_baseaddr, verbose=verbose)
tiles_by_grid = make_tiles_by_grid(tiles) tiles_by_grid = make_tiles_by_grid(tiles)
segments = make_segments(database, tiles_by_grid, tile_baseaddr) segments = make_segments(
database, tiles_by_grid, tile_baseaddrs, verbose=verbose)
# Reference adjacent CLBs to locate adjacent tiles by known offsets # Reference adjacent CLBs to locate adjacent tiles by known offsets
seg_base_addr_lr_INT(database, segments, tiles_by_grid) seg_base_addr_lr_INT(database, segments, tiles_by_grid, verbose=verbose)
seg_base_addr_up_INT(database, segments, tiles_by_grid) seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=verbose)
add_bits(database, segments) add_bits(database, segments)
annotate_segments(database, segments) annotate_segments(database, segments)
#database = {'BRAM_L_X6Y50': database["BRAM_L_X6Y50"]}
# Save # Save
json.dump( json.dump(
database, database,
@ -390,7 +476,7 @@ def main():
'deltas', nargs='+', help='.bit diffs to create base addresses from') 'deltas', nargs='+', help='.bit diffs to create base addresses from')
args = parser.parse_args() args = parser.parse_args()
run(args.tiles, args.out, args.deltas) run(args.tiles, args.out, args.deltas, verbose=args.verbose)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -34,7 +34,8 @@ proc loc_luts {} {
# 50 per column => 50, 100, 150, etc # 50 per column => 50, 100, 150, etc
# ex: SLICE_X2Y50/A6LUT # ex: SLICE_X2Y50/A6LUT
if [regexp "Y.*[05]0/" $lut] { # Only take one of the CLBs within a slice
if [regexp "X.*[02468]Y.*[05]0/" $lut] {
set cell [get_cells roi/luts[$lut_index].lut] set cell [get_cells roi/luts[$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]