问题 如何编译C代码以获得最小RISC-V汇编程序的裸机框架?


我有以下简单的C代码:

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

当我用它编译时

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

如果我想看到我使用的汇编代码

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

但是,当我探索汇编代码时,我发现这会产生很多代码,我假设代码内核支持(我是riscv的新手)。但是,我不希望这段代码支持Proxy内核,因为它的想法是在FPGA中只实现这个简单的C代码。

我读到riscv提供了三种类型的编译:裸机模式,newlib代理内核和riscv Linux。根据以前的研究,我应该做的编译类型是裸机模式。这是因为我希望在不支持操作系统或内核代理的情况下进行最小程序集。不需要组件作为系统调用。

但是,我还没有找到,因为我可以编译一个C代码来获取最小的riscv汇编程序的骨架。如何在裸机模式下编译上面的C代码或获取最小riscv汇编代码的框架?


3841
2017-07-13 17:51


起源



答案:


警告: 从最新的RISC-V Privileged Spec v1.9开始,这个答案有点过时,其中包括删除 tohost 控制/状态寄存器(CSR),它是非标准主机 - 目标接口(HTIF)的一部分,后来被删除。目前(截至2016年9月) riscv-tests 而是执行内存映射存储到 tohost 内存位置,在系留环境中由监控 front-end server


如果你  和  需要/想要运行RISC-V代码裸机,那么这里是指令。你丢失了一堆有用的东西,比如printf或FP-trap软件仿真,它们是riscv-pk(代理内核)提供的。

首先要做的事情 - Spike在0x200处启动。由于Spike是黄金ISA模拟器模型,您的核心也应该在0x200启动。

