fasm2frame: optional value on simple elements

Remove INT prefixes not in segdb
Better test cases

Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
John McMaster 2018-01-22 18:36:20 -08:00
parent 7110a67c55
commit 23813e3065
7 changed files with 305 additions and 110 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'