E2E integration test + RTL fixes: mixer sequencing, USB data-pending flags, receiver toggle wiring (19/19 FPGA)
RTL fixes discovered via new end-to-end testbench: - plfm_chirp_controller: TX/RX mixer enables now mutually exclusive by FSM state (Fix #4), preventing simultaneous TX+RX activation - usb_data_interface: stream control reset default 3'b001 (range-only), added doppler/cfar data_pending sticky flags, write FSM triggers on range_valid only — eliminates startup deadlock (Fix #5) - radar_receiver_final: STM32 toggle signals wired through for mode-00 pass-through, dynamic frame detection via host_chirps_per_elev - radar_system_top: STM32 toggle signal wiring to receiver instance - chirp_memory_loader_param: explicit readmemh range for short chirp Test infrastructure: - New tb_system_e2e.v: 46 checks across 12 groups (reset, TX, safety, RX, USB R/W, CDC, beam scanning, reset recovery, stream control, latency budgets, watchdog) - tb_usb_data_interface: Tests 21/22/56 updated for data_pending architecture (preload flags, verify consumption instead of state) - tb_chirp_controller: mixer tests T7.1/T7.2 updated for Fix #4 - run_regression.sh: PASS/FAIL regex fixed to match only [PASS]/[FAIL] markers, added E2E test entry - Updated rx_final_doppler_out.csv golden data
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -431,13 +431,14 @@ initial begin
|
||||
// =====================================================================
|
||||
$display("--- Group 7: Mixer Control Outputs ---");
|
||||
|
||||
// T7.1: rx_mixer_en follows mixers_enable
|
||||
// T7.1: In IDLE state, both mixers are off even with mixers_enable=1
|
||||
// (Fix #4: mixers are state-dependent, not tied to mixers_enable directly)
|
||||
mixers_enable = 1;
|
||||
#1;
|
||||
check("rx_mixer_en follows mixers_enable", rx_mixer_en == 1'b1);
|
||||
check("rx_mixer_en off in IDLE (state-dependent)", rx_mixer_en == 1'b0);
|
||||
|
||||
// T7.2: tx_mixer_en follows mixers_enable
|
||||
check("tx_mixer_en follows mixers_enable", tx_mixer_en == 1'b1);
|
||||
// T7.2: tx_mixer_en also off in IDLE
|
||||
check("tx_mixer_en off in IDLE (state-dependent)", tx_mixer_en == 1'b0);
|
||||
|
||||
// T7.3: ADAR load pins tied low
|
||||
check("ADAR load pins: adar_tx_load_1 is 0", adar_tx_load_1 == 1'b0);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -180,7 +180,10 @@ module tb_usb_data_interface;
|
||||
status_chirps_per_elev = 6'd32;
|
||||
repeat (6) @(posedge ft601_clk_in);
|
||||
reset_n = 1;
|
||||
repeat (2) @(posedge ft601_clk_in);
|
||||
// Wait enough cycles for stream_control CDC to propagate
|
||||
// (DUT resets stream_ctrl_sync to 3'b001; TB sets stream_control=3'b111
|
||||
// which needs 2-stage sync + 1 cycle = 4+ ft601_clk cycles)
|
||||
repeat (6) @(posedge ft601_clk_in);
|
||||
end
|
||||
endtask
|
||||
|
||||
@@ -242,6 +245,37 @@ module tb_usb_data_interface;
|
||||
end
|
||||
endtask
|
||||
|
||||
// Set data_pending flags directly via hierarchical access.
|
||||
// This is the standard TB technique for internal state setup —
|
||||
// bypasses the CDC path for immediate, reliable flag setting.
|
||||
// Call BEFORE assert_range_valid in tests that need SEND_DOPPLER/DETECT.
|
||||
task preload_pending_data;
|
||||
begin
|
||||
@(posedge ft601_clk_in);
|
||||
uut.doppler_data_pending = 1'b1;
|
||||
uut.cfar_data_pending = 1'b1;
|
||||
@(posedge ft601_clk_in);
|
||||
end
|
||||
endtask
|
||||
|
||||
// Set only doppler pending (no cfar)
|
||||
task preload_doppler_pending;
|
||||
begin
|
||||
@(posedge ft601_clk_in);
|
||||
uut.doppler_data_pending = 1'b1;
|
||||
@(posedge ft601_clk_in);
|
||||
end
|
||||
endtask
|
||||
|
||||
// Set only cfar pending (no doppler)
|
||||
task preload_cfar_pending;
|
||||
begin
|
||||
@(posedge ft601_clk_in);
|
||||
uut.cfar_data_pending = 1'b1;
|
||||
@(posedge ft601_clk_in);
|
||||
end
|
||||
endtask
|
||||
|
||||
// ── Helper: wait for read FSM to reach a specific state ───
|
||||
task wait_for_read_state;
|
||||
input [2:0] target;
|
||||
@@ -296,6 +330,8 @@ module tb_usb_data_interface;
|
||||
input [15:0] di;
|
||||
input det;
|
||||
begin
|
||||
// Pre-load pending flags so FSM enters doppler/cfar states
|
||||
preload_pending_data;
|
||||
assert_range_valid(rng);
|
||||
wait_for_state(S_SEND_DOPPLER, 100);
|
||||
pulse_doppler_once(dr, di);
|
||||
@@ -356,6 +392,7 @@ module tb_usb_data_interface;
|
||||
|
||||
// Stall at SEND_HEADER so we can verify first range word later
|
||||
ft601_txe = 1;
|
||||
preload_pending_data;
|
||||
assert_range_valid(32'hDEAD_BEEF);
|
||||
wait_for_state(S_SEND_HEADER, 50);
|
||||
repeat (2) @(posedge ft601_clk_in); #1;
|
||||
@@ -430,13 +467,17 @@ module tb_usb_data_interface;
|
||||
apply_reset;
|
||||
ft601_txe = 0;
|
||||
|
||||
// Preload only doppler pending (not cfar) so the FSM sends
|
||||
// doppler data. After doppler, SEND_DETECT sees cfar_data_pending=0
|
||||
// and skips to SEND_FOOTER, then WAIT_ACK, then IDLE.
|
||||
preload_doppler_pending;
|
||||
assert_range_valid(32'h0000_0001);
|
||||
wait_for_state(S_SEND_DOPPLER, 100);
|
||||
#1;
|
||||
check(uut.current_state === S_SEND_DOPPLER,
|
||||
"Reached SEND_DOPPLER_DATA");
|
||||
|
||||
// Provide doppler data
|
||||
// Provide doppler data via valid pulse (updates captured values)
|
||||
@(posedge clk);
|
||||
doppler_real = 16'hAAAA;
|
||||
doppler_imag = 16'h5555;
|
||||
@@ -451,33 +492,34 @@ module tb_usb_data_interface;
|
||||
check(uut.doppler_imag_cap === 16'h5555,
|
||||
"doppler_imag captured correctly");
|
||||
|
||||
// Pump remaining doppler pulses
|
||||
pulse_doppler_once(16'hAAAA, 16'h5555);
|
||||
pulse_doppler_once(16'hAAAA, 16'h5555);
|
||||
pulse_doppler_once(16'hAAAA, 16'h5555);
|
||||
|
||||
wait_for_state(S_SEND_DETECT, 100);
|
||||
// The FSM has doppler_data_pending set and sends 4 bytes, then
|
||||
// transitions past SEND_DETECT (cfar_data_pending=0) to IDLE.
|
||||
wait_for_state(S_IDLE, 100);
|
||||
#1;
|
||||
check(uut.current_state === S_SEND_DETECT,
|
||||
"Doppler complete, moved to SEND_DETECTION_DATA");
|
||||
check(uut.current_state === S_IDLE,
|
||||
"Doppler done, packet completed");
|
||||
|
||||
// ════════════════════════════════════════════════════════
|
||||
// TEST GROUP 5: CFAR detection data
|
||||
// ════════════════════════════════════════════════════════
|
||||
$display("\n--- Test Group 5: CFAR Detection Data ---");
|
||||
// Continue from SEND_DETECTION_DATA state
|
||||
check(uut.current_state === S_SEND_DETECT,
|
||||
// Start a new packet with both doppler and cfar pending to verify
|
||||
// cfar data is properly sent in SEND_DETECTION_DATA.
|
||||
apply_reset;
|
||||
ft601_txe = 0;
|
||||
preload_pending_data;
|
||||
assert_range_valid(32'h0000_0002);
|
||||
// FSM races through: HEADER -> RANGE -> DOPPLER -> DETECT -> FOOTER -> IDLE
|
||||
// All pending flags consumed proves SEND_DETECT was entered.
|
||||
wait_for_state(S_IDLE, 200);
|
||||
#1;
|
||||
check(uut.cfar_data_pending === 1'b0,
|
||||
"Starting in SEND_DETECTION_DATA");
|
||||
|
||||
pulse_cfar_once(1'b1);
|
||||
|
||||
// After CFAR pulse, the FSM should advance to SEND_FOOTER
|
||||
// The pulse may take a few cycles to propagate
|
||||
wait_for_state(S_SEND_FOOTER, 50);
|
||||
// Check if we passed through detect -> footer, or further
|
||||
check(uut.current_state === S_SEND_FOOTER ||
|
||||
uut.current_state === S_WAIT_ACK ||
|
||||
uut.current_state === S_IDLE,
|
||||
// Verify the full packet completed with cfar data consumed
|
||||
check(uut.current_state === S_IDLE &&
|
||||
uut.doppler_data_pending === 1'b0 &&
|
||||
uut.cfar_data_pending === 1'b0,
|
||||
"CFAR detection sent, FSM advanced past SEND_DETECTION_DATA");
|
||||
|
||||
// ════════════════════════════════════════════════════════
|
||||
@@ -494,6 +536,7 @@ module tb_usb_data_interface;
|
||||
ft601_txe = 0;
|
||||
|
||||
// Drive packet through range data
|
||||
preload_pending_data;
|
||||
assert_range_valid(32'hFACE_FEED);
|
||||
wait_for_state(S_SEND_DOPPLER, 100);
|
||||
// Feed doppler data (need 4 pulses)
|
||||
@@ -534,6 +577,7 @@ module tb_usb_data_interface;
|
||||
// Verify WAIT_ACK behavior by doing another packet and catching it
|
||||
apply_reset;
|
||||
ft601_txe = 0;
|
||||
preload_pending_data;
|
||||
assert_range_valid(32'h1234_5678);
|
||||
wait_for_state(S_SEND_DOPPLER, 100);
|
||||
pulse_doppler_once(16'hABCD, 16'hEF01);
|
||||
@@ -627,6 +671,7 @@ module tb_usb_data_interface;
|
||||
|
||||
// Drive a full packet and check WAIT_ACK
|
||||
ft601_txe = 0;
|
||||
preload_pending_data;
|
||||
assert_range_valid(32'h1111_2222);
|
||||
wait_for_state(S_SEND_DOPPLER, 100);
|
||||
pulse_doppler_once(16'h3333, 16'h4444);
|
||||
@@ -726,6 +771,7 @@ module tb_usb_data_interface;
|
||||
ft601_txe = 0;
|
||||
|
||||
// Start a write packet
|
||||
preload_pending_data;
|
||||
assert_range_valid(32'hFACE_FEED);
|
||||
wait_for_state(S_SEND_HEADER, 50);
|
||||
@(posedge ft601_clk_in); #1;
|
||||
@@ -774,19 +820,21 @@ module tb_usb_data_interface;
|
||||
// Wait for CDC propagation (2-stage sync)
|
||||
repeat (6) @(posedge ft601_clk_in);
|
||||
|
||||
// Drive range valid — this should trigger the write FSM
|
||||
// Preload cfar pending so the FSM enters the SEND_DETECT data path
|
||||
// (without it, SEND_DETECT skips immediately on !cfar_data_pending).
|
||||
preload_cfar_pending;
|
||||
// Drive range valid — triggers write FSM
|
||||
assert_range_valid(32'hAA11_BB22);
|
||||
// FSM: IDLE -> SEND_HEADER -> SEND_RANGE_DATA (doppler disabled) -> SEND_DETECTION_DATA -> SEND_FOOTER
|
||||
// With ft601_txe=0, SEND_RANGE completes in 4 cycles so we may not catch it.
|
||||
// Wait for SEND_DETECT (which proves range was sent and doppler was skipped).
|
||||
wait_for_state(S_SEND_DETECT, 200);
|
||||
// FSM: IDLE -> SEND_HEADER -> SEND_RANGE (doppler disabled) -> SEND_DETECT -> FOOTER
|
||||
// The FSM races through SEND_DETECT in 1 cycle (cfar_data_pending is consumed).
|
||||
// Verify the packet completed correctly (doppler was skipped).
|
||||
wait_for_state(S_IDLE, 200);
|
||||
#1;
|
||||
check(uut.current_state === S_SEND_DETECT,
|
||||
// Reaching IDLE proves: HEADER -> RANGE -> (skip DOPPLER) -> DETECT -> FOOTER -> ACK -> IDLE.
|
||||
// cfar_data_pending consumed confirms SEND_DETECT was entered.
|
||||
check(uut.current_state === S_IDLE && uut.cfar_data_pending === 1'b0,
|
||||
"Stream gate: reached SEND_DETECT (range sent, doppler skipped)");
|
||||
|
||||
pulse_cfar_once(1'b1);
|
||||
wait_for_state(S_IDLE, 100);
|
||||
#1;
|
||||
check(uut.current_state === S_IDLE,
|
||||
"Stream gate: packet completed without doppler");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user