咳嗽,截至2015年7月13日,riscv-tools的“主”分支(https://github.com/riscv/riscv-tools)正在使用较旧的pre-v1.7 Privileged ISA,因此从0x2000开始。这篇文章假设你使用的是v1.7 +,这可能需要使用riscv-tools的“new_privileged_isa”分支。

因此,当您拆卸裸机程序时,它会更好 从0x200开始!如果你想在代理内核之上运行它,它 最好从0x10000开始(如果是Linux,那就更大了......)。

现在,如果你想运行裸机,你就是强迫自己写出来 处理器启动代码。呸。但是,让我们对此表示不满,并假装不是 必要。

(您还可以查看riscv-tests / env / p,了解“虚拟机” 物理寻址机器的描述。你会找到链接器脚本 你需要和一些macros.h来描述一些初始设置代码。或更好 但是,在riscv-tests / benchmarks / common.crt.S中。


无论如何,拥有上述(令人困惑的)知识,让我们抛出一切 离开,从头开始自己......

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

和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 = .;
 }

现在编译这个......

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

这个编译成(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>

并运行它:

spike hello

这是一件美丽的事情。

链接脚本将我们的代码放在0x200。斯派克将开始 0x200,然后将#1写入控制/状态寄存器 “tohost”,告诉Spike“停止运行”。然后我们旋转一个地址 (1:j 1b)直到前端服务器收到消息并杀死我们。

如果你能弄明白如何,可能会抛弃链接器脚本 告诉编译器自己将<_start>移动到0x200。


对于其他示例,您可以仔细阅读以下存储库:

riscv-tests存储库保存的RISC-V ISA测试非常少 (https://github.com/riscv/riscv-tests)。

这个Makefile有编译器选项: https://github.com/riscv/riscv-tests/blob/master/isa/Makefile

许多“虚拟机”描述宏和链接器脚本都可以 在riscv-tests / env中找到(https://github.com/riscv/riscv-test-env)。

你可以看一下“最简单”的测试(riscv-tests/isa/rv64ui-p-simple.dump)。

你可以看看 riscv-tests/benchmarks/common 用于启动和支持运行裸机的代码。


11
2017-07-13 21:32



嗨,克里斯,谢谢你的回答,它已经完成了。在裸机模式下,我丢失了很多东西,比如printf,但如果我想在C语言中使用“pow”这样的aritmetic函数会发生什么?像pow这样的算术函数在数学库中。如果我编译它使用$ riscv-unknown-elf-gcc -nostdlib -nostartfiles -T.link.ld -o code code.c -lm,我想它在裸机模型中不起作用。我想我应该以裸机模式链接这个库吗?或者在这种情况下会发生什么? - Adrian
鉴于它在risc-v方面的丰富经验,当我使用数学库时,您是否建议我更好地在代理内核级别工作? - Adrian
我没有链接其他库函数的经验;我怀疑你在一些gcc /编译器论坛上做得更好(这不是一个真正的RISC-V问题)。我认为没有任何理由为什么它不起作用。 - Chris
如果您要在自己的简单RISC-V处理器上运行代码,那么我至少要从运行裸机开始。只支持M模式更容易,您可以理解它执行的每条指令。 - Chris


答案:


警告: 从最新的RISC-V Privileged Spec v1.9开始,这个答案有点过时,其中包括删除 tohost 控制/状态寄存器(CSR),它是非标准主机 - 目标接口(HTIF)的一部分,后来被删除。目前(截至2016年9月) riscv-tests 而是执行内存映射存储到 tohost 内存位置,在系留环境中由监控 front-end server


如果你  和  需要/想要运行RISC-V代码裸机,那么这里是指令。你丢失了一堆有用的东西,比如printf或FP-trap软件仿真,它们是riscv-pk(代理内核)提供的。

首先要做的事情 - Spike在0x200处启动。由于Spike是黄金ISA模拟器模型,您的核心也应该在0x200启动。

咳嗽,截至2015年7月13日,riscv-tools的“主”分支(https://github.com/riscv/riscv-tools)正在使用较旧的pre-v1.7 Privileged ISA,因此从0x2000开始。这篇文章假设你使用的是v1.7 +,这可能需要使用riscv-tools的“new_privileged_isa”分支。

因此,当您拆卸裸机程序时,它会更好 从0x200开始!如果你想在代理内核之上运行它,它 最好从0x10000开始(如果是Linux,那就更大了......)。

现在,如果你想运行裸机,你就是强迫自己写出来 处理器启动代码。呸。但是,让我们对此表示不满,并假装不是 必要。

(您还可以查看riscv-tests / env / p,了解“虚拟机” 物理寻址机器的描述。你会找到链接器脚本 你需要和一些macros.h来描述一些初始设置代码。或更好 但是,在riscv-tests / benchmarks / common.crt.S中。


无论如何,拥有上述(令人困惑的)知识,让我们抛出一切 离开,从头开始自己......

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

和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 = .;
 }

现在编译这个......

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

这个编译成(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>

并运行它:

spike hello

这是一件美丽的事情。

链接脚本将我们的代码放在0x200。斯派克将开始 0x200,然后将#1写入控制/状态寄存器 “tohost”,告诉Spike“停止运行”。然后我们旋转一个地址 (1:j 1b)直到前端服务器收到消息并杀死我们。

如果你能弄明白如何,可能会抛弃链接器脚本 告诉编译器自己将<_start>移动到0x200。


对于其他示例,您可以仔细阅读以下存储库:

riscv-tests存储库保存的RISC-V ISA测试非常少 (https://github.com/riscv/riscv-tests)。

这个Makefile有编译器选项: https://github.com/riscv/riscv-tests/blob/master/isa/Makefile

许多“虚拟机”描述宏和链接器脚本都可以 在riscv-tests / env中找到(https://github.com/riscv/riscv-test-env)。

你可以看一下“最简单”的测试(riscv-tests/isa/rv64ui-p-simple.dump)。

你可以看看 riscv-tests/benchmarks/common 用于启动和支持运行裸机的代码。


11
2017-07-13 21:32



嗨,克里斯,谢谢你的回答,它已经完成了。在裸机模式下,我丢失了很多东西,比如printf,但如果我想在C语言中使用“pow”这样的aritmetic函数会发生什么?像pow这样的算术函数在数学库中。如果我编译它使用$ riscv-unknown-elf-gcc -nostdlib -nostartfiles -T.link.ld -o code code.c -lm,我想它在裸机模型中不起作用。我想我应该以裸机模式链接这个库吗?或者在这种情况下会发生什么? - Adrian
鉴于它在risc-v方面的丰富经验,当我使用数学库时,您是否建议我更好地在代理内核级别工作? - Adrian
我没有链接其他库函数的经验;我怀疑你在一些gcc /编译器论坛上做得更好(这不是一个真正的RISC-V问题)。我认为没有任何理由为什么它不起作用。 - Chris
如果您要在自己的简单RISC-V处理器上运行代码,那么我至少要从运行裸机开始。只支持M模式更容易,您可以理解它执行的每条指令。 - Chris


“额外”代码由gcc放在那里,是任何程序所需的东西。代理内核旨在成为运行此类内容所需的最低限度的支持。一旦你的处理器工作,我建议在pk而不是裸机上运行。

与此同时,如果你想看一下简单的装配,我建议用'-c'跳过链接阶段:

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

有关运行没有pk或linux的代码的例子,我会看一下 riscv检验


4
2017-07-13 20:15