top of page

Understanding Asynchronous (Ripple) Counters in Verilog

Counters are one of the most important building blocks in digital electronics and VLSI systems. They are used for event counting, frequency division, address generation, and timing control. In this post, we’ll explore Asynchronous Counters, also known as Ripple Counters, and implement a 4-bit binary up-counter in Verilog.

1. What is an Asynchronous (Ripple) Counter?

An asynchronous counter is a type of counter where flip-flops do not share the same clock signal. Instead:

  • The first flip-flop (LSB) receives the external clock directly.
     

  • Each subsequent flip-flop’s clock is driven by the output of the previous flip-flop.
     

  • Bit changes propagate like a ripple, which is why it’s called a ripple counter.
     

This structure makes ripple counters simple and hardware-efficient, but they suffer from propagation delay—the time taken for the change to ripple through all flip-flops.

2. How Does a Ripple Counter Work?

Let’s take a 4-bit ripple up-counter as an example:

​

  1. Q0 (LSB) toggles on every rising edge of the external clock.
     

  2. Q1 toggles when Q0 transitions from 1 → 0.
     

  3. Q2 toggles when Q1 transitions from 1 → 0.
     

  4. Q3 (MSB) toggles when Q2 transitions from 1 → 0.
     

This creates a binary counting sequence from 0000 to 1111 and then back to 0000.

3. Key Specifications of a Ripple Counter

  1. Counting Range: 000 to 2n−12^n - 12n−1 (n = number of bits)
     

  2. Modulus (MOD): 2n2^n2n
     

  3. Triggering: Each stage triggered by the output of the previous stage
     

  4. Reset: Asynchronous reset to clear all bits to 0
     

  5. Delay: Bit updates occur sequentially, not simultaneously

4. Problem Statement

We need to design a 4-bit asynchronous binary up-counter in Verilog with the following requirements:

  • Inputs:

    • clk → external clock

    • arst → active-high asynchronous reset
       

  • Output:

    • Q[3:0] → current count value in binary
       

  • Behavior:

    • Increment count on each clock edge

    • Reset to 0000 when arst is high
       

  • Implementation Style:

    • Use edge-triggered flip-flops in ripple configuration

    • Avoid gated clocks to remain synthesis-friendly

5. Verilog Code - 4-Bit Ripple Counter

 Verilog

module ripple4 (
 
input wire clk,      // External clock
  input wire arst,     // Active-high async reset
  output reg [3:0] Q   // Counter output
);
 
// LSB Flip-Flop
  always @(posedge clk or posedge arst) begin
    if (arst)
      Q[0] <= 1'b0;
   
else
      Q[0] <= ~Q[0];
 
end
  // Second bit
  always @(posedge Q[0] or posedge arst) begin
    if (arst)
      Q[1] <= 1'b0;
   
else
      Q[1] <= ~Q[1];
 
end
  // Third bit
  always @(posedge Q[1] or posedge arst) begin
    if (arst)
      Q[2] <= 1'b0;
   
else
      Q[2] <= ~Q[2];
 
end
  // MSB
  always @(posedge Q[2] or posedge arst) begin
   
if (arst)
      Q[3] <= 1'b0;
   
else
      Q[3] <= ~Q[3];
 
end
endmodule

Understanding the Ripple Counter (ripple4)

​​

module ripple4 (

  input wire clk,

  input wire arst,

  output reg [3:0] Q

);

 

  • clk: external clock drives the least-significant bit (LSB).
     

  • arst: active-high asynchronous reset—forces the counter to 0 immediately, independent of clk.
     

  • Q[3:0]: 4-bit count output (Q[0] = LSB, Q[3] = MSB).

​​

​

Bit-by-bit behavior (ripple effect)

​

Bit 0 (LSB): toggles on every external clock edge

​

 

always @(posedge clk or posedge arst) begin

  if (arst)

    Q[0] <= 1'b0;

  else

    Q[0] <= ~Q[0];

end

 

  • On posedge clk: invert Q[0] → it toggles every clock → frequency = f_clk/2.
     

  • On arst: immediately clear to 0.
     

Bit 1: clocked by Q[0] (derived clock)

​

always @(posedge Q[0] or posedge arst) begin

  if (arst)

    Q[1] <= 1'b0;

  else

    Q[1] <= ~Q[1];

end

 

  • Toggles on rising edges of Q[0], i.e., every second external clock pulse.
     

  • Effective frequency = f_clk/4.
     

Bit 2: clocked by Q[1]

​

