Create RISC-V Core using Verilog HDL (1) “setting up a RISC-V cross compiler.”
About this project
Hi, I’m currently implementing a RISC-V core using Verilog HDL for a deeper understanding of the CPU. Here is the GitHub of this project. If you are interested, please give me a star.
What is a cross compiler, and why it is needed
This is my first article on this project, and today I will give you chips for the cross compiler for RISC-V. Cross compiler is a compiler that can create executable code for a platform other than the one on which the compiler is running. If you use a normal compiler on the x86 architecture, normally, your compiler gives you a code for x86. Our goal is to get a machine code for RISC-V; thus, the normal compiler does not work for this goal unless your PC runs on RISC-V. This time is exactly when the cross compiler is needed.
Installation
I went to this page https://github.com/riscv/riscv-gnu-toolchain and cloned it.
The first thing I needed to do was to set the configuration. Configuration can be set like this.
./configure --prefix="path for creating compiler" --with-arch="architecture mode" --with-abi=ilp32d
RISC-V has a lot of kinds of instruction sets. Some of it has instructions for floating points, and others do not. I needed to specify it for the compiler. For the first step, I wanted to create a simple architecture; thus, I set architecture mode as rv32ima. Because I did not use floating-point instructions, I did not specify the ABI.
After the configuration, I run the following command.
make linux
It took a very long time, but after that, I got my RISC-V compiler in the specified directory.
Configurations
Then, I wanted to compile a very simple program for the test; thus, I prepared this code.
int test_add_sub(int n){
return n + (n - 1);
}int main() {
test_add_sub(5);
for(;;) {}
return 0;
}
for(;;){} is for preventing removing from main function.
First, I needed to create “start.S” to disable the initialization. This assembly file calls the main function.
.section .text.init;
.globl _start
_start:
call main
Then, I created the “link.ld” like this. This file makes the initial place of the instruction memory zero.
OUTPUT_ARCH( "riscv" )
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
.text.init : { *(.text.init) }
.tohost : { *(.tohost) }
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
_end = .;
}
Creating binary files
After the configurations, I could finally create the binary file.
First, export the path for my compiler.
export PATH=/your-compiler-path/bin:$PATH
Second, compile it as follows.
riscv32-unknown-linux-gnu-gcc -march=rv32i -c -o start.o start.S
riscv32-unknown-linux-gnu-gcc -march=rv32i -c -o test.o test.c
riscv32-unknown-linux-gnu-ld test.o start.o -L riscv32-unknown-linux-gnu/lib/ -Tlink.ld -static -o test.elf
riscv32-unknown-linux-gnu-objcopy -O binary test.elf test.bin
Finally, I got the final code, “test.bin.”
Let’s look at the code through the following python code.
with open("test.hex", "rb") as file:
with open("out.txt", "w") as f:
data = file.readlines()
for d in data:
d = str(bin(int(d,16)))
d = d[2:]
d = "0" * (32 - len(d)) + d
d = "32\'b" + d + ","
f.write(d)
f.write("\n")
f.close()
file.close()
Seems working! I can use this output directly for the Verilog HDL.
I can also disassemble the output code using this command.
my-compiler-path/riscv32-unknown-linux-gnu/bin/objdump -d test.elfDisassembly of section .text.init:00000000 <_start>:
0: 034000ef jal ra,34 <main>Disassembly of section .text:00000004 <test_add_sub>:
4: fe010113 addi sp,sp,-32
8: 00812e23 sw s0,28(sp)
c: 02010413 addi s0,sp,32
10: fea42623 sw a0,-20(s0)
It also seems working!
Next plan
Firstly, I will implement an add function. I’m looking forward to finishing this project!