Commit Graph

44 Commits

Author SHA1 Message Date
Jason ada170ef1f feat(fpga,mcu,gui): PR-AB.b — drift-free dwell sync via DIG_6 frame_pulse + AGC always-on policy
FPGA (Phase 1+2):
- gpio_dig6 (PD14) now carries chirp_scheduler frame_pulse, FPGA-stretched
  to ~100 ns so the STM32 EXTI on PD14 can latch reliably.
- gpio_dig7 (PD15) returns to its pre-PR-AB.b role: control-fault OR
  (range_decim_watchdog | CDC overrun); MCU stuck-high sampler unchanged.
- rx_range_decim_watchdog gains a sticky in source clock domain so a slow
  status poll cannot miss a 1-cycle assertion (Phase 1).
- New tb_dig6_frame_pulse.v (13 checks); tb_status_words_stickies.v extended
  with DIG_7 fault-OR coverage (14 checks); retired tb_audit_s10_gpio_split.v.
- Port comments in radar_system_top.v / _50t.v and XDC roles refreshed.

MCU (Phase 3):
- PD14 reconfigured to GPIO_MODE_IT_RISING + GPIO_PULLDOWN; new
  EXTI15_10_IRQHandler in stm32f7xx_it.c dispatches to HAL_GPIO_EXTI_Callback
  that bumps a volatile g_frame_pulse_count.
- runRadarPulseSequence dwell loop replaces 3x HAL_Delay(8) with
  waitForFramePulse(20) — per-pattern dwell now tracks the actual mask-aware
  ladder length (drift-free, mask-aware), with a 20 ms timeout safety net.
- AGC outer loop is ALWAYS-ON in production (compile-time policy); bench
  builds compile the body out via -DMCU_AGC_FORCE_DISABLED. The runtime
  enable/debounce + DIG_6 polling that previously gated AGC are removed.
- main.h adds FPGA_FRAME_PULSE_* aliases pointing at FPGA_DIG6_*.

GUI (Phase 4):
- Settings tab gains a Bench / Diagnostics group with a BENCH-MODE checkbox
  (off by default, persisted via QSettings).
- AGC group header swaps between a green "AGC: ALWAYS-ON" badge (production)
  and Enable/Disable AGC buttons (bench), pinned to the top of the group.
  The redundant 0/1 spinbox row for opcode 0x28 is removed — buttons send
  the same opcode and cannot accept invalid input.
- Both the FPGA Control AGC Status box and the AGC Monitor strip share a
  helper that honours bench-mode in production (always shows ALWAYS-ON in
  green so the two views never disagree with the badge).
- _add_fpga_param_row uses setFixedWidth on label and Set button + explicit
  stretch=1 on the hint, so all rows align column-wise whether they sit
  directly in a QVBoxLayout or inside a wrapper QWidget.

Regression: FPGA 42/0/0 (PR-M.4 baseline) - MCU 34/34 - GPS extended 51/51
- GUI v7 150/150 - BENCH-MODE flip behaviorally verified.
Hardware-blocked steps deferred: bench-scope verify (PD14 dwell pulse,
counter advance, PD15 stuck-high recovery still triggers).

Closes #182.
2026-05-07 13:29:48 +05:45
Jason d6462c6d5b docs(fpga): PR-X.2 F-7.2 — refresh adc_clk_mmcm.xdc comments to SDR IFF
Constraint values are unchanged — only the explanatory comments are
refreshed to match the post-AUDIT-C4 (2026-05-01) RTL.

  - "IDDR outputs"          → "IFF Q output"
  - "adc_data_{rise,fall}_bufg" → "adc_data_iff_bufg"
  - "the IDDR ~1.4ns before the clock" → "the IOB-packed IFF ~1.4ns
    before the clock"
  - hold-waiver header "BUFIO-clocked IDDR" → "BUFIO-clocked IFF, SDR"
  - "BUFIO/IDDR domain"     → "BUFIO/IFF domain"

The 3.000 ns set_max_delay (BUFIO ↔ clk_mmcm_out0), the
set_false_path -hold on adc_d_p[*]/adc_or_p, the LOCKED-pin
through-waiver, and the 0.150 ns set_clock_uncertainty all remain
bit-identical — the IFF capture has the exact same source-synchronous
timing argument as the prior IDDR did.

One reference to "IDDR" deliberately kept inside a negation
("no IDDR, no rise/fall demux") — explicitly contrasts the current
SDR topology with the broken pre-C-4 shape so a reader who finds the
old vocabulary in git history understands what changed.
2026-05-05 13:46:12 +05:45
Jason 6f5ff792fa fix(fpga): C-4 — replace IDDR DDR demux with negedge IFF for AD9484 SDR
The AD9484 is SDR LVDS — datasheet p.5 lists "Output (LVDS—SDR)" as the
only output mode and p.16 confirms "data outputs are valid on the rising
edge of DCO." DCO runs at fs (400 MHz), one new sample per period, held
stable across the period. There is no DDR mode and no SPI access (CSB is
tied to +1V8 on the production board, RADAR_Main_Board.sch:46719).

ad9484_interface_400m.v previously instantiated an IDDR per data bit and
alternated Q1/Q2 via a `dco_phase` FSM, expecting to demux a "DDR" stream
into 400 MSPS. Because the chip is SDR, both Q1 and Q2 represent the same
sample, and the alternation produced approximately
  [s_{-1}, s_1, s_1, s_3, s_3, s_5, …]
