Arty 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.

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

Requirements

For this series, we will be using the Digilent Arty, a $99 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.

For this part you need:

  1. Digilent Arty Board
  2. Micro USB cable to program and power the Arty board
  3. Xilinx Vivado installed
  4. Arty board file installed, so Vivado knows your board specification

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 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 not too counter-intuitive once you've played with it for a little while. Vivado is available for Windows and Linux but not for macOS.

The Arty Board

In this part, we're interested in the four green LEDs (LD4-7), four switches (SW0-3), and four buttons (BTN0-3). You can see these towards the bottom of the board in the above photo.

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 "Arty" (Part: xc7a35ticsg324-1L).
    • If it's not on the list, you need to quit Vivado and install the Arty board file.
  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 CLK,
    output reg [3:0] led,
    input [3: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[3:0] - the four green LEDs (labelled LD4-7 on the Arty board itself)
  • sw[3:0] - the four 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.

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 Arty 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 arty.xdc then click "Finish".

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

## Arty Board Constraints
## Based on https://github.com/Digilent/digilent-xdc/blob/master/Arty-Master.xdc

## Clock
set_property -dict {PACKAGE_PIN E3  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 A8  IOSTANDARD LVCMOS33} [get_ports {sw[0]}];
set_property -dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33} [get_ports {sw[1]}];
set_property -dict {PACKAGE_PIN C10 IOSTANDARD LVCMOS33} [get_ports {sw[2]}];
set_property -dict {PACKAGE_PIN A10 IOSTANDARD LVCMOS33} [get_ports {sw[3]}];

## LEDs
set_property -dict {PACKAGE_PIN H5  IOSTANDARD LVCMOS33} [get_ports {led[0]}]; 
set_property -dict {PACKAGE_PIN J5  IOSTANDARD LVCMOS33} [get_ports {led[1]}];
set_property -dict {PACKAGE_PIN T9  IOSTANDARD LVCMOS33} [get_ports {led[2]}];
set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports {led[3]}];

Code to Board

To run our design on the Arty 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 takes 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. Connect your Arty board to your PC using a micro USB cable.
  2. At the bottom left of Vivado select "Open Target" under "Open Hardware Manager".
  3. Choose "Auto Connect" from the menu.
  4. Select "Program Device" under "Open Hardware Manager".
  5. There should be one option "xc7a35t_0"; this is your Arty board.
  6. Browse to your bitstream file tut01/tut01.runs/impl_1/top.bit.
  7. Click "Program".

Hello LED

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

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 four 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 two 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 CLK,
    output reg [3:0] led,
    input [3:0] sw
    );

    always @ (posedge CLK)
    begin
        if(sw[0] == 0)
        begin
            led[1:0] <= 2'b00;
        end
        else
        begin
            led[1:0] <= 2'b11;
        end

        if(sw[1] == 0)
        begin
            led[3:2] <= 2'b00;
        end
        else
        begin
            led[3:2] <= 2'b11;
        end
    end
endmodule

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

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

Buttons Too

We also have four 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 arty.xdc and add the following lines to the bottom:

## Buttons
set_property -dict {PACKAGE_PIN D9  IOSTANDARD LVCMOS33} [get_ports {btn[0]}]; 
set_property -dict {PACKAGE_PIN C9  IOSTANDARD LVCMOS33} [get_ports {btn[1]}];
set_property -dict {PACKAGE_PIN B9  IOSTANDARD LVCMOS33} [get_ports {btn[2]}];
set_property -dict {PACKAGE_PIN B8  IOSTANDARD LVCMOS33} [get_ports {btn[3]}];

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

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

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

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

SW0 controls all LEDs, while BTN0 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, counting, and coloured LEDs.

©2017-18 Will Green.