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;
endmoduleOverflow 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 ... endWhy 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
endHow 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
endmoduleDown 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