mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-08 22:47:16 +00:00
ci(fpga): PR-M.3 — wire T-6 drift cosim into regression + CI deps
Adds the T-6 independent reference drift cosim (PR-M.1, c30be89) as a
gated regression check so any future hand-edit drift in NCO_SINE_LUT,
fft_twiddle_*.mem, or DOPPLER_WINDOW_COEFF surfaces on every run.
run_regression.sh: new "Independent Reference Drift (T-6)" check after
the RX-B autocorrelation block in Phase 3. Plain `python3` (no path
sniffing). Distinguishes three states from the script's exit code +
markers:
rc=0, PASS markers -> PASS (counts toward `passed`)
rc=2, no markers -> SKIP (counts toward `skipped`)
rc!=0, FAIL markers -> FAIL (gates the regression)
compare_independent.py: detects missing numpy/scipy at startup and exits
with code 2 plus a [SKIP] marker pointing at `uv sync --group dev`.
Without that, an environment without scipy crashed mid-script and the
regression captured a partial 3-of-13 PASS count.
pyproject.toml: scipy>=1.13 added to the dev dependency group (used by
fpga_reference.doppler_window_ideal() for analytical Cheby ground truth).
.github/workflows/ci-tests.yml: fpga-regression now installs Python
3.12, sets up uv, runs `uv sync --group dev`, and activates the
resulting .venv before bash run_regression.sh. Without the activate
line the runner's system python3 (no scipy) would resolve first and
the drift check would [SKIP] in CI.
Verified locally:
with venv: Drift PASS (13 checks), Tests: 43 passed / 0 / 0
no scipy: Drift SKIP (msg points at install cmd), 42p / 0f / 1s
This commit is contained in:
@@ -69,7 +69,10 @@ jobs:
|
||||
working-directory: 9_Firmware/9_1_Microcontroller/tests
|
||||
|
||||
# ===========================================================================
|
||||
# FPGA RTL Regression (25 testbenches + lint)
|
||||
# FPGA RTL Regression (testbenches + lint + cosim helpers)
|
||||
# Python deps (numpy, scipy) come from the dev group in pyproject.toml
|
||||
# and are installed via uv so the run_regression.sh cosim and T-6 drift
|
||||
# check have everything they need.
|
||||
# ===========================================================================
|
||||
fpga-regression:
|
||||
name: FPGA Regression
|
||||
@@ -78,11 +81,26 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Install Python cosim deps (numpy + scipy)
|
||||
run: uv sync --group dev
|
||||
|
||||
- name: Install Icarus Verilog
|
||||
run: sudo apt-get update && sudo apt-get install -y iverilog
|
||||
|
||||
- name: Run full FPGA regression
|
||||
run: bash run_regression.sh
|
||||
# uv-managed venv lives at ./.venv; activate so run_regression.sh's
|
||||
# plain `python3` resolves to it. Without this the cosim helpers
|
||||
# would fall back to the runner's system python (no scipy) and the
|
||||
# T-6 drift check would emit [SKIP] instead of running.
|
||||
run: |
|
||||
source ../../.venv/bin/activate
|
||||
bash run_regression.sh
|
||||
working-directory: 9_Firmware/9_2_FPGA
|
||||
|
||||
# ===========================================================================
|
||||
|
||||
@@ -708,6 +708,52 @@ run_test --timeout=600 "RX-B Full-Chain Autocorrelation (tb_rxb_fullchain_latenc
|
||||
fft_engine_axi_bridge.v frequency_matched_filter.v \
|
||||
chirp_reference_rom.v
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# T-6 independent reference drift cosim (PR-M).
|
||||
# Bytewise spot-checks of NCO_SINE_LUT, fft_twiddle_{16,2048}.mem, and
|
||||
# DOPPLER_WINDOW_COEFF against analytical Q15 values, plus end-to-end peak
|
||||
# and roundtrip invariants for NCO / FFT / MF / Doppler. Catches the bug
|
||||
# class where a transcription error exists identically in both fpga_model.py
|
||||
# (RTL-mirroring twin) and the RTL — which the bit-exact cosim cannot detect
|
||||
# because both sides of that comparison are computing the same wrong values.
|
||||
# Pure Python (numpy + scipy), ~1 s wall, no iverilog compile.
|
||||
#
|
||||
# Required deps: numpy, scipy (declared in pyproject.toml dev group).
|
||||
# Install with: uv sync --group dev (from repo root)
|
||||
# CI handles this in the fpga-regression job; locally activate the
|
||||
# resulting .venv (or use `uv run bash run_regression.sh`).
|
||||
# If a dep is missing the script emits a [SKIP] marker and exits with
|
||||
# code 2; the regression treats it as SKIP rather than FAIL so the
|
||||
# missing-dep state is visible without breaking the gate.
|
||||
# ---------------------------------------------------------------------------
|
||||
printf " %-46s" "Independent Reference Drift (T-6)"
|
||||
set +e
|
||||
drift_output=$(python3 tb/cosim/compare_independent.py 2>&1)
|
||||
drift_rc=$?
|
||||
set -e
|
||||
drift_pass=$(echo "$drift_output" | grep -Ec '^[[:space:]]*\[PASS\]' || true)
|
||||
drift_fail=$(echo "$drift_output" | grep -Ec '^[[:space:]]*\[FAIL\]' || true)
|
||||
if [[ "$drift_rc" -eq 2 ]]; then
|
||||
# Script signalled missing-dep skip. Show its message body so the
|
||||
# operator knows which package to install.
|
||||
echo -e "${YELLOW}SKIP${NC} (missing python dep — see below)"
|
||||
echo "$drift_output" | sed 's/^/ /'
|
||||
SKIP=$((SKIP + 1))
|
||||
elif [[ "$drift_fail" -gt 0 ]]; then
|
||||
echo -e "${RED}FAIL${NC} (pass=$drift_pass, fail=$drift_fail)"
|
||||
ERRORS="$ERRORS\n Independent Reference Drift: $drift_fail failure(s)"
|
||||
echo "$drift_output" | sed 's/^/ /'
|
||||
FAIL=$((FAIL + 1))
|
||||
elif [[ "$drift_pass" -gt 0 && "$drift_rc" -eq 0 ]]; then
|
||||
echo -e "${GREEN}PASS${NC} ($drift_pass checks)"
|
||||
PASS=$((PASS + 1))
|
||||
else
|
||||
echo -e "${RED}FAIL${NC} (rc=$drift_rc, no clean PASS/FAIL markers)"
|
||||
ERRORS="$ERRORS\n Independent Reference Drift: rc=$drift_rc"
|
||||
echo "$drift_output" | sed 's/^/ /'
|
||||
FAIL=$((FAIL + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ===========================================================================
|
||||
|
||||
@@ -35,7 +35,28 @@ import math
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
# Required: numpy + scipy. If either is missing, exit code 2 with a [SKIP]
|
||||
# marker so the regression can distinguish missing-deps from real failures
|
||||
# (see run_regression.sh "Independent Reference Drift (T-6)" block).
|
||||
_MISSING = []
|
||||
try:
|
||||
import numpy as np # noqa: F401
|
||||
except ImportError:
|
||||
_MISSING.append("numpy")
|
||||
try:
|
||||
import scipy.signal.windows # noqa: F401
|
||||
except ImportError:
|
||||
_MISSING.append("scipy")
|
||||
if _MISSING:
|
||||
print(
|
||||
" [SKIP] T-6 drift cosim requires Python packages: "
|
||||
f"{', '.join(_MISSING)}.\n"
|
||||
" Install with: uv sync --group dev (from repo root)\n"
|
||||
" or: pip install numpy scipy"
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
import numpy as np # re-import to get module binding now that we know it's there
|
||||
|
||||
# Make local imports work when invoked from anywhere
|
||||
THIS_DIR = Path(__file__).resolve().parent
|
||||
|
||||
@@ -13,6 +13,7 @@ dev = [
|
||||
"ruff>=0.5",
|
||||
"pytest>=8",
|
||||
"numpy>=1.26",
|
||||
"scipy>=1.13", # PR-M: tb/cosim/compare_independent.py (T-6 drift cosim)
|
||||
"h5py>=3.10",
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user