D Flip-Flop — Synchronous & Asynchronous Reset
Complete D flip-flop implementations with synchronous reset and asynchronous reset — behavioral and structural models, timing diagrams, characteristic tables, and exhaustive self-checking testbenches that verify reset timing behaviour precisely.
🔁 Introduction & Theory
A D flip-flop (Data or Delay flip-flop) is the fundamental sequential building block in digital design. On the rising edge of the clock, it captures the value of the D input and presents it at the Q output — holding that value until the next rising clock edge. Every register, counter, shift register, and state machine is built from D flip-flops.
📊 Characteristic Table
Synchronous Reset DFF
| clk | rst_n | D | Q (next) |
|---|---|---|---|
| ↑ | 0 | x | 0 |
| ↑ | 1 | 0 | 0 |
| ↑ | 1 | 1 | 1 |
| no edge | x | x | Q (holds) |
Reset only takes effect at the clock edge. Between edges, Q holds its stored value regardless of rst_n.
Asynchronous Reset DFF
| rst_n | clk | D | Q (next) |
|---|---|---|---|
| 0 | x | x | 0 |
| 1 | ↑ | 0 | 0 |
| 1 | ↑ | 1 | 1 |
| 1 | no edge | x | Q (holds) |
Reset asserts immediately when rst_n=0, even between clock edges. Q goes to 0 without waiting for clk.
⚖️ Synchronous vs Asynchronous Reset
🔵 Synchronous Reset
Reset is sampled at the clock edge — it is just another data input.
always @(posedge clk) begin if (!rst_n) q <= 1'b0; // at clock edge only else q <= d; end
- Sensitivity list:
posedge clkonly - Reset glitches filtered by clock
- Preferred by ASIC STA tools
- Synthesises to DFF with mux on D
- Requires clock to be running at reset
🔴 Asynchronous Reset
Reset is in the sensitivity list — it triggers the always block immediately.
always @(posedge clk or negedge rst_n) begin if (!rst_n) q <= 1'b0; // any time rst_n falls else q <= d; end
- Sensitivity list:
posedge clk, negedge rst_n - Responds instantly to reset
- Works without a running clock
- Synthesises to DFF with async reset pin
- Needs CDC care at reset release (recovery time)
| Property | Synchronous Reset | Asynchronous Reset |
|---|---|---|
| Reset timing | Only at clock edge | Immediately when asserted |
| Clock required | Yes — must be running | No — works without clock |
| Sensitivity list | posedge clk only | posedge clk, negedge rst_n |
| Glitch immunity | Yes — clock filters glitches | No — glitches can spuriously reset |
| STA analysis | Simpler — single clock domain | Needs recovery/removal analysis |
| Synthesis cell | DFF with D-input MUX | DFF with dedicated async reset pin |
| FPGA mapping | Uses D-input LUT logic | Uses built-in async reset of FF |
| Preferred for | Most RTL design (ASIC/FPGA) | Power-on, safety-critical, CDC |
🔌 Circuit Diagram
🔵 Synchronous Reset D Flip-Flop
The synchronous reset DFF checks the reset condition inside the clock-edge always block. The reset signal is treated as a data input — it must be stable around the clock edge to take effect. The sensitivity list contains only posedge clk.
// ============================================================ // Module : dff_sync_reset // Function : D Flip-Flop with synchronous active-low reset // Reset : Sampled at posedge clk — does NOT respond between edges // ============================================================ `timescale 1ns/1ps `default_nettype none module dff_sync_reset ( input clk, // clock — active rising edge input rst_n, // synchronous reset, active LOW input d, // data input output reg q, // stored output output q_n // complemented output ); // ── Sensitivity list: ONLY posedge clk ────────────────── // rst_n is NOT in the sensitivity list. // Driving rst_n LOW between clock edges has no effect. // Reset is sampled like any other input at the clock edge. always @(posedge clk) begin if (!rst_n) // rst_n=0 at clock edge → reset q <= 1'b0; else // rst_n=1 at clock edge → capture D q <= d; end // Complemented output — combinational, always valid assign q_n = ~q; endmodule `default_nettype wire
🔴 Asynchronous Reset D Flip-Flop
The asynchronous reset DFF includes negedge rst_n in its sensitivity list. Whenever rst_n falls low, the always block triggers immediately — before the next clock edge — and forces Q to 0. The reset release must be synchronized to avoid metastability.
// ============================================================ // Module : dff_async_reset // Function : D Flip-Flop with asynchronous active-low reset // Reset : Asserts immediately when rst_n=0 (no clock needed) // Release : Synchronize reset release to avoid metastability // ============================================================ `timescale 1ns/1ps `default_nettype none module dff_async_reset ( input clk, // clock — active rising edge input rst_n, // asynchronous reset, active LOW input d, // data input output reg q, // stored output output q_n // complemented output ); // ── Sensitivity list: posedge clk AND negedge rst_n ───── // Adding negedge rst_n means: whenever rst_n FALLS to 0, // the block fires IMMEDIATELY regardless of clock state. always @(posedge clk or negedge rst_n) begin if (!rst_n) // rst_n=0 → reset NOW (no clock needed) q <= 1'b0; else // rst_n=1 AND posedge clk → capture D q <= d; end // Complemented output assign q_n = ~q; endmodule `default_nettype wire
rst_n releases (goes from 0→1) asynchronously, and this happens near a clock edge, the flip-flop’s internal recovery time constraint may be violated — causing metastability on Q. Always synchronize reset release using a two-stage synchroniser: pass rst_n through two flip-flops clocked by the target domain clock before distributing it to design flip-flops. This is called an asynchronous assert, synchronous release reset strategy.
🔧 Additional Variants
The same DFF structure supports active-high reset, synchronous set, enable input, and parameterised width. All four variations follow the same always-block pattern.
// ── Active-HIGH synchronous reset ──────────────────────────── always @(posedge clk) q <= rst ? 1'b0 : d; // rst=1 → reset // ── Active-HIGH asynchronous reset ──────────────────────────── always @(posedge clk or posedge rst) if (rst) q <= 1'b0; else q <= d; // ── Synchronous reset + synchronous set (reset takes priority) ─ always @(posedge clk) begin if (!rst_n) q <= 1'b0; // reset wins else if (!set_n) q <= 1'b1; // then set else q <= d; // normal capture end // ── DFF with clock enable ───────────────────────────────────── always @(posedge clk) begin if (!rst_n) q <= 1'b0; else if (en) q <= d; // only capture when enabled // else: q holds (implicit) end // ── N-bit register (parameterised width) ───────────────────── module dff_n_sync #(parameter N = 8) ( input clk, rst_n, input [N-1:0] d, output reg [N-1:0] q ); always @(posedge clk) q <= (!rst_n) ? {N{1'b0}} : d; endmodule
🧪 Comprehensive Testbench
The testbench verifies both DFFs simultaneously with carefully timed stimulus. Crucially, it tests the key distinguishing behaviour: applying reset mid-cycle to prove that the synchronous DFF ignores it until the next clock edge, while the asynchronous DFF responds immediately.
// ============================================================ // Testbench : dff_tb // DUT 1 : dff_sync_reset — synchronous active-low reset // DUT 2 : dff_async_reset — asynchronous active-low reset // Key tests : Reset at clock edge vs mid-cycle reset behaviour // Data capture: 0→Q, 1→Q // q_n = ~q verification // ============================================================ `timescale 1ns/1ps `default_nettype none module dff_tb; // ── Shared stimulus ──────────────────────────────────────── reg clk = 1'b0; reg rst_n = 1'b1; // start deasserted (not in reset) reg d = 1'b0; // ── DUT outputs ──────────────────────────────────────────── wire q_sync, qn_sync; wire q_async, qn_async; // ── Instantiate both DFFs ────────────────────────────────── dff_sync_reset dut_sync (.clk(clk),.rst_n(rst_n),.d(d), .q(q_sync),.q_n(qn_sync)); dff_async_reset dut_async (.clk(clk),.rst_n(rst_n),.d(d), .q(q_async),.q_n(qn_async)); // ── Clock: 10 ns period (100 MHz) ────────────────────────── always #5 clk = ~clk; // ── VCD dump ─────────────────────────────────────────────── initial begin $dumpfile("dff.vcd"); $dumpvars(0, dff_tb); end // ── Test tracking ────────────────────────────────────────── integer pass_cnt=0, fail_cnt=0, test_num=0; // ── Verification task ────────────────────────────────────── task check_both; input exp_sync; // expected q_sync input exp_async; // expected q_async input [63:0] desc; // test description begin #1; // small settle after posedge test_num++; if (q_sync===exp_sync && qn_sync===~exp_sync && q_async===exp_async && qn_async===~exp_async) begin $display(" PASS [%2d] %s", test_num, desc); $display(" q_sync=%b qn=%b | q_async=%b qn=%b", q_sync,qn_sync,q_async,qn_async); pass_cnt++; end else begin $display(" FAIL [%2d] %s", test_num, desc); $display(" GOT: q_sync=%b qn=%b | q_async=%b qn=%b", q_sync,qn_sync,q_async,qn_async); $display(" EXP: q_sync=%b qn=%b | q_async=%b qn=%b", exp_sync,~exp_sync,exp_async,~exp_async); fail_cnt++; end end endtask // ── Main stimulus ────────────────────────────────────────── initial begin $display("\n============================================================"); $display(" DFF Testbench — Synchronous vs Asynchronous Reset"); $display("============================================================"); // ── Phase 1: Reset both DFFs at start ───────────────── $display("\n --- Phase 1: Initial Reset ---"); d = 1'b1; // D=1 (should be ignored during reset) rst_n = 1'b0; // assert reset (active-low) @(posedge clk); // wait for clock edge // After clock edge: sync DFF sees reset → Q=0 // async DFF already Q=0 (immediate) check_both(1'b0, 1'b0, "Reset active: both Q=0"); // ── Phase 2: Release reset — capture D=1 ────────────── $display("\n --- Phase 2: Reset Release, Capture D=1 ---"); rst_n = 1'b1; // release reset d = 1'b1; // D=1 @(posedge clk); check_both(1'b1, 1'b1, "rst_n=1, D=1: both Q=1"); // ── Phase 3: Capture D=0 ────────────────────────────── $display("\n --- Phase 3: Capture D=0 ---"); d = 1'b0; @(posedge clk); check_both(1'b0, 1'b0, "rst_n=1, D=0: both Q=0"); // ── Phase 4: Hold — D toggles, no clock edge ────────── $display("\n --- Phase 4: Hold Between Clock Edges ---"); d = 1'b1; // D changes between edges #3; // wait 3ns (no clock edge yet) test_num++; if (q_sync===1'b0 && q_async===1'b0) begin $display(" PASS [%2d] D changed mid-cycle: both Q hold=0 (no edge yet)", test_num); pass_cnt++; end else begin $display(" FAIL [%2d] Q changed without clock edge! q_sync=%b q_async=%b", test_num,q_sync,q_async); fail_cnt++; end @(posedge clk); // now let it capture // ── Phase 5: KEY TEST — Mid-cycle reset ─────────────── // This test DISTINGUISHES sync from async behaviour. // Applies reset mid-cycle (between clock edges). // SYNC: should NOT respond → Q stays 1 until next edge // ASYNC: should respond IMMEDIATELY → Q goes to 0 now $display("\n --- Phase 5: MID-CYCLE RESET (KEY DISTINCTION TEST) ---"); d = 1'b1; @(posedge clk); // capture D=1 into both #1; // Now both Q=1. Apply reset MID-CYCLE: #2; // 3ns after clock edge (still mid-cycle) rst_n = 1'b0; // RESET asserted NOW — mid-cycle! #1; // settle // Check: async should ALREADY be 0, sync should STILL be 1 test_num++; if (q_sync===1'b1 && q_async===1'b0) begin $display(" PASS [%2d] Mid-cycle rst_n=0: q_sync=1(holds!) q_async=0(immediate!)", test_num); $display(" ✅ SYNC ignores reset between edges"); $display(" ✅ ASYNC responds immediately"); pass_cnt++; end else begin $display(" FAIL [%2d] Mid-cycle reset: q_sync=%b(exp=1) q_async=%b(exp=0)", test_num,q_sync,q_async); fail_cnt++; end // Now wait for the clock edge — sync should reset too @(posedge clk); check_both(1'b0, 1'b0, "After clock edge with rst_n=0: both Q=0"); // ── Phase 6: Reset release — confirm normal capture ──── $display("\n --- Phase 6: Reset Release + Normal Operation ---"); rst_n = 1'b1; d = 1'b1; @(posedge clk); check_both(1'b1, 1'b1, "Reset released, D=1: both Q=1"); d = 1'b0; @(posedge clk); check_both(1'b0, 1'b0, "D=0: both Q=0"); d = 1'b1; @(posedge clk); check_both(1'b1, 1'b1, "D=1: both Q=1"); // ── Phase 7: Async reset during active D=1 ──────────── $display("\n --- Phase 7: Async Reset Clears Q Instantly ---"); // Both Q=1 right now. Assert async reset immediately. rst_n = 1'b0; #1; test_num++; if(q_async===1'b0)begin $display(" PASS [%2d] Async Q cleared without clock: q_async=%b",test_num,q_async); pass_cnt++; endelsebegin $display(" FAIL [%2d] q_async=%b expected 0",test_num,q_async); fail_cnt++; end @(posedge clk); // let sync DFF reset too rst_n = 1'b1; // ── Summary ──────────────────────────────────────────── $display("\n============================================================"); $display(" RESULTS: %0d / %0d PASS | %0d FAIL", pass_cnt, test_num, fail_cnt); $display("============================================================"); if(fail_cnt==0) $display(" ✅ ALL TESTS PASSED\n"); else $fatal(1," ❌ %0d FAILURE(S)\n",fail_cnt); #20; $finish; end // ── Continuous monitor ───────────────────────────────────── initial $monitor(" @%0t clk=%b rst_n=%b d=%b | q_sync=%b q_async=%b", $time,clk,rst_n,d,q_sync,q_async); endmodule `default_nettype wire
Test Phase Summary
| Phase | Action | Sync DFF Expected | Async DFF Expected |
|---|---|---|---|
| 1 — Initial reset | rst_n=0 → clock edge | Q=0 (at edge) | Q=0 (immediately) |
| 2 — Capture D=1 | rst_n=1, D=1 → clock edge | Q=1 | Q=1 |
| 3 — Capture D=0 | D=0 → clock edge | Q=0 | Q=0 |
| 4 — Hold test | D changes, no clock edge | Q holds (0) | Q holds (0) |
| 5 — Mid-cycle reset ★ | rst_n=0 between edges | Q=1 (ignores!) | Q=0 (responds!) |
| 5b — Edge with reset | Clock edge arrives, rst_n still 0 | Q=0 (now resets) | Q=0 (still 0) |
| 6 — Normal operation | rst_n=1, D alternates | Q follows D | Q follows D |
| 7 — Async instant clear | rst_n=0, no clock needed | Q=1 (no edge yet) | Q=0 (instant!) |
📈 Waveform — Synchronous Reset
📉 Waveform — Asynchronous Reset
💻 Simulation Console Output
How to Run
# ── Icarus Verilog ──────────────────────────────────────────── iverilog -o dff_sim \ dff_sync_reset.v \ dff_async_reset.v \ dff_tb.v vvp dff_sim gtkwave dff.vcd # ── ModelSim ────────────────────────────────────────────────── vlog dff_sync_reset.v dff_async_reset.v dff_tb.v vsim -c dff_tb -do "run -all; quit -f"
🔬 Design Analysis & Timing
Setup and Hold Time Requirements
| Parameter | Symbol | Synchronous Reset | Asynchronous Reset |
|---|---|---|---|
| Setup time (D) | tSU | D must be stable tSU before posedge clk | Same as sync for data capture |
| Hold time (D) | tH | D must be stable tH after posedge clk | Same as sync for data capture |
| Setup time (rst_n) | tSU_RST | rst_n must be stable tSU before posedge clk | N/A — asserts async, no setup required |
| Recovery time | tREC | N/A — rst_n is synchronous | rst_n must deassert tREC before posedge clk |
| Removal time | tREM | N/A | rst_n must stay low tREM after posedge clk |
| Clock-to-Q | tCQ | Q updates tCQ after posedge clk | Same for normal capture; reset-to-Q may differ |
RTL to Gates — Synthesis Mapping
🔵 Synchronous Reset → DFF + MUX
Synthesizes to a standard DFF where 0 is muxed into D when reset:
// Equivalent gate-level view: // D_internal = rst_n ? D : 1'b0 // Q <= D_internal on posedge clk // // Cell: DFFR (DFF with D-mux) // Pins: D, CK, reset → Q, QB // No async reset pin used
🔴 Asynchronous Reset → DFF with CLR pin
Synthesizes directly to a DFF cell with a dedicated hardware CLR pin:
// Equivalent gate-level view: // Q <= D on posedge clk // Q <= 0 immediately on negedge rst_n // // Cell: DFFASYNC (DFF with async CLR) // Pins: D, CK, CLR → Q, QB // Reset bypasses the DFF latch loop
Extending to an N-bit Register
// ── 8-bit register, synchronous reset ──────────────────────── module reg8_sync #(parameter N=8, parameter RESET_VAL=0) ( input clk, rst_n, input [N-1:0] d, output reg [N-1:0] q ); always @(posedge clk) q <= !rst_n ? RESET_VAL[N-1:0] : d; endmodule // ── 8-bit register, asynchronous reset ─────────────────────── module reg8_async #(parameter N=8, parameter RESET_VAL=0) ( input clk, rst_n, input [N-1:0] d, output reg [N-1:0] q ); always @(posedge clk or negedge rst_n) if (!rst_n) q <= RESET_VAL[N-1:0]; else q <= d; endmodule // ── Example: 8-bit register with non-zero reset value ──────── reg8_sync #(.N(8), .RESET_VAL(8'hFF)) u_reg ( .clk(clk), .rst_n(rst_n), .d(data_in), .q(data_out) ); // resets to 0xFF instead of 0x00
