tilegrid iob: generate addresses automatically

Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
John McMaster 2018-12-12 14:39:29 -08:00
parent 1eeb359120
commit b501c10fa2
11 changed files with 594 additions and 62 deletions

View File

@ -20,13 +20,21 @@ build/clb/deltas:
build/bram/deltas:
bash generate.sh build/bram bram
# FIXME: review IOB
build/iob/deltas:
bash generate.sh build/iob iob
build/tilegrid.json: generate_full.py build/tilegrid_basic.json build/clb/deltas build/bram/deltas build/iob/deltas
build/tilegrid_tdb.json: iob/build/segbits_tilegrid.tdb
python3 add_tdb.py --fn-in build/tilegrid_basic.json --fn-out build/tilegrid_tdb.json
iob/build/segbits_tilegrid.tdb: build/tilegrid_basic.json
cd iob && $(MAKE)
# FIXME: review IOB
build/tilegrid.json: generate_full.py build/tilegrid_tdb.json build/clb/deltas build/bram/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
--json-in tilegrid_tdb.json --json-out ${BUILD_DIR}/tilegrid.json \
--tiles $(FUZDIR)/build/tiles/tiles.txt {clb,bram}/design_*.delta
run:
$(MAKE) clean

View File

@ -0,0 +1,127 @@
#!/usr/bin/env python3
from prjxray import util
import json
# Copied from generate_full.py
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 = util.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 parse_addr(line):
# 00020027_003_03
line = line.split("_")
frame = int(line[0], 16)
wordidx = int(line[1], 10)
bitidx = int(line[2], 10)
return frame, wordidx, bitidx
def load_db(fn):
for l in open(fn, "r"):
l = l.strip()
# FIXME: add offset to name
# IOB_X0Y101.DFRAME:27.DWORD:3.DBIT:3 00020027_003_03
tagstr, addrstr = l.split(' ')
frame, wordidx, bitidx = parse_addr(addrstr)
bitidx_up = False
tparts = tagstr.split('.')
tile = tparts[0]
for part in tparts[1:]:
k, v = part.split(':')
if k == "DFRAME":
frame -= int(v, 16)
elif k == "DWORD":
wordidx -= int(v, 10)
elif k == "DBIT":
bitidx -= int(v, 10)
bitidx_up = True
else:
assert 0, (l, part)
# XXX: maybe just ignore bitidx and always set to 0 instead of allowing explicit
# or detect the first delta auto and assert they are all the same
if not bitidx_up:
bitidx = 0
assert bitidx == 0
assert frame % 0x100 == 0, "Unaligned frame"
yield (tile, frame, wordidx)
def run(fn_in, fn_out, verbose=False):
database = json.load(open(fn_in, "r"))
# Load a map of sites to base addresses
# Need to figure out the
# FIXME: generate frames from part file (or equivilent)
# See https://github.com/SymbiFlow/prjxray/issues/327
# FIXME: generate words from pitch
tdb_fns = [("iob/build/segbits_tilegrid.tdb", 42, 4)]
for (tdb_fn, frames, words) in tdb_fns:
for (tile, frame, wordidx) in load_db(tdb_fn):
tilej = database[tile]
bitsj = tilej['bits']
bt = util.addr2btype(frame)
verbose and print("Add %s %08X_%03u" % (tile, frame, wordidx))
add_tile_bits(tilej, frame, wordidx, frames, words)
# Save
json.dump(
database,
open(fn_out, "w"),
sort_keys=True,
indent=4,
separators=(",", ": "))
def main():
import argparse
parser = argparse.ArgumentParser(
description="Annotate tilegrid addresses using solved base addresses")
parser.add_argument("--verbose", action="store_true", help="")
parser.add_argument("--fn-in", required=True, help="")
parser.add_argument("--fn-out", required=True, help="")
args = parser.parse_args()
run(args.fn_in, args.fn_out, verbose=args.verbose)
if __name__ == "__main__":
main()

View File

