How can I compile C code to get a bare-metal skeleton of a minimal RISC-V assembly program?
Asked Answered
R

3

17

I have the following simple C code:

   void main(){
    int A = 333;
    int B=244;
    int sum;
    sum = A + B;  
}

When I compile this with

$riscv64-unknown-elf-gcc code.c -o code.o

If I want to see the assembly code I use

$riscv64-unknown-elf-objdump -d code.o 

But when I explore the assembly code I see that this generates a lot of code which I assume is for Proxy Kernel support (I am a newbie to riscv). However, I do not want that this code has support for Proxy kernel, because the idea is to implement only this simple C code within an FPGA.

I read that riscv provides three types of compilation: Bare-metal mode, newlib proxy kernel and riscv Linux. According to previous research, the kind of compilation that I should do is bare metal mode. This is because I desire a minimum assembly without support for the operating system or kernel proxy. Assembly functions as a system call are not required.

However, I have not yet been able to find as I can compile a C code for get a skeleton of a minimal riscv assembly program. How can I compile the C code above in bare metal mode or for get a skeleton of a minimal riscv assembly code?

Recycle answered 13/7, 2015 at 17:51 Comment(0)
A
19

Warning: this answer is somewhat out-of-date as of the latest RISC-V Privileged Spec v1.9, which includes the removal of the tohost Control/Status Register (CSR), which was a part of the non-standard Host-Target Interface (HTIF) which has since been removed. The current (as of 2016 Sep) riscv-tests instead perform a memory-mapped store to a tohost memory location, which in a tethered environment is monitored by the front-end server.


If you really and truly need/want to run RISC-V code bare-metal, then here are the instructions to do so. You lose a bunch of useful stuff, like printf or FP-trap software emulation, which the riscv-pk (proxy kernel) provides.

First things first - Spike boots up at 0x200. As Spike is the golden ISA simulator model, your core should also boot up at 0x200.

(cough, as of 2015 Jul 13, the "master" branch of riscv-tools (https://github.com/riscv/riscv-tools) is using an older pre-v1.7 Privileged ISA, and thus starts at 0x2000. This post will assume you are using v1.7+, which may require using the "new_privileged_isa" branch of riscv-tools).

So when you disassemble your bare-metal program, it better start at 0x200!!! If you want to run it on top of the proxy-kernel, it better start at 0x10000 (and if Linux, it’s something even larger…).

Now, if you want to run bare metal, you’re forcing yourself to write up the processor boot code. Yuck. But let’s punt on that and pretend that’s not necessary.

(You can also look into riscv-tests/env/p, for the “virtual machine” description for a physically addressed machine. You’ll find the linker script you need and some macros.h to describe some initial setup code. Or better yet, in riscv-tests/benchmarks/common.crt.S).


Anyways, armed with the above (confusing) knowledge, let’s throw that all away and start from scratch ourselves...

hello.s:
 .align 6
 .globl _start
 _start:
 # screw boot code, we're going minimalist
 # mtohost is the CSR in machine mode
 csrw mtohost, 1;
 1:
 j 1b

and link.ld:

 OUTPUT_ARCH( "riscv" )
 ENTRY( _start )
 SECTIONS
 {
 /* text: test code section */
 . = 0x200;
 .text :
 {
 *(.text)
 }
 /* data: Initialized data segment */
 .data :
 {
 *(.data)
 }
 /* End of uninitalized data segement */
 _end = .;
 }

Now to compile this…

riscv64-unknown-elf-gcc -nostdlib -nostartfiles -Tlink.ld -o hello hello.s

This compiles to (riscv64-unknown-elf-objdump -d hello):

 hello: file format elf64-littleriscv

 Disassembly of section .text:

 0000000000000200 <_start>:
 200: 7810d073 csrwi tohost,1
 204: 0000006f j 204 <_start+0x4>

And to run it:

spike hello

It’s a thing of beauty.

The link script places our code at 0x200. Spike will start at 0x200, and then write a #1 to the control/status register “tohost”, which tells Spike “stop running”. And then we spin on an address (1: j 1b) until the front-end server has gotten the message and kills us.

It may be possible to ditch the linker script if you can figure out how to tell the compiler to move <_start> to 0x200 on its own.


For other examples, you can peruse the following repositories:

The riscv-tests repository holds the RISC-V ISA tests that are very minimal (https://github.com/riscv/riscv-tests).

This Makefile has the compiler options: https://github.com/riscv/riscv-tests/blob/master/isa/Makefile

And many of the “virtual machine” description macros and linker scripts can be found in riscv-tests/env (https://github.com/riscv/riscv-test-env).

You can take a look at the “simplest” test at (riscv-tests/isa/rv64ui-p-simple.dump).

And you can check out riscv-tests/benchmarks/common for start-up and support code for running bare-metal.

Agree answered 13/7, 2015 at 21:32 Comment(4)
Hi Chris, thank you for your answer, it was very completed. In bare metal mode I lose a bunch of stuff, like printf, but what happen if I want to use aritmetic functions in C like "pow"? Arithmetic functions like pow are inside the math library. If I compile it use $riscv-unknown-elf-gcc -nostdlib -nostartfiles -T.link.ld -o code code.c -lm, I suppose that it does not work in bare metal model. I suppose that Should I be linking this library in bare metal mode? Or what happen in this case?Recycle
Given its high experience in risc-v, Do you recommend me better to work at the proxy kernel level when I use for example math libraries?Recycle
I don't have experience in linking in other library functions; I suspect you'd do better to ask on some gcc/compiler forum instead (it's not really a RISC-V question). I don't think there's any reason why it wouldn't work though.Agree
If you are going to be running your code on your own simple RISC-V processor, then I'd at least start with running bare metal. It's easier to only support M-mode and you can understand every instruction it executes.Agree
P
6

The "extra" code is put there by gcc and is the sort of stuff required for any program. The proxy kernel is designed to be the bare minimum amount of support required to run such things. Once your processor is working, I would recommend running things on top of pk rather than bare-metal.

In the meantime, if you want to look at simple assembly, I would recommend skipping the linking phase with '-c':

riscv64-unknown-elf-gcc code.c -c -o code.o
riscv64-unknown-elf-objdump -d code.o

For examples of running code without pk or linux, I would look at riscv-tests.

Pneumograph answered 13/7, 2015 at 20:15 Comment(0)
F
0

I'm surprised no one mentioned gcc -S which skips assembly and linking altogether and outputs assembly code, albeit with a bunch of boilerplate, but it may be convenient just to poke around.

Fresco answered 14/12, 2020 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.