Maker.io main logo

Introduction to FPGA Part 11 - RISC-V Softcore Processor

2022-01-24 | By ShawnHymel

License: Attribution

A field-programmable gate array (FPGA) is a reconfigurable integrated circuit (IC) that lets you implement a wide range of custom digital circuits. Throughout the series, we will examine how an FPGA works as well as demonstrate the basic building blocks of implementing digital circuits using the Verilog hardware description language (HDL).

RISC-V is an open-source instruction set architecture (ISA) that allows anyone to implement a central processing unit (CPU) or system-on-a-chip (SOC) design without paying a licensing fee. As a result, it is popular with FPGA enthusiasts as the starting point for a softcore processor implementation.

In the previous tutorial, we saw how to implement a first-in, first-out (FIFO) system to mitigate the effects of metastability. This time, we will load a pre-existing RISC-V implementation onto our FPGA and modify the design to allow for button inputs.

Video

If you have not done so, please watch the following video, which explains the concepts required to complete the challenge. It also demonstrates a working version of the challenge:

 

Required Hardware

For this challenge, you will need the following hardware:

Hardware Connections

The PMOD connector at the end of the iCEstick has the following pinout:

iCEstick PMOD pinout

A full pinout of the iCEstick can be found here.

Connect 4 pushbuttons to the iCEstick as follows. Note that you do not need pull-up resistors on the buttons. We will use the internal pull-up resistors available in the FPGA.

iCEstick connections to buttons Fritzing diagram

Resources

The following datasheets and guides might be helpful as you tackle the challenges:

Build FemtoRV

Find your board on this README page (note that the FemtoRV has limited support at this time, and I’ll be showing how to use FemtoRV with the iCEstick). Follow the directions to install the required libraries and drivers. Note that you will need Linux (I recommend Raspberry Pi OS or Ubuntu) to build this project.

When you are done installing all of the dependencies and libraries, you’ll want to make some slight configuration changes, as we are not using IRDA, SSD13151 (OLED driver), or MAX7219 (LED matrix driver). Start in the FemtoRV directory:

Copy Code
cd ~/Projects/fpga/learn-fpga/FemtoRV
nano RTL/CONFIGS/icestick_config.v

Comment out the following lines (leave everything else alone):

