From b501c10fa2f194f36258ccd460eca892ea045e57 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 12 Dec 2018 14:39:29 -0800 Subject: [PATCH] tilegrid iob: generate addresses automatically Signed-off-by: John McMaster --- fuzzers/005-tilegrid/Makefile | 14 ++- fuzzers/005-tilegrid/add_tdb.py | 127 ++++++++++++++++++++++ fuzzers/005-tilegrid/generate_full.py | 88 +++++++-------- fuzzers/005-tilegrid/iob/Makefile | 24 +++++ fuzzers/005-tilegrid/iob/bitsmaker.py | 30 ++++++ fuzzers/005-tilegrid/iob/generate.py | 44 ++++++++ fuzzers/005-tilegrid/iob/generate.sh | 21 ++++ fuzzers/005-tilegrid/iob/generate.tcl | 105 ++++++++++++++++++ fuzzers/005-tilegrid/iob/top.py | 148 ++++++++++++++++++++++++++ prjxray/bitstream.py | 32 ++++-- prjxray/util.py | 23 ++++ 11 files changed, 594 insertions(+), 62 deletions(-) create mode 100644 fuzzers/005-tilegrid/add_tdb.py create mode 100644 fuzzers/005-tilegrid/iob/Makefile create mode 100644 fuzzers/005-tilegrid/iob/bitsmaker.py create mode 100644 fuzzers/005-tilegrid/iob/generate.py create mode 100644 fuzzers/005-tilegrid/iob/generate.sh create mode 100644 fuzzers/005-tilegrid/iob/generate.tcl create mode 100644 fuzzers/005-tilegrid/iob/top.py diff --git a/fuzzers/005-tilegrid/Makefile b/fuzzers/005-tilegrid/Makefile index 5729f254..3ff18819 100644 --- a/fuzzers/005-tilegrid/Makefile +++ b/fuzzers/005-tilegrid/Makefile @@ -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 diff --git a/fuzzers/005-tilegrid/add_tdb.py b/fuzzers/005-tilegrid/add_tdb.py new file mode 100644 index 00000000..86fb595e --- /dev/null +++ b/fuzzers/005-tilegrid/add_tdb.py @@ -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() diff --git a/fuzzers/005-tilegrid/generate_full.py b/fuzzers/005-tilegrid/generate_full.py index ed886a51..d713e3c0 100644 --- a/fuzzers/005-tilegrid/generate_full.py +++ b/fuzzers/005-tilegrid/generate_full.py @@ -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 diff --git a/fuzzers/005-tilegrid/iob/Makefile b/fuzzers/005-tilegrid/iob/Makefile new file mode 100644 index 00000000..782dba6a --- /dev/null +++ b/fuzzers/005-tilegrid/iob/Makefile @@ -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 + diff --git a/fuzzers/005-tilegrid/iob/bitsmaker.py b/fuzzers/005-tilegrid/iob/bitsmaker.py new file mode 100644 index 00000000..02dfb66b --- /dev/null +++ b/fuzzers/005-tilegrid/iob/bitsmaker.py @@ -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)) diff --git a/fuzzers/005-tilegrid/iob/generate.py b/fuzzers/005-tilegrid/iob/generate.py new file mode 100644 index 00000000..598a0c3c --- /dev/null +++ b/fuzzers/005-tilegrid/iob/generate.py @@ -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() diff --git a/fuzzers/005-tilegrid/iob/generate.sh b/fuzzers/005-tilegrid/iob/generate.sh new file mode 100644 index 00000000..7d49e204 --- /dev/null +++ b/fuzzers/005-tilegrid/iob/generate.sh @@ -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 + diff --git a/fuzzers/005-tilegrid/iob/generate.tcl b/fuzzers/005-tilegrid/iob/generate.tcl new file mode 100644 index 00000000..568febf1 --- /dev/null +++ b/fuzzers/005-tilegrid/iob/generate.tcl @@ -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 diff --git a/fuzzers/005-tilegrid/iob/top.py b/fuzzers/005-tilegrid/iob/top.py new file mode 100644 index 00000000..ba4b1748 --- /dev/null +++ b/fuzzers/005-tilegrid/iob/top.py @@ -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() diff --git a/prjxray/bitstream.py b/prjxray/bitstream.py index 834f83fe..e0d7d123 100644 --- a/prjxray/bitstream.py +++ b/prjxray/bitstream.py @@ -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() diff --git a/prjxray/util.py b/prjxray/util.py index 87278062..8329f496 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -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