Fix doppler_processor windowing pipeline bugs + multi-segment buffer_write_ptr bug, add co-sim suites
RTL bug fixes: - doppler_processor.v: Add S_PRE_READ state to prime BRAM pipeline, restructure S_LOAD_FFT with sub-counter staging, fix BRAM address off-by-one (read_doppler_index <= fft_sample_counter + 2, was +1). All 3 Doppler co-sim scenarios now achieve BIT-PERFECT match (correlation=1.0, energy=1.0). - matched_filter_multi_segment.v: Move buffer_write_ptr >= SEGMENT_ADVANCE check outside if(ddc_valid) block to prevent FSM deadlock. 32/32 tests PASS. New co-simulation infrastructure: - Doppler co-sim: tb_doppler_cosim.v (14/14 structural checks), gen_doppler_golden.py (3 scenarios: stationary/moving/two_targets), compare_doppler.py (bit-perfect thresholds) - Multi-segment co-sim: tb_multiseg_cosim.v (32/32), gen_multiseg_golden.py with short and long test vector suites
This commit is contained in:
@@ -106,14 +106,15 @@ assign mem_read_addr = (read_doppler_index * RANGE_BINS) + read_range_bin;
|
||||
// assign mem_write_addr = (write_range_bin * CHIRPS_PER_FRAME) + write_chirp_index;
|
||||
// assign mem_read_addr = (read_range_bin * CHIRPS_PER_FRAME) + read_doppler_index;
|
||||
|
||||
// ==============================================
|
||||
// State Machine
|
||||
// ==============================================
|
||||
reg [2:0] state;
|
||||
localparam S_IDLE = 3'b000;
|
||||
localparam S_ACCUMULATE = 3'b001;
|
||||
localparam S_LOAD_FFT = 3'b010;
|
||||
localparam S_FFT_WAIT = 3'b011;
|
||||
// ==============================================
|
||||
// State Machine
|
||||
// ==============================================
|
||||
reg [2:0] state;
|
||||
localparam S_IDLE = 3'b000;
|
||||
localparam S_ACCUMULATE = 3'b001;
|
||||
localparam S_PRE_READ = 3'b101; // Prime BRAM pipeline before FFT load
|
||||
localparam S_LOAD_FFT = 3'b010;
|
||||
localparam S_FFT_WAIT = 3'b011;
|
||||
localparam S_OUTPUT = 3'b100;
|
||||
|
||||
// Frame sync detection
|
||||
@@ -230,43 +231,97 @@ always @(posedge clk or negedge reset_n) begin
|
||||
if (write_chirp_index >= CHIRPS_PER_FRAME - 1) begin
|
||||
frame_buffer_full <= 1;
|
||||
chirp_state <= 0;
|
||||
state <= S_LOAD_FFT;
|
||||
state <= S_PRE_READ;
|
||||
read_range_bin <= 0;
|
||||
read_doppler_index <= 0;
|
||||
fft_sample_counter <= 0;
|
||||
fft_start <= 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
S_PRE_READ: begin
|
||||
// Prime the BRAM pipeline: present addr for chirp 0 of
|
||||
// current read_range_bin. read_doppler_index is already 0.
|
||||
// mem_read_addr = 0 * RANGE_BINS + read_range_bin.
|
||||
// After this cycle, mem_rdata_i will hold data[chirp=0][rbin].
|
||||
// Advance read_doppler_index to 1 so the NEXT BRAM read
|
||||
// (which happens every cycle in the memory block) will
|
||||
// fetch chirp 1.
|
||||
read_doppler_index <= 1;
|
||||
fft_start <= 1;
|
||||
state <= S_LOAD_FFT;
|
||||
end
|
||||
|
||||
S_LOAD_FFT: begin
|
||||
fft_start <= 0;
|
||||
|
||||
if (fft_sample_counter < DOPPLER_FFT_SIZE) begin
|
||||
// Use registered read data (one cycle latency from BRAM)
|
||||
// Pipeline alignment (after S_PRE_READ primed the BRAM):
|
||||
//
|
||||
// At cycle k (fft_sample_counter = k, k = 0..31):
|
||||
// mem_rdata_i = data[chirp=k][rbin] (from addr presented
|
||||
// LAST cycle: read_doppler_index was k)
|
||||
// We compute: mult_i <= mem_rdata_i * window_coeff[k]
|
||||
// We capture: fft_input_i <= (prev_mult_i + round) >>> 15
|
||||
// We present: BRAM addr for chirp k+1 (for next cycle)
|
||||
//
|
||||
// For k=0: fft_input_i captures the stale mult_i (= 0 from
|
||||
// reset or previous rbin's flush). This is WRONG
|
||||
// for a naive implementation. Instead, we use a
|
||||
// sub-counter approach:
|
||||
//
|
||||
// sub=0 (pre-multiply): We have mem_rdata_i = data[0].
|
||||
// Compute mult_i = data[0] * window[0].
|
||||
// Do NOT assert fft_input_valid yet.
|
||||
// Present BRAM addr for chirp 1.
|
||||
//
|
||||
// sub=1..31 (normal): mem_rdata_i = data[sub].
|
||||
// fft_input_i = (prev mult) >>> 15 -> VALID
|
||||
// mult_i = data[sub] * window[sub]
|
||||
// Present BRAM addr for chirp sub+1.
|
||||
//
|
||||
// sub=32 (flush): No new BRAM data needed.
|
||||
// fft_input_i = (mult from sub=31) >>> 15 -> VALID, LAST
|
||||
// Transition to S_FFT_WAIT.
|
||||
//
|
||||
// We reuse fft_sample_counter as the sub-counter (0..32).
|
||||
|
||||
if (fft_sample_counter == 0) begin
|
||||
// Sub 0: pre-multiply. mem_rdata_i = data[chirp=0][rbin].
|
||||
mult_i <= $signed(mem_rdata_i) *
|
||||
$signed(window_coeff[read_doppler_index]);
|
||||
$signed(window_coeff[0]);
|
||||
mult_q <= $signed(mem_rdata_q) *
|
||||
$signed(window_coeff[read_doppler_index]);
|
||||
|
||||
// Round instead of truncate
|
||||
$signed(window_coeff[0]);
|
||||
// Present BRAM addr for chirp 2 (sub=1 reads chirp 1
|
||||
// from the BRAM read we triggered in S_PRE_READ;
|
||||
// we need chirp 2 ready for sub=2).
|
||||
read_doppler_index <= 2;
|
||||
fft_sample_counter <= 1;
|
||||
end else if (fft_sample_counter <= DOPPLER_FFT_SIZE) begin
|
||||
// Sub 1..32
|
||||
// Capture previous mult into fft_input
|
||||
fft_input_i <= (mult_i + (1 << 14)) >>> 15;
|
||||
fft_input_q <= (mult_q + (1 << 14)) >>> 15;
|
||||
|
||||
fft_input_valid <= 1;
|
||||
|
||||
if (fft_sample_counter == DOPPLER_FFT_SIZE - 1) begin
|
||||
|
||||
if (fft_sample_counter == DOPPLER_FFT_SIZE) begin
|
||||
// Sub 32: flush last sample
|
||||
fft_input_last <= 1;
|
||||
state <= S_FFT_WAIT;
|
||||
fft_sample_counter <= 0;
|
||||
processing_timeout <= 1000;
|
||||
end else begin
|
||||
// Sub 1..31: also compute new mult from current BRAM data
|
||||
// mem_rdata_i = data[chirp = fft_sample_counter][rbin]
|
||||
mult_i <= $signed(mem_rdata_i) *
|
||||
$signed(window_coeff[fft_sample_counter]);
|
||||
mult_q <= $signed(mem_rdata_q) *
|
||||
$signed(window_coeff[fft_sample_counter]);
|
||||
// Advance BRAM read to chirp fft_sample_counter+2
|
||||
// (so data is ready two cycles later when we need it)
|
||||
read_doppler_index <= fft_sample_counter + 2;
|
||||
fft_sample_counter <= fft_sample_counter + 1;
|
||||
end
|
||||
|
||||
// Increment chirp index for next sample
|
||||
read_doppler_index <= read_doppler_index + 1;
|
||||
fft_sample_counter <= fft_sample_counter + 1;
|
||||
end else begin
|
||||
state <= S_FFT_WAIT;
|
||||
fft_sample_counter <= 0;
|
||||
processing_timeout <= 100;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -294,8 +349,8 @@ always @(posedge clk or negedge reset_n) begin
|
||||
if (read_range_bin < RANGE_BINS - 1) begin
|
||||
read_range_bin <= read_range_bin + 1;
|
||||
read_doppler_index <= 0;
|
||||
state <= S_LOAD_FFT;
|
||||
fft_start <= 1;
|
||||
fft_sample_counter <= 0;
|
||||
state <= S_PRE_READ;
|
||||
end else begin
|
||||
state <= S_IDLE;
|
||||
frame_buffer_full <= 0;
|
||||
|
||||
Reference in New Issue
Block a user