Tutorial 1: The Simplest FPGA in the World

Verilog

First we will make the simplest possible FPGA. It will be a wire. Create a new project in Vivado called tutorial1 and add a Verilog file called top.v. You can use the wizard to add ports if you like when Vivado creates the file, or you can add them yourself using the text editor. The design should have a single input called switch and a single output called led.

Edit the file so it looks like this

module top (
  input switch,
  output led
);

  assign led = switch;
endmodule

This declares a Verilog module called top. A module is just a container for part or all of your design. We will learn later that modules can instantiate other modules to create a hierarchical design. But, given that this is the simplest FPGA in the world, that is not necessary.

We declare the module to have a single input called switch and a single output called led. These will correspond to input and output pins on the actual FPGA. The only thing else in the design is the assignment statement. This statement just means that the output led is to change whenever the input switch changes. In effect, it simply creates a wire and connects led to switch.

Constraints

The compiler cannot turn your Verilog code into a working FPGA however because you have not given it enough information. Sure, it knows there is an input pin called switch and an output pin called led, but which pins do you want to use? There are hundreds to choose from. We need to tell it which pins to use. We call this information constraints because it constrains the compiler– it can’t just pick one, but instead must use the one you constrain it to.

Create an XDC constraints source file and call it top.xdc. You should download a copy of the ZedBoard schematics to determine the constraints. But even so, I have included the important information here.

ZedBoardPins

If you look at the schematic, you’ll see that there are eight switches and eight LEDs. Switch 0 comes into the FPGA as SW0 on the schematic and it connects to pin F22 on the FPGA. Notice also that the voltage on the IO bank where the input belongs (bank 35) is VADJ. This voltage is adjustable on the board between 2.5 and 1.8 volts. Assuming your jumper is set to 2.5V, that means this is a 2.5V input.

If you look at the upper left of the schematic you will see a signal called LD0 which is connected to LED 0. This is on a 3.3V bank connected to pin T22. Edit your top.xdc file so that it contains the following constraints. These constraints tell the compiler where we want to connect our inputs and outputs and what logic levels to use for those pins.

set_property IOSTANDARD LVCMOS33 [get_ports led]
set_property IOSTANDARD LVCMOS25 [get_ports switch]
set_property PACKAGE_PIN T22 [get_ports led]
set_property PACKAGE_PIN F22 [get_ports switch]

You should now be able to compile the design. Generate a bit file and program the board. If the blue LED comes on after programming everything should have worked. Slide Switch 0 on and off. The LED should turn on and off as well. Congratulations: you have just created a very expensive and complicated wire!

More Wires

So now let’s modify our design to use a bus of eight wires for the switches and LEDs. since we have eight of those on our board.

Change the Verilog file to

module top (
  input [7:0] switch,
  output [7:0] led
);

  assign led = switch;
endmodule

This will change the inputs and outputs to eight bit busses. You don’t need to modify the assignment statement. The width of the values just changes from one bit to eight bits. However, the constraints do change a fair amount.

Before, the name of the input pin was switch, and the output pin was called led. Now those values refer to eight pins. We need to apply the PACKAGE_PIN to individual pins. Our constraint files are actually a little program written in the TCL programming language. set_property is a function defined in TCL, and the values passed to it are arguments. get_ports is another function which searches the design database for ports. The square brackets tell TCL to call the function inside them and use the value of the result in place of the brackets. So we can still use the same IO_STANDARD commands. The [get_ports led] call will just return a list of eight ports that match the name led. The set_property command can apply the same IOSTANDARD attribute to all eight. For the PACKAGE_PIN attribute, we need to be more selective. The actual port name for the LSB of the led is led[0]. But we can’t just use this for the name since TCL will try to call a function 0 rather than using the literal characters. We quote the special characters in curly braces. So we will use [get_ports {led[0]}].

Looking at the schematic for all the pin names, we get the following constraints:

