Skip to content
VLSI Mentor

UART · Module 12

Reset Strategy and Reset During Active Traffic

Reset style and release, and what a reset asserted mid-frame actually leaves behind — measured, and worse than a framing error: well-formed frames carrying bytes nobody sent.

Reset looks like the simplest thing in a design and it is where a surprising number of bring-up failures live. Two questions matter, and they are unrelated to each other:

How does reset arrive and leave? That is a structural question with a standard answer, and §1–§3 give it.

What does reset leave behind on a link that was mid-transfer? That is a protocol question, it has no standard answer, and §4 measures it. The result is worse than the usual expectation: not a framing error but a perfectly well-formed frame carrying a byte that was never sent, with every status flag clear.

1. Assertion Is Easy; Release Is the Problem

AssertionRelease
Needs a clock?no, if asynchronousyes
Timing requirementnone — it is a forcing functionrecovery and removal, exactly like setup and hold
Consequence of getting it wrongnothingflops leave reset on different edges
Where it shows up—one cycle after release, then never again

The hazard is that release is a timing event. A reset that deasserts asynchronously, close to a clock edge, is captured by some flops on that edge and by others on the next. A state machine can then leave reset with its state register updated and its counter not, landing in a combination the designer never enumerated — and it happens once, at power-on, which is exactly when nobody is looking.

Asynchronous assertion is still wanted, because it works with no clock: at power-on, before a PLL locks, or when a clock has been gated away. The standard answer takes both — asynchronous assert, synchronous release.

