本文介绍基于RISC-V rv32i指令集的裸机C语言代码编译到执行流程。假设裸机为FPGA软核,采用哈佛架构,指令内存和数据内存相互隔离,现在需要将C语言程序编译链接生成elf文件,并且将elf文件相应部分,例如,指令、数据等,分别进行提取,并且转化成FPGA BRAM可以存储的格式。
生成elf文件
由于rv32i不支持浮点运算指令,所以需要解决的是C语言代码中使用的乘法、除法的问题。当使用rv32i指令集时,对于乘法会调用libgcc.a
中的__mulsi3
函数进行计算,除法也会调用libgcc.a
中的函数处理,所以只需要在编译时,使用-lgcc
表示使用libgcc.a
即可,具体的命令如下所示。
# test.c表示输入的C语言文件名,test.elf表示编译链接完成生成的elf文件名。
riscv32-unknown-elf-gcc test.c -march=rv32i --entry=main -nostdlib -lgcc -o test.elf
-march=rv32i
,表示使用rv32i指令集架构进行编译。--entry=main
,表示编译生成的程序的入口地址为main函数的开始地址。-nostdlib
,表示不使用标准库,考虑在裸机上编译运行尽可能降低程序的编译出来的复杂度,所以不使用标准库。-lgcc
,表示使用libgcc.a,以支持C语言中的浮点操作。
指令、数据提取
对于编译生成的elf文件,其中包含指令、数据等多段内容,现在需要提取出其中的指令部分,用于写入裸机的指令内存。对此可以使用riscv32-unknown-elf-objcopy
将elf文件中对应段(section)
提取到对应文件中。
## test.inst.bin表示提取出的文件名,test.elf表示输入的文件名。
riscv32-unknown-elf-objcopy --dump-section .text=test.inst.bin test.elf
--dump-section .text=test.inst.bin test.elf
,表示将test.elf
文件中的.text
段的内容提取到test.inst.bin
中,现在只需将test.inst.bin
的格式进行转换,即可写入FPGA的BRAM中。
程序的运行不止需要指令,还需要程序对应的数据,即elf文件中数据部分,由于程序需要的数据在elf文件中包含多段内容,所以现在采取一种简单的方式,即将elf文件转换成二进制文件,再讲该文件写入裸机的数据内存,作为该段程序所需要的数据。
# test.data.bin表示提取出的文件名,test.elf表示输入的文件名。
riscv32-unknown-elf-objcopy -O binary test.elf test.data.bin
现在只需将test.data.bin
的格式进行转换,即可写入FPGA的BRAM中。
指令、数据格式处理
上一步生成的*.inst.bin
和*.data.bin
为二进制文件,需要将其转换为小端模式的十六进制文件*.inst
和*.data
以写入FPGA的BRAM。下图为为转换前的*.bin
文件和转换后的*.inst
或*.data
文件对比。
*.bin
文件
*.inst
或*.data
文件
现在使用Bin2Data.c
将*.bin
转换为*.inst
或*.data
。
#include<stdio.h>
#include<stdlib.h>
void bytesToHexstring(char* bytes,int bytelength,char *hexstring,int hexstrlength)
{
char str2[16] = {
'0',