— odd-sample duplication with even-sample loss, equivalent to
decimate-by-2 followed by ZOH-upsample-by-2. In the frequency domain
that's a fold around fs/4 = 100 MHz; our 120-150 MHz IF lands at
50-80 MHz, so the DDC's 120 MHz NCO mixes the wrong frequency and the
matched filter sees baseband 40-70 MHz off where it expects.

The bug was hidden by tb/ad9484_interface_400m_stub.v, which has always
done single-rising-edge SDR-correct capture, so all iverilog regression
ran against the correct semantics — only the synthesizable Xilinx-
primitive path was wrong. This bug only fires on real silicon.

Fix:
- ad9484_interface_400m.v: drop IDDR + dco_phase; capture each data bit
  with a single (* IOB = "TRUE" *) negedge-clocked IFF on adc_dco_bufio.
  Falling DCO sits 1.25 ns inside AD9484's stable window, giving ~0.4 ns
  setup margin against tPD = 0.85 ns. Same pattern on the OR (overrange)
  path. Output FSM now emits one Q per BUFG cycle = clean 400 MSPS.
- tb_ad9484_xsim.v: add Test Group 8 (AUDIT-C4) that drives a 64-sample
  counter ramp synchronously with rising DCO, captures the output, and
  asserts (a) consecutive deltas equal +1 for ≥ (captured-6) of the
  stream, (b) zero duplicate samples (catches DDR-style demux), (c) zero
  unexpected jumps (catches DDR-style sample drops). This locks in SDR
  semantics so any future regression that reintroduces a DDR demux on
  this chip fails loudly.
- ad9484_interface_400m_stub.v: comment-only update — the stub already
  does correct SDR capture; document AUDIT-C4 + why iverilog regression
  was silent on the synth-path bug.
- xc7a200t_fbg484.xdc: fix stale "DDR class" comment near the OR pair
  (now "SDR LVDS").

Verification: bash run_regression.sh — 42 passed, 0 failed, 1 skipped
(the skip is the T-6 drift cosim, which needs scipy from the dev group;
CI installs it via uv sync --group dev). Test Group 8 in the xsim TB
runs against the real UNISIM primitives and is exercised separately on
the Vivado host (run_xfft_xsim.sh-style flow).
2026-05-01 23:12:55 +05:45
Jason b3b4580e9c xdc(200t): pin AD9484 OR LVDS pair to U20/V20 (L11_T1_SRCC_14)
Closes AUDIT-C15. The 200T XDC `adc_or_p/n` PACKAGE_PIN was a TODO
placeholder that blocked all 200T synth/impl with unplaced-IO errors.

Pins U20/V20 are L11P/L11N_T1_SRCC_14 — same T1 clock tile as adc_dco_p
on L12_MRCC (W19/W20), so OR captures with the same IBUFDS->BUFIO->IDDR
source-synchronous topology as adc_d_p[*]. Free-pair confirmed by Vivado
get_package_pins query against xc7a200tfbg484-2.

Adds DIFF_TERM TRUE on adc_or_n (was only on the p-side; explicit on
both is safer). Adds input_delay constraints mirroring adc_d_p
(max 1.0 ns / min 0.2 ns on both edges).

Header pin counts updated: Bank 14 21/50 used, total 184/285.

This is the FPGA-team RECOMMENDATION for the production PCB (NEW
design); the PCB designer must route AD9484 OR+ -> U20 and OR- -> V20.

Validation:
- read_xdc + link_design -part xc7a200tfbg484-2 -> READ OK on both
  xc7a200t_fbg484.xdc and adc_clk_mmcm.xdc; no PACKAGE_PIN errors.
- ./run_regression.sh --quick: 29/29 PASS (RTL untouched).
2026-04-29 16:01:04 +05:45
Jason cc6691dec9 perf(fpga): move CIC comb stages to fabric — 80→70 DSPs (-10)
Strip the explicit DSP48E1 instance from comb stage 0 and the
(* use_dsp = "yes" *) attribute from comb stages 1-4. The combs are
gated by data_valid_comb_pipe (fires once every 4 clk_400m cycles
post-decimation), so a multicycle path of 4 -setup / 3 -hold scoped
to the comb registers in xc7a50t_ftg256.xdc gives STA 10 ns of slack
for fabric carry-chain to close 28-bit subtracts comfortably.

Pipeline depth and bit-widths unchanged: the new fabric model mirrors
the prior CREG+AREG+BREG+PREG structure exactly, so data_valid_comb_0_out
alignment and downstream stages 1-4 see bit-identical samples. CIC
behavioral simulation model now lives outside the SIMULATION ifdef
branch (used unconditionally) since there is no longer a synthesis-only
DSP48E1 to replace.

50T post-impl results (Vivado 2025.2):
  DSPs:         80 → 70 / 120 (66.7% → 58.3%, freed 10)
  LUTs:         22114 / 32600 (67.8%)
  BRAM:         55.5 / 75 (74.0%, unchanged)
  adc_dco_p WNS: +0.022 ns → +0.906 ns (margin improved)
  All clocks meet timing, 0 failing endpoints.

