27 Commits

Author SHA1 Message Date
Jason
004c933ef0 feat(fpga): PR-AD AD.1 — usb_data_interface.v v2 bulk parity rewrite + top wiring
The FT601 USB 3.0 driver was 5 PRs behind the FT2232H driver: still
emitting pre-PR-G 12-byte per-cell streaming packets with 1-bit
cfar_detection, no subframe_enable snapshot (M-8), no MEDIUM PRI
status readback (M-5 / PR-AC.1), no CFAR telemetry (PR-G). Host
parser only speaks v2 bulk, so a 200T+FT601 build couldn't talk to
the GUI. This commit closes that gap by cloning the FT2232H module
body verbatim and swapping the output stage from 8b/cycle to
32b+4b-BE/cycle.

usb_data_interface.v — full rewrite (~1300 LOC):
  - 3 internal BRAMs (doppler_mag / range / detect), 3-cycle RMW
    pipeline, Manhattan magnitude, all CDC chains, 8-state WR FSM
    copied from FT2232H reference verbatim
  - status_words[0..7] 34-byte packet (M-5 parity with PR-AC.1
    21bf7a0): medium_chirp_cycles + medium_listen_cycles in word 7
  - subframe_enable_snapshot in frame byte 2 bits[5:3] (PR-U / M-8)
  - 2-bit cfar_detect_class (PR-F)
  - PR-G CFAR telemetry (detect_count_cand + detect_threshold_soft
    in status word 6)
  - AUDIT-C12 wr_done_toggle handshake + frame_drop_count
  - PR-Z A6 Bug C fix (detect_clearing on wr_done_pulse)
  - PR-AA fix (BRAM addr advance at phase 0 / MSB, not phase 1)
  - PR-Z A6 Bug B preload (detect_rd_addr=1 at WR_DETECT entry)
  - Output stage: byte_now combinational generator + 4-lane pack-emit
    accumulator. Full words BE=1111, partials BE=0001/0011/0111 at
    section ends. Byte-order convention: byte 0 -> ft601_data[7:0],
    byte N+1 -> next lane up (preserves MSB-first FT2232H wire order)
  - ODDR clock forwarding preserved (ft601_clk_out)
  - Vestigial ft601_txe_n / ft601_rxf_n outputs kept tied 1'b1
    (200T board has physical pins routed; removing breaks the build)

radar_system_top.v — 9 new connections to the FT601 instance
(no new top-level signals — all driven from already-existing internal
wires the FT2232H instance was already using):
  - cfar_detection (1-bit) -> cfar_detect_class (2-bit)
  - range_bin_in, doppler_bin_in (rx_detect_valid mux)
  - frame_complete, subframe_enable
  - status_medium_chirp, status_medium_listen, status_cfar_alpha_soft
  - status_detect_threshold_soft, status_detect_count_cand
2026-05-18 13:28:04 +05:45
Jason
4989c33aa6 PR-AB.b expanded commit 5: Beam-ready handshake (RTL + MCU + GUI)
Wire a per-frame MCU→FPGA "beam pattern ready" handshake so the chirp
scheduler can stall between 48-chirp frames until the MCU finishes
writing the next ADAR1000 pattern. The legacy unused stm32_new_chirp
input on PD8 is repurposed as stm32_beam_ready; chirp_scheduler.v gets
a new S_BEAM_WAIT state entered after each frame_pulse and an 80 ms
watchdog so a missed MCU toggle degrades to wall-clock cadence with a
sticky telemetry bit rather than stalling the radar.

Cold-reset defaults the handshake off (host_handshake_enable=0, new
opcode 0x1A); the GUI opts in once the MCU PD8 wiring is verified on
the bench. Both the FT601 and FT2232H status word 4 paths get the new
beam_handshake_watchdog_fired sticky at bit [1] (reclaimed from the
range_mode retirement in commit 1).

RTL:
- chirp_scheduler.v: 2-FF ASYNC_REG sync on beam_ready_async; 1-cycle
  edge detect (any transition, MCU side uses HAL_GPIO_TogglePin); new
  S_BEAM_WAIT state entered at frame_pulse when host_handshake_enable=1;
  23-bit beam_watchdog counter with BEAM_WATCHDOG_MAX = 8_000_000 (~80 ms
  at 100 MHz, ~10 nominal frames); beam_handshake_watchdog_fired output
  sticky across mixers_enable cycles, cleared only by reset_n; mid-wait
  disable releases the FSM so dropping the opcode never strands the
  radar between frames.
