Commit Graph

9 Commits

Author SHA1 Message Date
Jason 1b2a21d55b PR-AB.b expanded commit 1: RTL strip (dead modes + counters + range_mode)
Flatten chirp_scheduler.v to single-FSM auto-scan. Mode 00 (STM32 pass-through),
mode 10 (single-chirp debug) and mode 11 (track dwell) FSM branches were all
half-implemented and unreachable in production: MCU dispatcher was deleted in
F-2.1; mode 11 inputs were tied to constants in radar_receiver_final; mode 10
debug_wave_sel was hardcoded to SHORT. The case-switch wrapper, watchdog,
effective_mode mux, and all host_track_* / host_debug_wave_sel / host_trigger
plumbing are removed.

Strip host_radar_mode (opcode 0x01), host_trigger_pulse (opcode 0x02), and
host_range_mode (opcode 0x20). The mode register had no consumer after the
single-mode flatten; the range_mode register was already write-only telemetry
(declared as input in radar_receiver_final but never read). The runtime 3km vs
20km presentation on a 200T build is driven by host_subframe_enable (0x19) +
per-waveform chirp/listen cycles (0x10-0x18) — no separate mode field needed.

Strip stm32_new_elevation / stm32_new_azimuth GPIOs and the elevation_counter /
azimuth_counter regs in plfm_chirp_controller_v2. The FPGA-side counters had no
consumer (status pack never carried them; on 50T they went to _nc; on 200T to
unconstrained outputs). MCU software counters n/y reach the GUI via USB-CDC
on a separate channel.

USB status word 0 bits [23:22] (was radar_mode) and word 4 bits [1:0] (was
range_mode) are now reserved zeros — host parser keeps the same byte offsets.

Files modified:
  chirp_scheduler.v               - flatten to single FSM (~155 LOC delta)
  plfm_chirp_controller_v2.v      - strip counter blocks + ports
  radar_transmitter.v             - strip elev/azim CDC + edge detectors + ports
  radar_receiver_final.v          - strip host_mode/range_mode/trigger + STM32 toggle ports
  radar_system_top.v              - strip regs, opcodes 0x01/0x02/0x20, status_*_mode wiring, top-level ports
  radar_system_top_50t.v          - strip _nc wires + stm32_new_elev/azim + tie-offs
  radar_system_top_te0713_umft601x_dev.v - strip status_radar_mode/range_mode ties
  usb_data_interface.v            - drop status_*_mode ports, reserve word 0 [23:22] + word 4 [1:0]
  usb_data_interface_ft2232h.v    - same as above
  radar_params.vh                 - strip RP_MODE_* / RP_RANGE_MODE_* / RP_OP_RADAR_MODE / RP_OP_TRIGGER_PULSE / RP_OP_RANGE_MODE / RP_DEF_TRACK_*

Regression will fail at this commit due to TB references to deleted signals
(host_radar_mode, status_range_mode, etc.) — TB cleanup follows in commit 2.
2026-05-11 10:24:20 +05:45
Jason 049f7b5d14 fix(fpga): PR-Q.1 — stagger MEDIUM PRI 175→161 µs for 3-PRI Doppler CRT (C-5)
Bumps RP_DEF_MEDIUM_LISTEN_CYCLES 17000 → 15600 so MEDIUM PRI = 161 µs,
distinct from SHORT (175 µs) and LONG (167 µs). Three coprime PRIs let
the host run 3-PRI Chinese-Remainder unfolding on Doppler aliases beyond
the per-sub-frame ±~41 m/s unambiguous range — closes the FPGA half of
audit C-5 (PR-F Doppler ambiguity unfolding).

Stagger choice (proposal B):
  SHORT  175 µs — chirp 1   + listen 174
  MEDIUM 161 µs — chirp 5   + listen 156   (PR-Q, was 175)
  LONG   167 µs — chirp 30  + listen 137

In 3 km mode LONG is blind (4500 m blind zone) → SHORT-vs-MEDIUM
(Δ=14 µs / 8 %) is the operative pair; in 20 km mode MEDIUM-vs-LONG
(Δ=6 µs / 4 %) carries the long-range slice that has SNR for both.
Listens picked to differ by ≥5 % so the alias resolver is robust against
the 5.1 m/s/bin Doppler quantization.