Copy Code
`define NRV_IO_LEDS
//`define NRV_IO_IRDA
`define NRV_IO_UARD
//`define NRV_IO_SSD13151
//`define NRV_IO_MAX7219
`define NRV_MAPPED_SPI_FLASH

Save and exit. From the FemtoRV directory, call the following:

Copy Code
make ICESTICK

This will take some time. The first time you run it, it will download and install the RISC-V build system (so you can compile C/C code).

Challenge

Your challenge is to make buttons work on the FemtoRV. By default, buttons aren’t defined for the iCEstick, so you will need to make some changes to the FemtoRV Verilog code. Hint: look at the port list in femtosoc.v to get an idea of what you need to change. Also, the `-pullup yes` parameter does not work with the build tool used, so you’ll have to use an external pullup or define the internal pullup with the SB_IO directive (you can read about it in this Lattice doc).

Write a simple C program to test your button functionality. For example, whenever a button is held, make the LEDs blink or count up.

Solution

Spoilers below! I highly encourage you to try the challenge on your own before comparing your answer to mine. Note that my solution may not be the only way to solve the challenge.

Navigate to the FemtoRV directory and modify the PCF file:

Copy Code
cd ~/Projects/fpga/learn-fpga/FemtoRV
nano BOARDS/icestick.pcf

There, comment out the OLED and LED matrix pins. Add new button pin definitions.

Copy Code
set_io pclk 21

#set_io oled_DIN 91
#set_io oled_CLK 90
#set_io oled_CS 88
#set_io oled_DC 87
#set_io oled_RST 78

set_io D1 99
set_io D2 98
set_io D3 97
set_io D4 96
set_io D5 95

set_io TXD 8
set_io RXD 9

#set_io ledmtx_DIN 81
#set_io ledmtx_CS 80
#set_io ledmtx_CLK 79

set_io spi_cs_n 71
set_io spi_miso 68
set_io spi_mosi 67
set_io spi_clk 70

set_io RESET 47

set_io irda_TXD 105
set_io irda_RXD 106
set_io irda_SD 107

set_io pmod[0] 78
set_io pmod[1] 79
set_io pmod[2] 80
set_io pmod[3] 81

Save and exit. Open the iCEstick configuration file:

Copy Code
nano RTL/CONFIGS/icestick_config.v

Define NRV_IO_BUTTONS (this tells the SOC module to instantiate the buttons module).

Copy Code
/************************* Devices **********************************************************************************/

`define NRV_IO_BUTTONS // Mapped IO to PMOD connector (78, 79, 80, 81)
`define NRV_IO_LEDS // Mapped IO, LEDs D1,D2,D3,D4 (D5 is used to display errors)
//`define NRV_IO_IRDA // In IO_LEDS, support for the IRDA on the IceStick (WIP)
`define NRV_IO_UART // Mapped IO, virtual UART (USB)
//`define NRV_IO_SSD1351 // Mapped IO, 128x128x64K OLED screen
//`define NRV_IO_MAX7219 // Mapped IO, 8x8 led matrix
`define NRV_MAPPED_SPI_FLASH // SPI flash mapped in address space. Use with MINIRV32 to run code from SPI flash.

Save and exit. Open the femtosoc.v file:

Copy Code
nano RTL/femtosoc.v

Define the pmod pins as inputs in the port list for the iCEstick:

Copy Code


`ifdef NRV_IO_BUTTONS
`ifdef ICE_FEATHER
input [3:0] buttons,
`elsif ICE_STICK
input [3:0] pmod,
`else
input [5:0] buttons,
`endif
`endif

In the “Buttons” section, use the SB_IO module to enable the pull-up resistors on the pmod pins. Because we defined NRV_IO_BUTTONS in the config file, the Buttons module will be instantiated, which will give us access to the buttons bus.

Copy Code
/********************* Buttons  *************************************/
/*
* Directly wired to the buttons.
*/
`ifdef NRV_IO_BUTTONS
`ifdef ICE_STICK
wire [3:0] buttons;
SB_IO #(
.PIN_TYPE(6’b000001), // 0000: no output, 01: simple input
.PULLUP(1’b1) // Enable pull-up resistor
) sb_buttons [3:0] ( // Multiple modules
.PACKAGE_PIN({pmod[3], pmod[2], pmod[1], pmod[0]}),
.D_IN_0({buttons[3], buttons[2], buttons[1], buttons[0]})
);
`endif
wire [31:0] buttons_rdata;
Buttons buttons_driver(
.sel(io_word_address[IO_BUTTONS_bit]),
.rdata(buttons_rdata),
.BUTTONS(buttons)
);
`endif

Save and exit. Build and upload the SOC to board. Note that we use “ICESTICK” as our make target:

Copy Code
make ICESTICK

When that’s done, write a quick C program to test the buttons. Create a separate directory to keep your software and create main.c:

Copy Code
cd ~/Projects/fpga
mkdir -p femtorv32/button_test
cd femtorv32/button_test
nano main.c

Here’s one possible program. Note that we are using the IO_IN and IO_BUTTONS macros as defined in femtorv32.h. These read from the memory address associated with the buttons. This is not real memory (e.g. RAM)–it simply tells the SOC to read from the register in the Buttons module.

Copy Code
#include <femtorv32.h>

int main() {
int count = 0;
while (1) {
if ((IO_IN(IO_BUTTONS) & 1) == 0) {
count = (count 1) % 16;
IO_OUT(IO_LEDS, count);
delay(250);
}
}
return 0;
}

Levy created a Makefile template for us to use that handles importing header files, linking to the required libraries, and calling the RISC-V compiler. All we need to do is include it in a local Makefile:

Copy Code
nano Makefile

Add just the following line to that file:

Copy Code
include ../../learn-fpga/FemtoRV/FIRMWARE/makefile.inc

Save and exit. Use make to build the software. Note that we use “main.prog” as the target: “main” is the name of our main C file and “.prog” tells make to upload the compiled program to our board.

Copy Code
make main.prog

When that’s done, you should be able to hold the first button to see your LEDs counting!

Using buttons with RISCV FemtoRV implementation

Recommended Reading

The following content might be helpful if you would like to dig deeper:

Introduction to FPGA Part 1 - What is an FPGA?

Introduction to FPGA Part 2 - Toolchain Setup

Introduction to FPGA Part 3 - Getting Started with Verilog

Introduction to FPGA Part 4 - Clocks and Procedural Assignments

Introduction to FPGA Part 5 - Finite State Machine (FSM)

Introduction to FPGA Part 6 - Verilog Modules and Parameters

Introduction to FPGA Part 7 - Verilog Testbenches and Simulation

Introduction to FPGA Part 8 - Memory and Block RAM

Introduction to FPGA Part 9 - Phase-Locked Loop (PLL) and Glitches

Introduction to FPGA Part 10 - Metastability and FIFO

Introduction to FPGA Part 12 - RISC-V Custom Peripheral

制造商零件编号 ICE40HX1K-STICK-EVN
BOARD EVAL FPGA ICESTICK
Lattice Semiconductor Corporation
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum