Nexys Video FPGA 01: Hello World with Verilog & Vivado

Introduction

This Time to Explore tutorial series provides an introduction to development using FPGAs (Field Programmable Gate Arrays). On a microcontroller or PC, you write software to run on an existing CPU. With an FPGA you develop the hardware itself using logic gates. This makes FPGAs uniquely flexible and powerful: especially when dealing with complex or parallel tasks and high-speed I/O such as networking and video.

However, FPGAs are more than just a faster way to do what you already do; they're a fundamentally different tool that can solve many problems that would otherwise be impractical. Different materials have different properties: metal is not merely shiny plastic; it has its strengths and weaknesses. The same applies to FPGAs and microcontrollers: they can be used for some of the same purposes, but are fundamentally different. It's difficult to convey this in words: you need to experiment with FPGAs yourself to appreciate what they can do.

No prior experience of FPGA development is required to follow these tutorials, but basic knowledge of programming concepts is assumed. If you can write a simple program in Python, Ruby, or JavaScript, then you shouldn't have any trouble with this course.

Finally, I find working with FPGAs gives me a sense of delight so often lacking in modern software development. There's something profoundly satisfying about designing at the hardware level, be it drawing graphics on a screen, producing sound from a speaker, or even implementing your own arcade game from scratch. I hope you find these tutorials useful and have fun experimenting with FPGAs.

NB. This series was originally published for the Digilent Arty.

Feedback to @WillFlux is most welcome. Last updated October 2018.

Requirements

For this series, we will be using the Digilent Nexys Video, a $500 dev board based on a Xilinx Artix-7 FPGA. This board is widely available and supports Xilinx's latest Vivado software, which runs on Linux and Windows 10.

If you're looking for a simpler/cheaper board, this tutorial is also available for the Digilent Arty.

For this part you need:

  1. Digilent Nexys Video
  2. Micro USB cable to program the Nexys Video board
  3. 12V power adaptor (EU/US plug is provided by Digilent - UK users will need an adaptor)
  4. Xilinx Vivado installed (including Digilent board files)

Hello World with Verilog & Vivado

We're going to start with the traditional dev board hello world: using simple logic to control the green LEDs on our board. In part 2 (available soon) we'll move on to clocks, counting, pulse width modulation, and RGB LEDs.

Terminology

Before we dive into the practical business of creating our first project I should explain a couple of terms from the title:

  • Verilog is a hardware description language (HDL) that allows you to express your design at a higher level instead of directly describing logic gates. This is roughly analogous to how C lets you express your software design at a higher level than assembly language. Other HDLs include VHDL and Chisel.
  • Vivado is the FPGA development suite from Xilinx. It includes everything you need to write Verilog and program a Xilinx FPGA. There's no getting around the fact it's a monster: requiring around 20 GiB of disk space. Thankfully it has a no-cost version and is reasonably intuitive once you've played with it for a little while. Vivado is available for Windows and Linux but not for macOS.

The Nexys Video Board

In this part, we're interested in the eight green LEDs (LD0-7), eight switches (SW0-7), and five buttons (BTNx). You can see these towards the bottom of the board in the above photo. The FPGA itself is the square chip in the middle of the board with the heat sink.

Hello World Project

For our first Verilog, we're going to use a switch to turn an LED off an on. We then extend this to cover multiple LEDs, switches, and buttons.

Creating a Project

  1. Start Vivado.
  2. Select "Create Project" under "Quick Start".
  3. Project name: tut01 and make sure "Create project subdirectory" is selected.
  4. Project Type: "RTL Project" and make sure "Do not specify sources at this time" is selected.
  5. Default Part: Select "Boards" then choose "Nexys Video" (Part: xc7a200tsbg484-1).
    • If it's not on the list, you need to quit Vivado and install the Nexys Video board file (step 3 in the linked document).
  6. Review the "New Project Summary" and hit "Finish".

Hello Verilog

  1. In the top left of the Vivado window select "Add Sources" under "Project Manager".
  2. Choose "Add or create design sources" and click "Next".
  3. Select "Create File" in the middle of the dialog.
  4. Make sure File type is set to "Verilog" and name the file top.v then click "Finish".
  5. In the "Define Module" dialog hit "OK" then click "Yes" when prompted.

Replace the contents of the top module in top.v with:

module top(
    input wire CLK,
    output reg [7:0] led,
    input wire [7:0] sw
    );

    always @ (posedge CLK)
    begin
        if(sw[0] == 0)
        begin
            led[0] <= 1'b0;
        end
        else
        begin
            led[0] <= 1'b1;
        end
    end