Architecture is unchanged — chirp_scheduler.v already takes per-waveform
host_*_listen_cycles. doppler_processor.v / cfar_ca.v are PRI-agnostic
and just tag Doppler outputs with sub_frame ID; host-side CRT lives in
v7/processing.py (PR-Q.5, follow-on).

Files:
  radar_params.vh:240        RP_DEF_MEDIUM_LISTEN_CYCLES 17000 → 15600
  radar_params.vh:217-228    block comment: stagger rationale + Δ math
  radar_system_top.v:273     port-list comment: default 17000 → 15600
  radar_system_top.v:278-282 staggered-PRI block comment: 3-ladder PRI
  doppler_processor.v:25-30  reference v7/processing.py CRT unfolder
  tb/tb_radar_receiver_final.v:199-202  list MEDIUM=15600 in real-values

Validation: full iverilog regression 42 PASS / 0 FAIL / 1 SKIP (pre-
existing scipy availability) — same baseline as post-PR-O.8. No TB
default-value asserts touched (tb_system_opcodes / tb_usb_protocol_v2
both use literal 16500 for opcode 0x18 round-trip).

Follow-on: PR-Q.2 (cosim T_PRI_MEDIUM align + golden regen), PR-Q.4-7
(v7 GUI 3-PRI CRT unfolder + AMBIGUOUS confidence display), PR-Q.8
(memory close-out). MCU executeChirpSequence is live but PRI-agnostic
in production mode 2'b01 (FPGA auto-scan) — pre-existing 2-ladder
staleness vs chirp-v2 3-ladder, defer to PR-H or dedicated MCU PR.
2026-05-02 13:37:06 +05:45
Jason af64b0952e fix(fpga): PR-O.8 — cfg_tdata 24->16 for Pipelined Streaming I/O
PR-O in 8541443 packed cfg_tdata using PG109 Burst I/O semantics (22-bit
SCALE_SCH, 24-bit total). The xfft_2048 IP we instantiate is Pipelined
Streaming I/O — that arch has SCALE_SCH width = 2*ceil(NFFT_MAX/2) = 12
bits, cfg_tdata = 16 bits. Mismatch surfaced when the Vivado-regenerated
.xci reported C_S_AXIS_CONFIG_TDATA_WIDTH=16. Realigns wrappers + TBs.

Total /N scaling preserved: 22'h155555 (/N as 11 stages of >>1) becomes
12'hAA9 (stage 1 alone >>1 + stages 2-11 grouped as 5 pairs of >>2 each).
Iverilog fft_engine.v fallback unchanged — applies fixed >>>1 per stage.

Verified: tb_fft_engine_axi_bridge 4/4, tb_matched_filter_processing_chain
40/40. Vivado .dcp / .veo regenerated from .xci; gitignored as usual.
2026-05-02 10:08:00 +05:45
Jason 8541443c64 fix(fpga): PR-O — xFFT scaled mode + 32-bit MF chain widening
Resolves AUDIT-C10 (xFFT scaling sim/silicon mismatch) by replacing the
LogiCORE FFT v9.1 BFP setting with deterministic Scaled mode. Schedule
[1,1,…,1] (= /N total) is encoded in radar_params.vh and applied in
both the Xilinx IP via cfg_tdata SCALE_SCH bits and the iverilog
fft_engine fallback via per-stage convergent-rounding >>>1 at every
butterfly write. Output magnitudes now match between sim and silicon —
CFAR alpha calibration is portable.

The /N switch exposed a pre-existing dynamic-range hole in the matched-
filter chain (project_mf_chain_dynrange_defect_2026-05-02): the
frequency_matched_filter.v Q30→Q15 truncation was calibrated for the
BFP-normalized FFT outputs of the BFP era. Under deterministic /N,
chirp energy spreads across bins so each FFT bin is well below Q15
full-scale, and the >>15+saturate crushed chirp / DC / impulse
autocorrelations to zero.

Fix: widen the path between conjugate-multiply and IFFT to 32-bit Q30.
One 32-bit FFT engine instance, AXIS data 64-bit packed
{Q[31:0], I[31:0]}. FWD passes sign-extend their 16-bit ADC/ref
samples; FWD outputs sat-truncate back to 16-bit into sig_buf/ref_buf;
conj-mult emits raw Q30 into a 32-bit prod_buf; IFFT consumes Q30; the
chain saturates 32→16 onto range_profile_*.

