HomeFPGA from ScratchDay 7
DAY 7 · HDL FUNDAMENTALS

Sequential Logic, Clocks & Resets in Verilog

By EcrioniX · Updated Jun 9, 2026

On Day 6 you built combinational logic — outputs that change instantly with the inputs. But real designs need memory: registers, counters, state machines, pipelines. That requires sequential logic — the other half of digital design. This lesson explains the D flip-flop, the clock, synchronous and asynchronous resets, and setup/hold time. You'll build a complete 4-bit up-counter with a self-checking testbench.

1. Combinational vs sequential — the key difference

Combinational

Output = f(inputs right now). No clock. No memory. Change an input → output changes immediately. Gates, muxes, adders.

Sequential

Output = f(inputs + stored state). Changes only at the clock edge. Has memory. Flip-flops, registers, counters, FSMs.

Every real digital system uses both: sequential registers to hold state, combinational logic to compute what state comes next.

2. The D flip-flop — the atom of sequential logic

The most basic sequential element. On every rising clock edge, the output Q captures the value of input D. Between edges, Q holds its value no matter what D does.

D Flip-Flop — captures D on every rising clock edge D FF clocked register D CLK Q Q only changes at ↑ clk edge · holds value between edges
Figure — A D flip-flop. D is the data input, Q is the output. Q = D captured at each rising clock edge.

In Verilog, a D flip-flop is written with always @(posedge clk). The synthesis tool maps this directly to a DFF cell on the FPGA fabric.

dff.v — a single D flip-flop
module dff (
    input  wire clk,
    input  wire d,
    output reg  q
);
    always @(posedge clk)
        q <= d;     // non-blocking: captures d at rising edge
endmodule

3. Non-blocking assignment (<=) — always use it in clocked blocks

⚠️ = vs <= in always @(posedge clk)

Use <= (non-blocking) inside clocked always blocks. It schedules updates to happen after all right-hand sides are evaluated — correctly modelling that all flip-flops capture their input simultaneously on the clock edge. Using blocking = in clocked blocks causes simulation races that don't match hardware behaviour.

AssignmentWhere to useBehaviour
<= (non-blocking)always @(posedge clk)All RHS evaluated first, then all LHS updated — flip-flop model
= (blocking)always @(*) combinationalEach statement executes sequentially — like software

4. Synchronous vs asynchronous reset

Every flip-flop needs a way to start in a known state. Two styles:

Synchronous reset

Reset only applies on the clock edge. Checked inside the always block as an ordinary if. Easier to meet timing. Preferred on FPGAs.

Asynchronous reset

Reset applies immediately — listed in the sensitivity list. Faster response but needs a reset synchronizer to avoid metastability on release. Common in ASIC design.

resets.v — synchronous vs asynchronous reset
// Synchronous reset — rst checked at clock edge only
module dff_sync_rst (input clk, input rst, input d, output reg q);
    always @(posedge clk) begin
        if (rst) q <= 1'b0;
        else     q <= d;
    end
endmodule

// Asynchronous reset — rst acts immediately (in sensitivity list)
module dff_async_rst (input clk, input rst, input d, output reg q);
    always @(posedge clk or posedge rst) begin
        if (rst) q <= 1'b0;
        else     q <= d;
    end
endmodule

On FPGAs (Xilinx, Intel/Altera) the built-in flip-flop cells have a dedicated synchronous set/reset input — a synchronous reset maps cleanly to that cell and meets timing more easily.

5. Setup time, hold time and why they matter

A flip-flop only captures data reliably if the data signal is stable for a small window around the clock edge:

Violate either → the flip-flop may go metastable and output a random value. Your synthesis + place-and-route tools run Static Timing Analysis (STA) after every build to guarantee all paths meet setup and hold across the entire design. That's why running "implementation" on Vivado or Quartus takes time — it's placing and routing wires to satisfy these constraints.

The maximum clock frequency your design can run at is set by the longest combinational path between two flip-flops — the critical path. A shorter critical path = higher clock frequency.

6. Build it: a 4-bit synchronous up-counter (counter.v)

The classic first sequential module. It increments by 1 every clock cycle, wraps from 15 back to 0, and can be reset synchronously. Ports:

