PicoRV32-imt is a CPU core based on Claire Wolf's PicoRV32 and implementing IMT with up to six threads. To simplify the implementation of interleaved multithreading in this core, some features of the source have been dropped (for now). This core just implements the RISC-V RV32I Instruction Set. The interrupt controller of the original is still there but yet to be tested. This CPU has not yet successfully run on real hardware, but it works well in simulation.
Tools (gcc, binutils, etc..) can be obtained via the RISC-V Website.
The examples bundled with PicoRV32-imt expect various RV32 toolchains to be installed in /opt/riscv32i
. See
the build instructions below for details.
PicoRV32-imt is free and open hardware licensed under the ISC license (a license that is similar in terms to the MIT license or the 2-clause BSD license).
- Files in this Repository
- Verilog Module Parameters
- PicoRV32 Native Memory Interface
- Building a pure RV32I Toolchain
You are reading it right now.
This Verilog file contains the picorv32-imt Verilog. Simply copy this file into your project.
Automates installing the RISC-V toolchain. See below.
This contains benchmarks to test performance and ressource usage (size).
A simple example SoC using PicoRV32-imt that can execute code from RAM via a memory file.
This contains helper scripts.
The following Verilog module parameters can be used to configure the PicoRV32 core.
This parameter enables support for the RDCYCLE[H]
, RDTIME[H]
, and
RDINSTRET[H]
instructions. This instructions will cause a hardware
trap (like any other unsupported instruction) if ENABLE_COUNTERS
is set to zero.
Note: Strictly speaking the RDCYCLE[H]
, RDTIME[H]
, and RDINSTRET[H]
instructions are not optional for an RV32I core. But chances are they are not
going to be missed after the application code has been debugged and profiled.
This instructions are optional for an RV32E core.
This is untested.
This parameter enables support for the RDCYCLEH
, RDTIMEH
, and RDINSTRETH
instructions. If this parameter is set to 0, and ENABLE_COUNTERS
is set to 1,
then only the RDCYCLE
, RDTIME
, and RDINSTRET
instructions are available.
This is untested.
Set this to 1 if the mem_rdata
is kept stable by the external circuit after a
transaction. In the default configuration the PicoRV32 core only expects the
mem_rdata
input to be valid in the cycle with mem_valid && mem_ready
and
latches the value internally.
This is untested, but should work without much problems.
By default shift operations are performed in two stages: first shifts in units of 4 bits and then shifts in units of 1 bit. This speeds up shift operations, but adds additional hardware. Set this parameter to 0 to disable the two-stage shift to further reduce the size of the core.
This is unsupported for now.
By default shift operations are performed by successively shifting by a
small amount (see TWO_STAGE_SHIFT
above). With this option set, a barrel
shifter is used instead.
This relaxes the longest data path a bit by adding an additional FF stage at the cost of adding an additional clock cycle delay to the conditional branch instructions.
Note: Enabling this parameter will be most effective when retiming (aka "register balancing") is enabled in the synthesis flow.
This is unsupported for now.
This adds an additional FF stage in the ALU data path, improving timing at the cost of an additional clock cycle for all instructions that use the ALU.
Note: Enabling this parameter will be most effective when retiming (aka "register balancing") is enabled in the synthesis flow.
This is unsupported for now.
Set this to 0 to disable the circuitry for catching misaligned memory accesses.
This is unsupported for now.
Set this to 0 to disable the circuitry for catching illegal instructions.
The core will still trap on EBREAK
instructions with this option
set to 0. With IRQs enabled, an EBREAK
normally triggers an IRQ 1. With
this option set to 0, an EBREAK
will trap the processor without
triggering an interrupt.
Set this to 1 to enable IRQs. (see "Custom Instructions for IRQ Handling" below for a discussion of IRQs)
This is unsupported for now.
Set this to 0 to disable support for the getq
and setq
instructions. Without
the q-registers, the irq return address will be stored in x3 (gp) and the IRQ
bitmask in x4 (tp), the global pointer and thread pointer registers according
to the RISC-V ABI. Code generated from ordinary C code will not interact with
those registers.
Support for q-registers is always disabled when ENABLE_IRQ is set to 0.
This is unsupported for now.
Set this to 0 to disable support for the timer
instruction.
Support for the timer is always disabled when ENABLE_IRQ is set to 0.
This is unsupported for now.
Produce an execution trace using the trace_valid
and trace_data
output ports.
For a demontration of this feature run make test_vcd
to create a trace file
and then run python3 showtrace.py testbench.trace firmware/firmware.elf
to decode
it.
Set this to 1 to initialize all registers to zero (using a Verilog initial
block).
This can be useful for simulation or formal verification.
This is unsupported for now.
A 1 bit in this bitmask corresponds to a permanently disabled IRQ.
This is unsupported for now.
A 1 bit in this bitmask indicates that the corresponding IRQ is "latched", i.e. when the IRQ line is high for only one cycle, the interrupt will be marked as pending and stay pending until the interrupt handler is called (aka "pulse interrupts" or "edge-triggered interrupts").
Set a bit in this bitmask to 0 to convert an interrupt line to operate as "level sensitive" interrupt.
This is unsupported for now.
The start address of the program.
The start address of the interrupt handler.
This is unsupported for now.
When this parameter has a value different from 0xffffffff, then register x2
(the
stack pointer) is initialized to this value on reset. (All other registers remain
uninitialized.) Note that the RISC-V calling convention requires the stack pointer
to be aligned on 16 bytes boundaries (4 bytes for the RV32I soft float calling
convention).
The native memory interface of PicoRV32 is a simple valid-ready interface that can run one memory transfer and one instruction transfer at a time:
output mem_valid
input mem_ready
output [31:0] mem_addr
output [31:0] mem_wdata
output [ 3:0] mem_wstrb
input [31:0] mem_rdata
output mem_instr
output instr_valid
input instr_ready
output [31:0] instr_addr
input [31:0] instr_rdata
The core initiates a memory transfer by asserting mem_valid
. The valid
signal stays high until the peer asserts mem_ready
. All core outputs
are stable over the mem_valid
period. The instruction interface
works the same way, except that the core asserts mem_instr
.
In a read transfer mem_wstrb
has the value 0 and mem_wdata
is unused.
The memory reads the address mem_addr
and makes the read value available on
mem_rdata
in the cycle mem_ready
is high.
There is no need for an external wait cycle. The memory read can be implemented
asynchronously with mem_ready
going high in the same cycle as mem_valid
, or
mem_ready
being tied to constant 1.
Again, the instruction interface works the same way.
In a write transfer mem_wstrb
is not 0 and mem_rdata
is unused. The memory
write the data at mem_wdata
to the address mem_addr
and acknowledges the
transfer by asserting mem_ready
.
The 4 bits of mem_wstrb
are write enables for the four bytes in the addressed
word. Only the 8 values 0000
, 1111
, 1100
, 0011
, 1000
, 0100
, 0010
,
and 0001
are possible, i.e. no write, write 32 bits, write upper 16 bits,
write lower 16, or write a single byte respectively.
There is no need for an external wait cycle. The memory can acknowledge the
write immediately with mem_ready
going high in the same cycle as
mem_valid
, or mem_ready
being tied to constant 1.
The PicoRV32 core also provides a "Look-Ahead Memory Interface" that provides all information about the next memory transfer one clock cycle earlier than the normal interface.
output mem_la_read
output mem_la_write
output [31:0] mem_la_addr
output [31:0] mem_la_wdata
output [ 3:0] mem_la_wstrb
In the clock cycle before mem_valid
goes high, this interface will output a
pulse on mem_la_read
or mem_la_write
to indicate the start of a read or
write transaction in the next clock cycle.
Note: The signals mem_la_read
, mem_la_write
, and mem_la_addr
are driven
by combinatorial circuits within the PicoRV32 core. It might be harder to
achieve timing closure with the look-ahead interface than with the normal
memory interface described above.
TL;DR: Run the following commands to build the complete toolchain:
make download-tools
make -j$(nproc) build-tools
The default settings in the riscv-tools build scripts will build a compiler, assembler and linker that can target any RISC-V ISA, but the libraries are built for RV32G and RV64G targets. Follow the instructions below to build a complete toolchain (including libraries) that target a pure RV32I CPU.
The following commands will build the RISC-V GNU toolchain and libraries for a
pure RV32I target, and install it in /opt/riscv32i
:
# Ubuntu packages needed:
sudo apt-get install autoconf automake autotools-dev curl libmpc-dev \
libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo \
gperf libtool patchutils bc zlib1g-dev git libexpat1-dev
sudo mkdir /opt/riscv32i
sudo chown $USER /opt/riscv32i
git clone https://github.com/riscv/riscv-gnu-toolchain riscv-gnu-toolchain-rv32i
cd riscv-gnu-toolchain-rv32i
git checkout 411d134
git submodule update --init --recursive
mkdir build; cd build
../configure --with-arch=rv32i --prefix=/opt/riscv32i
make -j$(nproc)
The commands will all be named using the prefix riscv32-unknown-elf-
, which
makes it easy to install them side-by-side with the regular riscv-tools (those
are using the name prefix riscv64-unknown-elf-
by default).
Alternatively you can simply use one of the following make targets from PicoRV32's
Makefile to build a RV32I[M][C]
toolchain. You still need to install all
prerequisites, as described above. Then run any of the following commands in the
PicoRV32 source directory:
Command | Install Directory | ISA |
---|---|---|
make -j$(nproc) build-riscv32i-tools |
/opt/riscv32i/ |
RV32I |
Or simply run make -j$(nproc) build-tools
to build and install all four tool chains.
By default calling any of those make targets will (re-)download the toolchain
sources. Run make download-tools
to download the sources to /var/cache/distfiles/
once in advance.
Note: These instructions are for git rev 411d134 (2018-02-14) of riscv-gnu-toolchain.