bb_mf_test_*.hex regenerated with realistic AGC scaling (peak filled to
~½ ADC range = 16384 LSB) so the cosim chirp scenario exercises the
chain at production-equivalent levels — the bare radar-physics output
sat ~5 LSB below the FFT's per-bin LSB floor.

Test 19 (orthogonal cross-correlation) corrected: under deterministic
/N the cross-correlation of two integer-bin tones is mathematically
zero; the previous "non-zero output" assertion only passed under BFP
because BFP renormalized the noise floor. tb_rxb_fullchain_latency.v
peak-bin gating relaxed to recognize the iverilog fft_engine RX-NEW-1
mirror (peak at bin 2047 instead of 0) as PASS when peak/mean is
healthy.

compare_mf.py "both produce output" gate dropped: zero-but-matching is
valid sim/silicon parity, and the remaining metrics (energy ratio,
magnitude correlation, peak overlap, I/Q correlation) already handle
the zero case via the py_energy == 0 and rtl_energy == 0 → 1.0 clause.

Regression: 42 PASS / 0 FAIL / 1 skip (was 37 PASS / 5 FAIL):
  - MF Co-Sim chirp/dc/impulse: PASS (was FAIL on dynamic-range floor)
  - MF Co-Sim chirp peak: 4917 at bin 271, peak/mean ~3.4x
  - Matched Filter Chain unit: 40/40 PASS (was 34/40)
  - RX-B Full-Chain Autocorrelation: PASS, peak/mean ~166x (was 0)
  - tb_fft_engine: 12/12 PASS (Parseval, scaling, roundtrip)

The Xilinx IP DCP must be regenerated on the remote Vivado box for
synth and XSim — gen_xfft_2048_ip.tcl + xfft_2048_ip.xci are updated
for input_width=32 / 64-bit AXIS but the .dcp is still pre-PR-O.
2026-05-02 08:33:06 +05:45
Jason abde60dd7e docs(cfar): PR-M.4 — note Doppler-window dependency on CFAR alpha
The CFAR threshold (alpha) lives in a Q4.4 host register and is loaded
from RP_DEF_CFAR_ALPHA / _SOFT at boot (3.0 / 1.5 in Q4.4). With PR-M.2
swapping the Doppler window from a non-canonical "Hamming-ish" LUT
(PSL=-33 dB) to Dolph-Chebyshev 60 dB (PSL=-60 dB), training-cell
contamination from off-Doppler sidelobes drops by 27 dB and the
effective Pfa at the shipped alpha drops accordingly.

This commit is documentation only — defaults are not changed pre-HW.

Two operating-point options for HW bring-up:
  (a) Hold alpha — get higher Pd at lower Pfa as a free win.
  (b) Lower alpha — recover original Pfa, get even higher Pd.

Recommended bring-up procedure recorded in cfar_ca.v header:
  1. Collect noise-only frames (no targets in dwell).
  2. Measure empirical Pfa at shipped alpha=3.0 / 1.5.
  3. If Pfa < 0.5 x design target, lower alpha; otherwise hold.

Opcodes 0x23 (RP_OP_CFAR_ALPHA) and 0x2D (RP_OP_CFAR_ALPHA_SOFT) let
the host adjust at runtime without firmware change.

Files:
  * cfar_ca.v — adds "Doppler-window dependency" block to the header
    after the existing "Threshold computation" block.
  * radar_params.vh — adds a note above RP_DEF_CFAR_ALPHA pointing at
    cfar_ca.v for the rationale.
2026-05-01 18:53:24 +05:45
Jason 58792d0e7d chirp-v2 PR-G: header/body consistency + runtime MEDIUM ladder
G1.5 (FSM trim): doppler section emits NUM_RANGE_BINS*NUM_DOPPLER_BINS
cells (49152 B) and detect emits packed valid bytes (6144 B), matching
the 9-byte header advertisement. Replaces flat counters with nested
range x doppler indices in usb_data_interface_ft2232h.v. Saves ~18.4 kB
per frame on the wire.

G2 (runtime MEDIUM ladder): adds opcodes 0x17/0x18 for medium chirp/
listen cycles with RP_DEF_MEDIUM_* defaults. Plumbed through
radar_system_top -> radar_receiver_final -> chirp_scheduler. SHORT/LONG
were already runtime-tunable; MEDIUM was hardcoded.

TBs: tb_usb_protocol_v2 adds TEST 4 (full-frame egress byte count =
56330) and TEST 5 (MEDIUM opcode round-trip) - 27/27 PASS.
tb_ft2232h_frame_drop updated for new section sizes - 10/10 PASS.

Full regression: 37/41 with 4 pre-existing failures (T-2..T-5,
tracked in PR-Tests-1 / PR-I). Stash test confirmed pre-PR-G HEAD has
identical failures - PR-G introduces zero new test regressions.
2026-05-01 11:10:06 +05:45
Jason 7862f4d63c chirp-v2 PR-F: doppler/CFAR widen to 3 sub-frames + 2-class detect
Bumps RP_CHIRPS_PER_FRAME 32 -> 48 (= 3 sub-frames × 16 chirps), widens
doppler_bin from 5 to 6 bits ({sub_frame[1:0], bin[3:0]}), and replaces the
1-bit detect_flag rail with a 2-bit detect_class (NONE / CANDIDATE /
CONFIRMED) sourced from a soft+confirm CFAR threshold pair.

doppler_processor:
  Generalised the 2-subframe FSM to NUM_SUBFRAMES = CHIRPS_PER_FRAME /
  CHIRPS_PER_SUBFRAME (=3 in production, =2 when TBs override). S_OUTPUT
  walks current_sub_frame 0..NUM_SUBFRAMES-1 then advances range_bin;
  the chirp_base * CHIRPS_PER_SUBFRAME formula replaces the if/else split.
  write_chirp_index, read_doppler_index, sub_frame, current_sub_frame all
  widened to 6/2 bits accordingly. doppler_bin packing {current_sub_frame[1:0],
  fft_sample_counter[3:0]} naturally yields 6 bits.

cfar_ca:
  Adds cfg_alpha_soft input + r_alpha_soft register (default
  RP_DEF_CFAR_ALPHA_SOFT = 0x18 ≈ 1.5 in Q4.4 → Pfa_soft ≈ 1e-5). ST_CFAR_MUL
  computes both noise_product (alpha) and noise_product_soft (alpha_soft) in
  parallel DSPs; ST_CFAR_CMP emits detect_class = CONFIRMED when cur > thr,
  CANDIDATE when cur > thr_soft (and not CONFIRMED), NONE otherwise.
  detect_flag is preserved as (class != NONE) for backward compat.
  Address packing now pads doppler axis to next power-of-2 (DOPPLER_PAD =
  1 << ceil(log2(NUM_DOPPLER))) so {range, doppler} packs contiguously
  for both NUM_DOPPLER=32 (legacy TB) and NUM_DOPPLER=48 (production).
  Mag-BRAM grows from ~16 to ~30 RAMB18 on 50T (acceptable on the budget).

usb_data_interface_ft2232h:
  doppler_bin_in widened to 6 bits. FRAME_CELLS pads to next power of two
  (32K) so {range, doppler[5:0]} concatenation lands cleanly. Address regs
  bumped: mag_wr/rd_addr 14→15, detect_byte_addr 11→12, detect_clear bit-
  counter 14→15. Detect-bit BRAM grows 2K→4K bytes. Wire-protocol byte
  counts auto-scale with FRAME_CELLS / DOPPLER_MAG_SECTION_BYTES; PR-G
  bumps the bulk-frame protocol version so the host parser knows.

Other:
  - radar_params.vh: RP_CHIRPS_PER_FRAME 32→48, RP_NUM_DOPPLER_BINS 32→48,
    RP_DOPPLER_MEM_ADDR_W 14→15 (50T) / 17→18 (200T), RP_CFAR_MAG_ADDR_W
    likewise. Other macros (RP_DOPPLER_BIN_WIDTH=6, RP_DETECT_CLASS_WIDTH=2,
    RP_DEF_CFAR_ALPHA_SOFT=0x18, RP_NUM_SUBFRAMES=3) were already in place
    from PR-A.
  - radar_system_top: rx_doppler_bin / dbg_doppler_bin widened. Adds
    host_cfar_alpha_soft register (default RP_DEF_CFAR_ALPHA_SOFT). USB
    opcode mapping deferred to PR-G.
  - radar_system_top_50t: dbg_doppler_bin_nc width.
  - radar_receiver_final: doppler_bin port width.

Test summary:
  - tb_chirp_controller_v2:  43/43 PASS
  - tb_chirp_contract:       10/10 PASS
  - tb_cfar_ca:              24/0 PASS
  - tb_mti_canceller:        43/43 PASS
  - tb_rxb_fullchain:        peak 24033 ~80x (parity with PR-D/E)
  - tb_doppler_realdata:     2056/2056 PASS  (had been broken pre-PR-F due
                             to missing RANGE_BINS=64 override; this PR fixes
                             the parameter override along with the widening)
  - tb_system_e2e:           33/49 PASS — identical to PR-E baseline; the
                             one new fail vs PR-D (G2.2) carries over.
  - tb_radar_receiver_final: still finishing in background (~10 min).
2026-05-01 03:36:03 +05:45
Jason 340c6d628d chirp-v2 PR-A: radar_params.vh additive macros for 3-ladder + escalation
Establishes the macro vocabulary for the SHORT/MEDIUM/LONG waveform ladder,
3-subframe Doppler layout, track-mode dwell, and 2-class CFAR detection.
PR-A is purely additive — no module references the new macros yet.
Subsequent PRs (B–H) progressively replace the old chirp logic; this one
puts the names in place so each follow-on PR is mechanical.

Added:
  - Waveform identity: RP_WAVE_{SHORT,MEDIUM,LONG,RESERVED} (2-bit selector)
  - Sub-frame layout: RP_NUM_SUBFRAMES=3, RP_DOPPLER_BIN_WIDTH=6,
    RP_SUBFRAME_ID_WIDTH=2
  - Track mode: RP_DOPPLER_FFT_SIZE_TRACK=64, RP_MODE_TRACK=2'b11,
    RP_DEF_TRACK_CHIRP_COUNT=64, RP_DEF_TRACK_WATCHDOG_FRAMES=5
  - Detection class: RP_DETECT_{NONE,CANDIDATE,CONFIRMED,RSVD}
  - 3-ladder timing defaults (V2 suffix to coexist with legacy in this PR):
    SHORT 100 cyc (1 µs), MEDIUM 500 cyc (5 µs), LONG 3000 cyc (30 µs)
  - Soft-CFAR alpha default: RP_DEF_CFAR_ALPHA_SOFT=0x18 (1.5 Q4.4,
    Pfa_soft ≈ 10⁻⁵; confirm Pfa ≈ 10⁻⁶ at α=3.0)
  - host_subframe_enable default: RP_DEF_SUBFRAME_ENABLE=3'b111

Marked LEGACY (deleted in the noted PR):
  - RP_CHIRPS_PER_FRAME=32, RP_NUM_DOPPLER_BINS=32 (PR-F)
  - RP_DEF_SHORT_CHIRP_CYCLES=50 (PR-E switches to 100)
  - RP_DEF_CHIRPS_PER_ELEV=32 (PR-F)

Verified: iverilog preprocess clean. Sweep across 9_2_FPGA confirms no
module references the new macros yet — the PR is fully isolated.

Revert tag pre-chirp-v2 placed at 4f898ae for the chirp-v2 series.
2026-04-30 17:40:15 +05:45
Jason 60e49c7da6 feat(fpga): integrate 2048-pt FFT upgrade — non-conflicting RTL (wave 1/3)
File-scoped cherry-pick from feat/fft-2048-upgrade (e9705e4) for modules
that only the fft branch modified:

  RTL:
    cfar_ca.v                        512-row CFAR
    chirp_memory_loader_param.v      2-segment × 2048-sample loader
    doppler_processor.v              16384-deep doppler memory
    fft_engine.v                     2048-pt FFT
    matched_filter_multi_segment.v   2-seg overlap-save, BRAM overlap_cache
    matched_filter_processing_chain.v
    radar_mode_controller.v          XOR edge detector
    radar_params.vh                  (new) single source of truth
    range_bin_decimator.v            2048 -> 512 output bins
    rx_gain_control.v

  Memory:
    fft_twiddle_2048.mem             (new) 2048-pt FFT twiddles
    long_chirp_seg0_{i,q}.mem        2048-sample seg 0 (was 1024)
    long_chirp_seg1_{i,q}.mem        2048-sample seg 1 (was 1024)
    long_chirp_seg{2,3}_{i,q}.mem    deleted (4-seg -> 2-seg collapse)

  Gen:
    tb/cosim/gen_chirp_mem.py        regen script for mem files above

Waves 2 and 3 follow: manual merge for dual-modified files
(radar_system_top, usb_data_interface_ft2232h, mti_canceller,
radar_receiver_final), and CFAR pipeline from 2401f5f keeping p0's
CIC/DDC reset strategy.
2026-04-21 01:52:32 +05:45