When power comes up, the clock source takes time to stabilise. The design stays in reset until then.
Blocks come out in dependency order: first the clock status block, then the memory controller, then the CPU that uses it.
Read the lanes from left to right; the time scale before lock is not drawn to scale, because the PLL can take any time to lock.
Nothing is requested until pll_lock rises: the sequencer counts only qualified lock.
Request A rises at lock + 9: HOLD = 8 cycles of counting, plus one edge for the registered output.
B and C follow GAP = 4 cycles apart. These are requests, not local releases; the next sections show why order of completion needs an acknowledgement.
The sequencer's own reset must already be release-synchronised to the sequencer clock, using a reset synchroniser. The three outputs are registered release requests, not local reset completion. With HOLD = 8 and GAP = 4 they rise at lock + 9, +13 and +17 reference edges.
Each request then passes through its destination reset synchroniser. In ideal RTL local reset rises on the second subsequent destination edge; stopped clocks or physical late resolution can delay completion. Request order does not guarantee completion order. For a true A-before-B dependency, A must assert a readiness level after local reset and initialisation, synchronise that level back to the controller, and gate B's request on that acknowledgement. Repeat for B-before-C. Clear readiness and acknowledgement history on coordinated reset; mask traffic until both peers are ready. The fixed-gap sequencer below does not implement these acknowledgements.
The controller requests C's release. Local C reset still needs two subsequent C edges in ideal RTL, and C may need further initialisation before acknowledging readiness. A dependency must wait for that acknowledgement, not just a reference-clock gap.
`timescale 1ns/1ps
// Reset sequencer: releases three domain resets in a fixed order, after the PLL is locked.
module reset_seq #(parameter int HOLD = 8, parameter int GAP = 4) (
input logic clk, // always-running reference, not the monitored PLL output
input logic arst_n, // qualified faults assert asynchronously; release synchronised to clk
input logic pll_lock, // already synchronised to clk by the clock controller
output logic rst_a_n, rst_b_n, rst_c_n
);
logic [$clog2(HOLD + 2 * GAP + 2)-1:0] cnt; // sized from the parameters: it can always reach HOLD + 2 * GAP
always_ff @(posedge clk or negedge arst_n)
if (!arst_n) begin
cnt <= '0; rst_a_n <= 1'b0; rst_b_n <= 1'b0; rst_c_n <= 1'b0; // everything held in reset
end else if (!pll_lock) begin
cnt <= '0; rst_a_n <= 1'b0; rst_b_n <= 1'b0; rst_c_n <= 1'b0; // a lost lock restarts the sequence
end else begin
if (cnt != '1) cnt <= cnt + 1'b1; // saturate at all-ones, above every threshold
if (cnt >= HOLD) rst_a_n <= 1'b1; // 1st: A
if (cnt >= HOLD + GAP) rst_b_n <= 1'b1; // 2nd: B, GAP cycles later
if (cnt >= HOLD + 2 * GAP) rst_c_n <= 1'b1; // 3rd: C, GAP cycles after B
end
endmodule
Requirement | Source check |
|---|---|
Nothing released before lock | Checked after 10 cycles with |
Requests held at least HOLD cycles after lock | Source checks the registered request timing; expected first request at +9 |
Request order A, B, C, exactly GAP apart | Request cycles compared; no local completion claim |
Lock loss re-asserts everything | All three resets low on the edge after |
External fault/reset asserts without a clock | Directed check stops the reference, asserts arst_n, and requires all outputs low immediately and throughout the stop |
Phase 1 requests A/B/C at 10/50/90 ns with 70/14/22 ns destination periods: local releases come at 105/77/121 ns, so B overtakes A. Phase 2 gates each dependent request on a two-register readiness acknowledgement. Readiness here means only one local edge after reset release; replace it with the block's actual initialisation completion. Trace lines report destination edge numbers.
`timescale 1ns/1ps
module tb_reset_completion;
logic ref_clk = 0, ca = 0, cb = 0, cc = 0;
logic ra = 0, rb = 0, rc = 0;
logic [1:0] sa = 0, sb = 0, sc = 0;
logic ready_a = 0, ready_b = 0;
logic [1:0] ack_a = 0, ack_b = 0;
int ea = 0, eb = 0, ec = 0;
realtime ta = 0, tb = 0, tc = 0;
always #5 ref_clk = ~ref_clk;
always #35 ca = ~ca;
always #7 cb = ~cb;
always #11 cc = ~cc;
always @(posedge ca or negedge ra)
if (!ra) begin sa <= 0; ready_a <= 0; end
else begin sa <= {sa[0], 1'b1}; ready_a <= sa[1]; end
always @(posedge cb or negedge rb)
if (!rb) begin sb <= 0; ready_b <= 0; end
else begin sb <= {sb[0], 1'b1}; ready_b <= sb[1]; end
always @(posedge cc or negedge rc)
if (!rc) sc <= 0;
else sc <= {sc[0], 1'b1};
// Coordinated restart clears acknowledgement history before another sequence.
always @(posedge ref_clk or negedge ra)
if (!ra) begin ack_a <= 0; ack_b <= 0; end
else begin
ack_a <= {ack_a[0], ready_a};
ack_b <= {ack_b[0], ready_b};
end
always @(posedge ca) begin
ea++; #1;
if (sa[1] && ta == 0) begin ta = $realtime - 1; $display("A release: edge %0d at %0t", ea, ta); end
end
always @(posedge cb) begin
eb++; #1;
if (sb[1] && tb == 0) begin tb = $realtime - 1; $display("B release: edge %0d at %0t", eb, tb); end
end
always @(posedge cc) begin
ec++; #1;
if (sc[1] && tc == 0) begin tc = $realtime - 1; $display("C release: edge %0d at %0t", ec, tc); end
end
initial begin
$timeformat(-9, 0, " ns", 0); // print %t values in ns, not ps
#10 ra = 1;
#40 rb = 1;
#40 rc = 1;
#60;
if (ta != 105 || tb != 77 || tc != 121)
$fatal(1, "unexpected ideal destination-edge trace: %0t %0t %0t", ta, tb, tc);
// Restart away from all rising edges, then use completion acknowledgements.
#2; ra = 0; rb = 0; rc = 0;
#2;
if (sa !== 0 || sb !== 0 || sc !== 0 || ack_a !== 0 || ack_b !== 0)
$fatal(1, "restart did not clear reset/ack history");
ta = 0; tb = 0; tc = 0;
@(negedge ref_clk); ra = 1;
for (int n = 0; n < 100 && ack_a[1] !== 1'b1; n++) begin
@(posedge ref_clk); #1;
end
if (ack_a[1] !== 1'b1 || ta == 0) $fatal(1, "A readiness timeout");
@(negedge ref_clk); rb = 1;
for (int n = 0; n < 100 && ack_b[1] !== 1'b1; n++) begin
@(posedge ref_clk); #1;
end
if (ack_b[1] !== 1'b1 || tb == 0) $fatal(1, "B readiness timeout");
@(negedge ref_clk); rc = 1;
repeat (3) begin @(posedge cc); #2; end
if (!(ta < tb && tb < tc) || sc[1] !== 1'b1)
$fatal(1, "acknowledged completion order failed");
$display("PASS: request order can differ from completion; ready acknowledgements enforce dependencies");
$finish;
end
initial begin #5000; $fatal(1, "completion watchdog"); end
endmoduleSource checker, run with Icarus Verilog 13.0 (2026-10-16): prints PASS, with requests at +9, +13 and +17. It covers registered request order, spacing, hold time, lock loss and asynchronous assertion. Destination completion is checked separately.
Self-contained ideal-RTL checker, run with Icarus Verilog 13.0 (2026-10-16): prints the phase-1 trace at 105/77/121 ns, then PASS. These finite digital checks do not establish physical reset timing or CDC signoff.