Write a little test bench.
diff --git a/scripts/python-skywater-pdk/generate_symbols.sh b/scripts/python-skywater-pdk/generate_symbols.sh
index a063313..0b0410a 100755
--- a/scripts/python-skywater-pdk/generate_symbols.sh
+++ b/scripts/python-skywater-pdk/generate_symbols.sh
@@ -5,14 +5,20 @@
exit 1
fi
-find $1/skywater-pdk/ -name *.v -delete
+set -x
+set -e
+find $1/skywater-pdk/ -name *.tb.v -delete
+find $1/skywater-pdk/ -name *.blackbox.v -delete
+find $1/skywater-pdk/ -name *.symbol.v -delete
+#find $1/skywater-pdk/ -name *.svg -delete
+
+./generate_verilog_blackbox.py $1/skywater-pdk/libraries/*/*/models/*
./generate_verilog_blackbox.py $1/skywater-pdk/libraries/*/*/cells/*
+exit
(cd $1/ ; git diff --no-renames --name-only --diff-filter=D -z | xargs -0 git checkout --)
-exit
-
for LIB in $1/skywater-pdk/libraries/*; do
LIBNAME=$(basename $LIB)
diff --git a/scripts/python-skywater-pdk/generate_verilog_blackbox.py b/scripts/python-skywater-pdk/generate_verilog_blackbox.py
index 7675c3e..4dd09aa 100755
--- a/scripts/python-skywater-pdk/generate_verilog_blackbox.py
+++ b/scripts/python-skywater-pdk/generate_verilog_blackbox.py
@@ -10,6 +10,8 @@
# SPDX-License-Identifier: Apache-2.0
+import csv
+import itertools
import json
import os
import pprint
@@ -19,7 +21,6 @@
from collections import defaultdict
-
copyright_header = """\
/**
* Copyright 2020 The SkyWater PDK Authors
@@ -392,6 +393,10 @@
return cls.DATA_OUT_WORD
if name in ('HI', 'LO'):
return cls.DATA_OUT_WORD
+ if name in ('UDP_IN',):
+ return cls.DATA_IN_WORD
+ if name in ('UDP_OUT',):
+ return cls.DATA_OUT_WORD
if re.search('^S[0-9]+$', name):
return cls.DATA_CONTROL
@@ -404,6 +409,10 @@
if re.search('^(DE)(_[NB])?$', name):
return cls.DATA_CONTROL
+ if re.search('^(DATA_EN)$', name):
+ return cls.DATA_CONTROL
+ if re.search('^(SET_ASYNC)$', name):
+ return cls.DATA_CONTROL
if re.search('^((CLK)|(GCLK)|(GATE))_EN$', name):
return cls.CLOCK_CONTROL
@@ -417,6 +426,8 @@
return cls.CLOCK
if re.search('^[G]_?N?$', name):
return cls.CLOCK
+ if re.search('^((CK)|(CP))$', name):
+ return cls.CLOCK
if re.search('^SLEEP(_[NB])?$', name):
return cls.POWER_CONTROL
@@ -431,6 +442,8 @@
return cls.POWER_NEGATIVE
if name.startswith('DEST'):
return cls.POWER_OTHER
+ if 'NOT' in name:
+ return cls.POWER_OTHER
if 'DIODE' in name:
return cls.POWER_OTHER
if 'PWR' in name:
@@ -460,68 +473,6 @@
-def write_verilog(define_data, outfile, drive=None):
- with open(outfile, 'w') as f:
- f.write(copyright_header)
- f.write('\n')
- f.write(include_header.format(define_data['fullname']))
- f.write('\n')
- if not drive:
- return
- drive_name, drive_value = drive
-
- if not 'ports' in define_data:
- return
-
- module_signal_defports = []
- module_signal_ports = []
- for pname, ptype in define_data['ports']['signal']:
- module_signal_defports.append("{} {}, ".format(ptype, pname))
- module_signal_ports.append(pname)
-
- module_signal_defports = "".join(module_signal_defports)
- assert module_signal_defports.endswith(", "), module_signal_defports
- module_signal_defports = module_signal_defports[:-2]
- module_signal_ports = ", ".join(module_signal_ports)
-
- module_power_defports = []
- module_power_ports = []
- for pname, ptype in define_data['ports']['power']:
- module_power_defports.append(", {} {}".format('input', pname))
- module_power_ports.append(", {}".format(pname))
- module_power_defports = "".join(module_power_defports)
- module_power_ports = "".join(module_power_ports)
-
- library_name = "{} {}".format(
- define_data['library']['name'].upper(), define_data['library']['type'])
-
- f.write(module_header.format(
- module_base_name = define_data['fullname'],
- cell_name = define_data['name'],
- library_name = library_name,
- drive_name = drive_name,
- drive_value = drive_value,
- description = define_data.get('description', ''),
- module_signal_defports = module_signal_defports,
- module_signal_ports = module_signal_ports,
- module_power_defports = module_power_defports,
- module_power_ports = module_power_ports,
- ))
-
-
-def echo_file(fname):
- with open(fname) as f:
- sys.stdout.write('\n')
- sys.stdout.write('File: ')
- sys.stdout.write(fname)
- sys.stdout.write('\n')
- sys.stdout.write('------\n')
- sys.stdout.write(f.read())
- sys.stdout.write('------\n')
- sys.stdout.flush()
-
-
-
def seek_backwards(f):
start_pos = f.tell()
current_pos = f.tell()-1
@@ -549,8 +500,8 @@
drives.append(drive[1:])
return drives
-def wrap(s):
- return "\n".join(textwrap.wrap(s, initial_indent=' * ', subsequent_indent=' * '))
+def wrap(s, i=''):
+ return "\n".join(textwrap.wrap(s, initial_indent=' * '+i, subsequent_indent=' * '+i))
warning = """\
@@ -572,8 +523,14 @@
f.write(f'`define {guard}\n')
f.write('\n')
f.write(f"/**\n")
- f.write(wrap(f"{define_data['name']}: {define_data['description']}"))
- f.write('\n')
+ if '\n' in define_data['description']:
+ f.write(f" * {define_data['name']}:\n")
+ for l in define_data['description'].splitlines():
+ f.write(wrap(l.rstrip(), i=' '))
+ f.write('\n')
+ else:
+ f.write(wrap(f"{define_data['name']}: {define_data['description']}"))
+ f.write('\n')
f.write(f" *\n")
f.write(wrap(desc))
f.write('\n')
@@ -597,7 +554,7 @@
def write_module_header(f, define_data):
f.write('(* blackbox *)\n')
- f.write(f"module {define_data['library']}__{define_data['name']} (")
+ f.write(f"module {define_data['verilog_name']} (")
def write_verilog_parameters(f, define_data):
@@ -641,22 +598,32 @@
for pclass, pname, pdir, ptype in ports:
pname = pname.ljust(maxlen['pname'])
+ f.write(f"\n {pname},")
+ seek_backwards(f)
+ if ports:
+ f.write("\n")
+ f.write(");\n")
+
+ for pclass, pname, pdir, ptype in ports:
+ pname = pname.ljust(maxlen['pname'])
if maxlen['pdir']:
pdir = pdir.ljust(maxlen['pdir'])+' '
else:
pdir = ''
+ if pclass == 'power':
+ ptype = ''
+
if maxlen['ptype']:
ptype = ptype.ljust(maxlen['ptype'])+ ' '
else:
ptype = ''
- f.write(f"\n {pdir}{ptype}{pname},")
+ f.write(f"\n {pdir}{ptype}{pname};")
seek_backwards(f)
if ports:
f.write("\n")
- f.write(");\n")
def write_verilog_symbol_ports(f, pports):
@@ -713,10 +680,23 @@
return ports
+def outfile(cellpath, define_data, ftype='', extra='', exists=False):
+ fname = define_data['name'].lower().replace('$', '_')
+ if ftype:
+ ftype = '.'+ftype
+ outpath = os.path.join(cellpath, f'{define_data["file_prefix"]}{extra}{ftype}.v')
+ if exists is None:
+ pass
+ elif not exists:
+ #assert not os.path.exists(outpath), "Refusing to overwrite existing file:"+outpath
+ print("Creating", outpath)
+ elif exists:
+ assert os.path.exists(outpath), "Missing required:"+outpath
+ return outpath
+
+
def write_blackbox(cellpath, define_data):
- outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.blackbox.v")
- assert not os.path.exists(outpath), outpath
- print("Creating", outpath)
+ outpath = outfile(cellpath, define_data, 'blackbox')
with open(outpath, "w+") as f:
write_verilog_header(
f,
@@ -731,9 +711,11 @@
def write_blackbox_pp(cellpath, define_data):
- outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.pp.blackbox.v")
- assert not os.path.exists(outpath), outpath
- print("Creating", outpath)
+ if define_data['type'] == 'cell':
+ ofile = 'pp.blackbox'
+ else:
+ ofile = 'blackbox'
+ outpath = outfile(cellpath, define_data, ofile)
with open(outpath, "w+") as f:
write_verilog_header(
f,
@@ -758,6 +740,7 @@
pports = [None,]
while len(ports) > 0:
+ assert ports[0][0], ports[0]
if pports[-1] and pports[-1][0].type != ports[0][0].type:
pports.append(None)
pports.append(ports.pop(0))
@@ -768,10 +751,211 @@
return pports
+def write_primitive(cellpath, define_data):
+ assert define_data['type'] == 'primitive', define_data
+ outpath = outfile(cellpath, define_data)
+ table_datafile = outpath.replace('.v', '.table.tsv')
+ assert os.path.exists(table_datafile), (table_data, define_data)
+
+ table_data = list(csv.reader(open(table_datafile, 'r', newline=''), delimiter='\t'))
+
+ with open(outpath, "w+") as f:
+ write_verilog_header(
+ f,
+ "Verilog primitive definition.",
+ define_data)
+ f.write(f"primitive {define_data['verilog_name']} (")
+ write_verilog_ports(f, define_data['ports'])
+
+ if table_data[0].count(':') == 2:
+ _, pname, _, _ = define_data['ports'][0]
+ assert pname == 'Q', (define_data['ports'], table_data[0])
+ f.write('\n reg Q;\n')
+
+ maxlen = [max(len(r[i]) for r in table_data if len(r) > i) for i in range(0, len(table_data[0]))]
+ for i in range(0, len(maxlen)):
+ if table_data[0][i] == ':':
+ continue
+ if maxlen[i] == 1:
+ maxlen[i] += 2
+ if maxlen[i] == 2:
+ maxlen[i] += 1
+
+ f.write('\n')
+ f.write(' table\n')
+ prefix_first = ' // '
+ prefix_rest = ' '
+ for i, r in enumerate(table_data):
+ if i == 0:
+ f.write(prefix_first)
+ else:
+ f.write(prefix_rest)
+
+ if len(r) != len(maxlen):
+ f.write('// ')
+ f.write(repr(r))
+ continue
+
+ for j, c in enumerate(r[:-1]):
+ f.write(c.center(maxlen[j]))
+ f.write(' ')
+
+ if i == 0:
+ assert r[-1] == 'Comments', (i, r)
+ f.write('\n')
+ continue
+ f.write(' ;')
+ if r[-1]:
+ f.write(' // ')
+ f.write(r[-1])
+ f.write('\n')
+ f.write(' endtable\n')
+ f.write('endprimitive\n')
+ write_verilog_footer(f)
+
+
+def write_testbench(cellpath, define_data):
+ ports_by_class = defaultdict(lambda: list())
+ ports_by_dir = defaultdict(lambda: list())
+ for pclass, pname, pdir, ptype in define_data['ports']:
+ pg = PortGroup.classify(pname, define_data['name'])
+ ports_by_class[pg].append((pclass, pname, pdir, ptype))
+ ports_by_dir[pdir].append((pclass, pname, pdir, ptype))
+
+ pprint.pprint(ports_by_class)
+ pprint.pprint(ports_by_dir)
+
+ vfile = outfile(cellpath, define_data, exists=True)
+ outpath = outfile(cellpath, define_data, 'tb')
+
+ vfile = os.path.relpath(vfile, os.path.dirname(outpath))
+
+ with open(outpath, "w+") as f:
+ write_verilog_header(
+ f,
+ "Autogenerated test bench.",
+ define_data)
+
+ f.write('`include "{}"\n'.format(vfile))
+ f.write('\n')
+
+ f.write('module top();\n')
+ f.write('\n')
+
+ port_args = []
+
+
+ input_port_names = []
+ for pclass, pname, pdir, ptype in ports_by_dir['input']:
+ if PortGroup.classify(pname, define_data['name']) == PortGroup.CLOCK:
+ continue
+ input_port_names.append(pname)
+ maxlen = max(len(i) for i in input_port_names)
+ input_port_names = [n.ljust(maxlen) for n in input_port_names]
+
+ f.write(' // Inputs are registered\n')
+ for pclass, pname, pdir, ptype in ports_by_dir['input']:
+ if PortGroup.classify(pname, define_data['name']) == PortGroup.CLOCK:
+ continue
+ assert pdir == 'input'
+ f.write(" reg {};\n".format(pname))
+ port_args.append('.{0}({0})'.format(pname))
+ f.write('\n');
+
+ f.write(' // Outputs are wires\n')
+ for pclass, pname, pdir, ptype in ports_by_dir['output']:
+ assert pdir == 'output'
+ f.write(" wire {};\n".format(pname))
+ port_args.append('.{0}({0})'.format(pname))
+ f.write('\n');
+
+ f.write("""\
+ initial
+ begin
+ // Initial state is x for all inputs.
+""")
+ indent = " "
+ for n in sorted(input_port_names):
+ f.write(indent+"{0} = 1'bX;\n".format(n))
+
+ f.write("\n")
+
+ DELTA = 20
+ i = 0
+
+ # Set all the inputs to 0, one at a time
+ # x -> 0
+ for n in sorted(input_port_names):
+ i += DELTA
+ f.write(indent+"#{0:<4d} {1} = 1'b0;\n".format(i, n))
+
+ # Set all the inputs to 1, one at a time
+ # 0 -> 1
+ for n in sorted(input_port_names):
+ i += DELTA
+ f.write(indent+"#{0:<4d} {1} = 1'b1;\n".format(i, n))
+
+ # Set all the inputs to zero, one at a time
+ # 1 -> 0
+ for n in sorted(input_port_names):
+ i += DELTA
+ f.write(indent+"#{0:<4d} {1} = 1'b0;\n".format(i, n))
+
+ # Set all the inputs to input, one at a time
+ # 0 -> 1
+ for n in reversed(sorted(input_port_names)):
+ i += DELTA
+ f.write(indent+"#{0:<4d} {1} = 1'b1;\n".format(i, n))
+
+ # Set all the inputs to x, one at a time
+ # 1 -> 0
+ for n in reversed(sorted(input_port_names)):
+ i += DELTA
+ f.write(indent+"#{0:<4d} {1} = 1'bx;\n".format(i, n))
+
+ f.write("""\
+ end
+
+""")
+
+
+ if PortGroup.CLOCK in ports_by_class:
+ assert len(ports_by_class[PortGroup.CLOCK]) == 1, ports
+
+ clk_port = ports_by_class[PortGroup.CLOCK][0]
+ clk_class, clk_name, clk_dir, clk_type = clk_port
+ assert clk_class == 'signal', clk_port
+ assert clk_dir == 'input', clk_port
+ assert clk_type == '', clk_port
+ port_args.append('.{0}({0})'.format(clk_name))
+
+ f.write("""\
+ // Create a clock
+ reg {0};
+ initial
+ begin
+ {0} = 1'b0;
+ end
+
+ always
+ begin
+ #{1} {0} = ~{0};
+ end
+
+""".format(clk_name, DELTA//4))
+
+ f.write("""\
+ {} dut ({args});
+
+""".format(define_data['verilog_name'], args=", ".join(port_args)))
+
+ f.write('endmodule\n')
+ write_verilog_footer(f)
+ pass
+
+
def write_symbol(cellpath, define_data):
- outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.symbol.v")
- assert not os.path.exists(outpath), outpath
- print("Creating", outpath)
+ outpath = outfile(cellpath, define_data, 'symbol')
# Group the ports to make a nice symbol
pports = group_ports_for_symbol(define_data, ['signal'])
@@ -790,9 +974,11 @@
def write_symbol_pp(cellpath, define_data):
- outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.pp.symbol.v")
- assert not os.path.exists(outpath), outpath
- print("Creating", outpath)
+ if define_data['type'] == 'cell':
+ ofile = 'pp.symbol'
+ else:
+ ofile = 'symbol'
+ outpath = outfile(cellpath, define_data, ofile)
# Group the ports to make a nice symbol
pports = group_ports_for_symbol(define_data, ['signal', 'power'])
@@ -812,7 +998,7 @@
def write_verilog_wrapper(f, extra, supplies, ports, define_data):
f.write('\n')
f.write('`celldefine\n')
- f.write(f"module {define_data['library']}__{define_data['name']}{extra} (")
+ f.write(f"module {define_data['verilog_name']}{extra} (")
write_verilog_ports(f, pp_ports(define_data))
write_verilog_parameters(f, define_data)
if supplies:
@@ -828,7 +1014,7 @@
if ports:
ports_str += ",\n ".join('.{0}({0})'.format(p[1]) for p in ports);
- f.write(f" {define_data['library']}__{define_data['name']} cell ")
+ f.write(f" {define_data['verilog_name']} cell ")
f.write(param_str)
f.write('(\n ')
f.write(ports_str)
@@ -844,9 +1030,8 @@
def write_drive_wrapper(drive, cellpath, define_data):
- outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}_{drive}.v")
- basefile = f"{define_data['library']}__{define_data['name']}"
- assert not os.path.exists(outpath), outpath
+ outpath = os.path.join(cellpath, f'{define_data["file_prefix"]}_{drive}.v')
+ #assert not os.path.exists(outpath), outpath
print("Creating", outpath)
with open(outpath, "w+") as f:
@@ -854,7 +1039,7 @@
f,
f"Verilog wrapper for {define_data['name']} drive {drive}.",
define_data)
- f.write(f'`include "{basefile}.v"\n')
+ f.write(f'`include "{define_data["file_prefix"]}.v"\n')
f.write('\n')
f.write('`ifdef USE_POWER_PINS\n')
f.write('/*********************************************************/\n')
@@ -879,15 +1064,21 @@
assert os.path.exists(define_json), define_json
define_data = json.load(open(define_json))
- write_blackbox(cellpath, define_data)
+ if define_data['type'] == 'cell':
+ write_blackbox(cellpath, define_data)
write_blackbox_pp(cellpath, define_data)
- write_symbol(cellpath, define_data)
+ if define_data['type'] == 'cell':
+ write_symbol(cellpath, define_data)
write_symbol_pp(cellpath, define_data)
- for d in drive_strengths(define_data['name'], cellpath):
- write_drive_wrapper(d, cellpath, define_data)
+ if define_data['type'] == 'cell':
+ for d in drive_strengths(define_data['name'], cellpath):
+ write_drive_wrapper(d, cellpath, define_data)
+ if define_data['type'] == 'primitive':
+ write_primitive(cellpath, define_data)
+ write_testbench(cellpath, define_data)
return