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:
parent
e6fa717507
commit
db30e4a23f
68
tasks.py
68
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")
|
||||
|
|
Loading…
Reference in New Issue