chore: delete dead latency_buffer; doc cleanup for two stale comments

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.
This commit is contained in:
Jason
2026-04-28 12:52:13 +05:45
parent c49092f52b
commit b7ac2de1a4
8 changed files with 16 additions and 616 deletions
-131
View File
@@ -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
+9 -4
View File
@@ -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
+1 -2
View File
@@ -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
@@ -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" \
@@ -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()
-398
View File
@@ -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
+1 -1
View File
@@ -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
+5 -2
View File
@@ -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)