top of page

Designing a Parameterizable N-bit Up Counter in Verilog

Counters are among the most common building blocks in digital systems — from timers and event trackers to address generators in memory systems. In this article, we’ll implement an N-bit up counter in Verilog, make it parameterizable, and explore its design and testbench in detail.

What is an Up Counter?

An up counter is a sequential circuit that increments its binary value by one on each active clock edge when enabled.

​

  • Once it reaches its maximum value, it wraps around to zero (modulo operation).
     

  • It’s often used for counting events, generating timing sequences, or indexing through arrays.

3. Problem Statement

We want to design an N-bit up counter with:

  • Parameterizable bit width (default: 8 bits)
     

  • Synchronous enable control
     

Asynchronous reset for immediate clearing

Specifications

up_Counter_table.png

Expected Result

  • Counter increments when en is asserted.
     

  • Stops counting when en=0.
     

  • Resets immediately when arst=1.
     

  • Rolls over from maximum value → 0 automatically.

Verilog Implementation

 Verilog

module upcounter #(

  parameter N = 8   // Number of bits, default = 8

)(

  input  wire           clk,   // Clock input

  input  wire           arst,  // Asynchronous reset (active high)

  input  wire           en,    // Enable signal

  output reg  [N-1:0]   Q      // Counter output

);

 

  always @(posedge clk or posedge arst) begin

    if (arst)

      Q <= {N{1'b0}};       // Reset counter to 0

    else if (en)

      Q <= Q + 1'b1;         // Increment counter

  end

 

endmodule

Code Explanation

​

1.Parameterization

parameter N = 8

​​​

  • Allows the designer to change the counter width without modifying the core code.
     

  • {N{1'b0}} automatically creates an N-bit zero value.

​

2.Asynchronous Reset

always @(posedge clk or posedge arst)

​​​

  • Monitors both the clock and the reset signal.
     

  • If arst is high, the counter resets immediately, without waiting for a clock edge.

 

3.Enable Control
 

else if (en)

    Q <= Q + 1'b1;

​​​

  • When en=1, the counter increments on each clock cycle.
     

  • If en=0, the counter holds its current value.

​

​4.Wrap-around Behavior

​

Since we’re using fixed-width binary arithmetic, overflow automatically wraps the counter to 0.

Verilog Testbench

 Verilog

module tb_upcounter;

 

  parameter N = 8;

  reg         clk = 0;

  reg         arst = 1;

  reg         en = 0;

  wire [N-1:0] Q;

 

  // Instantiate the counter

  upcounter #(.N(N)) tb (

    .clk(clk),

    .arst(arst),

    .en(en),

    .Q(Q)

  );

 

  // Clock generation: 10 ns period

  always #5 clk = ~clk;

 

  initial begin

    // Initial reset

    arst = 1; en = 0;

    #15 arst = 0;

 

    // Start counting

    en = 1; #100;

 

    // Disable counting

    en = 0; #30;

 

    // Resume counting

    en = 1; #50;

 

    // Trigger reset mid-count

    arst = 1; #10;

    arst = 0; #30;

 

    $finish;

  end

 

  initial

    $monitor("Time=%0t | arst=%b | en=%b | Q=%0d (%b)",

              $time, arst, en, Q, Q);

 

endmodule

Testbench Walkthrough

 

1.Clock Generation
 

always #5 clk = ~clk;

 

  • Produces a 10 ns clock period (100 MHz).

​

​

2.Reset and Enable Sequence

​

  • arst starts high → counter is held at 0.
     

  • After 15 ns, reset is released.
     

  • en=1 → counter starts incrementing.
     

  • Later, en is toggled to pause and resume counting.

​

3.Monitoring Outputs

​

$monitor("Time=%0t | arst=%b | en=%b | Q=%0d (%b)", ...);

​

  • Displays simulation time, control signals, and counter values in decimal and binary.

​

​

Expected Simulation Output (Partial)

​

Time=0  | arst=1 | en=0 | Q=0 (00000000)

Time=15 | arst=0 | en=1 | Q=0 (00000000)

Time=25 | arst=0 | en=1 | Q=1 (00000001)

Time=35 | arst=0 | en=1 | Q=2 (00000010)

...

Time=115| arst=0 | en=0 | Q=10 (00001010)

...

Time=175| arst=1 | en=1 | Q=0 (00000000)

​

​

Key Takeaways

​

  • Parameterization makes your design reusable for different bit widths.
     

  • Asynchronous reset ensures immediate clearing, even without a clock.
     

  • Enable control allows pausing/resuming without losing the count.
     

  • Wrap-around behavior comes naturally from fixed-width binary arithmetic.

Interview Questions & Answers on Parameterizable Up Counters

​

Q1. Why parameterize counters?

​

Answer:
Parameterization allows you to design one reusable counter module that can work for any bit-width simply by changing a parameter value, without modifying the code.

  • Advantages:
     

    • Saves development time — no need to rewrite multiple versions.
       

    • Reduces human errors in manual code changes.
       

    • Improves maintainability and scalability in large projects.
       

  • Example: You can have an 8-bit, 16-bit, or 32-bit counter by just setting parameter N = 8, 16, or 32.

​

​

Q2. How do you handle arithmetic when the counter width N = 1?

​

Answer:
For a 1-bit counter, the expression Q <= Q + 1'b1; still works, toggling Q between 0 and 1.

  • Considerations:
     

    • Edge cases arise if your logic assumes multiple bits (e.g., Q[N-1:0] comparisons).
       

    • Synthesis tools optimize this into a single flip-flop with toggle logic.
       

    • Always test 1-bit configurations to confirm correct rollover behavior.

​

​

Q3. How can you detect overflow in an up counter?

​

Answer:
Overflow occurs when the counter value reaches all 1s ({N{1'b1}}).

Detection Logic:

verilog code:


overflow = (Q == {N{1'b1}});

​​​

  • Before incrementing, if overflow is high, the next value will wrap to zero.
     

  • Overflow flags are useful in timers, packet counters, or data overflow detection.

​

​

Q4. What are the pros and cons of wide up counters in FPGAs?

​

Answer:

  • Pros:
     

    • Simple to understand and implement.
       

    • Can be directly mapped to FPGA hardware.
       

  • Cons:
     

    • Large counters consume more flip-flops and routing resources.
       

    • Longer carry chains increase delay and reduce maximum frequency.
       

For very wide counters, block RAM-based counters or DSP adders may be more efficient.

​

Q5. How does synthesis optimize the statement Q <= Q + 1?

 

Answer:
Synthesis tools map this to fast adder circuits:

  • FPGA: Uses built-in carry chains for speed.
     

  • ASIC: Maps to optimized adder cells in standard cell libraries.

This ensures increment operations are performed efficiently, minimizing logic delay.

​

Q6. How do you add an enable signal without gating the clock?

​

Answer:
Never physically gate the clock — it causes clock skew and timing hazards.


Instead:

verilog

​

always @(posedge clk or posedge arst) begin

  if (arst)

    Q <= 0;

  else if (en)

    Q <= Q + 1;

end

 

  • This keeps the clock path clean and only changes data when en is active.

​

Q7. What simulation checks should be included for counters?

​

Answer:
You should verify:

  • Reset behavior (async or sync).
     

  • Increment only when enabled.
     

  • Hold value when disabled.
     

  • Correct wrap-around at maximum value.
     

  • Stability under random resets and enables.
     

  • Corner cases (e.g., reset during increment).

​

​

Q8. How to implement a saturating up counter?

​

Answer:
Instead of wrapping, hold the maximum value:

verilog

​

if (Q != MAX && en)

  Q <= Q + 1;

 

  • Useful in applications like rate limiters or event counters where you don’t want rollover.

​

​

Q9. Why might you prefer Gray code counters in ADC sampling applications?

​

Answer:
Gray code changes only one bit per increment, reducing switching noise and avoiding glitches in multi-bit transitions.

  • In ADC sampling, this minimizes errors when reading data asynchronously across clock domains.

​

​

Q10. How do you make a counter synthesizable for ASIC flow?

​

Answer:

  • Use non-blocking assignments (<=) in sequential logic.
     

  • Avoid simulation-only constructs like #delays or $display in the main logic.
     

  • Define reset behavior explicitly.
     

  • Ensure parameters are constants at synthesis time.
     

  • Follow ASIC timing and area constraints for high performance.

​

​

Q11. Can you implement an up counter with an LFSR for pseudo-counting?

​

Answer:
Yes, Linear Feedback Shift Registers (LFSRs) produce pseudo-random sequences but not linear counts.

  • Use Cases: PRBS generation, random number generation, hardware self-test.
     

  • Not suitable when you need exact sequential counting.

​

​

Q12. How do you create test vectors that stress an up counter?

​

Answer:

  • Apply long enable pulses for continuous counting.
     

  • Toggle en randomly to test hold/resume states.
     

  • Apply resets at random points mid-count.
     

  • Test wrap-around by setting count near max value.
     

  • In parameterized counters, test N=1, N=4, N=16, etc.

​

​

Q13. How do you efficiently merge compare and increment logic?

​

Answer:
Combine them in one block:

verilog

​

if (Q == MAX)

  Q <= 0;

else

  Q <= Q + 1;

 

  • Reduces logic levels and ensures faster timing closure.

​

​

Q14. What are common beginner bugs in counter designs?

​

Answer:

  • Clock gating instead of using enables.
     

  • Forgetting reset logic.
     

  • Using blocking assignments = in sequential blocks.
     

  • Not handling overflow or wrap-around.
     

  • Incorrect sensitivity lists in combinational logic (pre-SystemVerilog).

​

​

Q15. How do you support both synchronous load and enable in a counter?

​

Answer:
Use priority order inside the sequential block:

verilog

​

if (arst)

  Q <= 0;

else if (load)

  Q <= D;

else if (en)

  Q <= Q + 1;

 

  • Ensures load overrides enable when both are active.
     

  • This makes counter behavior predictable and easy to debug.

​​

​

Verilog LAB - Synchronous COunter

Verilog LAB - Down Counter 

© Copyright 2025 VLSI Mentor. All Rights Reserved.©

Connect with us

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