I am designing my own RISC-V CPU and have been able to implement a few instruction codes.
I have installed the RV32I version of the GCC compiler and so I now have the assembler riscv32-unknown-elf-as
available.
I'm trying to assemble a program with just one instruction:
# simple.asm
add x5,x6,x7
I compile this with the assembler and then run objdump with this command:
riscv32-unknown-elf-as simple.asm -o simple
riscv32-unknown-elf-objdump -D simple
This prints out the following:
new: file format elf32-littleriscv
Disassembly of section .text:
00000000 <.text>:
0: 007302b3 add t0,t1,t2
Disassembly of section .riscv.attributes:
00000000 <.riscv.attributes>:
0: 2d41 jal 0x690
2: 0000 unimp
4: 7200 flw fs0,32(a2)
6: 7369 lui t1,0xffffa
8: 01007663 bgeu zero,a6,0x14
c: 00000023 sb zero,0(zero) # 0x0
10: 7205 lui tp,0xfffe1
12: 3376 fld ft6,376(sp)
14: 6932 flw fs2,12(sp)
16: 7032 flw ft0,44(sp)
18: 5f30 lw a2,120(a4)
1a: 326d jal 0xfffff9c4
1c: 3070 fld fa2,224(s0)
1e: 615f 7032 5f30 0x5f307032615f
24: 3266 fld ft4,120(sp)
26: 3070 fld fa2,224(s0)
28: 645f 7032 0030 0x307032645f
My questions are:
Just to be clear, I'm treating my processor as bare metal at this point. I am imagining I will hardcode in the processor that the instructions start at memory address X and data is available at memory address Y and stack is available at memory address Z. Is this correct? Or is this the wrong approach?
@PeterCordes answer set me on the right path. I finally figured out how to generate a raw memory dump file that I can use.
The steps are as follows:
Modified the assembly file to have a .text
and .data
section and a _start
label. My simple.asm
file now looks as follows:
.globl _start
.text
_start:
add x5,x6,x7
.data
L1: .word 27
Assemble the .asm
to a .o
file using the following command:
riscv32-unknown-elf-as simple.asm -o simple.o
Create a linker script for the specific processor. I followed this amazing video which walks through the process on creating a linker script from scratch. For now, I just need .text
and .data
sections. So my linker script (mycpu.ld
) is as shown below:
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv")
ENTRY(_start)
MEMORY
{
DATA (rwx) : ORIGIN = 0x0, LENGTH = 0x80
INST (rx) : ORIGIN = 0x80, LENGTH = 0x80
}
SECTIONS
{
.data :
{
*(.data)
}> DATA
.text :
{
*(.text)
}> INST
}
Generate the ELF file using riscv32-unknown-elf-gcc
which automatically calls riscv32-unknown-elf-ld
:
riscv32-unknown-elf-gcc -nostdlib -T mycpu.ld -o simple.elf simple.o
Create a raw binary or hex file from the .elf
file which I will use to populate the contents of the memory.
riscv32-unknown-elf-objcopy -O binary simple.elf simple.hex
Final simple.hex
contains the following (using hexyl
):
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 1b 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │•0000000┊00000000│
│00000010│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000040│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000050│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000060│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000070│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000080│ b3 02 73 00 ┊ │וs0 ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
where b3027300
is the hex value for add x5,x6,x7
.
And that's it! Big thanks to @PeterCordes for his help! :)