D Flip-Flop Verilog Code: Async & Sync Reset

A D flip-flop (DFF) is the workhorse storage element of synchronous digital design: on every active clock edge it copies its data input D to its output Q and holds that value until the next edge. This tutorial gives you three pieces of runnable code — a flip-flop with asynchronous reset, one with synchronous reset, and a side-by-side D latch — each with a self-checking testbench. Everything is plain Verilog-2005; the simulation logs below are produced by compiling with Icarus Verilog (iverilog -g2005) and running the resulting binary.

The D flip-flop in one sentence

On a rising clock edge, Q takes the value D had just before that edge; at all other times Q is frozen. Because the capture happens only at the instant of the edge, flip-flops are edge-triggered — this is what separates them from level-sensitive latches, which we contrast at the end.

D flip-flop with asynchronous reset

Asynchronous reset forces Q to 0 the moment rst_n goes low, without waiting for a clock edge. You get this behaviour by putting the reset in the sensitivity list alongside the clock.

// dff_async.v  -- D flip-flop with asynchronous active-low reset
module dff_async (
    input  wire clk,       // clock
    input  wire rst_n,     // asynchronous reset, active low
    input  wire d,         // data input
    output reg  q          // registered output
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            q <= 1'b0;     // reset wins immediately, no clock needed
        else
            q <= d;        // sample d on the rising clock edge
    end
endmodule

// Testbench
module tb;
    reg  clk, rst_n, d;
    wire q;

    dff_async dut (.clk(clk), .rst_n(rst_n), .d(d), .q(q));

    // 10 ns clock: rising edges at t = 5, 15, 25, 35, ...
    initial clk = 1'b0;
    always #5 clk = ~clk;

    initial begin
        $display(" time | rst_n d | q");
        $monitor("%5t |   %b   %b | %b", $time, rst_n, d, q);

        rst_n = 1'b0; d = 1'b1;   // start held in reset
        #12 rst_n = 1'b1;         // release reset at t=12 (between edges)
        // edge at t=15 captures d=1 -> q=1
        #11 rst_n = 1'b0;         // t=23: assert reset BETWEEN edges -> q drops now
        #4  rst_n = 1'b1;         // t=27: release again
        // edge at t=35 captures d=1 -> q=1
        #13 d = 1'b0;             // t=40
        // edge at t=45 captures d=0 -> q=0
        #10 $finish;
    end
endmodule
time | rst_n d | q
    0 |   0   1 | 0
   12 |   1   1 | 0
   15 |   1   1 | 1
   23 |   0   1 | 0
   27 |   1   1 | 0
   35 |   1   1 | 1
   40 |   1   0 | 1
   45 |   1   0 | 0
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmp0z3mwfyv/dff_async.v:39: $finish called at 50 (1s)

How it works, line by line

  • always @(posedge clk or negedge rst_n) — the block re-evaluates on a rising clock edge or a falling edge of rst_n. Listing rst_n here is the single thing that makes the reset asynchronous.
  • if (!rst_n) q <= 1'b0; — the instant rst_n falls, the block runs and clears Q. In the log, note t=23: Q drops from 1 to 0 with no clock edge in sight.
  • else q <= d; — when reset is released, each rising edge copies D into Q (see t=15, t=35, t=45).
  • The non-blocking assignment <= models an edge-triggered register and lets many flip-flops update in parallel without read/write races.
  • output reg q — because Q is assigned inside a procedural block and must retain state, it is a reg, not a wire.

D flip-flop with synchronous reset

Synchronous reset only clears Q on a clock edge. The reset signal is treated like any other data input — it is sampled by the clock, so it never appears in the sensitivity list.

// dff_sync.v  -- D flip-flop with synchronous active-low reset
module dff_sync (
    input  wire clk,       // clock
    input  wire rst_n,     // synchronous reset, active low
    input  wire d,         // data input
    output reg  q          // registered output
);
    always @(posedge clk) begin    // ONLY the clock in the sensitivity list
        if (!rst_n)
            q <= 1'b0;             // reset is sampled like any other input
        else
            q <= d;
    end
endmodule

// Testbench
module tb;
    reg  clk, rst_n, d;
    wire q;

    dff_sync dut (.clk(clk), .rst_n(rst_n), .d(d), .q(q));

    initial clk = 1'b0;
    always #5 clk = ~clk;

    initial begin
        $display(" time | rst_n d | q");
        $monitor("%5t |   %b   %b | %b", $time, rst_n, d, q);

        rst_n = 1'b0; d = 1'b1;   // reset asserted
        #10 d = 1'b1;             // first edge: q goes 0 (reset)
        #10 rst_n = 1'b1;         // release reset
        #10 d = 1'b0;
        #3  rst_n = 1'b0;         // deassert between edges...
        #2  rst_n = 1'b1;         // ...and reassert before the edge: glitch ignored
        #10 d = 1'b1;
        #10 $finish;
    end
endmodule
time | rst_n d | q
    0 |   0   1 | x
    5 |   0   1 | 0
   20 |   1   1 | 0
   25 |   1   1 | 1
   30 |   1   0 | 1
   33 |   0   0 | 1
   35 |   1   0 | 0
   45 |   1   1 | 1
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpnm4_ej5s/dff_sync.v:37: $finish called at 55 (1s)

The only structural change from the asynchronous version is the sensitivity list: @(posedge clk) instead of @(posedge clk or negedge rst_n). Watch the short reset pulse the testbench injects at t=33 and removes at t=35: because it lands entirely between two rising edges, the synchronous flop never sees it — Q is undisturbed. That glitch-immunity is the headline difference between the two styles.

PropertyAsynchronous resetSynchronous reset
Sensitivity listposedge clk or negedge rst_nposedge clk only
When reset actsImmediately, no clock neededOnly on the next clock edge
Works with stopped clockYesNo — needs a running clock
Sensitive to reset glitchesYesNo (filtered between edges)
Typical usePower-on reset of the whole chipLocal resets on an already-clocked domain

D flip-flop vs D latch

A D latch is level-sensitive: while its enable is high it is transparent, meaning Q tracks D continuously; when enable goes low it freezes. A flip-flop only ever samples at the edge. The example below drives one of each from the same stimulus so you can watch them diverge.

// latch_vs_dff.v  -- level-sensitive D latch vs edge-triggered D flip-flop
module d_latch (
    input  wire en,        // enable (level sensitive)
    input  wire d,
    output reg  q
);
    always @(*) begin          // combinational sensitivity: en and d
        if (en)
            q = d;             // transparent while en is high
        // else: q holds its value (this is the latch)
    end
endmodule

module d_ff (
    input  wire clk,
    input  wire d,
    output reg  q
);
    always @(posedge clk)
        q <= d;                // captures d only on the rising edge
endmodule

// Testbench
module tb;
    reg  clk, d;
    wire q_latch, q_ff;

    d_latch u_latch (.en(clk), .d(d), .q(q_latch));  // reuse clk as the latch enable
    d_ff    u_ff    (.clk(clk), .d(d), .q(q_ff));

    initial clk = 1'b0;
    always #5 clk = ~clk;

    initial begin
        $display(" time | clk d | latch  ff");
        $monitor("%5t |  %b  %b |   %b    %b", $time, clk, d, q_latch, q_ff);

        d = 1'b0;
        #7  d = 1'b1;   // d changes WHILE clk is high -> latch follows, ff waits
        #2  d = 1'b0;   // d changes again while clk still high -> latch follows again
        #6  d = 1'b1;   // d changes while clk is low -> latch holds, ff will take it next edge
        #10 d = 1'b0;
        #10 $finish;
    end
endmodule
time | clk d | latch  ff
    0 |  0  0 |   x    x
    5 |  1  0 |   0    0
    7 |  1  1 |   1    0
    9 |  1  0 |   0    0
   10 |  0  0 |   0    0
   15 |  1  1 |   1    1
   20 |  0  1 |   1    1
   25 |  1  0 |   0    0
   30 |  0  0 |   0    0
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpg4fc1b5m/latch_vs_dff.v:43: $finish called at 35 (1s)
   35 |  1  0 |   0    0

In the log, clk is high between t=5–10. At t=7 D rises to 1 while the enable is high, so the latch immediately shows 1 — but the flip-flop stays 0 because no new edge has occurred. At t=9 D falls again and the latch dutifully follows back to 0. The flip-flop only moves at the edges (t=15, t=25). The latch’s output chased D twice inside one enable window; the flop ignored every intermediate value.

D latchD flip-flop
Triggered byLevel (enable high)Edge (clock transition)
TransparentYes, while enabledNever
Verilog patternalways @(*) + blocking =always @(posedge clk) + non-blocking <=
Captures per cycleAny change during the high phaseExactly one value, at the edge

Common mistakes

  • Blocking = inside a clocked block. Use non-blocking <= for flip-flops. Blocking assignments can create simulation races and simulation/synthesis mismatches when one flop feeds another.
  • Accidental latch from a missing else. In an always @(*) block, if any path leaves Q unassigned, the tool must keep the old value and infers a latch. Assign every output on every branch, or add a default at the top.
  • Putting a data signal in the sensitivity list. Only edges that should trigger capture belong there — the clock, and rst_n only if the reset is meant to be asynchronous.
  • Declaring the output as wire. Anything assigned in a procedural block must be reg.
  • Driving one reg from two always blocks. Each state element needs exactly one driver.
  • Mixing reset styles. If you list negedge rst_n but only want a synchronous reset (or vice versa), simulation and silicon will disagree about reset timing.

Quick check

Which single thing in the code makes a reset asynchronous?

Including negedge rst_n (the reset edge) in the sensitivity list: always @(posedge clk or negedge rst_n). That lets the reset act without waiting for a clock edge.

Why use non-blocking <= inside a clocked always block?

It models edge-triggered registers updating in parallel. All right-hand sides are read first, then the outputs update together, which avoids read/write races when one flip-flop feeds the next.

What accidentally creates a latch in combinational code?

An always @(*) block where some branch leaves the output unassigned (for example an if with no else). The tool must hold the old value, which requires a latch. Assign the output on every path to avoid it.

A reset pulse occurs entirely between two clock edges. Does a synchronous-reset flop see it?

No. A synchronous reset is only sampled at the clock edge, so a pulse that appears and disappears before the next edge is ignored — exactly the glitch-immunity shown at t=33 to t=35 in the synchronous example.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.

Learn Verilog with mentors, labs and placement support

This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.

Explore VLSI Courses
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD