Stop Guessing Your Reset Style: What Synchronous and Asynchronous Actually Do to Your FPGA

Every flip-flop in your FPGA has two reset options. Pick the wrong one and your design works perfectly in simulation, passes timing closure, and then corrupts its state at power-on — once, silently, under a specific timing condition you will never reproduce on a bench.

The choice between synchronous and asynchronous reset is not a style preference. The two options synthesize to fundamentally different silicon primitives, consume different resources, and interact with the FPGA fabric in different ways. This post shows what each one actually produces in hardware, where each one fails, and what the FPGA vendors actually recommend.

1. Synchronous Reset: The Clock as Guardian

A synchronous reset only takes effect on the rising clock edge. Between clock edges, any change on the reset signal is invisible to the flip-flop — the clock acts as a gate that filters everything, including reset. This makes synchronous reset completely glitch-immune and gives the timing tools a clean path: rst is treated like any other data input, subject to the same setup and hold constraints.

What the synthesizer actually produces is a 2-to-1 mux in front of the flip-flop’s D input. When rst is high, the mux selects the reset value. When rst is low, the mux passes d through. On Xilinx 7-series and UltraScale devices, this mux is absorbed into the LUT driving the FF — but it still consumes LUT logic. Every flip-flop with a synchronous reset costs one additional LUT.

sync_ff.sv
// Synchronous reset — rst only sampled on posedge clk always_ff @(posedge clk) begin if (rst) q <= ‘0; else q <= d; end // What Vivado synthesizes: // // rst clk // | | // 1 ───┤ _____|_____ // ├── MUX ── D ────────| |──── Q // d ───┤ (LUT) | FF | // |___________| // // The mux is absorbed into the LUT on the D input path. // Cost: 1 LUT per FF with synchronous reset. // Glitch on rst between clock edges: ignored entirely. // Clock stopped during rst assertion: reset cannot take effect.
Resource cost matters at scale: A single-FF synchronous reset costs one extra LUT. In a design with 2000 flip-flops, that is 2000 LUTs consumed by reset logic alone. Xilinx UltraScale flip-flop primitives have a dedicated synchronous reset pin (SR) that avoids this cost — but only if the reset value matches the primitive’s polarity. When it does not match, Vivado falls back to the LUT mux. Check your synthesis report for unexpected LUT usage on reset paths.

2. Asynchronous Reset: Instant, but Dangerous on Release

An asynchronous reset takes effect immediately, regardless of the clock. The flip-flop primitive has a dedicated R pin (on Xilinx: CLR or PRE) that overrides the D input and the clock entirely. No extra LUT is required — the reset path goes directly into the flip-flop primitive, which is why it is sometimes called a “free” reset.

The advantage is real: the flip-flop can be reset even when the clock is not running. Power-on initialization, clock domain startup, and emergency reset all work correctly with an asynchronous reset. The problem is not assertion — it is deassertion. When rst goes low and the flip-flop exits reset, if that transition lands near a clock edge, the flip-flop may go metastable. Worse: different flip-flops across the design will deassert at slightly different times. For one cycle, some FFs are still held in reset while others have already resumed normal operation — a brief but real inconsistency that can corrupt state.

async_ff.sv
// Asynchronous reset — rst takes effect immediately, no clock needed always_ff @(posedge clk or posedge rst) begin if (rst) q <= ‘0; else q <= d; end // What Vivado synthesizes: // // rst ────────────────────── CLR clk // | | // _____|________|_ // d ──────────────── D ──| |──── Q // | FF | // |______________| // // No LUT required — rst goes directly to the dedicated CLR pin. // // The deassertion problem: // // clk ___ ___ ___ ___ // | |___| |___| |___| |___ // // rst _____________ // |_______________________ <– deasserts here // ^ // └─ too close to clock edge: FF_A exits reset, // FF_B may not. One cycle of inconsistent state.
The deassertion window is real silicon physics: Flip-flop hold time on the async reset pin is typically 100–300 ps on modern FPGAs. A reset signal routed through several levels of logic before reaching different flip-flops can arrive with skew easily exceeding that window. Two FFs that look like they share the same reset in RTL can deassert on different clock cycles in silicon. This is not a simulation artifact — it is a routing-dependent timing problem that only appears on hardware.

3. The Reset Synchronizer: Assert Async, Deassert Sync

The industry solution — recommended by Xilinx, Intel, and every major FPGA vendor — is to get the advantages of both strategies without the weaknesses of either: assert asynchronously, deassert synchronously. Reset takes effect immediately (no clock required), but the release is synchronized so all flip-flops exit reset on the same clock edge with metastability fully resolved.

The circuit is a two-stage synchronizer on the deassertion path only. When rst_async goes high, both flip-flops are driven to 1 immediately through their asynchronous preset pins — the clock plays no role. When rst_async goes low, the value 0 propagates through the two-FF chain over two clock cycles before rst_sync releases the design. By then, any metastability has resolved and every downstream flip-flop sees a clean, edge-aligned deassertion.

reset_sync.sv
module reset_sync ( input logic clk, input logic rst_async, // raw reset — can arrive at any time output logic rst_sync // safe reset — synchronized deassertion ); logic [1:0] sync_chain; always_ff @(posedge clk or posedge rst_async) begin if (rst_async) sync_chain <= 2’b11; // assert: both FFs go high immediately else sync_chain <= {sync_chain[0], 1’b0}; // deassert: shift 0 through chain end assign rst_sync = sync_chain[1]; // Timing behavior: // // rst_async ___________ __________ // |__________________| // // clk _ _ _ _ _ _ _ _ _ // | |_| |_| |_| |_| |_| |_| |_| |_| // // rst_sync ___________ ______ // |______________________| // ^ // └─ 2 cycles after rst_async deasserts endmodule

4. Integrating the Reset Synchronizer

The reset synchronizer belongs at the top level of the design, once per clock domain. The raw board-level reset signal — which may be a button press, a power-good signal, or a PLL lock output — feeds into the synchronizer. The rst_sync output is then distributed to every submodule in that clock domain. All submodules use asynchronous reset in their flip-flops, but because the deassertion is synchronized, they all exit reset together on the same clock edge.

top.sv
module top ( input logic clk, input logic rst_btn // raw board-level reset (button, active high) ); logic rst; // synchronized reset distributed to all submodules reset_sync u_rst ( .clk (clk), .rst_async(rst_btn), .rst_sync (rst) ); // All submodules use async reset internally — safe because rst deasserts synchronously uart_top u_uart (.clk(clk), .rst(rst), /* … */ ); debouncer u_deb (.clk(clk), .rst(rst), /* … */ ); pipeline u_pipe (.clk(clk), .rst(rst), /* … */ ); // Rule: one reset_sync per clock domain. // A second clock domain needs its own reset_sync clocked by its own clock. endmodule

Final Thoughts: The FPGA Vendors Already Answered This

The debate between synchronous and asynchronous reset has a documented answer. Xilinx Application Note XAPP1226, Intel’s reset guidelines, and every serious FPGA methodology guide converge on the same recommendation: assert asynchronously, deassert synchronously via a reset synchronizer. This gives you immediate reset assertion without a clock dependency and a clean, metastability-free release that all flip-flops see on the same edge.

The reset synchronizer is a one-time write. Once it is in your project library, drop it at the top of every new design and connect every submodule’s reset to its output. The alternative — wrestling with reset-related power-on failures that only reproduce at certain temperatures, board revisions, or silicon lots — is a cost you pay indefinitely.


Happy coding.
fpgawizard.com

error: Selection is disabled!