CIC comb pipeline registers, BUFG sim guard, system TB fix, regression runner

- cic_decimator_4x_enhanced.v: Add integrator_sampled_comb and
  data_valid_comb_pipe pipeline stages between integrator sampling and
  comb computation to break the critical path (matches remote 40cda0f)
- radar_system_top.v: Wrap 3 BUFG instances in ifdef SIMULATION guard
  with pass-through assigns for iverilog compatibility
- radar_system_tb.v: Convert generate_radar_echo function to task and
  move sin_lut declaration before task (iverilog declaration-order fix),
  add modular index clamping to prevent LUT out-of-bounds
- run_regression.sh: Automated regression runner for all 18 FPGA
  testbenches with --quick mode. Results: 17 pass, 1 pre-existing fail
- .gitignore: Exclude *.vvp, *.vcd simulation artifacts
This commit is contained in:
Jason
2026-03-19 11:31:46 +02:00
parent c466021bb6
commit 463ebef554
6 changed files with 2354 additions and 2081 deletions
+12
View File
@@ -0,0 +1,12 @@
# Simulation build artifacts
*.vvp
*.vcd
tb/*.vvp
tb/*.vcd
# Vivado project files (managed separately)
*.jou
*.log
*.str
*.bit
*.ltx
@@ -475,7 +475,8 @@ assign pcout_3 = sim_int_3;
// ============================================================================ // ============================================================================
// CONTROL AND MONITORING (fabric logic) // CONTROL AND MONITORING (fabric logic)
// ============================================================================ // ============================================================================
reg signed [COMB_WIDTH-1:0] integrator_sampled; (* keep = "true", dont_touch = "true" *) reg signed [COMB_WIDTH-1:0] integrator_sampled;
(* keep = "true", dont_touch = "true", max_fanout = 1 *) reg signed [COMB_WIDTH-1:0] integrator_sampled_comb;
(* use_dsp = "yes" *) reg signed [COMB_WIDTH-1:0] comb [0:STAGES-1]; (* use_dsp = "yes" *) reg signed [COMB_WIDTH-1:0] comb [0:STAGES-1];
reg signed [COMB_WIDTH-1:0] comb_delay [0:STAGES-1][0:COMB_DELAY-1]; reg signed [COMB_WIDTH-1:0] comb_delay [0:STAGES-1][0:COMB_DELAY-1];
@@ -483,6 +484,7 @@ reg signed [COMB_WIDTH-1:0] comb_delay [0:STAGES-1][0:COMB_DELAY-1];
reg [1:0] decimation_counter; reg [1:0] decimation_counter;
(* keep = "true", max_fanout = 4 *) reg data_valid_delayed; (* keep = "true", max_fanout = 4 *) reg data_valid_delayed;
(* keep = "true", max_fanout = 4 *) reg data_valid_comb; (* keep = "true", max_fanout = 4 *) reg data_valid_comb;
(* keep = "true", max_fanout = 4 *) reg data_valid_comb_pipe;
reg [7:0] output_counter; reg [7:0] output_counter;
reg [ACC_WIDTH-1:0] max_integrator_value; reg [ACC_WIDTH-1:0] max_integrator_value;
reg overflow_detected; reg overflow_detected;
@@ -522,6 +524,7 @@ initial begin
decimation_counter = 0; decimation_counter = 0;
data_valid_delayed = 0; data_valid_delayed = 0;
data_valid_comb = 0; data_valid_comb = 0;
data_valid_comb_pipe = 0;
output_counter = 0; output_counter = 0;
max_integrator_value = 0; max_integrator_value = 0;
overflow_detected = 0; overflow_detected = 0;
@@ -605,8 +608,12 @@ end
always @(posedge clk) begin always @(posedge clk) begin
if (!reset_n) begin if (!reset_n) begin
data_valid_comb <= 0; data_valid_comb <= 0;
data_valid_comb_pipe <= 0;
integrator_sampled_comb <= 0;
end else begin end else begin
data_valid_comb <= data_valid_delayed; data_valid_comb <= data_valid_delayed;
data_valid_comb_pipe <= data_valid_comb;
integrator_sampled_comb <= integrator_sampled;
end end
end end
@@ -650,14 +657,14 @@ always @(posedge clk) begin
comb_saturation_event_count <= 0; comb_saturation_event_count <= 0;
end end
if (data_valid_comb) begin if (data_valid_comb_pipe) begin
for (i = 0; i < STAGES; i = i + 1) begin for (i = 0; i < STAGES; i = i + 1) begin
if (i == 0) begin if (i == 0) begin
comb[0] <= integrator_sampled - comb_delay[0][COMB_DELAY-1]; comb[0] <= integrator_sampled_comb - comb_delay[0][COMB_DELAY-1];
for (j = COMB_DELAY-1; j > 0; j = j - 1) begin for (j = COMB_DELAY-1; j > 0; j = j - 1) begin
comb_delay[0][j] <= comb_delay[0][j-1]; comb_delay[0][j] <= comb_delay[0][j-1];
end end
comb_delay[0][0] <= integrator_sampled; comb_delay[0][0] <= integrator_sampled_comb;
end else begin end else begin
comb[i] <= comb[i-1] - comb_delay[i][COMB_DELAY-1]; comb[i] <= comb[i-1] - comb_delay[i][COMB_DELAY-1];
for (j = COMB_DELAY-1; j > 0; j = j - 1) begin for (j = COMB_DELAY-1; j > 0; j = j - 1) begin
+7
View File
@@ -180,6 +180,12 @@ reg [3:0] status_reg;
// CLOCK BUFFERING // CLOCK BUFFERING
// ============================================================================ // ============================================================================
`ifdef SIMULATION
// In simulation (iverilog), BUFG is not available — pass-through assigns
assign clk_100m_buf = clk_100m;
assign clk_120m_dac_buf = clk_120m_dac;
assign ft601_clk_buf = ft601_clk_in;
`else
BUFG bufg_100m ( BUFG bufg_100m (
.I(clk_100m), .I(clk_100m),
.O(clk_100m_buf) .O(clk_100m_buf)
@@ -194,6 +200,7 @@ BUFG bufg_ft601 (
.I(ft601_clk_in), .I(ft601_clk_in),
.O(ft601_clk_buf) .O(ft601_clk_buf)
); );
`endif
// Reset synchronization (clk_100m domain) // Reset synchronization (clk_100m domain)
(* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync; (* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync;
+245
View File
@@ -0,0 +1,245 @@
#!/bin/bash
# ===========================================================================
# FPGA Regression Test Runner for AERIS-10 Radar
# Runs all verified iverilog testbenches and reports pass/fail summary.
#
# Usage: ./run_regression.sh [--quick]
# --quick Skip long-running integration tests (receiver golden, system TB)
#
# Exit code: 0 if all tests pass, 1 if any fail
# ===========================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
QUICK=0
if [[ "${1:-}" == "--quick" ]]; then
QUICK=1
fi
PASS=0
FAIL=0
SKIP=0
ERRORS=""
# Colors (if terminal supports it)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
# ---------------------------------------------------------------------------
# Helper: compile and run a single testbench
# run_test <name> <vvp_path> <iverilog_args...>
# ---------------------------------------------------------------------------
run_test() {
local name="$1"
local vvp="$2"
shift 2
local args=("$@")
printf " %-45s " "$name"
# Compile
if ! iverilog -g2001 -DSIMULATION -o "$vvp" "${args[@]}" 2>/tmp/iverilog_err_$$; then
echo -e "${RED}COMPILE FAIL${NC}"
ERRORS="$ERRORS\n $name: compile error ($(head -1 /tmp/iverilog_err_$$))"
FAIL=$((FAIL + 1))
return
fi
# Run
local output
output=$(timeout 120 vvp "$vvp" 2>&1) || true
# Count PASS/FAIL in output (testbenches use explicit [PASS]/[FAIL] markers)
local test_pass test_fail
test_pass=$(echo "$output" | grep -ci '\bPASS\b' || true)
test_fail=$(echo "$output" | grep -ci '\bFAIL\b' || true)
if [[ "$test_fail" -gt 0 ]]; then
echo -e "${RED}FAIL${NC} (pass=$test_pass, fail=$test_fail)"
ERRORS="$ERRORS\n $name: $test_fail failure(s)"
FAIL=$((FAIL + 1))
elif [[ "$test_pass" -gt 0 ]]; then
echo -e "${GREEN}PASS${NC} ($test_pass checks)"
PASS=$((PASS + 1))
else
# No PASS/FAIL markers — check for clean completion
if echo "$output" | grep -qi 'finish\|complete\|done'; then
echo -e "${GREEN}PASS${NC} (completed)"
PASS=$((PASS + 1))
else
echo -e "${YELLOW}UNKNOWN${NC} (no PASS/FAIL markers)"
ERRORS="$ERRORS\n $name: no pass/fail markers in output"
FAIL=$((FAIL + 1))
fi
fi
rm -f "$vvp"
}
# ===========================================================================
echo "============================================"
echo " AERIS-10 FPGA Regression Test Suite"
echo "============================================"
echo ""
echo "Date: $(date)"
echo "iverilog: $(iverilog -V 2>&1 | head -1)"
echo ""
# ===========================================================================
# UNIT TESTS — Changed Modules (HIGH PRIORITY)
# ===========================================================================
echo "--- HIGH PRIORITY: Changed Modules ---"
run_test "CIC Decimator" \
tb/tb_cic_reg.vvp \
tb/tb_cic_decimator.v cic_decimator_4x_enhanced.v
run_test "Chirp Controller (BRAM)" \
tb/tb_chirp_reg.vvp \
tb/tb_chirp_controller.v plfm_chirp_controller.v
run_test "Chirp Contract" \
tb/tb_chirp_ctr_reg.vvp \
tb/tb_chirp_contract.v plfm_chirp_controller.v
run_test "Doppler Processor (DSP48)" \
tb/tb_doppler_reg.vvp \
tb/tb_doppler_cosim.v doppler_processor.v xfft_32.v fft_engine.v
echo ""
# ===========================================================================
# INTEGRATION TESTS
# ===========================================================================
echo "--- INTEGRATION TESTS ---"
run_test "DDC Chain (NCO→CIC→FIR)" \
tb/tb_ddc_reg.vvp \
tb/tb_ddc_cosim.v ddc_400m.v nco_400m_enhanced.v \
cic_decimator_4x_enhanced.v fir_lowpass.v cdc_modules.v
if [[ "$QUICK" -eq 0 ]]; then
# Golden generate
run_test "Receiver (golden generate)" \
tb/tb_rx_golden_reg.vvp \
-DGOLDEN_GENERATE \
tb/tb_radar_receiver_final.v radar_receiver_final.v \
radar_mode_controller.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 \
matched_filter_multi_segment.v matched_filter_processing_chain.v \
range_bin_decimator.v doppler_processor.v xfft_32.v fft_engine.v
# Golden compare
run_test "Receiver (golden compare)" \
tb/tb_rx_compare_reg.vvp \
tb/tb_radar_receiver_final.v radar_receiver_final.v \
radar_mode_controller.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 \
matched_filter_multi_segment.v matched_filter_processing_chain.v \
range_bin_decimator.v doppler_processor.v xfft_32.v fft_engine.v
# Full system top
run_test "System Top (radar_system_tb)" \
tb/tb_system_reg.vvp \
tb/radar_system_tb.v radar_system_top.v \
radar_transmitter.v dac_interface_single.v plfm_chirp_controller.v \
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 \
matched_filter_multi_segment.v matched_filter_processing_chain.v \
range_bin_decimator.v doppler_processor.v xfft_32.v fft_engine.v \
usb_data_interface.v edge_detector.v radar_mode_controller.v
else
echo " (skipped receiver golden + system top — use without --quick)"
SKIP=$((SKIP + 3))
fi
echo ""
# ===========================================================================
# UNIT TESTS — Signal Processing
# ===========================================================================
echo "--- UNIT TESTS: Signal Processing ---"
run_test "FFT Engine" \
tb/tb_fft_reg.vvp \
tb/tb_fft_engine.v fft_engine.v
run_test "XFFT-32 Wrapper" \
tb/tb_xfft_reg.vvp \
tb/tb_xfft_32.v xfft_32.v fft_engine.v
run_test "NCO 400MHz" \
tb/tb_nco_reg.vvp \
tb/tb_nco_400m.v nco_400m_enhanced.v
run_test "FIR Lowpass" \
tb/tb_fir_reg.vvp \
tb/tb_fir_lowpass.v fir_lowpass.v
run_test "Matched Filter Chain" \
tb/tb_mf_reg.vvp \
tb/tb_matched_filter_processing_chain.v matched_filter_processing_chain.v \
xfft_32.v fft_engine.v chirp_memory_loader_param.v
echo ""
# ===========================================================================
# UNIT TESTS — Infrastructure
# ===========================================================================
echo "--- UNIT TESTS: Infrastructure ---"
run_test "CDC Modules (3 variants)" \
tb/tb_cdc_reg.vvp \
tb/tb_cdc_modules.v cdc_modules.v
run_test "Edge Detector" \
tb/tb_edge_reg.vvp \
tb/tb_edge_detector.v edge_detector.v
run_test "USB Data Interface" \
tb/tb_usb_reg.vvp \
tb/tb_usb_data_interface.v usb_data_interface.v
run_test "Range Bin Decimator" \
tb/tb_rbd_reg.vvp \
tb/tb_range_bin_decimator.v range_bin_decimator.v
run_test "Radar Mode Controller" \
tb/tb_rmc_reg.vvp \
tb/tb_radar_mode_controller.v radar_mode_controller.v
echo ""
# ===========================================================================
# SUMMARY
# ===========================================================================
TOTAL=$((PASS + FAIL + SKIP))
echo "============================================"
echo " RESULTS: $PASS passed, $FAIL failed, $SKIP skipped / $TOTAL total"
echo "============================================"
if [[ -n "$ERRORS" ]]; then
echo ""
echo "Failures:"
echo -e "$ERRORS"
fi
echo ""
# Exit with error if any failures
if [[ "$FAIL" -gt 0 ]]; then
exit 1
fi
exit 0
File diff suppressed because it is too large Load Diff
+31 -29
View File
@@ -297,7 +297,7 @@ initial begin
// Generate echo signal when transmitter is active // Generate echo signal when transmitter is active
if (tx_mixer_en && fpga_rf_switch) begin if (tx_mixer_en && fpga_rf_switch) begin
adc_data_pattern = generate_radar_echo(sample_count); compute_radar_echo(sample_count);
end else begin end else begin
adc_data_pattern = 8'h80; // Mid-scale noise floor adc_data_pattern = 8'h80; // Mid-scale noise floor
end end
@@ -311,35 +311,8 @@ initial begin
end end
end end
// Function to generate radar echo based on multiple targets
function [7:0] generate_radar_echo;
input integer sample;
integer t;
integer echo_sum;
integer chirp_phase;
reg [7:0] result;
begin
echo_sum = 128; // DC offset
for (t = 0; t < 5; t = t + 1) begin
if (echo_delay[t] > 0 && sample > echo_delay[t]) begin
// Simple Doppler modulation
chirp_phase = ((sample - echo_delay[t]) * 10) % 256;
echo_sum = echo_sum + $signed({1'b0, echo_amplitude[t]}) *
$signed({1'b0, sin_lut[chirp_phase + echo_phase[t]]}) / 128;
end
end
// Clamp to 8-bit range
if (echo_sum > 255) echo_sum = 255;
if (echo_sum < 0) echo_sum = 0;
result = echo_sum[7:0];
generate_radar_echo = result;
end
endfunction
// Sine LUT for echo modulation (pre-computed, equivalent to 128 + 127*sin(2*pi*i/256)) // Sine LUT for echo modulation (pre-computed, equivalent to 128 + 127*sin(2*pi*i/256))
// Declared before task so iverilog can resolve the reference.
reg [7:0] sin_lut [0:255]; reg [7:0] sin_lut [0:255];
integer lut_i; integer lut_i;
initial begin initial begin
@@ -409,6 +382,35 @@ initial begin
sin_lut[252] = 116; sin_lut[253] = 119; sin_lut[254] = 122; sin_lut[255] = 125; sin_lut[252] = 116; sin_lut[253] = 119; sin_lut[254] = 122; sin_lut[255] = 125;
end end
// Task to generate radar echo based on multiple targets
// (Uses task instead of function so iverilog can access module-level memories)
task compute_radar_echo;
input integer sample;
integer t;
integer echo_sum;
integer chirp_phase;
integer lut_idx;
begin
echo_sum = 128; // DC offset
for (t = 0; t < 5; t = t + 1) begin
if (echo_delay[t] > 0 && sample > echo_delay[t]) begin
// Simple Doppler modulation
chirp_phase = ((sample - echo_delay[t]) * 10) % 256;
lut_idx = (chirp_phase + echo_phase[t]) % 256;
echo_sum = echo_sum + $signed({1'b0, echo_amplitude[t]}) *
$signed({1'b0, sin_lut[lut_idx]}) / 128;
end
end
// Clamp to 8-bit range
if (echo_sum > 255) echo_sum = 255;
if (echo_sum < 0) echo_sum = 0;
adc_data_pattern = echo_sum[7:0];
end
endtask
// ============================================================================ // ============================================================================
// SPI COMMUNICATION MONITORING // SPI COMMUNICATION MONITORING
// ============================================================================ // ============================================================================