Lab 1: FPGA and MCU Setup and Testing

Introduction

In this lab you will familiarize yourself with the microcontroller unit (MCU) and field-programmable gate array (FPGA) development boards we will be using this semester.

Learning Objectives

By the end of this lab you will have…

  • Assembled your development board for the class and tested out your MCU and FPGA boards.
  • Written a Verilog module to control LEDs and a 7-segment display.
  • Programmed the FPGA with Verilog code.
  • Gained confidence in building, assembling, testing, and debugging circuits.
  • Interfaced a 7-segment display to the board.

Requirements

Follow the steps in this guide to test your FPGA and MCU boards. Write some Verilog code to exercise the FPGA using the switches, LEDs, and a 7-segment display to ensure your board is operational. Simulate and synthesize your code, then upload it to the flash memory and re-test the board. Hook up a 7-segment display and demonstrate that it works.

TipHow this lab uses the tutorials

The step-by-step tool instructions for this lab live in the tutorials section of the website rather than in this page, so that you can find them again later in the semester without digging through Lab 1.

When this page links you into a tutorial, work through the linked section and then come back here — each tutorial ends the relevant section with a callout telling you to return to Lab 1. You do not need to read the tutorials end to end.

Before You Start

Before you start working on this lab, you should familiarize yourself with the documentation for both the UPduino v3.1 and Nucleo-L432KC boards. The website has links for the UPduino v3.1 User Guide and the Nucleo-L432KC User Manuals on the Resources tab. These two documents contain information that will be helpful for answering questions that you may have during the course of this lab.

You should also read the Hardware Background tutorial, which has important programming instructions and background information for the FPGA and MCU.

Finally, install the toolchain you will need for the rest of the semester:

Getting Started

Create Your Git Repository

The first step before you begin writing any code is to create a Git repository. Follow the Creating and Initializing Your Git Repo video to get started with your project. If you are new to Git, the Git Setup tutorial covers the basics first.

Assemble Your Development Board

Next, assemble your development board as described in the E155 Development Board tutorial. The majority of your time on this lab will likely be spent assembling the board, so make sure to plan ahead.

Test Your Boards

Before writing any of your own HDL, confirm that both halves of your assembled board work. Follow the Testing the Boards section of the development board tutorial. You will program the FPGA with a provided design that toggles a pin at about 1 Hz, then program the MCU1 with a provided program that reads that pin and echoes it back, which verifies both devices and the communication between them.

Hold on to the FPGA test project — you will reuse its clock generation code in your own design below.

FPGA Design

Now it’s your turn to take the wheel and create your own design and dust off your digital design chops! Your goal is to write some Verilog modules to further test the hardware on your board and operate a 7-segment display. The system should have the following inputs and outputs:

Signal Name Signal Type Description
s[3:0] input the four DIP switches (on the board, SW6)
led[2:0] output 3 LEDs (you may use the on-board LEDs)
seg[6:0] output the segments of a common-anode 7-segment display
ImportantThe clock is not a top-level port

Notice that there is no clk input in the table above. The 48 MHz clock is generated inside the FPGA by the HSOSC primitive, which you instantiate in your top-level module. The clock is therefore an internal signal, not a pin you route in from outside.

This matters when you write your testbenches: a module that takes clk as an input port (like your counter) can be given a generated clock by its testbench, but your top-level testbench has to let the real HSOSC produce the clock.

The following tables define the relationship of the LEDs to the switches and clock.

S1 S0 led[0]
0 0 OFF
0 1 ON
1 0 ON
1 1 OFF
S3 S2 led[1]
0 0 OFF
0 1 OFF
1 0 OFF
1 1 ON
led[2]
Blink at 2.4 Hz

The 7-segment display should display a single hexadecimal digit specified by s[3:0]. Be sure each digit can be distinguished from other digits (e.g., b and 8 should look different).

Remember that you will be using a common anode display. The anode (positive terminal) of all of the LEDs is tied to 3.3 V through a single (“common”) pin. Each segment’s cathode (negative terminal) is connected to a pin. Therefore, you will need 7 separate control signals. Remember that a logic 0 applied to the cathode will turn on the segment. The segments are defined as shown below. Let seg[0] be A and seg[6] be G.

Seven-segment display segment mapping
Figure 1: Seven-segment display pin map
TipClock Generation

