From 18c03845e30580bae8e31b9a74b2992bb26c889b Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 18 Oct 2026 15:01:05 +0700 Subject: [PATCH] fpga(build): fpga-build skips t27b bitstream specs; no gen-verilog rows for rc_filter_ax7203 and ddr3_head_bandwidth (Closes #10300) Co-Authored-By: Claude Sonnet 5.5 --- bootstrap/src/main.rs | 66 +++++++++++++++++------------------------ tools/elab_baseline.txt | 19 ++++++------ 2 files changed, 38 insertions(+), 47 deletions(-) diff --git a/bootstrap/src/main.rs b/bootstrap/src/main.rs index 4d520bff07..4c82df2283 100644 --- a/bootstrap/src/main.rs +++ b/bootstrap/src/main.rs @@ -8014,6 +8014,14 @@ fn run_fpga_build( for module in &modules { let spec_file = specs_dir.join(format!("{}.t27", module)); let out_file = gen_dir.join(format!("{}.v", module)); + // #10300: a pin attribute (`const x_LOC : str`) marks t27b's bitstream idiom. Its + // pins and primitive instances are lowered by `t27b verilog`, which builds the + // board RTL; gen-verilog has no such lowering and would lint a file nobody ships. + let src = fs::read_to_string(&spec_file).with_context(|| format!("read {}", spec_file.display()))?; + if src.lines().any(|l| l.starts_with("const ") && l.contains("_LOC : str")) { + println!(" SKIP {}.v (t27b bitstream spec; `t27b verilog` lowers it)", module); + continue; + } let gen_cmd = if use_hir { "gen-verilog-hir" } else { "gen-verilog" }; let status = std::process::Command::new(&t27c) .arg(gen_cmd) @@ -8180,6 +8188,26 @@ endmodule let synth_json = synth_dir.join("synth.json"); + let synth_script = build_dir.join("synth.ys"); + let verilog_files = if minimal { + format!("{gen}/{top}.v", gen = gen_dir.display(), top = top) + } else { + format!("{gen}/mac.v {gen}/uart.v {gen}/spi.v {gen}/bridge.v {gen}/top_level.v {gen}/{top}.v", gen = gen_dir.display(), top = top) + }; + fs::write( + &synth_script, + format!( + // -sv: the emitter uses SV static casts. -DSIMULATION: strips `ifndef + // SIMULATION` bench blocks whose $display carries non-constant + // args that synthesis cannot evaluate (mac.v:535; the repo + // convention, see suite.rs and verify_emit_bitexact.py). + "read_verilog -sv -DSIMULATION {files}\nhierarchy -check -top {top}\nproc; opt; fsm; opt; memory; opt\nsynth_xilinx -top {top}\nwrite_json {json}\nstat\n", + files = verilog_files, + top = top, + json = synth_json.display(), + ), + )?; + if use_docker { if std::process::Command::new("docker") .arg("--version") @@ -8191,25 +8219,6 @@ endmodule anyhow::bail!("Docker is required for synthesis but not installed. Use --smoke for gen-only, or install Yosys locally and pass --docker false."); } println!("=== Synthesizing with Yosys (Docker) ==="); - let synth_script = build_dir.join("synth.ys"); - let verilog_files = if minimal { - format!("{gen}/{top}.v", gen = gen_dir.display(), top = top) - } else { - format!("{gen}/mac.v {gen}/uart.v {gen}/spi.v {gen}/bridge.v {gen}/top_level.v {gen}/{top}.v", gen = gen_dir.display(), top = top) - }; - fs::write( - &synth_script, - format!( - // -sv: the emitter uses SV static casts. -DSIMULATION: strips `ifndef - // SIMULATION` bench blocks whose $display carries non-constant - // args that synthesis cannot evaluate (mac.v:535; the repo - // convention, see suite.rs and verify_emit_bitexact.py). - "read_verilog -sv -DSIMULATION {files}\nhierarchy -check -top {top}\nproc; opt; fsm; opt; memory; opt\nsynth_xilinx -top {top}\nwrite_json {json}\nstat\n", - files = verilog_files, - top = top, - json = synth_json.display(), - ), - )?; let status = std::process::Command::new("docker") .args(["run", "--rm", "-v", &format!("{}:/project", repo_root.display()), "-w", "/project", "hdlc/oss-cad-suite:latest", "yosys", "-s", &format!("{}", synth_script.display())]) .status() @@ -8220,25 +8229,6 @@ endmodule println!("Synthesis complete (Docker)."); } else { println!("=== Synthesizing with local Yosys ==="); - let synth_script = build_dir.join("synth.ys"); - let verilog_files = if minimal { - format!("{gen}/{top}.v", gen = gen_dir.display(), top = top) - } else { - format!("{gen}/mac.v {gen}/uart.v {gen}/spi.v {gen}/bridge.v {gen}/top_level.v {gen}/{top}.v", gen = gen_dir.display(), top = top) - }; - fs::write( - &synth_script, - format!( - // -sv: the emitter uses SV static casts. -DSIMULATION: strips `ifndef - // SIMULATION` bench blocks whose $display carries non-constant - // args that synthesis cannot evaluate (mac.v:535; the repo - // convention, see suite.rs and verify_emit_bitexact.py). - "read_verilog -sv -DSIMULATION {files}\nhierarchy -check -top {top}\nproc; opt; fsm; opt; memory; opt\nsynth_xilinx -top {top}\nwrite_json {json}\nstat\n", - files = verilog_files, - top = top, - json = synth_json.display(), - ), - )?; let status = std::process::Command::new("yosys") .arg("-s") .arg(&synth_script) diff --git a/tools/elab_baseline.txt b/tools/elab_baseline.txt index c3fbd70676..fe39be3c82 100644 --- a/tools/elab_baseline.txt +++ b/tools/elab_baseline.txt @@ -47,14 +47,16 @@ # `eth_rx 0` (#8333) arrives with its spec; it elaborates clean too. # `trinet_node 0` (#8356) arrives with its spec; it elaborates clean too. # -# 2026-10-18: +8. `rc_filter_ax7203 8` is a NEW module -# (specs/fpga/rc_filter_ax7203.t27, #10278) and a KNOWN GAP, not a regression: -# the spec is written for `t27b verilog`, which is what builds its bitstream -# (board receipt PASS). t27c gen-verilog has no lowering for its pin attributes -# or primitive instances: 3 pins read `undefined`, and IBUFDS, PLLE2_ADV, BUFG -# and IBUF become localparams with a function call (5 errors, one of them the -# PLL's feedback loop read as a recursive parameter). Same class as -# ddr3_head_bandwidth, which is left to that spec's owner. +# 2026-10-18: no rows for `rc_filter_ax7203` and `ddr3_head_bandwidth`, and +# that is a skip, not a fix (#10300). Both are written in t27b's bitstream +# idiom: pin attributes (`const x_LOC : str`) and empty-bodied primitive fns +# (IBUFDS, PLLE2_ADV, BUFG, IBUF, litedram_core). `t27b verilog` lowers those +# and builds the board RTL (rc_filter receipt 2130/2130 PASS); t27c gen-verilog +# has no such lowering (8 and 64 errors measured, all unbound pins and +# primitives read as localparams). `t27c fpga-build` therefore prints SKIP for +# any spec with a `_LOC` pin and generates no .v for it. A GONE row would fail +# the ratchet, so these modules carry none. If gen-verilog ever lowers pins +# and instances, drop the skip and record the counts it measures. # `openxc7_dsp_fabric 0`, `wasm_board_ax7203 0` and `wasm_interp 0` reached # master without rows; all elaborate clean, so a 0 row hides nothing. adapter 0 @@ -90,7 +92,6 @@ partition 2 placement 6 power 2 power_analysis 1 -rc_filter_ax7203 8 reset_sync 2 rgmii 0 rgmii_sniff 0