2. The Reset Synchroniser

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Chapter 12.4 — asynchronous assertion, synchronous release.
// Reset asserts the instant the source asserts, with no clock required, and
// releases only on a clock edge — so every flop in this domain leaves reset
// on the SAME edge, which is the property recovery timing actually needs.
module uart_reset_sync #(
    parameter int unsigned STAGES = 2
) (
    input  logic clk,
    input  logic arst_n_i,      // asynchronous, from the board or a PLL lock
    output logic rst_n_o        // async assert, sync release, for this domain
);
    if (STAGES < 2) begin : g_bad_stages
        $error("uart_reset_sync: STAGES must be at least 2");
    end

    logic [STAGES-1:0] chain_q;

    always_ff @(posedge clk or negedge arst_n_i) begin
        if (!arst_n_i) chain_q <= '0;                 // ASYNC assert
        else           chain_q <= {chain_q[STAGES-2:0], 1'b1};  // SYNC release
    end

    assign rst_n_o = chain_q[STAGES-1];
endmodule

The whole design is in the sensitivity list. negedge arst_n_i makes assertion asynchronous; the shift register means the release propagates one stage per clock, so rst_n_o rises only on a clock edge — and every flop reset by it therefore leaves reset on the same edge.

Both properties measured:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  pass asserts with NO clock running at all
      clock stopped, arst_n driven low -> rst_n_o = 0
  pass does NOT release without a clock
  pass still in reset after 1 clock (STAGES=2)
  pass releases on the 2nd clock edge
      release took exactly STAGES = 2 clock edges

== 5 checks, 0 failures ==

The clock was stopped for the assertion test, which is the point: a design that needs a running clock to enter reset cannot be reset before its clock exists.

3. Reset in a Multi-Clock Design

One reset synchroniser per clock domain, all fed from the same asynchronous source:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
uart_reset_sync u_rst_bus  (.clk(bus_clk),  .arst_n_i(arst_n), .rst_n_o(bus_rst_n));
uart_reset_sync u_rst_uart (.clk(uart_clk), .arst_n_i(arst_n), .rst_n_o(uart_rst_n));

They assert together and release independently, each on its own clock — which is the correct behaviour and has a consequence worth stating: the two domains come out of reset at different times, by however many of the slower clock's periods the alignment demands.

For the asynchronous FIFO of Chapter 12.3 this matters. Its write side may leave reset while its read side is still held, and during that window the write side is pushing into a FIFO whose read pointer is pinned at zero. The structure survives it — the write side's full is computed from a read pointer it believes to be zero, which is the pessimistic direction — but the general lesson is that a design with two domains must be correct during the interval when only one of them is running.

4. Reset During a Frame

A serial link has no transaction boundary to abort at. The transmitter is holding a line at a level for a bit period; the receiver is counting sample ticks. Reset one of them and the other continues, because it is in a different chip and cannot know.

Setting up a real link — a transmitter and a receiver on separate reset synchronisers, exchanging real frames at 115,200 baud — and resetting one end 4.5 bit times into a frame:

The transmitter, reset mid-frame

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 2. reset the TRANSMITTER 4.5 bit times into a frame
      tx line just before reset = 1
      tx line during reset      = 1  (reset value is MARK)
      [B] received fd  frame_err=0
      [B] got 1 byte(s), 0 with framing errors

The far end received 0xFD with no framing error. The byte sent was 0xA5. Nothing anywhere reports a problem.

The arithmetic is exact and worth following, because it explains why this is the expected outcome rather than bad luck:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  0xA5 = 1010_0101, transmitted LSB-first:  1 0 1 0 0 1 0 1
  reset at 4.5 bit times = start bit + 3.5 data bits
      -> data bits 0,1,2 were sent:          1 0 1
      -> reset forces tx_o to MARK (idle):         1 1 1 1 1
  receiver assembles:                        1 0 1 1 1 1 1 1  =  0xFD
  then sees MARK where the stop bit belongs  -> stop bit VALID -> no error

The receiver, reset mid-frame

Worse, and less obvious. Sweeping the moment reset is released, with 0xC3 in flight:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 4. reset the RECEIVER mid-frame; sweep the RELEASE instant
      release +0.5 bit times -> B delivered 1 byte(s), last fe, fe=0
      release +1.0 bit times -> B delivered 1 byte(s), last ff, fe=0
      release +1.5 bit times -> B delivered 1 byte(s), last ff, fe=0
      release +2.0 bit times -> B delivered 1 byte(s), last ff, fe=0
      release +2.5 bit times -> B delivered 1 byte(s), last ff, fe=0
      release +3.0 bit times -> B delivered 0 byte(s)

The byte that was sent is never delivered. A different byte usually is — 0xFE or 0xFF depending on exactly when reset released — and always with no error flag.

The mechanism: the receiver comes out of reset in S_IDLE with no memory of being mid-frame, looks at the line, and finds it at SPACE — because a data bit happens to be zero. A data bit is indistinguishable from a start bit to a receiver that has just been reset, so it frames on it and assembles whatever follows into a byte. The value depends on the release instant, which is why the sweep produces different answers, and why no particular value is worth memorising: the point is that it is arbitrary and it is not flagged.

At +3.0 bit times the frame had already ended, so the receiver came back to a quiet line and correctly delivered nothing. That is the only benign case, and it is the one where reset missed the frame entirely.

A derivation showing two ways a mid-frame reset produces silent corruption. Along the first path, a reset asserted at the transmitter forces the transmit line to its reset value, which is the mark level. Mark is also the idle level and the stop-bit level, so the remaining data bits are received as ones and the stop bit check passes. The receiver therefore delivers a well-formed byte with wrong data and no error. Along the second path, a reset asserted at the receiver returns its state machine to idle with no memory of being mid-frame. On release it inspects the line and finds a data bit at the space level, which is indistinguishable from a start bit, so it frames on that data bit and assembles the following intervals into a byte that was never sent, again with no error. Both paths converge on the same conclusion: detection has to happen above the UART, in a protocol with a checksum or a length field.Mid-frame resetTX endRX endThe lineDelivered bytetx_o forced to MARKremaining bits readas onesMARK is a valid stop-> no errorFSM returns to IDLE,no memorya data bit at SPACElooks like a startframes on it,invents a bytewell-formed, wrong,unflagged
Figure 1 — how a mid-frame reset becomes silent corruption at each end. Both paths end in a well-formed frame that passes every check the receiver is able to make, which is why neither end reports anything.

0xA5 truncated into a valid 0xFD

12 cycles
A trace of twelve bit intervals showing a transmitter reset mid-frame. The frame begins with a start bit at the space level, followed by the first three data bits of the byte A5 transmitted least significant bit first, which are one, zero and one. Reset is then asserted, forcing the transmit line to the mark level for the remainder of the frame. The receiver, which knows nothing of the reset, samples the remaining five data bit intervals as ones and then finds the stop interval at mark, which is a legal stop bit. It therefore assembles the byte FD and delivers it with no framing error, although the byte actually intended was A5.intervals read as onesintervals read as onesreset — line forced to MARKreset — line forced to MARKMARK is a legal stop bitMARK is a legal stop bitintervalidlestartd0d1d2d3d4d5d6d7stopidletx_orst_nintended A5001010010111RX assembles0010111111ok0xFDt0t1t2t3t4t5t6t7t8t9t10t11
Figure 2 — the transmitter reset, bit by bit. Three data bits of 0xA5 reach the line before reset forces it to mark; the remaining five intervals are read as ones and the stop interval is legal, so 0xFD is delivered as a clean byte.
fsm
A state diagram of the UART receive state machine with reset transitions shown. The machine has five states: Idle, Start, Data, Parity and Stop, with the normal progression running from Idle through Start and Data to Parity or Stop and back to Idle. Overlaid on this, every one of the four active states — Start, Data, Parity and Stop — has an asynchronous reset transition back to Idle. Those transitions carry no information: the machine arrives in Idle indistinguishable from a machine that has just powered on, with no record that a frame was in progress. On release it therefore makes its next decision purely from the current line level, and a data bit at the space level looks exactly like a start bit.S_IDLES_STARTS_DATAS_PARITYS_STOPstart_candstart_candstill spacestill spacestore bitstore bitlast + paritylast + paritylast, no paritylast, no paritysamplesamplealwaysalways!rst_n!rst_n!rst_n!rst_n!rst_n!rst_n!rst_n — no record kept!rst_n — norecord kept
Figure 3 — why the receiver cannot know it was interrupted. Reset forces the state machine to Idle from every state, asynchronously and with no record that it happened. On release the machine is in exactly the state it occupies after power-on, so its next decision is made purely from the line level — and a data bit sitting at space is indistinguishable from a start bit.

Both ends recover

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 3. does the link recover?
      [B] received 3c  frame_err=0
  pass next frame after TX reset is clean
-- 5. does the receiver recover?
      [B] received 7e  frame_err=0
  pass next frame after RX reset is clean

The damage is confined to the frame in flight. That is worth stating because it is the design working: Chapter 6.2's decision that a bad stop returns to S_IDLE rather than retrying is what makes the next frame clean, and a receiver that tried to resynchronise cleverly would still be confused two frames later.

7 checks, 0 failures across the whole reset suite.

5. What To Do About It

Nothing, inside the UART. Every option is worse than the problem:

IdeaWhy it fails
hold reset until the frame completesreset must work when the design is wedged, which is when a frame will never complete
drive the line low on reset so the far end sees a breaka device powering on would emit a break into a healthy link
flag "reset occurred" to the far endrequires a channel that survives reset, which is the thing being reset
have the receiver detect impossible bit patternsthere are none; every byte value is legal

The answer is above the UART. A message protocol with a length and a checksum detects a truncated or fabricated byte; a raw byte stream cannot, because every byte is valid. §4's result is one of the clearest arguments for never treating a UART as a reliable byte pipe.

Inside the UART, the useful measures are smaller and real:

Reset the queues, not the engines, for a software-visible reset. Chapter 11.2 §4 already made cfg_tx_flush_i reset only the FIFO, so software can discard queued data without truncating the frame currently on the wire. A software reset that hits the engine turns a queue flush into §4's corruption.

Make the hardware reset genuinely global and rare. It is for power-on and for unwedging, not for flow control.

Ensure the far end can resynchronise, which a UART does automatically: an idle line plus a start edge is all a receiver needs, and §4 confirmed the next frame is clean in both directions.

6. The Reset Synchroniser in Verilog-2001 and VHDL-2008

The SystemVerilog above is the reference. Both translations are short, and both have one thing worth pointing at.

Verilog-2001 has no elaboration-time $error, so the depth guard becomes a runtime check in an initial block. That is genuinely weaker — it fires when the simulation starts rather than refusing to build — and it is what the language offers.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//===========================================================================
//  uart_reset_sync_v — Synthesizable Verilog-2001
//
//  Asynchronous assertion, synchronous release. Reset asserts the instant
//  the source asserts with no clock required, and releases only on a clock
//  edge -- so every flop in this domain leaves reset on the SAME edge, which
//  is the property recovery timing actually needs.
//
//  The chain fills with ones from the bottom, so rst_n_o is the LAST bit to
//  rise. That is the whole mechanism: STAGES clocks of settling between the
//  asynchronous release and this domain seeing it.
//===========================================================================
module uart_reset_sync_v #(
    parameter STAGES = 2          // MUST be >= 2 -- not enforceable in 2001
) (
    input  wire clk,
    input  wire arst_n_i,         // asynchronous, from the board or a PLL lock
    output wire rst_n_o           // async assert, sync release, for this domain
);
    reg [STAGES-1:0] chain_q;

    always @(posedge clk or negedge arst_n_i) begin
        if (!arst_n_i) chain_q <= {STAGES{1'b0}};                  // ASYNC assert
        else           chain_q <= {chain_q[STAGES-2:0], 1'b1};     // SYNC release
    end

    assign rst_n_o = chain_q[STAGES-1];
endmodule

VHDL puts the asynchronous assertion in the most legible form of the three. if arst_n_i = '0' then ... elsif rising_edge(clk) then ... inside a process sensitive to (clk, arst_n_i) says "assertion does not wait for a clock" without needing a reader to parse a sensitivity list for a negedge. And the depth guard is a concurrent assert checked at elaboration — the strongest of the three.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
--==========================================================================
--  uart_reset_sync -- Synthesizable VHDL-2008
--
--  Asynchronous assertion, synchronous release. Reset asserts the instant
--  the source asserts with no clock required, and releases only on a clock
--  edge -- so every flop in this domain leaves reset on the SAME edge.
--==========================================================================
library ieee;
use ieee.std_logic_1164.all;

entity uart_reset_sync is
    generic (
        STAGES : positive := 2
    );
    port (
        clk      : in  std_logic;
        arst_n_i : in  std_logic;   -- asynchronous, from the board or a PLL
        rst_n_o  : out std_logic    -- async assert, sync release
    );
end entity uart_reset_sync;

architecture rtl of uart_reset_sync is
    signal chain_q : std_logic_vector(STAGES-1 downto 0);
begin

    assert STAGES >= 2
        report "uart_reset_sync: STAGES must be at least 2" severity failure;

    rst_n_o <= chain_q(STAGES-1);

    process (clk, arst_n_i)
    begin
        if arst_n_i = '0' then
            chain_q <= (others => '0');                            -- ASYNC assert
        elsif rising_edge(clk) then
            chain_q <= chain_q(STAGES-2 downto 0) & '1';           -- SYNC release
        end if;
    end process;

end architecture rtl;

7. Testing a Reset, Which Means Stopping the Clock

Most testbenches for a reset synchroniser test the half that is easy to test and quietly skip the half that matters.

The release is easy. Drive arst_n_i high and count clock edges until rst_n_o follows. It must be exactly STAGES. Three instances at STAGES of 2, 3 and 4 make that a measurement rather than an assumption.

The assertion is the one that needs care, because a testbench with a free-running clock cannot tell an asynchronous reset from a synchronous one. Drive the source low while the clock is running and both designs deassert rst_n_o within a cycle; the waveforms are nearly identical and the check passes either way. So the suite stops the clock:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  PASS clock stopped, source still high: reset released
  PASS ASYNC assert: rst_n_o fell with the clock STOPPED
  PASS every depth asserts without a clock
  PASS release with the clock stopped does NOT take effect
  PASS release completes two edges after the clock returns

That pair of checks is the whole contract: assertion needs no clock; release needs a clock. A design that fails either one is broken in a way that will appear at power-on, before any clock is stable, and not in any test that ran with a clock.

One further check earns its place — a 1 ns assertion pulse, far shorter than a clock period, must still reach the output. It does, because the assertion path does not go through a flop's clock input at all. A design that required a minimum pulse width here would drop a short power-supply glitch on the reset line, which is exactly the event reset exists to survive.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//===========================================================================
//  tb_uart_reset_sync — self-checking SystemVerilog testbench
//
//  The two halves of the contract are tested with deliberately different
//  techniques, because they are different kinds of claim:
//
//    ASSERT  is asynchronous. The test STOPS THE CLOCK and asserts the
//            source; rst_n_o must fall anyway. A test that keeps the clock
//            running cannot distinguish an async reset from a sync one, and
//            will pass on a design that has no async path at all.
//    RELEASE is synchronous. The test measures the number of active edges
//            between the source releasing and this domain seeing it, and
//            requires exactly STAGES.
//
//  Same 16 counted checks as the Verilog twin.
//===========================================================================
`timescale 1ns/1ps

module tb_uart_reset_sync;

    logic clk = 1'b0;
    logic clk_en = 1'b1;               // lets the test stop the clock
    always #5 if (clk_en) clk = ~clk;

    logic arst_n = 1'b0;
    logic r2, r3, r4;

    uart_reset_sync #(.STAGES(2)) d2 (.clk(clk), .arst_n_i(arst_n), .rst_n_o(r2));
    uart_reset_sync #(.STAGES(3)) d3 (.clk(clk), .arst_n_i(arst_n), .rst_n_o(r3));
    uart_reset_sync #(.STAGES(4)) d4 (.clk(clk), .arst_n_i(arst_n), .rst_n_o(r4));

    int checks = 0, failures = 0;

    task automatic check(input logic cond, input string name);
        checks++;
        if (cond) $display("  PASS %0s", name);
        else begin failures++; $display("  FAIL %0s", name); end
    endtask

    // rst_n_o must never be high while the source is low. This is sampled on a
    // fine time grid that is deliberately OFFSET from the grid the stimulus
    // uses. An always @(*) observer — or a sampler aligned to whole
    // nanoseconds — wakes in the same simulation instant in which arst_n falls,
    // possibly BEFORE the non-blocking update of r2/r3/r4 for that instant has
    // been applied, and reports a violation that does not exist in the design.
    // Reading 0.3 ns after each whole nanosecond reads settled values only.
    int viol = 0;
    initial begin
        #0.3;
        forever begin
            if (arst_n === 1'b0 && (r2 === 1'b1 || r3 === 1'b1 || r4 === 1'b1)) begin
                viol++;
                a_no_glitch: assert (0)
                    else $error("rst_n_o high while the source is asserted");
            end
            #1;
        end
    end

    int lat;

    initial begin
        #200_000;
        $display("  FAIL watchdog: simulation did not finish");
        $display("== %0d checks, %0d failures ==", checks+1, failures+1);
        $display("   RESULT: SYSTEMVERILOG RESET-SYNC TESTS FAILED (timeout)");
        $finish;
    end

    initial begin
        $display("== uart_reset_sync : self-checking SystemVerilog testbench ==");

        arst_n = 1'b0;
        repeat (6) @(negedge clk);
        check(r2 === 1'b0 && r3 === 1'b0 && r4 === 1'b0,
              "source asserted: every domain reset is asserted");

        // ---- SYNCHRONOUS RELEASE, measured ---------------------------------
        @(negedge clk) arst_n = 1'b1;
        lat = 0;
        while (r2 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat++; end
        check(lat == 2, "STAGES=2: release takes exactly TWO active edges");
        check(r3 === 1'b0, "STAGES=3 has not released yet");
        while (r3 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat++; end
        check(lat == 3, "STAGES=3: release takes exactly THREE");
        while (r4 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat++; end
        check(lat == 4, "STAGES=4: release takes exactly FOUR");
        check(r2 === 1'b1 && r3 === 1'b1 && r4 === 1'b1, "all three released");

        // ---- ASYNCHRONOUS ASSERT, with the clock STOPPED -------------------
        repeat (4) @(negedge clk);
        @(negedge clk) clk_en = 1'b0;          // clock stops, parked low
        #50;
        check(r2 === 1'b1, "clock stopped, source still high: reset released");
        arst_n = 1'b0;                          // assert with NO clock
        #50;
        check(r2 === 1'b0, "ASYNC assert: rst_n_o fell with the clock STOPPED");
        check(r3 === 1'b0 && r4 === 1'b0, "every depth asserts without a clock");

        // ---- and release still needs a clock -------------------------------
        arst_n = 1'b1;
        #200;
        check(r2 === 1'b0, "release with the clock stopped does NOT take effect");
        clk_en = 1'b1;
        lat = 0;
        while (r2 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat++; end
        check(lat == 2, "release completes two edges after the clock returns");

        // ---- a narrow asynchronous assert is still captured -----------------
        repeat (4) @(negedge clk);
        arst_n = 1'b0;
        #1;
        check(r2 === 1'b0, "a 1 ns assert pulse still reaches the output");
        arst_n = 1'b1;
        lat = 0;
        while (r2 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat++; end
        check(lat == 2, "and the release is still synchronous afterwards");

        // ---- re-assert while already released --------------------------------
        repeat (4) @(negedge clk);
        @(negedge clk) arst_n = 1'b0;
        repeat (2) @(negedge clk);
        check(r2 === 1'b0, "re-assert works from the released state");
        @(negedge clk) arst_n = 1'b1;
        repeat (6) @(negedge clk);
        check(r2 === 1'b1, "and releases again");

        check(viol == 0, "rst_n_o never high while the source is low");

        $display("== %0d checks, %0d failures ==", checks, failures);
        if (failures == 0) $display("   RESULT: ALL SYSTEMVERILOG RESET-SYNC TESTS PASSED");
        else               $display("   RESULT: SYSTEMVERILOG RESET-SYNC TESTS FAILED");
        $finish;
    end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//===========================================================================
//  tb_uart_reset_sync_v — self-checking Verilog-2001 testbench
//
//  The two halves of the contract are tested with deliberately different
//  techniques, because they are different kinds of claim:
//
//    ASSERT  is asynchronous. The test STOPS THE CLOCK and asserts the
//            source; rst_n_o must fall anyway. A test that keeps the clock
//            running cannot distinguish an async reset from a sync one.
//    RELEASE is synchronous. The test measures the number of active edges
//            between the source releasing and this domain seeing it, and
//            requires exactly STAGES.
//
//  INDEPENDENCE: every expectation comes from the stimulus, never from
//  reading chain_q.
//===========================================================================
`timescale 1ns/1ps

module tb_uart_reset_sync_v;

    reg clk = 1'b0;
    reg clk_en = 1'b1;                 // lets the test stop the clock
    always #5 if (clk_en) clk = ~clk;

    reg  arst_n = 1'b0;
    wire r2, r3, r4;

    uart_reset_sync_v #(.STAGES(2)) d2 (.clk(clk), .arst_n_i(arst_n), .rst_n_o(r2));
    uart_reset_sync_v #(.STAGES(3)) d3 (.clk(clk), .arst_n_i(arst_n), .rst_n_o(r3));
    uart_reset_sync_v #(.STAGES(4)) d4 (.clk(clk), .arst_n_i(arst_n), .rst_n_o(r4));

    integer checks = 0, failures = 0;

    task check;
        input cond;
        input [8*80-1:0] name;
        begin
            checks = checks + 1;
            if (cond) $display("  PASS %0s", name);
            else begin failures = failures + 1; $display("  FAIL %0s", name); end
        end
    endtask

    // rst_n_o must never be high while the source is low. This is sampled on a
    // fine time grid that is deliberately OFFSET from the grid the stimulus uses.
    // An always @(*) observer -- or a sampler aligned to whole nanoseconds --
    // wakes in the same simulation instant in which arst_n falls, possibly BEFORE
    // the non-blocking update of r2/r3/r4 for that instant has been applied, and
    // reports a violation that does not exist in the design. Reading 0.3 ns after
    // each whole nanosecond reads settled values only. This is a testbench
    // artefact to be engineered away, not a property of the DUT.
    integer viol = 0;
    initial begin
        #0.3;                          // step OFF the integer-ns stimulus grid
        forever begin
            if (arst_n === 1'b0 && (r2 === 1'b1 || r3 === 1'b1 || r4 === 1'b1))
                viol = viol + 1;
            #1;
        end
    end

    integer lat;

    initial begin
        #200_000;
        $display("  FAIL watchdog: simulation did not finish");
        $display("== %0d checks, %0d failures ==", checks+1, failures+1);
        $display("   RESULT: VERILOG RESET-SYNC TESTS FAILED (timeout)");
        $finish;
    end

    initial begin
        $display("== uart_reset_sync_v : self-checking Verilog testbench ==");

        arst_n = 1'b0;
        repeat (6) @(negedge clk);
        check(r2 === 1'b0 && r3 === 1'b0 && r4 === 1'b0,
              "source asserted: every domain reset is asserted");

        // ---- SYNCHRONOUS RELEASE, measured ---------------------------------
        @(negedge clk) arst_n = 1'b1;
        lat = 0;
        while (r2 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat = lat + 1; end
        check(lat == 2, "STAGES=2: release takes exactly TWO active edges");
        check(r3 === 1'b0, "STAGES=3 has not released yet");
        while (r3 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat = lat + 1; end
        check(lat == 3, "STAGES=3: release takes exactly THREE");
        while (r4 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat = lat + 1; end
        check(lat == 4, "STAGES=4: release takes exactly FOUR");
        check(r2 === 1'b1 && r3 === 1'b1 && r4 === 1'b1, "all three released");

        // ---- ASYNCHRONOUS ASSERT, with the clock STOPPED -------------------
        // This is the half a running-clock test cannot distinguish. The clock
        // is halted, the source is asserted, and the outputs must fall with
        // no active edge available to carry them.
        repeat (4) @(negedge clk);
        @(negedge clk) clk_en = 1'b0;          // clock stops, parked low
        #50;
        check(r2 === 1'b1, "clock stopped, source still high: reset released");
        arst_n = 1'b0;                          // assert with NO clock
        #50;
        check(r2 === 1'b0, "ASYNC assert: rst_n_o fell with the clock STOPPED");
        check(r3 === 1'b0 && r4 === 1'b0, "every depth asserts without a clock");

        // ---- and release still needs a clock -------------------------------
        arst_n = 1'b1;
        #200;
        check(r2 === 1'b0, "release with the clock stopped does NOT take effect");
        clk_en = 1'b1;                          // clock restarts
        lat = 0;
        while (r2 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat = lat + 1; end
        check(lat == 2, "release completes two edges after the clock returns");

        // ---- a narrow asynchronous assert is still captured -----------------
        // A reset pulse shorter than a clock period must still assert, because
        // the assertion path does not go through a flop's clock input.
        repeat (4) @(negedge clk);
        arst_n = 1'b0;
        #1;
        check(r2 === 1'b0, "a 1 ns assert pulse still reaches the output");
        arst_n = 1'b1;
        lat = 0;
        while (r2 !== 1'b1 && lat < 12) begin @(posedge clk); #1; lat = lat + 1; end
        check(lat == 2, "and the release is still synchronous afterwards");

        // ---- re-assert while already released --------------------------------
        repeat (4) @(negedge clk);
        @(negedge clk) arst_n = 1'b0;
        repeat (2) @(negedge clk);
        check(r2 === 1'b0, "re-assert works from the released state");
        @(negedge clk) arst_n = 1'b1;
        repeat (6) @(negedge clk);
        check(r2 === 1'b1, "and releases again");

        check(viol == 0, "rst_n_o never high while the source is low");

        $display("== %0d checks, %0d failures ==", checks, failures);
        if (failures == 0) $display("   RESULT: ALL VERILOG RESET-SYNC TESTS PASSED");
        else               $display("   RESULT: VERILOG RESET-SYNC TESTS FAILED");
        $finish;
    end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
--===========================================================================
--  tb_uart_reset_sync — self-checking VHDL-2008 testbench
--
--  The two halves of the contract are tested with deliberately different
--  techniques, because they are different kinds of claim:
--
--    ASSERT  is asynchronous. The test STOPS THE CLOCK and asserts the
--            source; rst_n_o must fall anyway. A test that keeps the clock
--            running cannot distinguish an async reset from a sync one, and
--            will pass on a design that has no async path at all.
--    RELEASE is synchronous. The test measures the number of active edges
--            between the source releasing and this domain seeing it, and
--            requires exactly STAGES.
--
--  Same 16 counted checks as the Verilog and SystemVerilog twins.
--===========================================================================
library ieee;
use ieee.std_logic_1164.all;

entity tb_uart_reset_sync is
end entity tb_uart_reset_sync;

architecture sim of tb_uart_reset_sync is

    constant TCLK : time := 10 ns;

    signal clk    : std_logic := '0';
    signal clk_en : boolean   := true;      -- lets the test stop the clock
    signal done   : boolean   := false;

    signal arst_n : std_logic := '0';
    signal r2, r3, r4 : std_logic;

    signal viol : natural := 0;

begin

    -- Parked LOW when disabled, which is what the assert-without-a-clock test
    -- needs: no edge of any kind is available to carry the assertion.
    clk <= '0' when (done or not clk_en) else not clk after TCLK/2;

    d2 : entity work.uart_reset_sync generic map (STAGES => 2)
        port map (clk => clk, arst_n_i => arst_n, rst_n_o => r2);
    d3 : entity work.uart_reset_sync generic map (STAGES => 3)
        port map (clk => clk, arst_n_i => arst_n, rst_n_o => r3);
    d4 : entity work.uart_reset_sync generic map (STAGES => 4)
        port map (clk => clk, arst_n_i => arst_n, rst_n_o => r4);

    -- rst_n_o must never be high while the source is low. Sampled on a fine
    -- grid OFFSET from the whole-nanosecond grid the stimulus uses, so that
    -- every read is of settled values. VHDL's delta-cycle model makes this
    -- less critical than it is in Verilog, where the same observer written
    -- combinationally reports violations that do not exist; keeping the two
    -- files structurally identical is worth more than the saved line.
    obs : process
    begin
        wait for 0.3 ns;
        loop
            if arst_n = '0' and (r2 = '1' or r3 = '1' or r4 = '1') then
                viol <= viol + 1;
            end if;
            exit when done;
            wait for 1 ns;
        end loop;
        wait;
    end process obs;

    watchdog : process
    begin
        wait for 200 us;
        report "watchdog: simulation did not finish" severity failure;
    end process watchdog;

    stim : process
        variable checks, failures : natural := 0;
        variable lat              : natural := 0;

        procedure check(cond : boolean; name : string) is
        begin
            checks := checks + 1;
            if cond then
                report "  PASS " & name severity note;
            else
                failures := failures + 1;
                report "  FAIL " & name severity error;
            end if;
        end procedure check;
    begin
        report "== uart_reset_sync : self-checking VHDL testbench ==" severity note;

        arst_n <= '0';
        for i in 1 to 6 loop wait until falling_edge(clk); end loop;
        check(r2 = '0' and r3 = '0' and r4 = '0',
              "source asserted: every domain reset is asserted");

        ---- SYNCHRONOUS RELEASE, measured ------------------------------------
        wait until falling_edge(clk); arst_n <= '1';
        lat := 0;
        while r2 /= '1' and lat < 12 loop
            wait until rising_edge(clk); wait for 1 ns; lat := lat + 1;
        end loop;
        check(lat = 2, "STAGES=2: release takes exactly TWO active edges");
        check(r3 = '0', "STAGES=3 has not released yet");
        while r3 /= '1' and lat < 12 loop
            wait until rising_edge(clk); wait for 1 ns; lat := lat + 1;
        end loop;
        check(lat = 3, "STAGES=3: release takes exactly THREE");
        while r4 /= '1' and lat < 12 loop
            wait until rising_edge(clk); wait for 1 ns; lat := lat + 1;
        end loop;
        check(lat = 4, "STAGES=4: release takes exactly FOUR");
        check(r2 = '1' and r3 = '1' and r4 = '1', "all three released");

        ---- ASYNCHRONOUS ASSERT, with the clock STOPPED ----------------------
        -- This is the half a running-clock test cannot distinguish.
        for i in 1 to 4 loop wait until falling_edge(clk); end loop;
        wait until falling_edge(clk); clk_en <= false;
        wait for 50 ns;
        check(r2 = '1', "clock stopped, source still high: reset released");
        arst_n <= '0';                       -- assert with NO clock
        wait for 50 ns;
        check(r2 = '0', "ASYNC assert: rst_n_o fell with the clock STOPPED");
        check(r3 = '0' and r4 = '0', "every depth asserts without a clock");

        ---- and release still needs a clock -----------------------------------
        arst_n <= '1';
        wait for 200 ns;
        check(r2 = '0', "release with the clock stopped does NOT take effect");
        clk_en <= true;
        lat := 0;
        while r2 /= '1' and lat < 12 loop
            wait until rising_edge(clk); wait for 1 ns; lat := lat + 1;
        end loop;
        check(lat = 2, "release completes two edges after the clock returns");

        ---- a narrow asynchronous assert is still captured ---------------------
        -- A reset pulse shorter than a clock period must still assert, because
        -- the assertion path does not go through a flop's clock input.
        for i in 1 to 4 loop wait until falling_edge(clk); end loop;
        arst_n <= '0';
        wait for 1 ns;
        check(r2 = '0', "a 1 ns assert pulse still reaches the output");
        arst_n <= '1';
        lat := 0;
        while r2 /= '1' and lat < 12 loop
            wait until rising_edge(clk); wait for 1 ns; lat := lat + 1;
        end loop;
        check(lat = 2, "and the release is still synchronous afterwards");

        ---- re-assert while already released -----------------------------------
        for i in 1 to 4 loop wait until falling_edge(clk); end loop;
        wait until falling_edge(clk); arst_n <= '0';
        for i in 1 to 2 loop wait until falling_edge(clk); end loop;
        check(r2 = '0', "re-assert works from the released state");
        wait until falling_edge(clk); arst_n <= '1';
        for i in 1 to 6 loop wait until falling_edge(clk); end loop;
        check(r2 = '1', "and releases again");

        check(viol = 0, "rst_n_o never high while the source is low");

        report "== " & integer'image(checks) & " checks, "
                     & integer'image(failures) & " failures ==" severity note;
        if failures = 0 then
            report "   RESULT: ALL VHDL RESET-SYNC TESTS PASSED" severity note;
        else
            report "   RESULT: VHDL RESET-SYNC TESTS FAILED" severity error;
        end if;
        done <= true;
        wait;
    end process stim;

end architecture sim;

Sixteen checks, and all three languages agree:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Verilog-2001    : 16 checks, 0 failures
SystemVerilog   : 16 checks, 0 failures
VHDL-2008       : 16 checks, 0 failures

8. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Assertion — reset assertion needs no clock. Checked by stopping the clock
// entirely, which is the only way to test it meaningfully.
// Measured: with the clock halted, driving arst_n low took rst_n_o low.

// Assertion — release is synchronous: rst_n_o may only RISE at a clock edge.
property p_release_is_synchronous;
    @(negedge clk) disable iff (!arst_n_i)
        $rose(rst_n_o) |-> 1'b0;    // must never rise away from posedge clk
endproperty

// Assertion — every flop in the domain leaves reset together. This is the
// property the whole structure exists to provide, and it is why a design
// must never mix a synchronised reset with the raw asynchronous one.
property p_one_release_edge;
    @(posedge clk) $rose(rst_n_o) |-> (u_rx.state_q == S_IDLE && u_tx.state_q == S_IDLE);
endproperty

Test reset with traffic in flight, not only at time zero. A reset test that asserts before any activity and releases into a quiet design verifies almost nothing — it is the state every simulation starts in anyway. The interesting cases are all mid-transfer, and §4 is what they look like.

Sweep the release instant. The receiver result changed with the release offset, and a single-point test would have reported one arbitrary value as though it were the behaviour. Where an outcome depends on alignment, one measurement is an anecdote.

Check recovery explicitly. "Is the next frame clean?" is a different question from "was this frame damaged?", and it is the one that determines whether a reset is survivable or fatal.

Mutation testing

Two defects were installed in uart_reset_sync, each verified to have actually changed the source before being scored.

#Defect installedResult
M4assertion made synchronous — the negedge arst_n_i removed from the sensitivity listkilled, 7 checks
M5rst_n_o taken from the first flop instead of stage STAGES-1killed, 6 checks

M4 is the defect this chapter exists to prevent, and it is worth noticing which checks kill it. Not the release-latency checks — those still pass, because a synchronous-assert design releases on exactly the same edge. The seven failures are all in the clock-stopped block: the reset that must fall with no clock available does not fall, and everything downstream of that assumption collapses.

Had the suite been written with a free-running clock throughout — which is the natural way to write it — M4 would have survived every check. The design would have shipped with a reset that cannot assert before the PLL locks, and the failure would first appear on a board, intermittently, at power-on.

9. Debugging

10. What This Means on an FPGA

FPGAs initialise flops at configuration, so a reset is often not needed for correctness at all — the bitstream sets the initial state. A reset is still wanted for re-initialisation without reconfiguring, and adding one to every flop consumes routing for no benefit. Reset what genuinely needs a defined state; leave datapath registers alone.

ASIC flows are the opposite: nothing has a defined state at power-on, so every flop that is read before it is written must be reset.

Prefer one reset style per domain. Mixing synchronous and asynchronous resets in one clock domain gives the tool two different timing problems on the same signal and makes the reset tree harder to close — Chapter 12.5.

The release path needs a constraint too. Recovery and removal are real checks on a real path, and an unconstrained reset tree closes them by luck.

11. Understanding Check

12. Summary

Asynchronous assert, synchronous release. Assertion must work with no clock — verified with the clock stopped — and release must happen on a clock edge so every flop in the domain leaves reset together. Release took exactly STAGES edges, as designed.

Release is the timing event, with recovery and removal requirements analogous to setup and hold, and its failure appears once, at power-on.

The reset synchroniser's own asynchronous input is not a violation: it drives reset pins, not data inputs, and those carry different rules.

One synchroniser per clock domain, all from one source: they assert together and release independently, so the design must be correct while only one domain is running.

A transmitter reset mid-frame produces a well-formed frame with the wrong byte. Sending 0xA5 and resetting 4.5 bit times in delivered 0xFD with no framing error, because the line's reset value is mark and mark is a legal stop bit.

A receiver reset mid-frame can invent a byte, framing on a data bit that happens to sit at space. Sweeping the release instant gave 0xFE and 0xFF; the transmitted 0xC3 was never delivered and nothing was flagged.

Both ends recover on the next frame — the damage is confined, which is Chapter 6.2's return-to-idle decision paying off.

And the conclusion that generalises past UART: when every value is legal, the protocol above has to carry the check.

13. What Comes Next

The design is now correct in RTL terms — synchronised, reset properly, with the crossings identified. Chapter 12.5 hands it to synthesis, which will believe exactly what it is told.

That means constraining an asynchronous input that has no real setup requirement, the recovery and removal checks this chapter created, and the two things a tool will silently build if the RTL is ambiguous: an inferred latch and an accidental gated clock.

Browse the full path on the UART tutorials index. For the transmit line's reset value that makes §4's corruption silent, read back to Chapter 7.1.

Continue learning

Where this fits

Part of the UART curriculum.