fpga-contadores-temporizadores-prescalers

Counters in FPGA: Timers and Frequency Dividers

  • 5 min

A counter is a register that changes following a numerical sequence on each clock event.

Knowing how to make an FPGA count is fundamental. It is the basis for:

  • Creating timers (like Arduino’s millis()).
  • Generating PWM signals.
  • Slowing down the clock frequency (Prescalers).
  • Scanning memory addresses.

Today we are going to learn to master time.

Anatomy of a counter

A counter is nothing more than a register that adds to itself. Imagine a circuit with a register (memory) and an adder.

On each clock edge, the register reads its own current value.

It adds 1 to it.

It stores the new result.

module simple_counter (
    input wire clk,
    output wire [3:0] leds
);

    reg [3:0] count = 0; // Initialize to 0

    always @(posedge clk) begin
        count <= count + 1; // Add one on each cycle
    end

    assign leds = count;

endmodule
Copied!

Overflow is your friend.

When a 4-bit counter reaches 15 (1111) and you add 1, it automatically rolls over to 0 (0000). It doesn’t cause an error; it simply wraps around. This is ideal for creating repetitive cycles.

The frequency divider (prescaler)

Our FPGA’s clock is usually very fast (12 MHz, 50 MHz, 100 MHz). Sometimes we need things to happen slower (for example, blinking an LED at 1 Hz or reading a sensor at 100 kHz).

For this we use a Prescaler. Basically, we count pulses of the fast clock until we reach a limit value, and then we do “something”.

There are two ways to approach this: the ugly way and the less ugly way.

Derived clocks using logic

Beginners often try to create a new clock signal and use it in another always block.

// ⛔ NOT RECOMMENDED PRACTICE
reg slow_clock = 0;
always @(posedge clk) begin
    if (counter == LIMIT) begin
        slow_clock <= ~slow_clock; // Toggle
        counter <= 0;
    end
end

// Using this generated clock elsewhere
always @(posedge slow_clock) begin ... end
Copied!

Why is it wrong? FPGAs have special “highways” dedicated exclusively to the main clock signal (Clock Tree) to ensure it reaches every corner of the chip simultaneously. If you create a clock with normal logic (flip-flops), the signal will travel through regular paths, suffer delays (skew), and your design will become unstable.

Clock enable pulses (strobe)

Instead of creating a new clock, we create an enable pulse (Strobe) that lasts exactly one clock cycle. The entire system still runs at maximum speed (e.g., 12 MHz), but our circuit only “activates” when it receives this pulse.

// ✅ RECOMMENDED PRACTICE
reg pulse_1hz = 0;

always @(posedge clk) begin
    // Default value (so it only lasts 1 cycle)
    pulse_1hz <= 0;

    if (counter == LIMIT) begin
        counter <= 0;
        pulse_1hz <= 1; // Fire!
    end else begin
        counter <= counter + 1;
    end
end

// In the other module, we still use clk, but filter with the if
always @(posedge clk) begin
    if (pulse_1hz) begin
       // This runs only once per second
       led <= ~led;
    end
end
Copied!

How to calculate the limit

To know how high we need to count, the formula is trivial:

Example:

  • Board: IceStick / Alhambra (12 MHz clock = 12,000,000 Hz).
  • Goal: Change the LED state once per second (a full cycle every two seconds, i.e., 0.5 Hz).

We need a register capable of storing the number 12 million.

  • 2^23 = 8,388,608 (falls short).
  • 2^24 = 16,777,216 (is sufficient). We need a 24-bit register.

Complete example: one change per second

Let’s implement a complete module using the Strobe technique.

module blinky_exact (
    input wire clk,      // 12 MHz input
    output reg led = 0   // LED output
);

    // Constant for the limit (we use parameter to make it configurable)
    // 12 MHz - 1 because we count 0
    parameter CLOCK_FREQUENCY = 12000000;
    
    // Counter register (we calculate needed bits or over-provision)
    reg [23:0] counter = 0;
    
    // Control signal (Strobe)
    reg tick_second = 0;

    // --- BLOCK 1: The Prescaler ---
    always @(posedge clk) begin
        // Default behavior
        tick_second <= 0;
        counter <= counter + 1;

        if (counter == CLOCK_FREQUENCY - 1) begin
            counter <= 0;       // Reset counter
            tick_second <= 1;   // Generate the 1-cycle pulse
        end
    end

    // --- BLOCK 2: The LED Logic ---
    // Notice we are still using 'clk', NOT 'tick_second' as a clock
    always @(posedge clk) begin
        if (tick_second) begin
            led <= ~led; // Toggle the LED state
        end
    end

endmodule
Copied!

Down counters and other modes

Of course, we are not limited to adding 1.

  • Down counter: counter <= counter - 1;
  • Count by 2: counter <= counter + 2;
  • Parallel load: We can force the counter to a specific value if a condition is met (useful for making programmable timers).
if (load_enable) begin
    counter <= initial_value; // Manual load
end else begin
    counter <= counter - 1;   // Count down
end
Copied!