PortDirWidthMeaning
clkinput1clock — count increments on rising edge
rstinput1synchronous reset — drives count to 0 on next clock edge
eninput1enable — only counts when en=1; holds when en=0
countoutput4current counter value, 0–15
counter.v — 4-bit synchronous up-counter
// 4-bit synchronous up-counter with enable and synchronous reset
module counter (
    input  wire       clk,    // clock
    input  wire       rst,    // synchronous reset (active high)
    input  wire       en,     // count enable
    output reg  [3:0] count   // 4-bit counter output (0..15)
);
    always @(posedge clk) begin
        if      (rst) count <= 4'b0000;   // reset to 0
        else if (en)  count <= count + 1; // wrap 15->0 automatically (4-bit overflow)
    end
endmodule

Note the 4-bit overflow: when count is 4'b1111 (15) and count + 1 is computed, it naturally wraps to 4'b0000 — no extra logic needed. That's the beauty of fixed-width arithmetic in Verilog.

7. Test it: testbench (tb_counter.v)

tb_counter.v — self-checking testbench
`timescale 1ns/1ps
module tb_counter;
    reg        clk, rst, en;
    wire [3:0] count;

    counter dut (.clk(clk), .rst(rst), .en(en), .count(count));

    initial clk = 0;
    always #5 clk = ~clk;   // 10 ns period = 100 MHz

    integer errors = 0;
    task check(input [3:0] exp);
        if (count !== exp) begin
            $display("FAIL: count=%0d expected=%0d", count, exp);
            errors = errors + 1;
        end else
            $display("ok:   count=%0d", count);
    endtask

    integer i;
    initial begin
        // --- reset ---
        rst=1; en=0; @(posedge clk); #1;
        check(4'd0);    // after reset = 0

        // --- count 1..8 with en=1 ---
        rst=0; en=1;
        for (i=1; i<=8; i=i+1) begin
            @(posedge clk); #1;
            check(i[3:0]);
        end

        // --- hold at 8 (en=0) ---
        en=0; @(posedge clk); #1;
        check(4'd8);    // should not advance

        // --- count 9..15 then wrap to 0 ---
        en=1;
        for (i=9; i<=15; i=i+1) begin
            @(posedge clk); #1;
            check(i[3:0]);
        end
        @(posedge clk); #1;
        check(4'd0);    // wrapped back to 0

        // --- mid-count reset ---
        @(posedge clk); #1;  // count is now 1
        rst=1; @(posedge clk); #1;
        check(4'd0);    // synchronous reset applied

        if (errors==0) $display("ALL TESTS PASSED");
        else           $display("%0d TEST(S) FAILED", errors);
        $finish;
    end
endmodule
run.sh — compile & simulate
iverilog -o counter_tb counter.v tb_counter.v
vvp counter_tb

Expected output:

expected output
ok:   count=0
ok:   count=1
ok:   count=2
ok:   count=3
ok:   count=4
ok:   count=5
ok:   count=6
ok:   count=7
ok:   count=8
ok:   count=8
ok:   count=9
ok:   count=10
ok:   count=11
ok:   count=12
ok:   count=13
ok:   count=14
ok:   count=15
ok:   count=0
ok:   count=1
ok:   count=0
ALL TESTS PASSED

💡 A flip-flop is a photograph, not a mirror

A mirror (combinational logic) reflects whatever is in front of it right now — change the scene, the reflection changes instantly. A photograph (flip-flop) captures the scene at one moment (the clock edge) and holds that image until the next shot. No matter how the inputs change between clock edges, the flip-flop's output is frozen from the last snapshot.

🎯 Day 7 takeaways

Quick check

  1. What is the key difference between combinational and sequential logic?
  2. Why use <= instead of = inside always @(posedge clk)?
  3. What is setup time violation, and who checks it automatically?
  4. How does the counter wrap from 15 back to 0 without extra logic?

FAQ

What is sequential logic?

Logic whose output depends on current inputs AND stored state from previous clock cycles. Changes only at clock edges. Flip-flops are the building block.

Synchronous vs asynchronous reset?

Synchronous: reset takes effect on the next clock edge — simpler timing. Asynchronous: reset takes effect immediately, regardless of clock — needs a reset synchronizer.

What is setup and hold time?

Data must be stable for setup time before and hold time after the clock edge. Violation causes metastability. STA tools verify all paths meet these constraints.

Why always @(posedge clk)?

It triggers only on the rising clock edge, matching real flip-flop hardware behaviour. The synthesizer maps it directly to DFF cells.

What is a non-blocking assignment?

<= schedules all updates to happen after all RHS are evaluated — correctly models simultaneous flip-flop capture on a clock edge. Use it in all clocked always blocks.

Previous
← Day 6: Combinational logic in HDL

← Back to the full roadmap  ·  Open the Verilog simulator →