From 45817415007997c4f364d434443210359f71daaa Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Wed, 19 Aug 2026 21:13:59 +0700 Subject: [PATCH] fix(fpga-build): assert chipdb/device agreement before P&R nextpnr routes against whatever database it is handed; a mismatched pair survives P&R and dies two steps later in fasm2frames as 'Part None not found' (#2225). Fail at fpga-build start when an explicit --chipdb filename disagrees with --device, and derive the default chipdb path from --device instead of hardcoding the 100T filename -- the hardcode is exactly how the 200T default flip survived P&R. Negative control: planted mismatch bails instantly with both names and the consequence; a matched pair proceeds. Closes #2231. --- bootstrap/src/main.rs | 23 ++++++++++++++++++++++- docs/NOW.md | 13 +++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/bootstrap/src/main.rs b/bootstrap/src/main.rs index 48dd966c49..2a05e5e75d 100644 --- a/bootstrap/src/main.rs +++ b/bootstrap/src/main.rs @@ -6778,6 +6778,24 @@ fn run_fpga_build( prjxray_db_path: Option<&str>, output: &str, ) -> anyhow::Result<()> { + // #2225: P&R against a database for a different part is silent poison — the + // router succeeds against whatever database it is handed, and the mismatch + // surfaces two steps later in fasm2frames as "Part None not found". Check the + // pair HERE, before minutes of synthesis, where the cause is still visible. + if let Some(p) = chipdb_path { + let stem = Path::new(p) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or(""); + if stem != device { + anyhow::bail!( + "chipdb/device mismatch: --chipdb names '{}' but --device is '{}'.\n\ + nextpnr would place-and-route against the wrong database and the\n\ + build would fail two steps later in fasm2frames. Pass a matching pair.", + stem, device + ); + } + } let specs_dir = repo_root.join("specs/fpga"); let build_dir = repo_root.join(output); let gen_dir = build_dir.join("generated"); @@ -7069,7 +7087,10 @@ endmodule let chipdb = match chipdb_path { Some(p) => PathBuf::from(p), None => { - let default = PathBuf::from("build/fpga/chipdb/xc7a100tcsg324-1.bin"); + // Derive the default from --device instead of naming one part: a + // hardcoded 100T filename here is exactly how the 200T default flip + // survived P&R against the wrong database (#2225). + let default = PathBuf::from(format!("build/fpga/chipdb/{}.bin", device)); if repo_root.join(&default).exists() { repo_root.join(&default) } else { diff --git a/docs/NOW.md b/docs/NOW.md index 5e61bb8ce7..7aa66c44f4 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,3 +1,16 @@ +# NOW -- the mismatch now fails where the cause is (2026-08-19) + +Last updated: 2026-08-19 + +## fix(fpga-build): chipdb/device agreement asserted before P&R (Closes #2231) + +- t27c fpga-build now fails at start when an explicit --chipdb filename disagrees + with --device, naming both and the two-steps-later consequence (fasm2frames + 'Part None not found'); the default chipdb path is derived from --device instead + of hardcoding the 100T filename -- the hardcode is exactly how the 200T default + flip survived P&R against the wrong database (#2225) +- Negative controls: planted mismatch bails instantly; matched pair proceeds + # NOW -- the device default flipped under a clean merge (2026-08-19) Last updated: 2026-08-19