Check out our full list of Verilog based projects!

Verilog® HDL: Project 2

Using switches to control LEDs

Project Overview

The goal of this project is to take the simple example from Project 1 and program our FPGA with it so that we can control a single LED with a single switch. Then, we'll take the same program and expand it to have multiple switches control multiple LEDs. Brave users will be challenged at the end to do the same thing with some external components. If you have not already installed Vivado, you can find a guide to do so here. If you are hoping for a Project 0 where you learn how to navigate the Vivado interface in the first place, we highly recommend the Getting Started with Vivado Guide.

A list of the previous projects can be found here.

This project presumes you are using an FPGA development board that has external slide switches and LEDs built into the board, much like the Digilent system boards. However, the project can be easily modified to accommodate different types of external inputs (buttons instead of switches) or to use external I/O components rather than embedded components. If you have any questions after reading through the project, please feel free to post them on the Digilent Forum where an engineer will be happy to assist you.

Some Background Information

The digital circuit we are building from Project 1 is called led_sw. With this project, the FPGA is receiving an input signal, in this case from an embedded switch, that can be logic high, '1', or logic low, '0'. The input is then passed directly to an embedded LED that shows the corresponding logic by being “on” (a logic high) or “off” (a logic low). Both the embedded switch and LED are connected to their own ports on the FPGA and are specified in the constraint file (for Vivado, this is the XDC file)

Relooking at the Simple Example HDL

The code shown in the Simple Example from Project 1 that we will break down was as follows:

'timescale 1ns/1ps

module led_sw(
output led,
input sw
);
assign led = sw;

endmodule

The timescale portion 'timescale 1ns/1ps provides a base time length followed by a minimum time resolution. Generally speaking, this is only used during simulation and if delays are specifically implemented into the HDL, of which we are doing neither, but is a required part of Verilog modules nevertheless.

The module portion

module led_sw(
output led,
input sw
);

names and defines the number of inputs and outputs that we are creating on our custom “black box” (so-to-speak).

The assign portion assign led = sw; defines what our “black box” actually does. Multiple assign statements can be used as needed, as well as other ways of defining what our “black box” does (which we will get to in a later tutorial).

The endmodule is the keyword that signals the end of us defining what our module (our black box) does. Other modules could then be created with their own set of inputs and outputs, assign statements, and of course endmodule's.

But enough with the review, let's make our project!

Programming our FPGA

0) If you haven't already done so, go ahead and open up Vivado to the first screen and click on the “Create New Project” button. If you've gone through the Getting Started with Vivado Guide already, a lot of this will be very familiar to you (or near identical). You can follow along if you like, or if you feel that you can get your board programmed by yourself without the guide, feel free to do so (just to confirm you've got it right), and then jump down to the multiple switches and LEDs section.
1) On the opening screen, click on the Create New Project button.

Choose Create New Project (click to enlarge)
2) You'll then encounter some friendly instructions from Xilinx on how to create a new project. Click Next.

Some friendly instructions for creating a new project (click to enlarge)
3) Here, we'll name our project and choose where to save our project file on our computer. It is critical that no spaces are used in either the name of the project or the file path that project is saved in; Vivado notoriously has problems with spaces, so using underscores or camelCase is recommended. It is also recommended to keep the file names and file paths relatively short so that character limits are not accidentally reached (maximum of 256 characters for a Windows operating system). Click Next.

Name your project and choose a location (click to enlarge)
4) Choose create a RTL (Register Transfer Level) project and keep the box unchecked so that we can specify sources for our project. Click Next.

Create an RTL project with sources (click to enlarge)
5) Click Create file to add our Verilog module.

Add our HDL source (click to enlarge)
6) In the window that pops up, keep the file type as Verilog and name your Verilog module. A commonly used name for modules, especially ones that call and use other modules, is “top”. We'll keep the file saved within our project file (as determined by Vivado). Click OK.

Create the source file (click to enlarge)
7) After clicking next on the IP cores since we don't have any to add, click Add Files on the Add Constraints page. Digilent boards have a master XDC (Xilinx Design Constraints) file available on their Wiki pages. Since I am using the Arty, I used the Master XDC available on it's Resource Center on the right hand side under “Design Resources” and saved mine to a convenient location on my computer.

Add our constraint file (click to enlarge)
8) You'll get the opportunity to browse to your XDC file, which I happened to save in a folder that I named VivadoXDCfiles. Click OK.

Choose the constraint file (click to enlarge)
9) Make sure you have the Copy constraints files into project button checked; this ensures you later edit a copy of the master xdc, rather than the original file. Then click Next.

