mirror of https://github.com/openXC7/prjxray.git
commit
78fa4bd56e
|
|
@ -3,10 +3,11 @@ export XRAY_PART="xc7a50tfgg484-1"
|
|||
export XRAY_ROI_FRAMES="0x00000000:0xffffffff"
|
||||
|
||||
# All CLB's in part, all BRAM's in part, all DSP's in part.
|
||||
# tcl queries IOB => don't bother adding
|
||||
export XRAY_ROI_TILEGRID="SLICE_X0Y0:SLICE_X65Y99 SLICE_X0Y100:SLICE_X57Y149 RAMB18_X0Y0:RAMB18_X1Y59 RAMB36_X0Y0:RAMB36_X1Y29 RAMB18_X2Y0:RAMB18_X2Y39 RAMB36_X2Y0:RAMB36_X2Y19 DSP48_X0Y0:DSP48_X1Y59"
|
||||
|
||||
# These settings must remain in sync
|
||||
export XRAY_ROI="SLICE_X0Y100:SLICE_X35Y149 RAMB18_X0Y40:RAMB18_X0Y59 RAMB36_X0Y20:RAMB36_X0Y29 DSP48_X0Y40:DSP48_X0Y59"
|
||||
export XRAY_ROI="SLICE_X0Y100:SLICE_X35Y149 RAMB18_X0Y40:RAMB18_X0Y59 RAMB36_X0Y20:RAMB36_X0Y29 DSP48_X0Y40:DSP48_X0Y59 IOB_X0Y100:IOB_X0Y149"
|
||||
# Most of CMT X0Y2.
|
||||
export XRAY_ROI_GRID_X1="9"
|
||||
export XRAY_ROI_GRID_X2="58"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,32 @@
|
|||
# TODO: parallelize
|
||||
|
||||
FUZDIR=$(shell pwd)
|
||||
BUILD_DIR=$(FUZDIR)/build
|
||||
|
||||
database: build/tilegrid.json
|
||||
|
||||
pushdb:
|
||||
cp build/tilegrid.json ${XRAY_DATABASE_DIR}/$(XRAY_DATABASE)/tilegrid.json
|
||||
|
||||
build/tilegrid.json: build/deltas generate.py
|
||||
cd build && python3 ../generate.py design_*.delta > tilegrid.json
|
||||
build/tiles/tiles.txt:
|
||||
bash generate.sh build/tiles tiles
|
||||
|
||||
build/deltas:
|
||||
bash generate.sh build
|
||||
# TODO: only generate tiles
|
||||
build/tilegrid_basic.json: generate.py build/tiles/tiles.txt
|
||||
cd build && python3 ${FUZDIR}/generate.py --tiles $(FUZDIR)/build/tiles/tiles.txt --out ${BUILD_DIR}/tilegrid_basic.json
|
||||
|
||||
build/clb/deltas:
|
||||
bash generate.sh build/clb clb
|
||||
|
||||
build/bram/deltas:
|
||||
bash generate.sh build/bram bram
|
||||
|
||||
build/iob/deltas:
|
||||
bash generate.sh build/iob iob
|
||||
|
||||
# TODO: only generate addresses
|
||||
build/tilegrid.json: generate_full.py build/tilegrid_basic.json build/clb/deltas build/bram/deltas build/iob/deltas
|
||||
cd build && python3 ${FUZDIR}/generate_full.py --json-in tilegrid_basic.json --json-out ${BUILD_DIR}/tilegrid.json --tiles $(FUZDIR)/build/tiles/tiles.txt */design_*.delta
|
||||
|
||||
run:
|
||||
$(MAKE) clean
|
||||
|
|
|
|||
|
|
@ -1,51 +1,7 @@
|
|||
#!/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
|
||||
|
||||
# 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',
|
||||
}
|
||||
|
||||
|
||||
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):
|
||||
'''
|
||||
Remove _L or _R suffix tile_type suffix, if present
|
||||
Ex: BRAM_INT_INTERFACE_L => BRAM_INT_INTERFACE
|
||||
Ex: VBRK => VBRK
|
||||
'''
|
||||
postfix = tile_type[-2:]
|
||||
if postfix in ('_L', '_R'):
|
||||
return tile_type[:-2]
|
||||
else:
|
||||
return tile_type
|
||||
|
||||
|
||||
def load_tiles(tiles_fn):
|
||||
'''
|
||||
|
|
@ -76,19 +32,6 @@ def load_tiles(tiles_fn):
|
|||
return tiles
|
||||
|
||||
|
||||
def load_baseaddrs(deltas_fns):
|
||||
site_baseaddr = dict()
|
||||
for arg in deltas_fns:
|
||||
with open(arg) as f:
|
||||
line = f.read().strip()
|
||||
site = arg[7:-6]
|
||||
frame = int(line[5:5 + 8], 16)
|
||||
# was "0x%08x"
|
||||
site_baseaddr[site] = frame & ~0x7f
|
||||
|
||||
return site_baseaddr
|
||||
|
||||
|
||||
def make_database(tiles):
|
||||
# tile database with X, Y, and list of sites
|
||||
# tile name as keys
|
||||
|
|
@ -106,501 +49,12 @@ def make_database(tiles):
|
|||
return database
|
||||
|
||||
|
||||
def make_tile_baseaddrs(tiles, site_baseaddr, verbose=False):
|
||||
# Look up a base address by tile name
|
||||
tile_baseaddrs = dict()
|
||||
|
||||
verbose and print('')
|
||||
verbose and print('%u tiles' % len(tiles))
|
||||
added = 0
|
||||
for tile in tiles:
|
||||
for site_name in tile["sites"].keys():
|
||||
if site_name not in site_baseaddr:
|
||||
continue
|
||||
framebaseaddr = site_baseaddr[site_name]
|
||||
bt = addr2btype(framebaseaddr)
|
||||
tile_baseaddr = tile_baseaddrs.setdefault(tile["name"], {})
|
||||
if bt in tile_baseaddr:
|
||||
# actually lets just fail these, better to remove at tcl level to speed up processing
|
||||
assert 0, 'duplicate base address'
|
||||
assert tile_baseaddr[bt] == [framebaseaddr, 0]
|
||||
else:
|
||||
tile_baseaddr[bt] = [framebaseaddr, 0]
|
||||
verbose and print(
|
||||
"baseaddr: %s.%s @ %s.0x%08x" %
|
||||
(tile["name"], site_name, bt, framebaseaddr))
|
||||
added += 1
|
||||
|
||||
assert added
|
||||
return tile_baseaddrs
|
||||
|
||||
|
||||
def make_tiles_by_grid(tiles):
|
||||
# lookup tile names by (X, Y)
|
||||
tiles_by_grid = dict()
|
||||
|
||||
for tile in tiles:
|
||||
tiles_by_grid[(tile["grid_x"], tile["grid_y"])] = tile["name"]
|
||||
|
||||
return tiles_by_grid
|
||||
|
||||
|
||||
def add_segment(
|
||||
database, segments, name, tiles, segtype, verbose, 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 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 process_clb():
|
||||
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)]
|
||||
|
||||
add_segment(
|
||||
database=database,
|
||||
segments=segments,
|
||||
name="SEG_" + tile_name,
|
||||
tiles=[tile_name, int_tile_name],
|
||||
segtype=tile_type.lower(),
|
||||
baseaddr=tile_baseaddrs.get(tile_name, None),
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
def process_hclk():
|
||||
add_segment(
|
||||
database=database,
|
||||
segments=segments,
|
||||
name="SEG_" + tile_name,
|
||||
tiles=[tile_name],
|
||||
segtype=tile_type.lower(),
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
def process_bram_dsp():
|
||||
for k in range(5):
|
||||
if tile_type in ["BRAM_L", "DSP_L"]:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x + 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)]
|
||||
elif tile_type in ["BRAM_R", "DSP_R"]:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x - 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
|
||||
else:
|
||||
assert 0
|
||||
'''
|
||||
BRAM/DSP itself is at the base y address
|
||||
There is one huge switchbox on the right for the 5 tiles
|
||||
These fan into 5 BRAM_INT_INTERFACE tiles each which feed into their own CENTER_INTER (just like a CLB has)
|
||||
'''
|
||||
if k == 0:
|
||||
tiles = [tile_name, interface_tile_name, int_tile_name]
|
||||
baseaddr = tile_baseaddrs.get(tile_name, None)
|
||||
else:
|
||||
tiles = [interface_tile_name, int_tile_name]
|
||||
baseaddr = None
|
||||
|
||||
add_segment(
|
||||
database=database,
|
||||
segments=segments,
|
||||
# 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,
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
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 create_segment_for_int_lr(
|
||||
database, segments, tile, tiles_by_grid, verbose):
|
||||
""" Creates INT_[LR] segment for interconnect's without direct connectivity. """
|
||||
# Some INT_[LR] tiles have no adjacent connectivity, create a segment.
|
||||
grid_x = database[tile]["grid_x"]
|
||||
grid_y = database[tile]["grid_y"]
|
||||
if database[tile]["type"] == "INT_L":
|
||||
grid_x -= 1
|
||||
adjacent_tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
elif database[tile]["type"] == "INT_R":
|
||||
grid_x += 1
|
||||
adjacent_tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
else:
|
||||
assert False, database[tile]["type"]
|
||||
|
||||
if (database[adjacent_tile]['type'].startswith('INT_INTERFACE_') or
|
||||
database[adjacent_tile]['type'].startswith('PCIE_INT_INTERFACE_')
|
||||
or
|
||||
database[adjacent_tile]['type'].startswith('GTP_INT_INTERFACE')):
|
||||
# This INT_[LR] tile has no adjacent connectivity,
|
||||
# create a segment.
|
||||
add_segment(
|
||||
database=database,
|
||||
segments=segments,
|
||||
name='SEG_' + tile,
|
||||
tiles=[tile],
|
||||
segtype=database[tile]["type"],
|
||||
verbose=verbose,
|
||||
)
|
||||
else:
|
||||
assert False, database[adjacent_tile]['type']
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
inttile = get_inttile(database, segment)
|
||||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
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
|
||||
|
||||
# ROI at edge?
|
||||
if (grid_x, grid_y) not in tiles_by_grid:
|
||||
verbose and print(' Skip edge')
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
if 'segment' not in database[tile]:
|
||||
if database[tile]['type'] in ['T_TERM_INT']:
|
||||
continue
|
||||
create_segment_for_int_lr(
|
||||
database, segments, tile, tiles_by_grid, verbose)
|
||||
|
||||
seg = database[tile]["segment"]
|
||||
|
||||
seg_baseaddrs = segments[seg].setdefault("baseaddr", {})
|
||||
# At least one duplicate when we re-compute the entry for the base address
|
||||
# should give the same address
|
||||
if block_type in seg_baseaddrs:
|
||||
assert seg_baseaddrs[block_type] == [
|
||||
framebase, wordbase
|
||||
], (seg_baseaddrs[block_type], [framebase, wordbase])
|
||||
verbose and print(' Existing OK')
|
||||
else:
|
||||
seg_baseaddrs[block_type] = [framebase, wordbase]
|
||||
verbose and print(' Add new')
|
||||
|
||||
|
||||
def seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=False):
|
||||
'''Populate segment base addresses: Up along INT/HCLK columns'''
|
||||
|
||||
verbose and print('')
|
||||
# Copy the initial list containing only base addresses
|
||||
# and soon to have derived addresses
|
||||
src_segment_names = list()
|
||||
for segment_name in segments.keys():
|
||||
if "baseaddr" in segments[segment_name]:
|
||||
src_segment_names.append(segment_name)
|
||||
|
||||
verbose and print('up_INT: %u base addresses' % len(src_segment_names))
|
||||
|
||||
for src_segment_name in sorted(src_segment_names):
|
||||
src_segment = segments[src_segment_name]
|
||||
|
||||
for block_type, (framebase,
|
||||
wordbase) in sorted(src_segment["baseaddr"].items()):
|
||||
verbose and print(
|
||||
'up_INT: %s: %s.0x%08X:%u' %
|
||||
(src_segment_name, block_type, framebase, wordbase))
|
||||
|
||||
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)
|
||||
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"]
|
||||
|
||||
for i in range(50):
|
||||
grid_y -= 1
|
||||
if grid_y < 0:
|
||||
break
|
||||
|
||||
dst_tile = database[tiles_by_grid[(grid_x, grid_y)]]
|
||||
|
||||
if wordbase == 50:
|
||||
wordbase += 1
|
||||
else:
|
||||
wordbase += 2
|
||||
|
||||
if 'segment' not in dst_tile:
|
||||
if dst_tile['type'] in ['T_TERM_INT']:
|
||||
continue
|
||||
create_segment_for_int_lr(
|
||||
database, segments, tiles_by_grid[(grid_x,
|
||||
grid_y)],
|
||||
tiles_by_grid, verbose)
|
||||
|
||||
#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
|
||||
|
||||
if (grid_x, grid_y) not in tiles_by_grid:
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
# related to words...
|
||||
# deprecated field? Don't worry about for now
|
||||
# DSP has some differences between height and words
|
||||
block["words"] = words
|
||||
if height is None:
|
||||
height = words
|
||||
block["height"] = height
|
||||
|
||||
|
||||
def db_add_bits(database, segments):
|
||||
'''Transfer segment data into tiles'''
|
||||
for segment_name in segments.keys():
|
||||
if 'baseaddr' not in segments[segment_name]:
|
||||
continue
|
||||
|
||||
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, 10, None),
|
||||
("BRAM", "BLOCK_RAM"): (128, 10, 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):
|
||||
def run(tiles_fn, json_fn, 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(
|
||||
|
|
@ -622,15 +76,9 @@ def main():
|
|||
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)
|
||||
run(args.tiles, args.out, verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
#!/bin/bash -x
|
||||
|
||||
PRJ=$2
|
||||
|
||||
export FUZDIR=$PWD
|
||||
source ${XRAY_GENHEADER}
|
||||
|
||||
vivado -mode batch -source ../generate.tcl
|
||||
vivado -mode batch -source $FUZDIR/generate_$PRJ.tcl
|
||||
test -z "$(fgrep CRITICAL vivado.log)"
|
||||
|
||||
for x in design*.bit; do
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${x}s -z -y $x
|
||||
done
|
||||
|
||||
for x in design_*.bits; do
|
||||
diff -u design.bits $x | grep '^[-+]bit' > ${x%.bits}.delta
|
||||
done
|
||||
touch deltas
|
||||
if [ $PRJ != "tiles" ] ; then
|
||||
for x in design*.bit; do
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${x}s -z -y $x
|
||||
done
|
||||
|
||||
for x in design_*.bits; do
|
||||
diff -u design.bits $x | grep '^[-+]bit' > ${x%.bits}.delta
|
||||
done
|
||||
touch deltas
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,209 +0,0 @@
|
|||
proc make_project {} {
|
||||
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 clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
|
||||
create_pblock roi
|
||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||
foreach roi "$::env(XRAY_ROI_TILEGRID)" {
|
||||
puts "ROI: $roi"
|
||||
resize_pblock [get_pblocks roi] -add "$roi"
|
||||
}
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
set_param tcl.collectionResultDisplayLimit 0
|
||||
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
}
|
||||
|
||||
proc loc_luts {} {
|
||||
set luts [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT]
|
||||
set selected_luts {}
|
||||
set lut_index 0
|
||||
|
||||
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per CMT column)
|
||||
set lut_columns ""
|
||||
foreach lut $luts {
|
||||
regexp "SLICE_X([0-9]+)Y([0-9]+)/" $lut match slice_x slice_y
|
||||
|
||||
# Only even SLICEs should be used as column bases.
|
||||
if { $slice_x % 2 != 0 } {
|
||||
continue
|
||||
}
|
||||
|
||||
# 50 per column => 50, 100, 150, etc
|
||||
# ex: SLICE_X2Y50/A6LUT
|
||||
# Only take one of the CLBs within a slice
|
||||
set y_column [expr ($slice_y / 50) * 50]
|
||||
dict append lut_columns "X${slice_x}Y${y_column}" "$lut "
|
||||
}
|
||||
|
||||
# Pick the smallest Y in each column.
|
||||
dict for {column luts_in_column} $lut_columns {
|
||||
set min_slice_y 9999999
|
||||
|
||||
foreach lut $luts_in_column {
|
||||
regexp "SLICE_X([0-9]+)Y([0-9]+)/" $lut slice_x slice_y
|
||||
|
||||
if { $slice_y < $min_slice_y } {
|
||||
set selected_lut $lut
|
||||
}
|
||||
}
|
||||
|
||||
set cell [get_cells roi/luts[$lut_index].lut]
|
||||
set lut_site [get_sites -of_objects [get_bels $selected_lut]]
|
||||
puts "LOCing $selected_lut to $lut_site"
|
||||
set_property LOC $lut_site $cell
|
||||
set lut_index [expr $lut_index + 1]
|
||||
lappend selected_luts [get_bels $selected_lut]
|
||||
}
|
||||
return $selected_luts
|
||||
}
|
||||
|
||||
# Return a list of sites containing BRAMs
|
||||
# sites are better than bels because site type may change and invalidate the bel
|
||||
proc loc_brams {} {
|
||||
# BRAM have multiple mutually exclusive sites
|
||||
# They can be cycled by setting the site type
|
||||
# Ex:
|
||||
# - RAMB36_X0Y10/RAMBFIFO36E1
|
||||
# - RAMB36_X0Y10/RAMB36E1
|
||||
# Default is RAMBFIFO36E1?
|
||||
# Work primarily on sites, not bels,
|
||||
# to avoid issues when switching site type during PnR
|
||||
set bram_sites [get_sites -of_objects [get_pblocks roi] -filter {SITE_TYPE =~ RAMBFIFO36E1*}]
|
||||
set bram_bels [get_bels -of_objects $bram_sites]
|
||||
|
||||
set selected_bram_sites {}
|
||||
set bram_index 0
|
||||
|
||||
# LOC one BRAM (a "selected_lut") into each BRAM segment configuration column (ie 10 per CMT column)
|
||||
set bram_columns ""
|
||||
foreach bram $bram_bels {
|
||||
regexp "RAMB36_X([0-9]+)Y([0-9]+)/" $bram match slice_x slice_y
|
||||
|
||||
# 10 per column => 10, 20, ,etc
|
||||
# ex: RAMB36_X0Y10/RAMBFIFO36E1
|
||||
set y_column [expr ($slice_y / 10) * 10]
|
||||
dict append bram_columns "X${slice_x}Y${y_column}" "$bram "
|
||||
}
|
||||
|
||||
# Pick the smallest Y in each column.
|
||||
dict for {column brams_in_column} $bram_columns {
|
||||
set min_slice_y 9999999
|
||||
|
||||
foreach bram $brams_in_column {
|
||||
regexp "RAMB36_X([0-9]+)Y([0-9]+)/" $bram match slice_x slice_y
|
||||
|
||||
if { $slice_y < $min_slice_y } {
|
||||
set selected_bram_bel $bram
|
||||
}
|
||||
}
|
||||
|
||||
set selected_bram_bel [get_bels $selected_bram_bel]
|
||||
|
||||
set bram_site [get_sites -of_objects $selected_bram_bel]
|
||||
puts "LOCing BEL: $selected_bram_bel to $bram_site"
|
||||
set cell [get_cells roi/brams[$bram_index].bram]
|
||||
set_property LOC $bram_site $cell
|
||||
if {"$bram_site" == ""} {error "Bad site $bram_site from bel $selected_bram_bel"}
|
||||
|
||||
set bram_index [expr $bram_index + 1]
|
||||
# Output site, not bel, to avoid reference issues after PnR
|
||||
lappend selected_bram_sites $bram_site
|
||||
}
|
||||
|
||||
return $selected_bram_sites
|
||||
}
|
||||
|
||||
proc write_tiles_txt {} {
|
||||
# Get all tiles, ie not just the selected LUTs
|
||||
set tiles [get_tiles]
|
||||
|
||||
# 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 } {
|
||||
puts "write_brams: [llength $selected_luts] LUTs"
|
||||
puts ""
|
||||
# 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
|
||||
set site [get_sites -of_objects [lindex $selected_luts $i]]
|
||||
write_bitstream -force design_$site.bit
|
||||
set_property INIT $orig_init $cell
|
||||
}
|
||||
}
|
||||
|
||||
proc write_brams { selected_brams_sites } {
|
||||
puts "write_brams: [llength $selected_brams_sites] BRAMs"
|
||||
puts ""
|
||||
# Toggle one bit in each selected BRAM to generate base addresses
|
||||
for {set i 0} {$i < [llength $selected_brams_sites]} {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
|
||||
set site [lindex $selected_brams_sites $i]
|
||||
if {"$site" == ""} {error "Bad site $site"}
|
||||
write_bitstream -force design_$site.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_sites [loc_brams]
|
||||
puts "Selected BRAMs: [llength $selected_brams_sites]"
|
||||
|
||||
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_sites
|
||||
}
|
||||
|
||||
run
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
source "$::env(FUZDIR)/util.tcl"
|
||||
|
||||
# Return a list of sites containing BRAMs
|
||||
# sites are better than bels because site type may change and invalidate the bel
|
||||
proc loc_brams {} {
|
||||
# BRAM have multiple mutually exclusive sites
|
||||
# They can be cycled by setting the site type
|
||||
# Ex:
|
||||
# - RAMB36_X0Y10/RAMBFIFO36E1
|
||||
# - RAMB36_X0Y10/RAMB36E1
|
||||
# Default is RAMBFIFO36E1?
|
||||
# Work primarily on sites, not bels,
|
||||
# to avoid issues when switching site type during PnR
|
||||
set bram_sites [get_sites -of_objects [get_pblocks roi] -filter {SITE_TYPE =~ RAMBFIFO36E1*}]
|
||||
set bram_bels [get_bels -of_objects $bram_sites]
|
||||
|
||||
set bram_columns [group_dut_cols $bram_bels 10]
|
||||
# Output site, not bel, to avoid reference issues after PnR
|
||||
return [loc_dut_col_sites $bram_columns {roi/brams[} {].bram}]
|
||||
}
|
||||
|
||||
proc write_brams { selected_brams_sites } {
|
||||
puts "write_brams: [llength $selected_brams_sites] BRAMs"
|
||||
puts ""
|
||||
# Toggle one bit in each selected BRAM to generate base addresses
|
||||
for {set i 0} {$i < [llength $selected_brams_sites]} {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
|
||||
set site [lindex $selected_brams_sites $i]
|
||||
if {"$site" == ""} {error "Bad site $site"}
|
||||
write_bitstream -force design_$site.bit
|
||||
set_property INIT_00 $orig_init $cell
|
||||
}
|
||||
}
|
||||
|
||||
proc run {} {
|
||||
make_project
|
||||
set selected_brams_sites [loc_brams]
|
||||
puts "Selected BRAMs: [llength $selected_brams_sites]"
|
||||
|
||||
place_design
|
||||
route_design
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
|
||||
write_brams $selected_brams_sites
|
||||
}
|
||||
|
||||
run
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
source "$::env(FUZDIR)/util.tcl"
|
||||
|
||||
proc group_lut_cols { lut_bels } {
|
||||
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per CMT column)
|
||||
set lut_columns ""
|
||||
foreach lut $lut_bels {
|
||||
regexp "SLICE_X([0-9]+)Y([0-9]+)/" $lut match slice_x slice_y
|
||||
|
||||
# Only even SLICEs should be used as column bases.
|
||||
if { $slice_x % 2 != 0 } {
|
||||
continue
|
||||
}
|
||||
|
||||
# 50 per column => 0, 50, 100, 150, etc
|
||||
# ex: SLICE_X2Y50/A6LUT
|
||||
# Only take one of the CLBs within a slice
|
||||
set y_column [expr ($slice_y / 50) * 50]
|
||||
dict append lut_columns "X${slice_x}Y${y_column}" "$lut "
|
||||
}
|
||||
return $lut_columns
|
||||
}
|
||||
|
||||
proc loc_luts {} {
|
||||
set lut_bels [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT]
|
||||
set lut_columns [group_lut_cols $lut_bels]
|
||||
return [loc_dut_col_bels $lut_columns {roi/luts[} {].lut}]
|
||||
}
|
||||
|
||||
proc write_clbs { selected_luts } {
|
||||
puts "write_brams: [llength $selected_luts] LUTs"
|
||||
puts ""
|
||||
# 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
|
||||
set site [get_sites -of_objects [lindex $selected_luts $i]]
|
||||
write_bitstream -force design_$site.bit
|
||||
set_property INIT $orig_init $cell
|
||||
}
|
||||
}
|
||||
|
||||
proc run {} {
|
||||
make_project
|
||||
set selected_luts [loc_luts]
|
||||
puts "Selected LUTs: [llength $selected_luts]"
|
||||
|
||||
place_design
|
||||
route_design
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
|
||||
write_clbs $selected_luts
|
||||
}
|
||||
|
||||
run
|
||||
|
||||
|
|
@ -0,0 +1,668 @@
|
|||
#!/usr/bin/env python3
|
||||
import os, sys, json, re
|
||||
'''
|
||||
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
|
||||
'''
|
||||
|
||||
from generate import load_tiles
|
||||
|
||||
# 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',
|
||||
}
|
||||
|
||||
|
||||
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):
|
||||
'''
|
||||
Remove _L or _R suffix tile_type suffix, if present
|
||||
Ex: BRAM_INT_INTERFACE_L => BRAM_INT_INTERFACE
|
||||
Ex: VBRK => VBRK
|
||||
'''
|
||||
postfix = tile_type[-2:]
|
||||
if postfix in ('_L', '_R'):
|
||||
return tile_type[:-2]
|
||||
else:
|
||||
return tile_type
|
||||
|
||||
|
||||
def load_baseaddrs(deltas_fns):
|
||||
site_baseaddr = dict()
|
||||
for arg in deltas_fns:
|
||||
with open(arg) as f:
|
||||
line = f.read().strip()
|
||||
# clb/design_SLICE_X10Y100.delta
|
||||
# site = arg[7:-6]
|
||||
site = re.match(r".*/design_(.*).delta", arg).group(1)
|
||||
# +bit_00400026_100_02
|
||||
_, frame_str, _, _ = line.split('_')
|
||||
frame = int(frame_str, 16)
|
||||
site_baseaddr[site] = frame & ~0x7f
|
||||
|
||||
return site_baseaddr
|
||||
|
||||
|
||||
def make_tile_baseaddrs(tiles, site_baseaddr, verbose=False):
|
||||
# Look up a base address by tile name
|
||||
tile_baseaddrs = dict()
|
||||
|
||||
verbose and print('')
|
||||
verbose and print('%u tiles' % len(tiles))
|
||||
verbose and print("%u baseaddrs" % len(site_baseaddr))
|
||||
added = 0
|
||||
for tile in tiles:
|
||||
for site_name in tile["sites"].keys():
|
||||
if site_name not in site_baseaddr:
|
||||
continue
|
||||
framebaseaddr = site_baseaddr[site_name]
|
||||
bt = addr2btype(framebaseaddr)
|
||||
tile_baseaddr = tile_baseaddrs.setdefault(tile["name"], {})
|
||||
if bt in tile_baseaddr:
|
||||
# actually lets just fail these, better to remove at tcl level to speed up processing
|
||||
assert 0, 'duplicate base address'
|
||||
assert tile_baseaddr[bt] == [framebaseaddr, 0]
|
||||
else:
|
||||
tile_baseaddr[bt] = [framebaseaddr, 0]
|
||||
verbose and print(
|
||||
"baseaddr: %s.%s @ %s.0x%08x" %
|
||||
(tile["name"], site_name, bt, framebaseaddr))
|
||||
added += 1
|
||||
|
||||
assert added, "Failed to add any base addresses"
|
||||
assert added == len(site_baseaddr)
|
||||
return tile_baseaddrs
|
||||
|
||||
|
||||
def make_tiles_by_grid(tiles):
|
||||
# lookup tile names by (X, Y)
|
||||
tiles_by_grid = dict()
|
||||
|
||||
for tile in tiles:
|
||||
tiles_by_grid[(tile["grid_x"], tile["grid_y"])] = tile["name"]
|
||||
|
||||
return tiles_by_grid
|
||||
|
||||
|
||||
def add_segment(
|
||||
database, segments, name, tiles, segtype, verbose, 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 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:
|
||||
int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)]
|
||||
|
||||
add_segment(
|
||||
name="SEG_" + tile_name,
|
||||
tiles=[tile_name, int_tile_name],
|
||||
segtype=tile_type.lower(),
|
||||
baseaddr=tile_baseaddrs.get(tile_name, None))
|
||||
|
||||
def process_hclk():
|
||||
add_segment(
|
||||
name="SEG_" + tile_name,
|
||||
tiles=[tile_name],
|
||||
segtype=tile_type.lower())
|
||||
|
||||
def process_iob():
|
||||
tiles = [tile_name]
|
||||
# FIXME mcmaster: removing INT for now
|
||||
# Geometry doesn't line up with CLB, unclear if they are included
|
||||
if tile_type.startswith('LIOB'):
|
||||
# Two INT_L's
|
||||
# tiles.append(tiles_by_grid[(grid_x + 4, grid_y)])
|
||||
# tiles.append(tiles_by_grid[(grid_x + 4, grid_y - 1)])
|
||||
# One IO interface tile
|
||||
tiles.append(tiles_by_grid[(grid_x + 1, grid_y)])
|
||||
else:
|
||||
# Two INT_R's
|
||||
# tiles.append(tiles_by_grid[(grid_x - 4, grid_y)])
|
||||
# tiles.append(tiles_by_grid[(grid_x - 4, grid_y - 1)])
|
||||
# One IO interface tile
|
||||
tiles.append(tiles_by_grid[(grid_x - 1, grid_y)])
|
||||
|
||||
add_segment(
|
||||
name="SEG_" + tile_name,
|
||||
tiles=tiles,
|
||||
segtype=tile_type.lower(),
|
||||
baseaddr=tile_baseaddrs.get(tile_name, None))
|
||||
|
||||
def process_iob_sing():
|
||||
# FIXME mcmaster: removing INT for now
|
||||
# Geometry doesn't line up with CLB, unclear if they are included
|
||||
tiles = [tile_name]
|
||||
if tile_type.startswith('LIOB'):
|
||||
tiles.append(tiles_by_grid[(grid_x + 1, grid_y)])
|
||||
# tiles.append(tiles_by_grid[(grid_x + 4, grid_y)])
|
||||
else:
|
||||
tiles.append(tiles_by_grid[(grid_x - 1, grid_y)])
|
||||
# tiles.append(tiles_by_grid[(grid_x - 4, grid_y)])
|
||||
|
||||
add_segment(
|
||||
name="SEG_" + tile_name,
|
||||
tiles=tiles,
|
||||
segtype=tile_type.lower(),
|
||||
baseaddr=tile_baseaddrs.get(tile_name, None))
|
||||
|
||||
def process_bram_dsp():
|
||||
for k in range(5):
|
||||
if tile_type in ["BRAM_L", "DSP_L"]:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x + 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)]
|
||||
elif tile_type in ["BRAM_R", "DSP_R"]:
|
||||
interface_tile_name = tiles_by_grid[(
|
||||
grid_x - 1, grid_y - k)]
|
||||
int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)]
|
||||
else:
|
||||
assert 0
|
||||
'''
|
||||
BRAM/DSP itself is at the base y address
|
||||
There is one huge switchbox on the right for the 5 tiles
|
||||
These fan into 5 BRAM_INT_INTERFACE tiles each which feed into their own CENTER_INTER (just like a CLB has)
|
||||
'''
|
||||
if k == 0:
|
||||
tiles = [tile_name, interface_tile_name, int_tile_name]
|
||||
baseaddr = tile_baseaddrs.get(tile_name, None)
|
||||
else:
|
||||
tiles = [interface_tile_name, int_tile_name]
|
||||
baseaddr = None
|
||||
|
||||
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,
|
||||
"RIOB33": process_iob,
|
||||
"LIOB33": process_iob,
|
||||
"RIOB33_SING": process_iob_sing,
|
||||
"LIOB33_SING": process_iob_sing,
|
||||
}.get(nolr(tile_type), process_default)()
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
def get_inttile(database, segment):
|
||||
'''Return interconnect tile for given segment'''
|
||||
return (
|
||||
tile for tile in segment["tiles"]
|
||||
if database[tile]["type"] in ["INT_L", "INT_R"])
|
||||
|
||||
|
||||
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 create_segment_for_int_lr(
|
||||
database, segments, tile, tiles_by_grid, verbose):
|
||||
""" Creates INT_[LR] segment for interconnect's without direct connectivity. """
|
||||
# Some INT_[LR] tiles have no adjacent connectivity, create a segment.
|
||||
grid_x = database[tile]["grid_x"]
|
||||
grid_y = database[tile]["grid_y"]
|
||||
if database[tile]["type"] == "INT_L":
|
||||
grid_x -= 1
|
||||
adjacent_tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
elif database[tile]["type"] == "INT_R":
|
||||
grid_x += 1
|
||||
adjacent_tile = tiles_by_grid[(grid_x, grid_y)]
|
||||
else:
|
||||
assert False, database[tile]["type"]
|
||||
|
||||
if (database[adjacent_tile]['type'].startswith('INT_INTERFACE_') or
|
||||
database[adjacent_tile]['type'].startswith('PCIE_INT_INTERFACE_')
|
||||
or
|
||||
database[adjacent_tile]['type'].startswith('GTP_INT_INTERFACE')):
|
||||
# This INT_[LR] tile has no adjacent connectivity,
|
||||
# create a segment.
|
||||
add_segment(
|
||||
database=database,
|
||||
segments=segments,
|
||||
name='SEG_' + tile,
|
||||
tiles=[tile],
|
||||
segtype=database[tile]["type"],
|
||||
verbose=verbose,
|
||||
)
|
||||
else:
|
||||
assert False, database[adjacent_tile]['type']
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
for inttile in get_inttile(database, segment):
|
||||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
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
|
||||
|
||||
# ROI at edge?
|
||||
if (grid_x, grid_y) not in tiles_by_grid:
|
||||
verbose and print(' Skip edge')
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
if "segment" not in database[tile]:
|
||||
continue
|
||||
|
||||
seg = database[tile]["segment"]
|
||||
|
||||
seg_baseaddrs = segments[seg].setdefault("baseaddr", {})
|
||||
# At least one duplicate when we re-compute the entry for the base address
|
||||
# should give the same address
|
||||
if block_type in seg_baseaddrs:
|
||||
assert seg_baseaddrs[block_type] == [
|
||||
framebase, wordbase
|
||||
], (seg_baseaddrs[block_type], [framebase, wordbase])
|
||||
verbose and print(' Existing OK')
|
||||
else:
|
||||
seg_baseaddrs[block_type] = [framebase, wordbase]
|
||||
verbose and print(' Add new')
|
||||
|
||||
|
||||
def seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=False):
|
||||
'''Populate segment base addresses: Up along INT/HCLK columns'''
|
||||
|
||||
verbose and print('')
|
||||
# Copy the initial list containing only base addresses
|
||||
# and soon to have derived addresses
|
||||
src_segment_names = list()
|
||||
for segment_name in segments.keys():
|
||||
if "baseaddr" in segments[segment_name]:
|
||||
src_segment_names.append(segment_name)
|
||||
|
||||
verbose and print('up_INT: %u base addresses' % len(src_segment_names))
|
||||
|
||||
for src_segment_name in sorted(src_segment_names):
|
||||
src_segment = segments[src_segment_name]
|
||||
|
||||
for block_type, (framebase,
|
||||
wordbase) in sorted(src_segment["baseaddr"].items()):
|
||||
verbose and print(
|
||||
'up_INT: %s: %s.0x%08X:%u' %
|
||||
(src_segment_name, block_type, framebase, wordbase))
|
||||
|
||||
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
|
||||
'''
|
||||
|
||||
for inttile in get_inttile(database, src_segment):
|
||||
verbose and print(
|
||||
' up_INT CLK_IO_CLK: %s => inttile %s' %
|
||||
(src_segment_name, inttile),
|
||||
file=sys.stderr)
|
||||
grid_x = database[inttile]["grid_x"]
|
||||
grid_y = database[inttile]["grid_y"]
|
||||
|
||||
for i in range(50):
|
||||
grid_y -= 1
|
||||
loc = (grid_x, grid_y)
|
||||
|
||||
if loc not in tiles_by_grid:
|
||||
continue
|
||||
|
||||
dst_tile = database[tiles_by_grid[loc]]
|
||||
|
||||
if 'segment' not in dst_tile:
|
||||
continue
|
||||
#assert 'segment' in dst_tile, ((grid_x, grid_y), dst_tile, 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
|
||||
|
||||
# FIXME: PCIE block cuts out some BRAM
|
||||
# this messes up algorithm as is and may cause this to fail
|
||||
dst_tile_name = tiles_by_grid[(grid_x, grid_y)]
|
||||
|
||||
dst_tile = database[dst_tile_name]
|
||||
assert nolr(dst_tile['type']) == 'BRAM', dst_tile
|
||||
|
||||
dst_segment_name = dst_tile["segment"]
|
||||
verbose and print(
|
||||
' up_INT BLOCK_RAM: %s => %s' %
|
||||
(dst_segment_name, dst_tile_name))
|
||||
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
|
||||
|
||||
# related to words...
|
||||
# deprecated field? Don't worry about for now
|
||||
# DSP has some differences between height and words
|
||||
block["words"] = words
|
||||
if height is None:
|
||||
height = words
|
||||
block["height"] = height
|
||||
|
||||
|
||||
def db_add_bits(database, segments):
|
||||
'''Transfer segment data into tiles'''
|
||||
for segment_name in segments.keys():
|
||||
if 'baseaddr' not in segments[segment_name]:
|
||||
continue
|
||||
|
||||
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, 10, None),
|
||||
("BRAM", "BLOCK_RAM"): (128, 10, 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),
|
||||
|
||||
# IOB
|
||||
# design_IOB_X0Y100.delta:+bit_00020027_000_29
|
||||
# design_IOB_X0Y104.delta:+bit_00020027_008_29
|
||||
# design_IOB_X0Y112.delta:+bit_00020027_024_29
|
||||
# design_IOB_X0Y120.delta:+bit_00020027_040_29
|
||||
# design_IOB_X0Y128.delta:+bit_00020027_057_29
|
||||
# design_IOB_X0Y136.delta:+bit_00020027_073_29
|
||||
# design_IOB_X0Y144.delta:+bit_00020027_089_29
|
||||
# $XRAY_BLOCKWIDTH design_IOB_X0Y100.bit |grep 00020000
|
||||
# 0x00020000: 0x2A (42)
|
||||
("RIOI3", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("LIOI3", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("RIOI3_SING", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("LIOI3_SING", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("RIOI3_TBYTESRC", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("LIOI3_TBYTESRC", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("RIOI3_TBYTETERM", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("LIOI3_TBYTETERM", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("LIOB33", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("RIOB33", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("LIOB33", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("RIOB33_SING", "CLB_IO_CLK"): (42, 2, 2),
|
||||
("LIOB33_SING", "CLB_IO_CLK"): (42, 2, 2),
|
||||
}.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(json_in_fn, json_out_fn, tiles_fn, deltas_fns, verbose=False):
|
||||
# Load input files
|
||||
tiles = load_tiles(tiles_fn)
|
||||
site_baseaddr = load_baseaddrs(deltas_fns)
|
||||
|
||||
database = json.load(open(json_in_fn, "r"))
|
||||
|
||||
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_out_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(
|
||||
"--json-in",
|
||||
default="tiles_basic.json",
|
||||
help="Input .json without addresses")
|
||||
parser.add_argument(
|
||||
"--json-out", default="tilegrid.json", 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.json_in, args.json_out, args.tiles, deltas, verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
source "$::env(FUZDIR)/util.tcl"
|
||||
|
||||
proc loc_iob {} {
|
||||
# Some pads are output only (ex: OPAD_X0Y0/PAD) => filt IOB_*
|
||||
# XXX: GTX bank missing, deal with that later
|
||||
set roi_sites [get_sites -of_objects [get_pblocks roi]]
|
||||
set duts [get_bels -of_objects $roi_sites -filter {TYPE =~ PAD && NAME =~ IOB_*}]
|
||||
|
||||
# Sort them into CMT columns
|
||||
set dut_columns [group_dut_cols $duts 50]
|
||||
# Assign one from each
|
||||
return [loc_dut_col_sites $dut_columns {di_bufs[} {].ibuf} ]
|
||||
}
|
||||
|
||||
proc write_iob { sel_iob_sites } {
|
||||
foreach site $sel_iob_sites {
|
||||
puts ""
|
||||
set port [get_ports -of_objects $site]
|
||||
set tile [get_tiles -of_objects $site]
|
||||
set pin [get_property PACKAGE_PIN $port]
|
||||
puts "IOB $port $site $tile $pin"
|
||||
set orig_init [get_property PULLTYPE $port]
|
||||
set_property PULLTYPE PULLUP $port
|
||||
write_bitstream -force design_$site.bit
|
||||
set_property PULLTYPE "$orig_init" $port
|
||||
}
|
||||
}
|
||||
|
||||
proc run {} {
|
||||
make_project
|
||||
set sel_iob_sites [loc_iob]
|
||||
puts "Selected IOBs: [llength $sel_iob_sites]"
|
||||
|
||||
place_design
|
||||
route_design
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
|
||||
write_iob $sel_iob_sites
|
||||
}
|
||||
|
||||
run
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
source "$::env(FUZDIR)/util.tcl"
|
||||
|
||||
proc write_tiles_txt {} {
|
||||
# Get all tiles, ie not just the selected LUTs
|
||||
set tiles [get_tiles]
|
||||
|
||||
# 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 run {} {
|
||||
# Generate grid of entire part
|
||||
make_project_roi XRAY_ROI_TILEGRID
|
||||
|
||||
place_design
|
||||
route_design
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
|
||||
write_tiles_txt
|
||||
}
|
||||
|
||||
run
|
||||
|
||||
|
|
@ -1,10 +1,27 @@
|
|||
//Need at least one LUT per frame base address we want
|
||||
`ifndef N_LUT
|
||||
`define N_LUT 100
|
||||
`define N_BRAM 8
|
||||
`endif
|
||||
|
||||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 8;
|
||||
localparam integer DOUT_N = `N_LUT + `N_BRAM;
|
||||
`ifndef N_BRAM
|
||||
`define N_BRAM 8
|
||||
`endif
|
||||
|
||||
`ifndef N_DI
|
||||
`define N_DI 1
|
||||
`endif
|
||||
|
||||
module top(input clk, stb, [DIN_N-1:0] di, output do);
|
||||
parameter integer DIN_N = `N_DI;
|
||||
parameter integer DOUT_N = `N_LUT + `N_BRAM;
|
||||
|
||||
wire [DIN_N-1:0] di_buf;
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < `N_LUT; i = i+1) begin:di_bufs
|
||||
IBUF ibuf(.I(di[i]), .O(di_buf[i]));
|
||||
end
|
||||
endgenerate
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
|
@ -13,7 +30,7 @@ module top(input clk, stb, di, output do);
|
|||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
din_shr <= {din_shr, di_buf};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
|
|
@ -25,7 +42,7 @@ module top(input clk, stb, di, output do);
|
|||
|
||||
roi roi (
|
||||
.clk(clk),
|
||||
.din(din),
|
||||
.din(din[7:0]),
|
||||
.dout(dout)
|
||||
);
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
proc min_ysite { duts_in_column } {
|
||||
# Given a list of sites, return the one with the lowest Y coordinate
|
||||
|
||||
set min_dut_y 9999999
|
||||
|
||||
foreach dut $duts_in_column {
|
||||
# Ex: SLICE_X2Y50/A6LUT
|
||||
# Ex: IOB_X1Y50
|
||||
regexp ".*_X([0-9]+)Y([0-9]+)" $dut match dut_x dut_y
|
||||
|
||||
if { $dut_y < $min_dut_y } {
|
||||
set selected_dut $dut
|
||||
set min_dut_y $dut_y
|
||||
}
|
||||
}
|
||||
return $selected_dut
|
||||
}
|
||||
|
||||
proc group_dut_cols { duts ypitch } {
|
||||
# Group a list of sites into pitch sized buckets
|
||||
# Ex: IOBs occur 75 to a CMT column
|
||||
# Set pitch to 75 to get 0-74 in one bucket, 75-149 in a second, etc
|
||||
# X0Y0 {IOB_X0Y49 IOB_X0Y48 IOB_X0Y47 ... }
|
||||
# Anything with a different x is automatically in a different bucket
|
||||
|
||||
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per CMT column)
|
||||
set dut_columns ""
|
||||
foreach dut $duts {
|
||||
# Ex: SLICE_X2Y50/A6LUT
|
||||
# Ex: IOB_X1Y50
|
||||
regexp ".*_X([0-9]+)Y([0-9]+)" $dut match dut_x dut_y
|
||||
|
||||
# 75 per column => 0, 75, 150, etc
|
||||
set y_column [expr ($dut_y / $ypitch) * $ypitch]
|
||||
dict append dut_columns "X${dut_x}Y${y_column}" "$dut "
|
||||
}
|
||||
return $dut_columns
|
||||
}
|
||||
|
||||
proc loc_dut_col_bels { dut_columns cellpre cellpost } {
|
||||
# set cellpre di
|
||||
|
||||
# Pick the smallest Y in each column and LOC a cell to it
|
||||
# cells must be named like $cellpre[$dut_index]
|
||||
# Return the selected sites
|
||||
|
||||
set ret_bels {}
|
||||
set dut_index 0
|
||||
|
||||
dict for {column duts_in_column} $dut_columns {
|
||||
set sel_bel_str [min_ysite $duts_in_column]
|
||||
set sel_bel [get_bels $sel_bel_str]
|
||||
if {"$sel_bel" == ""} {error "Bad bel $sel_bel from bel str $sel_bel_str"}
|
||||
set sel_site [get_sites -of_objects $sel_bel]
|
||||
if {"$sel_site" == ""} {error "Bad site $sel_site from bel $sel_bel"}
|
||||
|
||||
set cell [get_cells $cellpre$dut_index$cellpost]
|
||||
puts "LOCing cell $cell to site $sel_site (from bel $sel_bel)"
|
||||
set_property LOC $sel_site $cell
|
||||
|
||||
set dut_index [expr $dut_index + 1]
|
||||
lappend ret_bels $sel_bel
|
||||
}
|
||||
|
||||
return $ret_bels
|
||||
}
|
||||
|
||||
proc loc_dut_col_sites { dut_columns cellpre cellpost } {
|
||||
set bels [loc_dut_col_bels $dut_columns $cellpre $cellpost]
|
||||
set sites [get_sites -of_objects $bels]
|
||||
return $sites
|
||||
}
|
||||
|
||||
proc make_io_pad_sites {} {
|
||||
# get all possible IOB pins
|
||||
foreach pad [get_package_pins -filter "IS_GENERAL_PURPOSE == 1"] {
|
||||
set site [get_sites -of_objects $pad]
|
||||
if {[llength $site] == 0} {
|
||||
continue
|
||||
}
|
||||
if [string match IOB33* [get_property SITE_TYPE $site]] {
|
||||
dict append io_pad_sites $site $pad
|
||||
}
|
||||
}
|
||||
return $io_pad_sites
|
||||
}
|
||||
|
||||
proc make_iob_pads {} {
|
||||
set io_pad_sites [make_io_pad_sites]
|
||||
|
||||
set iopad ""
|
||||
dict for {key value} $io_pad_sites {
|
||||
# Some sites have more than one pad?
|
||||
lappend iopad [lindex $value 0]
|
||||
}
|
||||
return $iopad
|
||||
}
|
||||
|
||||
proc make_iob_sites {} {
|
||||
set io_pad_sites [make_io_pad_sites]
|
||||
|
||||
set sites ""
|
||||
dict for {key value} $io_pad_sites {
|
||||
lappend sites $key
|
||||
}
|
||||
return $sites
|
||||
}
|
||||
|
||||
proc assign_iobs_old {} {
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
}
|
||||
|
||||
proc assign_iobs {} {
|
||||
# Set all I/Os on the bus to valid values somewhere on the chip
|
||||
# The iob fuzzer sets these to more specific values
|
||||
|
||||
# All possible IOs
|
||||
set iopad [make_iob_pads]
|
||||
# Basic pins
|
||||
# XXX: not all pads are valid, but seems to be working for now
|
||||
# Maybe better to set to XRAY_PIN_* and take out of the list?
|
||||
set_property -dict "PACKAGE_PIN [lindex $iopad 0] IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN [lindex $iopad 1] IOSTANDARD LVCMOS33" [get_ports do]
|
||||
set_property -dict "PACKAGE_PIN [lindex $iopad 2] IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
|
||||
# din bus
|
||||
set fixed_pins 3
|
||||
set iports [get_ports di*]
|
||||
for {set i 0} {$i < [llength $iports]} {incr i} {
|
||||
set pad [lindex $iopad [expr $i+$fixed_pins]]
|
||||
set port [lindex $iports $i]
|
||||
set_property -dict "PACKAGE_PIN $pad IOSTANDARD LVCMOS33" $port
|
||||
}
|
||||
}
|
||||
|
||||
proc make_project {} {
|
||||
# Generate .bit only over ROI
|
||||
make_project_roi XRAY_ROI
|
||||
}
|
||||
|
||||
proc make_project_roi { roi_var } {
|
||||
# 6 CMTs in our reference part
|
||||
# What is the largest?
|
||||
set n_di 16
|
||||
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
|
||||
read_verilog "$::env(FUZDIR)/top.v"
|
||||
synth_design -top top -verilog_define N_DI=$n_di
|
||||
|
||||
assign_iobs
|
||||
|
||||
create_pblock roi
|
||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||
foreach roi "$::env($roi_var)" {
|
||||
puts "ROI: $roi"
|
||||
resize_pblock [get_pblocks roi] -add "$roi"
|
||||
}
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
set_param tcl.collectionResultDisplayLimit 0
|
||||
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
build
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
source "$::env(SRC_DIR)/template.tcl"
|
||||
|
||||
# set vals "0 4 8 12 16 24"
|
||||
# ERROR: [Common 17-69] Command failed: Illegal DRIVE_STRENGTH value '0' for standard 'LVCMOS33'.
|
||||
# Legal values: 4, 8, 12, 16
|
||||
set prop DRIVE
|
||||
set port [get_ports do]
|
||||
source "$::env(SRC_DIR)/sweep.tcl"
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
source "$::env(SRC_DIR)/template.tcl"
|
||||
|
||||
set prop IOSTANDARD
|
||||
set port [get_ports do]
|
||||
source "$::env(SRC_DIR)/sweep.tcl"
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
all: build/env build/roi_roi_io.diff \
|
||||
build/PULLTYPE/run.ok \
|
||||
build/SLEW/run.ok \
|
||||
build/DRIVE/run.ok \
|
||||
build/IOSTANDARD/run.ok \
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
# hard coded LOCs in .v
|
||||
build/env:
|
||||
test "$(XRAY_PART)" = "xc7a50tfgg484-1"
|
||||
|
||||
# Didn't work
|
||||
build/roi_io.diff:
|
||||
$(MAKE) -f diff.mk OUT_DIFF=build/roi_io.diff PRJL=roi_io_a PRJR=roi_io_b
|
||||
|
||||
# Didn't work
|
||||
build/roi_prop.diff:
|
||||
$(MAKE) -f diff.mk OUT_DIFF=build/roi_prop.diff PRJL=roi_prop_a PRJR=roi_prop_b
|
||||
|
||||
build/PULLTYPE/run.ok:
|
||||
PROJECT=PULLTYPE bash runme_tcl.sh
|
||||
diff build/PULLTYPE/design_{PULLDOWN,KEEPER}.bits || true
|
||||
diff build/PULLTYPE/design_{PULLDOWN,PULLUP}.bits || true
|
||||
diff build/PULLTYPE/design_{PULLDOWN,NONE}.bits || true
|
||||
|
||||
build/SLEW/run.ok:
|
||||
PROJECT=SLEW bash runme_tcl.sh
|
||||
|
||||
build/DRIVE/run.ok:
|
||||
PROJECT=DRIVE bash runme_tcl.sh
|
||||
|
||||
build/IOSTANDARD/run.ok:
|
||||
PROJECT=IOSTANDARD bash runme_tcl.sh
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
source "$::env(SRC_DIR)/template.tcl"
|
||||
|
||||
set port [get_ports di]
|
||||
|
||||
set_property PULLTYPE "" $port
|
||||
write_checkpoint -force design_NONE.dcp
|
||||
write_bitstream -force design_NONE.bit
|
||||
|
||||
set vals "KEEPER PULLUP PULLDOWN"
|
||||
foreach {val} $vals {
|
||||
set_property PULLTYPE $val $port
|
||||
write_checkpoint -force design_$val.dcp
|
||||
write_bitstream -force design_$val.bit
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# PULLTYPE
|
||||
|
||||
PULLTYPE 28 29 30
|
||||
NONE X
|
||||
KEEPER X X
|
||||
PULLDOWN
|
||||
PULLUP X X
|
||||
|
||||
|
||||
# DRIVE
|
||||
|
||||
DRIVE A00 A02 A08 A10 B09 B01
|
||||
0 FIXME
|
||||
4 X X X
|
||||
8 X X X
|
||||
12 X X X
|
||||
16 X X X
|
||||
24 FIXME
|
||||
|
||||
|
||||
# IOSTANDARD
|
||||
|
||||
Effects bits, TBD exactly how
|
||||
Sample output:
|
||||
|
||||
diff LVCMOS33.bits LVTTL.bits
|
||||
< bit_00020026_006_00
|
||||
> bit_00020026_006_08
|
||||
|
||||
diff LVCMOS33.bits PCI33_3.bits
|
||||
< bit_00020026_006_02
|
||||
< bit_00020026_006_18
|
||||
< bit_00020026_006_22
|
||||
> bit_00020026_006_10
|
||||
> bit_00020026_006_16
|
||||
> bit_00020026_006_20
|
||||
> bit_00020027_006_11
|
||||
> bit_00020027_006_15
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
source "$::env(SRC_DIR)/template.tcl"
|
||||
|
||||
# set vals "SLOW MEDIUM FAST"
|
||||
# ERROR: [Common 17-69] Command failed: Slew type 'MEDIUM' is not supported by I/O standard 'LVCMOS33'
|
||||
set prop SLEW
|
||||
set port [get_ports do]
|
||||
source "$::env(SRC_DIR)/sweep.tcl"
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
all: $(OUT_DIFF)
|
||||
|
||||
$(OUT_DIFF): build/$(PRJL)/design.bits build/$(PRJR)/design.bits
|
||||
diff build/$(PRJL)/design.bits build/$(PRJR)/design.bits >$(OUT_DIFF) || true
|
||||
|
||||
build/$(PRJL)/design.bits:
|
||||
PROJECT=$(PRJL) bash runme.sh
|
||||
|
||||
build/$(PRJR)/design.bits:
|
||||
PROJECT=$(PRJR) bash runme.sh
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
all: $(OUT_DIFF)
|
||||
|
||||
$(OUT_DIFF): build/$(PRJL)/design.bits build/$(PRJR)/design.bits
|
||||
diff build/$(PRJL)/design.bits build/$(PRJR)/design.bits >$(OUT_DIFF) || true
|
||||
|
||||
build/$(PRJL)/design.bits:
|
||||
PROJECT=$(PRJL) bash runme_tcl.sh
|
||||
|
||||
build/$(PRJR)/design.bits:
|
||||
PROJECT=$(PRJR) bash runme_tcl.sh
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
: "${PROJECT:?Need to set PROJECT non-empty}"
|
||||
|
||||
# Create build dir
|
||||
export SRC_DIR=$PWD
|
||||
BUILD_DIR=build/$PROJECT
|
||||
mkdir -p $BUILD_DIR
|
||||
cd $BUILD_DIR
|
||||
|
||||
export TOP_V=$SRC_DIR/top.v
|
||||
|
||||
vivado -mode batch -source $SRC_DIR/runme.tcl
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o design.bits -z -y design.bit
|
||||
test -z "$(fgrep CRITICAL vivado.log)"
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
create_project -force -part $::env(XRAY_PART) design design
|
||||
#read_verilog $::env(SRC_DIR)/$::env(PROJECT).v
|
||||
read_verilog $::env(TOP_V)
|
||||
synth_design -top top -flatten_hierarchy none -verilog_define ROI=$::env(PROJECT)
|
||||
|
||||
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 stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
# set_property roi/dut
|
||||
|
||||
create_pblock roi
|
||||
set_property EXCLUDE_PLACEMENT 1 [get_pblocks roi]
|
||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
|
||||
# set_property BITSTREAM.GENERAL.DEBUGBITSTREAM Yes [current_design]
|
||||
write_bitstream -force design.bit
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
: "${PROJECT:?Need to set PROJECT non-empty}"
|
||||
|
||||
# Create build dir
|
||||
export SRC_DIR=$PWD
|
||||
BUILD_DIR=build/$PROJECT
|
||||
mkdir -p $BUILD_DIR
|
||||
cd $BUILD_DIR
|
||||
|
||||
export TOP_V=$SRC_DIR/tcl.v
|
||||
|
||||
vivado -mode batch -source $SRC_DIR/$PROJECT.tcl
|
||||
for x in design*.bit; do
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${x}s -z -y $x
|
||||
done
|
||||
test -z "$(fgrep CRITICAL vivado.log)"
|
||||
touch run.ok
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Sweep all values of $prop on given I/O $port
|
||||
# Write out bitstream for all legal values
|
||||
|
||||
set vals [list_property_value $prop $port]
|
||||
foreach {val} $vals {
|
||||
puts $val
|
||||
# Not all listable properties are settable
|
||||
# Its easiest to try setting and see if it sticks
|
||||
set_property -quiet $prop $val $port
|
||||
set got [get_property $prop $port]
|
||||
if {"$got" != "$val"} {
|
||||
puts " Skipping: wanted $val, got $got"
|
||||
continue
|
||||
}
|
||||
if {[catch {write_bitstream -force design_$val.bit} issue]} {
|
||||
puts "WARNING failed to write: $issue"
|
||||
continue
|
||||
}
|
||||
# Only write checkpoints for acceptable bitstreams
|
||||
write_checkpoint -force design_$val.dcp
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 256;
|
||||
localparam integer DOUT_N = 256;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
|
||||
roi
|
||||
roi (
|
||||
.clk(clk),
|
||||
.din(din),
|
||||
.dout(dout)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module roi(input clk, input [255:0] din, output [255:0] dout);
|
||||
assign dout = din;
|
||||
endmodule
|
||||
|
||||
/*
|
||||
module top (input i, output o);
|
||||
assign o = i;
|
||||
endmodule
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# Create a simple design with a few IOs
|
||||
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog $::env(TOP_V)
|
||||
synth_design -top top -flatten_hierarchy none
|
||||
|
||||
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 stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
write_checkpoint -force design.dcp
|
||||
|
||||
# set port [create_port -direction OUT myport]
|
||||
# set_property -dict "PACKAGE_PIN D19 IOSTANDARD LVCMOS33" $port
|
||||
# set_property PULLTYPE PULLUP $port
|
||||
# set_property PULLTYPE PULLDOWN $port
|
||||
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
IOBUF
|
||||
Not a primitive?
|
||||
Looks like it has an OBUFT
|
||||
|
||||
|
||||
Output buffer family:
|
||||
OBUF
|
||||
OBUFDS
|
||||
OBUFT
|
||||
OBUFTDS
|
||||
*/
|
||||
|
||||
`ifndef ROI
|
||||
ERROR: must set ROI
|
||||
`endif
|
||||
|
||||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 256;
|
||||
localparam integer DOUT_N = 256;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
|
||||
`ROI
|
||||
roi (
|
||||
.clk(clk),
|
||||
.din(din),
|
||||
.dout(dout)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module roi_io_a(input clk, input [255:0] din, output [255:0] dout);
|
||||
assign dout[0] = din[0] & din[1];
|
||||
|
||||
IOBUF_INTERMDISABLE #(
|
||||
.DRIVE(12),
|
||||
.IBUF_LOW_PWR("TRUE"),
|
||||
.IOSTANDARD("DEFAULT"),
|
||||
.SLEW("SLOW"),
|
||||
.USE_IBUFDISABLE("TRUE")
|
||||
) IOBUF_INTERMDISABLE_inst (
|
||||
.O(1'b0),
|
||||
.IO(1'bz),
|
||||
.I(dout[8]),
|
||||
.IBUFDISABLE(1'b0),
|
||||
.INTERMDISABLE(1'b0),
|
||||
.T(1'b1));
|
||||
|
||||
endmodule
|
||||
|
||||
module roi_io_b(input clk, input [255:0] din, output [255:0] dout);
|
||||
assign dout[0] = din[0] & din[1];
|
||||
|
||||
wire onet;
|
||||
|
||||
IOBUF_INTERMDISABLE #(
|
||||
.DRIVE(12),
|
||||
.IBUF_LOW_PWR("FALSE"),
|
||||
.IOSTANDARD("DEFAULT"),
|
||||
.SLEW("SLOW"),
|
||||
.USE_IBUFDISABLE("FALSE")
|
||||
) IOBUF_INTERMDISABLE_inst (
|
||||
.O(onet),
|
||||
.IO(1'bz),
|
||||
.I(dout[8]),
|
||||
.IBUFDISABLE(1'b0),
|
||||
.INTERMDISABLE(1'b0),
|
||||
.T(1'b1));
|
||||
|
||||
PULLUP PULLUP_inst (
|
||||
.O(onet)
|
||||
);
|
||||
|
||||
IOBUF_INTERMDISABLE #(
|
||||
.DRIVE(12),
|
||||
.IBUF_LOW_PWR("FALSE"),
|
||||
.IOSTANDARD("DEFAULT"),
|
||||
.SLEW("SLOW"),
|
||||
.USE_IBUFDISABLE("FALSE")
|
||||
) i2 (
|
||||
.O(),
|
||||
.IO(1'bz),
|
||||
.I(dout[8]),
|
||||
.IBUFDISABLE(1'b0),
|
||||
.INTERMDISABLE(1'b0),
|
||||
.T(1'b1));
|
||||
|
||||
endmodule
|
||||
|
||||
/*
|
||||
For some reason this doesn't diff
|
||||
Was this optimized out?
|
||||
|
||||
ERROR: [Place 30-69] Instance roi/dut/OBUFT (OBUFT) is unplaced after IO placer
|
||||
ERROR: [Place 30-68] Instance roi/dut/OBUFT (OBUFT) is not placed
|
||||
*/
|
||||
|
||||
/*
|
||||
module roi_prop_a(input clk, input [255:0] din, output [255:0] dout);
|
||||
assign dout[0] = din[0] & din[1];
|
||||
|
||||
//(* LOC="D19", KEEP, DONT_TOUCH *)
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
IOBUF #(
|
||||
.DRIVE(8),
|
||||
.IBUF_LOW_PWR("TRUE"),
|
||||
.IOSTANDARD("DEFAULT"),
|
||||
.SLEW("SLOW")
|
||||
) dut (
|
||||
.O(dout[1]),
|
||||
.I(din[0]),
|
||||
.T(din[1]));
|
||||
endmodule
|
||||
|
||||
module roi_prop_b(input clk, input [255:0] din, output [255:0] dout);
|
||||
assign dout[0] = din[0] & din[1];
|
||||
|
||||
//(* LOC="D19", KEEP, DONT_TOUCH *)
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
IOBUF #(
|
||||
.DRIVE(12),
|
||||
.IBUF_LOW_PWR("TRUE"),
|
||||
.IOSTANDARD("DEFAULT"),
|
||||
.SLEW("SLOW")
|
||||
) dut (
|
||||
.O(dout[1]),
|
||||
.I(din[0]),
|
||||
.T(din[1]));
|
||||
endmodule
|
||||
*/
|
||||
|
||||
/*
|
||||
ERROR: [DRC REQP-1581] obuf_loaded: OBUFT roi/dut pin O drives one or more invalid loads. The loads are: dout_shr[1]_i_1
|
||||
ERROR: [Place 30-69] Instance roi/dut (OBUFT) is unplaced after IO placer
|
||||
hmm
|
||||
Abandoning verilog approach
|
||||
tcl seems to work well, just use it directly
|
||||
*/
|
||||
module roi_prop_a(input clk, input [255:0] din, output [255:0] dout);
|
||||
(* LOC="D19", KEEP, DONT_TOUCH *)
|
||||
//(* KEEP, DONT_TOUCH *)
|
||||
OBUFT #(
|
||||
.DRIVE(8),
|
||||
.IOSTANDARD("DEFAULT"),
|
||||
.SLEW("SLOW")
|
||||
) dut (
|
||||
//.O(dout[1]),
|
||||
.O(),
|
||||
.I(din[0]),
|
||||
.T(din[1]));
|
||||
endmodule
|
||||
|
||||
module roi_prop_b(input clk, input [255:0] din, output [255:0] dout);
|
||||
(* LOC="D19", KEEP, DONT_TOUCH *)
|
||||
//(* KEEP, DONT_TOUCH *)
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
OBUFT #(
|
||||
.DRIVE(12),
|
||||
.IOSTANDARD("DEFAULT"),
|
||||
.SLEW("SLOW")
|
||||
) dut (
|
||||
//.O(dout[1]),
|
||||
.O(),
|
||||
.I(din[0]),
|
||||
.T(din[1]));
|
||||
endmodule
|
||||
|
||||
|
||||
|
|
@ -8,12 +8,13 @@ fi
|
|||
set -ex
|
||||
|
||||
# for some reason on sourced script set -e doesn't work
|
||||
test $# = 1 || exit 1
|
||||
# Scripts may have additional arguments, but first is reserved for build directory
|
||||
test $# -ge 1 || exit 1
|
||||
test ! -e "$SPECN"
|
||||
SPECN=$1
|
||||
|
||||
rm -rf "$SPECN"
|
||||
mkdir "$SPECN"
|
||||
mkdir -p "$SPECN"
|
||||
cd "$SPECN"
|
||||
|
||||
export SEED="$(echo $SPECN | md5sum | cut -c1-8)"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
set -ex
|
||||
|
||||
FUZDIR=$PWD
|
||||
export FUZDIR=$PWD
|
||||
source ${XRAY_GENHEADER}
|
||||
|
||||
# Some projects have hard coded top.v, others are generated
|
||||
|
|
|
|||
Loading…
Reference in New Issue