@ -11,28 +11,7 @@ A post processing step verifies that two tiles don't reference the same bitstrea
'''
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]
from prjxray import util
def nolr(tile_type):
@ -77,7 +56,7 @@ def make_tile_baseaddrs(tiles, site_baseaddr, verbose=False):
if site_name not in site_baseaddr:
continue
framebaseaddr = site_baseaddr[site_name]
bt = addr2btype(framebaseaddr)
bt = util.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
@ -251,16 +230,19 @@ def make_segments(database, tiles_by_grid, tile_baseaddrs, verbose=False):
'make_segment: drop %s' % (tile_type, ))
pass
"""
FIXME: review IOB
"RIOB33": process_iob,
"LIOB33": process_iob,
"RIOB33_SING": process_iob_sing,
"LIOB33_SING": process_iob_sing,
"""
{
"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
@ -516,7 +498,7 @@ def add_tile_bits(tile_db, baseaddr, offset, frames, words, height=None):
'''
bits = tile_db['bits']
block_type = addr2btype(baseaddr)
block_type = util.addr2btype(baseaddr)
assert 0 <= offset <= 100, offset
assert 1 <= words <= 101
@ -554,6 +536,32 @@ def db_add_bits(database, segments):
offset) in segments[segment_name]["baseaddr"].items():
for tile_name in segments[segment_name]["tiles"]:
tile_type = database[tile_name]["type"]
"""
FIXME: review IOB
# 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, 4),
("LIOI3", "CLB_IO_CLK"): (42, 2, 4),
("RIOI3_SING", "CLB_IO_CLK"): (42, 2, 4),
("LIOI3_SING", "CLB_IO_CLK"): (42, 2, 4),
("RIOI3_TBYTESRC", "CLB_IO_CLK"): (42, 2, 4),
("LIOI3_TBYTESRC", "CLB_IO_CLK"): (42, 2, 4),
("RIOI3_TBYTETERM", "CLB_IO_CLK"): (42, 2, 4),
("LIOI3_TBYTETERM", "CLB_IO_CLK"): (42, 2, 4),
("LIOB33", "CLB_IO_CLK"): (42, 2, 4),
("RIOB33", "CLB_IO_CLK"): (42, 2, 4),
("LIOB33", "CLB_IO_CLK"): (42, 2, 4),
("RIOB33_SING", "CLB_IO_CLK"): (42, 2, 4),
("LIOB33_SING", "CLB_IO_CLK"): (42, 2, 4),
"""
entry = {
# (tile_type, block_type): (frames, words, height)
("CLBLL", "CLB_IO_CLK"): (36, 2, 2),
@ -565,30 +573,6 @@ def db_add_bits(database, segments):
("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

View File

@ -0,0 +1,24 @@
N := 15
SPECIMENS := $(addprefix build/specimen_,$(shell seq -f '%03.0f' $(N)))
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
database: build/segbits_tilegrid.tdb
build/segbits_tilegrid.tdb: $(SPECIMENS_OK)
${XRAY_SEGMATCH} -o build/segbits_tilegrid.tdb $$(find build -name "segdata_tilegrid.txt")
$(SPECIMENS_OK):
bash generate.sh $(subst /OK,,$@)
touch $@
run:
$(MAKE) clean
$(MAKE) database
$(MAKE) pushdb
touch run.ok
clean:
rm -rf build
.PHONY: database pushdb run clean

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
from prjxray import bitstream
def write(bits_fn, fnout, tags):
'''
seg 00020000_046
bit 18_20
bit 39_63
tag LIOB33.IOB_Y1.REFBIT 0
'''
fout = open(fnout, "w")
def line(s):
fout.write(s + "\n")
# Everything relative to start of bitstream
line("seg 00000000_000")
bitdata = bitstream.load_bitdata2(open(bits_fn, "r"))
for frame, words in bitdata.items():
for word, wbits in words.items():
for bitidx in sorted(list(wbits)):
# Are the names arbitrary? Lets just re-create
line("bit %08X_%03u_%02u" % (frame, word, bitidx))
for k, v in tags.items():
line("tag %s %u" % (k, v))

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
import bitsmaker
def run(bits_fn, design_fn, fnout, verbose=False):
# Raw: IOB_X0Y101 00020027_003_03
metastr = "DFRAME:27.DWORD:3.DBIT:3"
tags = dict()
f = open(design_fn, 'r')
f.readline()
for l in f:
l = l.strip()
port, site, tile, pin, val = l.split(',')
'''
PULLTYPE 28 29 30
NONE X
KEEPER X X
PULLDOWN
PULLUP X X
'''
tags["%s.%s" % (tile, metastr)] = val == "KEEPER"
bitsmaker.write(bits_fn, fnout, tags)
def main():
import argparse
parser = argparse.ArgumentParser(
description=
"Solve bits (like segmaker) on raw .bits file without segments")
parser.add_argument("--bits-file", default="design.bits", help="")
parser.add_argument("--verbose", action="store_true", help="")
parser.add_argument("--design", default="design.csv", help="")
parser.add_argument("--fnout", default="/dev/stdout", help="")
args = parser.parse_args()
run(args.bits_file, args.design, args.fnout, args.verbose)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,21 @@
#!/bin/bash
set -ex
export FUZDIR=$PWD
source ${XRAY_GENHEADER}
# Some projects have hard coded top.v, others are generated
if [ -f $FUZDIR/top.py ] ; then
python3 $FUZDIR/top.py >top.v
fi
vivado -mode batch -source $FUZDIR/generate.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
python3 $FUZDIR/generate.py >segdata_tilegrid.txt

View File

@ -0,0 +1,105 @@
source "$::env(XRAY_DIR)/utils/utils.tcl"
proc make_io_pin_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_pin_sites $site $pad
}
}
return $io_pin_sites
}
proc load_pin_lines {} {
# IOB_X0Y103 clk input
# IOB_X0Y129 do[0] output
set fp [open "params.csv" r]
set pin_lines {}
for {gets $fp line} {$line != ""} {gets $fp line} {
lappend pin_lines [split $line ","]
}
close $fp
return $pin_lines
}
proc loc_pins {} {
set pin_lines [load_pin_lines]
set io_pin_sites [make_io_pin_sites]
set fp [open "design.csv" w]
puts $fp "port,site,tile,pin,val"
puts "Looping"
for {set idx 0} {$idx < [llength $pin_lines]} {incr idx} {
set line [lindex $pin_lines $idx]
puts "$line"
set site_str [lindex $line 0]
set pin_str [lindex $line 1]
set io [lindex $line 2]
set cell_str [lindex $line 3]
# Have: site
# Want: pin for site
set site [get_sites $site_str]
set pad_bel [get_bels -of_objects $site -filter {TYPE =~ PAD && NAME =~ IOB_*}]
# set port [get_ports -of_objects $site]
set port [get_ports $pin_str]
set tile [get_tiles -of_objects $site]
set pin "FIXME"
set pin [dict get $io_pin_sites $site]
#set pin [get_property PACKAGE_PIN $port]
#set cell [get_cells $cell_str]
# puts "LOCing cell $cell to site $site (from bel $pad_bel)"
# set_property LOC $site $cell
set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS33" $port
# list_property isn't working
# set keys [list_property_value PULLTYPE $port]
set keys "NONE KEEPER"
set val [randsample_list 1 $keys]
if { $val == "NONE" } {
set val ""
}
set_property PULLTYPE $val $port
# puts "IOB $port $site $tile $pin $val"
puts $fp "$port,$site,$tile,$pin,$val"
}
close $fp
}
proc run {} {
create_project -force -part $::env(XRAY_PART) design design
read_verilog top.v
synth_design -top top
# Mostly doesn't matter since IOB are special, but add anyway
create_pblock roi
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
loc_pins
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
place_design
route_design
write_checkpoint -force design.dcp
write_bitstream -force design.bit
}
run

View File

@ -0,0 +1,148 @@
'''
Generate a primitive to place at every I/O
Unlike CLB tests, the LFSR for this is inside the ROI, not driving it
'''
import os
import random
random.seed(int(os.getenv("SEED"), 16))
from prjxray import util
from prjxray import verilog
def gen_iobs():
'''
IOB33S: main IOB of a diff pair
IOB33M: secondary IOB of a diff pair
IOB33: not a diff pair. Relatively rare (at least in ROI...2 of them?)
Focus on IOB33S to start
'''
for _tile_name, site_name, site_type in util.get_roi().gen_sites(
# ['IOB33', 'IOB33S']):
# FIXME: special cases on IOB33
['IOB33S']):
yield site_name, site_type
def write_pins(ports):
pinstr = ''
for site, (name, dir_, cell) in sorted(ports.items(), key=lambda x: x[1]):
# pinstr += 'set_property -dict "PACKAGE_PIN %s IOSTANDARD LVCMOS33" [get_ports %s]' % (packpin, port)
pinstr += '%s,%s,%s,%s\n' % (site, name, dir_, cell)
open('params.csv', 'w').write(pinstr)
def run():
# All possible values
iosites = {}
for site_name, site_type in gen_iobs():
iosites[site_name] = site_type
# Assigned in this design
ports = {}
DIN_N = 0
DOUT_N = 0
def remain_sites():
return set(iosites.keys()) - set(ports.keys())
def rand_site():
'''Get a random, unused site'''
return random.choice(list(remain_sites()))
def assign_i(site, name):
nonlocal DIN_N
assert site not in ports
cell = "di_bufs[%u].ibuf" % DIN_N
DIN_N += 1
ports[site] = (name, 'input', cell)
def assign_o(site, name):
nonlocal DOUT_N
assert site not in ports
cell = "do_bufs[%u].obuf" % DOUT_N
DOUT_N += 1
ports[site] = (name, 'output', cell)
# Assign at least one di and one do
assign_i(rand_site(), 'di[0]')
assign_o(rand_site(), 'do[0]')
# Now assign the rest randomly
while len(remain_sites()):
if random.randint(0, 1):
assign_i(rand_site(), 'di[%u]' % DIN_N)
else:
assign_o(rand_site(), 'do[%u]' % DOUT_N)
write_pins(ports)
print(
'''
`define N_DI %u
`define N_DO %u
module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do);
genvar i;
//Instantiate BUFs so we can LOC them
wire [`N_DI-1:0] di_buf;
generate
for (i = 0; i < `N_DI; i = i+1) begin:di_bufs
IBUF ibuf(.I(di[i]), .O(di_buf[i]));
end
endgenerate
wire [`N_DO-1:0] do_unbuf;
generate
for (i = 0; i < `N_DO; i = i+1) begin:do_bufs
OBUF obuf(.I(do_unbuf[i]), .O(do[i]));
end
endgenerate
roi roi(.di(di_buf), .do(do_unbuf));
endmodule
//Arbitrary terminate into LUTs
module roi(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do);
genvar i;
generate
for (i = 0; i < `N_DI; i = i+1) begin:dis
(* KEEP, DONT_TOUCH *)
LUT6 #(
.INIT(64'h8000_0000_0000_0001)
) lut (
.I0(di[i]),
.I1(di[i]),
.I2(di[i]),
.I3(di[i]),
.I4(di[i]),
.I5(di[i]),
.O());
end
endgenerate
generate
for (i = 0; i < `N_DO; i = i+1) begin:dos
(* KEEP, DONT_TOUCH *)
LUT6 #(
.INIT(64'h8000_0000_0000_0001)
) lut (
.I0(),
.I1(),
.I2(),
.I3(),
.I4(),
.I5(),
.O(do[i]));
end
endgenerate
endmodule
''' % (DIN_N, DOUT_N))
if __name__ == '__main__':
run()

