From db30e4a23fdf6bbd944d36cafabd53a2dbbb4ddd Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 13 Aug 2024 20:53:47 -0700 Subject: [PATCH] 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. --- tasks.py | 68 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/tasks.py b/tasks.py index bc23654..b6170c4 100644 --- a/tasks.py +++ b/tasks.py @@ -74,49 +74,86 @@ def expand_test_target(target): else: raise ValueError(f"Unknown target type {t}") +def phase(name): + print("") + print("*"*(len(name)+6)) + print(f"** {name} **") + print("*"*(len(name)+6)) + print("") + @task def build(c, target="."): + phase("Compile Bluespec") + + verilog_files = [] 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}") + 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 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())) + module_name = Path(f"mk{target.stem}") + verilog_files = build(c, target) + phase("Logic synthesis") 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") + 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: 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} + 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 """)) c.run(f"yosys -q -L {yosys_log} -T {yosys_script}") 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") - 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: ") + nextpnr_log = out_nextpnr / module_name.with_suffix(".log") + 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") c.run(f"ecppack {nextpnr_out} {bitstream}") print(f"Wrote bitstream to {bitstream}") @@ -139,4 +176,5 @@ def test(c, target="."): @task def clean(c): - shutil.rmtree("out") + if Path("out").is_dir(): + shutil.rmtree("out")