Maker.io main logo

Part 15: Sequential Logic Using Verilog

2025-06-04 | By DWARAKAN RAMANATHAN

Introduction:

Sequential logic circuits are those in which the output depends not only on the current inputs but also on the history of previous inputs. Unlike combinational logic, sequential circuits have memory elements (like flip-flops or registers) that store the past states. Verilog, a hardware description language (HDL), allows us to model and implement these sequential systems efficiently. In this blog, we will explore the concepts of sequential logic using Verilog, followed by 5 detailed examples, including RTL (Register Transfer Level) code and test benches.

The memory elements of sequential logic circuits allow the circuit to "remember" previous states. The output is a function of both the current inputs and the past state of the system. These circuits often use clock signals to synchronize operations and typically include devices like flip-flops, registers, counters, and finite state machines (FSMs).

Key Characteristics of Sequential Logic:

Memory Elements: Sequential circuits store past inputs or states.

Clock Dependency: Many of the sequential circuits depend on some clock signal, which determines when they should update their state.

Finite State Machines: Sequential logic often gets implemented by finite state machines (FSMs), where on the basis of inputs and its current state, the system moves on from one state to another.

Sequential Logic in Verilog

Sequential circuits can be modeled in Verilog in two fundamental ways:

always Block: Uses a clock signal or other events for modeling state changes.

flip-flops: The basic building blocks for sequential logic (e.g., D-flip-flop, T-flip-flop, etc.).

Below are 5 examples illustrating different facets of sequential logic in Verilog.

Example 1: D Flip-Flop (Basic Memory Element)

Verilog Code:

Copy Code
module d_flip_flop(input clk, input reset, input d, output reg q);‎

‎    always @(posedge clk or posedge reset) begin

‎        if (reset)‎

‎            q <= 0;  // Reset the output to 0‎

‎        else

‎            q <= d;  // On rising edge of clock, latch the value of d

‎    end

endmodule

Testbench:

Copy Code
module tb_d_flip_flop;‎

‎    reg clk, reset, d;‎

‎    wire q;‎

‎   ‎

‎    // Instantiate the D Flip-Flop

‎    d_flip_flop uut(clk, reset, d, q);‎

‎   ‎

‎    always #5 clk = ~clk;  // Generate clock signal with period of 10 time units

‎   ‎

‎    initial begin

‎        $monitor("clk = %b, reset = %b, d = %b, q = %b", clk, reset, d, q);‎

‎       ‎

‎        clk = 0; reset = 0; d = 0;‎

‎        #10;‎

‎        reset = 1; #10; // Reset the flip-flop

‎        reset = 0; d = 1; #10; // Set input d to 1‎

‎        d = 0; #10; // Change d to 0‎

‎        d = 1; #10; // Change d back to 1‎

‎       ‎

‎        $finish;‎

‎    end

endmodule

Explanation:

A D flip-flop (Data or Delay flip-flop) is a simple sequential circuit that stores and holds a binary value depending on a clock signal. It has two primary inputs: D (data input) and CLK (clock signal), with an output Q and its complement Q'. The D flip-flop ensures that the output Q follows the input D, but only at the rising edge (or falling edge) of the clock signal. This characteristic eliminates the issue of random states that exist in other flip-flops, like the SR flip-flop. When the clock shifts from low to high (rising edge-triggered), the flip-flop captures the value of D and holds it until the next clock cycle, regardless of any following changes in D. This makes it apt for use in synchronizing data, registers, and memory storage elements. D flip-flops are widely used in shift registers, data buffers, and pipeline registers in microprocessors to suitably synchronize signals in different clock domains.

Testbench: The testbench generates a clock signal and tests the behavior of the flip-flop in terms of reset and input d.

Example 2: 4-bit Synchronous Counter

Verilog Code:

Copy Code
module counter_4bit(input clk, input reset, output reg [3:0] count);‎

‎    always @(posedge clk or posedge reset) begin

‎        if (reset)‎

‎            count <= 4'b0000;  // Reset the counter to 0‎

‎        else

‎            count <= count + 1;  // Increment the counter at each clock cycle

‎    end

endmodule

Testbench:

Copy Code
module tb_counter_4bit;‎

‎    reg clk, reset;‎

‎    wire [3:0] count;‎

‎   ‎

‎    // Instantiate the 4-bit counter

‎    counter_4bit uut(clk, reset, count);‎

‎   ‎

