Search code examples
assemblycpu-architectureelfriscvriscv32

Bare metal RISC-V CPU - how does the processor know which address to start fetching instructions from?


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:

  1. What is going on here? I thought I'd have a simple single line of hex, but there's a lot more going on.
  2. How do I instruct my processor to start reading the instructions at a certain memory address? It looks like objdump also doesn't know where the instructions will begin.

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?


Solution

  • @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:

    1. 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
      
    2. Assemble the .asm to a .o file using the following command:

      riscv32-unknown-elf-as simple.asm -o simple.o
      
    3. 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
      }
      
      
    4. 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
      
    5. 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! :)