Copy your constraint files into your local project directory (click to enlarge)
10) Here you will want to select the FPGA that you are using to ensure that Vivado designs and programs the hardware correctly. You can either search by FPGA part number from the Parts tab, or you can select your board from the Boards tab if you have a board file available. Digilent has board files available for each of their boards on GitHub; you can follow a tutorial on how to get them installed on your computer here. Once you have chosen the appropriate FPGA part (or board), click Next.

Choose by part number (click to enlarge)Choose by board file (click to enlarge)
11) Confirm that your project has what you want to have (at least initially) and click “Finish”.

Confirm project creation (click to enlarge)
12) After a brief loading, we are presented with a top module wizard (since we said we were creating our own source file called “top”). For this project we want to have an input called “sw” and an output called “led”. Both of these are single inputs and outputs, so leave the bus box unchecked for both of them. Click Ok when you are done.

Run through the top module wizard (click to enlarge)
13) Now we are finally at the main Vivado GUI where we can edit our source and constraint files and eventually program the FPGA. Double click on our Verilog module called (in my case) “top.v”; by default it is located in the sources window in the upper middle of the Vivado GUI.

First real look at the rest of Vivado (click to enlarge)
14) Our Verilog module will appear in the upper left window known as the workspace. It comes prefilled with some Verilog code including the required timescale, a nice comments section for documentation purposes, our named module with the inputs and outputs we created in the wizard, and the endmodule statement.

Let's edit the Verilog module (click to enlarge)
15) Now we just need to add the functionality of how our circuit module we are creating on the FPGA that has a single input and a single output is going to work. This is done by declaring our inputs and outputs between the }; in the module and the keyword endmodule. For this project, we just want to have the state of the output (the LED) be the same as the state of the input (the switch), so we will use an assign statement to assign the state of the LED to be equal to the state of the switch via the following line of code:
assign led = sw;
Press Cntl+S to save your changes.

The Verilog module has been edited (click to enlarge)
16) Now we'll edit our constraints file. We can find our copy of the XDC in the sources window in the Constraintsconstrs_1 folder. Double click on it to open it in the workspace window.

Where is the Vivado constraints file (click to enlarge)
17) Our XDC will become visible in the workspace window (as mentioned in the step above). As this is a master XDC file (at least for me) there's a lot of lines of text so we want to only uncomment the pins we are using and change the name of the pin to match the names of our inputs and outputs on our top module. You can learn some more details about a XDC file here.

Let's edit our constraints (click to enlarge)
18) The two lines I will uncomment in this project will be the line corresponding to the first switch (labeled SW0 on the silk screen of the Arty) and the line corresponding to the first mono-color LED (labeled LD4 on the silk screen of the Arty). I renamed the switch (pin A8 on the FPGA) to sw and renamed the LED (pin H5 on the FPGA) to led. Press Cntr+S to save your changes.

We have edited the XDC (click to enlarge)
19) Click the Run Synthesis button on the Flow Navigator that is on the left hand side of the Vivado GUI.

Click run synthesis (click to enlarge)
20) A wizard will pop up that will allow you to choose your synthesis options. You'll want to have your project launch in the local directory and run on the local host (as opposed to only generating scripts.
As a side note, the whole combination of synthesis, implementation, and the generation of the bitstream to program the FPGA can take a bit of time (i.e. more than 10 minutes in some cases) since Vivado processes a ton of things hidden to the user and works with the entire FPGA and not just what we are utilizing, so you will probably want to maximize the number of jobs (computer cores Vivado is allowed to use) unless you plan to be doing a lot of other things simultaneously.
When you are happy with your selections, click OK.

Choose your synthesis optimizations (click to enlarge)
21) After you click ok, you'll need to wait for the synthesis to finish running, which may take a minute or two depending on how fast your computer runs.

Wait for the synthesis to finish running (click to enlarge)
21) Click Run Implementation on the next wizard that pops up and then click Ok. You can again choose how many tasks (cores your computer uses) if you didn't click on the “don't show this dialog” box last time.

Run the implementation (click to enlarge)
22) After you click ok, you'll need to wait for the implementation to finish running, which may take a minute or two depending on how fast your computer runs.

Wait for the implementation to finish running (click to enlarge)
23) Another wizard will pop up where you will want to click Generate Bitstream.
As a side note, you may not necessarily want to click the don't show this dialog again box this time because if you are designing a circuit for your FPGA to run, you may just want to check to see if Vivado was able to successfully able to make it through synthesis and implementation. If there is a warning or error, you would not want to generate a faulty bitstream.
After this screen you will be able to choose some bitstream generation options, much like for synthesis and implementation. When you are happy with your selections, click Ok.

Click generate bitstream (click to enlarge)
24) After you click ok, you'll need to wait for the bitstream generation to finish running, which may take a minute or two depending on how fast your computer runs.