endmodule

The first part of the module defines the inputs and outputs we use:

  • CLK - the 100 MHz clock
  • led[7:0] - the eight green LEDs (labelled LD0-7 on the Nexys board)
  • sw[7:0] - the eight switches

reg is short for register: you can think of it as defining a variable. It creates logic to store the state of something; in this case, whether an LED is on or off. wire is a direct connection between elements: for example our switch inputs are wires: we accept the state of the switch.

The second part of the module is where we write our logic. Every time the clock fires we check the state of switch sw[0]: if it's off we turn led[0] off; otherwise we turn the LED on. 1'b0 is a one bit literal with the value 0. You'll see more examples of literals later in this tutorial.

Save your file by hitting Ctrl-S or by using the save button at the top left of the editor window (yes, it still looks like a floppy disk).

TIP. You can use Ctrl+/ to (un)comment the current line in the Vivado editor window.

Constraints File

An FPGA is very flexible; you can connect many different types of inputs and outputs to the many hundreds of pins. Before synthesising our code, we need to tell Vivado how our Nexys board is wired up. Vivado uses constraints to map a reference, such led[0], to the appropriate pin and I/O standard. Constraints also provide details on the clock source, CLK, which is fundamental to our designs.

  1. In the top left of the Vivado window select "Add Sources" under "Project Manager".
  2. Choose "Add or create constraints" and click "Next".
  3. Select "Create File" in the middle of the dialog.
  4. Make sure File type is set to "XDC" and name the file nexys.xdc then click "Finish".

Select nexys.xdc in the editor window, add the following content then save the file.

## Nexys Video Board Constraints

## Clock
set_property -dict {PACKAGE_PIN R4  IOSTANDARD LVCMOS33} [get_ports {CLK}];
create_clock -add -name sys_clk_pin -period 10.00 \
    -waveform {0 5} [get_ports {CLK}];

## Switches
set_property -dict {PACKAGE_PIN E22 IOSTANDARD LVCMOS12} [get_ports {sw[0]}];
set_property -dict {PACKAGE_PIN F21 IOSTANDARD LVCMOS12} [get_ports {sw[1]}];
set_property -dict {PACKAGE_PIN G21 IOSTANDARD LVCMOS12} [get_ports {sw[2]}];
set_property -dict {PACKAGE_PIN G22 IOSTANDARD LVCMOS12} [get_ports {sw[3]}];
set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS12} [get_ports {sw[4]}];
set_property -dict {PACKAGE_PIN J16 IOSTANDARD LVCMOS12} [get_ports {sw[5]}];
set_property -dict {PACKAGE_PIN K13 IOSTANDARD LVCMOS12} [get_ports {sw[6]}];
set_property -dict {PACKAGE_PIN M17 IOSTANDARD LVCMOS12} [get_ports {sw[7]}];

## LEDs
set_property -dict {PACKAGE_PIN T14 IOSTANDARD LVCMOS25} [get_ports {led[0]}];
set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS25} [get_ports {led[1]}];
set_property -dict {PACKAGE_PIN T16 IOSTANDARD LVCMOS25} [get_ports {led[2]}];
set_property -dict {PACKAGE_PIN U16 IOSTANDARD LVCMOS25} [get_ports {led[3]}];
set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS25} [get_ports {led[4]}];
set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS25} [get_ports {led[5]}];
set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS25} [get_ports {led[6]}];
set_property -dict {PACKAGE_PIN Y13 IOSTANDARD LVCMOS25} [get_ports {led[7]}];

Note how you can use a backslash as a line continuation.

Code to Board

To run our design on the Nexys board, we need to go through four steps:

  1. Synthesis - converts the Verilog into logic gates
  2. Implementation - optimises and lays out the logic design for the target FPGA
  3. Generate Bitstream - generates the FPGA bit file from the implementation
  4. Program Device - loads the bit file onto the FPGA

Even on a fast PC, this whole process can take a few minutes, so be patient.

  1. Click the green play button at the top of the Vivado screen.
  2. Select "Run Synthesis".
    • If a "Launch Runs" dialog appears then select "Don't show this dialog again" and click "OK".
    • In the top right of your Vivado window, you'll see a message "Running synth_design".
  3. A dialog will appear prompting you to "Run Implementation".
    • Click "OK" to start implementation.
  4. A dialog will appear saying "Implementation successfully completed".
    • Select "Generate Bitstream" and click "OK".
  5. A dialog will appear saying "Bitstream Generation successfully completed".
    • Select "Open Hardware Manager" and click "OK".

If you accidentally close any step you can find commands to run the different stages on the left-hand side of the Vivado window.

Hardware Manager

  1. Make sure jumper JP4 is in the middle position for JTAG programming (see photo, below).
  2. Connect your Nexys board PROG port to your PC using a micro USB cable.
  3. At the bottom left of Vivado select "Open Target" under "Open Hardware Manager".
  4. Choose "Auto Connect" from the menu.
  5. Select "Program Device" under "Open Hardware Manager".
  6. There should be one option "xc7a200t_0"; this is your Nexys board.
  7. Browse to your bitstream file tut01/tut01.runs/impl_1/top.bit.
  8. Click "Program".

Hello LED

Slide the switch SW0 on and off on your board to control green LED LD0.

This functionality isn't anything you couldn't trivially achieve with a microcontroller. However, it's worth pausing to think about what you've done here. You've used programmable logic to create a circuit board within the FPGA itself: there is no CPU running your code. It's as if you'd wired up the circuit with discrete hardware components.

To see a visual representation of your design you can view the Elaborated Design. You can find this under "RTL Analysis" on the left-hand side of the Vivado window.

You can see the clock and switch inputs going to the LEDs via eight registers, which hold the value for each LED.

More Switches, More LEDs

Now you have this working on one switch, and one LED you can easily apply the same approach to the others. The following uses two switches, each of which controls four LEDs.

NB. If you're still in Hardware Manager, you can get back to your Verilog source code by selecting "Project Manager" at the top left of the Vivado window.

Replace the existing top module in top.v with the following:

module top(
    input wire CLK,
    output reg [7:0] led,
    input wire [7:0] sw
    );

    always @ (posedge CLK)
    begin
        if(sw[0] == 0)
        begin
            led[3:0] <= 4'b0000;
        end
        else
        begin
            led[3:0] <= 4'b1111;
        end

        if(sw[1] == 0)
        begin
            led[7:4] <= 4'b0000;
        end
        else
        begin
            led[7:4] <= 4'b1111;
        end
    end
endmodule

Rerun synthesis, implementation, bitstream generation, and program the board as before. You should be able to control four LEDs with each of the first two switches.

4'b defines a 4-bit binary value: we have four LEDs each expecting one bit, so a four-bit value is appropriate here. You can also see the notation for selecting a range of the LED outputs: led[3:0] includes led[0], led[1], led[2] and led[3].

Buttons Too

We also have five push buttons on the board; we can easily add these to our design.

To include the push buttons we need to add them to our constraints file.

Open up nexys.xdc and add the following lines to the bottom:

## Buttons
set_property -dict {PACKAGE_PIN B22 IOSTANDARD LVCMOS12} [get_ports {btnc}];
set_property -dict {PACKAGE_PIN D22 IOSTANDARD LVCMOS12} [get_ports {btnd}];
set_property -dict {PACKAGE_PIN C22 IOSTANDARD LVCMOS12} [get_ports {btnl}];
set_property -dict {PACKAGE_PIN D14 IOSTANDARD LVCMOS12} [get_ports {btnr}];
set_property -dict {PACKAGE_PIN F15 IOSTANDARD LVCMOS12} [get_ports {btnu}];

Next, replace your top module with the following. Note the addition of the button inputs btnx, which correspond to the new entries in the constraints file:

module top(
    input wire CLK,
    output reg [7:0] led,
    input wire [7:0] sw,
    input wire btnc, btnd, btnl, btnr, btnu
    );

    always @ (posedge CLK)
    begin
        if(sw[0] == 0)
        begin
            led[7:0] <= 8'b00000000;
        end
        else
        begin
            if (btnc == 0)
            begin
                led[7:0] <= 8'b10101010;
            end
            else
            begin
                led[7:0] <= 8'b01010101;
            end
        end
    end
endmodule

Rerun synthesis, implementation, bitstream generation, and program the board as before.

SW0 controls all LEDs, while BTNC selects two different patterns of lights.

Try your own combinations of switches, buttons, and LEDs. Make sure your values are the same width as your inputs and outputs, for example, three bits for three LEDs: led[3:1] <= 3'b101;

In the next part of this tutorial, we'll look at clocks, push buttons, and counting. Part 2 is already available for the Arty board and will be added for the Nexys Video shortly. Follow @WillFlux for updates.

Find more on FPGAs and Verilog in the Time to Explore FPGA Index.

©2017-18 Will Green.