Baremetal Multicore in Spike
The purpose of this tutorial is to show you how to run a baremetal program in Spike, which is the RISC-V simulator. Baremetal means that it does not rely on a kernel like riscv-pk. This tutorial assumes you are working in a Linux environment.
Getting RISC-V Tools
Please follow the steps described on this github: https://github.com/riscv/riscv-tools
Make sure to always set the RISCV
environment variable and ammend your PATH
variable if you would like to be able to access all RISC-V tools. For me I added a lines to my ~/.bashrc
file so that everything is set up when I open a terminal window:
export RISCV="/path/to/install/riscv/toolchain"
export PATH=$RISCV/bin:$PATH
Compiling Some Code
Now to get started on coding, download this tarball. A short overview of what is in here:
hello.c
is the C file that contains our main function and makes the print statements.init.s
is some assembly code that we use to initialize our stack pointer and jump to the main function.link.ld
is the linker script that tells the linker where to put everything in memory. It puts our code at0x80000000
, which is the start of memory in Spike.Makefile
is the make recipe for how to build our project.
Once you have extracted this code you can run make
inside the hello folder. This will create a couple of files. The file a.out
is the file that we will run in a minute.
Modifying Spike
In order to have output in Spike without having to deal with the RISC-V front-end server. I decided to add a custom CSR (control and status register) which outputs a character corresponding to the value that is written to the register. So when you write to the CSR, using the csrrw
instruction, it will print the character corresponding to the value it contains (see the output_char function in hello.c
as an example).
To achieve this I made a couple of changes to the spike source code, which you can find in this diff file. It adds the definition of the CSR to encoding.h
and how to handle writes in processor.cc
.
You need to apply these changes to the source code in the riscv-tools that you built earlier. Go to the source code directory of your riscv-tools checkout. Go to the sub-folder named riscv-isa-sim and make your changes. Then go into the build directory and type make
and after that make install
.
Running Your Code
Going back to the directory where you build the bare metal hello world program. You can now execute:
spike -p2 a.out
This should print two lines. One saying "Core 0" and the other saying "Core 1". You can change the number after the -p
to denote how many cores you would like to run with.
Debugging Your Code
Now you will probably want to start modifying the C code to do what you want. So it is useful to know how to debug your code.
When running your code you can add the -d
flag to Spike, which enters a debug session. Every time you press enter it will execute an instruction and print which instruction that is. For more options on debugging you can see the README.md file on the riscv-isa-sim repository.
Besides the Spike debugger, it is also useful to see the instructions of your compiled code. You can use make dump
to get the dump of your program. This way you can see the program counter values that are produced by Spike and compare them with those in your program.
Interleaving Instructions
As you may have noticed, the print statements look very clean for a multi-threaded application. All of core 0's characters are print before core 1 start printing. This is not really what you expect if two threads are running in parallel. What I would expect is that the characters are interleaved.
The reason for this is that Spike by default runs 5000 instructions on core 0 and then runs 5000 instructions on core 1. To make it so that the instructions of the two cores are interleaved, we need to make a small adjustment to Spike's source-code. In riscv-tools/riscv-isa-sim/riscv/sim.h
look for the line that says static const size_t INTERLEAVE = 5000;
. Change 5000 to 1 and then go to the risv-tools/riscv-isa-sim/build/
directory. In the build directory, run the make
command and then the make install
command.
If you rerun our hello world application again, the letters will be interleaved as we expected earlier.