A synchronous reset is ordinary D logic evaluated at the clock edge. The sensitivity list holds the clock alone.
An enable holds the value by reloading Q when disabled. The full module sits under Testbench below.
Feature | Implementation | Cost |
|---|---|---|
Enable | A 2:1 mux in front of D (feedback from Q), or a flop cell with a built-in enable | One mux per bit if the library lacks an enable flop |
Sync reset | An AND gate on the D input, or a flop with a sync-reset pin | One gate per bit |
Async reset | A dedicated reset pin on the flop | No logic on D, but a larger cell |
Read each panel from d on the left to the flop's D pin; the gate nearest D decides who wins.
Top panel: in reg_sync_en the AND sits last. When rst_n is 0, D is 0 whatever the mux chose, so reset overrides both load and hold.
Bottom panel: in reg_bad_priority the mux sits last. When en is 0 it picks the Q loop, and the reset AND is not on that path at all.
Both: the gates and their count are the same; only the order differs. The order is set by the order of the if tests.
Reset is written first so it wins over enable. A reset must work even when the enable is low.
Checking enable first is a classic bug, but the two shapes fail differently. The nested example below holds while disabled, so it ignores reset when en is 0 yet still clears when en is 1. A flatter chain such as if (en) before reset, as in the check below, instead loads d when en is 1 during reset.
// bad-example: enable checked first -> reset is ignored while en = 0
module reg_bad_priority (input logic clk, rst_n, en, input logic [7:0] d, output logic [7:0] q);
always_ff @(posedge clk)
if (en) begin
if (!rst_n) q <= '0;
else q <= d;
end
endmoduleif (en) q <= d; else if (!rst_n) q <= 0;. When en = 0 and rst_n = 0, what happens to q?en is checked first and is false, so the else if runs and reset applies. But when en = 1 and rst_n = 0 the register just loads d, ignoring reset. Reset must come first in the priority chain.
The register with synchronous reset and enable, plus directed checks for priority, load, and hold. After loading nonzero Q, the test asserts reset while disabled: Q must hold before the next rising edge and clear after it. The nested bad-priority module above would retain the nonzero value at that edge. Checks use a 10 ns clock and observe Q 1 ns after the edge, after NBA updates.
module reg_sync_en #(parameter int W = 8) (
input logic clk, rst_n, en,
input logic [W-1:0] d,
output logic [W-1:0] q
);
always_ff @(posedge clk)
if (!rst_n) q <= '0; // reset has the highest priority
else if (en) q <= d; // load only when enabled
// else: hold (implicit)
endmodule
Testbench checks use a 10 ns clock and observe Q 1 ns after the edge, after NBA updates. This is an ideal RTL check, not a physical timing measurement.