VexRiscv introduction

VexRiscv is an open-source RISC-V processor written in SpinalHDL. It was awarded first place in the RISC-V Foundation’s SoftCPU contest in 2018 for achieving the highest performance on both Lattice and Microsemi FPGAs. It can be scaled to fit a wide range of use cases, from highly constrained bare-metal embedded applications to multi-core Linux with the same code base.

VexRiscv takes a uniquely software-inspired approach to hardware description. Virtually every component is a plugin object connected to one or more stages in the pipeline. Plugins can insert data into the pipeline at one stage, and VexRiscv will automatically propagate those signals through down the pipeline to be accessed by later stages. Plugins can also provide services, which are essentially APIs for other plugins to interact with. The result is a highly configurable CPU where everything down to the register file is interchangeable.

Setup

We recommend following the installation instructions from the VexRiscv README found here. The rest of the README requires an understanding of how the CPU is structured on a high level, which this page aims to provide.

SpinalHDL

SpinalHDL is a Scala-based hardware description language. Traditional HDLs offer few high-level abstractions, which leads to long and repetitive descriptions of wiring interconnects. Developing complex hardware in those languages is very time consuming, and maintenence can be difficult. SpinalHDL allows developers to leverage high-level abstractions, as well as object-oriented and functional programming patterns, to create concise self-documenting hardware.

A frequently asked question regarding SpinalHDL is how does it compare to Chisel? The language was inspired by, though not quite “forked” from, Chisel 2. The Chisel ecosystem exists to support ASIC design and manufacturing. At the time of this writing, it’s under active development with frequent API changes. SpinalHDL targets FPGAs, and is essentially “frozen” at this point. It is still actively maintained (i.e., bugs are fixed quickly and new features are added on occasion), but the API is fixed for the foreseeable future.

Supplementary materials

An overview of the language with illustrations is available here. A great starting point for learning is the workshop, and the docs are thorough and up-to-date.

Suggestions

CPU pipeline

VexRiscv has a canonincal 5-stage RISC pipeline. In piplelined CPUs, the control signals are propagated through the stages through dedicated register files. For example, if the program counter (PC) signal at the fetch stage is 0xabcd on some cycle, then the the PC will be 0xabcd when accessed by the writeback stage four cycles later (assuming no jumps or stalls occur).

VexRiscv pipeline

(Images taken from this SpinalHDL presentation.)

VexRiscv provides a convenient API for adding new signals of any data type to the CPU pipeline. This is, arguably, its greatest selling point. For example, the global declaration of PC is one line of code in the top-level CPU definition:

object PC extends Stageable(UInt(32 bits))

In the above illustration, the PC is given a value in the fetch stage by one plugin with fetch.insert(PC) := X. Another plugin needs to check the PC in the writeback stage, which is done with Y := writeback.input(PC). VexRiscv maintains internal consistency on its own; developers typically do not need to worry about how signals are propagated.

Plugins

The top-level CPU definition only declares which stages are present and some default Stageable objects. The remaining functionality consists almost entirely of Plugins (see below). Plugins can connect to one or more CPU stages. They interact with each other by reading and inserting Stageable objects, providing a service API or by influencing the CPU arbitration. For example, a plugin can halt the pipeline at any stage and flush any instruction.

VexRiscv plugins

VexRiscv includes two simple examples of custom plugins as a reference, available here. These are a good starting point for new developers. More advanced developers can also implement their own instructions. An example of that is provided here.

Side effects

Plugins that interact with memory or modify the CPU’s arbitration (such as PMP) must take into account side effects. This is relevant to more advanced developers. For example, a jump may originate from the fetch, decode or writeback stages, but CSR writes occur in the execute stage. This can lead to a hazard where the CSR write modifies the machine state, even though the instruction is going to be flushed. To avoid this, the CsrPlugin halts the execute stage for two cycles before performing to allow memory and writeback to finish.

VexRiscv side effects

Repository structure

The Scala source files for the CPU itself are found under src/main/scala/vexriscv. Within this directory, the demo folder contains several files prefixed with Gen*. These contain concrete CPU configurations and can be invoked to generate RTL. From the root directory of the repository, for example, run:

sbt "runMain vexriscv.demo.GenFull"

This will generate a VexRiscv.v file containing the entire CPU. Note that these configurations are not used in LiteX; those require an additional plugin to interface with the SoC’s Wishbone bridge. More information on LiteX integration is available here.