Wire self-test results (0x31) to USB status readback path, add fpga_self_test to regression

- usb_data_interface.v: Add 3 self-test status inputs, expand status packet
  from 7 words (header + 5 data + footer) to 8 words (header + 6 data + footer).
  New status_words[5] carries {busy, detail[7:0], flags[4:0]}.
- radar_system_top.v: Wire self_test_flags_latched, self_test_detail_latched,
  self_test_busy to usb_data_interface ports. Add opcode 0x31 as status
  readback alias so host can read self-test results.
- tb_usb_data_interface.v: Add self-test port connections, verify word 5 in
  Group 16, add Group 18 (busy flag + partial failure variant). 81 checks pass.
- run_regression.sh: Add fpga_self_test.v to PROD_RTL lint list and system-
  level compile lists. Add tb_fpga_self_test as Phase 1 unit test.
- 24/24 regression tests pass, lint clean (0 errors, 4 advisory warnings).
This commit is contained in:
Jason
2026-03-20 20:03:11 +02:00
parent eb907de3d1
commit 4985eccbae
6 changed files with 4271 additions and 4194 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+63 -7
View File
@@ -74,6 +74,11 @@ module tb_usb_data_interface;
reg [5:0] status_chirps_per_elev;
reg [1:0] status_range_mode;
// Self-test status readback inputs
reg [4:0] status_self_test_flags;
reg [7:0] status_self_test_detail;
reg status_self_test_busy;
// Clock generators (asynchronous)
always #(CLK_PERIOD / 2) clk = ~clk;
always #(FT_CLK_PERIOD / 2) ft601_clk_in = ~ft601_clk_in;
@@ -124,7 +129,12 @@ module tb_usb_data_interface;
.status_short_chirp (status_short_chirp),
.status_short_listen (status_short_listen),
.status_chirps_per_elev(status_chirps_per_elev),
.status_range_mode (status_range_mode)
.status_range_mode (status_range_mode),
// Self-test status readback
.status_self_test_flags (status_self_test_flags),
.status_self_test_detail(status_self_test_detail),
.status_self_test_busy (status_self_test_busy)
);
// Test bookkeeping
@@ -181,6 +191,9 @@ module tb_usb_data_interface;
status_short_listen = 16'd17450;
status_chirps_per_elev = 6'd32;
status_range_mode = 2'b00;
status_self_test_flags = 5'b00000;
status_self_test_detail = 8'd0;
status_self_test_busy = 1'b0;
repeat (6) @(posedge ft601_clk_in);
reset_n = 1;
// Wait enough cycles for stream_control CDC to propagate
@@ -867,7 +880,7 @@ module tb_usb_data_interface;
//
// TEST GROUP 16: Status Readback (Gap 2)
// Verify that pulsing status_request triggers a 7-word
// Verify that pulsing status_request triggers an 8-word
// status response via the SEND_STATUS state.
//
$display("\n--- Test Group 16: Status Readback (Gap 2) ---");
@@ -885,6 +898,10 @@ module tb_usb_data_interface;
status_short_listen = 16'd17450;
status_chirps_per_elev = 6'd32;
status_range_mode = 2'b10; // Long-range for status test
// Self-test status: all 5 tests passed, detail=0xA5, not busy
status_self_test_flags = 5'b11111;
status_self_test_detail = 8'hA5;
status_self_test_busy = 1'b0;
// Pulse status_request (1 cycle in clk domain toggles status_req_toggle_100m)
@(posedge clk);
@@ -903,14 +920,14 @@ module tb_usb_data_interface;
check(uut.current_state === S_SEND_STATUS,
"Status readback: FSM entered SEND_STATUS");
// The SEND_STATUS state sends 7 words (idx 0-6):
// idx 0: 0xBB header, idx 1-5: status_words[0-4], idx 6: 0x55 footer
// After idx 6 it transitions to WAIT_ACK -> IDLE.
// Since ft601_txe=0, all 7 words should stream without stall.
// The SEND_STATUS state sends 8 words (idx 0-7):
// idx 0: 0xBB header, idx 1-6: status_words[0-5], idx 7: 0x55 footer
// After idx 7 it transitions to WAIT_ACK -> IDLE.
// Since ft601_txe=0, all 8 words should stream without stall.
wait_for_state(S_IDLE, 100);
#1;
check(uut.current_state === S_IDLE,
"Status readback: returned to IDLE after 7-word response");
"Status readback: returned to IDLE after 8-word response");
// Verify the status snapshot was captured correctly.
// status_words[0] = {0xFF, 3'b000, mode[1:0], 5'b0, stream_ctrl[2:0], cfar_threshold[15:0]}
@@ -943,6 +960,10 @@ module tb_usb_data_interface;
"Status readback: word 3 = {short_listen, 0, chirps_per_elev}");
check(uut.status_words[4] === {30'd0, 2'b10},
"Status readback: word 4 = range_mode=2'b10");
// status_words[5] = {7'd0, busy, 8'd0, detail[7:0], 3'd0, flags[4:0]}
// = {7'd0, 1'b0, 8'd0, 8'hA5, 3'd0, 5'b11111}
check(uut.status_words[5] === {7'd0, 1'b0, 8'd0, 8'hA5, 3'd0, 5'b11111},
"Status readback: word 5 = self-test {busy=0, detail=A5, flags=1F}");
//
// TEST GROUP 17: New Chirp Timing Opcodes (Gap 2)
@@ -999,6 +1020,41 @@ module tb_usb_data_interface;
check(cmd_opcode === 8'hFF,
"Chirp opcode: 0xFF (status request)");
//
// TEST GROUP 18: Self-Test Readback Variants
// Verify self-test busy flag, partial failures, and
// alternate status word 5 values.
//
$display("\n--- Test Group 18: Self-Test Readback Variants ---");
apply_reset;
ft601_txe = 0;
// Scenario A: Self-test busy, partial failure, different detail
status_self_test_flags = 5'b10110; // T0 fail, T3 fail
status_self_test_detail = 8'h42;
status_self_test_busy = 1'b1;
// Trigger status readback
@(posedge clk);
status_request = 1;
@(posedge clk);
status_request = 0;
repeat (8) @(posedge ft601_clk_in); #1;
wait_for_state(S_SEND_STATUS, 20);
#1;
check(uut.current_state === S_SEND_STATUS,
"Self-test readback A: FSM entered SEND_STATUS");
wait_for_state(S_IDLE, 100);
#1;
check(uut.current_state === S_IDLE,
"Self-test readback A: returned to IDLE");
// Verify word 5: {7'd0, busy=1, 8'd0, detail=0x42, 3'd0, flags=5'b10110}
check(uut.status_words[5] === {7'd0, 1'b1, 8'd0, 8'h42, 3'd0, 5'b10110},
"Self-test readback A: word 5 = {busy=1, detail=42, flags=16}");
//
// Summary
//