Skip to main content Skip to local navigation

RISC-V on EECS Dept Computers: Compiling and Simulating

In this blog post I’m going to go through the steps for setting up a program for simulating basic RISC-V processors on computers in York University’s EECS department. This should be helpful for undergraduate and graduate students, as well as faculty who want to use RISC-V in their research or courses. While we’ve been using the RVS simulator in our computer architecture courses for a few years now, the simulator is a little long-in-the-tooth and there are alternatives out there that are worth exploring.

This blog post assumes that you have access to and are comfortable using a terminal and that you’re okay running the compiler and simulator from the terminal. If you’re not keen on the terminal or need more advanced compiling and simulation options, then the Segger Embedded Studio option may be more appropriate for you.

Depending on your use case, skip ahead to the section of this blog post that is appropriate for you:

  1. Assembler-only simulations for RISC-V with Kite
  2. C and Assembler simulations for RISC-V with Spike
  3. More advanced simulations with GDB and Spike.
  4. GUI development with Segger Embedded Studio

Currently, the files for RISC-V development, as they’re hosted on EECS computers, are located at:

  1. Kite executable: /eecs/local/pkg/kite/bin/
  2. Spike executable: /eecs/local/pkg/spike/bin/
  3. Compiler toolchain (64-bit bare-metal “unknown” and linux variants): /eecs/local/pkg/riscv-gnu-toolchain-08.12.2024/bin/

Scenario 1: Assembler Only simulations with Kite

Running the Kite simulator from the command line.
Running the Kite simulator from the command line.

You can run assembler-only simulations using the Kite simulator. This is similar to how we’ve used the RVS simulator in the past here at York University. Kite is available on the web — see this link.

In fact, if you want to try out the Kite simulator, try it two ways:

  1. Directly on the web simulator
  2. On the terminal using the sample code from the web simulator

Clearly, the web simulator is going to be the easiest and fastest way for you to thinker with Kite. However, if you want to work more in depth or wish to use it within other tools like Virtual Programming Lab, you’ll have to get used to the command line version. Here, I’ll talk about using that approach.

First, get three files and place them in a file folder on your EECS Department machine:

  1. program_code contains the example assembler program.
    • Use it as-is, for now. Later on you’ll modify it with whatever Assembler program you want.
  2. mem_state contains the values of the simulated RISC-V main memory
    • Use as-is. Don’t modify.
  3. reg_state contains the values of the simulated RISC-V register memory
    • Use as-is. Don’t modify.
Running the assembler commands in program_code file on the kite simulator by directly calling the simulator from the folder that contains program_code, mem_state and reg_state.
Running the assembler commands in program_code file on the kite simulator by directly calling the simulator from the folder that contains program_code, mem_state and reg_state.

You can run the kite simulator executable directly on the program_code file as long as both other files (mem_state and reg_state) are in the folder with program_code. You can run it directly, from the command line like this:

/eecs/local/pkg/kite/bin/kite program_code

But you can also invoke the simulator using a Makefile. You can do this by creating the Makefile in the same folder as program_code, reg_state and mem_state. Run it with this command:

make all

and it should give you text response that provides the end state for the registers.

Here is the Makefile:

# Define the path to the executable  (assuming tcsh; echo $SHELL)
SIMULATOR_DIR := /eecs/local/pkg/kite/bin/

# add the EXEC_PATH to the PATH
export PATH := $(SIMULATOR_DIR):$(PATH)

SIMULATOR_NAME := kite

# Record the name of the assembler file and the 
# current location of that file.
INPUT_FILE := program_code
INPUT_PATH = $(shell pwd)/$(INPUT_FILE)

all:	run

# Run the simulator on the assembler code.  mem_state and reg_state need to be there, too.
run:	
	$(SIMULATOR_NAME) $(INPUT_FILE)

clean:
	@echo "no files to clean.  Update as necessary."

You can’t program in C this way, but simple Assembler programs can be written and tried out without having to worry about compiler details.

Scenario 2: C and Assembler Simulator with Spike

Terminal showing the folder and contents.
View from the terminal of your folder with the source code from GitHub.

Go over to Ilya Sotnikov’s riscv-asm-spike repository. Download the code on to your computer and place it in a folder of your choice. Then, open a terminal and navigate to the top-most folder of that download. As shown on the right, you should see

  1. A Makefile
  2. A source folder called src

Now, use a text editor to update the Makefile as described below.

Makefile

The following makefile is an adaptation from the Ilya Sotnikov GitHub repository. It provides locations for the RISC-V compiler tools as well as the Spike simulator, as they’ve been set up on the EECS department computers.

.PHONY: all clean run

# Define the path to the executable  (assumed tcsh; echo $SHELL)
EXEC_PATH := /eecs/local/pkg/riscv-gnu-toolchain-08.12.2024/bin/

# add the EXEC_PATH to the PATH
export PATH := $(EXEC_PATH):$(PATH)

# Toolchain prefix.  First is on macOS.  Second is on EECS machines.
#TOOLCHAIN_PREFIX := riscv64-elf
TOOLCHAIN_PREFIX := riscv64-unknown-elf
AS := $(TOOLCHAIN_PREFIX)-as
CC := $(TOOLCHAIN_PREFIX)-gcc
LD := $(TOOLCHAIN_PREFIX)-ld

SRC_DIR := src
TARGET_DIR := target
# Spike simulator location.  First one is on my macOS.  Second is on EECS machines.
#SPIKE_DIR := /Users/jamessmith/riscv_spike/install/install/bin
SPIKE_DIR := /eecs/local/pkg/spike/bin/

