Skip to content

Commit 2f5af00

Browse files
Dmitrii Vasilevclaude
andcommitted
feat(igla): Wave Loop 431 — XADC→PVT context bridge, computable envelope, measured-to-lean --json summary hardening
Closes #1389 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e622787 commit 2f5af00

12 files changed

Lines changed: 825 additions & 89 deletions

.trinity/current-issue.md

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,61 @@
1-
# Wave Loop 431 — FPGA boot-evidence next variant (physical CCLK / XADC sweep / formal fallback)
1+
# Wave Loop 432 — FPGA boot-evidence next variant (real CCLK capture, live XADC validation, or master-merge)
22

3-
**Issue:** #1389
4-
**Branch:** `wave-loop-431`
5-
**Milestone:** Continue the FPGA boot-evidence line from Wave Loop 430.
3+
**Issue:** #1391
4+
**Branch:** `wave-loop-432`
5+
**Milestone:** Continue the FPGA boot-evidence line from Wave Loop 431.
66

77
---
88

99
## Goal
1010

11-
Wave Loop 430 added live XADC readout and a formal PVT-envelope bridge while
12-
the bench remained partially blocked (P12 unwired, no relay gate, no DLC10
13-
cable). Wave Loop 431 executes the first available variant from
14-
`docs/reports/FPGA_LOOP_COOPERATION_W431_2026-07-01.md`.
11+
Wave Loop 431 hardened the XADC → PVT context bridge, added a computable Lean 4
12+
envelope check, and improved the machine-readable `measured-to-lean --json`
13+
summary while the bench remained partially blocked (P12 unwired, no relay gate,
14+
no DLC10 cable). Wave Loop 432 executes the first available variant from
15+
`docs/reports/FPGA_LOOP_COOPERATION_W432_2026-07-01.md`.
1516