- radar_receiver_final.v: thread stm32_beam_ready_async +
  host_handshake_enable + beam_handshake_watchdog_fired through the
  scheduler instance.
- radar_system_top.v: rename input port stm32_new_chirp → stm32_beam_ready;
  add host_handshake_enable register (cold-reset = 1'b0); opcode 0x1A
  dispatch (value[0]); add rx_beam_handshake_watchdog wire; pack into
  status_words[4][1] in both USB paths.
- radar_system_top_50t.v: rename wrapper port + sub-instance wiring.
- usb_data_interface.v + usb_data_interface_ft2232h.v: add
  status_beam_handshake_watchdog input + 2-FF level CDC (same convention
  as F-6.4 / F-1.2 stickies); refresh word-4 layout doc comment; pack
  beam_handshake_wd_sync_1 into status_words[4][1].

XDC:
- xc7a50t_ftg256.xdc + xc7a200t_fbg484.xdc: rename stm32_new_chirp port
  references to stm32_beam_ready (same PD8 pin, F13 on 50T / L18 on
  200T).

MCU:
- main.h: add FPGA_BEAM_READY_Pin = GPIO_PIN_8 + FPGA_BEAM_READY_GPIO_Port
  = GPIOD alongside the existing FPGA_FRAME_PULSE alias.
- main.cpp:runRadarPulseSequence: insert HAL_GPIO_TogglePin(GPIOD,
  GPIO_PIN_8) after each setCustomBeamPattern16(RX) — once after the
  per-azimuth broadside (vector_0), once after matrix1, once after
  matrix2 — between the SPI burst completion and waitForFramePulse.

GUI:
- radar_protocol.py: Opcode.HANDSHAKE_ENABLE = 0x1A;
  StatusResponse.beam_handshake_watchdog = 0 default; parse word 4 bit
  [1] in parse_status_packet; update word-4 layout comment.
- test_GUI_V65_Tk.py: add beam_handshake_watchdog kwarg to
  _make_status_packet (sets bit [1] of word 4); refresh
  test_parse_status_word4_layout_co_spec to cover the new bit (used+9=32);
  add test_parse_status_beam_handshake_watchdog round-trip;
  test_handshake_enable_opcode pins 0x1A; defaults / chirps_mismatch /
  agc-coexist tests gain a watchdog==0 assertion; bump
  test_all_rtl_opcodes_present expected set to include 0x17/0x18/0x19/0x1A.

TB:
- new tb_chirp_scheduler_handshake.v (16 checks): legacy open-loop, edge
  exit (rising + falling), 200-cycle idle hold, watchdog auto-advance
  via force on dut.beam_watchdog, sticky-survives-mixers_disable,
  mid-wait disable release, reset_n clears sticky.
- run_regression.sh: register the new TB in PHASE 1.
- tb_radar_receiver_final.v: tie the 3 new receiver ports off
  (beam_ready_async=0, handshake_enable=0, watchdog unconnected).
- tb_system_mechanics.v / tb_system_opcodes.v: explicit
  .stm32_beam_ready(1'b0) connection (the cold-reset
  host_handshake_enable=0 keeps the FSM out of S_BEAM_WAIT).
- tb_usb_data_interface.v / tb_usb_protocol_v2.v / tb_e2e_dsp_to_host.v /
  tb_ft2232h_frame_drop.v: tie .status_beam_handshake_watchdog(1'b0).

Ride-along ruff sweep (14 → 0 across the repo):
- tb/cosim/compare_independent.py: RUF003 — '5×' → 'at least 5x'.
- tb/cosim/gen_e2e_expected.py: noqa: E402 on the post-sys.path import;
  drop unused EXPECTED_RANGE_BIN + EXPECTED_DOPPLER_BIN_PER_SF imports;
  fold the detect-class slot if/else into a ternary (SIM108).
- tb/cosim/gen_e2e_stimulus.py: drop int() wrapping round() at four
  call sites (RUF046 — round() already returns int in Python 3);
  rewrite the range-bin derivation comment block from code-like
  `# range_bin = ...` to prose (ERA001); strip stray f from
  placeholder-free error string (F541).
- tb/cosim/tb_e2e_dsp_to_host_parse.py: open(path, 'r') → open(path)
  (UP015).
- v7/dashboard.py: '3×' → '3x' (RUF003); drop quotes from
  'StatusResponse | None' annotation (UP037, file already has
  `from __future__ import annotations`).

CI summary (all suites green pre-commit):
- ruff: All checks passed!
- FPGA regression (iverilog): 43 / 0 / 0 (incl. new handshake TB 16/16).
- MCU tests: 51 / 0 + 34 / 0 + 13 / 13 ADAR1000_AGC.
- GUI Tk (test_GUI_V65_Tk): 120 / 0.
- GUI v7 (test_v7): 152 / 0.

Production rollout note: bitstream cold-resets with host_handshake_enable=0
so existing flashes keep their open-loop cadence until the GUI sends
opcode 0x1A=1. Once enabled, the per-pattern dwell tracks both the chirp
ladder (PD14 frame_pulse from commit-3 work) and the MCU pattern-write
completion (PD8 toggle from this commit), eliminating drift from the SPI
burst timing.
2026-05-11 12:07:08 +05:45
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
9bed35287a AUDIT-C16: parameterize NUM_CELLS + sample_counter width for 200T
Pre-fix usb_data_interface.v hardcoded `localparam [14:0] NUM_CELLS =
15'd16384` for the 50T 512-range x 32-doppler layout. On 200T builds
with SUPPORT_LONG_RANGE defined, RP_MAX_OUTPUT_BINS=4096 makes a real
frame 131072 cells, so the fixed value caused two distinct defects:

  (a) value: counter wrapped 8x per real frame; bit-7 frame-start
      marker fired 8x at incorrect host-frame offsets, silently
      desyncing the GUI parser
  (b) width: 15 bits could not represent 131072 (needs 17 bits)

Fix: derive NUM_CELLS = RP_MAX_OUTPUT_BINS * RP_NUM_DOPPLER_BINS and
counter width = RP_DOPPLER_MEM_ADDR_W (14 on 50T, 17 on 200T) from
radar_params.vh, so both scale together with the build define.

Tests:
- tb_audit_c16_num_cells.v: standalone counter-block exerciser (T1
  reset, T2 increment, T3 wrap at NUM_CELLS-1, T4 exactly 2 markers
  across 2*NUM_CELLS ticks, T5 top-bit observability) -- 6/6 PASS at
  both 50T (NUM_CELLS=16384, CTR_W=14) and 200T (131072, 17).
- tb_usb_data_interface.v: existing test 7-8 retargeted from the old
  hardcoded `>=15` / `==15'd16384` invariant to the new parameterized
  one (`==RP_DOPPLER_MEM_ADDR_W` / `==RP_MAX_OUTPUT_BINS*RP_NUM_DOPPLER_BINS`).

Regression: 41/41 PASS (+2 new entries: 50T default + 200T
`+define+SUPPORT_LONG_RANGE`).
2026-04-29 23:01:41 +05:45
Jason
58154a6bf1 fpga: split gpio_dig5/dig7 by fault class (AUDIT-S10)
gpio_dig5 (PD13) previously OR'd six flags — four signal-saturation
classes (AGC, DDC overflow, DDC saturation, MTI saturation) and two
control-fault classes (range-decimator watchdog from F-6.4, CIC->FIR
CDC overrun from F-1.2). The MCU outer-loop AGC reduces RF gain on
PD13 assertion, which is the wrong response to a watchdog or CDC
stall — it just hides the stall behind a quiet receive chain. gpio_dig7
(PD15) was tied 1'b0 as "reserved".

Split:
  gpio_dig5 = signal-saturation only (AGC continues to react correctly)
  gpio_dig7 = control-fault classes

Telemetry: status_words[5][6:5] now exposes the two control-fault
classes in BOTH legacy (FT601) and FT2232H USB variants, with 2-FF
level CDC sync from clk_100m to ft601_clk_in / ft_clk. Bit [7] is
reserved. AUDIT-C12's frame_drop_count at [31:25] is preserved.

50T XDC H12 -> gpio_dig7 pin already assigned (audit AUDIT-C15-era);
no XDC change.

Test: tb/tb_audit_s10_gpio_split.v 17/17 PASS — exercises both the
combinational GPIO split and the CDC status-word packing path.
Regression: 39/39 PASS (was 34/34).
2026-04-29 20:06:52 +05:45
Jason
5d334bfdd6 fix(fpga): TX-N9 — sim-only payload-hold checker on cmd CDC
cmd_data / cmd_opcode / cmd_addr / cmd_value feed downstream CDC sync
chains; the safety property is that they only change on the cycle
cmd_valid rises (RD_PROCESS), and stay held on every other cycle so the
receiver's 2-FF synchronizer sees a clean payload regardless of where
its sample window lands. The FSM satisfies this implicitly today, but
nothing flagged a regression that introduced a stray write somewhere
in the same always block.

Added an `ifdef SIMULATION block at the bottom of both
usb_data_interface.v (FT601 / ft601_clk_in / ft601_reset_n) and
usb_data_interface_ft2232h.v (FT2232H / ft_clk / ft_reset_n). It
snapshots the payload + cmd_valid each cycle and fires
[ASSERT FAIL] TX-N9: cmd_<field> changed while cmd_valid=0 (old -> new)
on any payload change while cmd_valid is low. Local regs suffixed _n9
to avoid future name collisions. Synthesis-inert.

Quick FPGA regression unchanged: USB Data Interface 91/91 PASS, overall
28/29 (same baseline; the 1 fail is the pre-existing iverilog/Xilinx-IP
RX-NEW-3 gap).
2026-04-28 10:03:08 +05:45
Jason
ca2b6e527d fix(fpga): TX-G — surface chirps_mismatch_error to host status
`chirps_mismatch_error` was set in radar_system_top when the host
requested chirps_per_elev != Doppler FFT size, but never wired into the
USB status response — a latent silent failure.

Wired the flag through both USB interfaces (FT601 + FT2232H) into bit
[10] of status word 4 (was reserved). GUI parser exposes it as
StatusResponse.chirps_mismatch.

- usb_data_interface*.v: new status_chirps_mismatch input, packed at [10]
- radar_system_top.v: connect chirps_mismatch_error to both USB instances
- radar_protocol.py + test_GUI_V65_Tk.py: parse new bit, +1 round-trip test
- tb_usb_data_interface.v: drive the new port, update word-4 expectation

Tests: GUI 92/92 (was 91), MCU 75/75, USB TB 91/91, ruff clean repo-wide.
The 2 remaining FPGA regression failures (Receiver Integration, MF Chain)
are the pre-existing iverilog-can't-link-Xilinx-IP issue tracked
separately as the open RX-NEW-3 follow-up.
2026-04-24 11:06:26 +05:45
Jason
21aaa5ac33 fix(fpga): correct USB frame-sync counter for 512x32 cell grid
usb_data_interface.v NUM_CELLS was still 12'd2048 (64 range x 32 doppler)
from the pre-2048-FFT architecture. With 512 range bins x 32 Doppler, the
12-bit counter wrapped every 2048 packets and the host received 8 false
frame-start markers per real frame via the sample_counter==0 bit packed
into the detection byte. Widen counter to 15 bits and set NUM_CELLS to
16384. Sister file usb_data_interface_ft2232h.v was already correct.

Remove three stale testbenches hardcoded to the old 1024-pt / 64-bin
architecture (tb_mf_chain_synth, tb_fullchain_mti_cfar_realdata,
tb_range_fft_realdata). Equivalent current-architecture coverage already
exists in tb_matched_filter_processing_chain, tb_fullchain_realdata,
tb_fft_engine, tb_multiseg_cosim, and tb_mf_cosim.
2026-04-22 15:44:48 +05:45
Jason
5f3002a4d1 merge(wave2): manual resolution of 6 shared files — fft-2048 × p0 audit
Hand-merged files modified on both fix/pre-bringup-audit-p0 and
feat/fft-2048-upgrade. Wave 1 (commit 60e49c7) took 20 files from fft
verbatim; this wave resolves the overlap.

- run_regression.sh: 3-way merge. Adopts fft's ${RECEIVER_RTL[@]} array
  refactor and drops the self-blessing golden pair from p0. Skip count
  bumped to 5.

- usb_data_interface.v (FT601/200T): p0 FSM + clock-loss watchdog kept
  wholesale; widened stream_control 3 -> 6 bits to carry fft's extended
  mode bits through the CDC sync chain and the 0xFF status word.

- mti_canceller.v: fft's BRAM-inferred 512-range-bin implementation as
  the base, with p0's F-6.3 saturation counter grafted onto the d1
  pipeline stage. Overflow detection uses the top-two-bits disagreement
  on diff_{i,q}_full (DATA_WIDTH+1 signed).

- radar_receiver_final.v: fft's 2048-pt / 512-bin structure + p0
  diagnostic plumbing (ADC overrange sticky+CDC, DDC diagnostics,
  tx_frame_start edge detector replacing chirp_counter frame sync,
  mti_saturation_count, range_decim_watchdog).

- radar_system_top.v: clean 3-way merge, orthogonal regions
  (+38 / -27).

- usb_data_interface_ft2232h.v (FT2232H/50T): fft's per-frame bulk BRAM
  rewrite kept wholesale. Ported two p0 items that are orthogonal to
  the write FSM:
    * ft_clk-loss watchdog (heartbeat + 2FF ASYNC_REG sync + 16-bit
      timeout) ORed into a 2FF sync'd ft_effective_reset_n for the FSM.
    * rd_cmd_complete flag so RD_DEASSERT can distinguish a legitimate
      3-byte completion from an ft_rxf_n abort that also zeros
      rd_byte_cnt.

Deliberately NOT taken from 2401f5f: cic_decimator_4x_enhanced.v and
ddc_400m.v reset-strategy changes. Those conflict with p0's shipped
registered-sync-reset + max_fanout=25 distribution, which is already
timing-clean on the production build.
2026-04-21 02:12:04 +05:45
Jason
161e9a66e4 fix: clarify comments — AGC width, dual-USB docstring, BE datasheet ref 2026-04-16 17:51:09 +05:45
Jason
f393e96d69 feat(fpga): make FT2232H default USB interface, rewrite FT601 write FSM, add clock-loss watchdog
- Set USB_MODE default to 1 (FT2232H) in radar_system_top.v; 200T build
  overrides to USB_MODE=0 via build_200t.tcl generic property
- Rewrite FT601 write FSM: 4-state architecture with 3-word packed data,
  pending-flag gating, and frame sync counter
- Add FT2232H read FSM rd_cmd_complete flag, stream field zeroing, and
  range_data_ready 1-cycle pipeline delay in both USB modules
- Implement clock-loss watchdog: ft_heartbeat toggle + 16-bit timeout
  counter drives ft_clk_lost, feeding ft_effective_reset_n via 2-stage
  ASYNC_REG synchronizer chain
- Fix sample_counter reset literal width (11'd0 -> 12'd0)
- Add FT2232H I/O timing constraints to 50T XDC; fix dac_clk comments
- Document vestigial ft601_txe_n/rxf_n ports (needed for 200T XDC)
- Tie off AGC ports on TE0713 dev wrapper
- Rewrite tb_usb_data_interface.v for new 4-state FSM (89 checks)
- Add USB_MODE=1 regression runs; remove dead CHECK 5/6 loop
- Update diag_log.h USB interface comment
2026-04-16 16:18:52 +05:45
Jason
063fa081fe fix: FPGA timing margins (WNS +0.002→+0.080ns) + 11 bug fixes from code review
FPGA timing (400MHz domain WNS: +0.339ns, was +0.002ns):
- DONT_TOUCH on BUFG to prevent AggressiveExplore cascade replication
- NCO→mixer pipeline registers break critical 1.5ns route
- Clock uncertainty reduced 200ps→100ps (adequate guardband)
- Updated golden/cosim references for +1 cycle pipeline latency

STM32 bug fixes:
- Guard uint32_t underflow in processStartFlag (length<4)
- Replace unbounded strcat in getSystemStatusForGUI with snprintf
- Early-return error masking in checkSystemHealth
- Add HAL_Delay in emergency blink loop

GUI bug fixes:
- Remove 0x03 from _HARDWARE_ONLY_OPCODES (was in both sets)
- Wire real error count in V7 diagnostics panel
- Fix _stop_demo showing 'Live' label during replay mode

FPGA comment fixes + CI: add test_v7.py to pytest command

Vivado build 50t passed: 0 failing endpoints, WHS=+0.056ns
2026-04-14 00:08:26 +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
23b2beee53 fix: resolve 3 cross-layer bugs (status_words truncation, mode readback, buffer overread)
Bug 1 (FPGA): status_words[0] was 37 bits (8+3+2+5+3+16), silently
truncated to 32. Restructured to {0xFF, mode[1:0], stream[2:0],
3'b000, threshold[15:0]} = 32 bits exactly. Fixed in both
usb_data_interface_ft2232h.v and usb_data_interface.v.

Bug 2 (Python): radar_mode extracted at bit 21 but was actually at
bit 24 after truncation — always returned 0. Updated shift/mask in
parse_status_packet() to match new layout (mode>>22, stream>>19).

Bug 3 (STM32): parseFromUSB() minimum size check was 74 bytes but
9 doubles + uint32 + markers = 82 bytes. Buffer overread on last
fields when 74-81 bytes passed.

All 166 tests pass (29 cross-layer, 92 GUI, 20 MCU, 25 FPGA).
2026-04-12 22:51:26 +05:45
Jason
4985eccbae Wire self-test results (0x31) to USB status readback path, add fpga_self_test to regression
- usb_data_interface.v: Add 3 self-test status inputs, expand status packet
  from 7 words (header + 5 data + footer) to 8 words (header + 6 data + footer).
  New status_words[5] carries {busy, detail[7:0], flags[4:0]}.
- radar_system_top.v: Wire self_test_flags_latched, self_test_detail_latched,
  self_test_busy to usb_data_interface ports. Add opcode 0x31 as status
  readback alias so host can read self-test results.
- tb_usb_data_interface.v: Add self-test port connections, verify word 5 in
  Group 16, add Group 18 (busy flag + partial failure variant). 81 checks pass.
- run_regression.sh: Add fpga_self_test.v to PROD_RTL lint list and system-
  level compile lists. Add tb_fpga_self_test as Phase 1 unit test.
- 24/24 regression tests pass, lint clean (0 errors, 4 advisory warnings).
2026-03-20 20:03:11 +02:00
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
Jason
2efab23cd9 Fix Vivado DRC: consolidate data_pending flags into single always block, fix MMCM LOCKED false_path
usb_data_interface.v: doppler_data_pending and cfar_data_pending were
driven by two always blocks (CDC sync block set them, write FSM cleared
them). Vivado DRC MDRV-1 flagged this as multiple drivers. Moved all
set/clear logic into the write FSM always block using doppler_valid_ft
and cfar_valid_ft edge wires.

adc_clk_mmcm.xdc: changed set_false_path -from to -through for MMCM
LOCKED pin (not a valid timing startpoint). Eliminates CRITICAL WARNING
from Builds 19/20/21.

19/19 FPGA regression pass.
2026-03-20 01:56:20 +02:00
Jason
0773001708 E2E integration test + RTL fixes: mixer sequencing, USB data-pending flags, receiver toggle wiring (19/19 FPGA)
RTL fixes discovered via new end-to-end testbench:
- plfm_chirp_controller: TX/RX mixer enables now mutually exclusive
  by FSM state (Fix #4), preventing simultaneous TX+RX activation
- usb_data_interface: stream control reset default 3'b001 (range-only),
  added doppler/cfar data_pending sticky flags, write FSM triggers on
  range_valid only — eliminates startup deadlock (Fix #5)
- radar_receiver_final: STM32 toggle signals wired through for mode-00
  pass-through, dynamic frame detection via host_chirps_per_elev
- radar_system_top: STM32 toggle signal wiring to receiver instance
- chirp_memory_loader_param: explicit readmemh range for short chirp

Test infrastructure:
- New tb_system_e2e.v: 46 checks across 12 groups (reset, TX, safety,
  RX, USB R/W, CDC, beam scanning, reset recovery, stream control,
  latency budgets, watchdog)
- tb_usb_data_interface: Tests 21/22/56 updated for data_pending
  architecture (preload flags, verify consumption instead of state)
- tb_chirp_controller: mixer tests T7.1/T7.2 updated for Fix #4
- run_regression.sh: PASS/FAIL regex fixed to match only [PASS]/[FAIL]
  markers, added E2E test entry
- Updated rx_final_doppler_out.csv golden data
2026-03-20 01:45:00 +02:00
Jason
7cdfa486e5 Gap 2 GUI Settings: runtime chirp timing, stream control gating, status readback (18/18 FPGA, 20/20 MCU)
Register map: 0x10-0x15 chirp timing overrides, 0xFF status readback,
0x03 CFAR threshold now wired to actual compare, 0x04 stream control
gates USB write FSM. Status readback sends 7-word packet (0xBB header,
5 status words, 0x55 footer) via toggle CDC.

radar_mode_controller: 6 cfg_* input ports replace hardcoded parameters.
usb_data_interface: stream_control CDC, status_request toggle CDC,
  SEND_STATUS state (3'd7), stream gating in IDLE/HEADER/RANGE/DOPPLER.
radar_system_top: 6 host registers + command decode for 0x10-0x15/0xFF.
radar_receiver_final: 6 host_* timing passthrough ports.

Testbench coverage: RMC 81 checks (+TG16 runtime reconfig), USB 77
checks (+TG15 stream gating, TG16 status readback, TG17 chirp opcodes).
Fixed iverilog 13.0 forward-ref for status_req_toggle_100m.
2026-03-19 23:54:48 +02:00
Jason
e5d1b3cfc3 Gap 4 USB Read Path: wire host-to-FPGA command path with toggle CDC, add read path tests
- usb_data_interface.v: Add FT601 read FSM (RD_IDLE/OE_ASSERT/READING/
  DEASSERT/PROCESS) + cmd_data/valid/opcode/addr/value output ports
- radar_system_top.v: Connect cmd_* ports from usb_inst, add toggle CDC
  for cmd_valid (ft601_clk -> clk_100m), add command decode registers
  (mode/trigger/cfar_threshold/stream_control), wire host_mode and
  host_trigger to rx_inst
- radar_receiver_final.v: Add host_mode[1:0] and host_trigger input
  ports, replace hardcoded mode/trigger in radar_mode_controller instance
- tb_usb_data_interface.v: Connect cmd_* ports, add host data bus driver,
  add Test Groups 12 (single command), 13 (multiple commands), 14
  (read/write interleave). USB TB now has 55 checks.

Regression: 18/18 PASS
2026-03-19 23:16:26 +02:00
Jason
5fd632bc47 Fix all 10 CDC bugs from report_cdc audit, add overflow guard in range_bin_decimator
CDC fixes across 6 RTL files based on post-implementation report_cdc analysis:
- P0: sync stm32_mixers_enable and new_chirp_pulse to clk_120m via toggle CDC
       in radar_transmitter, add ft601 reset synchronizer and USB holding
       registers with proper edge detection in usb_data_interface
- P1: add ASYNC_REG to edge_detector, convert new_chirp_frame to toggle CDC,
       fix USB valid edge detect to use fully-synced signal
- P2: register Gray encoding in cdc_adc_to_processing source domain, sync
       ft601_txe and stm32_mixers_enable for status_reg in radar_system_top
- Safety: add in_bin_count overflow guard in range_bin_decimator to prevent
          downstream BRAM corruption

All 13 regression test suites pass (159 individual tests).
2026-03-17 13:48:47 +02:00
Jason
49eb6169b6 Widen ft601_be to [3:0] for 32-bit FT601 mode, fix NCO XSim TB
- Expand ft601_be from [1:0] to [3:0] across RTL, top-level, testbenches,
  and XDC (uncomment be[2:3] pin assignments B21/A21)
- Fix NCO XSim testbench: correct reset check (0x7FFF not 0), add pipeline
  warmup and sample skip for DSP48E1 quadrature test
- All local regression tests pass (39/39 USB, 10/10 integration, all co-sim)
2026-03-16 23:17:38 +02:00
Jason
1acedf494c Migrate hardware platform from XC7A50T to XC7A200T-2FBG484I
Production FPGA: Artix-7 XC7A200T-2FBG484I (33,650 slices, 740 DSP48E1,
365 BRAM, -2 speed grade). Pin-mapped across 6 banks with proper VCCO
assignment (3.3V/2.5V/1.8V).

RTL timing primitives added for clean timing closure:
- ad9484_interface_400m.v: BUFIO for IDDR capture at 400MHz DDR,
  BUFG for fabric logic, reset synchronizer (P1-7)
- dac_interface_single.v: ODDR for dac_clk forwarding + dac_data[7:0]
  output registration, eliminates clock-forwarding insertion delay
- usb_data_interface.v: ODDR for ft601_clk_out forwarding, FSM runs
  on ft601_clk_in domain with CDC synchronizers

Constraints:
- New production XDC (xc7a200t_fbg484.xdc): 182 pins, generated clocks
  for ODDR outputs, BUFIO/DDR input delays, fixed false_path strategy
  (from reset source, not to CLR pins), IOB packing on cells not ports
- Preserved upstream XDC as xc7a50t_ftg256.xdc for reference
- Updated cntrt.xdc with DRC fixes (I/O standards, missing constraints)
2026-03-16 22:24:22 +02:00
Jason
c983a3c705 Achieve timing closure: DSP48E1 pipelines, 4-stage NCO, 28-bit CIC, ASYNC_REG
Phase 0+ timing optimization (attempts #13-22 + implementation):

NCO (nco_400m_enhanced.v):
- 4-stage pipeline: DSP48E1 accumulate -> LUT read -> negate -> quadrant MUX
- DSP48E1 phase accumulator in P=P+C mode (eliminates 8-stage CARRY4 chain)
- Registered phase_inc_dithered to break cascaded 32-bit add path

DDC (ddc_400m.v):
- Direct DSP48E1 instantiation for I/Q mixers (AREG=1, BREG=1, MREG=1, PREG=1)
- CEP=1, RSTP=!reset_n for proper pipeline control
- 3-stage dsp_valid_pipe for PREG=1 latency
- Behavioral sim model under ifdef SIMULATION for Icarus compatibility

CIC (cic_decimator_4x_enhanced.v):
- 28-bit accumulators (was 36) per CIC width formula: 18 + 5*log2(4) = 28
- Removed integrator/comb saturation (CIC uses wrapping arithmetic by design)
- Pipelined output saturation comparison

CDC/ASYNC_REG:
- ASYNC_REG attribute on all CDC synchronizer registers (cdc_modules.v,
  radar_system_top.v, usb_data_interface.v)
- Sync reset in generate blocks (cdc_modules.v)

Results: Vivado post-implementation WNS=+1.196ns, 0 failing endpoints,
850 LUTs (1.34%), 466 FFs (0.37%), 2 DSP48E1 (0.83%) on xc7a100t.
All testbenches pass: 241/244 (3 known stub failures).
2026-03-16 01:02:07 +02:00
Jason
ffed7c1623 Fix CDC timing violations: add synchronizers for all inter-clock crossings
Resolve all 4 inter-clock timing violations found in Vivado synthesis
attempt #11 (WNS was -2.552 ns). Changes:

- Add reset synchronizer for clk_120m_dac domain (2-FF chain)
- Add Gray-code CDC for chirp_counter (6-bit, 120MHz->100MHz)
- Add single-bit CDC for new_chirp_frame (3-stage, 120MHz->100MHz)
- Add 2-stage input synchronizers for valid signals in USB module
  (clk_100m->ft601_clk_in) with data capture on rising edge
- Fix ft601_clk_out multi-driven net (removed duplicate assignment)
- Update XDC: set_max_delay -datapath_only for CDC, false_path for reset

Result: Vivado attempt #12 passes with 0 errors, 0 timing violations,
and 'All user specified timing constraints are met.' (WNS +0.983 ns)
2026-03-15 17:58:14 +02:00
Jason
c871281f1e Fix synthesis blockers in 9 RTL files for Vivado compatibility
Guard all $display/$error/$time calls with ifdef SIMULATION in:
- chirp_memory_loader_param.v (initial + always block debug prints)
- cic_decimator_4x_enhanced.v (5 saturation/overflow displays + monitor block)
- ddc_400m.v (FIR/baseband debug monitor block)
- fft_1024_inverse.v (IFFT config debug block)
- matched_filter_multi_segment.v (16 state machine displays + monitor)
- nco_400m_enhanced.v (initial block debug print)
- radar_receiver_final.v (frame/chirp counter displays)

Replace SystemVerilog constructs with Verilog-2001:
- usb_data_interface.v (typedef enum -> localparam + reg)
- usb_packet_analyzer.v (typedef enum -> localparam, output reg -> wire)
2026-03-15 14:53:35 +02:00
NawfalMotii79
9d99a8e976
Add files via upload 2026-03-10 01:23:26 +00:00