mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-09 23:17:33 +00:00
0c82de54a2
Bug: bridge advanced in_count and asserted tlast on din_valid alone, ignoring the IP's tready handshake. With LogiCORE FFT v9.1 in nonrealtime throttle mode (per .xci), tready can deassert briefly during BFP normalization or pipeline events, silently dropping input samples and shifting tlast off-by-N. Fix: add 1-deep skid buffer + AXI-correct handshake. Phase 1 drains the active beat when the IP accepts it (and shifts skid up); Phase 2 loads new upstream samples respecting post-handshake slot availability. Track accept_count separately from in_count to drive the S_FEED->S_DRAIN transition on the Nth accepted beat. Sustained 2+ cycle backpressure exhausts the skid and sets overflow_sticky for debug visibility. Audit cross-refs (AUDIT-C10): - "tready ignored" - CONFIRMED, fixed here - "SCALE_SCH unset" - REFUTED (BFP mode uses tuser, not cfg_tdata) - "output ordering not configured" - REFUTED (.xci natural_order) Verification: new tb_fft_engine_axi_bridge.v with stub xfft_2048 exercises 4 backpressure patterns (none / dip-at-3 / dip-at-100 / 3-cycle sustained). Quick regression 30/30 PASS.
734 lines
25 KiB
Bash
Executable File
734 lines
25 KiB
Bash
Executable File
#!/bin/bash
|
|
# ===========================================================================
|
|
# FPGA Regression Test Runner for AERIS-10 Radar
|
|
# Phase 0: Vivado-style lint (catches issues iverilog silently accepts)
|
|
# Phase 1+: Compile and run all verified iverilog testbenches
|
|
#
|
|
# Usage: ./run_regression.sh [--quick] [--skip-lint]
|
|
# --quick Skip long-running integration tests (receiver golden, system TB)
|
|
# --skip-lint Skip Phase 0 lint checks (not recommended)
|
|
#
|
|
# 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
|
|
SKIP_LINT=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--quick) QUICK=1 ;;
|
|
--skip-lint) SKIP_LINT=1 ;;
|
|
esac
|
|
done
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
SKIP=0
|
|
LINT_WARN=0
|
|
LINT_ERR=0
|
|
ERRORS=""
|
|
|
|
# Colors (if terminal supports it)
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# ===========================================================================
|
|
# PHASE 0: VIVADO-STYLE LINT
|
|
# Two layers:
|
|
# (A) iverilog -Wall full-design compile — parse for serious warnings
|
|
# (B) Custom regex checks for patterns Vivado treats as errors
|
|
# ===========================================================================
|
|
|
|
# Production RTL file list (same as system TB minus testbench files)
|
|
# Uses ADC stub for IBUFDS/BUFIO primitives that iverilog can't parse
|
|
PROD_RTL=(
|
|
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
|
|
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
|
|
frequency_matched_filter.v
|
|
usb_data_interface.v
|
|
usb_data_interface_ft2232h.v
|
|
edge_detector.v
|
|
radar_mode_controller.v
|
|
rx_gain_control.v
|
|
cfar_ca.v
|
|
mti_canceller.v
|
|
fpga_self_test.v
|
|
)
|
|
|
|
# Source-only RTL (not instantiated at top level, but should still be lint-clean)
|
|
# Note: ad9484_interface_400m.v is excluded — it uses Xilinx primitives
|
|
# (IBUFDS, BUFIO, BUFG, IDDR) that iverilog cannot compile. The production
|
|
# design uses tb/ad9484_interface_400m_stub.v for simulation instead.
|
|
EXTRA_RTL=(
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Shared RTL file lists for integration / system tests
|
|
# Centralised here so a new module only needs adding once.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Receiver chain (used by golden generate/compare tests)
|
|
RECEIVER_RTL=(
|
|
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
|
|
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
|
|
frequency_matched_filter.v
|
|
rx_gain_control.v mti_canceller.v
|
|
)
|
|
|
|
# Full system top (receiver chain + TX + USB + detection + self-test)
|
|
SYSTEM_RTL=(
|
|
radar_system_top.v
|
|
radar_transmitter.v dac_interface_single.v plfm_chirp_controller.v
|
|
"${RECEIVER_RTL[@]}"
|
|
usb_data_interface.v usb_data_interface_ft2232h.v edge_detector.v
|
|
cfar_ca.v fpga_self_test.v
|
|
)
|
|
|
|
# ---- Layer A: iverilog -Wall compilation ----
|
|
run_lint_iverilog() {
|
|
local label="$1"
|
|
shift
|
|
local files=("$@")
|
|
local warn_file="/tmp/iverilog_lint_$$_${label}.log"
|
|
|
|
printf " %-45s " "iverilog -Wall ($label)"
|
|
|
|
if ! iverilog -g2001 -DSIMULATION -Wall -o /dev/null "${files[@]}" 2>"$warn_file"; then
|
|
# Hard compile error — always fatal
|
|
echo -e "${RED}COMPILE ERROR${NC}"
|
|
while IFS= read -r line; do
|
|
echo " $line"
|
|
done < "$warn_file"
|
|
LINT_ERR=$((LINT_ERR + 1))
|
|
rm -f "$warn_file"
|
|
return 1
|
|
fi
|
|
|
|
# Parse warnings — classify as error-level or info-level
|
|
local err_count=0
|
|
local info_count=0
|
|
local err_lines=""
|
|
|
|
while IFS= read -r line; do
|
|
# Part-select out of range — Vivado Synth 8-524 (ERROR in Vivado)
|
|
if echo "$line" | grep -q 'Part select.*is selecting after the vector\|out of bound bits'; then
|
|
err_count=$((err_count + 1))
|
|
err_lines="$err_lines\n ${RED}[VIVADO-ERR]${NC} $line"
|
|
# Port width mismatch / connection mismatch
|
|
elif echo "$line" | grep -q 'port.*does not match\|Port.*mismatch'; then
|
|
err_count=$((err_count + 1))
|
|
err_lines="$err_lines\n ${RED}[VIVADO-ERR]${NC} $line"
|
|
# Informational warnings (timescale, dangling ports, array sensitivity)
|
|
elif echo "$line" | grep -q 'timescale\|dangling\|sensitive to all'; then
|
|
info_count=$((info_count + 1))
|
|
# Unknown warning — report but don't fail
|
|
elif [[ -n "$line" ]]; then
|
|
info_count=$((info_count + 1))
|
|
fi
|
|
done < "$warn_file"
|
|
|
|
if [[ "$err_count" -gt 0 ]]; then
|
|
echo -e "${RED}FAIL${NC} ($err_count Vivado-class errors, $info_count info)"
|
|
echo -e "$err_lines"
|
|
LINT_ERR=$((LINT_ERR + err_count))
|
|
else
|
|
echo -e "${GREEN}PASS${NC} ($info_count info warnings)"
|
|
fi
|
|
|
|
rm -f "$warn_file"
|
|
}
|
|
|
|
# ---- Layer B: Custom regex static checks ----
|
|
# Catches patterns that Vivado treats as errors/warnings but iverilog ignores
|
|
run_lint_static() {
|
|
printf " %-45s " "Static RTL checks"
|
|
|
|
local err_count=0
|
|
local warn_count=0
|
|
local err_lines=""
|
|
local warn_lines=""
|
|
|
|
for f in "$@"; do
|
|
[[ -f "$f" ]] || continue
|
|
# Skip testbench files (tb/ directory) — only lint production RTL
|
|
case "$f" in tb/*) continue ;; esac
|
|
|
|
local linenum=0
|
|
while IFS= read -r line; do
|
|
linenum=$((linenum + 1))
|
|
|
|
# --- CHECK 1: Part-select with literal range on reg ---
|
|
# Pattern: identifier[N:M] where N exceeds declared width
|
|
# (iverilog catches this, but belt-and-suspenders)
|
|
|
|
# --- CHECK 2: case/casex/casez without default (non-full case) ---
|
|
# Vivado SYNTH-6 / inferred latch warning
|
|
# Heuristic: look for case/casex/casez, then check if 'default' appears
|
|
# before the matching 'endcase'. This is approximate — full parsing
|
|
# would need a real parser. We flag 'case' lines so the developer
|
|
# can manually verify.
|
|
# (Handled below as a multi-line check)
|
|
|
|
# --- CHECK 3: Blocking assignment (=) inside always @(posedge ...) ---
|
|
# Vivado SYNTH-5 warning for inferred latches / race conditions
|
|
# Only flag if the always block is clocked (posedge/negedge)
|
|
# This is a heuristic — we check for '= ' that isn't '<=', '==', '!='
|
|
# inside an always block header containing 'posedge' or 'negedge'.
|
|
# (Too complex for line-by-line — skip for now, handled by testbenches)
|
|
|
|
# --- CHECK 4: Multi-driven register (assign + always on same signal) ---
|
|
# (Would need cross-file analysis — skip for v1)
|
|
|
|
done < "$f"
|
|
done
|
|
|
|
# --- Multi-line check: case without default ---
|
|
for f in "$@"; do
|
|
[[ -f "$f" ]] || continue
|
|
case "$f" in tb/*) continue ;; esac
|
|
|
|
# Find case blocks and check for default
|
|
# Use awk to find case..endcase blocks missing 'default'
|
|
local missing_defaults
|
|
missing_defaults=$(awk '
|
|
/^[[:space:]]*(case|casex|casez)[[:space:]]*\(/ {
|
|
case_line = NR
|
|
case_file = FILENAME
|
|
has_default = 0
|
|
in_case = 1
|
|
next
|
|
}
|
|
in_case && /default[[:space:]]*:/ {
|
|
has_default = 1
|
|
}
|
|
in_case && /endcase/ {
|
|
if (!has_default) {
|
|
printf "%s:%d: case statement without default\n", FILENAME, case_line
|
|
}
|
|
in_case = 0
|
|
}
|
|
' "$f" 2>/dev/null)
|
|
|
|
if [[ -n "$missing_defaults" ]]; then
|
|
while IFS= read -r hit; do
|
|
warn_count=$((warn_count + 1))
|
|
warn_lines="$warn_lines\n ${YELLOW}[SYNTH-6]${NC} $hit"
|
|
done <<< "$missing_defaults"
|
|
fi
|
|
done
|
|
|
|
# CHECK 5 ($readmemh in synth code) and CHECK 6 (unused includes)
|
|
# require multi-line ifdef tracking / cross-file analysis. Not feasible
|
|
# with line-by-line regex. Omitted — use Vivado lint instead.
|
|
|
|
if [[ "$err_count" -gt 0 ]]; then
|
|
echo -e "${RED}FAIL${NC} ($err_count errors, $warn_count warnings)"
|
|
echo -e "$err_lines"
|
|
LINT_ERR=$((LINT_ERR + err_count))
|
|
elif [[ "$warn_count" -gt 0 ]]; then
|
|
echo -e "${YELLOW}WARN${NC} ($warn_count warnings)"
|
|
echo -e "$warn_lines"
|
|
LINT_WARN=$((LINT_WARN + warn_count))
|
|
else
|
|
echo -e "${GREEN}PASS${NC}"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper: compile, run, and compare a matched-filter co-sim scenario
|
|
# run_mf_cosim <scenario_name> <define_flag>
|
|
# ---------------------------------------------------------------------------
|
|
run_mf_cosim() {
|
|
local name="$1"
|
|
local define="$2"
|
|
local vvp="tb/tb_mf_cosim_${name}.vvp"
|
|
local scenario_lower="$name"
|
|
|
|
printf " %-45s " "MF Co-Sim ($name)"
|
|
|
|
# Compile — build command as string to handle optional define
|
|
local cmd="iverilog -g2001 -DSIMULATION"
|
|
if [[ -n "$define" ]]; then
|
|
cmd="$cmd $define"
|
|
fi
|
|
cmd="$cmd -o $vvp tb/tb_mf_cosim.v matched_filter_processing_chain.v fft_engine.v xfft_2048.v fft_engine_axi_bridge.v frequency_matched_filter.v chirp_memory_loader_param.v"
|
|
|
|
if ! eval "$cmd" 2>/tmp/iverilog_err_$$; then
|
|
echo -e "${RED}COMPILE FAIL${NC}"
|
|
ERRORS="$ERRORS\n MF Co-Sim ($name): compile error ($(head -1 /tmp/iverilog_err_$$))"
|
|
FAIL=$((FAIL + 1))
|
|
return
|
|
fi
|
|
|
|
# Run TB
|
|
local output
|
|
output=$(timeout 120 vvp "$vvp" 2>&1) || true
|
|
rm -f "$vvp"
|
|
|
|
# Check TB internal pass/fail
|
|
local tb_fail
|
|
tb_fail=$(echo "$output" | grep -Ec '^\[FAIL' || true)
|
|
if [[ "$tb_fail" -gt 0 ]]; then
|
|
echo -e "${RED}FAIL${NC} (TB internal failure)"
|
|
ERRORS="$ERRORS\n MF Co-Sim ($name): TB internal failure"
|
|
FAIL=$((FAIL + 1))
|
|
return
|
|
fi
|
|
|
|
# Run Python compare
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
local compare_out
|
|
local compare_rc=0
|
|
compare_out=$(python3 tb/cosim/compare_mf.py "$scenario_lower" 2>&1) || compare_rc=$?
|
|
if [[ "$compare_rc" -ne 0 ]]; then
|
|
echo -e "${RED}FAIL${NC} (compare_mf.py mismatch)"
|
|
ERRORS="$ERRORS\n MF Co-Sim ($name): Python compare failed"
|
|
FAIL=$((FAIL + 1))
|
|
return
|
|
fi
|
|
else
|
|
echo -e "${YELLOW}SKIP${NC} (RTL passed, python3 not found — compare skipped)"
|
|
SKIP=$((SKIP + 1))
|
|
return
|
|
fi
|
|
|
|
echo -e "${GREEN}PASS${NC} (RTL + Python compare)"
|
|
PASS=$((PASS + 1))
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper: compile, run, and compare a Doppler co-sim scenario
|
|
# run_doppler_cosim <scenario_name> <define_flag>
|
|
# ---------------------------------------------------------------------------
|
|
run_doppler_cosim() {
|
|
local name="$1"
|
|
local define="$2"
|
|
local vvp="tb/tb_doppler_cosim_${name}.vvp"
|
|
|
|
printf " %-45s " "Doppler Co-Sim ($name)"
|
|
|
|
# Compile — build command as string to handle optional define
|
|
local cmd="iverilog -g2001 -DSIMULATION"
|
|
if [[ -n "$define" ]]; then
|
|
cmd="$cmd $define"
|
|
fi
|
|
cmd="$cmd -o $vvp tb/tb_doppler_cosim.v doppler_processor.v xfft_16.v fft_engine.v"
|
|
|
|
if ! eval "$cmd" 2>/tmp/iverilog_err_$$; then
|
|
echo -e "${RED}COMPILE FAIL${NC}"
|
|
ERRORS="$ERRORS\n Doppler Co-Sim ($name): compile error ($(head -1 /tmp/iverilog_err_$$))"
|
|
FAIL=$((FAIL + 1))
|
|
return
|
|
fi
|
|
|
|
# Run TB
|
|
local output
|
|
output=$(timeout 120 vvp "$vvp" 2>&1) || true
|
|
rm -f "$vvp"
|
|
|
|
# Check TB internal pass/fail
|
|
local tb_fail
|
|
tb_fail=$(echo "$output" | grep -Ec '^\[FAIL' || true)
|
|
if [[ "$tb_fail" -gt 0 ]]; then
|
|
echo -e "${RED}FAIL${NC} (TB internal failure)"
|
|
ERRORS="$ERRORS\n Doppler Co-Sim ($name): TB internal failure"
|
|
FAIL=$((FAIL + 1))
|
|
return
|
|
fi
|
|
|
|
# Run Python compare
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
local compare_out
|
|
local compare_rc=0
|
|
compare_out=$(python3 tb/cosim/compare_doppler.py "$name" 2>&1) || compare_rc=$?
|
|
if [[ "$compare_rc" -ne 0 ]]; then
|
|
echo -e "${RED}FAIL${NC} (compare_doppler.py mismatch)"
|
|
ERRORS="$ERRORS\n Doppler Co-Sim ($name): Python compare failed"
|
|
FAIL=$((FAIL + 1))
|
|
return
|
|
fi
|
|
else
|
|
echo -e "${YELLOW}SKIP${NC} (RTL passed, python3 not found — compare skipped)"
|
|
SKIP=$((SKIP + 1))
|
|
return
|
|
fi
|
|
|
|
echo -e "${GREEN}PASS${NC} (RTL + Python compare)"
|
|
PASS=$((PASS + 1))
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper: compile and run a single testbench
|
|
# run_test <name> <vvp_path> <iverilog_args...>
|
|
# ---------------------------------------------------------------------------
|
|
run_test() {
|
|
# Optional: --timeout=N as first arg overrides default 120s
|
|
local timeout_secs=120
|
|
if [[ "$1" == --timeout=* ]]; then
|
|
timeout_secs="${1#--timeout=}"
|
|
shift
|
|
fi
|
|
|
|
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 "$timeout_secs" vvp "$vvp" 2>&1) || true
|
|
|
|
# Count PASS/FAIL in output (testbenches use explicit [PASS]/[FAIL] markers)
|
|
# Match `[PASS]` and `[PASS <digits>]` (and same for FAIL). Excludes
|
|
# informational tags like `[FAIL-INFO]` (used for known unrelated bugs,
|
|
# e.g. RX-NEW-1 fft_engine bin-shift in tb_matched_filter_processing_chain.v)
|
|
# which would otherwise false-fire as real failures.
|
|
local test_pass test_fail
|
|
test_pass=$(echo "$output" | grep -Ec '^\[PASS( [0-9]+)?\]' || true)
|
|
test_fail=$(echo "$output" | grep -Ec '^\[FAIL( [0-9]+)?\]' || 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 ""
|
|
|
|
# ===========================================================================
|
|
# PHASE 0: LINT (Vivado-class error detection)
|
|
# ===========================================================================
|
|
if [[ "$SKIP_LINT" -eq 0 ]]; then
|
|
echo "--- PHASE 0: LINT (Vivado-class checks) ---"
|
|
|
|
# Layer A: iverilog -Wall on full production design
|
|
run_lint_iverilog "production" "${PROD_RTL[@]}"
|
|
|
|
# Layer A: standalone modules not in top-level hierarchy
|
|
# (use ${EXTRA_RTL[@]+...} guard so empty array doesn't trip set -u)
|
|
if [[ ${#EXTRA_RTL[@]} -gt 0 ]]; then
|
|
for extra in "${EXTRA_RTL[@]}"; do
|
|
if [[ -f "$extra" ]]; then
|
|
run_lint_iverilog "$(basename "$extra" .v)" "$extra"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Layer B: custom static regex checks
|
|
if [[ ${#EXTRA_RTL[@]} -gt 0 ]]; then
|
|
ALL_RTL=("${PROD_RTL[@]}" "${EXTRA_RTL[@]}")
|
|
else
|
|
ALL_RTL=("${PROD_RTL[@]}")
|
|
fi
|
|
run_lint_static "${ALL_RTL[@]}"
|
|
|
|
echo ""
|
|
if [[ "$LINT_ERR" -gt 0 ]]; then
|
|
echo -e "${RED} LINT FAILED: $LINT_ERR Vivado-class error(s) detected.${NC}"
|
|
echo " Fix lint errors before pushing to Vivado. Aborting regression."
|
|
echo ""
|
|
exit 1
|
|
elif [[ "$LINT_WARN" -gt 0 ]]; then
|
|
echo -e "${YELLOW} LINT: $LINT_WARN advisory warning(s) (non-blocking)${NC}"
|
|
else
|
|
echo -e "${GREEN} LINT: All checks passed${NC}"
|
|
fi
|
|
echo ""
|
|
else
|
|
echo "--- PHASE 0: LINT (skipped via --skip-lint) ---"
|
|
echo ""
|
|
fi
|
|
|
|
# ===========================================================================
|
|
# PHASE 1: UNIT TESTS — Changed Modules (HIGH PRIORITY)
|
|
# ===========================================================================
|
|
echo "--- PHASE 1: 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_doppler_cosim "stationary" ""
|
|
run_doppler_cosim "moving" "-DSCENARIO_MOVING"
|
|
run_doppler_cosim "two_targets" "-DSCENARIO_TWO"
|
|
|
|
run_test "Threshold Detector (detection bugs)" \
|
|
tb/tb_threshold_detector.vvp \
|
|
tb/tb_threshold_detector.v
|
|
|
|
run_test "RX Gain Control (digital gain)" \
|
|
tb/tb_rx_gain_control.vvp \
|
|
tb/tb_rx_gain_control.v rx_gain_control.v
|
|
|
|
run_test "MTI Canceller (ground clutter)" \
|
|
tb/tb_mti_canceller.vvp \
|
|
tb/tb_mti_canceller.v mti_canceller.v
|
|
|
|
run_test "CFAR CA Detector" \
|
|
tb/tb_cfar_ca.vvp \
|
|
tb/tb_cfar_ca.v cfar_ca.v
|
|
|
|
run_test "FPGA Self-Test" \
|
|
tb/tb_fpga_self_test.vvp \
|
|
tb/tb_fpga_self_test.v fpga_self_test.v
|
|
|
|
run_test "FFT AXI Bridge tready handshake (AUDIT-C10)" \
|
|
tb/tb_fft_engine_axi_bridge.vvp \
|
|
tb/tb_fft_engine_axi_bridge.v fft_engine_axi_bridge.v
|
|
|
|
echo ""
|
|
|
|
# ===========================================================================
|
|
# PHASE 2: INTEGRATION TESTS
|
|
# ===========================================================================
|
|
echo "--- PHASE 2: 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
|
|
|
|
# Real-data co-simulation: committed golden hex vs RTL (exact match required).
|
|
# These catch architecture mismatches (e.g. 32-pt → dual 16-pt Doppler FFT)
|
|
# that self-blessing golden-generate/compare tests cannot detect.
|
|
run_test "Doppler Real-Data (ADI CN0566, exact match)" \
|
|
tb/tb_doppler_realdata.vvp \
|
|
tb/tb_doppler_realdata.v doppler_processor.v xfft_16.v fft_engine.v
|
|
|
|
run_test "Full-Chain Real-Data (decim→Doppler, exact match)" \
|
|
tb/tb_fullchain_realdata.vvp \
|
|
tb/tb_fullchain_realdata.v range_bin_decimator.v \
|
|
doppler_processor.v xfft_16.v fft_engine.v
|
|
|
|
if [[ "$QUICK" -eq 0 ]]; then
|
|
# Receiver integration (structural + bounds + pulse assertions).
|
|
# Replaces the earlier "Receiver golden generate/compare" pair, which was
|
|
# self-blessing (both passes ran identical RTL on identical stimulus, so
|
|
# it passed regardless of bugs). Real co-sim coverage is now provided by
|
|
# tb_doppler_realdata.v and tb_fullchain_realdata.v (Python goldens,
|
|
# exact match); this integration test exercises the full RX pipeline
|
|
# (ADC stub → DDC → MF → Decim → Doppler) and verifies that
|
|
# doppler_frame_done is a single-cycle pulse at module boundaries.
|
|
run_test --timeout=600 "Receiver Integration (tb_radar_receiver_final)" \
|
|
tb/tb_rx_final_reg.vvp \
|
|
tb/tb_radar_receiver_final.v "${RECEIVER_RTL[@]}"
|
|
|
|
# Full system top (monitoring-only, legacy)
|
|
run_test "System Top (radar_system_tb)" \
|
|
tb/tb_system_reg.vvp \
|
|
tb/radar_system_tb.v "${SYSTEM_RTL[@]}"
|
|
|
|
# E2E integration (46 strict checks: TX, RX, USB R/W, CDC, safety, reset)
|
|
run_test "System E2E (tb_system_e2e)" \
|
|
tb/tb_system_e2e_reg.vvp \
|
|
tb/tb_system_e2e.v "${SYSTEM_RTL[@]}"
|
|
|
|
# USB_MODE=1 (FT2232H production) variants of system tests
|
|
run_test "System Top USB_MODE=1 (FT2232H)" \
|
|
tb/tb_system_ft2232h_reg.vvp \
|
|
-DUSB_MODE_1 \
|
|
tb/radar_system_tb.v "${SYSTEM_RTL[@]}"
|
|
|
|
run_test "System E2E USB_MODE=1 (FT2232H)" \
|
|
tb/tb_system_e2e_ft2232h_reg.vvp \
|
|
-DUSB_MODE_1 \
|
|
tb/tb_system_e2e.v "${SYSTEM_RTL[@]}"
|
|
else
|
|
echo " (skipped receiver integration + system top + E2E + USB_MODE=1 variants — use without --quick)"
|
|
SKIP=$((SKIP + 5))
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# ===========================================================================
|
|
# PHASE 2b: MATCHED FILTER CO-SIMULATION (RTL vs Python golden reference)
|
|
# Runs tb_mf_cosim.v for 4 scenarios, then compare_mf.py validates output
|
|
# against committed Python golden CSV files. In SIMULATION mode, thresholds
|
|
# are generous (behavioral vs fixed-point twiddles differ) — validates
|
|
# state machine mechanics, output count, and energy sanity.
|
|
# ===========================================================================
|
|
echo "--- PHASE 2b: Matched Filter Co-Sim ---"
|
|
|
|
run_mf_cosim "chirp" ""
|
|
run_mf_cosim "dc" "-DSCENARIO_DC"
|
|
run_mf_cosim "impulse" "-DSCENARIO_IMPULSE"
|
|
run_mf_cosim "tone5" "-DSCENARIO_TONE5"
|
|
|
|
echo ""
|
|
|
|
# ===========================================================================
|
|
# PHASE 3: UNIT TESTS — Signal Processing
|
|
# ===========================================================================
|
|
echo "--- PHASE 3: Signal Processing ---"
|
|
|
|
run_test "FFT Engine" \
|
|
tb/tb_fft_reg.vvp \
|
|
tb/tb_fft_engine.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 --timeout=600 "Matched Filter Chain" \
|
|
tb/tb_mf_reg.vvp \
|
|
tb/tb_matched_filter_processing_chain.v matched_filter_processing_chain.v \
|
|
fft_engine.v xfft_2048.v fft_engine_axi_bridge.v \
|
|
chirp_memory_loader_param.v frequency_matched_filter.v
|
|
|
|
# RX-B regression coverage: chain pipeline depth + full-chain
|
|
# autocorrelation peak position. Both run the production fft_engine
|
|
# (no SIMULATION-only behavioural FFT exists). Long-running because
|
|
# the production FFT is BRAM-pipelined (~153k cycles per chain pass).
|
|
run_test --timeout=120 "RX-B Chain Pipeline Latency (tb_rxb_latency_measure)" \
|
|
tb/tb_rxb_lat_reg.vvp \
|
|
tb/tb_rxb_latency_measure.v matched_filter_processing_chain.v \
|
|
fft_engine.v xfft_2048.v fft_engine_axi_bridge.v frequency_matched_filter.v
|
|
|
|
run_test --timeout=600 "RX-B Full-Chain Autocorrelation (tb_rxb_fullchain_latency)" \
|
|
tb/tb_rxb_fc_reg.vvp \
|
|
tb/tb_rxb_fullchain_latency.v matched_filter_multi_segment.v \
|
|
matched_filter_processing_chain.v fft_engine.v xfft_2048.v \
|
|
fft_engine_axi_bridge.v frequency_matched_filter.v \
|
|
chirp_memory_loader_param.v
|
|
|
|
echo ""
|
|
|
|
# ===========================================================================
|
|
# PHASE 4: UNIT TESTS — Infrastructure
|
|
# ===========================================================================
|
|
echo "--- PHASE 4: 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"
|
|
echo "============================================"
|
|
if [[ "$SKIP_LINT" -eq 0 ]]; then
|
|
if [[ "$LINT_ERR" -gt 0 ]]; then
|
|
echo -e " Lint: ${RED}$LINT_ERR error(s)${NC}, $LINT_WARN warning(s)"
|
|
elif [[ "$LINT_WARN" -gt 0 ]]; then
|
|
echo -e " Lint: ${GREEN}0 errors${NC}, ${YELLOW}$LINT_WARN warning(s)${NC}"
|
|
else
|
|
echo -e " Lint: ${GREEN}clean${NC}"
|
|
fi
|
|
fi
|
|
echo " Tests: $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
|