Observe-before-fix instrumentation for bench bring-up: adds timestamped
DIAG logging to the AD9523 clock config, ADF4382A LO manager, power
sequencing, lock monitoring, temperature monitoring, and safe-mode entry.
Annotates known bugs (double ad9523_setup call, timed-sync init ordering,
TriggerTimedSync no-op, phase-shift before init-check, last_check timer
collision) without changing any runtime behavior.
Reduce routing pressure on CIC/NCO critical paths and move Doppler BRAM read-address registers to sync-reset datapath logic so Build 13 closes with stronger setup/hold slack while preserving functional behavior.
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).
DSP48E1 lookup table quantization causes dithering near zero crossings
at low frequencies (1 MHz), producing ~11 sign transitions vs ~5 expected.
Widen accepted range from [3,8] to [3,15].
- 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)
New testbenches:
- tb_latency_buffer.v: 13/13 tests for BRAM delay line (P1-3)
- tb_cdc_modules.v: 27/27 tests for all 3 CDC primitives (P1-4)
- tb_ad9484_xsim.v: XSim testbench for AD9484 with Xilinx BUFIO/IDDR
- tb_nco_xsim.v: XSim testbench for NCO DSP48E1 path verification
Fixes:
- tb_usb_data_interface.v: updated test 33 from divide-by-2 check to
ODDR-style clock forwarding verification (39/39 pass)
- rx_final_doppler_out.csv: updated golden reference after bug fixes
P0-1: nco_400m_enhanced.v — DSP48E1 OPMODE corrected from PCIN to P
feedback (was routing stale cascade data into accumulator)
P0-2: radar_receiver_final.v — removed same-clock CDC that corrupted
ADC data path between ad9484_interface and DDC
P1-5: fir_lowpass.v — fixed zero replication count in coefficient
symmetric extension ({0{1'b0}} is empty, now uses explicit 0)
Also updates .gitignore to exclude debug/scratch artifacts.
All 30+ testbenches pass (unit, co-sim, integration).
Previously segment 0 only filled positions [0:895], leaving [896:1023]
as zeros from the initial block. These zeros propagated into the overlap
region of subsequent segments, corrupting the convolution.
Fix: change buffer-full threshold from SEGMENT_ADVANCE (896) to
BUFFER_SIZE (1024). Add zero-padding for the last segment when chirp
data runs out before the buffer is full. Updated testbench accordingly.
Verified: multi-segment 32/32 PASS, integration test 10/10 PASS.
Previously the S_IDLE->S_ACCUMULATE transition consumed one data_valid
cycle without writing to BRAM, losing the first sample. The testbench
worked around this by sending sample[0] twice.
Fix: drive mem_we + data capture in S_IDLE on the transition cycle and
advance write_range_bin to 1. Testbench workaround removed.
Verified: 3/3 Doppler co-sim BIT-PERFECT, integration test 10/10 PASS.
RTL fix: matched_filter_multi_segment.v ST_WAIT_FFT now waits for
processing chain to complete ALL 1024 outputs and return to IDLE
before advancing to next segment. Previously, it transitioned on the
first fft_pc_valid edge, causing the chain to still be outputting
while multi-seg started collecting data for the next segment. This
broke the handshake and caused permanent deadlock after segment 0.
Also fixes forward reference of sample_addr_from_chain in
radar_receiver_final.v (declaration moved before first use).
New files:
- tb/tb_radar_receiver_final.v: P0 integration test for full RX
pipeline (ADC->DDC->MF->range_bin_decimator->doppler), 10 checks
- tb/ad9484_interface_400m_stub.v: behavioral ADC stub for iverilog
All existing tests still pass: multi-seg 32/32, MF co-sim 3/3,
Doppler co-sim 14/14.
Bug: rounding logic 'adc_i <= ddc_i[17:2] + ddc_i[1]' overflows when
ddc_i[17:2]=0x7FFF and ddc_i[1]=1, causing 0x7FFF+1=0x8000 (sign flip
from max positive to most negative value).
Fix: add explicit saturation — clamp to 0x7FFF when truncated value is
max positive and round bit is set. Negative values cannot overflow since
rounding only moves toward zero.
New testbench: tb_ddc_input_interface.v with 26 tests covering rounding,
truncation, overflow saturation at positive boundary, negative full scale,
valid synchronization, and sync error detection.
Bit-accurate Python model (fpga_model.py) mirrors full DDC RTL chain:
NCO -> mixer -> CIC -> FIR with exact fixed-point arithmetic matching
RTL DSP48E1 pipeline behavior including CREG=1 delay on CIC int_0.
Synthetic radar scene generator (radar_scene.py) produces ADC test
vectors for 5 scenarios: DC, single target (500m), multi-target (5),
noise-only, and 1 MHz sine wave.
DDC co-sim testbench (tb_ddc_cosim.v) feeds hex vectors through RTL
DDC and exports baseband I/Q to CSV. All 5 scenarios compile and run
with Icarus Verilog (iverilog -g2001 -DSIMULATION).
Comparison framework (compare.py) validates Python vs RTL using
statistical metrics (RMS ratio, DC offset, peak ratio) rather than
exact sample match due to RTL LFSR phase dithering. Results: 5/5 PASS.