Local regression: 32/34 PASS — same as baseline; the two failures
(Receiver Integration, Matched Filter Chain) are pre-existing
RX-NEW-3 (FFT throughput) and unaffected by this change. Bit-exact
through DDC chain (NCO→CIC→FIR) and MF cosim verified.

Cumulative DSP savings today: 112 → 70 (freed 42), enough headroom
for Xilinx LogiCORE FFT Pipelined Streaming swap (~33 DSPs for the
3-instance matched-filter chain) with 17 DSPs to spare.
2026-04-23 11:32:03 +05:45
Jason 8865e9a0ef fix(fpga): pre-bringup RTL hardening + test-suite hardening
RTL (P0 pre-bringup findings R-1/R-2/R-3/R-5/R-6):

- mti_canceller: add use_long_chirp input and waveform-boundary mute
  so the long->short transition in mode 01 no longer subtracts across
  heterogeneous waveforms (R-1). Prev buffer is overwritten in-flight
  at the boundary so the next same-waveform chirp subtracts cleanly.
- ad9484_interface_400m: 2FF sync of mmcm_locked into the 400 MHz
  domain before gating reset_n_gated (R-6).
- cic_decimator_4x_enhanced: correct max_fanout narrative (R-3).
- ad9484_interface_400m: strip stale pblock comment, note 3.0 ns
  max_delay instead (R-2).
- mti_canceller / doppler_processor: 200T-20km WARNING banners
  flagging the broken 4096-bin path (R-5). 9-bit BRAM address aliases
  silently until rewritten.
- adc_clk_mmcm.xdc: relax set_max_delay from 2.700 -> 3.000 ns,
  closes WNS with headroom on 50T build.
- radar_receiver_final: wire use_long_chirp into mti_inst.

Architecture-bump finalization (2048-pt range FFT, 512 range bins,
32 Doppler bins -> 16384 output cells per frame):

- tb/cosim/radar_scene.py: FFT_SIZE 1024 -> 2048, RANGE_BINS 64 -> 512.
- tb/gen_mf_golden_ref.py: N 1024 -> 2048.
- Regenerate all affected hex goldens (MF cases 1-4, Doppler inputs
  + py goldens, receiver integration golden_doppler.mem 2048 -> 16384).
- tb_radar_receiver_final: widen range_bin_out 6 -> 9 bits, bump
  GOLDEN_ENTRIES 2048 -> 16384, expand bitmaps/arrays to 512 bins,
  update all check messages and thresholds.
- tb_mti_canceller, tb_fullchain_mti_cfar_realdata: tie/pass
  use_long_chirp so compile still works after RTL port add.

Test-suite hardening (coverage audit findings):

- tb_mti_canceller T12: 10 new assertions exercising R-1 waveform-
  boundary mute across a long/long/short/short/long sequence. Catches
  a regression that re-enables subtraction across the boundary.