View File

@ -3,10 +3,20 @@ WORD_SIZE_BITS = 32
# How many 32-bit words for frame in a 7-series bitstream?
FRAME_WORD_COUNT = 101
'''
Sample:
bit_0002000f_079_06
Where:
-0002000f: FDRI address
-079: FDIR word number (0-100)
-06: bit index (0-31)
'''
def load_bitdata(f):
""" Read bit file and return bitdata map.
Similar to segbits file
bitdata is a map of of two sets.
The map key is the frame address.
@ -34,16 +44,24 @@ def load_bitdata(f):
# used by segprint
# TODO: merge these
def load_bitdata2(f):
# these are not compatible
# return bitstream.load_bitdata(open(bits_file, "r"))
'''
return as bitdata[frame][wordidx].add(bitidx)
ie indexed by frame, word index, and then a set with bit indexes
Similar to .bits file: bit_00020012_014_20
'''
bitdata = dict()
for line in f:
line = line.split("_")
frame = int(line[1], 16)
wordidx = int(line[2], 10)
bitidx = int(line[3], 10)
for lineraw in f:
lineraw = lineraw.strip()
line = lineraw.split("_")
try:
frame = int(line[1], 16)
wordidx = int(line[2], 10)
bitidx = int(line[3], 10)
except:
print("Invalid line %s" % lineraw)
raise
if frame not in bitdata:
bitdata[frame] = dict()

View File

@ -124,6 +124,29 @@ def addr2str(addr, word, bit):
return "%08x_%03u_%02u" % (addr, word, bit)
# 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 gen_tile_bits(db_root, tilej, strict=False, verbose=False):
'''
For given tile yield