‎    always #5 clk = ~clk;  // Generate clock signal with period of 10 time units

‎   ‎

‎    initial begin

‎        $monitor("clk = %b, reset = %b, count = %b", clk, reset, count);‎

‎       ‎

‎        clk = 0; reset = 0;‎

‎        #10;‎

‎        reset = 1; #10; // Reset the counter to 0‎

‎        reset = 0; #50;  // Run the counter for a few clock cycles

‎        $finish;‎

‎    end

endmodule

Explanation:

A 4-bit synchronous counter is a sequence circuit that counts in binary from 0000 (0) to 1111 (15) and back to zero, incrementing its value on each clock pulse. As opposed to an asynchronous counter, where flip-flops are triggered at different times, a synchronous counter triggers all flip-flops simultaneously, which reduces propagation delay and maximizes speed of operation. The counter is typically implemented using four JK or T flip-flops, each corresponding to one of the counter bits. The least significant bit (LSB) toggles on each clock pulse, and higher-order bits toggle based on the previous bit's state. For example, the second bit toggles when the first bit toggles from 1 to 0, the third bit toggles when the first two bits together toggle from 11 to 00, and so on. Synchronous counters have widespread application in digital clocks, frequency dividers, event counting applications, and microcontrollers to produce accurate timing and sequencing operations.

Testbench: The testbench produces the clock signal and applies a reset for testing the counter behavior.

Example 3: T Flip-Flop (Toggle Flip-Flop)

Verilog Code:

Copy Code
module t_flip_flop(input clk, input reset, input t, output reg q);‎

‎    always @(posedge clk or posedge reset) begin

‎        if (reset)‎

‎            q <= 0;  // Reset the output to 0‎

‎        else if (t)‎

‎            q <= ~q;  // Toggle output if t is 1‎

‎    end

endmodule

Testbench:

Copy Code
module tb_t_flip_flop;‎

‎    reg clk, reset, t;‎

‎    wire q;‎

‎   ‎

‎    // Instantiate the T Flip-Flop

‎    t_flip_flop uut(clk, reset, t, q);‎

‎   ‎

‎    always #5 clk = ~clk;  // Generate clock signal with period of 10 time units

‎   ‎

‎    initial begin

‎        $monitor("clk = %b, reset = %b, t = %b, q = %b", clk, reset, t, q);‎

‎       ‎

‎        clk = 0; reset = 0; t = 0;‎

‎        #10;‎

‎        reset = 1; #10; // Reset the flip-flop

‎        reset = 0; t = 1; #10; // Toggle q with t = 1‎

‎        t = 0; #10;  // Keep t = 0, no toggle

‎        t = 1; #10;  // Toggle again

‎        $finish;‎

‎    end

endmodule

Explanation:

A T flip-flop (Toggle flip-flop) is a sequential circuit whose output state is toggled for each clock pulse when the T input (toggle enable) is 1. If T = 0, the output does not toggle from its previous state. Mathematically, the next state Q(n+1) can be represented as Q(n) ⊕ T, i.e., if T is 1, the output toggles 0 and 1 with each clock pulse. T flip-flops are generally realized from JK flip-flops by connecting both J and K inputs together, actually toggling them with each clock pulse. This flip-flop is widely used in counters, frequency dividers, and toggle circuits, where the output is to be toggled sequentially. For example, in a frequency divider circuit, a T flip-flop can be utilized to divide the input clock frequency by half and thus is utilized in clock generation and digital timing circuits. Moreover, it plays a central role in constructing binary counters and minimizing state transitions in finite state machines (FSMs). Testbench: The testbench tests the toggle behavior by applying different values of t and observing the toggling of the output q.

Example 4: 2-bit Johnson Counter

Verilog Code:

Copy Code
module johnson_counter(input clk, input reset, output reg [1:0] count);‎

‎    always @(posedge clk or posedge reset) begin

‎        if (reset)‎

‎            count <= 2'b00;  // Reset the counter

‎        else

‎            count <= {count[0], ~count[1]};  // Johnson counter logic

‎    end

endmodule

Testbench:

