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.
📘 New to the concept? Read the theory first: D Flip-Flop vs D Latch: the theory.
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
endmoduletime | 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. Listingrst_nhere is the single thing that makes the reset asynchronous.if (!rst_n) q <= 1'b0;— the instantrst_nfalls, the block runs and clearsQ. In the log, notet=23:Qdrops from 1 to 0 with no clock edge in sight.else q <= d;— when reset is released, each rising edge copiesDintoQ(seet=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— becauseQis assigned inside a procedural block and must retain state, it is areg, not awire.
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
endmoduletime | 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.
| Property | Asynchronous reset | Synchronous reset |
|---|---|---|
| Sensitivity list | posedge clk or negedge rst_n | posedge clk only |
| When reset acts | Immediately, no clock needed | Only on the next clock edge |
| Works with stopped clock | Yes | No — needs a running clock |
| Sensitive to reset glitches | Yes | No (filtered between edges) |
| Typical use | Power-on reset of the whole chip | Local 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
endmoduletime | 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 0In 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 latch | D flip-flop | |
|---|---|---|
| Triggered by | Level (enable high) | Edge (clock transition) |
| Transparent | Yes, while enabled | Never |
| Verilog pattern | always @(*) + blocking = | always @(posedge clk) + non-blocking <= |
| Captures per cycle | Any change during the high phase | Exactly 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 analways @(*)block, if any path leavesQunassigned, 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_nonly if the reset is meant to be asynchronous. - Declaring the output as
wire. Anything assigned in a procedural block must bereg. - Driving one
regfrom twoalwaysblocks. Each state element needs exactly one driver. - Mixing reset styles. If you list
negedge rst_nbut 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.
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