From 27c9c22ad2f1bfa5ad8fcba13a181b76d1f57587 Mon Sep 17 00:00:00 2001 From: Jason <83615043+JJassonn69@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:44:25 +0545 Subject: [PATCH] test(fpga): regression coverage for C-3 and USB NUM_CELLS bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs fixed recently had no tests that would have failed before the fix. Add direct regressions so either cannot silently return: 1. tb_chirp_controller Group 3b (multi-frame, C-3): run a second full frame back-to-back after DONE and assert chirp_counter returns to 0, frame 2 reaches GUARD_TIME after exactly CHIRP_MAX/2 long chirps, and frame 2 reaches DONE. Before the fix, chirp_counter held at CHIRP_MAX after frame 1, the LONG_LISTEN -> GUARD guard (=CHIRP_MAX/2-1) never matched, and frame 2 ran extra chirps until the 6-bit counter wrapped — these checks fail loudly if that regresses. 2. tb_usb_data_interface frame-sync width + value pins: assert $bits(uut.sample_counter) >= 15 and uut.NUM_CELLS == 15'd16384. Protects against reintroducing the 12-bit / 2048-cell constants that fired 8 false frame-start markers per real 512 x 32 frame. Regression: 32/32 PASS; USB TB 89 -> 91 checks. --- 9_Firmware/9_2_FPGA/tb/tb_chirp_controller.v | 51 ++++++++++++++++++- .../9_2_FPGA/tb/tb_usb_data_interface.v | 15 ++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/9_Firmware/9_2_FPGA/tb/tb_chirp_controller.v b/9_Firmware/9_2_FPGA/tb/tb_chirp_controller.v index 0ed8281..87615fd 100644 --- a/9_Firmware/9_2_FPGA/tb/tb_chirp_controller.v +++ b/9_Firmware/9_2_FPGA/tb/tb_chirp_controller.v @@ -322,7 +322,56 @@ initial begin // With new_chirp=0, FSM should stay in IDLE @(posedge clk_120m); check("FSM: returns to IDLE after DONE", dut.current_state == 3'b000); - + + // ===================================================================== + // TEST GROUP 3b: MULTI-FRAME REGRESSION (C-3) + // + // Bug: plfm_chirp_controller_enhanced never reset chirp_counter when the + // frame completed. After frame 1 the counter sat at CHIRP_MAX, so the + // LONG_LISTEN -> GUARD transition guard (== CHIRP_MAX/2-1) never matched + // on subsequent frames and frame 2+ ran extra chirps until the 6-bit + // counter wrapped. + // + // These checks prove the counter is cleared at DONE and frame 2 matches + // frame 1 exactly. + // ===================================================================== + $display("--- Group 3b: Multi-Frame Regression (C-3) ---"); + + // T3b.1: Immediately after frame 1 DONE -> IDLE, counter is back to 0. + check("C-3: chirp_counter reset to 0 after 1st DONE", chirp_counter == 6'd0); + + // Kick off frame 2 from the same IDLE state (no reset between frames). + @(posedge clk_120m); + new_chirp = 1; + @(posedge clk_120m); + + // T3b.2: Frame 2 enters LONG_CHIRP. + wait_for_state(3'b001, 10); + check("Frame 2: enters LONG_CHIRP", dut.current_state == 3'b001); + + // T3b.3: Frame 2 reaches GUARD_TIME after exactly CHIRP_MAX/2 long chirps. + // (If the counter were not reset, the FSM would stall in + // LONG_CHIRP/LONG_LISTEN until the 6-bit counter wrapped.) + wait_for_state(3'b011, + (T1_SAMPLES + T1_RADAR_LISTENING) * (CHIRP_MAX/2) + 20); + check("Frame 2: reaches GUARD_TIME after CHIRP_MAX/2 long chirps", + dut.current_state == 3'b011); + check("Frame 2: chirp_counter == CHIRP_MAX/2 at GUARD_TIME", + chirp_counter == CHIRP_MAX/2); + + // T3b.4: Frame 2 reaches DONE. + wait_for_state(3'b110, + GUARD_SAMPLES + + (T2_SAMPLES + T2_RADAR_LISTENING) * (CHIRP_MAX/2) + 20); + check("Frame 2: reaches DONE", dut.current_state == 3'b110); + + // Deassert new_chirp so FSM stays in IDLE after DONE. + new_chirp = 0; + @(posedge clk_120m); + + // T3b.5: Counter cleared again after frame 2 completes. + check("C-3: chirp_counter reset to 0 after 2nd DONE", chirp_counter == 6'd0); + // ===================================================================== // TEST GROUP 4: SINGLE-DRIVER VERIFICATION (A5 FIX CORE TEST) // ===================================================================== diff --git a/9_Firmware/9_2_FPGA/tb/tb_usb_data_interface.v b/9_Firmware/9_2_FPGA/tb/tb_usb_data_interface.v index e2862d5..4a0f1f8 100644 --- a/9_Firmware/9_2_FPGA/tb/tb_usb_data_interface.v +++ b/9_Firmware/9_2_FPGA/tb/tb_usb_data_interface.v @@ -411,6 +411,21 @@ module tb_usb_data_interface; check(ft601_siwu_n === 1'b1, "ft601_siwu_n=1 after reset"); + // ──────────────────────────────────────────────────────── + // Frame-sync regression (NUM_CELLS bug) + // + // sample_counter was 12 bits wide with NUM_CELLS=2048 before the + // 2048-pt FFT architecture was completed. With 512 range bins x 32 + // Doppler = 16384 cells per frame, the old 12-bit counter wrapped + // 8 times per real frame and the host saw 8 false frame-start + // markers. These checks pin the counter width and NUM_CELLS value + // so the regression fails loudly if either is narrowed again. + // ──────────────────────────────────────────────────────── + check($bits(uut.sample_counter) >= 15, + "Frame-sync: sample_counter width >= 15 bits (holds 0..16383)"); + check(uut.NUM_CELLS === 15'd16384, + "Frame-sync: NUM_CELLS == 16384 (512 range x 32 Doppler)"); + // ════════════════════════════════════════════════════════ // TEST GROUP 2: Data packet word packing //