fix: align all range/carrier/velocity values to PLFM hardware + FPGA bug fixes

- Correct carrier from 10.525/10 GHz to 10.5 GHz (verified ADF4382 config)
- Correct range-per-bin from 4.8/5.6/781.25 m to 24.0 m (matched-filter)
- Correct velocity resolution from 1.484 to 2.67 m/s/bin (PRI-based)
- Correct processing rate from 4 MSPS to 100 MSPS (post-DDC)
- Correct max range from 307/5000/50000 m to 1536 m (64 bins x 24 m)
- Add WaveformConfig.pri_s field (167 us PRI for velocity calculation)
- Fix short chirp chirp_complete deadlock (Bug A)
- Remove dead short_chirp ports, rename long_chirp to ref_chirp (Bug B)
- Fix stale latency comment 2159 -> 3187 cycles (Bug C)
- Create radar_params.vh as single source of truth for FPGA parameters
- Lower RadarSettings.cpp map_size validation bound from 1000 to 100
- Add PLFM hardware constants to golden_reference.py
- Update all GUI versions, tests, and cross-layer contracts

All 244 tests passing (167 Python + 21 MCU + 29 cross-layer + 27 FPGA)
This commit is contained in:
Jason
2026-04-15 10:38:59 +05:45
parent f67440ee9a
commit 02925ac34e
26 changed files with 415 additions and 4826 deletions
@@ -2,8 +2,8 @@
"""
golden_reference.py — AERIS-10 FPGA bit-accurate golden reference model
Uses ADI CN0566 Phaser radar data (10.525 GHz X-band FMCW) to validate
the FPGA signal processing pipeline stage by stage:
Uses ADI CN0566 Phaser radar data (10.525 GHz, used as test stimulus only) to
validate the FPGA signal processing pipeline stage by stage:
ADC → DDC (NCO+mixer+CIC+FIR) → Range FFT → Doppler FFT → Detection
@@ -90,7 +90,8 @@ HAMMING_Q15 = [
0x3088, 0x1B6D, 0x0E5C, 0x0A3D,
]
# ADI dataset parameters
# ADI dataset parameters — used ONLY for loading/requantizing ADI Phaser test data.
# These are NOT PLFM hardware parameters. See AERIS-10 constants below.
ADI_SAMPLE_RATE = 4e6 # 4 MSPS
ADI_IF_FREQ = 100e3 # 100 kHz IF
ADI_RF_FREQ = 9.9e9 # 9.9 GHz
@@ -99,9 +100,17 @@ ADI_RAMP_TIME = 300e-6 # 300 us
ADI_NUM_CHIRPS = 256
ADI_SAMPLES_PER_CHIRP = 1079
# AERIS-10 parameters
AERIS_FS = 400e6 # 400 MHz ADC clock
AERIS_IF = 120e6 # 120 MHz IF
# AERIS-10 hardware parameters (from ADF4382/AD9523/main.cpp configuration)
AERIS_FS = 400e6 # 400 MHz ADC clock (AD9523 OUT4)
AERIS_IF = 120e6 # 120 MHz IF (TX 10.5 GHz - RX 10.38 GHz)
AERIS_FS_PROCESSING = 100e6 # Post-DDC rate (400 MSPS / 4x CIC)
AERIS_CARRIER_HZ = 10.5e9 # TX LO (ADF4382, verified)
AERIS_RX_LO_HZ = 10.38e9 # RX LO (ADF4382)
AERIS_CHIRP_BW = 20e6 # Chirp bandwidth (target: 30 MHz Phase 1)
AERIS_LONG_CHIRP_S = 30e-6 # Long chirp duration
AERIS_PRI_S = 167e-6 # Pulse repetition interval
AERIS_DECIMATION = 16 # Range bin decimation (1024 → 64)
AERIS_RANGE_PER_BIN = 24.0 # Meters per decimated bin
# ===========================================================================
File diff suppressed because it is too large Load Diff
@@ -421,13 +421,13 @@ def test_latency_buffer():
#
# For synthesis: the latency_buffer feeds ref data to the chain via
# chirp_memory_loader_param → latency_buffer → chain.
# But wait — looking at radar_receiver_final.v:
# Looking at radar_receiver_final.v:
# - mem_request drives valid_in on the latency buffer
# - The buffer delays {ref_i, ref_q} by LATENCY valid_in cycles
# - The delayed output feeds long_chirp_real/imag → chain
# - The delayed output feeds ref_chirp_real/imag → chain
#
# The purpose: the chain in the SYNTHESIS branch reads reference data
# via the long_chirp_real/imag ports DURING ST_FWD_FFT (while collecting
# via the ref_chirp_real/imag ports DURING ST_FWD_FFT (while collecting
# input samples). The reference data needs to arrive LATENCY cycles
# after the first mem_request, where LATENCY accounts for:
# - The fft_engine pipeline latency from input to output
File diff suppressed because it is too large Load Diff
@@ -18,10 +18,8 @@ module tb_matched_filter_processing_chain;
reg [15:0] adc_data_q;
reg adc_valid;
reg [5:0] chirp_counter;
reg [15:0] long_chirp_real;
reg [15:0] long_chirp_imag;
reg [15:0] short_chirp_real;
reg [15:0] short_chirp_imag;
reg [15:0] ref_chirp_real;
reg [15:0] ref_chirp_imag;
wire signed [15:0] range_profile_i;
wire signed [15:0] range_profile_q;
wire range_profile_valid;
@@ -83,10 +81,8 @@ module tb_matched_filter_processing_chain;
.adc_data_q (adc_data_q),
.adc_valid (adc_valid),
.chirp_counter (chirp_counter),
.long_chirp_real (long_chirp_real),
.long_chirp_imag (long_chirp_imag),
.short_chirp_real (short_chirp_real),
.short_chirp_imag (short_chirp_imag),
.ref_chirp_real (ref_chirp_real),
.ref_chirp_imag (ref_chirp_imag),
.range_profile_i (range_profile_i),
.range_profile_q (range_profile_q),
.range_profile_valid (range_profile_valid),
@@ -133,10 +129,8 @@ module tb_matched_filter_processing_chain;
adc_data_i = 16'd0;
adc_data_q = 16'd0;
chirp_counter = 6'd0;
long_chirp_real = 16'd0;
long_chirp_imag = 16'd0;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'd0;
ref_chirp_imag = 16'd0;
cap_enable = 0;
cap_count = 0;
cap_max_abs = 0;
@@ -168,10 +162,8 @@ module tb_matched_filter_processing_chain;
angle = 6.28318530718 * tone_bin * k / (1.0 * FFT_SIZE);
adc_data_i = $rtoi(8000.0 * $cos(angle));
adc_data_q = $rtoi(8000.0 * $sin(angle));
long_chirp_real = $rtoi(8000.0 * $cos(angle));
long_chirp_imag = $rtoi(8000.0 * $sin(angle));
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = $rtoi(8000.0 * $cos(angle));
ref_chirp_imag = $rtoi(8000.0 * $sin(angle));
adc_valid = 1'b1;
@(posedge clk);
#1;
@@ -187,10 +179,8 @@ module tb_matched_filter_processing_chain;
for (k = 0; k < FFT_SIZE; k = k + 1) begin
adc_data_i = 16'sh1000;
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh1000;
long_chirp_imag = 16'sh0000;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh1000;
ref_chirp_imag = 16'sh0000;
adc_valid = 1'b1;
@(posedge clk);
#1;
@@ -233,10 +223,8 @@ module tb_matched_filter_processing_chain;
for (k = 0; k < FFT_SIZE; k = k + 1) begin
adc_data_i = gold_sig_i[k];
adc_data_q = gold_sig_q[k];
long_chirp_real = gold_ref_i[k];
long_chirp_imag = gold_ref_q[k];
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = gold_ref_i[k];
ref_chirp_imag = gold_ref_q[k];
adc_valid = 1'b1;
@(posedge clk);
#1;
@@ -374,10 +362,8 @@ module tb_matched_filter_processing_chain;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = 16'd0;
adc_data_q = 16'd0;
long_chirp_real = 16'd0;
long_chirp_imag = 16'd0;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'd0;
ref_chirp_imag = 16'd0;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -449,10 +435,8 @@ module tb_matched_filter_processing_chain;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = $rtoi(8000.0 * $cos(6.28318530718 * 5 * i / 1024.0));
adc_data_q = $rtoi(8000.0 * $sin(6.28318530718 * 5 * i / 1024.0));
long_chirp_real = $rtoi(8000.0 * $cos(6.28318530718 * 10 * i / 1024.0));
long_chirp_imag = $rtoi(8000.0 * $sin(6.28318530718 * 10 * i / 1024.0));
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = $rtoi(8000.0 * $cos(6.28318530718 * 10 * i / 1024.0));
ref_chirp_imag = $rtoi(8000.0 * $sin(6.28318530718 * 10 * i / 1024.0));
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -568,10 +552,8 @@ module tb_matched_filter_processing_chain;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = 16'sh7FFF;
adc_data_q = 16'sh7FFF;
long_chirp_real = 16'sh7FFF;
long_chirp_imag = 16'sh7FFF;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh7FFF;
ref_chirp_imag = 16'sh7FFF;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -589,10 +571,8 @@ module tb_matched_filter_processing_chain;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = 16'sh8000;
adc_data_q = 16'sh8000;
long_chirp_real = 16'sh8000;
long_chirp_imag = 16'sh8000;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh8000;
ref_chirp_imag = 16'sh8000;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -611,16 +591,14 @@ module tb_matched_filter_processing_chain;
if (i % 2 == 0) begin
adc_data_i = 16'sh7FFF;
adc_data_q = 16'sh7FFF;
long_chirp_real = 16'sh7FFF;
long_chirp_imag = 16'sh7FFF;
ref_chirp_real = 16'sh7FFF;
ref_chirp_imag = 16'sh7FFF;
end else begin
adc_data_i = 16'sh8000;
adc_data_q = 16'sh8000;
long_chirp_real = 16'sh8000;
long_chirp_imag = 16'sh8000;
ref_chirp_real = 16'sh8000;
ref_chirp_imag = 16'sh8000;
end
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -641,10 +619,8 @@ module tb_matched_filter_processing_chain;
for (i = 0; i < 512; i = i + 1) begin
adc_data_i = 16'sh1000;
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh1000;
long_chirp_imag = 16'sh0000;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh1000;
ref_chirp_imag = 16'sh0000;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -683,10 +659,8 @@ module tb_matched_filter_processing_chain;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = 16'sh1000;
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh1000;
long_chirp_imag = 16'sh0000;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh1000;
ref_chirp_imag = 16'sh0000;
adc_valid = 1'b1;
@(posedge clk); #1;
+24 -46
View File
@@ -28,10 +28,8 @@ module tb_mf_chain_synth;
reg [15:0] adc_data_q;
reg adc_valid;
reg [5:0] chirp_counter;
reg [15:0] long_chirp_real;
reg [15:0] long_chirp_imag;
reg [15:0] short_chirp_real;
reg [15:0] short_chirp_imag;
reg [15:0] ref_chirp_real;
reg [15:0] ref_chirp_imag;
wire signed [15:0] range_profile_i;
wire signed [15:0] range_profile_q;
wire range_profile_valid;
@@ -78,10 +76,8 @@ module tb_mf_chain_synth;
.adc_data_q (adc_data_q),
.adc_valid (adc_valid),
.chirp_counter (chirp_counter),
.long_chirp_real (long_chirp_real),
.long_chirp_imag (long_chirp_imag),
.short_chirp_real (short_chirp_real),
.short_chirp_imag (short_chirp_imag),
.ref_chirp_real (ref_chirp_real),
.ref_chirp_imag (ref_chirp_imag),
.range_profile_i (range_profile_i),
.range_profile_q (range_profile_q),
.range_profile_valid (range_profile_valid),
@@ -130,10 +126,8 @@ module tb_mf_chain_synth;
adc_data_i = 16'd0;
adc_data_q = 16'd0;
chirp_counter = 6'd0;
long_chirp_real = 16'd0;
long_chirp_imag = 16'd0;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'd0;
ref_chirp_imag = 16'd0;
cap_enable = 0;
cap_count = 0;
cap_max_abs = 0;
@@ -177,10 +171,8 @@ module tb_mf_chain_synth;
for (k = 0; k < FFT_SIZE; k = k + 1) begin
adc_data_i = 16'sh1000; // +4096
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh1000;
long_chirp_imag = 16'sh0000;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh1000;
ref_chirp_imag = 16'sh0000;
adc_valid = 1'b1;
@(posedge clk);
#1;
@@ -199,10 +191,8 @@ module tb_mf_chain_synth;
angle = 6.28318530718 * tone_bin * k / (1.0 * FFT_SIZE);
adc_data_i = $rtoi(8000.0 * $cos(angle));
adc_data_q = $rtoi(8000.0 * $sin(angle));
long_chirp_real = $rtoi(8000.0 * $cos(angle));
long_chirp_imag = $rtoi(8000.0 * $sin(angle));
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = $rtoi(8000.0 * $cos(angle));
ref_chirp_imag = $rtoi(8000.0 * $sin(angle));
adc_valid = 1'b1;
@(posedge clk);
#1;
@@ -219,16 +209,14 @@ module tb_mf_chain_synth;
if (k == 0) begin
adc_data_i = 16'sh4000; // 0.5 in Q15
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh4000;
long_chirp_imag = 16'sh0000;
ref_chirp_real = 16'sh4000;
ref_chirp_imag = 16'sh0000;
end else begin
adc_data_i = 16'sh0000;
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh0000;
long_chirp_imag = 16'sh0000;
ref_chirp_real = 16'sh0000;
ref_chirp_imag = 16'sh0000;
end
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
adc_valid = 1'b1;
@(posedge clk);
#1;
@@ -309,10 +297,8 @@ module tb_mf_chain_synth;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = 16'd0;
adc_data_q = 16'd0;
long_chirp_real = 16'd0;
long_chirp_imag = 16'd0;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'd0;
ref_chirp_imag = 16'd0;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -379,10 +365,8 @@ module tb_mf_chain_synth;
for (i = 0; i < 512; i = i + 1) begin
adc_data_i = 16'sh1000;
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh1000;
long_chirp_imag = 16'sh0000;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh1000;
ref_chirp_imag = 16'sh0000;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -439,10 +423,8 @@ module tb_mf_chain_synth;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = $rtoi(8000.0 * $cos(6.28318530718 * 5 * i / 1024.0));
adc_data_q = $rtoi(8000.0 * $sin(6.28318530718 * 5 * i / 1024.0));
long_chirp_real = $rtoi(8000.0 * $cos(6.28318530718 * 10 * i / 1024.0));
long_chirp_imag = $rtoi(8000.0 * $sin(6.28318530718 * 10 * i / 1024.0));
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = $rtoi(8000.0 * $cos(6.28318530718 * 10 * i / 1024.0));
ref_chirp_imag = $rtoi(8000.0 * $sin(6.28318530718 * 10 * i / 1024.0));
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -469,10 +451,8 @@ module tb_mf_chain_synth;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = 16'sh7FFF;
adc_data_q = 16'sh7FFF;
long_chirp_real = 16'sh7FFF;
long_chirp_imag = 16'sh7FFF;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh7FFF;
ref_chirp_imag = 16'sh7FFF;
adc_valid = 1'b1;
@(posedge clk); #1;
end
@@ -495,10 +475,8 @@ module tb_mf_chain_synth;
for (i = 0; i < FFT_SIZE; i = i + 1) begin
adc_data_i = 16'sh1000;
adc_data_q = 16'sh0000;
long_chirp_real = 16'sh1000;
long_chirp_imag = 16'sh0000;
short_chirp_real = 16'd0;
short_chirp_imag = 16'd0;
ref_chirp_real = 16'sh1000;
ref_chirp_imag = 16'sh0000;
adc_valid = 1'b1;
@(posedge clk); #1;
+10 -18
View File
@@ -88,10 +88,8 @@ reg [15:0] adc_data_i;
reg [15:0] adc_data_q;
reg adc_valid;
reg [5:0] chirp_counter;
reg [15:0] long_chirp_real;
reg [15:0] long_chirp_imag;
reg [15:0] short_chirp_real;
reg [15:0] short_chirp_imag;
reg [15:0] ref_chirp_real;
reg [15:0] ref_chirp_imag;
wire signed [15:0] range_profile_i;
wire signed [15:0] range_profile_q;
@@ -108,10 +106,8 @@ matched_filter_processing_chain dut (
.adc_data_q(adc_data_q),
.adc_valid(adc_valid),
.chirp_counter(chirp_counter),
.long_chirp_real(long_chirp_real),
.long_chirp_imag(long_chirp_imag),
.short_chirp_real(short_chirp_real),
.short_chirp_imag(short_chirp_imag),
.ref_chirp_real(ref_chirp_real),
.ref_chirp_imag(ref_chirp_imag),
.range_profile_i(range_profile_i),
.range_profile_q(range_profile_q),
.range_profile_valid(range_profile_valid),
@@ -157,10 +153,8 @@ task apply_reset;
adc_data_q <= 16'd0;
adc_valid <= 1'b0;
chirp_counter <= 6'd0;
long_chirp_real <= 16'd0;
long_chirp_imag <= 16'd0;
short_chirp_real <= 16'd0;
short_chirp_imag <= 16'd0;
ref_chirp_real <= 16'd0;
ref_chirp_imag <= 16'd0;
repeat(4) @(posedge clk);
reset_n <= 1'b1;
@(posedge clk);
@@ -201,18 +195,16 @@ initial begin
@(posedge clk);
adc_data_i <= sig_mem_i[i];
adc_data_q <= sig_mem_q[i];
long_chirp_real <= ref_mem_i[i];
long_chirp_imag <= ref_mem_q[i];
short_chirp_real <= 16'd0;
short_chirp_imag <= 16'd0;
ref_chirp_real <= ref_mem_i[i];
ref_chirp_imag <= ref_mem_q[i];
adc_valid <= 1'b1;
end
@(posedge clk);
adc_valid <= 1'b0;
adc_data_i <= 16'd0;
adc_data_q <= 16'd0;
long_chirp_real <= 16'd0;
long_chirp_imag <= 16'd0;
ref_chirp_real <= 16'd0;
ref_chirp_imag <= 16'd0;
$display("All samples fed. Waiting for processing...");
+10 -16
View File
@@ -56,10 +56,8 @@ reg [5:0] chirp_counter;
reg mc_new_chirp;
reg mc_new_elevation;
reg mc_new_azimuth;
reg [15:0] long_chirp_real;
reg [15:0] long_chirp_imag;
reg [15:0] short_chirp_real;
reg [15:0] short_chirp_imag;
reg [15:0] ref_chirp_real;
reg [15:0] ref_chirp_imag;
reg mem_ready;
wire signed [15:0] pc_i_w;
@@ -84,10 +82,8 @@ matched_filter_multi_segment dut (
.mc_new_chirp(mc_new_chirp),
.mc_new_elevation(mc_new_elevation),
.mc_new_azimuth(mc_new_azimuth),
.long_chirp_real(long_chirp_real),
.long_chirp_imag(long_chirp_imag),
.short_chirp_real(short_chirp_real),
.short_chirp_imag(short_chirp_imag),
.ref_chirp_real(ref_chirp_real),
.ref_chirp_imag(ref_chirp_imag),
.segment_request(segment_request),
.sample_addr_out(sample_addr_out),
.mem_request(mem_request),
@@ -123,11 +119,11 @@ end
always @(posedge clk) begin
if (mem_request) begin
if (use_long_chirp) begin
long_chirp_real <= ref_mem_i[{segment_request, sample_addr_out}];
long_chirp_imag <= ref_mem_q[{segment_request, sample_addr_out}];
ref_chirp_real <= ref_mem_i[{segment_request, sample_addr_out}];
ref_chirp_imag <= ref_mem_q[{segment_request, sample_addr_out}];
end else begin
short_chirp_real <= ref_mem_i[sample_addr_out];
short_chirp_imag <= ref_mem_q[sample_addr_out];
ref_chirp_real <= ref_mem_i[sample_addr_out];
ref_chirp_imag <= ref_mem_q[sample_addr_out];
end
mem_ready <= 1'b1;
end else begin
@@ -176,10 +172,8 @@ task apply_reset;
mc_new_chirp <= 1'b0;
mc_new_elevation <= 1'b0;
mc_new_azimuth <= 1'b0;
long_chirp_real <= 16'd0;
long_chirp_imag <= 16'd0;
short_chirp_real <= 16'd0;
short_chirp_imag <= 16'd0;
ref_chirp_real <= 16'd0;
ref_chirp_imag <= 16'd0;
mem_ready <= 1'b0;
repeat(10) @(posedge clk);
reset_n <= 1'b1;