From 23813e3065551cec3013ecafa1d948fb45b1db13 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Mon, 22 Jan 2018 18:36:20 -0800 Subject: [PATCH] fasm2frame: optional value on simple elements Remove INT prefixes not in segdb Better test cases Signed-off-by: John McMaster --- tools/fasm2frame.py | 193 ++++++++++++++++++++++---------- tools/segprint2fasm.py | 79 +++++++------ tools/test_data/ff_int.fasm | 21 ++-- tools/test_data/ff_int_0s.fasm | 25 +++++ tools/test_data/ff_int_op1.fasm | 24 ++++ tools/test_data/lut_int.fasm | 14 +-- tools/test_fasm2frame.py | 59 ++++++++++ 7 files changed, 305 insertions(+), 110 deletions(-) create mode 100644 tools/test_data/ff_int_0s.fasm create mode 100644 tools/test_data/ff_int_op1.fasm diff --git a/tools/fasm2frame.py b/tools/fasm2frame.py index b288f310..10baa80d 100755 --- a/tools/fasm2frame.py +++ b/tools/fasm2frame.py @@ -4,9 +4,37 @@ import os import re import sys import json +import collections -# Based on segprint function -# Modified to return dict instead of list + +class FASMSyntaxError(Exception): + pass + + +def parsebit(val): + '''Return "!012_23" => (12, 23, False)''' + isset = True + # Default is 0. Skip explicit call outs + if val[0] == '!': + isset = False + val = val[1:] + # 28_05 => 28, 05 + seg_word_column, word_bit_n = val.split('_') + return int(seg_word_column), int(word_bit_n), isset + + +''' +Loosely based on segprint function +Maybe better to return as two distinct dictionaries? + +{ + 'tile.blah': {None: (12, 23)}, + 'tile.meh': { + 'O5': [(11, 2, False), (12, 2, True)], + 'O6': [(11, 2, True), (12, 2, False)], + }, +} +''' segbitsdb = dict() @@ -16,25 +44,49 @@ def get_database(segtype): segbitsdb[segtype] = {} + def process(l): + l = l.strip() + + # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 + parts = line.split() + name = parts[0] + bit_vals = parts[1:] + + # Assumption + # only 1 bit => non-enumerated value + if len(bit_vals) == 1: + seg_word_column, word_bit_n, isset = parsebit(bit_vals[0]) + if not isset: + raise Exception( + "Expect single bit DB entries to be set, got %s" % l) + segbitsdb[segtype][name] = {None: (seg_word_column, word_bit_n)} + else: + # An enumerated value + # Split the base name and selected key + m = re.match(r'(.+)[.](.+)', name) + name = m.group(1) + key = m.group(2) + # May or may not be the first key encountered + m = segbitsdb[segtype].get(name, {}) + segbitsdb[segtype][name] = m + l = [] + m[key] = l + #print("Add %s.%s" % (name, key)) + for bit_val in bit_vals: + seg_word_column, word_bit_n, isset = parsebit(bit_val) + l.append((seg_word_column, word_bit_n, isset)) + 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 + process(line) 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 + process(line) return segbitsdb[segtype] @@ -79,6 +131,10 @@ def dump_frm(f, frames): def run(f_in, f_out, sparse=False, debug=False): # address to array of 101 32 bit words frames = {} + # Directives we've seen so far + # Complain if there is a duplicate + # Contains line number of last entry + used_names = {} def frames_init(): '''Set all frames to 0''' @@ -97,6 +153,10 @@ def run(f_in, f_out, sparse=False, debug=False): '''Set given bit in given frame address and word''' frames[frame_addr][word_addr] |= 1 << bit_index + def frame_clear(frame_addr, word_addr, bit_index): + '''Set given bit in given frame address and word''' + frames[frame_addr][word_addr] &= 0xFFFFFFFF ^ (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) @@ -105,7 +165,7 @@ def run(f_in, f_out, sparse=False, debug=False): # Initiaize bitstream to 0 frames_init() - for l in f_in: + for line_number, l in enumerate(f_in, 1): # Comment # Remove all text including and after # i = l.rfind('#') @@ -119,16 +179,22 @@ def run(f_in, f_out, sparse=False, debug=False): # 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) + # Optional value + m = re.match(r'([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_.\[\]]+)([ ](.+))?', l) if not m: - raise Exception("Bad line: %s" % l) + raise FASMSyntaxError("Bad line: %s" % l) tile = m.group(1) - site = m.group(2) - suffix = m.group(3) + name = m.group(2) value = m.group(4) + used_name = (tile, name) + old_line_number = used_names.get(used_name, None) + if old_line_number: + raise FASMSyntaxError( + "Duplicate name lines %d and %d, second line: %s" % + (old_line_number, line_number, l)) + used_names[used_name] = line_number + tilej = grid['tiles'][tile] seg = tilej['segment'] segj = grid['segments'][seg] @@ -140,53 +206,60 @@ def run(f_in, f_out, sparse=False, debug=False): 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): - db_k = '%s.%s.%s' % (tilej['type'], site, suffix) - return db_k - - def int2dbkey(tile, tilej, site, suffix, value): - return '%s.%s.%s' % (tilej['type'], suffix, value) - - tile2dbkey = { - 'CLBLL_L': clb2dbkey, - 'CLBLL_R': clb2dbkey, - 'CLBLM_L': clb2dbkey, - 'CLBLM_R': clb2dbkey, - 'INT_L': int2dbkey, - 'INT_R': int2dbkey, - 'HCLK_L': int2dbkey, - 'HCLK_R': 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) + def update_segbit(seg_word_column, word_bit_n, isset): + '''Set or clear a single bit in a segment at the given word column and word bit position''' # 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 isset: + frame_set(frame_addr, word_addr, bit_index) + else: + frame_clear(frame_addr, word_addr, bit_index) + + # Now lets look up the bits we need frames for + segdb = get_database(segj['type']) + + db_k = '%s.%s' % (tilej['type'], name) + try: + db_vals = segdb[db_k] + except KeyError: + raise FASMSyntaxError( + "Segment DB %s, key %s not found from line '%s'" % + (segj['type'], db_k, l)) + ''' + Creating the key depends on whether its an enumerated or a fixed value + If its enumerated, the value forms part of the key + We'd also like to ideally identify if there is a syntax error + Ultimately probably easier to separate out these two cases + ''' + # A single bit value? + if None in db_vals: + seg_word_column, word_bit_n = db_vals[None] + # Value optional, defaults to 1 + isset = 1 + if value: + isset = {'0': 0, '1': 1}.get(value, None) + if isset is None: + raise FASMSyntaxError( + "Bad binary value %s on line %s" % (value, l)) + update_segbit(seg_word_column, word_bit_n, isset) + # An enumerated value + else: + if not value: + raise FASMSyntaxError( + "Enumerable entry %s must have explicit value" % name) + # Get the specific entry we need + try: + db_vals = db_vals[value] + except KeyError: + raise FASMSyntaxError( + "Invalid entry %s. Valid entries are %s" % + (value, db_vals.keys())) + for seg_word_column, word_bit_n, isset in db_vals: + update_segbit(seg_word_column, word_bit_n, isset) if debug: #dump_frames_verbose(frames) diff --git a/tools/segprint2fasm.py b/tools/segprint2fasm.py index fd185fbb..5906044f 100755 --- a/tools/segprint2fasm.py +++ b/tools/segprint2fasm.py @@ -5,29 +5,50 @@ import re import sys import json +enumdb = dict() + + +def get_enums(segtype): + if segtype in enumdb: + return enumdb[segtype] + + enumdb[segtype] = {} + + def process(l): + l = l.strip() + + # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 + parts = line.split() + name = parts[0] + bit_vals = parts[1:] + + # Assumption + # only 1 bit => non-enumerated value + enumdb[segtype][name] = len(bit_vals) != 1 + + with open("%s/%s/segbits_%s.db" % (os.getenv("XRAY_DATABASE_DIR"), + os.getenv("XRAY_DATABASE"), segtype), + "r") as f: + for line in f: + process(line) + + 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: + process(line) + + return enumdb[segtype] + + +def isenum(segtype, tag): + return get_enums(segtype)[tag] + def tag2fasm(grid, seg, tag): '''Given tilegrid, segment name and tag, return fasm directive''' segj = grid['segments'][seg] - def clbf(seg, tile, tag_post): - return '%s.%s 1' % (tile, tag_post) - - def intf(seg, tile, tag_post): - # Make the selection an argument of the configruation - m = re.match(r'(.*)[.]([A-Za-z0-9_]+)', tag_post) - which = m.group(1) - value = m.group(2) - site = { - 'clbll_l': 'CENTER_INTER_L', - 'clbll_r': 'CENTER_INTER_R', - 'clblm_l': 'CENTER_INTER_L', - 'clblm_r': 'CENTER_INTER_R', - 'hclk_l': 'HCLK_L', - 'hclk_r': 'HCLK_R', - }[segj['type']] - return '%s.%s.%s %s' % (tile, site, which, value) - m = re.match(r'([A-Za-z0-9_]+)[.](.*)', tag) tile_type = m.group(1) tag_post = m.group(2) @@ -39,20 +60,14 @@ def tag2fasm(grid, seg, tag): else: raise Exception("Couldn't find tile type %s" % tile_type) - tag2asm = { - 'CLBLL_L': clbf, - 'CLBLL_R': clbf, - 'CLBLM_L': clbf, - 'CLBLM_R': clbf, - 'INT_L': intf, - 'INT_R': intf, - 'HCLK_L': intf, - 'HCLK_R': intf, - } - f = tag2asm.get(tile_type, None) - if f is None: - raise Exception("Unhandled segment type %s" % tile_type) - return f(seg, tile, tag_post) + if not isenum(segj['type'], tag): + return '%s.%s 1' % (tile, tag_post) + else: + # Make the selection an argument of the configruation + m = re.match(r'(.*)[.]([A-Za-z0-9_]+)', tag_post) + which = m.group(1) + value = m.group(2) + return '%s.%s %s' % (tile, which, value) def run(f_in, f_out, sparse=False): diff --git a/tools/test_data/ff_int.fasm b/tools/test_data/ff_int.fasm index 3d7e30b7..4406ed65 100644 --- a/tools/test_data/ff_int.fasm +++ b/tools/test_data/ff_int.fasm @@ -2,23 +2,22 @@ # segprint -zd test_data/clb_ff/design.bits # FF as LDCE -CLBLM_L_X10Y102.SLICEM_X0.AFF.DMUX.AX 1 +CLBLM_L_X10Y102.SLICEM_X0.AFF.DMUX AX CLBLM_L_X10Y102.SLICEM_X0.AFF.ZINI 1 CLBLM_L_X10Y102.SLICEM_X0.AFF.ZRST 1 CLBLM_L_X10Y102.SLICEM_X0.CEUSEDMUX 1 +# CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1 CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1 -# CLBLM_L_X10Y102.SLICEM_X0.FFSYNC 0 -# CLBLM_L_X10Y102.SLICEM_X0.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 +INT_L_X10Y102.BYP_ALT0 EE2END0 +INT_L_X10Y102.BYP_ALT1 EL1END1 +INT_L_X10Y102.CLK_L1 GCLK_L_B11_WEST +INT_L_X10Y102.CTRL_L1 ER1END2 +INT_L_X10Y102.FAN_ALT7 BYP_BOUNCE0 +INT_L_X10Y102.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 +HCLK_L_X31Y130.ENABLE_BUFFER.HCLK_CK_BUFHCLK8 1 +HCLK_L_X31Y130.HCLK_LEAF_CLK_B_BOTL5 HCLK_CK_BUFHCLK8 diff --git a/tools/test_data/ff_int_0s.fasm b/tools/test_data/ff_int_0s.fasm new file mode 100644 index 00000000..3f113f7d --- /dev/null +++ b/tools/test_data/ff_int_0s.fasm @@ -0,0 +1,25 @@ +# Loosely based on +# segprint -zd test_data/clb_ff/design.bits + +# FF as LDCE +CLBLM_L_X10Y102.SLICEM_X0.AFF.DMUX AX +CLBLM_L_X10Y102.SLICEM_X0.AFF.ZINI 1 +CLBLM_L_X10Y102.SLICEM_X0.AFF.ZRST 1 +CLBLM_L_X10Y102.SLICEM_X0.CEUSEDMUX 1 +CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1 +# Unused bits explicitly set to 0 +CLBLM_L_X10Y102.SLICEM_X0.FFSYNC 0 +CLBLM_L_X10Y102.SLICEM_X0.LATCH 0 + +# Note: a number of pseudo pips here +# Omitted +INT_L_X10Y102.BYP_ALT0 EE2END0 +INT_L_X10Y102.BYP_ALT1 EL1END1 +INT_L_X10Y102.CLK_L1 GCLK_L_B11_WEST +INT_L_X10Y102.CTRL_L1 ER1END2 +INT_L_X10Y102.FAN_ALT7 BYP_BOUNCE0 +INT_L_X10Y102.WW2BEG0 LOGIC_OUTS_L4 + +HCLK_L_X31Y130.ENABLE_BUFFER.HCLK_CK_BUFHCLK8 1 +HCLK_L_X31Y130.HCLK_LEAF_CLK_B_BOTL5 HCLK_CK_BUFHCLK8 + diff --git a/tools/test_data/ff_int_op1.fasm b/tools/test_data/ff_int_op1.fasm new file mode 100644 index 00000000..c77b5ffc --- /dev/null +++ b/tools/test_data/ff_int_op1.fasm @@ -0,0 +1,24 @@ +# Loosely based on +# segprint -zd test_data/clb_ff/design.bits + +# FF as LDCE +CLBLM_L_X10Y102.SLICEM_X0.AFF.DMUX AX +CLBLM_L_X10Y102.SLICEM_X0.AFF.ZINI 1 +CLBLM_L_X10Y102.SLICEM_X0.AFF.ZRST 1 +CLBLM_L_X10Y102.SLICEM_X0.CEUSEDMUX 1 +# CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1 +# Optional entry +CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX + +# Note: a number of pseudo pips here +# Omitted +INT_L_X10Y102.BYP_ALT0 EE2END0 +INT_L_X10Y102.BYP_ALT1 EL1END1 +INT_L_X10Y102.CLK_L1 GCLK_L_B11_WEST +INT_L_X10Y102.CTRL_L1 ER1END2 +INT_L_X10Y102.FAN_ALT7 BYP_BOUNCE0 +INT_L_X10Y102.WW2BEG0 LOGIC_OUTS_L4 + +HCLK_L_X31Y130.ENABLE_BUFFER.HCLK_CK_BUFHCLK8 1 +HCLK_L_X31Y130.HCLK_LEAF_CLK_B_BOTL5 HCLK_CK_BUFHCLK8 + diff --git a/tools/test_data/lut_int.fasm b/tools/test_data/lut_int.fasm index df88dd5d..0fca2026 100644 --- a/tools/test_data/lut_int.fasm +++ b/tools/test_data/lut_int.fasm @@ -18,18 +18,18 @@ CLBLM_L_X10Y102.SLICEM_X0.ALUT.INIT[63] 1 # din bus # din[0] -INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0 +INT_L_X10Y102.IMUX_L1 EE2END0 # din[1] -INT_L_X10Y102.CENTER_INTER_L.IMUX_L2 EE2END1 +INT_L_X10Y102.IMUX_L2 EE2END1 # din[2] -INT_L_X10Y102.CENTER_INTER_L.IMUX_L4 EE2END2 +INT_L_X10Y102.IMUX_L4 EE2END2 # din[3] -INT_L_X10Y102.CENTER_INTER_L.IMUX_L7 EE2END3 +INT_L_X10Y102.IMUX_L7 EE2END3 # din[4] -INT_L_X10Y102.CENTER_INTER_L.IMUX_L8 EL1END0 +INT_L_X10Y102.IMUX_L8 EL1END0 # din[5] -INT_L_X10Y102.CENTER_INTER_L.IMUX_L11 EL1END1 +INT_L_X10Y102.IMUX_L11 EL1END1 # dout[0] -INT_L_X10Y102.CENTER_INTER_L.WW2BEG0 LOGIC_OUTS_L12 +INT_L_X10Y102.WW2BEG0 LOGIC_OUTS_L12 diff --git a/tools/test_fasm2frame.py b/tools/test_fasm2frame.py index 87f372fc..27c2815c 100644 --- a/tools/test_fasm2frame.py +++ b/tools/test_fasm2frame.py @@ -1,3 +1,5 @@ +# TODO: need better coverage for different tile types + import fasm2frame import unittest @@ -74,6 +76,63 @@ class TestStringMethods(unittest.TestCase): self.bitread_frm_equals( 'test_data/ff_int.fasm', 'test_data/ff_int/design.bits') + def test_ff_int_op1(self): + '''Omitted key set to ''' + self.bitread_frm_equals( + 'test_data/ff_int_op1.fasm', 'test_data/ff_int/design.bits') + + # Same check as above, but isolated test case + def test_opkey_01_default(self): + '''Optional key with binary omitted value should produce valid result''' + fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX") + fout = StringIO.StringIO() + fasm2frame.run(fin, fout) + + def test_opkey_01_1(self): + fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1") + fout = StringIO.StringIO() + fasm2frame.run(fin, fout) + + def test_opkey_enum(self): + '''Optional key with enumerated value should produce syntax error''' + # CLBLM_L.SLICEM_X0.AMUX.O6 !30_06 !30_07 !30_08 30_11 + fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.AMUX.O6") + fout = StringIO.StringIO() + try: + fasm2frame.run(fin, fout) + self.fail("Expected syntax error") + except fasm2frame.FASMSyntaxError: + pass + + def test_ff_int_0s(self): + '''Explicit 0 entries''' + self.bitread_frm_equals( + 'test_data/ff_int_0s.fasm', 'test_data/ff_int/design.bits') + + def test_badkey(self): + '''Bad key should throw syntax error''' + fin = StringIO.StringIO("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 2") + fout = StringIO.StringIO() + try: + fasm2frame.run(fin, fout) + self.fail("Expected syntax error") + except fasm2frame.FASMSyntaxError: + pass + + def test_dupkey(self): + '''Duplicate key should throw syntax error''' + fin = StringIO.StringIO( + """\ +CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 0 +CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1 +""") + fout = StringIO.StringIO() + try: + fasm2frame.run(fin, fout) + self.fail("Expected syntax error") + except fasm2frame.FASMSyntaxError: + pass + def test_sparse(self): '''Verify sparse equivilent to normal encoding''' frm_fn = 'test_data/lut_int.fasm'