tasks.py: prettify output, support running partial synthesis

Partial synth is handy when writing gnarly Bluespec modules, because it
lets you inspect the Verilog output of the Bluespec compiler as well as
Yosys's compile output at various stages of synthesis, to see if things
are being produced the way you expect.
This commit is contained in:
David Anderson 2024-08-13 20:53:47 -07:00
parent e6fa717507
commit db30e4a23f
1 changed files with 53 additions and 15 deletions

View File

@ -74,49 +74,86 @@ def expand_test_target(target):
else: else:
raise ValueError(f"Unknown target type {t}") raise ValueError(f"Unknown target type {t}")
def phase(name):
print("")
print("*"*(len(name)+6))
print(f"** {name} **")
print("*"*(len(name)+6))
print("")
@task @task
def build(c, target="."): def build(c, target="."):
phase("Compile Bluespec")
verilog_files = []
for target in expand_build_target(target): for target in expand_build_target(target):
out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc") out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc")
print(f"Building {target}") 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}") 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}")
module_name = Path(f"mk{target.stem}")
verilog_files.append(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()))
print("\nVerilog files for synthesis:")
for v in verilog_files:
print(" "+str(v))
return verilog_files
@task @task
def synth(c, target): def synth(c, target):
target = resolve_synth_target(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") out_info, out_verilog, out_bsc, out_yosys, out_nextpnr = ensure_build_dirs(target, "info", "verilog", "bsc", "yosys", "nextpnr")
build(c, target) module_name = Path(f"mk{target.stem}")
verilog_files = 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()))
phase("Logic synthesis")
yosys_script = out_yosys / "script.ys" yosys_script = out_yosys / "script.ys"
yosys_json = out_yosys / module_name.with_suffix(".json") yosys_json = out_yosys / module_name.with_suffix(".json")
yosys_report = out_yosys / module_name.with_suffix(".stats") yosys_report = out_yosys / module_name.with_suffix(".stats")
yosys_log = out_yosys / module_name.with_suffix(".log") yosys_log = out_yosys / module_name.with_suffix(".log")
yosys_preprocessed = out_yosys / module_name.with_suffix(".v")
yosys_compiled = out_yosys / f"{module_name.stem}_compiled.v"
with open(yosys_script, "w", encoding="UTF-8") as f: with open(yosys_script, "w", encoding="UTF-8") as f:
f.write(dedent(f"""\ f.write(dedent(f"""\
read_verilog -sv -defer {' '.join(str(f) for f in verilog_files)} read_verilog -sv -defer {' '.join(str(f) for f in verilog_files)}
hierarchy -top {module_name} hierarchy -top {module_name}
synth_ecp5 -top {module_name} -json {yosys_json} synth_ecp5 -top {module_name} -run :map_ram
write_verilog -sv {yosys_preprocessed}
synth_ecp5 -run map_ram: -json {yosys_json}
write_verilog -sv {yosys_compiled}
tee -o {yosys_report} stat tee -o {yosys_report} stat
""")) """))
c.run(f"yosys -q -L {yosys_log} -T {yosys_script}") c.run(f"yosys -q -L {yosys_log} -T {yosys_script}")
with open(yosys_report) as f: with open(yosys_report) as f:
print(f.read()) ls = f.readlines()[3:]
print("".join(ls))
print(f" JSON : {yosys_json}")
print(f" Log : {yosys_log}")
print(f" Flat : {yosys_preprocessed}")
print(f"Compiled : {yosys_compiled}")
pin_map = target.parent / "pin_map.lpf"
if not pin_map.is_file():
print(f"WARNING: no pin map at {pin_map}, skipping place&route and bitstream generation")
return
phase("Place and route")
nextpnr_out = out_nextpnr / module_name.with_suffix(".pnr") 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') nextpnr_log = out_nextpnr / module_name.with_suffix(".log")
print_filtered_paragraphs(out.stderr, "Device utilization", "Critical path", common_prefix="Info: ") nextpnr_timing = out_nextpnr / module_name.with_suffix(".log")
out = c.run(f"nextpnr-ecp5 --85k --detailed-timing-report -l {nextpnr_log} --report {nextpnr_timing} --json {yosys_json} --lpf {pin_map} --package=CABGA381 --textcfg {nextpnr_out}") #, hide='stderr')
print_filtered_paragraphs(out.stderr, "Device utilisation", "Critical path", "Max frequency", "Max delay", common_prefix="Info: ")
print(f" PNR : {nextpnr_out}")
print(f" Log : {nextpnr_log}")
print(f"Timing : {nextpnr_timing}")
phase("Bitstream generation")
bitstream = nextpnr_out.with_suffix(".bit") bitstream = nextpnr_out.with_suffix(".bit")
c.run(f"ecppack {nextpnr_out} {bitstream}") c.run(f"ecppack {nextpnr_out} {bitstream}")
print(f"Wrote bitstream to {bitstream}") print(f"Wrote bitstream to {bitstream}")
@ -139,4 +176,5 @@ def test(c, target="."):
@task @task
def clean(c): def clean(c):
shutil.rmtree("out") if Path("out").is_dir():
shutil.rmtree("out")