SRC := $(wildcard $(SRC_DIR)/*.s $(SRC_DIR)/*.c)
OBJ := $(filter %.o, $(SRC:$(SRC_DIR)/%.s=$(TARGET_DIR)/%.o) \
	   $(SRC:$(SRC_DIR)/%.c=$(TARGET_DIR)/%.o))

# Name of the executable (on the simulator) file is main.elf.
TARGET_NAME := main.elf
LINKER_SCRIPT := riscv.ld

# Compiler flag.  Note: 64 bits RISCV, and 
#			C standard c2x, and 
#			no standard library used.
RV_ARCH := rv64gc
AS_FLAGS := -march=$(RV_ARCH)
LD_FLAGS := -Map=$(TARGET_DIR)/$(TARGET_NAME).map -T $(SRC_DIR)/$(LINKER_SCRIPT)
C_FLAGS := -Wall -Wextra -std=c2x -pedantic -g -Og -ffreestanding -nostdlib \
		   -mcmodel=medany -march=$(RV_ARCH) -c

# Type make all to compile the code found in the source folder.  Files go to target folder.
all: $(TARGET_DIR)/$(TARGET_NAME)

$(TARGET_DIR)/$(TARGET_NAME): $(OBJ)
	$(LD) $(LD_FLAGS) -o $@ $^

$(TARGET_DIR)/%.o: $(SRC_DIR)/%.s
	mkdir -p $(TARGET_DIR)
	$(AS) $(AS_FLAGS) -I $(SRC_DIR) -o $@ $<

$(TARGET_DIR)/%.o: $(SRC_DIR)/%.c
	mkdir -p $(TARGET_DIR)
	$(CC) $(C_FLAGS) -o $@ $<

# Type make clean to erase all the files in the target directory.
clean:
	rm -rf $(TARGET_DIR)

# Type make run to compile and then simulate using Spike.
run: all
	$(SPIKE_DIR)/spike --isa=$(RV_ARCH) $(TARGET_DIR)/$(TARGET_NAME)

# Type make disassemble to provide an assembler view of the whole program.
disassemble: all
	$(TOOLCHAIN_PREFIX)-objdump -d $(TARGET_DIR)/$(TARGET_NAME)

# Type make disassemble-main to provide an assembler view of just the main function.
disassemble-main: all
	$(TOOLCHAIN_PREFIX)-objdump -d --disassemble=main $(TARGET_DIR)/$(TARGET_NAME)

Erase the existing Makefile and replace it with the text above.

Usage

There is no need to modify any other elements in the source code. All you need to do is to type the following commands, one at a time. The compiler will take the example code found in src and create executables in a new target folder for the simulator or disassembler.

make clean

from the riscv-asm-spike-main folder type make clean and it will erase anything found in the target folder.

make all

From the riscv-asm-spike-main folder type make all and it will simply compile all the code and link all the resulting object files according to the instructions in the linker file, riscv.ld. Importantly, it will locate the code at memory address 0x8000000. It won’t run the simulator.

Running the simulator on the RISC-V ELF file.
I typed make run and it compiled and simulated the code on the Spike simulator.

make run

From the riscv-asm-spike-main folder type make run and it will compile all the files in the src folder, put the compiled ELF file (main.elf) into target folder and then runs the simulator, Spike, on it.

make disassemble

If you want to see what the disassembled code looks like after it’s been compiled, then type in make disassemble and it will execute “object dump” on the ELF file. This will provide a lot of code back to you.

make disassemble-main

Alternatively to the simple object dump disassembly, maybe you want to look at the disassembly of a single function. Here, type make disassemble-main to get the assembler code interpretation of what happens in the main function only.

Scenario 3: Going further with Spike + GDB Simulations

The Spike simulator is pretty limited. You can do better, more detailed simulations if you augment Spike with GDB. There are instructions on how to do that here. That said, I’ve tried previously to go that route, combining that example with the Ilya Sotnikov codebase and wasn’t able to develop an example that I was happy with. Another option may be to augment Spike with the pk interpreter. I’ll likely try to explore these extensions further when I have more time.

So, if you’re looking for a better simulator experience, with detailed access to registers, then you may wish to focus on programming within Segger Embedded Studio.

Segger Embedded Studio screenshot.
Segger Embedded Studio screenshot.

Scenario 4: IDE and Simulator: Segger Embedded Studio

Command-line compilers and simulators are not for everyone. Recently, one of my favourite development environments for ARM processors, Segger Embedded Studio, added support for RISC-V processors and included a simulator, emSim, in addition to standard hardware support using tools such as the inexpensive and super useful J-Link. Embedded Studio is based on Crossworks from Rowley Associates and is really solid.

Acknowledgments

I want to thank both Mr. Paul Griffith and Mr. Jason Keltz for their expertise and support in making these tools available on the EECS Department computers. Thank you!!


a pen

James Andrew Smith is a Professional Engineer and Associate Professor in the Electrical Engineering and Computer Science Department of York University’s Lassonde School, with degrees in Electrical and Mechanical Engineering from the University of Alberta and McGill University.  Previously a program director in biomedical engineering, his research background spans robotics, locomotion, human birth and engineering education. While on sabbatical in 2018-19 with his wife and kids he lived in Strasbourg, France and he taught at the INSA Strasbourg and Hochschule Karlsruhe and wrote about his personal and professional perspectives.  James is a proponent of using social media to advocate for justice, equity, diversity and inclusion as well as evidence-based applications of research in the public sphere. You can find him on Twitter. Originally from Québec City, he now lives in Toronto, Canada.