Wait for the bitstream generation to finish running (click to enlarge)
25) Click Open Hardware Manager on the wizard that pops up and then click Ok.

Open the hardware manager (click to enlarge)
26) If your FPGA is not connected to your computer already via a micro-USB cable, you'll see a no hardware target is open message at the top of the screen. Go ahead and click on the Open Target text that is next to the message.

No hardware target is open if your FPGA is not connected yet (click to enlarge)
27) Make sure your FPGA is connected to your computer (and that the power LEDs on the FPGA turn on when it is connected to your computer) and then choose Auto Connect on the menu that appears after you click on Open Target.

Auto connect to your FPGA after it is connected (click to enlarge)
28) You'll then be asked to choose the bitstream file that we are to program the FPGA with; we don't have a debug core in our code, so we won't have to put anything in that field.

Let's choose a bitstream to program the FPGA with (click to enlarge)
29) The bitstream file itself is a little unintuitive to find. You can find it by opening up the folder where you told Vivado to initially save your project, go to <projectName>.runs, then impl_1, and then choose the .bit that you see. I named my overall Verilog module top.v, so the bit file is top.bit. Then click Ok.

Where is the Vivado bitstream file (click to enlarge)
30) Finally, we can click Program; the actually programming process takes under 10 seconds for any computer, so it's not too bad of a wait.

Program the FPGA (click to enlarge)

Now we're done! The way this project was setup, the first switch (labeled SW0 on the Arty) controls the status of the first LED (labeled LD4 on the Arty).

An image of the above demo in action (click to enlarge)

Multiple Switches and LEDs

But why restrict ourselves to a single input and a single output? Perhaps instead we want create a bus of inputs and outputs (like an array in a C/C++ environment) so we can try out some more complex (but still simple) combinatorial logic.

As I am using the Arty board which has four embedded switches and four mono-color LEDs (as well as four tri-color LEDs if I want to use them) I can easily use up to a 4-bit bus for my inputs and outputs. Your FPGA board may have a different number or a different type of available inputs and outputs, or perhaps you need to use some external components to get some physical inputs and outputs. If I took advantage of some external components, I could theoretically create as large of a bus as I wanted, but that is beyond the scope of this project. We won't be going through every step like we did the first time, although some key screenshots will be shown as a reference point for your convenience. Remember, If you have any questions after reading through the project, please feel free to post them on the Digilent Forum where an engineer will be happy to assist you.

This tutorial presumes that you created a new project to for the bus inputs and outputs, but you can easily modify your existing project to accept these additions.

1) The same process of a creating a Vivado project was followed; a top module was created, no IP was added, and the Master XDC file was copied into the local directory. Once we confirm our project settings, we can then create our new module with buses on the module wizard that appears. For each input or output that you want to be a bus, check the bus box and indicate the size of the bus through the MSB and LSB options.

Creating a bus through the module wizard (click to enlarge)
2) Once the corresponding Verilog module has been created, we can assign individual values within each bus to other individual values. As you might expect, you cannot assign a whole bus to a single input or output, but can perform some logical operations, such as the logical negation or ! operator.

Assign individual values inside of a bus (click to enlarge)
3) The XDC file will also need to be edited so that Vivado knows which FPGA pins we intend to use. With this Master XDC file, the names of the pins for both the switches (shown) and the LEDs (not shown in the image) are already named in such a way so that they match the names used in our Verilog module. You could change the names in both the module and the XDC file to something else, but why do more work if it is already legible?

What part of the XDC file will look like for the bus inputs and outputs (click to enlarge)
4) Here's what the Arty looks like with SW0 switched “on” and SW1 switched “off”.

Image of the above demo in action (click to enlarge)
5) It is also possible to assign a bus of outputs the value of a bus of inputs. The trick here is that you need to use the bit-wise negation operator, ~, rather than the logical negation operator, !, which operates on the entire bus rather than bit by bit. The ~ operator will also work for single bits much like the example immediately above this one.

Verilog Module assigning a bus of LEDs to the opposite state of a bus of switches (click to enlarge)
6) Here's what the Arty looks like for the above code with SW3 switched “off” and switches SW0, SW1, and SW2 switched “on”.

Image of the above demo in action (click to enlarge)

Taking it one step further

Try using some of the external I/O rather than the embedded I/O on your system board. You can even keep the Verilog module code exactly the same; the key here will be to change the XDC file so that the pins you want to use instead (such as some Pmod Host port pins) match the names used in your port declarations in your Verilog module.

Important Takeaways from Project 2

  • Creating single bit and multi-bit inputs and outputs
  • Assigning input ports to output ports for single bits, bits within bus's, and bus's to bus's
  • Confidence in going through the Vivado design flow from creating a project, editing it, and programming the system board