mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-09 15:07:14 +00:00
AUDIT-S19/S20/S21: replace fpga_self_test tautologies with real arithmetic
Pre-fix Tests 1/2/4 in fpga_self_test.v gave false PASS even on broken
silicon:
S-19 Test 1 (CIC): `result_flags[1] <= 1'b1` unconditional, comment
admitted "always true for simple check".
S-20 Test 2 (FFT): `(16'sd100+16'sd100 == 16'sd200) && (...)` —
both predicates compile-time-fold to 1; synth reduces to a
constant write.
S-21 Test 4 (ADC): PASS once N samples land, regardless of value.
A stuck-at-0 / stuck-at-MAX / dead LVDS link still PASSed
provided adc_valid_in toggled.
Fixes:
Test 1: drive impulse {5,0,0,0,0,0,0} through registered integrator
y[n]=y[n-1]+x[n]; require accumulator==5 after step
response. Real adder + register path; sign-extension
exercised. Detail = 0xC1 on fail.
Test 2: real radix-2 butterfly with twiddle multiply across 4 FSM
states. A=8, B=4 (real), W=2+3j -> WB=(8,12), A'=(16,12),
B'=(0,-12). Forces synth to instantiate signed multiplier
(DSP slice) + 17-bit signed add/sub. Detail = 0xF2 on fail.
Test 4: track min/max across 256-sample capture, require
(max - min) > ADC_RANGE_THRESHOLD (10 LSB). Catches stuck-at
faults. Does NOT distinguish AD9484 format mismatches
(audit's per-mode mean check requires SPI, impossible per
AUDIT-C13). Detail = 0xAD on fail.
Tests:
- tb_fpga_self_test.v existing Group 1-4 (16 PASS) still pass: varied
ADC counter input gives range >> 10.
- New Group 5: drive constant 0 -> expect Test 4 FAIL + detail=0xAD.
- New Group 6: drive constant 0x7FFF -> expect Test 4 FAIL + detail=0xAD.
- Regression: 41/41 PASS; fpga_self_test 22/22 (was 16/16).
This commit is contained in:
@@ -119,6 +119,21 @@ localparam ADC_CAP_SAMPLES = 256; // Number of raw ADC samples to capture
|
||||
reg [BRAM_AW-1:0] bram_rd_addr_d;
|
||||
reg bram_rd_valid;
|
||||
|
||||
// ============================================================================
|
||||
// AUDIT-S19/S20/S21: real Test 1/2/4 state (replaces pre-fix tautologies)
|
||||
// ============================================================================
|
||||
// Test 1 (CIC integrator impulse response)
|
||||
reg signed [31:0] cic_accum;
|
||||
reg signed [15:0] cic_input;
|
||||
// Test 2 (radix-2 butterfly with complex twiddle)
|
||||
reg signed [15:0] fft_a_re, fft_b_re, fft_w_re, fft_w_im;
|
||||
reg signed [31:0] fft_wb_re, fft_wb_im;
|
||||
reg signed [16:0] fft_aprime_re, fft_aprime_im;
|
||||
reg signed [16:0] fft_bprime_re, fft_bprime_im;
|
||||
// Test 4 (ADC activity check — min/max over capture window)
|
||||
reg signed [15:0] adc_min, adc_max;
|
||||
localparam signed [15:0] ADC_RANGE_THRESHOLD = 16'sd10;
|
||||
|
||||
// ============================================================================
|
||||
// Main FSM
|
||||
// ============================================================================
|
||||
@@ -140,6 +155,21 @@ always @(posedge clk or negedge reset_n) begin
|
||||
adc_cap_cnt <= 0;
|
||||
bram_rd_addr_d <= 0;
|
||||
bram_rd_valid <= 1'b0;
|
||||
// AUDIT-S19/S20/S21
|
||||
cic_accum <= 32'sd0;
|
||||
cic_input <= 16'sd0;
|
||||
fft_a_re <= 16'sd0;
|
||||
fft_b_re <= 16'sd0;
|
||||
fft_w_re <= 16'sd0;
|
||||
fft_w_im <= 16'sd0;
|
||||
fft_wb_re <= 32'sd0;
|
||||
fft_wb_im <= 32'sd0;
|
||||
fft_aprime_re <= 17'sd0;
|
||||
fft_aprime_im <= 17'sd0;
|
||||
fft_bprime_re <= 17'sd0;
|
||||
fft_bprime_im <= 17'sd0;
|
||||
adc_min <= 16'sd0;
|
||||
adc_max <= 16'sd0;
|
||||
end else begin
|
||||
// Default one-shot signals
|
||||
result_valid <= 1'b0;
|
||||
@@ -211,43 +241,81 @@ always @(posedge clk or negedge reset_n) begin
|
||||
end
|
||||
|
||||
// ============================================================
|
||||
// Test 1: CIC Impulse Response (simplified)
|
||||
// Test 1: CIC integrator impulse response (AUDIT-S19 fix)
|
||||
// ============================================================
|
||||
// We don't instantiate a full CIC here — instead we verify
|
||||
// the integrator/comb arithmetic that the CIC uses.
|
||||
// A 4-stage integrator with input {1,0,0,0,...} should produce
|
||||
// {1,1,1,1,...} at the integrator output.
|
||||
// Pre-fix this state set `result_flags[1] <= 1'b1` unconditionally
|
||||
// ("always true for simple check") so a broken integrator path on
|
||||
// the silicon would still PASS. Now drives a real impulse {5,0,0,...}
|
||||
// through y[n] = y[n-1] + x[n] and checks the registered accumulator
|
||||
// value at the end. Catches stuck-at, broken adder, or sign-extension
|
||||
// bug in the arithmetic path.
|
||||
ST_CIC_SETUP: begin
|
||||
// Simulate 4-tap running sum: impulse → step response
|
||||
// After 4 cycles of input 0 following a 1, accumulator = 1
|
||||
// This tests the core accumulation logic.
|
||||
// We use step_cnt as a simple state tracker.
|
||||
if (step_cnt < 8) begin
|
||||
step_cnt <= step_cnt + 1;
|
||||
if (step_cnt == 0) begin
|
||||
cic_accum <= 32'sd0;
|
||||
cic_input <= 16'sd5; // impulse value
|
||||
step_cnt <= 1;
|
||||
end else if (step_cnt < 8) begin
|
||||
cic_accum <= cic_accum +
|
||||
{{16{cic_input[15]}}, cic_input}; // sign-extend
|
||||
cic_input <= 16'sd0; // zero-pad after impulse
|
||||
step_cnt <= step_cnt + 1;
|
||||
end else begin
|
||||
// CIC test: pass if arithmetic is correct (always true for simple check)
|
||||
result_flags[1] <= 1'b1;
|
||||
state <= ST_FFT_SETUP;
|
||||
// After impulse + 6 zeros, integrator holds at 5 (step response)
|
||||
if (cic_accum == 32'sd5) begin
|
||||
result_flags[1] <= 1'b1;
|
||||
end else begin
|
||||
result_flags[1] <= 1'b0;
|
||||
result_detail <= 8'hC1; // CIC fail marker
|
||||
end
|
||||
state <= ST_FFT_SETUP;
|
||||
step_cnt <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
// ============================================================
|
||||
// Test 2: FFT Known-Input (simplified)
|
||||
// Test 2: Radix-2 butterfly with twiddle multiply (AUDIT-S20 fix)
|
||||
// ============================================================
|
||||
// Verify DC input produces energy in bin 0.
|
||||
// Full FFT instantiation is too heavy for self-test — instead we
|
||||
// verify the butterfly computation: (A+B, A-B) with known values.
|
||||
// A=100, B=100 → sum=200, diff=0. This matches radix-2 butterfly.
|
||||
// Pre-fix this evaluated `(16'sd100+16'sd100 == 16'sd200) &&
|
||||
// (16'sd100-16'sd100 == 16'sd0)` — both predicates compile-time-fold
|
||||
// to 1'b1, so synth reduces the whole test to `result_flags[2] <= 1'b1`.
|
||||
// Replaced with a real radix-2 butterfly that exercises signed
|
||||
// multiplications + adds across multiple FSM states with register
|
||||
// dataflow (synth must instantiate DSP/multiplier resources).
|
||||
//
|
||||
// Inputs: A = 8 (real), B = 4 (real), W = 2 + 3j
|
||||
// Step 1: WB = W*B (with B_im=0, so only 2 mults)
|
||||
// WB_re = W_re * B_re = 2 * 4 = 8
|
||||
// WB_im = W_im * B_re = 3 * 4 = 12
|
||||
// Step 2: Butterfly:
|
||||
// A' = A + WB = (8+8, 0+12) = (16, 12)
|
||||
// B' = A - WB = (8-8, 0-12) = (0, -12)
|
||||
// Step 3: Compare against golden.
|
||||
ST_FFT_SETUP: begin
|
||||
if (step_cnt < 4) begin
|
||||
step_cnt <= step_cnt + 1;
|
||||
if (step_cnt == 0) begin
|
||||
fft_a_re <= 16'sd8;
|
||||
fft_b_re <= 16'sd4;
|
||||
fft_w_re <= 16'sd2;
|
||||
fft_w_im <= 16'sd3;
|
||||
step_cnt <= 1;
|
||||
end else if (step_cnt == 1) begin
|
||||
fft_wb_re <= fft_w_re * fft_b_re; // 2*4 = 8
|
||||
fft_wb_im <= fft_w_im * fft_b_re; // 3*4 = 12
|
||||
step_cnt <= 2;
|
||||
end else if (step_cnt == 2) begin
|
||||
fft_aprime_re <= {fft_a_re[15], fft_a_re} + fft_wb_re[16:0];
|
||||
fft_aprime_im <= 17'sd0 + fft_wb_im[16:0];
|
||||
fft_bprime_re <= {fft_a_re[15], fft_a_re} - fft_wb_re[16:0];
|
||||
fft_bprime_im <= 17'sd0 - fft_wb_im[16:0];
|
||||
step_cnt <= 3;
|
||||
end else begin
|
||||
// Butterfly check: 100+100=200, 100-100=0
|
||||
// Both fit in 16-bit signed — PASS
|
||||
result_flags[2] <= (16'sd100 + 16'sd100 == 16'sd200) &&
|
||||
(16'sd100 - 16'sd100 == 16'sd0);
|
||||
state <= ST_ARITH;
|
||||
if (fft_aprime_re == 17'sd16 && fft_aprime_im == 17'sd12 &&
|
||||
fft_bprime_re == 17'sd0 && fft_bprime_im == -17'sd12) begin
|
||||
result_flags[2] <= 1'b1;
|
||||
end else begin
|
||||
result_flags[2] <= 1'b0;
|
||||
result_detail <= 8'hF2; // FFT fail marker
|
||||
end
|
||||
state <= ST_ARITH;
|
||||
step_cnt <= 0;
|
||||
end
|
||||
end
|
||||
@@ -281,19 +349,40 @@ always @(posedge clk or negedge reset_n) begin
|
||||
end
|
||||
|
||||
// ============================================================
|
||||
// Test 4: ADC Raw Data Capture
|
||||
// Test 4: ADC activity (min/max range) check (AUDIT-S21 fix)
|
||||
// ============================================================
|
||||
// Pre-fix this set `result_flags[4] <= 1'b1` once N samples were
|
||||
// observed, regardless of value. A stuck-at-0 ADC (broken LVDS link,
|
||||
// wrong AD9484 mode per AUDIT-C3, dead sample-and-hold) would still
|
||||
// PASS as long as adc_valid_in toggled. Now tracks min/max across the
|
||||
// capture window and requires range > ADC_RANGE_THRESHOLD (10 LSB).
|
||||
// Catches stuck-at faults; does NOT distinguish AD9484 format
|
||||
// mismatches (audit's per-mode mean check requires AD9484 SPI which
|
||||
// is impossible on production HW per AUDIT-C13).
|
||||
ST_ADC_CAP: begin
|
||||
capture_active <= 1'b1;
|
||||
if (adc_valid_in) begin
|
||||
capture_data <= adc_data_in;
|
||||
capture_valid <= 1'b1;
|
||||
adc_cap_cnt <= adc_cap_cnt + 1;
|
||||
// Activity tracking: seed min/max on first sample, then update
|
||||
if (adc_cap_cnt == 0) begin
|
||||
adc_min <= adc_data_in;
|
||||
adc_max <= adc_data_in;
|
||||
end else begin
|
||||
if ($signed(adc_data_in) < $signed(adc_min)) adc_min <= adc_data_in;
|
||||
if ($signed(adc_data_in) > $signed(adc_max)) adc_max <= adc_data_in;
|
||||
end
|
||||
adc_cap_cnt <= adc_cap_cnt + 1;
|
||||
if (adc_cap_cnt >= ADC_CAP_SAMPLES - 1) begin
|
||||
// ADC capture complete — PASS if we got samples
|
||||
result_flags[4] <= 1'b1;
|
||||
capture_active <= 1'b0;
|
||||
state <= ST_DONE;
|
||||
// PASS if observed range exceeds stuck-at threshold
|
||||
if (($signed(adc_max) - $signed(adc_min)) > ADC_RANGE_THRESHOLD) begin
|
||||
result_flags[4] <= 1'b1;
|
||||
end else begin
|
||||
result_flags[4] <= 1'b0;
|
||||
result_detail <= 8'hAD; // stuck-at / no-activity marker
|
||||
end
|
||||
capture_active <= 1'b0;
|
||||
state <= ST_DONE;
|
||||
end
|
||||
end
|
||||
// Timeout: if no ADC data after 1000 cycles (10 us @ 100 MHz), FAIL
|
||||
|
||||
@@ -77,8 +77,12 @@ task check;
|
||||
end
|
||||
endtask
|
||||
|
||||
// ADC data generator: provides synthetic samples when capture is active
|
||||
// ADC data generator: provides synthetic samples when capture is active.
|
||||
// `adc_stuck_mode` (driven from main test) forces every sample to a constant
|
||||
// value, exercising the AUDIT-S21 stuck-at detection path.
|
||||
reg [15:0] adc_sample_cnt;
|
||||
reg adc_stuck_mode; // 1 = drive constant adc_stuck_value
|
||||
reg [15:0] adc_stuck_value;
|
||||
always @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
adc_data_in <= 16'd0;
|
||||
@@ -89,7 +93,8 @@ always @(posedge clk or negedge reset_n) begin
|
||||
// Provide a new ADC sample every 4 cycles (simulating 25 MHz sample rate)
|
||||
adc_sample_cnt <= adc_sample_cnt + 1;
|
||||
if (adc_sample_cnt[1:0] == 2'b11) begin
|
||||
adc_data_in <= adc_sample_cnt[15:0];
|
||||
adc_data_in <= adc_stuck_mode ? adc_stuck_value
|
||||
: adc_sample_cnt[15:0];
|
||||
adc_valid_in <= 1'b1;
|
||||
end else begin
|
||||
adc_valid_in <= 1'b0;
|
||||
@@ -123,7 +128,9 @@ initial begin
|
||||
pass_count = 0;
|
||||
fail_count = 0;
|
||||
|
||||
trigger = 0;
|
||||
trigger = 0;
|
||||
adc_stuck_mode = 1'b0;
|
||||
adc_stuck_value = 16'd0;
|
||||
|
||||
$display("");
|
||||
$display("============================================================");
|
||||
@@ -221,6 +228,68 @@ initial begin
|
||||
check("Re-trigger completes", result_valid);
|
||||
check("All pass on re-run", result_flags == 5'b11111);
|
||||
|
||||
// =====================================================================
|
||||
// Group 5: AUDIT-S21 — stuck-at ADC must FAIL Test 4
|
||||
// =====================================================================
|
||||
// Pre-fix Test 4 set result_flags[4] <= 1'b1 once N samples landed,
|
||||
// regardless of value. Drive a constant ADC sample (stuck-at-0) and
|
||||
// verify Test 4 now FAILs with result_detail == 0xAD.
|
||||
$display("");
|
||||
$display("--- Group 5: ADC Stuck-At Detection (AUDIT-S21) ---");
|
||||
|
||||
adc_stuck_mode = 1'b1;
|
||||
adc_stuck_value = 16'd0;
|
||||
repeat (10) @(posedge clk);
|
||||
@(posedge clk);
|
||||
trigger = 1;
|
||||
@(posedge clk);
|
||||
trigger = 0;
|
||||
|
||||
begin : wait_for_done3
|
||||
integer i;
|
||||
for (i = 0; i < 5000; i = i + 1) begin
|
||||
@(posedge clk);
|
||||
if (result_valid) begin
|
||||
i = 5000;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check("Stuck ADC: result_valid", result_valid);
|
||||
check("Stuck ADC: Tests 0-3 pass", result_flags[3:0] == 4'b1111);
|
||||
check("Stuck ADC: Test 4 FAILS", !result_flags[4]);
|
||||
check("Stuck ADC: detail=0xAD", result_detail == 8'hAD);
|
||||
|
||||
// =====================================================================
|
||||
// Group 6: AUDIT-S21 — stuck-at-MAX (different stuck value) must also FAIL
|
||||
// =====================================================================
|
||||
$display("");
|
||||
$display("--- Group 6: ADC Stuck-At-MAX Detection (AUDIT-S21) ---");
|
||||
|
||||
adc_stuck_mode = 1'b1;
|
||||
adc_stuck_value = 16'h7FFF; // stuck high
|
||||
repeat (10) @(posedge clk);
|
||||
@(posedge clk);
|
||||
trigger = 1;
|
||||
@(posedge clk);
|
||||
trigger = 0;
|
||||
|
||||
begin : wait_for_done4
|
||||
integer i;
|
||||
for (i = 0; i < 5000; i = i + 1) begin
|
||||
@(posedge clk);
|
||||
if (result_valid) begin
|
||||
i = 5000;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check("Stuck-MAX: Test 4 FAILS", !result_flags[4]);
|
||||
check("Stuck-MAX: detail=0xAD", result_detail == 8'hAD);
|
||||
|
||||
// Restore varied-sample mode
|
||||
adc_stuck_mode = 1'b0;
|
||||
|
||||
// =====================================================================
|
||||
// Summary
|
||||
// =====================================================================
|
||||
|
||||
Reference in New Issue
Block a user