mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-10 23:41:18 +00:00
feat(fpga,mcu,gui): PR-AB.b — drift-free dwell sync via DIG_6 frame_pulse + AGC always-on policy
FPGA (Phase 1+2): - gpio_dig6 (PD14) now carries chirp_scheduler frame_pulse, FPGA-stretched to ~100 ns so the STM32 EXTI on PD14 can latch reliably. - gpio_dig7 (PD15) returns to its pre-PR-AB.b role: control-fault OR (range_decim_watchdog | CDC overrun); MCU stuck-high sampler unchanged. - rx_range_decim_watchdog gains a sticky in source clock domain so a slow status poll cannot miss a 1-cycle assertion (Phase 1). - New tb_dig6_frame_pulse.v (13 checks); tb_status_words_stickies.v extended with DIG_7 fault-OR coverage (14 checks); retired tb_audit_s10_gpio_split.v. - Port comments in radar_system_top.v / _50t.v and XDC roles refreshed. MCU (Phase 3): - PD14 reconfigured to GPIO_MODE_IT_RISING + GPIO_PULLDOWN; new EXTI15_10_IRQHandler in stm32f7xx_it.c dispatches to HAL_GPIO_EXTI_Callback that bumps a volatile g_frame_pulse_count. - runRadarPulseSequence dwell loop replaces 3x HAL_Delay(8) with waitForFramePulse(20) — per-pattern dwell now tracks the actual mask-aware ladder length (drift-free, mask-aware), with a 20 ms timeout safety net. - AGC outer loop is ALWAYS-ON in production (compile-time policy); bench builds compile the body out via -DMCU_AGC_FORCE_DISABLED. The runtime enable/debounce + DIG_6 polling that previously gated AGC are removed. - main.h adds FPGA_FRAME_PULSE_* aliases pointing at FPGA_DIG6_*. GUI (Phase 4): - Settings tab gains a Bench / Diagnostics group with a BENCH-MODE checkbox (off by default, persisted via QSettings). - AGC group header swaps between a green "AGC: ALWAYS-ON" badge (production) and Enable/Disable AGC buttons (bench), pinned to the top of the group. The redundant 0/1 spinbox row for opcode 0x28 is removed — buttons send the same opcode and cannot accept invalid input. - Both the FPGA Control AGC Status box and the AGC Monitor strip share a helper that honours bench-mode in production (always shows ALWAYS-ON in green so the two views never disagree with the badge). - _add_fpga_param_row uses setFixedWidth on label and Set button + explicit stretch=1 on the hint, so all rows align column-wise whether they sit directly in a QVBoxLayout or inside a wrapper QWidget. Regression: FPGA 42/0/0 (PR-M.4 baseline) - MCU 34/34 - GPS extended 51/51 - GUI v7 150/150 - BENCH-MODE flip behaviorally verified. Hardware-blocked steps deferred: bench-scope verify (PD14 dwell pulse, counter advance, PD15 stuck-high recovery still triggers). Closes #182.
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
// ============================================================================
|
||||
// tb_dig6_frame_pulse.v
|
||||
//
|
||||
// PR-AB.b: gpio_dig6 (PD14) carries the chirp_scheduler frame_pulse, stretched
|
||||
// to ~100 ns so the STM32 EXTI on PD14 can latch the rising edge reliably.
|
||||
// The MCU dwell loop (runRadarPulseSequence) replaces
|
||||
// HAL_Delay(BEAM_PATTERN_DWELL_MS) with osSemaphoreAcquire so per-pattern
|
||||
// dwell tracks the actual ladder length — drift-free, mask-aware.
|
||||
//
|
||||
// Companion to tb_status_words_stickies.v which covers the gpio_dig7 fault-OR
|
||||
// (watchdog | CDC overrun) semantic and the status_words[5][6:5] CDC packing.
|
||||
//
|
||||
// This TB mirrors the production stretcher fragment from radar_system_top.v
|
||||
// and asserts:
|
||||
//
|
||||
// T1 Reset → count=0, dig6=0
|
||||
// T2 Single 1-cycle pulse → dig6 high for exactly 10 cycles, low on 11
|
||||
// T3 Pulse during stretch → counter reloads to 10 (longer total high time)
|
||||
// T4 Two pulses spaced > 10 cycles → two clean rising edges
|
||||
// T5 Pulse asserted continuously for many cycles → counter pinned high
|
||||
// T6 No pulse activity → dig6 stays low forever
|
||||
// T7 Reset mid-stretch → counter and dig6 drop immediately
|
||||
// ============================================================================
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_dig6_frame_pulse;
|
||||
|
||||
reg clk_100m = 1'b0;
|
||||
reg sys_reset_n = 1'b0;
|
||||
reg frame_pulse = 1'b0;
|
||||
wire dig6;
|
||||
|
||||
frame_pulse_stretcher_block dut (
|
||||
.clk_100m_buf (clk_100m),
|
||||
.sys_reset_n (sys_reset_n),
|
||||
.frame_pulse_in(frame_pulse),
|
||||
.gpio_dig6 (dig6)
|
||||
);
|
||||
|
||||
// 100 MHz clock (10 ns period)
|
||||
always #5 clk_100m = ~clk_100m;
|
||||
|
||||
integer pass = 0;
|
||||
integer fail = 0;
|
||||
|
||||
task check (input [127:0] label, input expected);
|
||||
begin
|
||||
#1;
|
||||
if (dig6 === expected) begin
|
||||
$display(" [PASS] %0s: dig6=%b", label, dig6);
|
||||
pass = pass + 1;
|
||||
end else begin
|
||||
$display(" [FAIL] %0s: dig6=%b (exp %b)", label, dig6, expected);
|
||||
fail = fail + 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task fire_pulse;
|
||||
begin
|
||||
@(posedge clk_100m); #1;
|
||||
frame_pulse = 1'b1;
|
||||
@(posedge clk_100m); #1;
|
||||
frame_pulse = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
integer i;
|
||||
|
||||
initial begin
|
||||
$display("============================================================");
|
||||
$display("PR-AB.b: gpio_dig6 = stretched(frame_pulse)");
|
||||
$display("============================================================");
|
||||
|
||||
// ---- T1: reset state ----
|
||||
sys_reset_n = 1'b0;
|
||||
repeat (4) @(posedge clk_100m);
|
||||
check("T1 reset asserted, dig6 low", 1'b0);
|
||||
@(posedge clk_100m); #1;
|
||||
sys_reset_n = 1'b1;
|
||||
@(posedge clk_100m); #1;
|
||||
check("T1b after deassert, dig6 still low", 1'b0);
|
||||
|
||||
// ---- T2: single pulse → exactly 10 cycles high ----
|
||||
fire_pulse();
|
||||
// After fire_pulse, frame_pulse was high for 1 clk; the always block
|
||||
// sampled it on that posedge and loaded count=10. dig6 is high while
|
||||
// count != 0. Count decrements each cycle: 10,9,...,1,0. So dig6 is
|
||||
// high for 10 posedges starting on the cycle the load took effect.
|
||||
for (i = 0; i < 10; i = i + 1) begin
|
||||
#1;
|
||||
if (dig6 !== 1'b1) begin
|
||||
$display(" [FAIL] T2 cycle %0d: dig6=%b (exp 1)", i, dig6);
|
||||
fail = fail + 1;
|
||||
end
|
||||
@(posedge clk_100m);
|
||||
end
|
||||
// 11th cycle: count has wrapped to 0, dig6 should be low.
|
||||
#1;
|
||||
check("T2 dig6 low on 11th cycle", 1'b0);
|
||||
// Make sure it stays low.
|
||||
repeat (5) @(posedge clk_100m);
|
||||
check("T2 dig6 stays low after stretch", 1'b0);
|
||||
|
||||
// ---- T3: pulse during stretch reloads counter ----
|
||||
fire_pulse();
|
||||
@(posedge clk_100m); #1; // count = 9
|
||||
@(posedge clk_100m); #1; // count = 8
|
||||
@(posedge clk_100m); #1; // count = 7
|
||||
// Reload by firing another pulse mid-stretch.
|
||||
@(posedge clk_100m); #1;
|
||||
frame_pulse = 1'b1;
|
||||
@(posedge clk_100m); #1;
|
||||
frame_pulse = 1'b0;
|
||||
// Counter is now reloaded to 10. dig6 stays high for another 10 cycles.
|
||||
for (i = 0; i < 10; i = i + 1) begin
|
||||
#1;
|
||||
if (dig6 !== 1'b1) begin
|
||||
$display(" [FAIL] T3 reload cycle %0d: dig6=%b (exp 1)", i, dig6);
|
||||
fail = fail + 1;
|
||||
end
|
||||
@(posedge clk_100m);
|
||||
end
|
||||
#1;
|
||||
check("T3 dig6 low after reloaded stretch", 1'b0);
|
||||
|
||||
// ---- T4: spaced pulses produce two distinct rising edges ----
|
||||
fire_pulse();
|
||||
repeat (15) @(posedge clk_100m); // > 10 cycles, dig6 must be 0
|
||||
check("T4a dig6 low between pulses", 1'b0);
|
||||
fire_pulse();
|
||||
#1;
|
||||
check("T4b dig6 high after second pulse", 1'b1);
|
||||
repeat (10) @(posedge clk_100m);
|
||||
#1;
|
||||
check("T4c dig6 low after second stretch", 1'b0);
|
||||
|
||||
// ---- T5: continuous pulse pins counter at 10 ----
|
||||
@(posedge clk_100m); #1;
|
||||
frame_pulse = 1'b1;
|
||||
repeat (50) @(posedge clk_100m);
|
||||
#1;
|
||||
check("T5 dig6 high under continuous pulse", 1'b1);
|
||||
@(posedge clk_100m); #1;
|
||||
frame_pulse = 1'b0;
|
||||
// After deassert, dig6 should drain over 10 cycles.
|
||||
repeat (10) @(posedge clk_100m);
|
||||
#1;
|
||||
check("T5 dig6 drains after pulse deassert", 1'b0);
|
||||
|
||||
// ---- T6: idle line stays low ----
|
||||
repeat (200) @(posedge clk_100m);
|
||||
check("T6 dig6 idle stays low", 1'b0);
|
||||
|
||||
// ---- T7: reset mid-stretch drops dig6 immediately ----
|
||||
fire_pulse();
|
||||
@(posedge clk_100m); #1;
|
||||
@(posedge clk_100m); #1;
|
||||
check("T7a dig6 high pre-reset", 1'b1);
|
||||
sys_reset_n = 1'b0;
|
||||
@(posedge clk_100m); #1;
|
||||
check("T7b reset drops dig6", 1'b0);
|
||||
sys_reset_n = 1'b1;
|
||||
@(posedge clk_100m); #1;
|
||||
|
||||
$display("============================================================");
|
||||
$display("DIG6 FRAME PULSE RESULTS: pass=%0d fail=%0d", pass, fail);
|
||||
$display("============================================================");
|
||||
if (fail == 0) $display("[OVERALL] PASS");
|
||||
else $display("[OVERALL] FAIL");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
#1_000_000;
|
||||
$display("[FATAL] timeout");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// ============================================================================
|
||||
// frame_pulse_stretcher_block — mirrors the production stretcher in
|
||||
// radar_system_top.v (PR-AB.b). 1-cycle frame_pulse_in loads count=10;
|
||||
// counter decrements each clock; gpio_dig6 = (count != 0). reset_n async
|
||||
// clear. Reloads on subsequent pulses. Result: dig6 high for 10 clk_100m
|
||||
// cycles = 100 ns starting on the cycle after the input pulse is sampled.
|
||||
// ============================================================================
|
||||
module frame_pulse_stretcher_block (
|
||||
input wire clk_100m_buf,
|
||||
input wire sys_reset_n,
|
||||
input wire frame_pulse_in,
|
||||
output wire gpio_dig6
|
||||
);
|
||||
reg [3:0] frame_pulse_stretch_count;
|
||||
always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
if (!sys_reset_n)
|
||||
frame_pulse_stretch_count <= 4'd0;
|
||||
else if (frame_pulse_in)
|
||||
frame_pulse_stretch_count <= 4'd10;
|
||||
else if (frame_pulse_stretch_count != 4'd0)
|
||||
frame_pulse_stretch_count <= frame_pulse_stretch_count - 4'd1;
|
||||
end
|
||||
assign gpio_dig6 = (frame_pulse_stretch_count != 4'd0);
|
||||
endmodule
|
||||
+85
-171
@@ -1,68 +1,35 @@
|
||||
// ============================================================================
|
||||
// tb_audit_s10_gpio_split.v
|
||||
// tb_status_words_stickies.v
|
||||
//
|
||||
// AUDIT-S10: gpio_dig5 previously OR'd six unrelated flags — four signal-
|
||||
// saturation classes (AGC, DDC overflow, DDC saturation, MTI saturation) and
|
||||
// two control-fault classes (range-decimator watchdog, CIC->FIR CDC overrun)
|
||||
// — into the single MCU-visible bit at PD13. The MCU outer-loop AGC reduces
|
||||
// RF gain on PD13 assertion, which is the wrong response to a watchdog or
|
||||
// CDC stall. gpio_dig7 (PD15) was tied 1'b0 (reserved).
|
||||
// AUDIT-S10 + PR-AB.b: status_words[5][6:5] CDC packing for the two
|
||||
// control-fault flags (range-decimator watchdog F-6.4, CIC->FIR CDC overrun
|
||||
// F-1.2) AND the gpio_dig7 fault-OR semantic. PR-AB.b Step 1 made the F-6.4
|
||||
// half sticky in the source clock domain so a slow host poll cannot miss
|
||||
// the event; F-1.2 is already sticky inside ddc_400m.v. PR-AB.b drives
|
||||
// gpio_dig7 = (watchdog | cic_fir_overrun) so the MCU stuck-high sampler
|
||||
// (main.cpp:880-1000) can fire attemptErrorRecovery(ERROR_FPGA_DSP_STALL).
|
||||
// status_words[5][7] is reserved (must stay 0).
|
||||
//
|
||||
// Fix: split the OR-network so gpio_dig5 carries only signal-saturation flags
|
||||
// (AGC continues to react correctly) and gpio_dig7 carries control-fault
|
||||
// flags (MCU follow-up will log + reset; until then host telemetry covers).
|
||||
// Status words[5][6:5] expose the two control-fault classes so host-side
|
||||
// can graph them regardless of MCU consumption.
|
||||
// Replaces the GROUP B tests in the retired tb_audit_s10_gpio_split.v and
|
||||
// also restores the GROUP A dig7 fault-OR coverage (the dig5 saturation
|
||||
// portion of GROUP A lives in the radar_receiver_final TBs). gpio_dig6
|
||||
// stretcher (chirp_scheduler frame_pulse) coverage is in
|
||||
// tb_dig6_frame_pulse.v.
|
||||
//
|
||||
// This TB mirrors the production fragments from radar_system_top.v and
|
||||
// usb_data_interface[*_ft2232h].v and asserts:
|
||||
//
|
||||
// GROUP A GPIO split (combinational)
|
||||
// T1 All inputs 0 -> dig5=0, dig7=0
|
||||
// T2 Each signal-sat input -> dig5=1, dig7=0 (no cross-route to dig7)
|
||||
// T3 Each control-fault -> dig5=0, dig7=1 (no cross-route to dig5)
|
||||
// T4 Mixed sat + fault -> dig5=1, dig7=1 (independent)
|
||||
// T5 AGC count >0 (boundary) -> dig5=1
|
||||
// T6 DDC count =0 (boundary) -> dig5=0
|
||||
//
|
||||
// GROUP B Status-word CDC packing (sequential)
|
||||
// T7 Reset state -> sync regs 0, status_word_bits 0
|
||||
// T8 Watchdog asserted in src -> after 2 ft_clk edges, status[5]=1
|
||||
// T9 CIC overrun asserted in src -> after 2 ft_clk edges, status[6]=1
|
||||
// T10 Both asserted, both cleared -> status[6:5] tracks (sticky in src;
|
||||
// TB drives explicit clears)
|
||||
// T11 status_words[5][7] stays 0 (reserved bit, not stomped by sync)
|
||||
// T12 status_words[5][4:0] (self_test_flags) stays = src input
|
||||
// T1 Reset state -> sync regs 0, status[6:5]=0, dig7=0
|
||||
// T2 Watchdog asserted -> after 2 ft_clk edges, status[5]=1, dig7=1
|
||||
// T3 CIC overrun asserted -> after 2 ft_clk edges, status[6]=1, dig7=1
|
||||
// T4a Both asserted -> status[6:5]=11, dig7=1
|
||||
// T4b Both cleared -> status[6:5]=00, dig7=0
|
||||
// T5 status_words[5][7] stays 0 (reserved bit not stomped by sync); dig7=1
|
||||
// T6 status_words[5][4:0] (self_test_flags) pass through; dig7=0
|
||||
// ============================================================================
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_audit_s10_gpio_split;
|
||||
module tb_status_words_stickies;
|
||||
|
||||
// ===== GROUP A: GPIO split inputs/outputs =====
|
||||
reg [7:0] agc_saturation_count;
|
||||
reg ddc_overflow_any;
|
||||
reg [2:0] ddc_saturation_count;
|
||||
reg [7:0] mti_saturation_count;
|
||||
reg range_decim_watchdog;
|
||||
reg ddc_cic_fir_overrun;
|
||||
|
||||
wire dig5;
|
||||
wire dig7;
|
||||
|
||||
gpio_split_block gpio_dut (
|
||||
.agc_saturation_count (agc_saturation_count),
|
||||
.ddc_overflow_any (ddc_overflow_any),
|
||||
.ddc_saturation_count (ddc_saturation_count),
|
||||
.mti_saturation_count (mti_saturation_count),
|
||||
.range_decim_watchdog (range_decim_watchdog),
|
||||
.ddc_cic_fir_overrun (ddc_cic_fir_overrun),
|
||||
.gpio_dig5 (dig5),
|
||||
.gpio_dig7 (dig7)
|
||||
);
|
||||
|
||||
// ===== GROUP B: status-word CDC packing =====
|
||||
reg clk_src = 1'b0; // 100 MHz radar domain
|
||||
reg ft_clk = 1'b0; // 60/100 MHz USB domain
|
||||
reg ft_clk = 1'b0; // 60 MHz USB domain
|
||||
reg reset_n = 1'b0;
|
||||
reg src_watchdog;
|
||||
reg src_overrun;
|
||||
@@ -70,6 +37,7 @@ module tb_audit_s10_gpio_split;
|
||||
reg status_req_pulse;
|
||||
|
||||
wire [31:0] status_word_5;
|
||||
wire gpio_dig7; // PR-AB.b: production OR of fault flags
|
||||
|
||||
status_packing_block status_dut (
|
||||
.clk (clk_src),
|
||||
@@ -82,29 +50,20 @@ module tb_audit_s10_gpio_split;
|
||||
.status_word_5 (status_word_5)
|
||||
);
|
||||
|
||||
// 100 MHz src clock
|
||||
// Mirrors production combinational OR in radar_system_top.v:
|
||||
// assign gpio_dig7 = rx_range_decim_watchdog | rx_ddc_cic_fir_overrun;
|
||||
gpio_dig7_or_block dig7_dut (
|
||||
.watchdog (src_watchdog),
|
||||
.overrun (src_overrun),
|
||||
.gpio_dig7(gpio_dig7)
|
||||
);
|
||||
|
||||
always #5 clk_src = ~clk_src;
|
||||
// 60 MHz ft_clk (~16.67 ns)
|
||||
always #8 ft_clk = ~ft_clk;
|
||||
|
||||
// ----- bookkeeping -----
|
||||
integer pass = 0;
|
||||
integer fail = 0;
|
||||
|
||||
task check_dig (input [127:0] label, input expected_dig5, input expected_dig7);
|
||||
begin
|
||||
#1; // settle combinational
|
||||
if (dig5 === expected_dig5 && dig7 === expected_dig7) begin
|
||||
$display(" [PASS] %0s: dig5=%b dig7=%b", label, dig5, dig7);
|
||||
pass = pass + 1;
|
||||
end else begin
|
||||
$display(" [FAIL] %0s: dig5=%b (exp %b) dig7=%b (exp %b)",
|
||||
label, dig5, expected_dig5, dig7, expected_dig7);
|
||||
fail = fail + 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task check_status (input [127:0] label, input [31:0] mask, input [31:0] expected);
|
||||
begin
|
||||
if ((status_word_5 & mask) === (expected & mask)) begin
|
||||
@@ -119,155 +78,117 @@ module tb_audit_s10_gpio_split;
|
||||
end
|
||||
endtask
|
||||
|
||||
task check_dig7 (input [127:0] label, input expected);
|
||||
begin
|
||||
if (gpio_dig7 === expected) begin
|
||||
$display(" [PASS] %0s: dig7=%b", label, gpio_dig7);
|
||||
pass = pass + 1;
|
||||
end else begin
|
||||
$display(" [FAIL] %0s: dig7=%b (exp %b)", label, gpio_dig7, expected);
|
||||
fail = fail + 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task pulse_status_req;
|
||||
begin
|
||||
@(posedge ft_clk); #1;
|
||||
status_req_pulse = 1'b1;
|
||||
@(posedge ft_clk); #1;
|
||||
status_req_pulse = 1'b0;
|
||||
// Allow the registered status_words update to land.
|
||||
@(posedge ft_clk); #1;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
$display("============================================================");
|
||||
$display("AUDIT-S10: gpio_dig split + status_words[5][6:5] visibility");
|
||||
$display("status_words[5][6:5] CDC + gpio_dig7 fault-OR (AUDIT-S10 + PR-AB.b)");
|
||||
$display("============================================================");
|
||||
|
||||
// ---- GROUP A: GPIO split ----
|
||||
agc_saturation_count = 8'd0;
|
||||
ddc_overflow_any = 1'b0;
|
||||
ddc_saturation_count = 3'd0;
|
||||
mti_saturation_count = 8'd0;
|
||||
range_decim_watchdog = 1'b0;
|
||||
ddc_cic_fir_overrun = 1'b0;
|
||||
|
||||
// T1
|
||||
check_dig("T1 all zero", 1'b0, 1'b0);
|
||||
|
||||
// T2 each signal-sat individually
|
||||
agc_saturation_count = 8'd1;
|
||||
check_dig("T2a agc_sat>0", 1'b1, 1'b0);
|
||||
agc_saturation_count = 8'd0;
|
||||
|
||||
ddc_overflow_any = 1'b1;
|
||||
check_dig("T2b ddc_overflow", 1'b1, 1'b0);
|
||||
ddc_overflow_any = 1'b0;
|
||||
|
||||
ddc_saturation_count = 3'd1;
|
||||
check_dig("T2c ddc_sat>0", 1'b1, 1'b0);
|
||||
ddc_saturation_count = 3'd0;
|
||||
|
||||
mti_saturation_count = 8'd1;
|
||||
check_dig("T2d mti_sat>0", 1'b1, 1'b0);
|
||||
mti_saturation_count = 8'd0;
|
||||
|
||||
// T3 each control-fault individually
|
||||
range_decim_watchdog = 1'b1;
|
||||
check_dig("T3a watchdog", 1'b0, 1'b1);
|
||||
range_decim_watchdog = 1'b0;
|
||||
|
||||
ddc_cic_fir_overrun = 1'b1;
|
||||
check_dig("T3b cic_fir_overrun", 1'b0, 1'b1);
|
||||
ddc_cic_fir_overrun = 1'b0;
|
||||
|
||||
// T4 mixed
|
||||
agc_saturation_count = 8'd5;
|
||||
range_decim_watchdog = 1'b1;
|
||||
check_dig("T4 mixed sat+fault", 1'b1, 1'b1);
|
||||
agc_saturation_count = 8'd0;
|
||||
range_decim_watchdog = 1'b0;
|
||||
|
||||
// T5 boundary: largest agc count
|
||||
agc_saturation_count = 8'hFF;
|
||||
check_dig("T5 agc_sat=FF", 1'b1, 1'b0);
|
||||
agc_saturation_count = 8'd0;
|
||||
|
||||
// T6 boundary: ddc_sat=0 stays low
|
||||
ddc_saturation_count = 3'd0;
|
||||
check_dig("T6 ddc_sat=0", 1'b0, 1'b0);
|
||||
|
||||
// ---- GROUP B: status-word CDC packing ----
|
||||
src_watchdog = 1'b0;
|
||||
src_overrun = 1'b0;
|
||||
src_self_test_flags = 5'b00000;
|
||||
status_req_pulse = 1'b0;
|
||||
|
||||
// Apply reset
|
||||
reset_n = 1'b0;
|
||||
repeat (5) @(posedge ft_clk);
|
||||
reset_n = 1'b1;
|
||||
repeat (3) @(posedge ft_clk);
|
||||
|
||||
// T7 reset state
|
||||
// T1 reset state
|
||||
pulse_status_req();
|
||||
check_status("T7 reset state",
|
||||
check_status("T1 reset state",
|
||||
32'h000000E0, // [7:5]
|
||||
32'h00000000);
|
||||
check_dig7("T1 dig7 idle low", 1'b0);
|
||||
|
||||
// T8 watchdog asserted only
|
||||
// T2 watchdog asserted only
|
||||
@(posedge clk_src); #1;
|
||||
src_watchdog = 1'b1;
|
||||
// give 4 ft_clk for sync chain to settle
|
||||
repeat (5) @(posedge ft_clk);
|
||||
pulse_status_req();
|
||||
check_status("T8 watchdog asserted",
|
||||
check_status("T2 watchdog asserted",
|
||||
32'h00000060, // [6:5]
|
||||
32'h00000020); // [5]=1
|
||||
check_dig7("T2 dig7 watchdog -> high", 1'b1);
|
||||
|
||||
// T9 cic_fir_overrun asserted only (clear watchdog first)
|
||||
// T3 cic_fir_overrun asserted only (clear watchdog first)
|
||||
@(posedge clk_src); #1;
|
||||
src_watchdog = 1'b0;
|
||||
src_overrun = 1'b1;
|
||||
repeat (5) @(posedge ft_clk);
|
||||
pulse_status_req();
|
||||
check_status("T9 cic_fir_overrun asserted",
|
||||
check_status("T3 cic_fir_overrun asserted",
|
||||
32'h00000060,
|
||||
32'h00000040); // [6]=1
|
||||
check_dig7("T3 dig7 overrun -> high", 1'b1);
|
||||
|
||||
// T10 both, then both cleared
|
||||
// T4 both, then both cleared
|
||||
@(posedge clk_src); #1;
|
||||
src_watchdog = 1'b1;
|
||||
src_overrun = 1'b1;
|
||||
repeat (5) @(posedge ft_clk);
|
||||
pulse_status_req();
|
||||
check_status("T10a both asserted",
|
||||
check_status("T4a both asserted",
|
||||
32'h00000060,
|
||||
32'h00000060); // [6:5]=11
|
||||
check_dig7("T4a dig7 both -> high", 1'b1);
|
||||
|
||||
@(posedge clk_src); #1;
|
||||
src_watchdog = 1'b0;
|
||||
src_overrun = 1'b0;
|
||||
repeat (5) @(posedge ft_clk);
|
||||
pulse_status_req();
|
||||
check_status("T10b both cleared",
|
||||
check_status("T4b both cleared",
|
||||
32'h00000060,
|
||||
32'h00000000);
|
||||
check_dig7("T4b dig7 both cleared -> low", 1'b0);
|
||||
|
||||
// T11 reserved bit [7] stays 0 even when neighbours are 1
|
||||
// T5 reserved bit [7] stays 0 even when neighbours are 1
|
||||
@(posedge clk_src); #1;
|
||||
src_watchdog = 1'b1;
|
||||
src_overrun = 1'b1;
|
||||
repeat (5) @(posedge ft_clk);
|
||||
pulse_status_req();
|
||||
check_status("T11 [7] reserved stays 0",
|
||||
32'h00000080, // [7] only
|
||||
check_status("T5 [7] reserved stays 0",
|
||||
32'h00000080,
|
||||
32'h00000000);
|
||||
check_dig7("T5 dig7 both asserted -> high", 1'b1);
|
||||
|
||||
// T12 self_test_flags pass through unchanged
|
||||
// T6 self_test_flags pass through unchanged
|
||||
@(posedge clk_src); #1;
|
||||
src_watchdog = 1'b0;
|
||||
src_overrun = 1'b0;
|
||||
src_self_test_flags = 5'b10110;
|
||||
repeat (5) @(posedge ft_clk);
|
||||
pulse_status_req();
|
||||
check_status("T12 self_test_flags untouched",
|
||||
check_status("T6 self_test_flags untouched",
|
||||
32'h0000001F,
|
||||
32'h00000016);
|
||||
check_dig7("T6 dig7 cleared -> low", 1'b0);
|
||||
|
||||
$display("============================================================");
|
||||
$display("AUDIT-S10 RESULTS: pass=%0d fail=%0d", pass, fail);
|
||||
$display("STATUS STICKIES + DIG7 OR RESULTS: pass=%0d fail=%0d", pass, fail);
|
||||
$display("============================================================");
|
||||
if (fail == 0) $display("[OVERALL] PASS");
|
||||
else $display("[OVERALL] FAIL");
|
||||
@@ -282,31 +203,6 @@ module tb_audit_s10_gpio_split;
|
||||
|
||||
endmodule
|
||||
|
||||
// ============================================================================
|
||||
// gpio_split_block — mirrors the production fragment from radar_system_top.v
|
||||
// post AUDIT-S10. Two combinational ORs:
|
||||
// gpio_dig5 = signal-saturation classes (AGC + DDC + MTI)
|
||||
// gpio_dig7 = control-fault classes (range-decimator watchdog + CIC->FIR
|
||||
// CDC overrun)
|
||||
// ============================================================================
|
||||
module gpio_split_block (
|
||||
input wire [7:0] agc_saturation_count,
|
||||
input wire ddc_overflow_any,
|
||||
input wire [2:0] ddc_saturation_count,
|
||||
input wire [7:0] mti_saturation_count,
|
||||
input wire range_decim_watchdog,
|
||||
input wire ddc_cic_fir_overrun,
|
||||
output wire gpio_dig5,
|
||||
output wire gpio_dig7
|
||||
);
|
||||
assign gpio_dig5 = (agc_saturation_count != 8'd0)
|
||||
| ddc_overflow_any
|
||||
| (ddc_saturation_count != 3'd0)
|
||||
| (mti_saturation_count != 8'd0);
|
||||
assign gpio_dig7 = range_decim_watchdog
|
||||
| ddc_cic_fir_overrun;
|
||||
endmodule
|
||||
|
||||
// ============================================================================
|
||||
// status_packing_block — mirrors the production CDC fragment from
|
||||
// usb_data_interface.v (and usb_data_interface_ft2232h.v) for the AUDIT-S10
|
||||
@@ -355,3 +251,21 @@ module status_packing_block (
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
// ============================================================================
|
||||
// gpio_dig7_or_block — mirrors the production combinational OR in
|
||||
// radar_system_top.v:
|
||||
//
|
||||
// assign gpio_dig7 = rx_range_decim_watchdog | rx_ddc_cic_fir_overrun;
|
||||
//
|
||||
// MCU's PD15 stuck-high sampler at main.cpp:880-1000 fires
|
||||
// attemptErrorRecovery(ERROR_FPGA_DSP_STALL) → bitstream reload on either
|
||||
// fault class.
|
||||
// ============================================================================
|
||||
module gpio_dig7_or_block (
|
||||
input wire watchdog,
|
||||
input wire overrun,
|
||||
output wire gpio_dig7
|
||||
);
|
||||
assign gpio_dig7 = watchdog | overrun;
|
||||
endmodule
|
||||
Reference in New Issue
Block a user