Merge pull request #81 from mithro/libfixes
Fix a number of issues with the generated liberty output
diff --git a/scripts/python-skywater-pdk/skywater_pdk/liberty.py b/scripts/python-skywater-pdk/skywater_pdk/liberty.py
index c2c8de7..ab32f09 100755
--- a/scripts/python-skywater-pdk/skywater_pdk/liberty.py
+++ b/scripts/python-skywater-pdk/skywater_pdk/liberty.py
@@ -343,7 +343,8 @@
if ocorner_type != TimingType.ccsnoise:
remove_ccsnoise_from_library(common_data, "library")
- output = liberty_dict("library", lib+"__"+corner, common_data)
+ attribute_types = {}
+ output = liberty_dict("library", lib+"__"+corner, common_data, attribute_types)
assert output[-1] == '}', output
top_write(output[:-1])
@@ -360,7 +361,13 @@
remove_ccsnoise_from_cell(cell_data, cell_with_size)
top_write([''])
- top_write(liberty_dict("cell", "%s__%s" % (lib, cell_with_size), cell_data, [cell_with_size]))
+ top_write(liberty_dict(
+ "cell",
+ "%s__%s" % (lib, cell_with_size),
+ cell_data,
+ [cell_with_size],
+ attribute_types,
+ ))
top_write([''])
top_write(['}'])
@@ -602,6 +609,135 @@
return k in ('variable', 'index', 'values')
+def liberty_guess(o):
+ """
+
+ >>> liberty_guess('hello') # doctest: +ELLIPSIS
+ <function liberty_str at ...>
+ >>> liberty_guess(1.0) # doctest: +ELLIPSIS
+ <function liberty_float at ...>
+ >>> liberty_guess(1) # doctest: +ELLIPSIS
+ <function liberty_float at ...>
+ >>> liberty_guess(None)
+ Traceback (most recent call last):
+ ...
+ ValueError: None has unguessable type: <class 'NoneType'>
+
+ """
+ if isinstance(o, str):
+ return liberty_str
+ elif isinstance(o, (float,int)):
+ return liberty_float
+ else:
+ raise ValueError("%r has unguessable type: %s" % (o, type(o)))
+
+
+def liberty_bool(b):
+ """
+
+ >>> liberty_bool(True)
+ 'true'
+ >>> liberty_bool(False)
+ 'false'
+ >>> liberty_bool(1.0)
+ 'true'
+ >>> liberty_bool(1.5)
+ Traceback (most recent call last):
+ ...
+ ValueError: 1.5 is not a bool
+
+ >>> liberty_bool(0.0)
+ 'false'
+ >>> liberty_bool(0)
+ 'false'
+ >>> liberty_bool(1)
+ 'true'
+ >>> liberty_bool("error")
+ Traceback (most recent call last):
+ ...
+ ValueError: 'error' is not a bool
+
+ """
+ try:
+ b2 = bool(b)
+ except ValueError:
+ b2 = None
+
+ if b2 != b:
+ raise ValueError("%r is not a bool" % b)
+
+ return {True: 'true', False: 'false'}[b]
+
+
+def liberty_str(s):
+ """
+
+ >>> liberty_str("hello")
+ '"hello"'
+
+ >>> liberty_str('he"llo')
+ Traceback (most recent call last):
+ ...
+ ValueError: '"' is not allow in the string: 'he"llo'
+
+ >>> liberty_str(1.0)
+ '"1.0000000000"'
+
+ >>> liberty_str(1)
+ '"1.0000000000"'
+
+ >>> liberty_str([])
+ Traceback (most recent call last):
+ ...
+ ValueError: [] is not a string
+
+ >>> liberty_str(True)
+ Traceback (most recent call last):
+ ...
+ ValueError: True is not a string
+
+ """
+ try:
+ if isinstance(s, (int, float)):
+ s = liberty_float(s)
+ except ValueError:
+ pass
+
+ if not isinstance(s, str):
+ raise ValueError("%r is not a string" % s)
+
+ if '"' in s:
+ raise ValueError("'\"' is not allow in the string: %r" % s)
+
+ return '"'+s+'"'
+
+
+def liberty_int(f):
+ """
+
+ >>> liberty_int(1.0)
+ 1
+ >>> liberty_int(1.5)
+ Traceback (most recent call last):
+ ...
+ ValueError: 1.5 is not an int
+
+ >>> liberty_int("error")
+ Traceback (most recent call last):
+ ...
+ ValueError: 'error' is not an int
+
+ """
+ try:
+ f2 = int(f)
+ except ValueError as e:
+ f2 = None
+
+ if f2 is None or f2 != f:
+ raise ValueError("%r is not an int" % f)
+ return int(f)
+
+
def liberty_float(f):
"""
@@ -617,7 +753,42 @@
>>> liberty_float(1)
'1.0000000000'
+ >>> liberty_float(True)
+ Traceback (most recent call last):
+ ...
+ ValueError: True is not a float
+
+ >>> liberty_float(False)
+ Traceback (most recent call last):
+ ...
+ ValueError: False is not a float
+
+ >>> liberty_float(0)
+ '0.0000000000'
+
+ >>> liberty_float(None)
+ Traceback (most recent call last):
+ ...
+ ValueError: None is not a float
+
+ >>> liberty_float('hello')
+ Traceback (most recent call last):
+ ...
+ ValueError: 'hello' is not a float
+
+
"""
+ try:
+ f2 = float(f)
+ except (ValueError, TypeError):
+ f2 = None
+
+ if isinstance(f, bool):
+ f2 = None
+
+ if f is None or f2 != f:
+ raise ValueError("%r is not a float" % f)
+
WIDTH = len(str(0.0083333333))
s = json.dumps(f)
@@ -639,6 +810,14 @@
return s
+LIBERTY_ATTRIBUTE_TYPES = {
+ 'boolean': liberty_bool,
+ 'string': liberty_str,
+ 'int': liberty_int,
+ 'float': liberty_float,
+}
+
+
INDENT=" "
@@ -738,8 +917,42 @@
return o
-def liberty_dict(dtype, dvalue, data, indent=tuple()):
+def liberty_dict(dtype, dvalue, data, indent=tuple(), attribute_types=None):
+
+ """
+
+ >>> def g(a, b, c):
+ ... return {"group_name": a, "attribute_name":b, "attribute_type": c}
+ >>> d = {'float': 1.0, "str": "str"}
+ >>> print('\\n'.join(liberty_dict("library", "test", d)))
+ library ("test") {
+ float : 1.0000000000;
+ str : "str";
+ }
+ >>> d['define'] = [g("cell", "float", "string")]
+ >>> print('\\n'.join(liberty_dict("library", "test", d)))
+ library ("test") {
+ define(float,cell,string);
+ float : 1.0000000000;
+ str : "str";
+ }
+ >>> d['define'] = [g("library", "float", "string")]
+ >>> print('\\n'.join(liberty_dict("library", "test", d)))
+ library ("test") {
+ define(float,library,string);
+ float : "1.0000000000";
+ str : "str";
+ }
+
+ """
+
+
assert isinstance(data, dict), (dtype, dvalue, data)
+
+ if attribute_types is None:
+ attribute_types = {}
+ assert isinstance(attribute_types, dict), (dtype, dvalue, attribute_types)
+
o = []
if dvalue:
@@ -773,28 +986,47 @@
di = [attr_sort_key(i) for i in data.items()]
di.sort()
if debug:
+ print(" "*len(str(indent)), "s1 s2 ", "%-40s" % "ktype", '%-40r' % "kvalue", "value")
+ print("-"*len(str(indent)), "---- ---- ", "-"*40, "-"*40, "-"*44)
for sk, kt, skv, kv, k, v in di:
- print(str(indent), "%4.0f %4.0f -- " % sk, "%-40s" % kt, '%-40r' % kv, str(v)[:40], '...')
+ print(str(indent), "%4.0f %4.0f --" % sk, "%-40s" % kt, '%-40r' % kv, end=" ")
+ sv = str(v)
+ print(sv[:40], end=" ")
+ if len(sv) > 40:
+ print('...', end=" ")
+ print()
+
# Output all the attributes
+ if dtype not in attribute_types:
+ dtype_attribute_types = {}
+ attribute_types[dtype] = dtype_attribute_types
+ dtype_attribute_types = attribute_types[dtype]
+
for _, ktype, _, kvalue, k, v in di:
indent_n = list(indent)+[k]
if ktype == 'define':
for d in sorted(data['define'], key=lambda d: d['group_name']+'.'+d['attribute_name']):
+
+ aname = d['attribute_name']
+ gname = d['group_name']
+ atype = d['attribute_type']
+
o.append('%sdefine(%s,%s,%s);' % (
- INDENT*len(indent_n),
- d['attribute_name'],
- d['group_name'],
- d['attribute_type']),
- )
+ INDENT*len(indent_n), aname, gname, atype))
+
+ assert atype in LIBERTY_ATTRIBUTE_TYPES, (atype, d)
+ if gname not in attribute_types:
+ attribute_types[gname] = {}
+ attribute_types[gname][aname] = LIBERTY_ATTRIBUTE_TYPES[atype]
elif ktype == "comp_attribute":
o.extend(liberty_composite(kvalue, v, indent_n))
elif isinstance(v, dict):
assert isinstance(v, dict), (dtype, dvalue, k, v)
- o.extend(liberty_dict(ktype, kvalue, v, indent_n))
+ o.extend(liberty_dict(ktype, kvalue, v, indent_n, attribute_types))
elif isinstance(v, list):
assert len(v) > 0, (dtype, dvalue, k, v)
@@ -803,24 +1035,25 @@
return o.items()
for l in sorted(v, key=sk):
- o.extend(liberty_dict(ktype, kvalue, l, indent_n))
+ o.extend(liberty_dict(ktype, kvalue, l, indent_n, attribute_types))
elif is_liberty_list(ktype):
o.extend(liberty_list(ktype, v, indent_n))
elif "clk_width" == ktype:
for l in sorted(v):
- o.append("%s%s : %s;" % (INDENT*len(indent_n), k, l))
+ o.append('%s%s : "%s";' % (INDENT*len(indent_n), k, l))
else:
raise ValueError("Unknown %s: %r\n%s" % (k, v, indent_n))
else:
- if isinstance(v, str):
- v = '"%s"' % v
- elif isinstance(v, (float,int)):
- v = liberty_float(v)
- o.append("%s%s : %s;" % (INDENT*len(indent_n), k, v))
+ if ktype in dtype_attribute_types:
+ liberty_out = dtype_attribute_types[ktype]
+ else:
+ liberty_out = liberty_guess(v)
+ ov = liberty_out(v)
+ o.append("%s%s : %s;" % (INDENT*len(indent_n), k, ov))
o.append("%s}" % (INDENT*len(indent)))
return o