From e995034158663d2ca80f3e825a22ffaac5496326 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 17 Jan 2018 14:39:37 -0800 Subject: [PATCH] fasm2frame FPGA assembler tool. Remove minitest rel path Signed-off-by: John McMaster --- minitests/util/common.mk | 2 +- minitests/util/runme.sh | 2 +- tools/fasm2frame.py | 242 ++++++++++++++++++++++++++++ tools/test_data/clb_ff/.gitignore | 8 + tools/test_data/clb_ff/design.bits | 24 +++ tools/test_data/clb_ff/top.v | 39 +++++ tools/test_data/clb_lut/.gitignore | 8 + tools/test_data/clb_lut/design.bits | 31 ++++ tools/test_data/clb_lut/top.v | 42 +++++ tools/test_data/ff_int.fasm | 24 +++ tools/test_data/lut.fasm | 15 ++ tools/test_data/lut_int.fasm | 35 ++++ tools/test_fasm2frame.py | 101 ++++++++++++ 13 files changed, 571 insertions(+), 2 deletions(-) create mode 100644 tools/fasm2frame.py create mode 100644 tools/test_data/clb_ff/.gitignore create mode 100644 tools/test_data/clb_ff/design.bits create mode 100644 tools/test_data/clb_ff/top.v create mode 100644 tools/test_data/clb_lut/.gitignore create mode 100644 tools/test_data/clb_lut/design.bits create mode 100644 tools/test_data/clb_lut/top.v create mode 100644 tools/test_data/ff_int.fasm create mode 100644 tools/test_data/lut.fasm create mode 100644 tools/test_data/lut_int.fasm create mode 100644 tools/test_fasm2frame.py diff --git a/minitests/util/common.mk b/minitests/util/common.mk index 07cce754..95a101e5 100644 --- a/minitests/util/common.mk +++ b/minitests/util/common.mk @@ -1,5 +1,5 @@ all: - bash ../util/runme.sh + bash $$XRAY_DIR/minitests/util/runme.sh clean: rm -rf specimen_[0-9][0-9][0-9]/ seg_clblx.segbits vivado*.log vivado_*.str vivado*.jou design *.bits *.dcp *.bit design.txt .Xil diff --git a/minitests/util/runme.sh b/minitests/util/runme.sh index 8abd5acf..1cacff2d 100755 --- a/minitests/util/runme.sh +++ b/minitests/util/runme.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ex -vivado -mode batch -source ../util/runme.tcl +vivado -mode batch -source $XRAY_DIR/minitests/util/runme.tcl ${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o design.bits -z -y design.bit test -z "$(fgrep CRITICAL vivado.log)" ${XRAY_SEGPRINT} -z -D design.bits >design.txt diff --git a/tools/fasm2frame.py b/tools/fasm2frame.py new file mode 100644 index 00000000..27c2e255 --- /dev/null +++ b/tools/fasm2frame.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 + +import os +import re +import sys +import json + +# Based on segprint function +# Modified to return dict instead of list +segbitsdb = dict() + + +def get_database(segtype): + if segtype in segbitsdb: + return segbitsdb[segtype] + + segbitsdb[segtype] = {} + + with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"), + os.getenv("XRAY_DATABASE"), segtype), + "r") as f: + for line in f: + # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 + parts = line.split() + name = parts[0] + vals = parts[1:] + segbitsdb[segtype][name] = vals + + with open("%s/%s/segbits_int_%s.db" % + (os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), + segtype[-1]), "r") as f: + for line in f: + # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 + parts = line.split() + name = parts[0] + vals = parts[1:] + segbitsdb[segtype][name] = vals + + return segbitsdb[segtype] + + +def dump_frames_verbose(frames): + print() + print("Frames: %d" % len(frames)) + for addr in sorted(frames.keys()): + words = frames[addr] + print( + '0x%08X ' % addr + ', '.join(['0x%08X' % w + for w in words]) + '...') + + +def dump_frames_sparse(frames): + print() + print("Frames: %d" % len(frames)) + for addr in sorted(frames.keys()): + words = frames[addr] + + # Skip frames without filled words + for w in words: + if w: + break + else: + continue + + print('Frame @ 0x%08X' % addr) + for i, w in enumerate(words): + if w: + print(' % 3d: 0x%08X' % (i, w)) + + +def dump_frm(f, frames): + '''Write a .frm file given a list of frames, each containing a list of 101 32 bit words''' + for addr in sorted(frames.keys()): + words = frames[addr] + f.write( + '0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n') + + +def run(f_in, f_out, sparse=False, debug=False): + # address to array of 101 32 bit words + frames = {} + + def frames_init(): + '''Set all frames to 0''' + for segj in grid['segments'].values(): + seg_baseaddr, seg_word_base = segj['baseaddr'] + seg_baseaddr = int(seg_baseaddr, 0) + for coli in range(segj['frames']): + frame_init(seg_baseaddr + coli) + + def frame_init(addr): + '''Set given frame to 0''' + if not addr in frames: + frames[addr] = [0 for _i in range(101)] + + def frame_set(frame_addr, word_addr, bit_index): + '''Set given bit in given frame address and word''' + frames[frame_addr][word_addr] |= 1 << bit_index + + with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"), + os.getenv("XRAY_DATABASE")), "r") as f: + grid = json.load(f) + + if not sparse: + # Initiaize bitstream to 0 + frames_init() + + for l in f_in: + # Comment + # Remove all text including and after # + i = l.rfind('#') + if i >= 0: + l = l[0:i] + l = l.strip() + + # Ignore blank lines + if not l: + continue + + # tile.site.stuff value + # INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0 + m = re.match( + r'([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_.\[\]]+)[ ](.+)', + l) + if not m: + raise Exception("Bad line: %s" % l) + tile = m.group(1) + site = m.group(2) + suffix = m.group(3) + value = m.group(4) + + tilej = grid['tiles'][tile] + seg = tilej['segment'] + segj = grid['segments'][seg] + seg_baseaddr, seg_word_base = segj['baseaddr'] + seg_baseaddr = int(seg_baseaddr, 0) + + # Ensure that all frames exist for this segment + # FIXME: type dependent + for coli in range(segj['frames']): + frame_init(seg_baseaddr + coli) + + # Now lets look up the bits we need frames for + segdb = get_database(segj['type']) + + def clb2dbkey(tile, tilej, site, suffix, value): + def slice_global2x01(tile_name, tile_type, site): + # SLICE_X12Y102 => SLICEL_X0 + m = re.match(r'SLICE_X([0-9]+)Y[0-9]+', site) + xg = int(m.group(1)) + + prefix = { + 'CLBLL_L': { + 0: 'SLICEL', + 1: 'SLICEL' + }, + 'CLBLM_L': { + 0: 'SLICEM', + 1: 'SLICEL' + }, + 'CLBLL_R': { + 0: 'SLICEL', + 1: 'SLICEL' + }, + 'CLBLM_R': { + 0: 'SLICEM', + 1: 'SLICEL' + }, + } + x01 = xg % 2 + return '%s_X%d' % (prefix[tile_type][x01], x01) + + db_site = slice_global2x01(tile, tilej['type'], site) + db_k = '%s.%s.%s' % (tilej['type'], db_site, suffix) + return db_k + + def int2dbkey(tile, tilej, site, suffix, value): + return '%s.%s.%s' % (tilej['type'], suffix, value) + + tile2dbkey = { + 'CLBLM_L': clb2dbkey, + 'CLBLM_R': clb2dbkey, + 'INT_L': int2dbkey, + 'INT_R': int2dbkey, + 'HCLK_L': int2dbkey, + } + + f = tile2dbkey.get(tilej['type'], None) + if f is None: + raise Exception("Unhandled segment type %s" % tilej['type']) + db_k = f(tile, tilej, site, suffix, value) + + try: + db_vals = segdb[db_k] + except KeyError: + raise Exception( + "Key %s (from line '%s') not found in segment DB %s" % + (db_k, l, segj['type'])) + + for val in db_vals: + # Default is 0. Skip explicit call outs + if val[0] == '!': + continue + # 28_05 => 28, 05 + seg_word_column, word_bit_n = val.split('_') + seg_word_column, word_bit_n = int(seg_word_column), int(word_bit_n) + # Now we have the word column and word bit index + # Combine with the segments relative frame position to fully get the position + frame_addr = seg_baseaddr + seg_word_column + # 2 words per segment + word_addr = seg_word_base + word_bit_n // 32 + bit_index = word_bit_n % 32 + frame_set(frame_addr, word_addr, bit_index) + + if debug: + #dump_frames_verbose(frames) + dump_frames_sparse(frames) + + dump_frm(f_out, frames) + + +if __name__ == '__main__': + import argparse + + parser = argparse.ArgumentParser( + description= + 'Convert FPGA configuration description ("FPGA assembly") into binary frame equivalent' + ) + + parser.add_argument( + '--sparse', action='store_true', help="Don't zero fill all frames") + parser.add_argument( + '--debug', action='store_true', help="Print debug dump") + parser.add_argument('fn_in', help='Input FPGA assembly (.fasm) file') + parser.add_argument('fn_out', help='Output FPGA frame (.frm) file') + + args = parser.parse_args() + run( + open(args.fn_in, 'r'), + open(args.fn_out, 'w'), + sparse=args.sparse, + debug=args.debug) diff --git a/tools/test_data/clb_ff/.gitignore b/tools/test_data/clb_ff/.gitignore new file mode 100644 index 00000000..231974d0 --- /dev/null +++ b/tools/test_data/clb_ff/.gitignore @@ -0,0 +1,8 @@ +/.Xil +/design/ +/design.bit +/design.dcp +/usage_statistics_webtalk.* +/vivado* + +!design.bits diff --git a/tools/test_data/clb_ff/design.bits b/tools/test_data/clb_ff/design.bits new file mode 100644 index 00000000..1efa3b42 --- /dev/null +++ b/tools/test_data/clb_ff/design.bits @@ -0,0 +1,24 @@ +bit_00020500_004_27 +bit_00020500_005_01 +bit_00020500_005_10 +bit_00020500_050_14 +bit_00020501_004_25 +bit_00020501_004_26 +bit_00020501_004_29 +bit_00020501_005_03 +bit_00020501_005_07 +bit_00020502_050_20 +bit_00020504_050_22 +bit_00020509_004_14 +bit_0002050f_004_14 +bit_00020511_004_15 +bit_00020512_004_06 +bit_00020514_005_00 +bit_00020516_004_15 +bit_00020518_004_07 +bit_00020518_004_15 +bit_00020519_004_15 +bit_00020519_005_00 +bit_0002051e_004_01 +bit_0002051e_004_12 +bit_0002051f_004_03 diff --git a/tools/test_data/clb_ff/top.v b/tools/test_data/clb_ff/top.v new file mode 100644 index 00000000..d3980122 --- /dev/null +++ b/tools/test_data/clb_ff/top.v @@ -0,0 +1,39 @@ +module top(input clk, stb, di, output do); + localparam integer DIN_N = 3; + localparam integer DOUT_N = 1; + + 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 [2:0] din, output [0:0] dout); + (* LOC="SLICE_X12Y102", BEL="AFF" *) + FDCE ff ( + .C(clk), + .Q(dout[0]), + .CE(din[0]), + .CLR(din[1]), + .D(din[2]) + ); +endmodule + diff --git a/tools/test_data/clb_lut/.gitignore b/tools/test_data/clb_lut/.gitignore new file mode 100644 index 00000000..231974d0 --- /dev/null +++ b/tools/test_data/clb_lut/.gitignore @@ -0,0 +1,8 @@ +/.Xil +/design/ +/design.bit +/design.dcp +/usage_statistics_webtalk.* +/vivado* + +!design.bits diff --git a/tools/test_data/clb_lut/design.bits b/tools/test_data/clb_lut/design.bits new file mode 100644 index 00000000..051aada0 --- /dev/null +++ b/tools/test_data/clb_lut/design.bits @@ -0,0 +1,31 @@ +bit_0002050b_004_14 +bit_0002050c_004_14 +bit_00020511_004_17 +bit_00020511_004_26 +bit_00020511_005_01 +bit_00020512_004_03 +bit_00020512_004_08 +bit_00020512_005_24 +bit_00020517_004_02 +bit_00020517_004_26 +bit_00020518_004_02 +bit_00020518_004_09 +bit_00020518_004_17 +bit_00020518_004_26 +bit_00020518_005_01 +bit_00020518_005_25 +bit_00020519_004_02 +bit_00020519_004_26 +bit_00020520_004_04 +bit_00020520_004_05 +bit_00020520_004_12 +bit_00020520_004_14 +bit_00020520_004_15 +bit_00020521_004_00 +bit_00020521_004_04 +bit_00020521_004_06 +bit_00020521_004_07 +bit_00020521_004_12 +bit_00020521_004_13 +bit_00020521_004_14 +bit_00020522_004_15 diff --git a/tools/test_data/clb_lut/top.v b/tools/test_data/clb_lut/top.v new file mode 100644 index 00000000..48d75a30 --- /dev/null +++ b/tools/test_data/clb_lut/top.v @@ -0,0 +1,42 @@ +module top(input clk, stb, di, output do); + localparam integer DIN_N = 6; + localparam integer DOUT_N = 1; + + 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 [5:0] din, output [0:0] dout); + (* LOC="SLICE_X12Y102", BEL="A6LUT", KEEP, DONT_TOUCH *) + LUT6 #( + .INIT(64'h8000_DEAD_0000_0001) + ) lutd ( + .I0(din[0]), + .I1(din[1]), + .I2(din[2]), + .I3(din[3]), + .I4(din[4]), + .I5(din[5]), + .O(dout[0])); +endmodule + diff --git a/tools/test_data/ff_int.fasm b/tools/test_data/ff_int.fasm new file mode 100644 index 00000000..222423ee --- /dev/null +++ b/tools/test_data/ff_int.fasm @@ -0,0 +1,24 @@ +# Loosely based on +# segprint -zd test_data/clb_ff/design.bits + +# FF as LDCE +CLBLM_L_X10Y102.SLICE_X12Y102.AFF.DMUX.AX 1 +CLBLM_L_X10Y102.SLICE_X12Y102.AFF.ZINI 1 +CLBLM_L_X10Y102.SLICE_X12Y102.AFF.ZRST 1 +CLBLM_L_X10Y102.SLICE_X12Y102.CEUSEDMUX 1 +CLBLM_L_X10Y102.SLICE_X12Y102.SRUSEDMUX 1 +# CLBLM_L_X10Y102.SLICE_X12Y102.FFSYNC 0 +# CLBLM_L_X10Y102.SLICE_X12Y102.LATCH 0 + +# Note: a number of pseudo pips here +# Omitted +INT_L_X10Y102.CENTER_INTER_L.BYP_ALT0 EE2END0 +INT_L_X10Y102.CENTER_INTER_L.BYP_ALT1 EL1END1 +INT_L_X10Y102.CENTER_INTER_L.CLK_L1 GCLK_L_B11_WEST +INT_L_X10Y102.CENTER_INTER_L.CTRL_L1 ER1END2 +INT_L_X10Y102.CENTER_INTER_L.FAN_ALT7 BYP_BOUNCE0 +INT_L_X10Y102.CENTER_INTER_L.WW2BEG0 LOGIC_OUTS_L4 + +HCLK_L_X31Y130.HCLK_L.ENABLE_BUFFER HCLK_CK_BUFHCLK8 +HCLK_L_X31Y130.HCLK_L.HCLK_LEAF_CLK_B_BOTL5 HCLK_CK_BUFHCLK8 + diff --git a/tools/test_data/lut.fasm b/tools/test_data/lut.fasm new file mode 100644 index 00000000..e4975449 --- /dev/null +++ b/tools/test_data/lut.fasm @@ -0,0 +1,15 @@ +# LUT +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[00] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[08] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[10] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[11] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[13] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[14] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[15] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[41] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[43] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[44] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[46] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[47] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[63] 1 + diff --git a/tools/test_data/lut_int.fasm b/tools/test_data/lut_int.fasm new file mode 100644 index 00000000..b4e32fd5 --- /dev/null +++ b/tools/test_data/lut_int.fasm @@ -0,0 +1,35 @@ +# Loosely based on +# segprint -zd test_data/clb_lut/design.bits + +# LUT +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[00] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[08] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[10] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[11] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[13] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[14] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[15] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[41] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[43] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[44] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[46] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[47] 1 +CLBLM_L_X10Y102.SLICE_X12Y102.ALUT.INIT[63] 1 + +# din bus +# din[0] +INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0 +# din[1] +INT_L_X10Y102.CENTER_INTER_L.IMUX_L2 EE2END1 +# din[2] +INT_L_X10Y102.CENTER_INTER_L.IMUX_L4 EE2END2 +# din[3] +INT_L_X10Y102.CENTER_INTER_L.IMUX_L7 EE2END3 +# din[4] +INT_L_X10Y102.CENTER_INTER_L.IMUX_L8 EL1END0 +# din[5] +INT_L_X10Y102.CENTER_INTER_L.IMUX_L11 EL1END1 + +# dout[0] +INT_L_X10Y102.CENTER_INTER_L.WW2BEG0 LOGIC_OUTS_L12 + diff --git a/tools/test_fasm2frame.py b/tools/test_fasm2frame.py new file mode 100644 index 00000000..4d6ef4f8 --- /dev/null +++ b/tools/test_fasm2frame.py @@ -0,0 +1,101 @@ +import fasm2frame + +import unittest +import StringIO +import re + + +def frm2bits(txt): + ''' + Convert output .frm file text to set of (frame addr, word #, bit index) tuples + ''' + bits_out = set() + for l in txt.split('\n'): + l = l.strip() + if not l: + continue + # 0x00020500 0x00000000,0x00000000,0x00000000,... + addr, words = l.split(' ') + addr = int(addr, 0) + words = words.split(',') + assert (101 == len(words)) + for wordi, word in enumerate(words): + word = int(word, 0) + for biti in xrange(32): + val = word & (1 << biti) + if val: + bits_out.add((addr, wordi, biti)) + return bits_out + + +def bitread2bits(txt): + ''' + Convert .bits text file (ie bitread output) to set of (frame addr, word #, bit index) tuples + ''' + bits_ref = set() + for l in txt.split('\n'): + l = l.strip() + if not l: + continue + # bit_0002050b_004_14 + m = re.match(r'bit_(.{8})_(.{3})_(.{2})', l) + addr = int(m.group(1), 16) + word = int(m.group(2), 10) + bit = int(m.group(3), 10) + bits_ref.add((addr, word, bit)) + return bits_ref + + +class TestStringMethods(unittest.TestCase): + def test_lut(self): + '''Simple smoke test on just the LUTs''' + fout = StringIO.StringIO() + fasm2frame.run(open('test_data/lut.fasm', 'r'), fout) + + def bitread_frm_equals(self, frm_fn, bitread_fn): + fout = StringIO.StringIO() + fasm2frame.run(open(frm_fn, 'r'), fout) + + # Build a list of output used bits + bits_out = frm2bits(fout.getvalue()) + + # Build a list of reference used bits + bits_ref = bitread2bits(open(bitread_fn, 'r').read()) + + # Now check for equivilence vs reference design + self.assertEquals(len(bits_ref), len(bits_out)) + self.assertEquals(bits_ref, bits_out) + + def test_lut_int(self): + self.bitread_frm_equals( + 'test_data/lut_int.fasm', 'test_data/clb_lut/design.bits') + + def test_ff_int(self): + self.bitread_frm_equals( + 'test_data/ff_int.fasm', 'test_data/clb_ff/design.bits') + + def test_sparse(self): + '''Verify sparse equivilent to normal encoding''' + frm_fn = 'test_data/lut_int.fasm' + + fout_sparse = StringIO.StringIO() + fasm2frame.run(open(frm_fn, 'r'), fout_sparse, sparse=True) + fout_sparse_txt = fout_sparse.getvalue() + bits_sparse = frm2bits(fout_sparse_txt) + + fout_full = StringIO.StringIO() + fasm2frame.run(open(frm_fn, 'r'), fout_full, sparse=False) + fout_full_txt = fout_full.getvalue() + bits_full = frm2bits(fout_full_txt) + + # Now check for equivilence vs reference design + self.assertEquals(len(bits_sparse), len(bits_full)) + self.assertEquals(bits_sparse, bits_full) + + # Verify the full ROI is way bigger description + # It will still be decent size though since even sparse occupies all columns in that area + self.assertGreaterEqual(len(fout_full_txt), len(fout_sparse_txt) * 4) + + +if __name__ == '__main__': + unittest.main()