Just yesterday I took delivery of my first ever FPGA board, and I thought I’d document my first program — a counter which displays its output using the 8 on-board LEDs!
The Digilent Atlys board has a Xilinx Spartan 6 FPGA on board. Consequently some of the Verilog code I’ve got in this article is Xilinx specific. Furthermore, the pin specifications, and peripherals described in this article are specific to the Digilent Atlys board.
For those who have not come across the term FPGA before, it stands for ‘field programmable gate array’. It can be thought of as a large collection of logic gates (and, or, nor, xor, etc.), which can be dynamically configured, allowing developers to build logic circuits. They’re incredibly versatile, with the ability to create circuits that do basic logical operations, or the ability to create entire processor cores and embedded peripherals.
However, first things first. Let’s create a small project that simply counts and displays its output on the 8 on-board LEDs.
Building a Counter
On the second page of the wizard, a lot of the settings can be left as the default. However, make sure to set ‘Family’ to ‘Spartan6′, set ‘Device’ to ‘XC6SLX45′, and set ‘Package’ to ‘CSG324′. Below is a screenshot of the wizard window as I’ve configured it.
Once you’ve completed the wizard, right click on the ‘xc6slx45-2csg324′ item under ‘Hierarchy’, then click ‘New Source’.
In the resulting wizard, choose ‘Verilog Module’, and enter some file name — I’ve called mine ‘Counter’. Clicking next will take you to a page in which you can define the inputs and outputs of your Verilog module. We need a clock input, and 8 outputs for the counter value.
Just like in any other programming language, the names you give here really aren’t important, so long as they’re used consistently. Of course, its a good idea to use sensible names.
I’ve chosen ‘clock’, and ‘value’, as can be seen in the screenshot below.
Notice that for the ‘value’ port, I’ve selected ‘Bus’, and set the MSB and LSB fields to 7 and 0 respectively. If you think of these ports as wires, this basically says that the module has one wire going in to it labelled as ‘clock’, and a bundle of 8 wires collectively labelled as ‘value’ coming out of it.
Finish the wizard, and open the newly created Verilog file if it isn’t opened automatically.
For the Spartan 6, the first thing we’ll need to do is create a clock buffer, which must be used to drive any clock signals used inside any combinatorial logic circuits. We do this by creating an instance of the ‘IBUFG’ primitive, which has been provided by a Xilinx library that Xilinx ISE automatically loads. We must also create a wire, which will connect the output of the ‘IBUFG’ instance that we create to any other components of our circuit. We do this by placing the following code under the ‘module Counter ( … );’ lines:
wire clock_buffered; IBUFG globalClockBuffer ( .I(clock), .O(clock_buffered) );
The syntax ‘.I(clock)’ states that we’re connecting ‘clock’ to the port of IBUFG named ‘I’. Similarly, ‘.O(clock_buffered)’ states that we’re connecting ‘clock_buffered’ to the ‘O’ port of IBUFG.
In order that we can use our counter in future code more easily, we can make it take a parameter to determine how many bits it should count to. This is limited by the size of the ‘value’ bus, and we’ve currently made it 8 bits wide. We can make it ‘N’ bits wide by changing the module definition to the following:
module Counter #(N = 8)( input clock, output [N-1:0] value );
We’ll see how we can use this a bit later in the tutorial.
Now to implement the actual counter. Currently, the output of the module is assumed to be a set of wires. We can insert the “reg” keyword in the module definition, as shown below, in order to define the value bus as a set of registers, which can store the state of the counter.
module Counter #(N = 8)( input wire clock, output reg [N-1:0] value );
We also need to define a set of internal wires, which can be used to transmit the next state of the counter — i.e. the value on these wires will always be the current value of the counter + 1. We do this using a continuous assignment, as shown below.
wire [N-1:0] next_value; assign next_value = value + 1'b1;
Now, we want the counter to take on the value of ‘next_value’ every time the clock ticks. This is done through using an ‘always’ block, as follows:
always @ (posedge clock_buffered) begin value <= next_value; end
The parentheses after the @ symbol above is known as the sensitivity list. It defines a list of signals that cause the code between the 'begin' and 'end' keywords to execute. In this case, we've specified 'posedge clock_buffered', which means the code will start to be executed when the signal on clock_buffered changes from a 0 to a 1. It should be noted that you can have as many always blocks as you like, but you must be careful not to accidentally write code that cannot be synthesised (i.e. turned into something the FPGA can execute). For example, assigning to the same registers/wires from multiple always blocks creates a circuit that cannot be created in hardware, and therefore is not allowed.
The code that we execute in the always block above is fairly trivial. It simply assigns the value on the bus 'next_value' to the register 'value'. It should be noted that we use <=, which is a non-blocking assignment. That is, the value doesn't take effect until the beginning of the next clock cycle. Blocking assignments can be done using the = sign. The value of the left hand side immediately takes on the value of the right hand side.
That's it for the counter. The next section will output the value of the counter to the LEDs.
Outputting the value of the counter
Create a new verilog module, like we did before, but this time call it CounterMain, give it an input -- clock -- and an 8 bit wide output bus named 'leds'.
Now we'll instantiate a Counter, which we'll output to the LEDs. However, the clock frequency on the Atlys board is 100MHz (up to 500MHz), so simply counting using 8 bits will be far too fast to see anything useful. We could divide down the clock using a couple of methods -- using a clock manager, or one of Xilinx's clock cores (which I have yet to learn about), or even simply using a counter that outputs a 1 when it is below half of its maximum value, and a 0 when it is above half of its maximum value. However, just to keep things relatively simple, we'll just create a large counter (say 32 bits), and simply use the 8 most significant bits to drive the LEDs.
We'll need a 32 bit wire bus in order to connect the output of the counter to the leds via a set of 7 output buffers. Note that the Spartan 6 IO documentation says that you must use an output buffer when driving output pins -- although it does seem to work if you forget to do so. In the example here, we use the OBUF primitive output buffer.
module CounterMain( input clock, output [7:0] leds ); wire [31:0] value; Counter #(.N(32)) counter ( .clock(clock), .value(value) ); OBUF led7 ( .I(value), .O(leds) ); OBUF led6 ( .I(value), .O(leds) ); OBUF led5 ( .I(value), .O(leds) ); OBUF led4 ( .I(value), .O(leds) ); OBUF led3 ( .I(value), .O(leds) ); OBUF led2 ( .I(value), .O(leds) ); OBUF led1 ( .I(value), .O(leds) ); OBUF led0 ( .I(value), .O(leds) ); endmodule
And that should be it, as far as the verilog code is concerned anyway. The next thing we need to do is create an 'implementation/user constraints file' (hereon referred to as a UCF file), which tells the compiler the pins on the FPGA to which each input/output variable of the main module is referring.
Creating the Constraints File to Map Variables to Pins
You can do this in a couple of ways -- one way is to use Xilinx's PlanAhead to create mappings using a GUI, or a much faster way (in my limited experience), is to simply create a UCF file by right clicking on your device name in the hierarchy, clicking 'New File', then 'Implementation Constraints File'.
Open the newly created file in the editor window, and paste in the following (note that this is specific to the Digilent Atlys board).
NET "clock" LOC = L15; NET "leds" LOC = U18; NET "leds" LOC = M14; NET "leds" LOC = N14; NET "leds" LOC = L14; NET "leds" LOC = M13; NET "leds" LOC = D4; NET "leds" LOC = P16; NET "leds" LOC = N12;
This maps the 'clock' input to pin 'L15' on the FPGA, and so on. Now, that truly is it. All that is left is to synthesise the circuit, and write it to the FPGA.
Synthesising the Circuit and Programming the FPGA
Select 'CounterMain' from your hierarchy, and right click 'Synthesize - XST', then click run. After that has completed (all being well, successfully), do the same for 'Implement Design' and 'Generate Programming File'.
Next, right click 'Configure Target Device' and click Run, you'll get a dialog saying that iMPACT needs to be open. Click OK. Once iMPACT has opened, double click on 'Boundary Scan', then right click in the newly opened tab and click 'Initialize Chain'. Click yes when it asks you to specify a configuration file, and select the .bit file that is associated with your project (mine is called countermain.bit). Click no when it asks you about SPI Flash, then click OK on the dialog. Right click on the FPGA chip that has appeared, and click program, then bask in the glory of the counting LEDs.
A Note on Persistence in FPGAs
FPGAs use volatile memory to store their circuits -- that is, when power to the FPGA is lost, the data is lost too. Because of this, many (if not all) boards come with some sort of on-board flash, which can be used to store the FPGA program such that when the FPGA is powered on, the FPGA can automatically program itself with the data from the flash.
Thanks for reading this article. I hope you found it informative, please leave comments and questions below and I'll do my best to respond to them. Don't forget to check back soon, as I'll continue to write about my FPGA projects here.
The Verilog source code can be downloaded below from here.