Wiring my board up to an LCD screen on top of a copy of Hegel's Aesthetics. |
Hi folks, in this post I'm going to give you as gentle and introduction to FPGA (I will unpack the acronym later) programming as possible, hopefully explaining as much as I know (which is a very little up to this point, but enough to I guess help some folks so), while providing lots of examples and challenges for folks who need ideas to try out that are easy enough to get a foothold.
If you're familiar with any programming you should have enough to get going in Verilog it just requires a bit of re-orientation and practice - just like any language basically ;)
To start lets think about what we are going to do here, FPGAs are pieces of hardware that we can configure. That configuration is done by taking the language we speak (which is English/Human-Language equivalent Verilog); and converting it, into that configuration for the FPGA, this is configuration is called a bit stream. A bit stream is what the final effort of "synthesizing" is essentially; its kind of like putting together a little song for the chip that the computer tweets over lovingly, lullying it into total subservience.
There are many different kind so FPGAs so you can play little songs to many different kinds of chips, the tunes are ever wilder, faster and more exciting the more powerful the chips get - I've only programmed like one so far. But have a shop around, there are tons of boards and kits out there that aren't that expensive, some of them are open source as well! I'm going to use the Mojo v3 in this post though (purely because its a well documented board, there's a book out on it as well); i thought it would be an easy way to start. I will definitely cover more open source FPGA tech as well in future :P.
Mojo V3 in a super hipster instagram filter |
What are FPGAs
FPGAs (Field Programmable Gate Arrays - see told'cha) are essentially a very very small grid of (usually thousands) of configurable circuits. FPGAs provide a way to describe combinations of these small configurable circuits that are provably analogous (intended to work exactly the same as) some hardware description language.To configure the circuitry I mentioned, you essentially tell the FPGA what to tell its different components. This list might be a ton of information but its not super crucial you understand it, you can program a board fine without knowing any of this, its just good to know about it so you can be a little more aware of what you're doing.
These different components of an FPGA are of the following (some manufacturers may differ in many ways):
- Programmable Interconnects (Points) (PIPs) : These are (according to my sources [2]) basically blocks of circuitry that allow you to route signals between CLBs.
- Controllable Logic Blocks (CLBs) : This component of the FPGA is where most of the magic happens, the more CLBs an FPGA has the more data it can store and process essentially. The CLBs have a couple of components to them:
- Flip Flops : these are for responding to clocked events, storing data (I will explain how this happens later). You will essentially orientate your programming to leverage these Flip Flops to modify information in response to clock events (when the clock signal goes from high to low or vice versa). This I think why they are called" flip flops" because they allow you to flip flop along with the clock lol.
- Internal RAM: for configuring Look up tables (LUTs), these are basically just switch statements of a certain kind, they hold configurations for the logic components[2] inside the CLB.
- Multiplexers : These essentially take in multiple input signals and combined them into fewer output signals or as one post puts it:
The multiplexer, shortened to “MUX” or “MPX”, is a combinational logic circuit designed to switch one of several input lines through to a single common output line by the application of a control signal. [6]
- Configurable I/O Blocks (IO Blocks) : These are basically input/output points (or if you like ports), that allow you to tap signals into the FPGA, or push out signals from the FPGA. A simple example would be turning on an LED, you will need some place to stick the LED 'into' to make it turn on, the I/O block is where this signal for the LED will get fed from. Please don't stick LEDs directly into your I/O ports on your boards I'm just making an analogy - I will most likely cover a simple external LED tutorial as well, because its pretty vital in the journey to more complex external stuff ;). The I/O blocks have components to them as well, these essentially allow you to respond to the signal when it makes a certain transition or takes a certain state.
The high or low state of the signal is called the logic level. If we tell a circuit to respond to an event when an input signal is high, the input is referred to as active high. When we tell it to respond to an input signal is low, guess what its called active low!
Check out [2] in the Reading and References section for cool pictures about it.
Anyway its basically like play dough for hackers and electrical engineers. They aren't hard to get your head around but the can be very very useful when you do! I think getting over the FPGA hill is a very important step in your career as a hacker (just my opinion lets say).What you will need
DISCLAIMER: The current tutorial involves giving your address to the Xilinx folks (I think this is because of US Export laws) which is pretty crappy not for any other reason than its private information and it can be mistreated, lost or stolen. So if you're not up for that, please just follow along for interests sake I promise I will provide fully open source no address information bribery required - tutorials as well.
But, if you're okay with the Xilinx folks knowing where you live and that you're learning the dangerous sorcery of FPGA hardware - please go download the Xilinx ISE below.
But, if you're okay with the Xilinx folks knowing where you live and that you're learning the dangerous sorcery of FPGA hardware - please go download the Xilinx ISE below.
- Mojo V3 Board available at amazon[10], SparkFun[9], Alchitry [8] and I'm sure a ton of other places.
- Xilinx ISE available at https://www.xilinx.com/ (Sign up for an account and download the ISE)
- USB 3.0 to Micro-B cable
- Mojo Loader (for loading your program onto the board)
- Mojo IDE (provides a different interface for verilog programing, simpler but less specific than the ISE verilog IDE) Mojo IDE also comes with its own programming variant called Lucid: https://alchitry.com/pages/lucid
You will need to install the ISE on which ever platform you like (I suggest using Linux based one's its just way easier and doesn't require a ton of driver drama). For more information how to get that going please see the following example: https://alchitry.com/pages/installing-ise .
Verilog Crash Course
I'm going to be doing this tutorial in Verilog; realizing this can be a bit of an obscure language (I agree it is obscure - but for no super hard to understand reason to be honest) you might need a bit of a jump start into to it if you've not ever done it before, but don't fret the whole point of this blog post is to try to explain this to someone who's never done it before.
Lets get to it, Verilog is a hardware description language, we call Verilog hardware language register-transfer-level (or its an RTL language); this wording is meant to describe what Verilog targets with its abstraction, essentially the transfer of signals between hardware level registers (and other components) [11,12,13,14]. Anyway once you have everything installed according to the tutorials above; you can then start scripting some Verilog for the Mojo.
Here's what a bare bones Verilog script looks like:
Here's what a bare bones Verilog script looks like:
module hello_verilog(input clk, output external_led)
always @ (posedge clk) begin
external_led <= clk ^ external_led;
end
endmodule
This is just an example Verilog script to show how elements of the language work, it probably won't achieve anything profound if it were implemented as a real Verilog module on an actual FPGA though (this is because of the frequency of the clock - more on this later). And the reason is something that I need to cover before I can show you a real Verilog script.
Anyway, before we unpack that; lets take a look at this script and explain each part.
Anyway, before we unpack that; lets take a look at this script and explain each part.
module hello_verilog(input clk, output external_led);
...
endmodule
This is the module header; it declares the inputs and outputs of the module begin defined here. Also it obviously needs to have a declared endmodule as well.
These inputs and outputs (and I'm no deep expert here) can essentially be driven by the I/O blocks - depending on what you feed the module as inputs. So if you want to drive in some external module with pins the FPGA board needs to talk to (like a logic analyzer or oscilloscope) - these input / output declarations are what you designate them as.
The next section of code I want to get around is this:
always @ (posedge clk) begin
...
end
This is an always section of Verilog code, it defines actions that happen "in tune" or according to certain changes with the specified sensitivity list in the brackets. This means that when the clock "clk" (which is for all intents and purposes a square wave - see the example further down), is registering a change from a 0 to 1 value this is called a positive edge, or posegde in Verilog (the other one is called a negedge or negative edge).
So it essentially says in summary, when the clock changes from 0 to 1 always do this stuff. And it closes with an end token of course.
Next we have the assignment operation in side the always block:
external_led <= clk ^ external_led;
This is called a non-blocking assignment[14]. This kind of assignment means if there are any assignments after it or before it (of this same kind "<="), they will all be assigned in parallel (see [14] for an excellent explanation of the different pit falls and always block politics as well). Its kind of like attaching connectors to things from a source, if they are in parallel the electricity can be expected to be seen running down each connector at the same time. The assignment gives external_led the value of clk xor'd with the current value of external led. If you think it through, you'll see that this is a way to make it flip on and off not so? A good exercise is to try and write out the values.
Okay so I mentioned this is a non-blocking assignment, does that mean there is another kind? YES! Guess what its called a blocking assignment! Here's what it looks like:
// syntax [left operand] "=" [right operand]
external_led = clk ^ external_led;
A dead plain "=" symbol is used. The way to remember this lies in understanding the header of an always where this assignment is allowed, here's a blocking always block:
always @ (*) begin
external_led = clk ^ external_led
end
These always blocks, are for designing combinatorial logic circuits or logic gates (literally AND, OR, XOR etc gates ) they are great if you need to get really really low level, as in as base implementation of any digital logic as you'd like on the FPGA.
To summarize:
- blocking = assignments are for combinatorial logic : Logic gates, where all statements take on their respective actions in parallel to each other.
- non-blocking <= for sequential logic : statements potentially depend on a certain sequence to derive their values.
Anyway what I'm trying to say is blocking always blocks are for combinatorial logic, non-blocking always blocks are for sequential logic or building registers - these are things that store information for us, we can chain them up in "sequences" and do useful things like building finite state machines! The reason I'm going with a non-block always block in the example, and will most probably go with it in the real example too - is because the on board led's on the Mojo are declared as reg's in Verilog- and we will modulate our clock speed using regs too! You will run into a fair amount of frustration with this concept anyway - so just get used to wading out your problems with it, I don't think its a completely avoidable mistake hehe.
You probably want to pick up a book on assignment operations, and constraints and practice them a bit (I've tried to be as verbose as possible). This is because one can immediately tell that this imposes major drama on which types are assignable to which types i.e. can you assign reg to wire? Its basically due to how it resolves different types to the board components and combinations of them mentioned above - if you give it something that works sequentially or is meant for non-storage options it can cause antagonisms that are annoying. So be careful and try out stuff a lot, write some bad Verilog so you know what it looks like lol.
Okay so that's pretty bare bones Verilog, you will probably be able to develop "valid" Verilog using this but to be able to compute on real things, real clocks and achieve actual interaction with real "hardware" you're going to need to learn how to divide the clock!
Clock Divider Circuits and Blinking LEDs
I like starting with this because it will be an integral part of a lot of Verilog problems and problem solving strategies. We will most probably be sampling from a certain module at a given rate, clocking in data at given rates; you might need to run multiple different things at multiple different clock frequencies or be required to by certain protocols. So you shouldn't think of a clock as a single rhythm to dance your circuit to, instead see it as something consisting of beats you can skip, group together or chop up anyway you need (within in physical bounds of course) - multiple loving tweets at different frequencies :)
Here's a simple clock divider circuit in Verilog, this essentially drives the Mojo v3 boards on board led[0] to follow a clock that flips on and off at half the frequency of the boards natural clock:
module mojo_top( /*module declaration*/
input clk, //clock input @ 50 MHz (according to ucf file)
input rst_n, //reset input
output [7:0]led //array of LEDs on the Mojo Board
);
wire rst = ~rst_n; // make reset active high
reg [25:0] clk_div; //declare 25 bits worth of D-FlipFlop "storage"
assign led[6:0] = 6'bz; //set the LEDs from 6 down to 7 to "off"
assign led[7] = clk_div[20]; //assign 20th bit in clk_div to led 0
always @ (posedge clk) begin //declare always block
clk_div <= clk_div + 1; //add one to the 25 bit array
end
endmodule
And the user constraints file looks like this:
NET "clk" TNM_NET = clk;
TIMESPEC TS_clk = PERIOD "clk" 50 MHz HIGH 50%;
# PlanAhead Generated physical constraints
NET "clk" LOC = P56 | IOSTANDARD = LVTTL; //clock signal
NET "rst_n" LOC = P38 | IOSTANDARD = LVTTL; //reset button
NET "led<0>" LOC = P134 | IOSTANDARD = LVTTL; //on board led 1
NET "led<1>" LOC = P133 | IOSTANDARD = LVTTL; //on board led 2
NET "led<2>" LOC = P132 | IOSTANDARD = LVTTL; //and so forth...
NET "led<3>" LOC = P131 | IOSTANDARD = LVTTL;
NET "led<4>" LOC = P127 | IOSTANDARD = LVTTL;
NET "led<5>" LOC = P126 | IOSTANDARD = LVTTL;
NET "led<6>" LOC = P124 | IOSTANDARD = LVTTL;
NET "led<7>" LOC = P123 | IOSTANDARD = LVTTL;
Lets run through the code a bit. I need to cover this declaration:
reg [25:0] clk_div;
This declares what is called a register. Now it doesn't mean that it simply stores values, it kind of just keeps a value until you give it another one. And whats also important to remember is that sometimes you can string up reg's that won't actually make it into the bit stream. Check out this awesome stack overflow answer:
- (extract from: https://stackoverflow.com/questions/33459048/what-is-the-difference-between-reg-and-wire-in-a-verilog-module )
Besides the other declarations and (already covered) always blocks, we see a input/output declaration mentioning :
The other interesting piece of code here are the reg declarations I added (not outputs or inputs, just internal registers we need to store some of the clock flips):
assign led[6:0] = 6'bz;
This assigns 0 to the 6 bit positions from 7th (number 6) down to 0th which leaves 1 open, namely the 8th bit is set as:
assign led[7] = clk_div[20];
This assignment sets the 7th bit to the clk_div array of registers 20th bit value. So i declared 25 bits and I'm taking the 20th one and making it always the same as led[7]. The final piece of the puzzle is in the always block:
clk_div <= clk_div + 1;
This adds 1 bit to the 25 bit value of clk_div. As far as I understand this, it means that clk_div will add up and when whatever number its at has a "1" in the 20th bit position, the LED[1] will turn on.
If you think about how binary numbers add up you can see that this means it will slowly bubble up the bit values, floating the carry up the 25 bit places until it hits the 20th one. This is much slower than the raw clock, which flips up n down at 50Mhz which is like a bajillion times a second your eyes won't even see the LED move its so fast. So we basically tell the clock to add up its flips in a total and when we are happy with the amount we tell the LED to turn on! I hope that makes it easier to understand! What this means is obviously we can control how fast the LED appears to pulse, we can make it pules faster and slower, we can even make multiple LEDs on the board pulse at different rates if you want to give yourself a tension head ache lol but its fun!
It can be abit hard to get examples If you understand how the clk_div trick works then you essentially know how to build a counter already. All you need to do is make a counter for the led's. But there's one caveat, and that is again the issue of blinking the LED on and off too fast. To solve this we just divide the clock, and then add only when the clk_div[20] reads a 1. If we then map the counter to the LEDs, jobs done!
Here's the Verilog:
module mojo_top( /*module declaration*/
input clk, //clock input @ 50 MHz (according to ucf file)
input rst_n, //reset input
output [7:0]led //array of LEDs on the Mojo Board
);
reg [25:0] clk_div;
reg [7:0] led_dff;
assign led[7:0] = led_dff[7:0];
always @ (posedge clk) begin
clk_div <= clk_div + 1;
if (clk_div[20:0] == 0) begin
led_dff <= led_dff + 1;
end
if (rst) begin
led_dff <= 0;
end
end
endmodule
You can also split up the counter by making the lower bits "add" up to the high bits like so:
module mojo_top( /*module declaration*/
input clk, //clock input @ 50 MHz (according to ucf file)
input rst_n, //reset input
output [7:0]led //array of LEDs on the Mojo Board
);
wire rst = ~rst_n;
reg [50:0] clk_div;
reg [2:0] dff_1; //d-flip flop for led's
reg [2:0] dff_2;
assign led[1:0] = dff_1[2:0]; //assign first two bits
assign led[3:2] = dff_2[2:0]; //assign second two bits
assign led[6:4] = dff_1 + dff_2; //save total
assign led[7] = clk_div[20];
always @ (posedge clk) begin
clk_div <= clk_div + 1;
if (clk_div[24:0] == 0) begin
dff_1 <= dff_1 + 1;
end
if (clk_div[27:0] == 0) begin
dff_2 <= dff_2 + 1;
end
if (rst) begin
dff_1 <= 0;
dff_2 <= 0;
end
end
endmodule
In this example I also made them add at different speeds you can of course make them all tick the same way but that would be kind of lame. Anyway that's it for this one, let me know if I got some stuff wrong, I'm going to keep posting about other boards n blinky lights. Stay tuned!
reg [25:0] clk_div;
This declares what is called a register. Now it doesn't mean that it simply stores values, it kind of just keeps a value until you give it another one. And whats also important to remember is that sometimes you can string up reg's that won't actually make it into the bit stream. Check out this awesome stack overflow answer:
> Contrary to their name, regs don't necessarily correspond to
> physical registers. They represent data storage elements in
> Verilog/SystemVerilog. They retain their value till next value is
> assigned to them (not through assign statement). They can be
> synthesized to FF, latch or combinatorial circuit. (They might not be
> synthesizable !!!)
- (extract from: https://stackoverflow.com/questions/33459048/what-is-the-difference-between-reg-and-wire-in-a-verilog-module )
Besides the other declarations and (already covered) always blocks, we see a input/output declaration mentioning :
- input clk - the input clock signal (pay attention to the User Constraints File to see how this is declared). In our example it simply a slowed down version of the clock on the board.
- input rst_n - we don't actually use this just yet, its the input from the reset button on the Mojov3 Board; this is a great little "toggle" to have around.
- output [7:0] led - this is an array of reg's declared as output. I know they are reg's (registers) because of how they are elaborated in the User Constraints file namely: NET "rst_n" LOC = P38 | IOSTANDARD meaning essentially location P38 on the board should be a NET called rst_n;
The NET type is essentially the type that allows driving form and to I/O blocks, check out this other awesome stackoverflow answer:
"The net data types can represent physical connections between structural entities, such as gates. A net shall not store a value (except for the trireg net). Instead, its value shall be determined by the values of its drivers, such as a continuous assignment or a gate."
The other interesting piece of code here are the reg declarations I added (not outputs or inputs, just internal registers we need to store some of the clock flips):
assign led[6:0] = 6'bz;
This assigns 0 to the 6 bit positions from 7th (number 6) down to 0th which leaves 1 open, namely the 8th bit is set as:
assign led[7] = clk_div[20];
This assignment sets the 7th bit to the clk_div array of registers 20th bit value. So i declared 25 bits and I'm taking the 20th one and making it always the same as led[7]. The final piece of the puzzle is in the always block:
clk_div <= clk_div + 1;
This adds 1 bit to the 25 bit value of clk_div. As far as I understand this, it means that clk_div will add up and when whatever number its at has a "1" in the 20th bit position, the LED[1] will turn on.
If you think about how binary numbers add up you can see that this means it will slowly bubble up the bit values, floating the carry up the 25 bit places until it hits the 20th one. This is much slower than the raw clock, which flips up n down at 50Mhz which is like a bajillion times a second your eyes won't even see the LED move its so fast. So we basically tell the clock to add up its flips in a total and when we are happy with the amount we tell the LED to turn on! I hope that makes it easier to understand! What this means is obviously we can control how fast the LED appears to pulse, we can make it pules faster and slower, we can even make multiple LEDs on the board pulse at different rates if you want to give yourself a tension head ache lol but its fun!
8 bit led counter and split counter
It can be abit hard to get examples If you understand how the clk_div trick works then you essentially know how to build a counter already. All you need to do is make a counter for the led's. But there's one caveat, and that is again the issue of blinking the LED on and off too fast. To solve this we just divide the clock, and then add only when the clk_div[20] reads a 1. If we then map the counter to the LEDs, jobs done!
Here's the Verilog:
module mojo_top( /*module declaration*/
input clk, //clock input @ 50 MHz (according to ucf file)
input rst_n, //reset input
output [7:0]led //array of LEDs on the Mojo Board
);
reg [25:0] clk_div;
reg [7:0] led_dff;
assign led[7:0] = led_dff[7:0];
always @ (posedge clk) begin
clk_div <= clk_div + 1;
if (clk_div[20:0] == 0) begin
led_dff <= led_dff + 1;
end
if (rst) begin
led_dff <= 0;
end
end
endmodule
You can also split up the counter by making the lower bits "add" up to the high bits like so:
module mojo_top( /*module declaration*/
input clk, //clock input @ 50 MHz (according to ucf file)
input rst_n, //reset input
output [7:0]led //array of LEDs on the Mojo Board
);
wire rst = ~rst_n;
reg [50:0] clk_div;
reg [2:0] dff_1; //d-flip flop for led's
reg [2:0] dff_2;
assign led[1:0] = dff_1[2:0]; //assign first two bits
assign led[3:2] = dff_2[2:0]; //assign second two bits
assign led[6:4] = dff_1 + dff_2; //save total
assign led[7] = clk_div[20];
always @ (posedge clk) begin
clk_div <= clk_div + 1;
if (clk_div[24:0] == 0) begin
dff_1 <= dff_1 + 1;
end
if (clk_div[27:0] == 0) begin
dff_2 <= dff_2 + 1;
end
if (rst) begin
dff_1 <= 0;
dff_2 <= 0;
end
end
endmodule
In this example I also made them add at different speeds you can of course make them all tick the same way but that would be kind of lame. Anyway that's it for this one, let me know if I got some stuff wrong, I'm going to keep posting about other boards n blinky lights. Stay tuned!
Reading and References
- Register Transfer Level (Wikipedia) - https://en.wikipedia.org/wiki/Register-transfer_level
- Field Programmable Gate Array (Wikipedia) - https://en.wikipedia.org/wiki/Field-programmable_gate_array
- All about FPGAs - https://www.eetimes.com/document.asp?doc_id=1274496
- HyperPhysics : The D Flip Flop - http://hyperphysics.phy-astr.gsu.edu/hbase/Electronic/Dflipflop.html
- Logic Levels - https://learn.sparkfun.com/tutorials/logic-levels/all
- "What is the meaning of Active high and Active low in digital circuits" https://www.quora.com/What-is-the-meaning-of-active-low-and-active-high-in-digital-circuits-and-logic-design
- Electronic Tutorials : The Multiplexer https://www.electronics-tutorials.ws/combination/comb_2.html
- Mojo V3 (Alchitry) - https://en.wikipedia.org/wiki/Field-programmable_gate_array
- Mojo V3 (SparkFun) - https://www.sparkfun.com/products/11953
- Mojo V3 (Amazon) - https://www.amazon.com/Mojo-V3-FPGA-Development-Board/dp/B0752XX7G6
- Verilog (Wikipedia) https://en.wikipedia.org/wiki/Verilog
- IEEE Standard for Verilog Hardware Description Language : Standard 1364 https://www.eg.bucknell.edu/~csci320/2016-fall/wp-content/uploads/2015/08/verilog-std-1364-2005.pdf
- Verilog Module Structure (Wikibooks) https://en.wikibooks.org/wiki/Programmable_Logic/Verilog_Module_Structure
- Verilog Always @ Blocks - https://class.ece.uw.edu/371/peckol/doc/Always@.pdf
- Introduction to Verilog - http://www.doe.carleton.ca/~jknight/97.478/PetervrlK.pdf
- Best Practices for FPGA Development - http://www.irtc-hq.com/wp-content/uploads/2015/04/Best-FPGA-Development-Practices-2014-02-20.pdf
- Frequency Divider Circuit (Tutorials Point) https://www.youtube.com/watch?v=nL8u0YBhyWg
- Modular Monthly: Clock dividers & multipliers (Future Music Magazine) - https://www.youtube.com/watch?v=ilo52K8Oje8
- Your first FPGA program (nandland) - https://www.nandland.com/vhdl/tutorials/tutorial-your-first-vhdl-program-part1.html
- Xilinx Constraights Guide - http://www.fdi.ucm.es/profesor/mendias/DAS/docs/cgd.pdf
- Altering the FPGA clock frequency of the Mojo (Smolloy.com) https://www.smolloy.com/2016/01/altering-the-fpga-clock-frequency-of-the-mojo/
- Arty FPGA 01: Hello World with Verilog & Vivado - https://timetoexplore.net/blog/arty-fpga-verilog-01
- https://www.edaplayground.com/
- LEARNING VERILOG FOR FPGAS: THE TOOLS AND BUILDING AN ADDER - https://hackaday.com/2015/08/19/learning-verilog-on-a-25-fpga-part-i/
- A Verilog HDL Test Bench Primer - https://people.ece.cornell.edu/land/courses/ece5760/Verilog/LatticeTestbenchPrimer.pdf
- Icarus + GTK Wave Guide http://inf-server.inf.uth.gr/~konstadel/resources/Icarus_Verilog_GTKWave_guide.pdf
- http://iverilog.wikia.com/wiki/GTKWAVE
- Verilog and Number Litreals - http://web.engr.oregonstate.edu/~traylor/ece474/beamer_lectures/verilog_number_literals.pdf
- What's the deal with Verilog's reg's and wires - https://blogs.mentor.com/verificationhorizons/blog/2013/05/03/wire-vs-reg/
Comments
Post a Comment