Merge pull request #61 from mcmasterg/fasm2frame_oparg
Fasm2frame oparg
diff --git a/tools/fasm2frame.py b/tools/fasm2frame.py
index b288f31..c4e845a 100755
--- a/tools/fasm2frame.py
+++ b/tools/fasm2frame.py
@@ -4,9 +4,36 @@
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.meh': {
+ 'O5': [(11, 2, False), (12, 2, True)],
+ 'O6': [(11, 2, True), (12, 2, False)],
+ },
+}
+'''
segbitsdb = dict()
@@ -16,25 +43,48 @@
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)
+ # Treat like an enumerated value with keys 0 or 1
+ segbitsdb[segtype][name] = {
+ '0': [(seg_word_column, word_bit_n, 0)],
+ '1': [(seg_word_column, word_bit_n, 1)],
+ }
+ 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
+ bits_map = segbitsdb[segtype].setdefault(name, {})
+ bits_map[key] = [parsebit(x) for x in bit_vals]
+
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 +129,10 @@
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 +151,10 @@
'''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 +163,7 @@
# 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 +177,22 @@
# 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 +204,46 @@
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))
+
+ if not value:
+ # If its binary, allow omitted value default to 1
+ if tuple(sorted(db_vals.keys())) == ('0', '1'):
+ value = '1'
+ else:
+ 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 fd185fb..5906044 100755
--- a/tools/segprint2fasm.py
+++ b/tools/segprint2fasm.py
@@ -5,29 +5,50 @@
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 @@
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 3d7e30b..4406ed6 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 0000000..3f113f7
--- /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 0000000..c77b5ff
--- /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 df88dd5..0fca202 100644
--- a/tools/test_data/lut_int.fasm
+++ b/tools/test_data/lut_int.fasm
@@ -18,18 +18,18 @@
# 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 87f372f..27c2815 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 @@
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'