Introduction

The RISC-V is an exciting Free and Open Instruction Set
architecture originally designed by researchers at the University of California,
Berkely. Clifford Wolf has implemented (in verilog),
a size-optimized version of the RISC-V architecture called the PicoRV32. It is possible to implement a PicoRV32 processor using the
Lattice ICE40 FPGA present on the IcoBoard; all the stages involved in the
synthesis of this processor are done using the amazing IceStorm suite of tools! So we now have an open processor implemented completely using
open tools!

If this is your first time working with the IcoBoard and
the IceStorm suite of tools
which enables a 100% Free Software flow for FPGA programming, it may be good to
read this post to get some general
ideas.

Setting up the development PC/Laptop

You need to set up the FPGA bitstream synthesis, programming and related tools by
installing IceStorm tools, Arachne-PNR and Yosys following the instructions
given here.

These tools can also be set up on the Raspberry Pi (making the Pi a stand-alone
platform for FPGA development), but as some of them run slowly on the Pi (for
larger designs), it
is recommended that you install them on the development PC/laptop.

RISC-V processors can be programmed using the GNU toolchain; you need to install it
next:

This process will take some time to complete as it will be installing a few
different variants of the toolchain. The final executables will be under
/opt/riscv32i, /opt/riscv32ic, /opt/riscv32im, /opt/riscv32imc. In-between
the installation process, you will be asked to provide superuser privilege
(to write to /opt).

The next step is to clone the icotools repository.
This repository contains the source code of the PicoRV32 processor (and associated peripherals)
besides a few other things.

Configuring the PC/Laptop for connecting with the Raspberry Pi

The process of uploading the bitstream which describes the picorv32 processor, and also
the machine code which is to run on the picorv32 processor, is done using a tool called
icoprog. This is the only tool which we will run on the Raspberry Pi. A script running
on the PC/Laptop will non-interactively connect to the Raspberry Pi (using ssh) - this
script expects the Raspberry Pi to be accessible using the name raspi; so we need to
find out the IP address of our Raspberry Pi (can be done easily using nmap) and create an
entry in our /etc/hosts file; here is what I have on my machine:

192.168.0.102 raspi

The script will connect to the user pi, and expects the connection to proceed
without a password being asked. As this may require configuring ssh keys, an
easier way to do it would be to use sshpass (you may need to install it). If
the user pi has password xyz, make sure you are able to log in like this:

sshpass -pxyz ssh pi@raspi

You can then make a small change to the file icotools/icosoc/icosoc.py; change the
line:

icosoc_mk["10-top"].append("SSH_RASPI ?= ssh pi@raspi")

to

icosoc_mk["10-top"].append("SSH_RASPI ?= sshpass -pxyz ssh pi@raspi")

Configuring the Raspberry Pi

We need to install a tool called icoprog on the Raspberry Pi.
The source code of this tool
is available as part of the icotools repository.

cd icotools/icoprog
make icoprog
make install

LED blinker running on the PicoRV32 processor!

The icotools/icosoc/examples directory contains C programs which can be
executed on the PicoRV32 processor.

I have modified two of the programs and made them a bit simpler; you can
download the code from here. You should untar the file under the icotools/icosoc/examples folder. You will
get two new folders: blink and simple-timer.

First, we don’t really have a PicoRV32 processor with us - the IcoBoard contains
only a Lattice ICE40 FPGA. This FPGA has to be morphed into a processor by
wiring the Logic blocks contained within it; this is a very complex process and
the journey starts with a precise description of the processor’s functionality in
Verilog; this is the verilog file which describes the processor:
icotools/icosoc/common/picorv32.v.

Besides the processor core, we need to implement peripherals like GPIO ports, UART,
SPI etc. The Verilog descriptions of these peripherals can be seen under directories
named icotools/icosoc/mod_rs232, icotools/icosoc/mod_gpio etc.

When building an application program for the first time (say the example given
above),the build scripts will first generate a bitstream that will configure
the FPGA to act as a PicoRV32 processor (with the required peripherals). The bitstream
will be transferred to the FPGA by icoprog running on the Raspberry Pi. We now
have a working processor! Next, the application code will get compiled by
the RISC-V GCC toolchain and it will be loaded into the “processor” by icoprog
running on the Raspberry Pi; the processor will then start running the code.

Let’s look at how the above program works. The icosoc_leds function controls
the 3 built-in LED’s on the IcoBoard. You can consider the 3 FPGA pins to which
the LED’s are connected as forming a 3 bit write-only port. For example, the statement:

icosoc_leds(7);

will result in all three LED’s lighting up! It is now easy to see that what
we have here is a simple LED blinker!

You can see the code in action by typing:

make run

This will take some time to finish when you do it for the first time because the
processor and required peripherals have to be synthesized from the Verilog code and
only then will the application code get compiled and flashed.

The icotools/icosoc/examples/blink folder contains a file called icosoc.cfg; this
file is used by the build scripts to configure the processor in different ways; say your
program needs a serial port, you can then configure the processor in such a way that
it comes with a UART attached. This is something you can’t do with hard-wired processors.

Digging a bit deeper

If you read the auto-generated file icosoc.h (note: you will
have to do “make run” first), you will find the definition for
icosoc_leds: