A Blinky is the absolute minimal circuit we typically build on an FPGA: an LED that blinks using the system clock and a counter.
In the software world, the first program is always printing text to the screen. In the electronics world, our “Hello World” is making an LED blink. We affectionately call it Blinky.
If you come from Arduino, you might think: “Bah, this is a piece of cake. I’ll just put a delay(1000) and done”.
Well, I have some bad news: in an FPGA, there is no delay function. In fact, the concept of “waiting” as in a microcontroller doesn’t exist. The hardware is always running, current is always flowing.
Today, we’re going to write our first Verilog module and understand how to tame time in a digital circuit.
The Problem of Time in Hardware
Let’s analyze why we can’t simply “wait”.
In a processor (Arduino), delay(1000) means: “Mr. Processor, stay in this loop doing nothing useful for 1000 milliseconds”. You stop the execution flow.
In an FPGA, you have a clock signal that is beating at a dizzying speed (for example, 12 MHz, meaning 12 million times per second). Every time the clock “ticks”, all your Flip-Flops update. You can’t tell them to stop.
If you want something to happen once per second, you have to count the ticks.
To make an LED blink at 1 Hz with a 12 MHz clock, we need a circuit that counts up to 6 million (on) and then another 6 million (off).
Our First Verilog Module
We’ll describe this behavior. We’ll use a binary counter.
Imagine a counter that adds 1 on each clock cycle. The lowest bits will change frantically (0, 1, 0, 1…), but the higher bits will change much more slowly. If the counter is large enough, the highest bit will blink at a speed visible to the human eye.
Here’s the complete code for our blinky.v:
// Module Definition
module blinky (
input wire clk, // Clock Input (12 MHz on IceStick/Alhambra)
output wire led // Output to LED
);
// 1. Declare a register (memory) to count
// A 24-bit register allows us to count up to 2^24 = ~16 million
reg [23:0] counter = 24'd0;
// 2. Sequential Logic Block
// "Every time the 'clk' signal has a positive edge (posedge)..."
always @(posedge clk) begin
counter <= counter + 1; // ...increment the counter.
end
// 3. Combinational Logic (Output)
// Connect the 'led' wire to the most significant bit (the 23rd) of the counter.
assign led = counter[23];
endmoduleSee what we did? There are no for or while loops. There’s a description of structure.
Analyzing the Syntax
Let’s break down the new elements that appeared:
module and endmodule
This is the black box. It defines the inputs and outputs of our component. It’s equivalent to a “class” in OOP, but in hardware.
reg [23:0] counter
Here, we are defining a bus of 24 wires that have memory (Flip-Flops).
The notation [23:0] indicates it’s a vector ranging from bit 23 (the highest) down to bit 0 (the lowest).
always @(posedge clk)
This is the most important structure in Verilog. It defines a block that executes always when a specific event occurs.
posedge clk: Means Positive Edge. Exactly the instant when the clock voltage transitions from 0 to 1.- Everything inside this block will become Flip-Flops on our FPGA.
The Assignment <=
Notice that we used <= instead of =.
<=is a non-blocking assignment. It’s used in sequential logic. It means: “Calculate the value now, but don’t update it until the clock cycle finishes”.- This is important so that the entire circuit changes state in unison.
assign led = counter[23]
This is a continuous assignment. We are virtually “soldering” a wire from bit 23 of our counter register to the LED output pin.
Since bit 23 is the one that takes the longest to change (divides the frequency by 2^24), our LED will blink slowly. Initializing the counter prevents simulation from starting in X; if you require portability across families, use an explicit reset input.
The Math of Blinking
If our clock runs at 12 MHz, the counter advances 12 million times per second.
Bit 0 changes at 6 MHz.
Bit 1 changes at 3 MHz.
…
Bit n changes at a frequency of:
f_bit = f_clk / 2^(n+1)For bit 23:
f_bit23 = 12,000,000 / 2^24 = 0.715 HzPerfect! It will blink a bit slower than once per second, slow enough to be seen with the naked eye.
Comparison: Arduino vs FPGA
To illustrate the mindset shift, here’s how you achieve the same effect in both worlds:
| Concept | Arduino (C++) | FPGA (Verilog) |
|---|---|---|
| Philosophy | Sequential Instructions | Parallel Data Flow |
| Time | delay(ms) (Blocking) | Count Clock Cycles (Non-blocking) |
| Execution | CPU “sleeps” during delay | Counter works, rest of chip remains free |
| Resources | CPU Cycles | Flip-Flops and Adders |
The wonderful thing about the FPGA is that, even while this counter is running, the rest of the chip is 100% free. You could add 50 more counters, or a video processor, and the LED would keep blinking exactly the same, unaffected by the rest of the code.
How to Build and Test the Project
If you installed Apio as we discussed in the previous article, the process to see this on your board is trivial:
- Save the code as
blinky.v. - Create an
apio.inifile specifying your board (e.g.,board = icezum). - Run
apio build(Synthesis). - Run
apio upload(Upload).
And that’s it! You should see your LED blinking. You have just designed your first digital hardware. You are no longer just a programmer; you are a hardware architect.