Merge pull request #1186 from antmicro/in_term_group

Grouping of IN_TERM features
This commit is contained in:
litghost 2020-02-18 09:20:31 -08:00 committed by GitHub
commit c0289c5948
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 435 additions and 30 deletions

View File

@ -13,7 +13,7 @@ build/segbits_xiob33.rdb: $(SPECIMENS_OK)
build/segbits_xiob33.db: build/segbits_xiob33.rdb process_rdb.py bits.dbf
python3 process_rdb.py build/segbits_xiob33.rdb > build/segbits_xiob33_processed.rdb
${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --seg-fn-in build/segbits_xiob33_processed.rdb --seg-fn-out $@
${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --groups tag_groups.txt --seg-fn-in build/segbits_xiob33_processed.rdb --seg-fn-out $@
${XRAY_MASKMERGE} build/mask_xiob33.db $$(find -name segdata_liob33.txt) $$(find -name segdata_riob33.txt)
build/segbits_hclk_ioi3.rdb: $(SPECIMENS_OK)

View File

@ -1,4 +0,0 @@
38_92 39_93 38_94,IOB33.IOB_Y0.PULLTYPE.PULLDOWN
38_106 39_107 39_111 38_106 38_110 39_105 39_109,IOB33.IOB_Y0.SLEW.FAST
39_33 38_34 39_35,IOB33.IOB_Y1.PULLTYPE.PULLDOWN
39_21 38_16 38_20 38_18 38_22 39_17,IOB33.IOB_Y1.SLEW.FAST

View File

@ -0,0 +1,5 @@
IOB33.IOB_Y0.IN_TERM.NONE IOB33.IOB_Y0.IN_TERM.UNTUNED_SPLIT_40 IOB33.IOB_Y0.IN_TERM.UNTUNED_SPLIT_50 IOB33.IOB_Y0.IN_TERM.UNTUNED_SPLIT_60
IOB33.IOB_Y1.IN_TERM.NONE IOB33.IOB_Y1.IN_TERM.UNTUNED_SPLIT_40 IOB33.IOB_Y1.IN_TERM.UNTUNED_SPLIT_50 IOB33.IOB_Y1.IN_TERM.UNTUNED_SPLIT_60
IOB33.IOB_Y0.PULLTYPE.KEEPER IOB33.IOB_Y0.PULLTYPE.NONE IOB33.IOB_Y0.PULLTYPE.PULLDOWN IOB33.IOB_Y0.PULLTYPE.PULLUP
IOB33.IOB_Y1.PULLTYPE.KEEPER IOB33.IOB_Y1.PULLTYPE.NONE IOB33.IOB_Y1.PULLTYPE.PULLDOWN IOB33.IOB_Y1.PULLTYPE.PULLUP

View File

@ -1,6 +1,7 @@
#/usr/bin/env python3
import sys, os, re
import itertools
from prjxray import util
clb_int_zero_db = [
@ -199,7 +200,30 @@ class ZeroGroups(object):
bits.add("!" + bit)
def add_zero_bits(fn_in, zero_db, clb_int=False, strict=True, verbose=False):
def read_segbits(fn_in):
"""
Reads a segbits file. Removes duplcated lines. Returns a list of the lines.
"""
lines = []
llast = None
with open(fn_in, "r") as f:
for line in f:
# Hack: skip duplicate lines
# This happens while merging a new multibit entry
line = line.strip()
if len(line) == 0:
continue
if line == llast:
continue
lines.append(line)
return lines
def add_zero_bits(
fn_in, lines, zero_db, clb_int=False, strict=True, verbose=False):
'''
Add multibit entries
This requires adding some zero bits (ex: !31_09)
@ -208,27 +232,17 @@ def add_zero_bits(fn_in, zero_db, clb_int=False, strict=True, verbose=False):
zero_groups = ZeroGroups(zero_db)
lines = []
new_lines = set()
changes = 0
llast = None
drops = 0
with open(fn_in, "r") as f:
for line in f:
# Hack: skip duplicate lines
# This happens while merging a new multibit entry
line = line.strip()
if line == llast:
continue
for line in lines:
lines.append(line)
tag, bits, mode, _ = util.parse_db_line(line)
tag, bits, mode, _ = util.parse_db_line(line)
if bits is not None and mode is None:
zero_groups.add_tag_bits(tag, bits)
if bits is not None and mode is None:
zero_groups.add_tag_bits(tag, bits)
if verbose:
zero_groups.print_groups()
@ -366,7 +380,7 @@ def remove_ambiguous_solutions(fn_in, db_lines, strict=True, verbose=True):
return 0, db_lines
drops = 0
output_lines = []
output_lines = set()
for l in db_lines:
parts = l.split()
@ -374,7 +388,7 @@ def remove_ambiguous_solutions(fn_in, db_lines, strict=True, verbose=True):
bits = frozenset(parts[1:])
if bits not in dropped_solutions:
output_lines.append(l)
output_lines.add(l)
drops += 1
else:
if verbose:
@ -387,8 +401,81 @@ def remove_ambiguous_solutions(fn_in, db_lines, strict=True, verbose=True):
return drops, output_lines
def group_tags(lines, tag_groups, bit_groups):
"""
Implements tag grouping. If a tag belongs to a group then the common bits
of that group are added to is as zeros.
>>> tg = [{"A", "B"}]
>>> bg = [{(1, 2), (3, 4)}]
>>> res = group_tags({"A 1_2", "B 3_4"}, tg, bg)
>>> (res[0], sorted(list(res[1])))
(2, ['A 1_2 !3_4', 'B !1_2 3_4'])
>>> tg = [{"A", "B"}]
>>> bg = [{(1, 2), (3, 4)}]
>>> res = group_tags({"A 1_2", "B 3_4", "C 1_2"}, tg, bg)
>>> (res[0], sorted(list(res[1])))
(2, ['A 1_2 !3_4', 'B !1_2 3_4', 'C 1_2'])
"""
changes = 0
new_lines = set()
# Process lines
for line in lines:
line = line.strip()
if not len(line):
continue
# Parse the line
tag, bits, mode, _ = util.parse_db_line(line)
if not bits:
bits = set()
else:
bits = set([util.parse_tagbit(b) for b in bits])
# Check if the tag belongs to a group
for tag_group, bit_group in zip(tag_groups, bit_groups):
if tag in tag_group:
# Add zero bits to the tag if not already there
bit_coords = set([b[1] for b in bits])
for zero_bit in bit_group:
if zero_bit not in bit_coords:
bits.add((False, zero_bit))
# Format the line
bit_strs = []
for bit in sorted(list(bits), key=lambda b: b[1]):
s = "!" if not bit[0] else ""
s += "{}_{}".format(bit[1][0], bit[1][1])
bit_strs.append(s)
new_line = " ".join([tag] + bit_strs)
# Add the line
new_lines.add(new_line)
changes += 1
break
# It does not, pass it through unchanged
else:
new_lines.add(line)
return changes, new_lines
def update_seg_fns(
fn_inouts, zero_db, clb_int, lazy=False, strict=True, verbose=False):
fn_inouts,
zero_db,
tag_groups,
clb_int,
lazy=False,
strict=True,
verbose=False):
seg_files = 0
seg_lines = 0
for fn_in, fn_out in fn_inouts:
@ -396,20 +483,35 @@ def update_seg_fns(
if lazy and not os.path.exists(fn_in):
continue
changes, new_lines = add_zero_bits(
fn_in, zero_db, clb_int=clb_int, strict=strict, verbose=verbose)
lines = read_segbits(fn_in)
changes = 0
new_changes, final_lines = remove_ambiguous_solutions(
# Find common bits for tag groups
bit_groups = find_common_bits_for_tag_groups(lines, tag_groups)
# Group tags
new_changes, lines = group_tags(lines, tag_groups, bit_groups)
changes += new_changes
new_changes, lines = add_zero_bits(
fn_in,
new_lines,
lines,
zero_db,
clb_int=clb_int,
strict=strict,
verbose=verbose)
changes += new_changes
new_changes, lines = remove_ambiguous_solutions(
fn_in,
lines,
strict=strict,
verbose=verbose,
)
changes += new_changes
with open(fn_out, "w") as f:
for line in sorted(final_lines):
for line in sorted(lines):
print(line, file=f)
if changes is not None:
@ -453,6 +555,7 @@ def update_segs(
seg_fn_in,
seg_fn_out,
zero_db_fn,
tag_groups,
strict=True,
verbose=False):
if clb_int:
@ -479,7 +582,72 @@ def update_segs(
print("CLB INT mode: %s" % clb_int)
print("Segbit groups: %s" % len(zero_db))
update_seg_fns(
fn_inouts, zero_db, clb_int, lazy=lazy, strict=strict, verbose=verbose)
fn_inouts,
zero_db,
tag_groups,
clb_int,
lazy=lazy,
strict=strict,
verbose=verbose)
def find_common_bits_for_tag_groups(lines, tag_groups):
"""
For each tag group finds a common set of bits that have value of one.
"""
bit_groups = []
for tag_group in tag_groups:
bit_group = set()
for line in lines:
tag, bits, mode, _ = util.parse_db_line(line)
if not bits:
continue
bits = set([util.parse_tagbit(b) for b in bits])
if tag in tag_group and len(bits):
ones = set([b[1] for b in bits if b[0]])
bit_group |= ones
bit_groups.append(bit_group)
return bit_groups
def load_tag_groups(file_name):
"""
Loads tag groups from a text file.
A tag group is defined by specifying a space separated list of tags within
a single line. Lines that are empty or start with '#' are ignored.
"""
tag_groups = []
# Load tag group specifications
with open(file_name, "r") as fp:
for line in fp:
line = line.strip()
if len(line) == 0 or line.startswith("#"):
continue
group = set(line.split())
if len(group):
tag_groups.append(group)
# Check if all tag groups are exclusive
for tag_group_a, tag_group_b in itertools.combinations(tag_groups, 2):
tags = tag_group_a & tag_group_b
if len(tags):
raise RuntimeError(
"Tag(s) {} are present in multiple groups".format(
" ".join(tags)))
return tag_groups
def run(
@ -488,12 +656,18 @@ def run(
zero_db_fn=None,
seg_fn_in=None,
seg_fn_out=None,
groups_fn_in=None,
strict=None,
verbose=False):
if strict is None:
strict = not clb_int
# Load tag groups
tag_groups = []
if groups_fn_in is not None:
tag_groups = load_tag_groups(groups_fn_in)
# Probably should split this into two programs
update_segs(
db_root,
@ -501,6 +675,7 @@ def run(
seg_fn_in=seg_fn_in,
seg_fn_out=seg_fn_out,
zero_db_fn=zero_db_fn,
tag_groups=tag_groups,
strict=strict,
verbose=verbose)
if clb_int:
@ -520,6 +695,14 @@ def main():
parser.add_argument('--seg-fn-in', help='')
parser.add_argument('--seg-fn-out', help='')
util.add_bool_arg(parser, "--strict", default=False)
parser.add_argument(
"-g",
"--groups",
type=str,
default=None,
help="Input tag group definition file")
args = parser.parse_args()
run(
@ -528,6 +711,7 @@ def main():
args.zero_db,
args.seg_fn_in,
args.seg_fn_out,
args.groups,
strict=args.strict,
verbose=args.verbose)

220
utils/group.py Executable file
View File

@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""
This script Reads tag group definition from a file and applies the tag grouping.
First a set of common bits for each group is found (logical OR among all tags
belonging to the group). Then in each tag belonging to the group those bits are
set to 0 but only if they are not already present there.
The resulting data is written into a segbits file.
"""
import argparse
import re
import itertools
# =============================================================================
def load_tag_groups(file_name):
"""
Loads tag groups from a text file.
A tag group is defined by specifying a space separated list of tags within
a single line. Lines that are empty or start with '#' are ignored.
"""
tag_groups = []
# Load tag group specifications
with open(file_name, "r") as fp:
for line in fp:
line = line.strip()
if len(line) == 0 or line.startswith("#"):
continue
group = set(line.split())
if len(group):
tag_groups.append(group)
# Check if all tag groups are exclusive
for tag_group_a, tag_group_b in itertools.combinations(tag_groups, 2):
tags = tag_group_a & tag_group_b
if len(tags):
raise RuntimeError(
"Tag(s) {} are present in multiple groups".format(
" ".join(tags)))
return tag_groups
# =============================================================================
def parse_bit(bit):
"""
Decodes string describing a bit. Returns a tuple (frame, bit, value)
"""
match = re.match("^(!?)([0-9]+)_([0-9]+)$", bit)
assert match != None, bit
val = int(match.group(1) != "!")
frm = int(match.group(2))
bit = int(match.group(3))
return frm, bit, val
def bit_to_str(bit):
"""
Converts a tuple (frame, bit, value) to its string representation.
"""
s = "!" if not bit[2] else ""
return "{}{}_{:02d}".format(s, bit[0], bit[1])
def load_segbits(file_name):
"""
Loads a segbits file.
"""
segbits = {}
with open(file_name, "r") as fp:
for line in fp:
line = line.strip()
fields = line.split()
if len(fields) < 2:
raise RuntimeError("Malformed line: '%s'" % line)
tag = fields[0]
if "<" in line or ">" in line:
segbits[tag] = " ".join(fields[1:])
else:
bits = set([parse_bit(bit) for bit in fields[1:]])
segbits[tag] = bits
return segbits
def save_segbits(file_name, segbits):
"""
Save segbits to a .db or .rdb file
"""
with open(file_name, "w") as fp:
for tag, bits in segbits.items():
if isinstance(bits, str):
line = tag + " " + bits
elif isinstance(bits, set):
line = tag + " "
line += " ".join(
[bit_to_str(bit) for bit in sorted(list(bits))])
fp.write(line + "\n")
# =============================================================================
def mask_out_bits(segbits, mask, tags_to_mask=None):
"""
Given a set of bits and a list of tags to affect (optional) removes all
the bits from each tag that are present (and equal) in the masking set.
"""
if tags_to_mask is None:
tags_to_mask = segbits.keys()
# Mask out matching bits
for tag in tags_to_mask:
bits = segbits[tag]
bits = set(bits) - set(mask)
segbits[tag] = bits
return segbits
def find_common_bits_for_tag_groups(segbits, tag_groups):
"""
For each tag group finds a common set of bits that have value of one.
"""
bit_groups = []
for tag_group in tag_groups:
bit_group = set()
for tag, bits in segbits.items():
if tag in tag_group and isinstance(bits, set):
ones = set([b for b in bits if b[2]])
bit_group |= ones
bit_groups.append(bit_group)
return bit_groups
def group_tags(segbits, tag_groups, bit_groups):
"""
Implements tag grouping. If a tag belongs to a group then the common bits
of that group are added to is as zeros.
"""
for tag_group, bit_group in zip(tag_groups, bit_groups):
zeros = set([(b[0], b[1], 0) for b in bit_group])
for tag in tag_group:
# Insert zero bits
if tag in segbits.keys():
bits = segbits[tag]
if not isinstance(bits, set):
bits = set()
segbits[tag] = bits
for z in zeros:
if not (z[0], z[1], 1) in bits:
bits.add(z)
return segbits
# =============================================================================
def main():
# Parse arguments
parser = argparse.ArgumentParser()
parser.add_argument("-i", required=True, type=str, help="Input .rdb file")
parser.add_argument(
"-g", required=True, type=str, help="Input tag group definition file")
parser.add_argument("-o", required=True, type=str, help="Output .rdb file")
args = parser.parse_args()
# Load tag groups
tag_groups = load_tag_groups(args.g)
# Load raw database file
segbits = load_segbits(args.i)
# Find common bits
bit_groups = find_common_bits_for_tag_groups(segbits, tag_groups)
# Apply tag grouping
segbits = group_tags(segbits, tag_groups, bit_groups)
# Save fixed database file
save_segbits(args.o, segbits)
if __name__ == "__main__":
main()