always @(posedge Q[1] or posedge arst) begin

  if (arst)

    Q[2] <= 1'b0;

  else

    Q[2] <= ~Q[2];

end

 

  • Toggles on Q[1] rising edges → frequency = f_clk/8.
     

Bit 3 (MSB): clocked by Q[2]

​

always @(posedge Q[2] or posedge arst) begin

  if (arst)

    Q[3] <= 1'b0;

  else

    Q[3] <= ~Q[3];

end

 

  • Toggles on Q[2] rising edges → frequency = f_clk/16.
     

 

What “ripple” means here

​

  • Only Q[0] uses the external clock.
     

  • Each higher bit uses the previous bit as its clock.
     

  • After a clk rising edge, you’ll see Q[0] change first, then (after propagation delay) Q[1], then Q[2], then Q[3]. That sequential settling is the ripple.
     

Counting sequence

The 4 bits together count 0 → 15 → 0 ... in binary:

0000, 0001, 0010, 0011, 0100, ..., 1111, 0000

​

 

Notes on reset (arst)

 

  • Because reset is asynchronous, any posedge arst immediately clears the bit, regardless of its clock source.
     

  • This guarantees a known startup state (all zeros).
     

 

Practical synthesis note

​

  • This is a textbook ripple counter. It uses derived clocks (Q[0], Q[1], Q[2]) as clock inputs to other flops.
     

  • That’s fine for learning, simulation, or low-speed discrete logic.

 

  • In FPGA/ASIC production RTL, derived clocks are discouraged: they complicate clock-tree/timing. Prefer a synchronous counter where all flops use clk and you compute the next state combinationally.

5. Verilog Code - 4-Bit Ripple Counter

 Verilog

module tb_ripple4;
  reg clk = 0;
  reg arst = 1;
  wire [3:0] Q;
  ripple4 tb_ripple4_VM (
    .clk(clk),
    .arst(arst),
    .Q(Q)
  );
  // Clock: 10 ns period
  always #5 clk = ~clk;
  initial begin
    #10 arst = 0;       // Release reset after 10 ns
    #500;               // Run for 500 ns
    $display("Final Counter Value: %b", Q);
    $finish;
  end
  // Monitor counter value
  initial begin
    $monitor("Time: %0t | Counter: %b", $time, Q);
  end
  // Waveform dump
  initial begin
    $dumpfile("tb_ripple4.vcd");
    $dumpvars(1, tb_ripple4);
  end
endmodule

Understanding the Testbench (tb_ripple4)

​

reg clk = 0;

reg arst = 1;

wire [3:0] Q;

ripple4 tb_ripple_VM ( .clk(clk), .arst(arst), .Q(Q) );

 

  • Instantiates the DUT (ripple4) and connects signals.
     

Clock generator

​

always #5 clk = ~clk;

 

  • Generates a 10 time-unit period clock (toggle every 5 → period = 10).
     

  • So f_clk = 1/10 TU.
     

Reset and run sequence

​

initial begin

  #10 arst = 0;        // hold reset high for the first 10 TU

  #500;                // simulate for 500 TU more

  $display("Final Counter Value: %b", Q);

  $finish;

end

 

  • Keeps the counter in reset for the first 10 TU so Q starts from 0000.
     

  • Runs long enough to observe many counts (500/10 = 50 clock cycles).
     

Live monitor

​

initial begin

  $monitor("Time: %0t | Counter: %b", $time, Q);

end

 

  • Prints the time and current count whenever Q changes.
     

  • You’ll see the binary count rolling and, if you open a waveform, the ripple (bit-by-bit settling).

6. What you’ll observe in the waveform

​

  • Q[0] flips every clock edge.
     

  • Q[1] flips on alternate Q[0] rising edges.
     

  • Q[2] and Q[3] flip correspondingly slower.
     

On each clock, for a brief sim time, the bus Q may pass through intermediate values (due to ripple delays) before settling to the final count.

7. Common pitfalls (and how your code avoids them)

 

  • Uninitialized state → avoided by arst.
     

  • Gated clocks / glitches → here you use clean flops; the “gating” is really clocking from Qn, which is a valid flop output (still, not ideal for high-speed RTL).

 

  • Sensitivity lists → correctly use posedge events and include arst for async reset.

8. CLICK ON BELOW LOGO to run the Verilog Code on EDA PLAYGROUND:

Eda playground link

Verilog LAB - Counter LAB

Verilog LAB - Synchronous Counter LAB

© Copyright 2025 VLSI Mentor. All Rights Reserved.©

Connect with us

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
bottom of page