5 Commits

Author SHA1 Message Date
Jason
d9e7a5becf fix(fpga): PR-Y.1 + PR-X.3 — DC notch boundary, audit cleanups, formal retarget
Bundles audit items unblocked by the AERIS-10 end-to-end audit:

S-1 (radar_system_top.v) — DC notch off-by-one at width=7

  Audit S-1: ±W around DC in a 16-bin FFT covers bins {0..W, 16-W..15}
  (2W+1 total, bin 8 the only one excluded at W=7). The previous form
  `< W || > 15-W+1` missed both boundaries: at W=1 it notched only {0}
  (skipping 1 and 15); at W=7 it missed 7 and 9. Replaced with inclusive
  comparators against 5-bit limits (`<= notch_lo || >= notch_hi`) which
  hit the intended set for all W ∈ {1..7}. Cosim DC golden
  (tb/cosim/rtl_bb_dc.csv) regenerated against the corrected behaviour.

S-7 (rx_gain_control.v) — reg→wire for combinational helpers

  `wire_frame_sat_incr` / `wire_frame_peak_update` were declared `reg`
  and blocking-assigned inside the clocked always block. They are pure
  combinational functions of the registered inputs — promoted to
  module-scope continuous assigns. Behaviour is bit-identical (the read
  inside the always still reflects the prior-cycle latched values) but
  the iverilog warnings disappear and the sim/synth correspondence is
  unambiguous.

M-9 (formal/fv_radar_mode_controller.sby) — delete orphan

  radar_mode_controller.v was retired in PR-D in favour of
  chirp_scheduler.v; the .sby was never updated and pointed at a
  non-existent module. Deleted.

M-10 (radar_receiver_final.v) — document `data_sync_error` unconnected

  In production AD9484 produces a single 8-bit stream that the DDC mixes
  into matched I/Q paths with symmetric pipelines, so `ddc_valid_i` and
  `ddc_valid_q` rise on the same cycle and `data_sync_error` cannot
  fire by construction. The check is retained inside
  ddc_input_interface for the standalone tb_ddc_input_interface
  unit-test (which intentionally drives valid_i ≠ valid_q). Adds
  comments explaining the unconnected port at both call sites; no
  functional change.

M-11 (radar_receiver_final.v) — `force_saturation_pulse` symmetric hook

  The DDC has a `force_saturation` debug input that previously was tied
  1'b0 directly. Routed through a new `force_saturation_pulse` wire
  alongside the existing `clear_monitors_pulse` so a future host opcode
  surface for "diagnostic force/clear" lands both at the same dispatch
  point. Still tied 1'b0 today — RTL change is a placeholder for the
  opcode plumbing.

PR-X.3 F-7.5 (formal/fv_cdc_adc.{v,sby}) — retarget to cdc_async_fifo

  Prior wrapper instantiated `cdc_adc_to_processing`, retired by
  AUDIT-C11 in favour of `cdc_async_fifo` (the production CIC→FIR
  boundary CDC, see ddc_400m.v line 646). Wrapper rewritten with
  FIFO-shaped equivalents of the original Gray-CDC properties:

    P1 reset behaviour, P2 no spurious dst_valid, P3 overrun semantics,
    P4 data integrity (cooldown-spaced, FIFO-equivalent of the
       original single-element latch property),
    P5 bounded liveness (depth 100 gclk),
    P6 cover sequences for the basic write→read pipeline.

  P4's true multi-in-flight FIFO order proof is left as Option B work;
  for the AERIS-10 use case the upstream ddc_400m CIC→FIR consumer
  operates below FIFO-fill rate by design, so the cooldown-spacing
  assumption is a tight model.

Verification: full FPGA regression 41 / 0 / 0.
2026-05-06 01:23:21 +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
Jason
b4d1869582 fix: 9 bugs from code review — RTL sign-ext & snapshot, thread safety, protocol fixes
- rx_gain_control.v: sign-extension fix ({agc_gain[3],agc_gain} not {1'b0,agc_gain})
  + inclusive frame_boundary snapshot via combinational helpers (Bug #7)
- v7/dashboard.py: Qt thread-safe logging via pyqtSignal bridge (Bug #1)
  + table headers corrected to 'Range (m)' / 'Velocity (m/s)' (Bug #2)
- main.cpp: guard outerAgc.applyGain() with if(outerAgc.enabled) (Bug #3)
- radar_protocol.py: replay L1 threshold detection when CFAR disabled (Bug #4)
  + IndexError guard in replay open (Bug #5) + AGC opcodes in _HARDWARE_ONLY_OPCODES
- radar_dashboard.py: AGC monitor attribute name fixes (3 labels)
- tb_rx_gain_control.v: Tests 17-19 (sign-ext, simultaneous valid+boundary, enable toggle)
- tb_cross_layer_ft2232h.v: AGC opcode vectors 0x28-0x2C in Exercise A (Bug #6)

Vivado 50T build verified: WNS=+0.002ns, WHS=+0.028ns — all timing constraints met.
All tests pass: MCU 21/21, GUI 120/120, cross-layer 29/29, FPGA 25/25 (68 checks).
2026-04-13 23:35:10 +05:45
Jason
ffba27a10a feat: hybrid AGC (FPGA phases 1-3 + GUI phase 6) with timing fix
FPGA:
- rx_gain_control.v rewritten: per-frame peak/saturation tracking,
  auto-shift AGC with attack/decay/holdoff, signed gain -7 to +7
- New registers 0x28-0x2C (agc_enable/target/attack/decay/holdoff)
- status_words[4] carries AGC metrics (gain, peak, sat_count, enable)
- DIG_5 GPIO outputs saturation flag for STM32 outer loop
- Both USB interfaces (FT601 + FT2232H) updated with AGC status ports

Timing fix (WNS +0.001ns -> +0.045ns, 45x improvement):
- CIC max_fanout 4->16 on valid pipeline registers
- +200ps setup uncertainty on 400MHz domain
- ExtraNetDelay_high placement + AggressiveExplore routing

GUI:
- AGC opcodes + status parsing in radar_protocol.py
- AGC control groups in both tkinter and V7 PyQt dashboards
- 11 new AGC tests (103/103 GUI tests pass)

Cross-layer:
- AGC opcodes/defaults/status assertions added (29/29 pass)
- contract_parser.py: fixed comment stripping in concat parser

All tests green: 25 FPGA + 103 GUI + 29 cross-layer = 157 pass
2026-04-13 19:24:11 +05:45
Jason
e93bc33c6c Production fixes 1-7: detection bugs, cfar→threshold rename, digital gain control, Doppler mismatch protection, decimator watchdog, bypass_mode dead code removal, range-mode register (21/21 regression PASS)
Fix 1: Combinational magnitude + non-sticky detection flag (tb: 23/23)
Fix 2: Rename all cfar_* signals to detect_*/threshold_* (honest naming)
Fix 3: New rx_gain_control.v between DDC and FFT, opcode 0x16 (tb: 33/33)
Fix 4: Clamp host_chirps_per_elev to DOPPLER_FFT_SIZE, error flag (E2E: 54/54)
Fix 5: Decimator watchdog timeout, 256-cycle limit (tb: 63/63)
Fix 6: Remove bypass_mode dead code from ddc_400m.v (DDC tb: 21/21)
Fix 7: Range-mode register 0x20 with status readback (USB tb: 77/77)
2026-03-20 04:38:35 +02:00