To generate the clock for your design, use the onboard high-speed oscillator. This is the same strategy as in the FPGA testing source code you downloaded when you tested your boards. If you look at that Verilog code, you’ll notice an instantiated module called HSOSC (for High-Speed OSCillator). This module is part of the iCE40 Technology Library (see the iCE40 Technology Library document linked on the course website) and is a built-in module included with Lattice Radiant designed for controlling the internal oscillator on the iCE40 chip. You can simply copy the line from the testing program and use it in your project.

Design and Synthesis in Radiant

Launch Lattice Radiant and start the New Project Wizard from the File or startup menu. Set up a new project for the UP5K chip following the Project Setup section of the Lattice Radiant tutorial.

Choose File → New and create a SystemVerilog HDL file. Save the file as lab1_xx.sv in your project directory and check the box to add the file to the current project.

Create modules to perform the functions described above. The seven-segment display decoder should be combinational logic, and the counter that blinks led[2] should be a module of its own. Name the top-level module lab1_xx and keep it to instantiated modules plus the switch-to-LED assign logic. Put each module in its own file with a descriptive name matching the module. The seven-segment decoder in particular will be reused in future labs, so keeping it self-contained will save you work later. See the lab specifications for the full structure and style requirements.

Every module should begin with a comment section that includes your name and email address, the date of creation, and a brief summary of its purpose, so that somebody else can understand what the module does and get a hold of you if they need help with it. Comment the modules as appropriate.

Logic Simulation in ModelSim

Simulate your logic in ModelSim following the QuestaSim/ModelSim tutorial. Start by forcing the inputs by hand so you can see your design respond.

You should see the led and seg outputs displaying appropriate values. Check the outputs against your expectations. If you find any discrepancies, fix the code and resimulate.

TipFixing code without restarting ModelSim

A helpful shortcut to avoid restarting ModelSim is that you can edit the module by finding it under “work” in the library pane, right clicking, and choosing Edit. Make your fixes, then right click again and choose Recompile. Then type restart -f in the transcript window to restart simulation without having to set up the waveforms window again. When you return to Radiant, you’ll find your corrected code.

Automatic Testing with a Testbench

Forcing signals by hand gets tedious quickly, and it does not leave you with anything you can rerun. Next, test your design automatically using a stim/assert style testbench. Rather than manually forcing inputs or using pre-defined test vectors, this means using your testbench to set inputs and check for correct outputs.

Start by writing a testbench for one of your submodules. Before you write it, read Designing a Good Testbench in the simulation tutorial — it explains how much coverage each kind of module needs, which is what the specifications are graded against.

The example below tests a module named lab1_led_controller that implements the truth tables for led[0] and led[1] described above. Try writing yours first, and open the example only if you get stuck or want to check your structure against it. You almost certainly structured your code differently, so treat this as a template for the shape of a testbench rather than something to copy verbatim.

