From b7ac2de1a4e48011e56a6a978c6318e9024af54a Mon Sep 17 00:00:00 2001 From: Jason <83615043+JJassonn69@users.noreply.github.com> Date: Tue, 28 Apr 2026 12:52:13 +0545 Subject: [PATCH] chore: delete dead latency_buffer; doc cleanup for two stale comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit latency_buffer.v has had zero non-tb instantiations since RX-B (2026-04-23) replaced its hookup in radar_receiver_final with a 1-FF alignment register. The module was being kept "for potential future use" — exactly the kind of dead weight the codebase does not need. Deleted, along with all build / test infrastructure that dragged it along: - 9_Firmware/9_2_FPGA/latency_buffer.v - 9_Firmware/9_2_FPGA/tb/tb_latency_buffer.v - run_regression.sh: removed from RTL_FILES and RECEIVER_RTL - scripts/200t/build_200t.tcl: removed from synthesis source list - tb/tb_system_e2e.v: removed from header compile-string example - tb/cosim/validate_mem_files.py: deleted test_latency_buffer() (~75 lines), its call site, and the corresponding entry in the module docstring Historical RX-B comments referencing latency_buffer in radar_receiver_final.v, tb_rxb_fullchain_latency.v, and tb_rxb_latency_measure.v are kept — they explain WHY the module was removed, which is still useful design archaeology. Two doc-only housekeeping touches bundled in: - plfm_chirp_controller.v: replaced two empty "CRITICAL FIX: Generate valid signal" labels at LONG_CHIRP and SHORT_CHIRP with one shared chirp_valid policy comment block above LONG_CHIRP that explains the actual rationale (downstream FIFO underrun on trailing samples). - v7/models.py: replaced the "range_resolution and velocity_resolution should be calibrated" docstring (sounded like an open TODO but was a documented placeholder) with a clear pointer to the GUI-C3 fix in workers.py:RadarDataWorker so future readers know the live path derives correct values from WaveformConfig. FPGA quick regression unchanged: 28/29 (1 fail is the unrelated iverilog/ Xilinx-IP RX-NEW-3 gap). GUI suite 180/180. Ruff clean. --- 9_Firmware/9_2_FPGA/latency_buffer.v | 131 ------ 9_Firmware/9_2_FPGA/plfm_chirp_controller.v | 13 +- 9_Firmware/9_2_FPGA/run_regression.sh | 3 +- .../9_2_FPGA/scripts/200t/build_200t.tcl | 1 - .../9_2_FPGA/tb/cosim/validate_mem_files.py | 77 ---- 9_Firmware/9_2_FPGA/tb/tb_latency_buffer.v | 398 ------------------ 9_Firmware/9_2_FPGA/tb/tb_system_e2e.v | 2 +- 9_Firmware/9_3_GUI/v7/models.py | 7 +- 8 files changed, 16 insertions(+), 616 deletions(-) delete mode 100644 9_Firmware/9_2_FPGA/latency_buffer.v delete mode 100644 9_Firmware/9_2_FPGA/tb/tb_latency_buffer.v diff --git a/9_Firmware/9_2_FPGA/latency_buffer.v b/9_Firmware/9_2_FPGA/latency_buffer.v deleted file mode 100644 index 1fe9fed..0000000 --- a/9_Firmware/9_2_FPGA/latency_buffer.v +++ /dev/null @@ -1,131 +0,0 @@ -`timescale 1ns / 1ps - -// latency_buffer.v — Parameterized BRAM-based latency/delay buffer -// Renamed from latency_buffer_2159 to latency_buffer (module name was -// inconsistent with the actual LATENCY=3187 parameter). -module latency_buffer #( - parameter DATA_WIDTH = 32, - parameter LATENCY = 3187 -) ( - input wire clk, - input wire reset_n, - input wire [DATA_WIDTH-1:0] data_in, - input wire valid_in, - output wire [DATA_WIDTH-1:0] data_out, - output wire valid_out -); - -// ========== FIXED PARAMETERS ========== -localparam ADDR_WIDTH = 12; // Enough for 4096 entries (>2159) - -// ========== FIXED LOGIC ========== -(* ram_style = "block" *) reg [DATA_WIDTH-1:0] bram [0:4095]; -reg [ADDR_WIDTH-1:0] write_ptr; -reg [ADDR_WIDTH-1:0] read_ptr; -reg valid_out_reg; - -// Delay counter to track when LATENCY cycles have passed -reg [ADDR_WIDTH-1:0] delay_counter; -reg buffer_has_data; // Flag when buffer has accumulated LATENCY samples - -// ========== FIXED INITIALIZATION ========== -integer k; -initial begin - for (k = 0; k < 4096; k = k + 1) begin - bram[k] = {DATA_WIDTH{1'b0}}; - end - write_ptr = 0; - read_ptr = 0; - valid_out_reg = 0; - delay_counter = 0; - buffer_has_data = 0; -end - -// ========== BRAM WRITE (synchronous only, no async reset) ========== -// Xilinx Block RAMs do not support asynchronous resets. -// Separating the BRAM write into its own always block avoids Synth 8-3391. -// The initial block above handles power-on initialization for FPGA. -always @(posedge clk) begin - if (valid_in) begin - bram[write_ptr] <= data_in; - end -end - -// ========== CONTROL LOGIC (with async reset) ========== -always @(posedge clk or negedge reset_n) begin - if (!reset_n) begin - write_ptr <= 0; - read_ptr <= 0; - valid_out_reg <= 0; - delay_counter <= 0; - buffer_has_data <= 0; - end else begin - // Default: no valid output - valid_out_reg <= 0; - - // ===== WRITE SIDE ===== - if (valid_in) begin - // Increment write pointer (wrap at 4095) - if (write_ptr == 4095) begin - write_ptr <= 0; - end else begin - write_ptr <= write_ptr + 1; - end - - // Count how many samples we've written - if (delay_counter < LATENCY) begin - delay_counter <= delay_counter + 1; - - // When we've written LATENCY samples, buffer is "primed" - if (delay_counter == LATENCY - 1) begin - buffer_has_data <= 1'b1; - end - end - end - - // ===== READ SIDE ===== - // Only start reading after we have LATENCY samples in buffer - if (buffer_has_data && valid_in) begin - // Read pointer follows write pointer with LATENCY delay - // Calculate: read_ptr = (write_ptr - LATENCY) mod 4096 - - // Handle wrap-around correctly - if (write_ptr >= LATENCY) begin - read_ptr <= write_ptr - LATENCY; - end else begin - // Wrap around: 4096 + write_ptr - LATENCY - read_ptr <= 4096 + write_ptr - LATENCY; - end - - // Output is valid - valid_out_reg <= 1'b1; - end - end -end - -// ========== BRAM READ (synchronous — required for Block RAM inference) ========== -// Xilinx Block RAMs physically register the read output. An async read -// (assign data_out = bram[addr]) forces Vivado to use distributed LUTRAM -// instead, wasting ~704 LUTs. Registering the read adds 1 cycle of latency, -// compensated by the valid pipeline stage below. -reg [DATA_WIDTH-1:0] data_out_reg; - -always @(posedge clk) begin - data_out_reg <= bram[read_ptr]; -end - -// Pipeline valid_out_reg by 1 cycle to align with registered BRAM read -reg valid_out_pipe; -always @(posedge clk or negedge reset_n) begin - if (!reset_n) - valid_out_pipe <= 1'b0; - else - valid_out_pipe <= valid_out_reg; -end - -assign data_out = data_out_reg; -assign valid_out = valid_out_pipe; - - - -endmodule \ No newline at end of file diff --git a/9_Firmware/9_2_FPGA/plfm_chirp_controller.v b/9_Firmware/9_2_FPGA/plfm_chirp_controller.v index 4b70ebb..d0f9313 100644 --- a/9_Firmware/9_2_FPGA/plfm_chirp_controller.v +++ b/9_Firmware/9_2_FPGA/plfm_chirp_controller.v @@ -280,11 +280,16 @@ always @(posedge clk_120m or negedge reset_n) begin chirp_data <= 8'd128; end + // chirp_valid policy (LONG_CHIRP + SHORT_CHIRP states): assert + // chirp_valid HIGH for the entire active-sample window of each + // chirp (sample_counter < T?_SAMPLES) so the downstream DAC sees a + // continuous data-valid pulse, then ride out the remaining state + // duration on idle code 8'd128. Without the per-cycle assert, + // downstream FIFOs underrun on the trailing samples of each chirp. LONG_CHIRP: begin rf_switch_ctrl <= 1'b1; {adar_tr_1, adar_tr_2, adar_tr_3, adar_tr_4} <= 4'b1111; - - // CRITICAL FIX: Generate valid signal + if (sample_counter < T1_SAMPLES) begin chirp_data <= long_chirp_rd_data; chirp_valid <= 1'b1; // Valid during entire chirp @@ -306,8 +311,8 @@ always @(posedge clk_120m or negedge reset_n) begin SHORT_CHIRP: begin rf_switch_ctrl <= 1'b1; {adar_tr_1, adar_tr_2, adar_tr_3, adar_tr_4} <= 4'b1111; - - // CRITICAL FIX: Generate valid signal for short chirp + + /* see chirp_valid policy block above LONG_CHIRP */ if (sample_counter < T2_SAMPLES) begin chirp_data <= short_chirp_lut[sample_counter]; chirp_valid <= 1'b1; // Valid during entire chirp diff --git a/9_Firmware/9_2_FPGA/run_regression.sh b/9_Firmware/9_2_FPGA/run_regression.sh index da3bc86..7f110f2 100755 --- a/9_Firmware/9_2_FPGA/run_regression.sh +++ b/9_Firmware/9_2_FPGA/run_regression.sh @@ -62,7 +62,6 @@ PROD_RTL=( fir_lowpass.v ddc_input_interface.v chirp_memory_loader_param.v - latency_buffer.v matched_filter_multi_segment.v matched_filter_processing_chain.v range_bin_decimator.v @@ -101,7 +100,7 @@ RECEIVER_RTL=( tb/ad9484_interface_400m_stub.v ddc_400m.v nco_400m_enhanced.v cic_decimator_4x_enhanced.v cdc_modules.v fir_lowpass.v ddc_input_interface.v - chirp_memory_loader_param.v latency_buffer.v + chirp_memory_loader_param.v matched_filter_multi_segment.v matched_filter_processing_chain.v range_bin_decimator.v doppler_processor.v xfft_16.v fft_engine.v xfft_2048.v fft_engine_axi_bridge.v diff --git a/9_Firmware/9_2_FPGA/scripts/200t/build_200t.tcl b/9_Firmware/9_2_FPGA/scripts/200t/build_200t.tcl index f048cfa..72ef327 100644 --- a/9_Firmware/9_2_FPGA/scripts/200t/build_200t.tcl +++ b/9_Firmware/9_2_FPGA/scripts/200t/build_200t.tcl @@ -64,7 +64,6 @@ set rtl_files [list \ "${rtl_dir}/edge_detector.v" \ "${rtl_dir}/fir_lowpass.v" \ "${rtl_dir}/frequency_matched_filter.v" \ - "${rtl_dir}/latency_buffer.v" \ "${rtl_dir}/matched_filter_multi_segment.v" \ "${rtl_dir}/matched_filter_processing_chain.v" \ "${rtl_dir}/nco_400m_enhanced.v" \ diff --git a/9_Firmware/9_2_FPGA/tb/cosim/validate_mem_files.py b/9_Firmware/9_2_FPGA/tb/cosim/validate_mem_files.py index 8b9d79e..9645768 100644 --- a/9_Firmware/9_2_FPGA/tb/cosim/validate_mem_files.py +++ b/9_Firmware/9_2_FPGA/tb/cosim/validate_mem_files.py @@ -7,7 +7,6 @@ Checks: 2. FFT twiddle files: bit-exact match against cos(2*pi*k/N) in Q15 3. Long chirp .mem files: reverse-engineer parameters, check for chirp structure 4. Short chirp .mem files: check length, value range, spectral content - 5. latency_buffer LATENCY=3187 parameter validation Usage: python3 validate_mem_files.py @@ -378,81 +377,6 @@ def test_chirp_vs_model(): ) -# ============================================================================ -# TEST 6: Latency Buffer LATENCY=3187 Validation -# ============================================================================ -def test_latency_buffer(): - - # The latency buffer delays the reference chirp data to align with - # the matched filter processing chain output. - # - # The total latency through the processing chain depends on the branch: - # - # SYNTHESIS branch (fft_engine.v): - # - Load: 1024 cycles (input) - # - Forward FFT: LOG2N=10 stages x N/2=512 butterflies x 5-cycle pipeline = variable - # - Reference FFT: same - # - Conjugate multiply: 1024 cycles (4-stage pipeline in frequency_matched_filter) - # - Inverse FFT: same as forward - # - Output: 1024 cycles - # Total: roughly 3000-4000 cycles depending on pipeline fill - # - # The LATENCY=3187 value was likely determined empirically to align - # the reference chirp arriving at the processing chain with the - # correct time-domain position. - # - # Key constraint: LATENCY must be < 4096 (BRAM buffer size) - LATENCY = 3187 - BRAM_SIZE = 4096 - - check(LATENCY < BRAM_SIZE, - f"LATENCY ({LATENCY}) < BRAM size ({BRAM_SIZE})") - - # The fft_engine processes in stages: - # - LOAD: 1024 clocks (accepts input) - # - Per butterfly stage: 512 butterflies x 5 pipeline stages = ~2560 clocks + overhead - # Actually: 512 butterflies, each takes 5 cycles = 2560 per stage, 10 stages - # Total compute: 10 * 2560 = 25600 clocks - # But this is just for ONE FFT. The chain does 3 FFTs + multiply. - # - # For the SIMULATION branch, it's 1 clock per operation (behavioral). - # LATENCY=3187 doesn't apply to simulation branch behavior — - # it's the physical hardware pipeline latency. - # - # 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: - # - 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 purpose: the chain in the SYNTHESIS branch reads reference data - # via the long_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 - # - Specifically, the chain processes: load 1024 → FFT → FFT → multiply → IFFT → output - # The reference is consumed during the second FFT (ST_REF_BITREV/BUTTERFLY) - # which starts after the first FFT completes. - - # For now, validate that LATENCY is reasonable (between 1000 and 4095) - check(1000 < LATENCY < 4095, - f"LATENCY={LATENCY} in reasonable range [1000, 4095]") - - # Check that the module name vs parameter is consistent - # Module name was renamed from latency_buffer_2159 to latency_buffer - # to match the actual parameterized LATENCY value. No warning needed. - - # Validate address arithmetic won't overflow - min_read_ptr = 4096 + 0 - LATENCY - check(min_read_ptr >= 0 and min_read_ptr < 4096, - f"Min read_ptr after wrap = {min_read_ptr} (valid: 0..4095)") - - # The latency buffer uses valid_in gated reads, so it only counts - # valid samples. The number of valid_in pulses between first write - # and first read is LATENCY. - - # ============================================================================ # TEST 7: Cross-check chirp memory loader addressing # ============================================================================ @@ -553,7 +477,6 @@ def main(): test_long_chirp() test_short_chirp() test_chirp_vs_model() - test_latency_buffer() test_memory_addressing() test_seg3_padding() diff --git a/9_Firmware/9_2_FPGA/tb/tb_latency_buffer.v b/9_Firmware/9_2_FPGA/tb/tb_latency_buffer.v deleted file mode 100644 index 009bfdf..0000000 --- a/9_Firmware/9_2_FPGA/tb/tb_latency_buffer.v +++ /dev/null @@ -1,398 +0,0 @@ -`timescale 1ns / 1ps - -module tb_latency_buffer; - - // ── Parameters ───────────────────────────────────────────── - localparam CLK_PERIOD = 10.0; // 100 MHz - localparam DATA_WIDTH = 32; - // Use small LATENCY for fast simulation; full 3187 is too slow for iverilog - localparam LATENCY = 17; - - // ── Signals ──────────────────────────────────────────────── - reg clk; - reg reset_n; - reg [DATA_WIDTH-1:0] data_in; - reg valid_in; - wire [DATA_WIDTH-1:0] data_out; - wire valid_out; - - // ── Test bookkeeping ─────────────────────────────────────── - integer pass_count; - integer fail_count; - integer test_num; - integer i; - - reg [DATA_WIDTH-1:0] expected; - integer valid_output_count; - integer first_valid_cycle; - reg saw_valid; - - // ── Clock ────────────────────────────────────────────────── - always #(CLK_PERIOD/2) clk = ~clk; - - // ── DUT ──────────────────────────────────────────────────── - latency_buffer #( - .DATA_WIDTH(DATA_WIDTH), - .LATENCY(LATENCY) - ) uut ( - .clk (clk), - .reset_n (reset_n), - .data_in (data_in), - .valid_in (valid_in), - .data_out (data_out), - .valid_out(valid_out) - ); - - // ── Check task ───────────────────────────────────────────── - task check; - input cond; - input [511:0] label; - begin - test_num = test_num + 1; - if (cond) begin - $display("[PASS] Test %0d: %0s", test_num, label); - pass_count = pass_count + 1; - end else begin - $display("[FAIL] Test %0d: %0s", test_num, label); - fail_count = fail_count + 1; - end - end - endtask - - // ── Helper: apply reset ──────────────────────────────────── - task do_reset; - begin - reset_n = 0; - valid_in = 0; - data_in = 0; - repeat (4) @(posedge clk); - reset_n = 1; - @(posedge clk); #1; - end - endtask - - // ── Stimulus ─────────────────────────────────────────────── - initial begin - $dumpfile("tb_latency_buffer.vcd"); - $dumpvars(0, tb_latency_buffer); - - clk = 0; - reset_n = 0; - data_in = 0; - valid_in = 0; - pass_count = 0; - fail_count = 0; - test_num = 0; - - // ════════════════════════════════════════════════════════ - // TEST GROUP 1: Reset behaviour - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 1: Reset Behaviour ---"); - repeat (4) @(posedge clk); #1; - check(valid_out === 1'b0, "valid_out = 0 during reset"); - check(data_out === {DATA_WIDTH{1'b0}}, "data_out = 0 during reset"); - - // ════════════════════════════════════════════════════════ - // TEST GROUP 2: Priming phase — no output for LATENCY cycles - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 2: Priming Phase ---"); - do_reset; - - // Feed samples 1, 2, 3, ... continuously - saw_valid = 0; - for (i = 0; i < LATENCY; i = i + 1) begin - data_in = i + 1; - valid_in = 1; - @(posedge clk); #1; - if (valid_out) saw_valid = 1; - end - check(!saw_valid, "No valid output during first LATENCY input samples"); - - // The LATENCY-th sample is being written THIS cycle. - // The buffer_has_data flag is set when delay_counter == LATENCY-1, - // which happens on the LATENCY-th valid_in pulse (i == LATENCY-1 above). - // But valid_out only appears on the NEXT valid_in cycle because - // buffer_has_data is registered. - - // ════════════════════════════════════════════════════════ - // TEST GROUP 3: Exact latency measurement - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 3: Exact Latency Measurement ---"); - do_reset; - - // Feed a known sequence: sample N has value (N+1) - // After priming, output[N] should equal input[N - LATENCY] - valid_output_count = 0; - first_valid_cycle = -1; - - for (i = 0; i < LATENCY + 20; i = i + 1) begin - data_in = i + 1; - valid_in = 1; - @(posedge clk); #1; - if (valid_out) begin - if (first_valid_cycle < 0) first_valid_cycle = i; - valid_output_count = valid_output_count + 1; - end - end - - $display(" First valid output at input sample #%0d (expected ~%0d)", - first_valid_cycle, LATENCY + 1); - // After LATENCY samples written, buffer_has_data goes high. - // On the NEXT valid_in, valid_out_reg fires. Then valid_out_pipe - // (the actual output) fires one cycle later due to BRAM read register. - // So first valid is at sample LATENCY + 1. - check(first_valid_cycle == LATENCY + 1, - "First valid output appears at sample LATENCY+1 (BRAM read pipeline)"); - - // ════════════════════════════════════════════════════════ - // TEST GROUP 4: Data integrity (exact delay) - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 4: Data Integrity ---"); - do_reset; - - // Feed samples: value = (i + 100) - // After priming, each valid output should match data_in from LATENCY samples ago. - // - // The DUT calculates read_ptr from write_ptr, with BRAM read output - // registered for Block RAM inference. This adds 1 cycle of read latency - // beyond the LATENCY parameter. The valid_out pipeline stage tracks this. - // The auto-calibration below handles any offset empirically. - - begin : data_check_block - reg all_match; - reg [DATA_WIDTH-1:0] input_history [0:4095]; - integer out_idx; - integer match_count; - integer expected_idx; - - all_match = 1; - match_count = 0; - out_idx = 0; - - for (i = 0; i < LATENCY + 100; i = i + 1) begin - data_in = i + 100; - input_history[i] = i + 100; - valid_in = 1; - @(posedge clk); #1; - if (valid_out) begin - // Determine which input this output corresponds to. - // The first valid output appears at input cycle LATENCY. - // At that point, read_ptr was set from write_ptr = LATENCY - // => read_ptr = LATENCY - LATENCY = 0 => bram[0] = input_history[0]. - // But read_ptr is registered, so it takes effect next cycle. - // Actually, let's just check: output should be input_history[out_idx] - // where out_idx starts from 0. - expected = input_history[out_idx]; - if (data_out !== expected) begin - // Try out_idx+1 (off-by-one from registered read_ptr) - if (out_idx > 0 && data_out === input_history[out_idx - 1]) begin - // off by one — adjust - end else begin - if (all_match && match_count == 0) begin - // First output — calibrate - // Find which index data_out matches - begin : find_idx - integer j; - for (j = 0; j <= i; j = j + 1) begin - if (input_history[j] === data_out) begin - out_idx = j; - disable find_idx; - end - end - // No match found - all_match = 0; - $display(" [WARN] First output %0d does not match any input", - data_out); - end - end else begin - all_match = 0; - $display(" [WARN] Mismatch at out#%0d: got %0d, exp %0d", - match_count, data_out, expected); - end - end - end - match_count = match_count + 1; - out_idx = out_idx + 1; - end - end - - $display(" Verified %0d output samples", match_count); - check(match_count > 0, "Produced output samples after priming"); - check(all_match, "All outputs match input delayed by LATENCY"); - end - - // ════════════════════════════════════════════════════════ - // TEST GROUP 5: Valid gating — no output when valid_in=0 - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 5: Valid Gating ---"); - do_reset; - - // Prime the buffer - for (i = 0; i < LATENCY + 5; i = i + 1) begin - data_in = i + 1; - valid_in = 1; - @(posedge clk); #1; - end - - // Now de-assert valid_in — after pipeline drains (1 cycle), no more outputs - valid_in = 0; - data_in = 32'hDEADBEEF; - // Allow 1 cycle for the valid pipeline to drain - @(posedge clk); #1; - valid_output_count = 0; - for (i = 0; i < 20; i = i + 1) begin - @(posedge clk); #1; - if (valid_out) valid_output_count = valid_output_count + 1; - end - check(valid_output_count == 0, "No output when valid_in deasserted (after pipeline drain)"); - - // ════════════════════════════════════════════════════════ - // TEST GROUP 6: Intermittent valid_in - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 6: Intermittent Valid ---"); - do_reset; - - // Feed with valid_in toggling every other cycle - valid_output_count = 0; - begin : intermittent_block - integer valid_in_count; - valid_in_count = 0; - - for (i = 0; i < (LATENCY + 30) * 2; i = i + 1) begin - if (i[0] == 1'b0) begin - data_in = valid_in_count + 200; - valid_in = 1; - valid_in_count = valid_in_count + 1; - end else begin - valid_in = 0; - end - @(posedge clk); #1; - if (valid_out) valid_output_count = valid_output_count + 1; - end - end - - $display(" Intermittent: %0d valid outputs", valid_output_count); - check(valid_output_count > 0, "Outputs produced with intermittent valid_in"); - - // ════════════════════════════════════════════════════════ - // TEST GROUP 7: Pointer wrap-around - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 7: Pointer Wrap-Around ---"); - do_reset; - - // Feed 4096 + LATENCY + 50 samples to force write_ptr wrap-around - // (4096 is the BRAM depth) - begin : wrap_block - reg wrap_all_match; - reg [DATA_WIDTH-1:0] wrap_history [0:8191]; - integer wrap_out_idx; - integer wrap_match_count; - integer total_samples; - - total_samples = 4096 + LATENCY + 50; - wrap_all_match = 1; - wrap_match_count = 0; - wrap_out_idx = 0; - - for (i = 0; i < total_samples; i = i + 1) begin - data_in = i + 500; - wrap_history[i] = i + 500; - valid_in = 1; - @(posedge clk); #1; - if (valid_out) begin - if (wrap_match_count == 0) begin - // Calibrate: find which index - begin : find_wrap_idx - integer j; - for (j = 0; j <= i; j = j + 1) begin - if (wrap_history[j] === data_out) begin - wrap_out_idx = j; - disable find_wrap_idx; - end - end - end - end else begin - expected = wrap_history[wrap_out_idx]; - if (data_out !== expected) begin - wrap_all_match = 0; - if (wrap_match_count < 5) begin - $display(" [WARN] Wrap mismatch out#%0d: got %0d, exp %0d", - wrap_match_count, data_out, expected); - end - end - end - wrap_match_count = wrap_match_count + 1; - wrap_out_idx = wrap_out_idx + 1; - end - end - - $display(" Wrap-around: %0d outputs verified", wrap_match_count); - check(wrap_match_count > 4096, "More than 4096 outputs (proves wrap-around)"); - check(wrap_all_match, "Data integrity across pointer wrap-around"); - end - - // ════════════════════════════════════════════════════════ - // TEST GROUP 8: Reset mid-operation - // ════════════════════════════════════════════════════════ - $display("\n--- Test Group 8: Reset Mid-Operation ---"); - - // Prime and get some outputs flowing - do_reset; - for (i = 0; i < LATENCY + 10; i = i + 1) begin - data_in = i + 1; - valid_in = 1; - @(posedge clk); #1; - end - // Feed one more cycle to ensure pipeline has flushed - data_in = 32'hFFFF; - valid_in = 1; - @(posedge clk); #1; - // Should be producing outputs now - check(valid_out === 1'b1, "Outputs flowing before mid-op reset"); - - // Apply reset mid-stream - reset_n = 0; - valid_in = 0; - repeat (4) @(posedge clk); #1; - check(valid_out === 1'b0, "valid_out = 0 after mid-operation reset"); - - // Release reset and verify buffer needs full re-priming - reset_n = 1; - @(posedge clk); #1; - - saw_valid = 0; - for (i = 0; i < LATENCY; i = i + 1) begin - data_in = i + 1000; - valid_in = 1; - @(posedge clk); #1; - if (valid_out) saw_valid = 1; - end - check(!saw_valid, "No output during re-priming after reset"); - - // ════════════════════════════════════════════════════════ - // TEST GROUP 9: Large LATENCY parameter test - // ════════════════════════════════════════════════════════ - // (We use a second instance with LATENCY=100 to verify parameterization) - // Skipped in this TB to keep simulation short — the wrap-around test - // already validates 4000+ samples. - - // ════════════════════════════════════════════════════════ - // Summary - // ════════════════════════════════════════════════════════ - $display(""); - $display("========================================"); - $display(" LATENCY BUFFER TESTBENCH RESULTS"); - $display(" PASSED: %0d / %0d", pass_count, test_num); - $display(" FAILED: %0d / %0d", fail_count, test_num); - if (fail_count == 0) - $display(" ** ALL TESTS PASSED **"); - else - $display(" ** SOME TESTS FAILED **"); - $display("========================================"); - $display(""); - - #100; - $finish; - end - -endmodule diff --git a/9_Firmware/9_2_FPGA/tb/tb_system_e2e.v b/9_Firmware/9_2_FPGA/tb/tb_system_e2e.v index 87de989..9f47286 100644 --- a/9_Firmware/9_2_FPGA/tb/tb_system_e2e.v +++ b/9_Firmware/9_2_FPGA/tb/tb_system_e2e.v @@ -32,7 +32,7 @@ * radar_receiver_final.v tb/ad9484_interface_400m_stub.v \ * ddc_400m.v nco_400m_enhanced.v cic_decimator_4x_enhanced.v \ * cdc_modules.v fir_lowpass.v ddc_input_interface.v \ - * chirp_memory_loader_param.v latency_buffer.v \ + * chirp_memory_loader_param.v \ * matched_filter_multi_segment.v matched_filter_processing_chain.v \ * range_bin_decimator.v doppler_processor.v xfft_16.v fft_engine.v \ * usb_data_interface.v edge_detector.v radar_mode_controller.v diff --git a/9_Firmware/9_3_GUI/v7/models.py b/9_Firmware/9_3_GUI/v7/models.py index 82ac97c..7c69c9b 100644 --- a/9_Firmware/9_3_GUI/v7/models.py +++ b/9_Firmware/9_3_GUI/v7/models.py @@ -105,8 +105,11 @@ class RadarSettings: tab and Opcode enum in radar_protocol.py. This dataclass holds only host-side display/map settings and physical-unit conversion factors. - range_resolution and velocity_resolution should be calibrated to - the actual waveform parameters. + range_resolution and velocity_resolution below are placeholders. Live + operation derives the actual values from WaveformConfig in + workers.py:RadarDataWorker (see GUI-C3 fix); these literals are only + consulted by code paths that have not yet been migrated, and should + not be relied on for physics-accurate display. """ system_frequency: float = 10.5e9 # Hz (carrier, used for velocity calc) range_resolution: float = 6.0 # Meters per range bin (c/(2*Fs)*decim = 1.5*4)