Merge pull request #280 from mcmasterg/iob

tilegrid IOB
This commit is contained in:
John Mcmaster 2018-12-03 22:03:33 -08:00 committed by GitHub
commit 78fa4bd56e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1585 additions and 786 deletions

View File

@ -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"

View File

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

View File

@ -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__':

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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]
}

1
minitests/iob/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

9
minitests/iob/DRIVE.tcl Normal file
View File

@ -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"

View File

@ -0,0 +1,6 @@
source "$::env(SRC_DIR)/template.tcl"
set prop IOSTANDARD
set port [get_ports do]
source "$::env(SRC_DIR)/sweep.tcl"

37
minitests/iob/Makefile Normal file
View File

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

View File

@ -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
}

39
minitests/iob/README.md Normal file
View File

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

8
minitests/iob/SLEW.tcl Normal file
View File

@ -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"

11
minitests/iob/diff.mk Normal file
View File

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

11
minitests/iob/diff_tcl.mk Normal file
View File

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

18
minitests/iob/runme.sh Normal file
View File

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

31
minitests/iob/runme.tcl Normal file
View File

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

View File

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

22
minitests/iob/sweep.tcl Normal file
View File

@ -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
}

39
minitests/iob/tcl.v Normal file
View File

@ -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
*/

View File

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

183
minitests/iob/top.v Normal file
View File

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

View File

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

View File

@ -4,7 +4,7 @@
set -ex
FUZDIR=$PWD
export FUZDIR=$PWD
source ${XRAY_GENHEADER}
# Some projects have hard coded top.v, others are generated