Copy Code
module tb_johnson_counter;‎
‎    reg clk, reset;‎
‎    wire [1:0] count;‎
‎    ‎
‎    // Instantiate the Johnson counter
‎    johnson_counter uut(clk, reset, count);‎
‎    ‎
‎    always #5 clk = ~clk;  // Generate clock signal with period of 10 time units
‎    ‎
‎    initial begin
‎        $monitor("clk = %b, reset = %b, count = %b", clk, reset, count);‎
‎        ‎
‎        clk = 0; reset = 0;‎
‎        #10;‎
‎        reset = 1; #10;  // Reset the counter
‎        reset = 0; #50;  // Run the counter for a few clock cycles
‎        $finish;‎
‎    end
endmodule

Explanation:

A 2-bit Johnson counter, or twisted ring counter, is a shift register counter that counts through a special set of states. Unlike a standard ring counter, where the last bit is fed back directly into the first flip-flop, a Johnson counter feeds back the inverted output of the last flip-flop to the first input, doubling the number of states. A 2-bit Johnson counter can be implemented with two D flip-flops connected in series; the inverted output of the last flip-flop given to the input of the first. The counter cycles through four distinct states: 00 → 10 → 11 → 01 → 00, effectively generating a mod-4 counting sequence. This characteristic makes Johnson counters highly efficient in pattern generation, sequence controllers, and digital timing circuits, as they employ fewer flip-flops than standard binary counters for the same number of states. They're applied in LED chasers, frequency division circuits, and state transition designs.

Testbench: The testbench verifies the correct behavior of the Johnson counter by executing a number of clock cycles.

Example 5: Simple Finite State Machine (FSM)

Verilog Code:

Copy Code
module fsm(input clk, input reset, input x, output reg z);‎

‎    reg [1:0] state, next_state;‎

‎   ‎

‎    // State encoding

‎    parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10;‎

‎   ‎

‎    // State transition logic

‎    always @(posedge clk or posedge reset) begin

‎        if (reset)‎

‎            state <= S0;  // Reset to initial state

‎        else

‎            state <= next_state;  // Transition to next state

‎    end

‎   ‎

‎    // Next state logic

‎    always @(state or x) begin

‎        case (state)‎

‎            S0: next_state = (x) ? S1 : S0;‎

‎            S1: next_state = (x) ? S2 : S0;‎

‎            S2: next_state = (x) ? S1 : S0;‎

‎            default: next_state = S0;‎

‎        endcase

‎    end

‎   ‎

‎    // Output logic

‎    always @(state) begin

‎        case (state)‎

‎            S0: z = 0;‎

‎            S1: z = 1;‎

‎            S2: z = 0;‎

‎            default: z = 0;‎

‎        endcase

‎    end

endmodule

Testbench:

Copy Code
module tb_fsm;‎

‎    reg clk, reset, x;‎

‎    wire z;‎

‎   ‎

‎    // Instantiate the FSM

‎    fsm uut(clk, reset, x, z);‎

‎   ‎

‎    always #5 clk = ~clk;  // Generate clock signal with period of 10 time units

‎   ‎

‎    initial begin

‎        $monitor("clk = %b, reset = %b, x = %b, z = %b", clk, reset, x, z);‎

‎       ‎

‎        clk = 0; reset = 0; x = 0;‎

‎        #10;‎

‎        reset = 1; #10;  // Reset the FSM

‎        reset = 0; x = 1; #10;  // Test state transitions

‎        x = 0; #10;‎

‎        x = 1; #10;‎

‎        $finish;‎

‎    end

endmodule

Explanation:

A finite state machine (FSM) is a sequential logic circuit that changes states according to inputs and clock pulses. An FSM has a finite number of states, a present state, a next state, inputs, and outputs. FSMs can be classified as Moore machines, where the output is a function of the current state alone, and Mealy machines, where the output is a function of the current state and also of the inputs. A basic FSM works by storing its current state in flip-flops and changing to a new state according to specified conditions. For instance, in a traffic light controller, an FSM goes through states Green → Yellow → Red → Green, changing as a function of timing inputs. The FSM logic enables the transitions to be controlled, and thus it is necessary to control circuits, digital protocol design, vending machines, elevator systems, and embedded system automation. FSMs offer a systematic approach to sequential decision-making in hardware and software systems.

Testbench: The testbench simulates the FSM operation by applying different inputs and monitoring the output z.

Conclusion

Sequential logic in Verilog enables designers to implement systems that have a memory of previous states, like counters, flip-flops, and finite state machines. The following examples illustrate several different types of sequential elements and how they can be represented in Verilog. The testbenches are also an important part of verifying the correct functionality of the sequential circuits: they must respond as desired to any sequence of inputs and states.

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.