add a simple build/test script
This commit is contained in:
parent
3111d069e6
commit
e83f3a993c
|
@ -1 +1,2 @@
|
||||||
.envrc
|
.envrc
|
||||||
|
out/
|
||||||
|
|
|
@ -27,7 +27,9 @@
|
||||||
nextpnr
|
nextpnr
|
||||||
openfpgaloader
|
openfpgaloader
|
||||||
picocom
|
picocom
|
||||||
python3
|
(python3.withPackages (py-pkgs: [
|
||||||
|
py-pkgs.invoke
|
||||||
|
]))
|
||||||
symbiyosys
|
symbiyosys
|
||||||
trellis
|
trellis
|
||||||
verilator
|
verilator
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
from pathlib import Path
|
||||||
|
from invoke import task
|
||||||
|
from textwrap import dedent
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
def ensure_build_dirs(root : Path, *subdirs : list[Path]):
|
||||||
|
if root.is_file():
|
||||||
|
root = root.parent / root.stem
|
||||||
|
ret = []
|
||||||
|
for d in subdirs:
|
||||||
|
out = "out" / root / d
|
||||||
|
out.mkdir(mode=0o750, parents=True, exist_ok=True)
|
||||||
|
ret.append(out)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def bsc_root(c):
|
||||||
|
out = c.run("bsc --help", hide="both")
|
||||||
|
for l in out.stdout.splitlines():
|
||||||
|
dir_prefix = "Bluespec directory: "
|
||||||
|
if l.startswith(dir_prefix):
|
||||||
|
return Path(l[len(dir_prefix):])
|
||||||
|
raise RuntimeError("Couldn't locate Bluespec root dir")
|
||||||
|
|
||||||
|
def find_verilog_modules(c, modules):
|
||||||
|
libpaths = [Path("lib"), bsc_root(c) / "Verilog"]
|
||||||
|
ret = []
|
||||||
|
for m in modules:
|
||||||
|
module_path = None
|
||||||
|
for p in libpaths:
|
||||||
|
f = p / Path(module).with_suffix(".v")
|
||||||
|
if f.is_file():
|
||||||
|
module_path = f
|
||||||
|
break
|
||||||
|
if module_path is None:
|
||||||
|
raise RuntimeError(f"Cannot find verilog module {m} in {libpaths}")
|
||||||
|
ret.append(module_path)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def print_filtered_paragraphs(s, *paragraph_prefixes, common_prefix=""):
|
||||||
|
for block in s.split("\n\n"):
|
||||||
|
for p in paragraph_prefixes:
|
||||||
|
if block.removeprefix(common_prefix).startswith(p):
|
||||||
|
print('\n'.join(s.removeprefix(common_prefix) for s in block.splitlines()))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
def expand_build_target(target):
|
||||||
|
t = Path(target)
|
||||||
|
if t.is_dir():
|
||||||
|
yield from t.glob("**/*.bsv")
|
||||||
|
elif t.is_file() and t.suffix == ".bsv":
|
||||||
|
yield t
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown target type {t}")
|
||||||
|
|
||||||
|
def resolve_synth_target(target):
|
||||||
|
if '/' not in str(target):
|
||||||
|
target = "hardware" / Path(target)
|
||||||
|
if target.is_dir():
|
||||||
|
target /= "Top.bsv"
|
||||||
|
if not target.is_file():
|
||||||
|
raise ArgumentError(f"Unknown target type {target}")
|
||||||
|
return target
|
||||||
|
|
||||||
|
def expand_test_target(target):
|
||||||
|
t = Path(target)
|
||||||
|
if t.is_dir():
|
||||||
|
yield from t.glob("**/*_Test.bsv")
|
||||||
|
elif t.is_file() and t.name.endswith("_Test.bsv"):
|
||||||
|
yield t
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown target type {t}")
|
||||||
|
|
||||||
|
@task
|
||||||
|
def build(c, target="."):
|
||||||
|
for target in expand_build_target(target):
|
||||||
|
out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc")
|
||||||
|
print(f"Building {target}")
|
||||||
|
c.run(f"bsc -aggressive-conditions -check-assert -u -verilog -info-dir {out_info} -vdir {out_verilog} -bdir {out_bsc} -p {target.parent}:lib:%/Libraries -show-module-use -show-compiles {target}")
|
||||||
|
|
||||||
|
@task
|
||||||
|
def synth(c, target):
|
||||||
|
target = resolve_synth_target(target)
|
||||||
|
|
||||||
|
pin_map = target.parent / "pin_map.lpf"
|
||||||
|
if not pin_map.is_file():
|
||||||
|
raise ArgumentError(f"Pin mapping {pin_map} not found")
|
||||||
|
|
||||||
|
module_name = Path(f"mk{target.stem}")
|
||||||
|
out_info, out_verilog, out_bsc, out_yosys, out_nextpnr = ensure_build_dirs(target, "info", "verilog", "bsc", "yosys", "nextpnr")
|
||||||
|
|
||||||
|
build(c, target)
|
||||||
|
|
||||||
|
verilog_files = [out_verilog / module_name.with_suffix(".v")]
|
||||||
|
with open(out_verilog / module_name.with_suffix(".use")) as f:
|
||||||
|
verilog_files.extend(find_verilog_modules(c, f.read().splitlines()))
|
||||||
|
|
||||||
|
yosys_script = out_yosys / "script.ys"
|
||||||
|
yosys_json = out_yosys / module_name.with_suffix(".json")
|
||||||
|
yosys_report = out_yosys / module_name.with_suffix(".stats")
|
||||||
|
yosys_log = out_yosys / module_name.with_suffix(".log")
|
||||||
|
with open(yosys_script, "w", encoding="UTF-8") as f:
|
||||||
|
f.write(dedent(f"""\
|
||||||
|
read_verilog -sv -defer {' '.join(str(f) for f in verilog_files)}
|
||||||
|
hierarchy -top {module_name}
|
||||||
|
synth_ecp5 -top {module_name} -json {yosys_json}
|
||||||
|
tee -o {yosys_report} stat
|
||||||
|
"""))
|
||||||
|
c.run(f"yosys -q -L {yosys_log} -T {yosys_script}")
|
||||||
|
with open(yosys_report) as f:
|
||||||
|
print(f.read())
|
||||||
|
|
||||||
|
nextpnr_out = out_nextpnr / module_name.with_suffix(".pnr")
|
||||||
|
out = c.run(f"nextpnr-ecp5 --85k --detailed-timing-report --report {out_nextpnr / "timing.json"} --json {yosys_json} --lpf {pin_map} --package=CABGA381 --textcfg {nextpnr_out}", hide='stderr')
|
||||||
|
print_filtered_paragraphs(out.stderr, "Device utilization", "Critical path", common_prefix="Info: ")
|
||||||
|
|
||||||
|
bitstream = nextpnr_out.with_suffix(".bit")
|
||||||
|
c.run(f"ecppack {nextpnr_out} {bitstream}")
|
||||||
|
print(f"Wrote bitstream to {bitstream}")
|
||||||
|
|
||||||
|
@task
|
||||||
|
def test(c, target="."):
|
||||||
|
for target in expand_test_target(target):
|
||||||
|
out_info, out_sim, out_bsc = ensure_build_dirs(target, "info", "sim", "bsc")
|
||||||
|
c.run(f"bsc -show-schedule -aggressive-conditions -check-assert -u -sim -info-dir {out_info} -simdir {out_sim} -bdir {out_bsc} -g mkTB -p {target.parent}:lib:%/Libraries {target}")
|
||||||
|
exec = out_sim / "TB"
|
||||||
|
c.run(f"bsc -p {out_bsc} -sim -simdir {out_sim} -e mkTB -o {exec}")
|
||||||
|
testdata = out_sim / "testdata"
|
||||||
|
testdata_tgt = target.parent / "testdata"
|
||||||
|
testdata_tgt = testdata_tgt.relative_to(out_sim, walk_up=True)
|
||||||
|
testdata.unlink(missing_ok=True)
|
||||||
|
testdata.symlink_to(testdata_tgt, target_is_directory=True)
|
||||||
|
print("before run")
|
||||||
|
with c.cd(out_sim):
|
||||||
|
c.run(f"./TB -V {target.stem}.vcd")
|
||||||
|
|
||||||
|
@task
|
||||||
|
def clean(c):
|
||||||
|
shutil.rmtree("out")
|
Loading…
Reference in New Issue