doppler: gate S_IDLE→S_ACCUMULATE on frame_start_pulse (AUDIT-S3)

Pre-fix S_IDLE had two independent if-branches: one for frame_start_pulse
(resets pointers) and one for data_valid (transitions to S_ACCUMULATE).
A data_valid arriving before frame_start_pulse would advance the FSM with
whatever pointers happened to be live, and the BRAM write block would write
the sample into mem_write_addr = (write_chirp_index*RANGE_BINS) + 0.

In current operation the race is benign — end-of-S_ACCUMULATE always zeros
write_chirp_index/write_range_bin (line 287-288) and the MF pipeline latency
(~165 µs) is millions of cycles longer than the frame_start CDC latency
(~50 ns), so frame_start always arrives first. But the FSM relies on an
undocumented system-level invariant; a future code path that leaves
pointers stale on entry to S_IDLE would silently corrupt the first sample.

Fix: add a `frame_armed` register set when frame_start_pulse arrives in
S_IDLE, cleared on transition to S_ACCUMULATE. Both the FSM transition and
the BRAM write block gate on `(frame_start_pulse || frame_armed)`. The OR
admits the same-cycle case where both arrive together (write to addr 0
still resolves correctly because both blocks use the same gate).

Verification: tb_doppler_frame_start_gate 21/21 PASS, quick regression
32/32 PASS (was 31/31; +1 new test, 0 regressions). tb_doppler_realdata
(full FFT pipeline) still passes — gate transparent to normal operation.
This commit is contained in:
Jason
2026-04-29 18:36:31 +05:45
parent 53c7f416a7
commit ea2615ef84
3 changed files with 311 additions and 5 deletions
+4
View File
@@ -554,6 +554,10 @@ run_test "FT2232H Frame Drop Counter (AUDIT-C12)" \
tb/tb_ft2232h_frame_drop.vvp \
tb/tb_ft2232h_frame_drop.v usb_data_interface_ft2232h.v
run_test "Doppler Frame-Start Gate (AUDIT-S3)" \
tb/tb_doppler_frame_start_gate.vvp \
tb/tb_doppler_frame_start_gate.v doppler_processor.v xfft_16.v fft_engine.v
echo ""
# ===========================================================================