set_property IOSTANDARD LVCMOS33 [get_ports led]
set_property IOSTANDARD LVCMOS25 [get_ports switch]
set_property PACKAGE_PIN T22 [get_ports {led[0]}]
set_property PACKAGE_PIN T21 [get_ports {led[1]}]
set_property PACKAGE_PIN U22 [get_ports {led[2]}]
set_property PACKAGE_PIN U21 [get_ports {led[3]}]
set_property PACKAGE_PIN V22 [get_ports {led[4]}]
set_property PACKAGE_PIN W22 [get_ports {led[5]}]
set_property PACKAGE_PIN U19 [get_ports {led[6]}]
set_property PACKAGE_PIN U14 [get_ports {led[7]}]
set_property PACKAGE_PIN F22 [get_ports {switch[0]}]
set_property PACKAGE_PIN G22 [get_ports {switch[1]}]
set_property PACKAGE_PIN H22 [get_ports {switch[2]}]
set_property PACKAGE_PIN F21 [get_ports {switch[3]}]
set_property PACKAGE_PIN H19 [get_ports {switch[4]}]
set_property PACKAGE_PIN H18 [get_ports {switch[5]}]
set_property PACKAGE_PIN H17 [get_ports {switch[6]}]
set_property PACKAGE_PIN M15 [get_ports {switch[7]}]

Make these changes, generate a new bit file, and program the board and now all the switches should control the corresponding LED.

Adding a Clock

One final thing to do is to add a clock. Clocks are important in FPGA designs because they allow the FPGA to have states that vary over time. Make some more changes to your Verilog code. One thing we will be doing is changing the output declaration a little. Notice the reg keyword. In Verilog 2005 there are basically two types of nets: a wire and a reg. Wires are assigned using the assign statement or as outputs of module instances. Reg nets are assigned from inside of procedural blocks. What are procedural blocks, you ask? They are basically blocks of code which behave in a sequential manner. They have some event that activates them and causes them to run. Code within the block runs from start to finish in a sequential manner, just like software.

In the example above, the assign statement said that the output led was equal to the input switch. This isn’t something that is equal only once, and then the system forgets about it. switch and led are always equal. If the right hand side changes, the left hand side is updated along with it. This is called a continuous assignment, so named because it happens continuously. As such, it never makes sense to have multiple continuous assignments to a wire. You’ll only ever have one.

We can rewrite the previous code using a sequential assignment like this:

module top (
  input [7:0] switch,
  output reg [7:0] led
);

  always @(switch)
    begin
      led = switch;
    end
endmodule

This tells Verilog to run the code between the begin/end pair every time switch changes. This has the same effect as the continuous assignment. We can also clean up our code a little by removing the begin/end pair since there is only one statement. The part within @() section is called the sensitivity list. This is a list of variables separated by the keyword or. If any of the variables changes, the always block is reevaluated. We can just use the shorthand notation @(*) as well. This lets the compiler decide which variables have an effect on the outcome and use those instead. So we now have our slightly optimized code, as shown below:

module top (
  input [7:0] switch,
  output reg [7:0] led
);

  always @(*)
    led = switch;
endmodule

We can now add a clock to our design. To do that, we add another input pin called clk and we use that in the sensitivity list for the always block. However, we don’t want any change to clk to sensitize the always block, just the rising edge. So we also use the posedge keyword. Like this:

module top (
  input clk,
  input [7:0] switch,
  output reg [7:0] led
);

  always @(posedge clk)
    led = switch;
endmodule

Now all that is left is to tell the compiler which pin the clock comes in on, its logic level, and finally the fact that it is a clock and it runs at a frequency of 100MHz. So here are the additional lines that we add to the constraints file:

set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN Y9 [get_ports clk]
create_clock -period 10 [get_ports clk]

If you compile this, you should see the exact same behavior as before.

51 thoughts on “Tutorial 1: The Simplest FPGA in the World

Leave a Reply