1617
1. **Variant A (preferred when bench becomes fully available):**
1718
- Confirm P12 is wired to a logic-analyzer channel and a relay/remote-power
1819
gate is available.
19-
- Program SPI flash with OSCFSEL=6 and OSCFSEL=7 variants.
20-
- Capture real CCLK during cold-POR boot for both variants.
21-
- Run `tri fpga cclk-sweep ... --xadc` so boot logs record live operating
22-
points.
20+
- Program SPI flash with OSCFSEL=6 (and OSCFSEL=7 if time permits).
21+
- Capture real CCLK during cold-POR boot.
22+
- Run `tri fpga cclk-sweep ... --xadc` so boot logs record live operating points.
2323
- Import with `tri fpga measured-to-lean --csv/--vcd --raw-ns --standalone
24-
--validate --pvt-context <ctx.json> --out <theorem.lean> --json` and commit
24+
--validate --pvt-context <xadc.json> --out <theorem.lean> --json` and commit
2525
generated Lean theorems plus JSON summaries.
26-
- Document frequencies, duty cycles, and XADC/PVT context in
27-
`fpga/HARDWARE_SSOT.md` §3.6 / §9.6.
26+
- Update `fpga/HARDWARE_SSOT.md` §3.6 with measured frequency/duty/margin.
2827

2928
2. **Variant B (if board is reachable but P12 / relay are still blocked):**
30-
- Run a real `tri fpga cclk-sweep` over OSCFSEL 0..7 with `--xadc` and a
31-
supplied `--pvt-context`, performing manual power cycles.
32-
- Alternatively, import at least one external CSV/VCD capture end-to-end.
33-
- Add a Lean theorem or decidability lemma connecting a concrete XADC JSON
34-
operating point to `xadc_operating_point_envelope_implies_worst_case_bound`.
35-
- Document the sweep/import recipe in `fpga/HARDWARE_SSOT.md`.
29+
- Capture a real `tri fpga read-xadc --json` from the live board.
30+
- Verify the JSON converts to a valid `PvtContext` via `tri fpga pvt-envelope`.
31+
- Generate at least one `measured-to-lean` theorem using the real XADC context
32+
(synthetic CCLK fixture is acceptable for proof-of-pipeline).
33+
- Alternatively, run `tri fpga cclk-sweep` over OSCFSEL 0..5 with `--xadc` and
34+
manual power cycles.
35+
- Refresh `docs/reports/T27_VS_FORMAL_HDL_2026.md` and re-evaluate
36+
`docs/reports/GEN_VERILOG_DEFECTS_REPRO.md`.
3637

3738
3. **Variant C (fallback if bench still blocked):**
38-
- Extend the XADC/PVT theorem library (e.g. computable envelope check or an
39-
implication theorem linking measured raw-ns + XADC to transaction OK).
40-
- Land one safe gen-verilog #1245 sub-fix if narrow and regression-free;
41-
otherwise explicitly defer and update
42-
`docs/reports/GEN_VERILOG_DEFECTS_REPRO.md`.
43-
- Harden `tri fpga measured-to-lean` JSON output further.
44-
- Refresh `docs/reports/T27_VS_FORMAL_HDL_2026.md` if new competitor signals
45-
appear.
39+
- Execute the deferred master-merge/rebase wave to bring the `master` fix set
40+
(`701d79b3b`) into `wave-loop-432` and clear the 7 residual yosys smoke
41+
failures (#1245).
42+
- If the merge is too risky for one wave, land a formal/tooling sub-task:
43+
per-OSCFSEL PVT-corner theorems, machine-readable `sweep-report --json`, or
44+
a deeper competitor refresh.
45+
- Update `docs/reports/GEN_VERILOG_DEFECTS_REPRO.md` with the new baseline.
4646

4747
---
4848

4949
## Definition of done
5050

5151
- [ ] The chosen variant is executed and its acceptance criteria are met.
5252
- [ ] `lake build Trinity.TernaryFPGABoot` passes.
53-
- [ ] `./scripts/tri test` passes with no new failures beyond the documented
54-
7 gen-verilog #1245 failures.
53+
- [ ] `./scripts/tri test` passes with the new documented baseline (ideally 0 new
54+
gen-verilog failures; if Variant C succeeds, the 7 #1245 failures are
55+
cleared).
5556
- [ ] `cargo test --bin tri fpga::` passes.
5657
- [ ] Close-out report and next-wave cooperation variants are written.
57-
- [ ] Issue/branch for Wave Loop 432 are created.
58+
- [ ] Issue/branch for Wave Loop 433 are created.
5859

5960
---
6061

.trinity/current_task/activity.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,3 +1588,7 @@
15881588
- **Commit:** feat(igla): Wave Loop 430 — live XADC readout, PVT-envelope bridge, W431 cooperation\n\n- Add tri fpga read-xadc and --xadc flags on boot-log/cold-por/cclk-sweep.\n- Parse openFPGALoader --read-xadc output and embed live temp/vccint/vccaux in JSON logs.\n- Add XadcOperatingPoint formal bridge and worst-case-bound theorem in TernaryFPGABoot.lean.\n- Update fpga/HARDWARE_SSOT.md, T27_VS_FORMAL_HDL_2026.md, GEN_VERILOG_DEFECTS_REPRO.md.\n- Create WAVE_LOOP_430_REPORT.md and FPGA_LOOP_COOPERATION_W431_2026-07-01.md.\n- Verification: cargo test fpga:: 79/79, lake build pass, tri test pass except 7 pre-existing #1245 failures.\n\nCloses #1388\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
15891589
- **Files:** .trinity/experience.md
15901590

1591+
## 2026-07-05T10:42:19Z — wave-loop-431
1592+
- **Commit:** docs(trinity): W431 setup — current issue, NOW, experience (Wave Loop 430 close-out)\n\n- Point .trinity/current-issue.md and docs/NOW.md to W431 (#1389).\n- Add W430 learnings to .trinity/experience.md.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1593+
- **Files:** .trinity/current-issue.md,.trinity/experience.md,cli/tri/src/fpga.rs,docs/NOW.md,docs/reports/FPGA_LOOP_COOPERATION_W432_2026-07-01.md,docs/reports/FPGA_LOOP_EVIDENCE_W431_2026-07-01.md,docs/reports/GEN_VERILOG_DEFECTS_REPRO.md,docs/reports/T27_VS_FORMAL_HDL_2026.md,docs/reports/WAVE_LOOP_431_REPORT.md,fpga/HARDWARE_SSOT.md,proofs/lean4/Trinity/TernaryFPGABoot.lean
1594+

.trinity/experience.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,60 @@
1+
## 2026-07-01 — Wave Loop 431 (FPGA boot-evidence: XADC → PVT context bridge, computable envelope check, `measured-to-lean --json` summary hardening, W431 close-out / W432 setup)
2+
3+
### What worked
4+
- Executing **Variant C** kept the wave shippable: P12 and the relay gate are
5+
still unwired, so the wave focused on formal/tooling debt instead of physical
6+
capture.
7+
- Converting live XADC `f64` values (°C / V) into the integer `PvtContext` in
8+
`XadcContext::to_pvt_context` removes the manual JSON editing step and makes
9+
`tri fpga read-xadc --json` directly consumable as `--pvt-context`.
10+
- Writing a direct `Bool` envelope check (`xadc_operating_point_within_envelope_dec`)
11+
and proving equivalence with the propositional version avoids the Lean
12+
`Decidable` synthesis failure that blocked the naive `decide (predicate pt)`
13+
approach.
14+
- Proving `xadc_envelope_implies_raw_ns_satisfies_any_in_envelope` and
15+
`xadc_envelope_justifies_worstcase_transaction_proof` means a real, in-envelope
16+
XADC measurement can be used in proof goals without weakening the existing
17+
worst-case transaction theorem.
18+
- Extending `build_measured_to_lean_summary` with `flash_min_half_period_ns`,
19+
`margin_ns`, and a closed `recommendation` vocabulary gives downstream CI a
20+
machine-readable signal instead of free-form text.
21+
- Updating the existing summary unit tests to assert the new fields catches
22+
schema drift immediately.
23+
- Keeping the gen-verilog #1245 deferral explicit in
24+
`docs/reports/GEN_VERILOG_DEFECTS_REPRO.md` prevents scope creep.
25+
26+
### What changed behavior
27+
- `cli/tri/src/fpga.rs`:
28+
- Added `XadcContext::to_pvt_context` and unit tests for rounding / unit
29+
conversion.
30+
- Extended `build_measured_to_lean_summary` with `flash_min_half_period_ns`,
31+
`margin_ns`, and `recommendation`.
32+
- Updated unit tests for the summary builder.
33+
- `proofs/lean4/Trinity/TernaryFPGABoot.lean`:
34+
- Added `xadc_operating_point_within_envelope_dec` with proven `Bool`
35+
propositional equivalence.
36+
- Added `xadc_envelope_implies_raw_ns_satisfies_any_in_envelope`.
37+
- Added `xadc_envelope_justifies_worstcase_transaction_proof`.
38+
- `fpga/HARDWARE_SSOT.md`: added §9.6.1 documenting the XADC → PVT bridge and
39+
the `--json` summary fields.
40+
- `docs/reports/T27_VS_FORMAL_HDL_2026.md`: refreshed for W431; noted Sparkle
41+
July 2026 activity signals.
42+
- `docs/reports/GEN_VERILOG_DEFECTS_REPRO.md`: added W431 triage decision
43+
confirming the same 7 residual yosys smoke failures and recommending a
44+
dedicated master-merge wave in W432.
45+
- Close-out artifacts: `docs/reports/WAVE_LOOP_431_REPORT.md`,
46+
`docs/reports/FPGA_LOOP_EVIDENCE_W431_2026-07-01.md`,
47+
`docs/reports/FPGA_LOOP_COOPERATION_W432_2026-07-01.md`.
48+
- Issue/branch: GitHub issue #1391, branch `wave-loop-432`; PR closes #1389.
49+
50+
### Verification
51+
- `cargo test --bin tri fpga::`: 81/81 pass.
52+
- `lake build Trinity.TernaryFPGABoot`: 2967 jobs, 0 errors.
53+
- `./scripts/tri test`: all phases pass; 7 pre-existing gen-verilog yosys smoke
54+
failures (#1245); 0 FPGA smoke failures; 0 seal mismatches.
55+
56+
---
57+
158
## 2026-07-01 — Wave Loop 430 (FPGA boot-evidence: live XADC readout, PVT-envelope bridge, W430 close-out / W431 setup)
259

360
### What worked

cli/tri/src/fpga.rs

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2694,6 +2694,30 @@ impl XadcContext {
26942694
}
26952695
value
26962696
}
2697+
2698+
/// Convert the live XADC readout to the `PvtContext` used by the PVT envelope.
2699+
/// The XADC reports temperatures in °C and voltages in volts, while the PVT
2700+
/// model uses integer °C and millivolts. Values are rounded to the nearest
2701+
/// integer. The process corner is not measured by the XADC and must be
2702+
/// supplied by the caller (default `ss` for worst-case reasoning).
2703+
fn to_pvt_context(&self, corner: ProcessCorner) -> Result<PvtContext> {
2704+
let temp_c = self
2705+
.temp_c
2706+
.round()
2707+
.clamp(i64::MIN as f64, i64::MAX as f64) as i64;
2708+
let vccint_mv = (self.vccint_v * 1000.0)
2709+
.round()
2710+
.clamp(0.0, u64::MAX as f64) as u64;
2711+
let vccaux_mv = (self.vccaux_v * 1000.0)
2712+
.round()
2713+
.clamp(0.0, u64::MAX as f64) as u64;
2714+
Ok(PvtContext {
2715+
temp_c,
2716+
vccint_mv,
2717+
vccaux_mv,
2718+
process_corner: corner,
2719+
})
2720+
}
26972721
}
26982722

26992723
/// Remove trailing commas that appear immediately before a closing brace or
@@ -3537,7 +3561,7 @@ fn build_measured_to_lean_summary(
35373561
pvt_ctx: &Option<PvtContext>,
35383562
text: &str,
35393563
) -> Result<serde_json::Value> {
3540-
let (source, theorem_base, predicate) = if raw_ns {
3564+
let (source, theorem_base, predicate, period_ns, low_ns, high_ns) = if raw_ns {
35413565
let m: MeasuredCclkRawNs =
35423566
serde_json::from_str(text).context("parse MeasuredCclkRawNs JSON for summary")?;
35433567
let source_suffix = sanitize_lean_ident(&m.source);
@@ -3557,7 +3581,14 @@ fn build_measured_to_lean_summary(
35573581
} else {
35583582
"measured_cclk_from_raw_ns_satisfies_flash_spec"
35593583
};
3560-
(m.source, theorem_base, predicate.to_string())
3584+
(
3585+
m.source,
3586+
theorem_base,
3587+
predicate.to_string(),
3588+
m.period_ns,
3589+
m.sck_low_ns,
3590+
m.sck_high_ns,
3591+
)
35613592
} else {
35623593
let m: MeasuredCclk =
35633594
serde_json::from_str(text).context("parse MeasuredCclk JSON for summary")?;
@@ -3575,7 +3606,32 @@ fn build_measured_to_lean_summary(
35753606
} else {
35763607
"measured_cclk_satisfies_flash_spec"
35773608
};
3578-
(m.source, theorem_base, predicate.to_string())
3609+
(
3610+
m.source,
3611+
theorem_base,
3612+
predicate.to_string(),
3613+
m.period_ns,
3614+
m.sck_low_ns,
3615+
m.sck_high_ns,
3616+
)
3617+
};
3618+
3619+
// Compute the flash-spec minimum half-period and the measured margin.
3620+
let nominal_min_half_ns = (N25Q128_MIN_SCK_LOW_S * 1.0e9).round() as i64;
3621+
let flash_min_half_period_ns = pvt_ctx
3622+
.as_ref()
3623+
.map(|ctx| n25q128_min_sck_half_ns_pvt(ctx) as i64)
3624+
.unwrap_or(nominal_min_half_ns);
3625+
let measured_min_half_ns = std::cmp::min(low_ns, high_ns) as i64;
3626+
let margin_ns = measured_min_half_ns - flash_min_half_period_ns;
3627+
let recommendation = if pvt_ctx.is_none() {
3628+
"needs_pvt_context"
3629+
} else if raw_ns && low_ns + high_ns != period_ns {
3630+
"out_of_spec"
3631+
} else if margin_ns >= 0 {
3632+
"in_spec"
3633+
} else {
3634+
"out_of_spec"
35793635
};
35803636

35813637
Ok(serde_json::json!({
@@ -3585,6 +3641,9 @@ fn build_measured_to_lean_summary(
35853641
"pvt_context": pvt_ctx.as_ref().map(|ctx| serde_json::to_value(ctx).unwrap_or(serde_json::Value::Null)),
35863642
"raw_ns": raw_ns,
35873643
"margin": margin,
3644+
"flash_min_half_period_ns": flash_min_half_period_ns,
3645+
"margin_ns": margin_ns,
3646+
"recommendation": recommendation,
35883647
}))
35893648
}
35903649

@@ -6452,6 +6511,12 @@ mod tests {
64526511
assert_eq!(summary["raw_ns"].as_bool().unwrap(), false);
64536512
assert_eq!(summary["margin"].as_bool().unwrap(), false);
64546513
assert!(summary["pvt_context"].is_null());
6514+
assert_eq!(summary["flash_min_half_period_ns"].as_i64().unwrap(), 6);
6515+
assert_eq!(summary["margin_ns"].as_i64().unwrap(), 194);
6516+
assert_eq!(
6517+
summary["recommendation"].as_str().unwrap(),
6518+
"needs_pvt_context"
6519+
);
64556520
}
64566521

64576522
#[test]
@@ -6476,6 +6541,12 @@ mod tests {
64766541
);
64776542
assert_eq!(summary["raw_ns"].as_bool().unwrap(), true);
64786543
assert!(summary["pvt_context"].is_null());
6544+
assert_eq!(summary["flash_min_half_period_ns"].as_i64().unwrap(), 6);
6545+
assert_eq!(summary["margin_ns"].as_i64().unwrap(), 14);
6546+
assert_eq!(
6547+
summary["recommendation"].as_str().unwrap(),
6548+
"needs_pvt_context"
6549+
);
64796550
}
64806551

64816552
#[test]
@@ -6499,6 +6570,9 @@ mod tests {
64996570
let ctx_out = summary["pvt_context"].as_object().unwrap();
65006571
assert_eq!(ctx_out["temp_c"].as_i64().unwrap(), 85);
65016572
assert_eq!(ctx_out["process_corner"].as_str().unwrap(), "ss");
6573+
assert_eq!(summary["flash_min_half_period_ns"].as_i64().unwrap(), 13);
6574+
assert_eq!(summary["margin_ns"].as_i64().unwrap(), 7);
6575+
assert_eq!(summary["recommendation"].as_str().unwrap(), "in_spec");
65026576
}
65036577

65046578
#[test]
@@ -8329,4 +8403,45 @@ mod tests {
83298403
assert_eq!(json["temp_c"], 35);
83308404
assert_eq!(json["vccint_mv"], 1000);
83318405
}
8406+
8407+
#[test]
8408+
fn test_xadc_context_to_pvt_context_rounds_and_converts_units() {
8409+
let ctx = XadcContext {
8410+
temp_c: 42.7,
8411+
max_temp_c: 85.0,
8412+
min_temp_c: -40.0,
8413+
vccint_v: 1.00049,
8414+
max_vccint_v: 1.050,
8415+
min_vccint_v: 0.950,
8416+
vccaux_v: 1.80615,
8417+
max_vccaux_v: 1.890,
8418+
min_vccaux_v: 1.710,
8419+
raw: None,
8420+
};
8421+
let pvt = ctx.to_pvt_context(ProcessCorner::Ss).unwrap();
8422+
assert_eq!(pvt.temp_c, 43);
8423+
assert_eq!(pvt.vccint_mv, 1000);
8424+
assert_eq!(pvt.vccaux_mv, 1806);
8425+
assert_eq!(pvt.process_corner, ProcessCorner::Ss);
8426+
}
8427+
8428+
#[test]
8429+
fn test_xadc_context_to_pvt_context_negative_temp_rounds() {
8430+
let ctx = XadcContext {
8431+
temp_c: -12.4,
8432+
max_temp_c: 85.0,
8433+
min_temp_c: -40.0,
8434+
vccint_v: 0.950,
8435+
max_vccint_v: 1.050,
8436+
min_vccint_v: 0.950,
8437+
vccaux_v: 1.800,
8438+
max_vccaux_v: 1.890,
8439+
min_vccaux_v: 1.710,
8440+
raw: None,
8441+
};
8442+
let pvt = ctx.to_pvt_context(ProcessCorner::Tt).unwrap();
8443+
assert_eq!(pvt.temp_c, -12);
8444+
assert_eq!(pvt.vccint_mv, 950);
8445+
assert_eq!(pvt.process_corner, ProcessCorner::Tt);
8446+
}
83328447
}

0 commit comments

Comments
 (0)