- tb_fir_lowpass: replace tautological check(1'b1, ...) on coefficient
  symmetry with a real hierarchical check coeff[k]===coeff[31-k];
  replace always-pass overflow check with a well-driven (not X/Z)
  assertion on filter_overflow.
- tb_matched_filter_processing_chain: replace three always-pass peak-
  bin placeholders with peak-to-mean-|out| > 2x ratio checks (catches
  flat/zero output that the old tautologies silently accepted).
- tb_cdc_modules M2: replace always-pass narrow-pulse check with a
  well-defined-output assertion on the synchronizer.
- tb_nco_400m: replace always-pass freq-switch check with a swing +
  no-X assertion across 200 post-switch samples.
- tb_system_e2e G12.1: replace check(1, ...) with test_num > 20 so
  it catches a stalled TB that skipped prior groups.
- tb_multiseg_cosim TEST 4: replace always-pass placeholder with a
  bitmap that asserts segment_request visited all 4 values.
- tb_mf_chain_synth and tb_fullchain_mti_cfar_realdata: add DEPRECATED
  headers plus \$fatal guards (ifndef ALLOW_STALE_*) so they cannot
  be silently re-enabled in CI with stale 1024-bin goldens against
  current 2048-pt RTL.

Regression: 32 passed, 0 failed. MTI TB grew 30 -> 39 checks;
receiver integration grew 17 -> 18 checks with 16384/16384 golden
match at tolerance +/- 2 LSB.
2026-04-22 13:23:38 +05:45
Jason 8b4de5f9ee fix(fpga): extend ADC hold waiver to include adc_or_p (F-0.1 follow-up)
adc_or_p (overrange pin, added in commit 70067c6 for audit finding F-0.1)
uses the same IBUFDS→BUFIO source-synchronous capture topology as the 8
data pins adc_d_p[*]. STA reports identical -1.913 ns hold on this path
for the same reason (clock insertion ~4.0 ns via BUFIO vs data IBUFDS
~0.9 ns). External PCB layout guarantees hold, not FPGA clock tree.

Extends the existing adc_d_p[*] false_path waiver to cover adc_or_p.
Post-route now clean: WNS +0.034 ns, WHS positive.
2026-04-20 23:28:58 +05:45
Jason 0496291fc5 fix(fpga): F-0.9 option B — FT2232H output_delay 11.667→3.5 ns (TN_167)
Previous output_delay of 11.667 ns was a synthetic back-calculation
(period − 5 ns), not a datasheet number. It over-constrained FPGA
launch by ~8 ns vs the actual FT2232H 245-Sync FIFO setup requirement.

Per FTDI TN_167:
- t_su (data to CLKOUT rising):  3.5 ns  (was 11.667 — too tight)
- t_h  (data hold after CLKOUT): 1.0 ns  (was 0.0 — no hold check)
- t_co (CLKOUT to data valid):   10.0 ns (was 9.667 — close)
- t_coh (CLKOUT to data hold):   0.5 ns  (was 0.0 — no hold check)

NB: values must be verified against the exact TN_167 revision in use
before shipping. If the engineer's revision differs, numbers change
but the direction (big relaxation of output_delay_max) is correct.
2026-04-20 21:47:26 +05:45
Jason bec578a5e7 Revert "fix(fpga): F-0.9 option A — BUFIO+BUFR for 50T ft_clkout (SRCC pin)"
This reverts commit 30279e8c4d.
2026-04-20 21:47:19 +05:45
Jason 30279e8c4d fix(fpga): F-0.9 option A — BUFIO+BUFR for 50T ft_clkout (SRCC pin)
C4 is an SRCC pin (IS_CLK_CAPABLE=1, IS_MASTER=0 in the Vivado device
model), not an MRCC as earlier comments claimed. SRCC cannot drive BUFG
through dedicated routing, so the previous CLOCK_DEDICATED_ROUTE=FALSE
override forced fabric routing and burned ~5 ns on the ft_clkout path
(WNS -5.362 ns in the d36a4c9 build).

Swap to BUFIO + BUFR for USB_MODE=1 (50T/FT2232H): SRCC → BUFIO → BUFR
is the standard 7-series path for regional clock distribution. All
ft_clkout-domain logic (FT2232H FSM, toggle CDCs, USB FIFO flops) is
contained in bank 35 / one clock region, so regional distribution is
sufficient. USB_MODE=0 (200T/FT601) keeps the BUFG because D17 is a
proper MRCC pin.

Removed CLOCK_DEDICATED_ROUTE=FALSE from both the XDC and the build
script — no longer needed with dedicated BUFIO/BUFR routing.
2026-04-20 20:53:49 +05:45
Jason d36a4c93e2 fix(fpga): audit F-2026-04-20-A/B — CIC reset fan-out + BUFIO→BUFG max_delay
A: cic_decimator_4x_enhanced.v reset_h max_fanout 50→25. More replicas
mean each drives fewer DSP48 RSTB loads, letting Vivado place each
closer to its consumers. Targets the rep__24 → comb_reg[4]/RSTB path
that failed clk_mmcm_out0 intra by -10 ps (1.4 ns of pure routing).

B: adc_clk_mmcm.xdc BUFIO↔BUFG max_delay 2.500→2.700 ns. The 2.5 ns
target was tighter than achievable for the IDDR (ILOGIC) → FDRE (fabric
SLICE) re-registration. The effective window is the BUFIO↔BUFG phase
relationship (not the clock period), so 2.7 ns remains safe. Fixes the
adc_dco_p→clk_mmcm_out0 inter path -113 ps failure on lane 7.
2026-04-20 20:20:43 +05:45
Jason bf89984f04 Revert "fix(fpga): IOB=TRUE on FT2232H pads to meet 5 ns FPGA launch budget"
This reverts commit 94bf6944a3.
2026-04-20 20:20:02 +05:45
Jason 94bf6944a3 fix(fpga): IOB=TRUE on FT2232H pads to meet 5 ns FPGA launch budget
Post-route WNS = -5.355 ns on path group ft_clkout, net
  u_core/gen_ft2232h.usb_inst/ft_data_TRI[0]_repN_1

FT2232H 245-sync FIFO input setup (t_su = 11.667 ns on a 16.667 ns
CLKOUT) leaves the FPGA only ~5 ns from clock edge to pad. Without
IOB=TRUE, the output / tristate FFs live in fabric and FF→OBUFT
routing eats 2–3 ns, forcing Vivado to replicate the tristate
driver (ft_data_TRI[*]_repN) and still miss timing.

The FSM in usb_data_interface_ft2232h.v already registers
ft_data_out / ft_data_oe / ft_{rd,wr,oe}_n at the output boundary
in the ft_clk domain, so packing them into the IOB is safe with
no RTL change.
2026-04-20 16:43:12 +05:45
Jason 0067969ee7 fix(fpga): wire F-0.1 adc_or_p/n through 50T wrapper + remove xdc control-flow
Build-blocking fixes surfaced by gpu-server synth:

1. radar_system_top_50t.v wrapper was missing adc_or_p/n ports and the
   u_core instantiation left them unconnected. Every XDC line in the 50T
   anchor block (PACKAGE_PIN M6/N6, IOSTANDARD, DIFF_TERM, set_input_delay)
   therefore matched no ports and emitted CRITICAL WARNINGs, leaving the
   overrange pin effectively tied off. Added the two inputs and wired them
   through to the core.

2. adc_clk_mmcm.xdc used foreach / unset — Vivado's XDC parser only
   accepts a restricted Tcl subset and rejected them as
   [Designutils 20-1307]. Moved the clk_mmcm_out0 ↔ USB-clock false paths
   into each board XDC (ft_clkout for 50T, ft601_clk_in for 200T) where
   the clock name is already known.
2026-04-20 16:08:13 +05:45
Jason 70067c6121 fix(fpga): F-0.1 wire AD9484 OR overrange pin into diagnostics
The AD9484 OR (overrange) LVDS pair is routed on the 50T main board to
xc7a50t-ftg256 bank-14 pins M6/N6 but was previously left unconnected at
the top level. Plumb it through the full stack so saturation at the raw
ADC boundary shows up in the existing overflow aggregation:

- ad9484_interface_400m: add adc_or_p/n inputs, IBUFDS + IDDR capture of
  both phases in the BUFIO domain, re-register into the clk_400m BUFG
  domain, OR rise|fall into adc_overrange_400m output.
- radar_receiver_final: stickify adc_overrange_400m in clk_400m, CDC to
  clk_100m via a 2FF ASYNC_REG chain (same reasoning as F-1.2's
  cdc_cic_fir_overrun — single-bit, latched low→high, GPIO-class
  diagnostic), OR into the existing ddc_overflow_any aggregation.
- radar_system_top: expose adc_or_p/n top-level ports and pass through.
- xc7a50t_ftg256.xdc: anchor M6/N6 as LVDS_25 DIFF_TERM, with the same
  DCO-relative input-delay constraints as adc_d_p[*].
- xc7a200t_fbg484.xdc: IOSTANDARD/DIFF_TERM set; PACKAGE_PIN left as a
  documented TODO — the 200T dev-board schematic has not been checked
  and the 200T build will need the anchor filled in before place/route.
2026-04-20 15:48:34 +05:45
Jason 675b1c0015 fix(pre-bringup): second-batch P1/P2/P3 audit findings
Addresses the remaining actionable items from
docs/DEVELOP_AUDIT_2026-04-19.md after commit 3f47d1e.

XDC (dead waivers — F-0.4, F-0.5, F-0.6, F-0.7):
- ft_clkout_IBUF CLOCK_DEDICATED_ROUTE now uses hierarchical filter;
  flat net name did not exist post-synth.
- reset_sync_reg[*] false-path rewritten to walk hierarchy and filter
  on CLR/PRE pins.
- adc_clk_mmcm.xdc ft601_clk_in references replaced with foreach-loop
  over real USB clock names, gated on -quiet existence.
- MMCM LOCKED waiver uses REF_PIN_NAME filter instead of the
  previously-missing u_core/ literal path.

CDC (F-1.1, F-1.2, F-1.3):
- Documented the quasi-static-bus stability invariant above the
  FT601 cmd_valid toggle block.
- cdc_adc_to_processing gains an `overrun` output; the two CIC->FIR
  instances feed a sticky cdc_cic_fir_overrun flag surfaced on
  gpio_dig5 so silent sample drops become visible to the MCU.
- Removed the dead mixers_enable synchronizer in ddc_400m.v; the _sync
  output was unused and every caller ties the port to 1'b1.

Diagnostics (F-6.4):
- range_bin_decimator watchdog_timeout plumbed through receiver
  and top-level, OR'd into gpio_dig5.

ADAR (F-4.7):
- delayUs() replaced with DWT cycle counter; self-initialising
  TRCENA/CYCCNTENA, overflow-safe unsigned subtraction.

Regression: tb_cdc_modules.v 57/57 passes under iverilog after
the cdc_modules.v change. Remote Vivado verification in progress.
2026-04-20 14:28:22 +05:45
Jason 3f47d1ef71 fix(pre-bringup): resolve P0 + quick-win P1 findings from 2026-04-19 audit
Addresses findings from docs/DEVELOP_AUDIT_2026-04-19.md:

P0 source-level:
- F-4.3 ADAR1000_Manager::adarSetTxPhase now writes REG_LOAD_WORKING
  with LD_WRK_REGS_LDTX_OVERRIDE (0x02) instead of 0x01. Previous value
  toggled the LDRX latch on a TX-phase write, so host TX phase updates
  never reached the working registers.
- F-6.1 DDC mixer_saturation / filter_overflow / diagnostics were deleted
  at the receiver boundary. Now plumbed to new outputs on
  radar_receiver_final (ddc_overflow_any, ddc_saturation_count) and
  aggregated into gpio_dig5 in radar_system_top. Added mark_debug
  attributes for ILA visibility. Test/debug inputs tied low explicitly.
- F-0.8 adc_clk_mmcm.xdc set_clock_uncertainty: removed invalid -add
  flag (Vivado silently rejected it, applying zero guardband). Now uses
  absolute 0.150 ns which covers 53 ps jitter + ~100 ps PVT margin.

P1:
- F-4.2 adarSetBit / adarResetBit reject broadcast=ON — the RMW sampled
  a single device but wrote to all four, clobbering the other three's
  state.
- F-4.4 initializeSingleDevice returns false and leaves initialized=false
  when scratchpad verification fails; previously marked the device
  initialized anyway so downstream PA enable could drive a dead bus.
- F-6.2 FIR I/Q filter_overflow ports, previously unconnected, now OR'd
  into the module-level filter_overflow output.
- F-6.3 mti_canceller exposes 8-bit saturation counter. Saturation was
  previously invisible and produces spurious Doppler harmonics.

Verification:
- 27/27 iverilog testbenches pass
- 228/228 pytest pass (cross-layer contract + cosim)
- MCU unit tests 51/51 + 24/24 pass
- Remote Vivado 2025.2 build: bitstream writes; 400 MHz mixer pipeline
  now shows WNS -0.109 ns which MATCHES the audit's F-0.9 prediction
  that the design only closed because F-0.8's guardband was silently
  dropped. ft_clkout F-0.9 remains a show-stopper (requires MRCC pin
  move), tracked separately.

Not addressed in this PR (larger scope, follow-up tickets):
F-0.4, F-0.5, F-0.6, F-0.7, F-0.9, F-1.1, F-1.2, F-2.2, F-3.2, F-4.1,
F-4.7, F-6.4, F-6.5.
2026-04-20 13:48:36 +05:45
NawfalMotii79 d3476139e3 Merge pull request #89 from NawfalMotii79/feat/ft2232h-default-ft601-option
feat: make FT2232H default USB interface, add FT601 premium option, deprecate GUI V6
2026-04-17 22:21:58 +01:00
Jason 658752abb7 fix: propagate FPGA AGC enable to MCU outer loop via DIG_6 GPIO
Resolve cross-layer AGC control mismatch where opcode 0x28 only
controlled the FPGA inner-loop AGC but the STM32 outer-loop AGC
(ADAR1000_AGC) ran independently with its own enable state.

FPGA: Drive gpio_dig6 from host_agc_enable instead of tied low,
making the FPGA register the single source of truth for AGC state.

MCU: Change ADAR1000_AGC constructor default from enabled(true) to
enabled(false) so boot state matches FPGA reset default (AGC off).
Read DIG_6 GPIO every frame with 2-frame confirmation debounce to
sync outerAgc.enabled — prevents single-sample glitch from causing
spurious AGC state transitions.

Tests: Update MCU unit tests for new default, add 6 cross-layer
contract tests verifying the FPGA-MCU-GUI AGC invariant chain.
2026-04-17 00:04:37 +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 e4db996db9 fix: remove server credentials from constraints README
Accidentally included SSH key path, hostname, port, and internal server
paths in the build quick-reference section. Replaced with generic
instructions.
2026-04-07 21:37:30 +03:00
Jason 75854a39ca docs: update constraints README with USB_MODE architecture and build guide
Add USB Interface Architecture section documenting the USB_MODE parameter,
generate block mechanism, per-target wrapper pattern, FT2232H pin map, and
build quick-reference. Update top modules table (50T now uses
radar_system_top_50t), bank voltage tables, and signal differences to
reflect the FT2232H/FT601 dual-interface design.
2026-04-07 21:34:38 +03:00
Jason 408f4d126f feat(usb): add FT2232H USB 2.0 interface for 50T production board
Replace FT601 (USB 3.0, 32-bit) with FT2232H (USB 2.0, 8-bit) on the
50T production board per updated Eagle schematic (commit 0db0e7b).
USB 3.0 via FT601 remains available on the 200T premium board.

RTL changes:
- Add usb_data_interface_ft2232h.v: 245 Sync FIFO interface with toggle
  CDC (3-stage) for reliable 100MHz->60MHz clock domain crossing,
  mux-based byte serialization for 11-byte data packets, 26-byte status
  packets, and 4-byte sequential command read FSM
- Add USB_MODE parameter to radar_system_top.v with generate block:
  USB_MODE=0 selects FT601 (200T), USB_MODE=1 selects FT2232H (50T)
- Wire FT2232H ports in radar_system_top_50t.v with USB_MODE=1 override,
  connect ft_clkout to shared clock input port
- Add post-DSP retiming register in ddc_400m.v to fix marginal 400MHz
  timing path (WNS improved from +0.070ns to +0.088ns)

Constraints:
- Add FT2232H pin assignments for all 15 signals on Bank 35 (LVCMOS33)
- Add 60MHz ft_clkout clock constraint (16.667ns) on MRCC N-type pin C4
- Add CLOCK_DEDICATED_ROUTE FALSE for N-type MRCC workaround
- Add CDC false paths between ft_clkout and clk_100m/clk_120m_dac

Build scripts:
- Add PLIO-9 DRC demotion and CLOCK_DEDICATED_ROUTE property in build_50t.tcl
- Add usb_data_interface_ft2232h.v to build_200t.tcl explicit file list

Python host:
- Add FT2232HConnection class using pyftdi SyncFIFO (VID 0x0403:0x6010)
- Add compact 11-byte packet parser for FT2232H data packets
- Update RadarAcquisition to support both FT601 and FT2232H connections

Test results:
- iverilog regression: 23/23 PASS
- Vivado Build 15 (XC7A50T): WNS=+0.088ns, WHS=+0.059ns, 0 violations
- DSP48E1: 112/120 (93.3%), LUTs: 10,060/32,600 (30.9%)
2026-04-07 19:22:16 +03:00
Jason 849b32240b fix(xdc): add hold false_path for ADC IDDR + reorganize build scripts by target
- 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 (.. -> ../..)
2026-04-07 15:13:13 +03:00
Jason 44460e7443 fix(constraints): change adc_pwdn from LVCMOS33 to LVCMOS25 for Bank 14 compatibility
The placer enforces a single VCCO per bank. LVDS_25 forces Bank 14
to VCCO=2.5V, which conflicts with LVCMOS33 (needs 3.3V). Changing
adc_pwdn to LVCMOS25 resolves [Place 30-372] bank incompatibility.
The AD9484 PWDN pin has CMOS-level thresholds (~0.8V), so 2.5V
output drives it correctly.
2026-04-07 06:07:47 +03:00
Jason 7d90e5e7d6 fix(constraints,scripts): resolve 50T build failures — LVDS_25 + DRC waivers + unconstrained ports
Three issues prevented the 50T (FTG256) build from completing:

1. LVDS standard: LVDS_33 and LVDS do not exist on 7-series HR banks.
   Changed to LVDS_25 (the only valid differential input standard).
   IBUFDS inputs are VCCO-independent, so LVDS_25 works correctly even
   with Bank 14 VCCO=3.3V.

2. BIVC-1 DRC: Bank 14 has LVDS_25 (needs 2.5V) and LVCMOS33 adc_pwdn
   (needs 3.3V). Since all LVDS ports are inputs (IBUFDS only), the
   voltage conflict does not affect functionality. Demoted to warning.

3. Pin overflow: 113 ports vs 69 available FTG256 pins. The 118
   unconstrained port bits (FT601 unwired, status/debug unrouted,
   dac_clk unconnected) cause NSTD-1/UCIO-1 DRC errors. Demoted to
   warnings since these ports have no physical connections on this board.

Also added: CFGBVS/CONFIG_VOLTAGE settings, build_50t_test.tcl to repo.
2026-04-07 05:48:35 +03:00
Jason 30f56f3089 fix(constraints): use LVDS (not LVDS_33) for 50T Bank 14 ADC inputs
LVDS_33 is not a valid I/O standard on 7-series FPGAs. The correct
standard for LVDS inputs in HR banks with VCCO != 2.5V is LVDS, which
works with any VCCO for input-only buffers (IBUFDS). LVDS_25 requires
VCCO=2.5V exactly.

Note: the 50T FTG256 build still fails at placement due to pin overflow
(113 ports vs 69 available pins) — this is a pre-existing package
limitation unrelated to this fix.
2026-04-07 05:31:48 +03:00
Jason d50e51ada6 fix(rtl,constraints): change IBUFDS to IOSTANDARD DEFAULT for multi-target bank voltage compatibility
The IBUFDS primitives in ad9484_interface_400m.v hardcoded LVDS_25 and
DIFF_TERM TRUE, which overrode XDC constraints. On the XC7A50T (Bank 14
VCCO=3.3V), this caused a BIVC-1 DRC error: LVDS_25 requires VCCO=2.5V,
conflicting with adc_pwdn (LVCMOS33, VCCO=3.3V) in the same bank.

Changes:
- ad9484_interface_400m.v: IBUFDS parameters changed from LVDS_25/DIFF_TERM
  TRUE to DEFAULT/DIFF_TERM FALSE, delegating control to XDC per target
- xc7a50t_ftg256.xdc: Re-enable DIFF_TERM TRUE (safe now that RTL does not
  hardcode LVDS_25), update DRC Fix History with correct root cause
2026-04-07 05:17:11 +03:00
Jason 1f315a62c8 fix(scripts,constraints): handle empty STATS properties in build summaries, fix 50T XDC DRC errors
Build scripts (17-21): STATS.WNS/TNS/WHS/THS/TPWS from get_property can
return empty strings in Vivado 2025.2 after write_bitstream auto-launch.
Wrap in catch with N/A fallback. Guard all expr delta calculations and
signoff comparisons with [string is double -strict] checks.

XDC (xc7a50t_ftg256): Fix PLIO-9 by moving clk_120m_dac from C13 (N-type)
to D13 (P-type MRCC) — clock inputs require P-type MRCC pin. Fix BIVC-1 by
disabling DIFF_TERM on Bank 14 LVDS pairs to resolve VCCO conflict with
single-ended adc_pwdn (LVCMOS33) on T5 — requires external termination.
2026-04-07 05:07:14 +03:00
Jason 5499827ab7 add TE0713+UMFT601X-B FT601 integration dev bitstream (timing clean)
FMC LPC dev build for TE0713/TE0701 + UMFT601X-B stack. Fixed timing
closure: replaced set_output_delay with set_max_delay -datapath_only
to eliminate false IBUF+BUFG clock skew penalty on source-synchronous
outputs. Removed erroneous set_input_delay on output-only ft601_be[*].
Added IOB packing for siwu_n, false paths for async GPIO/reset/wakeup.
Strategy: Performance_ExplorePostRoutePhysOpt.

Results: WNS +0.059 ns, WHS +0.121 ns, DRC 0 errors, 0 failing endpoints.
Bitstream: docs/artifacts/te0713-te0701-umft601x-dev-2026-03-21.bit
2026-03-21 20:43:52 +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 c6103b37de Gap 7 MMCM jitter cleaner + CIC comb CREG pipeline + XDC clock-name fix
MMCM (Gap 7):
- Add adc_clk_mmcm.v: MMCME2_ADV wrapper (VCO=800MHz, CLKOUT0=400MHz)
- Modify ad9484_interface_400m.v: replace BUFG with MMCM path, gate reset on mmcm_locked
- Add adc_clk_mmcm.xdc: CDC false paths for clk_mmcm_out0 <-> clk_100m

XDC Fix (Build 19 WNS=-0.011 root cause):
- Remove conflicting create_generated_clock -name clk_400m_mmcm
- Replace all clk_400m_mmcm references with Vivado auto-generated clk_mmcm_out0
- CDC false paths now correctly apply to actual timing paths

CIC CREG Pipeline (Build 18 critical path fix):
- Explicit DSP48E1 for comb[0] with CREG=1/AREG=1/BREG=1/PREG=1
- Absorbs integrator_sampled_comb fabric FDRE into DSP48 C-port register
- Eliminates 0.643ns fabric->DSP routing delay (Build 18 tightest path)
- +1 cycle comb latency via data_valid_comb_0_out pipeline
- Move shared register declarations above ifndef SIMULATION (iverilog fix)
- Update golden data for +1 cycle CIC pipeline shift

Build scripts: build19_mmcm.tcl, build20_mmcm_creg.tcl
Regression: 18/18 FPGA pass, 20/20 MCU pass
Build 20 launched on remote Vivado (pending results)
2026-03-19 22:59:46 +02:00
Jason 683e70e784 Update heartbeat dev target: LVCMOS33 for Bank 16 FT601 compat, add comments
- Changed user_led/system_status IOSTANDARD from LVCMOS25 to LVCMOS33
  to match VIOTB=3.3V needed for FT601 on Bank 16
- Added register init value for hb_counter
- Added comments documenting clock source (50 MHz FIFO0CLK at U20, Bank 14)
  and expected LED toggle rates
2026-03-19 16:47:59 +02:00
Jason 9b786eb33f Add FMC-path FT601 XDC for TE0713+TE0701+UMFT601X-B pin mapping
Maps all 47 FT601 signals through FMC LPC J10 to correct FPGA pins:
- DATA[31:0] + D_CLK: Bank 15 (LA17-LA33)
- BE_N[3:0], control, status: Bank 16 (LA00-LA15)
Both banks share VIOTB rail — set to 3.3V for LVCMOS33.
Includes timing constraints and RTL adaptation notes.
2026-03-19 16:20:56 +02:00
Jason e62f3cd950 Port validated Build 16 XDC cleanup and sync docs 2026-03-19 14:34:26 +02:00
Jason 967ce179eb Add TE0713/TE0701 alternate dev target for in-stock SoM path 2026-03-18 15:02:09 +02:00
Jason 0ae7b40ff0 Add TE0712/TE0701 split target with dedicated top, XDC, and build flow 2026-03-18 03:57:26 +02:00
Jason f6877aab64 Phase 1 hardware bring-up prep: ILA debug probes, CDC waivers, programming scripts
- Rename latency_buffer_2159 -> latency_buffer (module + file + all refs)
- Add CDC waivers for 5 verified false-positive criticals to XDC
- Add ILA debug probe insertion script (4 cores, 126 probe bits, 2 clock domains)
- Add FPGA programming script (7-step flow with DONE pin verification)
- Add ILA capture script (4 scenarios + health check, CSV export)
- Add debug_ila.xdc with MARK_DEBUG fallback attributes
- Full regression clean: 13/13 suites, 266/266 checks, 2048/2048 golden match
2026-03-18 01:28:42 +02:00
Jason fcf3999e39 Fix CDC reset domain bug (P0), strengthen testbenches with 31 structural assertions
Split cdc_adc_to_processing reset_n into src_reset_n/dst_reset_n so source
and destination clock domains use correctly-synchronized resets. Previously
cdc_chirp_counter's destination-side sync chain (100MHz) was reset by
sys_reset_120m_n (120MHz domain), causing 30 CDC critical warnings.

RTL changes:
- cdc_modules.v: split reset port, source logic uses src_reset_n,
  destination sync chains + output logic use dst_reset_n
- radar_system_top.v: cdc_chirp_counter gets proper per-domain resets
- ddc_400m.v: CDC_FIR_i/q use reset_n_400m (src) and reset_n (dst)
- formal/fv_cdc_adc.v: updated wrapper for new port interface

Build 7 fixes (previously untouched):
- radar_transmitter.v: SPI level-shifter assigns, STM32 GPIO CDC sync
- latency_buffer_2159.v: BRAM read registration
- constraints: ft601 IOB -quiet fix
- tb_latency_buffer.v: updated for BRAM changes

Testbench hardening (tb_cdc_modules.v, +31 new assertions):
- A5-A7: split-domain reset tests (staggered deassertion, independent
  dst reset while src active — catches the P0 bug class)
- A8: port connectivity (no X/Z on outputs)
- B7: cdc_single_bit port connectivity
- C6: cdc_handshake reset recovery + port connectivity

Full regression: 13/13 test suites pass (257 total assertions).
2026-03-17 19:38:09 +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 af1af3bb91 Fix XDC for timing closure: add hold waivers, remove stale constraints
Build 3 on XC7A200T-2FBG484I achieves full timing closure:
- WNS +0.040ns (setup), WHS +0.036ns (hold), 0 failing endpoints
- Add 3 hold false-path waivers for ODDR/BUFIO I/O boundaries
  (DAC clk_120m→dac_clk_fwd, FT601 ft601_clk_in→ft601_clk_fwd,
  ADC adc_d_p→adc_dco_p) — all artifacts of STA modeling
- Comment out ft601_be[2:3] pins (RTL only drives [1:0])
- Remove CIC multicycle paths (DSP48E1 cells not matchable)
- Add -quiet to IOB properties for tristate/optimized registers
2026-03-16 23:04:16 +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