Replaces plfm_chirp_controller_enhanced (5-state FSM with hardcoded
LONG/SHORT timings + 60-entry inline short LUT) with plfm_chirp_controller_v2,
a pure DAC playback driver: IDLE -> CHIRP -> IDLE keyed off a 1-cycle
dst_chirp_valid pulse, with sample count selected by dst_wave_sel
(SHORT=120 / MEDIUM=600 / LONG=3600). Inter-chirp timing (LISTEN, GUARD,
frame boundaries) is now owned exclusively by chirp_scheduler.
Scheduler -> TX bridge: cdc_async_fifo (Cummings style #2, WIDTH=2 DEPTH=4)
crosses {wave_sel} from clk_100m to clk_120m_dac, with chirp_pulse as
src_valid. frame_pulse rides a separate toggle CDC for chirp_counter
clear and the new_chirp_frame status output. mixers_enable now also gates
the scheduler so it stays in S_IDLE while the radar is "off" — without
this gate the first chirp_pulse fires at reset and gets dropped before
mixers come up.
Files:
- NEW plfm_chirp_controller_v2.v DAC playback driver (3 LUTs, FSM)
- DEL plfm_chirp_controller.v legacy controller (382 lines)
- DEL long_chirp_lut.mem legacy LUT (3600 lines), replaced
by tx_long_lut.mem from PR-B
- chirp_scheduler.v + mixers_enable input (master quiesce)
- radar_receiver_final.v + sched_*_out output ports + mixers_enable_100m
- radar_system_top.v wire sched_*_out -> tx_inst.sched_*; pass
stm32_mixers_enable_100m to rx_inst
- radar_transmitter.v full rewrite: drop new_chirp edge detector +
toggle CDC, instantiate cdc_async_fifo for
{wave_sel}, toggle CDC for frame_pulse,
plfm_chirp_controller_v2 in place of _enhanced
- tb/tb_chirp_controller.v + tb/tb_chirp_contract.v rewritten for v2
contract (43/43 unit + 10/10 contract green)
- tb/tb_radar_receiver_final.v + .mixers_enable_100m(1'b1) pin
- run_regression.sh, scripts/200t/build_200t.tcl file-list bumped
Test summary:
- tb_chirp_controller_v2: 43/43 PASS
- tb_chirp_contract: 10/10 contracts upheld
- tb_rxb_fullchain: peak 24033 ~80x (parity with PR-D)
- tb_mti_canceller: 43/43 PASS
- tb_system_e2e: 33/49 (1 new vs 34/49 PR-D baseline: G2.2
new_chirp_frame, intentional v2 frame-pulse
semantics — fires once per Doppler frame
instead of once per stm32 chirp toggle.
TB needs widening in PR-H to wait the full
frame.)
Single 100 MHz scheduler emits wave_sel[1:0] and chirp_pulse natively. Modes
00 (STM32 pass-through), 01 (auto-scan over SHORT/MEDIUM/LONG sub-frames),
10 (single-chirp debug), 11 (track dwell with watchdog scan-fallback after
RP_DEF_TRACK_WATCHDOG_FRAMES=5 idle frames). Sub-frame mask lets ops drop a
waveform without recompiling.
Drops the receiver_final wave_sel shim added in PR-C: wave_sel comes
straight from the scheduler; chirp_pulse replaces the old mc_new_chirp
toggle + XOR edge converter. matched_filter_multi_segment and mti_canceller
take wave_sel[1:0] and chirp_pulse directly — no parallel paths.
multi_segment also bumped: SHORT_CHIRP_SAMPLES 50 -> 100 (V2 1 us SHORT)
and MEDIUM_CHIRP_SAMPLES = 500 (5 us). LONG path unchanged. Dead
mc_new_elevation/azimuth XOR converters removed.
Deletes radar_mode_controller.v, formal/fv_radar_mode_controller.v, and
tb/tb_radar_mode_controller.v. Build manifests (run_regression.sh,
scripts/200t/build_200t.tcl) updated. Receiver_final pins medium/track/
subframe_enable inputs to RP_DEF_* defaults until PR-G plumbs USB opcodes.
Verification:
- tb_rxb_fullchain_latency: peak |I|+|Q|=24033 at bin 0, ~80x peak/mean
(up from PR-C's 15115 since matched filter now uses full 100 SHORT samples)
- tb_mti_canceller: 43/43 PASS with new wave_sel[1:0] input
- tb_radar_receiver_final: 8/8 PASS, ALL TESTS PASSED
- tb_system_e2e: 34/49 PASS - identical to pre-PR-D baseline (15 failures
are pre-existing matched-filter cycle-budget skips); G8.2/G8.3 chirp_scheduler
probes PASS
- tb_multiseg_cosim: 16/32 - same as pre-PR-D baseline
Drop the chirp-v1 1-bit use_long_chirp memory loader and its 6 .mem files;
introduce chirp_reference_rom — wave_sel-native, single 8192x16 BRAM array
per Q15 lane, 4-region init (SHORT, MEDIUM, LONG seg0/seg1) loaded from the
PR-B mem files. Same 1-clk read latency as the legacy module so the RX-B
autocorrelation alignment fix carries through unchanged.
Receiver-side wave_sel shim added in radar_receiver_final.v:
wire [1:0] wave_sel = use_long_chirp ? RP_WAVE_LONG : RP_WAVE_SHORT;
This is a 1-line transitional bridge while radar_mode_controller still
emits 1-bit use_long_chirp; PR-D deletes the shim and wires chirp_scheduler
straight through. MEDIUM is loaded into the ROM but unreachable through
the production path until PR-D.
BRAM cost: 8 RAMB18 (was 6 in chirp-v1). +2 BRAM is the cost of adding
MEDIUM to the waveform set; not avoidable.
Files added:
- chirp_reference_rom.v
Files removed:
- chirp_memory_loader_param.v
- long_chirp_seg{0,1}_{i,q}.mem (4 files)
- short_chirp_{i,q}.mem (2 files)
- tb/cosim/validate_mem_files.py (legacy file-set validator; replaced by
gen_chirp_mem.py's internal verify_phase_match)
- tb/cosim/analyze_short_chirp_mismatch.py (one-shot tool from the
chirp-v1 TX-I investigation; finding incorporated, references the
deleted short_chirp_*.mem files)
Files updated for module rename:
- radar_receiver_final.v — instance, comments, wave_sel shim
- radar_mode_controller.v — header comment
- matched_filter_processing_chain.v — header comment
- scripts/200t/build_200t.tcl — explicit RTL list
- run_regression.sh — 5 spots
- tb/tb_rxb_fullchain_latency.v — instance, wave_sel shim, mem filenames,
SHORT_LEN 50 → 100 (1 µs at 100 MHz)
- tb/tb_system_e2e.v — header comment
Verification:
- chirp_reference_rom standalone iverilog compile: clean
- Full receiver chain compile (21 RTL files): clean
- tb_rxb_fullchain_latency runs end-to-end with new ROM + new mem files
+ 100-sample SHORT chirp; autocorrelation peak at bin 0, peak |I|+|Q|
= 15115. Confirms 1-clk ROM read latency is preserved and the RX-B
direct-wire-with-1-FF alignment still holds.
- 50T build script (scripts/50t/build_50t.tcl) uses glob *.v — no edit
needed; it picks up the new file automatically.
latency_buffer.v has had zero non-tb instantiations since RX-B (2026-04-23)
replaced its hookup in radar_receiver_final with a 1-FF alignment register.
The module was being kept "for potential future use" — exactly the kind of
dead weight the codebase does not need. Deleted, along with all build /
test infrastructure that dragged it along:
- 9_Firmware/9_2_FPGA/latency_buffer.v
- 9_Firmware/9_2_FPGA/tb/tb_latency_buffer.v
- run_regression.sh: removed from RTL_FILES and RECEIVER_RTL
- scripts/200t/build_200t.tcl: removed from synthesis source list
- tb/tb_system_e2e.v: removed from header compile-string example
- tb/cosim/validate_mem_files.py: deleted test_latency_buffer() (~75 lines),
its call site, and the corresponding entry in the module docstring
Historical RX-B comments referencing latency_buffer in radar_receiver_final.v,
tb_rxb_fullchain_latency.v, and tb_rxb_latency_measure.v are kept — they
explain WHY the module was removed, which is still useful design archaeology.
Two doc-only housekeeping touches bundled in:
- plfm_chirp_controller.v: replaced two empty "CRITICAL FIX: Generate
valid signal" labels at LONG_CHIRP and SHORT_CHIRP with one shared
chirp_valid policy comment block above LONG_CHIRP that explains the
actual rationale (downstream FIFO underrun on trailing samples).
- v7/models.py: replaced the "range_resolution and velocity_resolution
should be calibrated" docstring (sounded like an open TODO but was a
documented placeholder) with a clear pointer to the GUI-C3 fix in
workers.py:RadarDataWorker so future readers know the live path
derives correct values from WaveformConfig.
FPGA quick regression unchanged: 28/29 (1 fail is the unrelated iverilog/
Xilinx-IP RX-NEW-3 gap). GUI suite 180/180. Ruff clean.
- Add set_false_path -hold for source-synchronous ADC IDDR paths in
adc_clk_mmcm.xdc (eliminates 8 hold violations from build 12)
- Add DDR falling-edge input delay constraints to xc7a50t_ftg256.xdc
(parity with 200T XDC)
- Reorganize scripts/ into target subdirectories: 50t/, 200t/, te0712/,
te0713/, utils/ so users can run the correct build for their hardware
- Delete obsolete build scripts (build17-20) superseded by build_50t/200t
- Update project_root paths in all moved scripts (.. -> ../..)