mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-05-14 03:42:00 +00:00
test(fpga): PR-X.1 F-7.4 — gate tb_ad9484_xsim on MMCM lock (closes PR-N #86)
Stage-7 ADC chain audit. The MMCM SIMULATION model in adc_clk_mmcm.v takes 4096 DCO cycles (~10 µs at 400 MHz) to assert mmcm_locked. The existing TB waited only ~5 cycles after each reset deassert, so the gated reset_n_400m never released and adc_data_valid_400m stayed low for every test group past the initial reset checks. Expose mmcm_locked as a new module output on ad9484_interface_400m (real path) and ad9484_interface_400m_stub (sim path; tied high one DCO cycle after reset deassert since the stub has no MMCM). Connect it through to tb_ad9484_xsim.v and add a `wait_for_adc_ready` task that waits on the lock signal plus 6 DCO cycles for the 2-FF lock-sync, 2-FF reset-sync, and pipeline drain. Apply the task at each of the five reset cycles in the testbench. radar_receiver_final.v: tie the new port off (.mmcm_locked()) — host status pipeline doesn't surface lock yet, deferred for a future status-word widening. Local iverilog regression (36/0/7) clean. xsim verification of the xsim-only TB itself is pending (remote Vivado host).
This commit is contained in:
@@ -21,7 +21,13 @@ module ad9484_interface_400m (
|
||||
output wire adc_dco_bufg, // Buffered 400MHz DCO clock for downstream use
|
||||
// Audit F-0.1: OR flag, clk_400m domain. High on any sample in the
|
||||
// current 400 MHz cycle where the ADC reports overrange.
|
||||
output wire adc_overrange_400m
|
||||
output wire adc_overrange_400m,
|
||||
// Audit F-7.4: MMCM lock indicator. Surfaces the internal mmcm_locked
|
||||
// wire so testbenches (and, in future, the host status pipeline) can
|
||||
// gate on a stable jitter-cleaned 400 MHz clock instead of guessing at
|
||||
// a fixed wait. Combinational MMCME2 output — synchronize before use
|
||||
// outside the clk_400m domain.
|
||||
output wire mmcm_locked
|
||||
);
|
||||
|
||||
// LVDS to single-ended conversion
|
||||
@@ -80,15 +86,18 @@ BUFIO bufio_dco (
|
||||
// MMCME2 jitter-cleaning wrapper replaces the direct BUFG.
|
||||
// The PLL feedback loop attenuates input jitter from ~50 ps to ~20-30 ps,
|
||||
// reducing clock uncertainty and improving WNS on the 400 MHz CIC path.
|
||||
wire mmcm_locked;
|
||||
// Audit F-7.4: mmcm_locked is now exposed as a module output — see the
|
||||
// port-list comment above.
|
||||
wire mmcm_locked_int;
|
||||
|
||||
adc_clk_mmcm mmcm_inst (
|
||||
.clk_in (adc_dco), // 400 MHz from IBUFDS output
|
||||
.reset_n (reset_n),
|
||||
.clk_400m_out (adc_dco_buffered), // Jitter-cleaned 400 MHz on BUFG
|
||||
.mmcm_locked (mmcm_locked)
|
||||
.mmcm_locked (mmcm_locked_int)
|
||||
);
|
||||
assign adc_dco_bufg = adc_dco_buffered;
|
||||
assign mmcm_locked = mmcm_locked_int;
|
||||
|
||||
// AUDIT-C4 (2026-05-01): AD9484 outputs SDR LVDS (datasheet p.5: "Output
|
||||
// (LVDS—SDR)"; p.16: "data outputs are valid on the rising edge of DCO").
|
||||
|
||||
@@ -301,6 +301,9 @@ wire adc_valid; // Data valid signal
|
||||
assign adc_pwdn = host_adc_pwdn;
|
||||
|
||||
wire adc_overrange_400m;
|
||||
// Audit F-7.4: mmcm_locked exposed as a port for sim-side gating; not
|
||||
// surfaced to the host status pipeline yet (deferred for a future status
|
||||
// widening). Leave unconnected here.
|
||||
ad9484_interface_400m adc (
|
||||
.adc_d_p(adc_d_p),
|
||||
.adc_d_n(adc_d_n),
|
||||
@@ -313,7 +316,8 @@ ad9484_interface_400m adc (
|
||||
.adc_data_400m(adc_data_cmos),
|
||||
.adc_data_valid_400m(adc_valid),
|
||||
.adc_dco_bufg(clk_400m),
|
||||
.adc_overrange_400m(adc_overrange_400m)
|
||||
.adc_overrange_400m(adc_overrange_400m),
|
||||
.mmcm_locked()
|
||||
);
|
||||
|
||||
// Audit F-0.1: stickify the 400 MHz OR pulse, then CDC to clk_100m via 2FF.
|
||||
|
||||
@@ -40,7 +40,11 @@ module ad9484_interface_400m (
|
||||
output wire [7:0] adc_data_400m,
|
||||
output wire adc_data_valid_400m,
|
||||
output wire adc_dco_bufg,
|
||||
output wire adc_overrange_400m
|
||||
output wire adc_overrange_400m,
|
||||
// Audit F-7.4: stub has no MMCM, but must mirror the real module's
|
||||
// port shape. Tie the lock indicator HIGH after reset deassert so
|
||||
// testbenches that gate on it (e.g. tb_ad9484_xsim) don't stall.
|
||||
output wire mmcm_locked
|
||||
);
|
||||
|
||||
// Pass-through clock (no BUFG needed in simulation)
|
||||
@@ -74,4 +78,16 @@ always @(posedge adc_dco_p or negedge reset_n) begin
|
||||
end
|
||||
assign adc_overrange_400m = adc_overrange_400m_reg;
|
||||
|
||||
// Audit F-7.4: mock the real MMCM lock indicator. The synthesizable
|
||||
// path takes ~4096 DCO cycles to lock; here we just track reset_n so
|
||||
// callers see "locked" within one DCO edge of reset deassert.
|
||||
reg mmcm_locked_stub;
|
||||
always @(posedge adc_dco_p or negedge reset_n) begin
|
||||
if (!reset_n)
|
||||
mmcm_locked_stub <= 1'b0;
|
||||
else
|
||||
mmcm_locked_stub <= 1'b1;
|
||||
end
|
||||
assign mmcm_locked = mmcm_locked_stub;
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -38,6 +38,17 @@ module tb_ad9484_xsim;
|
||||
wire [7:0] adc_data_400m;
|
||||
wire adc_data_valid_400m;
|
||||
wire adc_dco_bufg;
|
||||
// Audit F-7.4: mmcm_locked exposed by ad9484_interface_400m so the TB
|
||||
// can wait deterministically for the MMCM SIM model to lock (~4096
|
||||
// DCO cycles) instead of guessing at a fixed 5-cycle delay.
|
||||
wire mmcm_locked;
|
||||
|
||||
// Audit F-7.4: AD9484 OR (overrange) pair — TB doesn't drive overrange,
|
||||
// so tie complementary pair to a quiescent low/high so IBUFDS sees
|
||||
// valid differential input.
|
||||
reg adc_or_p = 1'b0;
|
||||
wire adc_or_n = ~adc_or_p;
|
||||
wire adc_overrange_400m;
|
||||
|
||||
// Differential complements
|
||||
assign adc_d_n = ~adc_d_p;
|
||||
@@ -59,13 +70,29 @@ module tb_ad9484_xsim;
|
||||
.adc_d_n (adc_d_n),
|
||||
.adc_dco_p (adc_dco_p),
|
||||
.adc_dco_n (adc_dco_n),
|
||||
.adc_or_p (adc_or_p),
|
||||
.adc_or_n (adc_or_n),
|
||||
.sys_clk (sys_clk),
|
||||
.reset_n (reset_n),
|
||||
.adc_data_400m (adc_data_400m),
|
||||
.adc_data_valid_400m(adc_data_valid_400m),
|
||||
.adc_dco_bufg (adc_dco_bufg)
|
||||
.adc_dco_bufg (adc_dco_bufg),
|
||||
.adc_overrange_400m(adc_overrange_400m),
|
||||
.mmcm_locked (mmcm_locked)
|
||||
);
|
||||
|
||||
// ── F-7.4: gate post-reset waiting on the MMCM lock indicator ──
|
||||
// The MMCM SIMULATION model takes 4096 DCO cycles (~10 µs at 400 MHz)
|
||||
// to assert mmcm_locked. The interface's reset synchronizer then
|
||||
// requires 2 more cycles for the 2-FF lock sync, plus 2 cycles for the
|
||||
// 2-FF reset sync. Allow a couple more for the data pipeline to fill.
|
||||
task wait_for_adc_ready;
|
||||
begin
|
||||
wait (mmcm_locked === 1'b1);
|
||||
repeat (6) @(posedge adc_dco_p);
|
||||
end
|
||||
endtask
|
||||
|
||||
// ── Check task ─────────────────────────────────────────────
|
||||
task check;
|
||||
input cond;
|
||||
@@ -134,40 +161,18 @@ module tb_ad9484_xsim;
|
||||
#(DCO_PERIOD * 0.3); // mid-cycle
|
||||
reset_n = 1;
|
||||
|
||||
// valid should NOT assert immediately (needs 2 sync stages)
|
||||
// F-7.4: valid stays 0 while the MMCM is locking. Verifying it
|
||||
// immediately after deassert covers the 2-FF reset-sync window
|
||||
// and is also vacuously true throughout the MMCM lock phase.
|
||||
@(posedge adc_dco_p); #0.1;
|
||||
// After 1 dco cycle: reset_sync_400m[0] = 1, [1] still = 0
|
||||
// So reset_n_400m should still be 0
|
||||
check(adc_data_valid_400m === 1'b0,
|
||||
"valid stays 0 for 1 cycle after reset de-assert (sync stage 1)");
|
||||
|
||||
@(posedge adc_dco_p); #0.1;
|
||||
// After 2 dco cycles: reset_sync_400m = 2'b11, reset_n_400m = 1
|
||||
// But the data pipeline has its own 1-cycle delay
|
||||
// So valid might assert this cycle or next
|
||||
|
||||
// Wait one more cycle for pipeline
|
||||
@(posedge adc_dco_p); #0.1;
|
||||
|
||||
// By now (3 dco cycles after reset de-assert), valid should be 1
|
||||
// Allow one more for IDDR pipeline
|
||||
begin : wait_valid
|
||||
reg saw_valid;
|
||||
saw_valid = 0;
|
||||
for (i = 0; i < 5; i = i + 1) begin
|
||||
@(posedge adc_dco_p); #0.1;
|
||||
if (adc_data_valid_400m) begin
|
||||
saw_valid = 1;
|
||||
$display(" valid asserted %0d dco cycles after reset de-assert", i + 4);
|
||||
disable wait_valid;
|
||||
end
|
||||
end
|
||||
if (!saw_valid) begin
|
||||
$display(" [WARN] valid did not assert within 8 dco cycles");
|
||||
end
|
||||
end
|
||||
// Wait for the MMCM SIM model to lock and the 2-FF reset-sync
|
||||
// pipeline to drain, then confirm the data pipeline starts producing.
|
||||
wait_for_adc_ready();
|
||||
check(adc_data_valid_400m === 1'b1,
|
||||
"valid asserts after reset sync pipeline completes");
|
||||
"valid asserts after MMCM lock + reset sync pipeline completes");
|
||||
|
||||
// ════════════════════════════════════════════════════════
|
||||
// TEST GROUP 4: Data capture via IDDR
|
||||
@@ -179,8 +184,9 @@ module tb_ad9484_xsim;
|
||||
adc_d_p = 8'h00;
|
||||
#100;
|
||||
reset_n = 1;
|
||||
// Wait for reset sync pipeline
|
||||
repeat (5) @(posedge adc_dco_p);
|
||||
// F-7.4: every reset cycle re-arms the MMCM lock countdown, so
|
||||
// wait for lock + sync drain before driving the test pattern.
|
||||
wait_for_adc_ready();
|
||||
|
||||
// Feed a known pattern on rising edges
|
||||
// IDDR in SAME_EDGE_PIPELINED mode captures:
|
||||
@@ -232,7 +238,7 @@ module tb_ad9484_xsim;
|
||||
reset_n = 0;
|
||||
#100;
|
||||
reset_n = 1;
|
||||
repeat (5) @(posedge adc_dco_p);
|
||||
wait_for_adc_ready(); // F-7.4: wait for MMCM lock + sync drain
|
||||
|
||||
// Feed incrementing pattern: 0, 1, 2, ... on each half-cycle
|
||||
begin : seq_test
|
||||
@@ -280,15 +286,15 @@ module tb_ad9484_xsim;
|
||||
// De-assert and verify sync pipeline
|
||||
#30;
|
||||
reset_n = 1;
|
||||
// Should NOT be valid yet (2-stage sync)
|
||||
// Should NOT be valid yet (2-stage sync + MMCM lock countdown)
|
||||
@(posedge adc_dco_p); #0.1;
|
||||
check(adc_data_valid_400m === 1'b0,
|
||||
"valid stays 0 during reset sync de-assertion");
|
||||
|
||||
// Wait for full pipeline
|
||||
repeat (5) @(posedge adc_dco_p); #0.1;
|
||||
// F-7.4: wait for MMCM lock + sync drain, then confirm reassert.
|
||||
wait_for_adc_ready();
|
||||
check(adc_data_valid_400m === 1'b1,
|
||||
"valid reasserts after sync pipeline completes");
|
||||
"valid reasserts after MMCM lock + sync pipeline completes");
|
||||
|
||||
// ════════════════════════════════════════════════════════
|
||||
// TEST GROUP 7: ADC power-down output
|
||||
@@ -317,7 +323,7 @@ module tb_ad9484_xsim;
|
||||
adc_d_p = 8'h00;
|
||||
#100;
|
||||
reset_n = 1;
|
||||
repeat (8) @(posedge adc_dco_p);
|
||||
wait_for_adc_ready(); // F-7.4: wait for MMCM lock + sync drain
|
||||
|
||||
begin : sdr_ramp
|
||||
reg [7:0] ramp_value;
|
||||
|
||||
Reference in New Issue
Block a user