In Spring 2022, various mentorship projects were offered by RISC-V International on the LFX platform of the Linux Foundation. I got selected for one of the projects, which is titled Compressed Instructions for SERV. This project aims to add support for RISC-V Compressed Extension onto the base ISA of the SERV CPU. In this mentorship program, I get to work with my mentor Olof Kindgren, who is the father of SERV and also the director and co-founder of the FOSSI Foundation. This mentorship program went from March 2022 to May 2022.
RISC-V is the open-source, royalty-free Instruction Set Architecture (ISA) which was developed in 2010 as a summer project at the University of California, Berkeley. The ISA has the specification for 32-bit and 64-bit instructions. It has a modular design that comprises of base ISA along with the optional extension. The base ISA comprises Integer (I) instructions. Some common standard extensions include C for ‘Compressed instructions’, M for ‘Integer Multiplication and Division’, Zicsr for Control and Status Register (CSR), and Zifence. There are two categories of RISC-V ISA: privilege and user-space which is now called un-privilege ISA.
SERV is an award-winning, world’s smallest, open-source RISC-V CPU which is created by my mentor Olof Kindgren back in 2018. It is a bit-serial CPU with base ISA support i.e. RV32I, which is verified by RISC-V Formal compliance tests. SERV also has enough privilege level support to run Zephyr on it, which is Real-Time Operating System (RTOS). At the start of the mentorship program, SERV has the support of some RISC-V extensions which are mentioned before, except the C-extension. The support of all the existing extensions is parameterized and is managed by FuseSoC. SERV along with some peripherals such as memory, GPIO, and UART forms a complete SoC which is called SERVANT. The SERV along with SERVANT is capable of running Zephyr OS.
FuseSoC is an award-winning open-source package manager and a set of build tools for HDL (Hardware Description Language) code. Its main purpose is to increase the reuse of IP (Intellectual Property) cores and be an aid for creating, building, and simulating SoC solutions.
We can run the SERV for various simulations, FPGA, or ASIC targets with the help of a command line using FuseSoC. Various parameters of SERV and the SERVANT are exposed to FuseSoC which can be altered on the command line while compiling it for different targets. We will add the compressed parameter to turn on the support for compressed extension into SERV as well as SERVANT.
Why Compressed extension…
For most of the embedded processors, we are limited by the size of programmable memory. Starting from the top, we have a high-level program — say written in C language — that is required to run on some CPU. It is then
converted from high-level to low-level machine code using a software compiler tool. In our case, the hardware target is SERV core. The software will compile the high-level code into 32-bit binary instructions which will be placed into the program/instruction memory of the CPU for processing. And here we are limited by the size of memory. So, the question arises: Can we do better in running larger programs for the limited size of instruction memory?
We can utilize the instruction memory efficiently by adding the support for RISC-V Compressed Extension onto the existing base ISA. Frequently used instructions can be compiled into 16-bit machine code instead of 32-bit and have all the information encoded inside that 16-bits. In this way, we can reduce the size of instruction by half for most of the commonly used instructions. On the other side, we have to put extra hardware to decode the 16-bit compressed instruction into its 32-bit equivalent integer instruction. Our CPU core will only process the 32-bit integer instruction regardless of what type of instruction (compressed or uncompressed) is fetched from the memory.
Chapter 16 of RISC-V Unprivileged spec explains more about Compressed instructions and describes its 16-bit encodings.
Compressed extension for SERV
Since C-extension allows free intermixing of compressed (16-bit) instructions with that of uncompressed (32-bit) instructions inside the instruction memory. So, with the addition of the Compressed extension, instructions can raise instruction-address-misaligned exceptions.
As the instruction memory is (32-bit) word-aligned, which means that the program counter will increment by 4, by default (for non-branching or non-jumping instructions). But, whenever the compressed(16-bit) instruction is encountered, the program counter would increment by 2, which makes the address misaligned. So, our first problem is how to handle this misalignment in SERV so that we fetch the right instruction from the memory and feed it to the SERV.
Let’s take a step back and look at the signals on the wishbone bus that involve fetching the instructions from the memory and feeding them to the SERV. And there are four of them as shown in the following figure.
SERV generates the address to the instruction and the strobe signal. And in response, memory gives the instruction and the acknowledge signal is also generated which tells about the validity of the instruction. Now, this setup works fine when there is no misaligned access to the memory or the misaligned access of memory is illegal. With the support of the compressed extension, we inherently have the support of misaligned access to the memory.
In order to handle the misalignment of the instruction, we introduce an extra module, called serv_aligner, inside the SERV.
serv_aligner checks the address of the instruction. If it is aligned, it will just pass the signals from SERV to Memory and vice versa. serv_aligner comes into action whenever the misaligned address is encountered. For the misaligned memory access, serv_aligner first fetches the aligned instruction at address pc-2 and stores the upper half-word [31:16] of this instruction in a register. Then it makes another bus transaction and fetches the aligned instruction at pc+2 and concatenates its lower half-word [15:0] with that of the previously-stored half-word. And the concatenated instruction is passed to the SERV core by enabling the acknowledge signal. Note that, pc is the program counter or address of the current misaligned instruction. The instruction cycles for two cases of serv_aligner are illustrated in the following figure.
Hardware design of SERV aligner
Here, ctrl_misal and en_ack are implemented using a state machine according to the waveform specification described above.
In the current implementation of the serv_aligner, the output will always be 32-bit instruction and if there is any compressed instruction inside the memory then it always has to be in the lower half-word of that 32-bit output instruction.
Now, the next important thing is to add a compressed decoder module after the aligner module that will decode the compressed instruction — if it is encountered — into its 32-bit equivalent instruction. If the compressed instruction is not encountered it will simply pass the incoming instruction from the serv_aligner to the SERV core. There is one additional signal o_iscomp coming out of the compressed decoder to tell if the compressed instruction is encountered (1) or not (0) so that it can be used to increment the program counter either by 2 or 4 respectively.
The design of the compressed decoder is totally based on instruction encodings described in the RISC-V unprivileged spec. The compressed decoder is available in System Verilog for the ibex RISC-V processor from lowRISC which is open-sourced and free under Apache 2.0 license. It is translated from System Verilog to Verilog and adapted to SERV by removing extra hardware and assertions.
The following figure illustrates the final design that is implemented to support the compressed extension inside SERV.
The compressed extension is parameterized and SERV can be run with compressed instructions if the COMPRESSED parameter is set on the command line using FuseSoC. When the COMPRESSED parameter is set then CPU will include the serv_compdec otherwise it will bypass this module at compile time. The parameters were added to SERV and SERVANT core description files of FuseSoC.
Compressed SERV in action
First of all, install the FuseSoC using
pip install fusesoc
Now create a directory and refer to it as $WORKSPACE. All the FuseSoC commands will be run from $WORKSPACE. Also, add SERV as a library in the workspace.
fusesoc library add serv https://github.com/olofk/serv
The SERV repo will now be available in $WORKSPACE/fusesoc_libraries/serv. It can be referred to as $SERV. Now, install Verilator and run:
fusesoc run --target=lint serv
If everything works fine, then the output should look as follows
INFO: Preparing ::serv:1.1.0 INFO: Setting up projectINFO: Building simulation model verilator -f serv_1.1.0.vc -Wall INFO: Running
We can enable the support of compressed instructions while compiling from assembly programs to machine code using the RISC-V GNU compiler toolchain by setting the march flag as
Now you can simulate the user program (hex machine code) on SERVANT with the compressed extension of SERV enabled using:
fusesoc run --target=verilator_tb servant --compressed=1 --uart_baudrate=57600 --firmware=/path/to/hex --memsize=16384
It is also possible to just synthesize the SERV for different targets to check the resource usage. For example, we can synthesize the SERV for iCE40 FPGA — make sure you have relevant tools installed— without compressed extension enabled using:
fusesoc run --tool=icestorm serv --pnr=none
With the Compressed extension enabled, we can synthesize SERV using;
fusesoc run --tool=icestorm serv --pnr=none --COMPRESSED=1
Testing and Verification
SERV with the support of the compressed extension is tested on the FPGA board and verified for the compressed instructions using RISC-V formal. I had access to the NEXY-2 FPGA board from the Spartan-3E series of Xilinx and support for this board is also added in FuseSoC: Blinky to Believe project. See PR 86 to learn how to add support for the FPGA board in FuseSoC. Also, the support of the NEXYS-2 FPGA board is also added for the SERVANT (See PR 80). Now, SERVANT can build for this target board using
fusesoc run --target=nexys_2_500 servant --compressed=1 --memfile=/path/to/hex
Zephyr hello world program is tested on Nexys-2 FPGA board. The Nexys-2 does not have a built-in UART. So, a USB to TTL was used to display the hello world message on the PuTTY terminal using pmod header pin of FPGA as a UART transmitter.
The Zephyr terminal shows the hello world message on the serial monitor as follows
The compressed instructions are verified with the help of the RISC-V formal compliance test. First, we need to build the Verilator model using FuseSoC with the compressed extension enabled using
fusesoc run --target=verilator_tb --build servant --memsize=8388608 --compressed=1
Clone the RISC-V formal test suites called arch-tests using:
git clone --branch 2.7.4 https://github.com/riscv-non-isa/riscv-arch-test.git
Now, run the tests by setting the flag
cd riscv-arch-test && make TARGETDIR=$SERV/riscv-target RISCV_TARGET=serv RISCV_DEVICE=C TARGET_SIM=$WORKSPACE/build/servant_1.1.0/verilator_tb-verilator/Vservant_sim
The results of compliance tests for the Compressed instructions are as follows.
At the time of implementing the support of Compressed instructions, the arch tests were updated to version 2.7.4. Some more privilege tests were added in the arch-test suit. So, the SERV core was also updated to support and pass those newly added privilege tests. Privilege tests can be verified by setting
To conclude, the following work has been done as part of the Spring ’22 LFX Mentorship program for RISC-V International’s project.
- Added support of RISC-V Compressed Extention to SERV. See here.
- Updated SERV to make it pass the privilege compliance tests.
- Added support of new target FPGA board — Nexys 2 — to fuseSoC blinky to believe project. See here.
- Added support of Nexys 2 FPGA board to the SERVANT. See here.
- Future work: Update the documentation of SERV.
I’m thankful to RISC-V International for providing me with this opportunity, my parents for all the prayers, my faculty members for investing in me and my friends for always believing in me. The best part of this program is to get to work under the mentorship of Olof Kindgren, who is by all means the most valid source of inspiration for me. I really enjoyed the whole learning experience and working with him broadens my horizan. He is a mentor in its true sense. Thank you Sir for all the support, guidance and being all ears for my silly questions.
Photo by Matt Jones on Unsplash