`timescale 1 ns/1 ns

module lab1_led_controller_tb();
  logic           clk;    // system clock
  logic           reset;  // active high reset
  logic   [3:0]   s;      // 4-bit input switches
  logic   [1:0]   led;    // 2 output leds

    lab1_led_controller dut (
        .clk(clk),
        .reset(reset),
        .s(s),
        .led(led)
    );

  // generate clock
  always begin
      clk = 0; #5;
      clk = 1; #5;
  end

  // apply stimuli and check outputs
  initial begin
    reset = 1;
    #22 reset = 0;

    // for each test case we setup the inputs, wait for the outputs to update,
    // and then check that the outputs match what we expect using `assert`
    // in this case, the leds use combinational logic, so we don't *need* to wait
    // a full clock cycle (#10)

    // example test 1
        s = 4'b0000;                // setup inputs
        #10;                        // wait required time
        assert (led == 2'b00)       // check outputs
            $display("PASSED! The led controller behaves as desired at time: %0t.", $time);
        else 
            $error("FAILED! The led controller behaves incorrectly at time: %0t.", $time); 
            
    // test 2
        s = 4'b0101;
        #10;
        assert (led == 2'b01)
            $display("PASSED! The led controller behaves as desired at time: %0t.", $time);
        else 
            $error("FAILED! The led controller behaves incorrectly at time: %0t.", $time); 
        
        // ... add the rest of the states you want to check here

    #100 $stop;
  end
endmodule

Once your testbench is written, run it using the Simulation Wizard.

TipWatch out for optimization

If some of your testbench signals are missing from the Wave window, QuestaSim has likely optimized them away. See Preventing Optimization in the simulation tutorial.

Write a testbench for each of your submodules. Your seven-segment decoder is combinational with only 16 input combinations, so test all of them. Your counter is sequential, so rather than simulating every count for example, verify its reset, enable, and max count behavior.

Finally, write a top-level testbench that covers only what the submodule tests could not: that the modules are wired together correctly, that the HSOSC produces a clock, and that your assign logic works. Remember that the top level generates its own clock, so the testbench should not drive one.

Pin Assignment

Next, assign pins to relate the signal names in your Verilog code to physical pin numbers on the FPGA, following the Pin Assignment section of the Lattice Radiant tutorial.

For the 7-segment display outputs you may select any I/O pins you’d like, but make sure that these pins are not being used for other purposes by checking the E155 Development Board Schematic. Write down which pins you choose — you will wire the display to them in the next section.

The pins connected to s[3:0] must be configured with pull-up resistors to avoid invalid logic levels. The exact value is not very important; a value between 10 kΩ and 100 kΩ is generally sufficient. See Configuring Pull-Up Resistors in the Lattice Radiant tutorial.

Seven Segment Display Circuit

The 7-segment display will be used throughout the class for general output of numbers. In this lab assignment, though, it will be used to output the hexadecimal number entered by the user through the DIP switches.

Each segment of the display works as an independent LED. Therefore, the same current-limiting concern with the LEDs applies to the display as to the on-board bank of LEDs. You can limit the current into each segment of the display the same way you did for the LEDs on board, adding a suitable resistor to provide roughly 5-20 mA of current. You can find resistors and other such components in the supply cabinet or in the stockroom.

The course datasheet is for the MAN6410 — a two-digit, common anode display from the Fairchild MAN6400 series (datasheet). If you have a single-digit display instead, the common anode member of the same series is the MAN6460, and the pinout section of the datasheet covers both.

Whatever part you have, confirm it is common anode before wiring anything. The series also includes common cathode models (MAN6440, MAN6980) that are wired the opposite way and will not work with the circuit described here.

All seven segments of a digit share the same anode, which should be connected to 3.3 V. Each segment has its own cathode, which can be pulled to 0 to turn the segment on.

Warning

Be sure to turn power off before wiring circuits on your board.

You only need one digit for this lab. If you have a two-digit part, pick whichever digit you like and ignore the other one — each digit has its own common anode pin, so the digit you don’t use simply stays unpowered.

Connect the common anode pin for your chosen digit to 3.3 V. Then connect that digit’s seven segment pins to the header pins you selected during pin assignment, with a suitable current-limiting resistor in series with each one. Consult the datasheet pinout to identify which physical pin is which segment, since the numbering is not in alphabetical order.

Because the segments are common anode, you drive each segment by pulling its cathode low. A logic 0 turns a segment on and a logic 1 turns it off — the opposite of what you might assume. Given this, you may need to adjust your seven-segment decoder. Do so in the simplest way possible.

Generating the FPGA Configuration Files

Now you will synthesize your HDL into a programming file to be transferred onto the FPGA, following the Synthesizing the Design section of the Lattice Radiant tutorial.

While you are there, use the Netlist Analyzer to examine the RTL schematic of your design and confirm the synthesized hardware matches your expectations, and check the Resource Usage report for a register and pin count that matches what you intended.

Program and Test Your Design

Finally, program your design onto the flash memory on the UPduino following the Programming the Device section of the Lattice Radiant tutorial.

Once programmed, re-test the board against the specification above:

  • Step s[3:0] through all sixteen values and confirm the 7-segment display shows the correct hexadecimal digit and that every digit is distinguishable.
  • Confirm led[0] and led[1] follow the truth tables above.
  • Confirm led[2] blinks at roughly 2.4 Hz.

If a specific pin misbehaves, the Troubleshooting callout in the Radiant tutorial covers the most common causes, including solder bridges and pins shared with the MCU.

AI Prototype and Reflection

Don’t forget to complete the AI Prototype and reflect on it in your lab 1 writeup.

What to Submit

See the Lab 1 specifications for the full list of deliverables and how this lab is graded.

Share Your Feedback

NoteShare Your Feedback!

If you caught any typos or have any suggestions for this page, please open an issue on the website Github repository. Click the link here for instructions on how to create an issue.

Footnotes

  1. In order for the NUCLEO board to be compatible with SEGGER, it is necessary to reflash the ST-LINK programmer with SEGGER J-Link firmware to convert it to a J-Link. This has been already taken care of on the board in your kit, but if you want to use another STM32 board with SEGGER you’ll need to follow the instructions in Appendix B of the SEGGER Embedded Studio Setup tutorial to do this yourself.↩︎