2021-02-08阅读 3070
Booting RISC-V on QEMU | Juraj’s Blog
本文主要梳理riscv32在qemu的移植过程,将通过几天时间将其整理和最小系统的bring up。为了保证代码的可维护性,所有修改符合rt-thread bsp制作规范。目标就是riscv32 qemu 上运行rt-thread。以RT-Thread v4.0.3 released为工程代码的基线,进行开发移植工作。
目前在Ubuntu20.04上面进行开发调试工作,需要下载qemu,riscv的交叉编译工具链即可。
qemu的可以到官网上下载https://www.qemu.org/
。
编译和运行环境在Ubuntu20.04平台上。
Install RISC-V toolchains $ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain 国内下载地址,每天更新一次 #git clone --recursive [email protected]:mirrors/riscv-gnu-toolchain.git $ cd riscv-gnu-toolchain/ $ mkdir build && cd build $ ../configure --prefix=/opt/riscv --enable-multilib $ sudo make $ export PATH=$PATH:/opt/riscv/bin
编译和下载过程很慢,可以直接下载直接编译好的
https://www.sifive.com/software
然后导入环境变量
export PATH=/xxxx/riscv64-unknown-elf-gcc-8.3.0-2020.04.0-x86_64-linux-ubuntu14/bin/:$PATH
设置完成后,在该终端可以生效。
首先需要下载qemu,可以到qemu的官网上下载。
https://www.qemu.org/
可选择最新版本下载即可。
解压后进入qemu-5.2
$ ./configure --target-list=riscv32-softmmu $ make $ sudo make install
可以输入qemu-system-riscv32 --version
验证是否成功。
以RT-Thread v4.0.3 released
为基线,进行riscv32 qemu开发工作的代码已经推到gitee上。
https://gitee.com/bigmagic/riscv32_rtt.git
不定时持续推进开发工作。
第一阶段的任务是将riscv32最小系统在qemu-system-riscv32
上bring up起来,第二阶段会考虑将其适配到具体的硬件平台上。
对于第一阶段的任务划分,规划如下:
1.riscv32工程模板的构建
输出目标:工程编译正常
该工作主要适配scons工程、目录结构、编译脚本,链接脚本、文件组织。
2.代码正常跳转
输出目标:可以通过gdb跟踪代码运行
该工作主要整理底层汇编代码,对芯片的状态进行设置,让其正常执行C代码
3.串口输出rt-thread logo
输出目标:可以看到rt-thread logo正常输出
该工作适配opensbi的服务函数,通过ecall系统调用使用M-Mode的终端输出
4.栈帧布局
输出目标:可以正常进行压栈和出栈操作,并可进入main函数
该工作主要对现场栈的存放布局以及恢复进行设计
5.中断设计和定时器正常
输出目标:定时器可以正常的工作
该工作主要在于对定时器和中断外设的理解。
6.串口输入
输出目标:系统可以正常响应命令
该工作测试系统的整体移植情况。
通过上述6个里程碑将任务进行细化,每个节点的目标明确,工作内容明确,节点与节点之间环环相扣,可以作为测试的依据。
rt-thread是以scons脚本进行编译和链接的,所以在制作bsp时需要依据scons的构建规则进行统筹规划。
可以根据之前的
https://gitee.com/bigmagic/riscv64_rtt
工程进行修改和整理,只留下必要的文件即可。并且移除掉其他无关的bsp以减少工程项目体积。
首先,在bsp目录添加一个riscv32-virt
的bsp包。
最简单的工程只需要包括
1.scons构建的必须文件
Sconscript、SConstruct
2.menuconfig配置文件
Kconfig
3.链接脚本文件
link.lds
4.rtt配置文件
.config、rtconfig.h
5.编译脚本
rtconfig.py
接着在libcpu/risc-v
下新建一个virt
目录。
上述目录结构基本是这样。接着就需要进行设计将代码能够正常的编译通过。
不改变其他工程代码,经过一些列的调整,将代码能够通过scons
编译即可。
如果要代码正常的运行起来,主要需要注意的是目前qemu上运行riscv32的代码是在S-Mode,所以修改部分代码。
首先可以在BSP的Kconfig中添加一个宏定义
因为目前libcpu\risc-v\common
基本上都是实现的M-Mode下的操作,所以部分逻辑需要进行调整。
修改代码libcpu/risc-v/common/context_gcc.S
, S-Mode中断的屏蔽与使能:
执行运行脚本,就可以看到rt-thread的logo正常的执行起来了。
qemu-system-riscv32 -M virt -kernel rtthread.elf -nographic
效果如下所示:
在rt-thread中,栈的出和入的顺序十分重要,这里需要非常的清楚。
与操作系统来说,在调度器还没开始工作时,线程首先会被压入栈空间中。
也就是会执行rt_hw_stack_init
函数。每个线程在创建的时候,都会将寄存器压入到栈顶,因为目前是S-Mode,所以需要将寄存器进行修改。
将将入栈寄存器还是按照这种方式进行,主要改动的地方如下:
另外就是压入:
出栈则是与压栈一一对应的关系。
libcpu/risc-v/common/context_gcc.S
也就是将该文件下所有的M-Mode下的寄存器,替换成S-Mode下的寄存器即可。
替换完成后,入栈出栈顺序就可以对应上了,然后开始测试代码。
OK!此时已经可以看到任务正常的切换了。
实验进行到这里已经基本搭建完成系统的骨架,系统的后续还有几件事比较麻烦:
这三块也是riscv架构中最核心与最复杂的部分,会单独用一篇文章分析其设计和使用,文章中代码实现已经提交至
https://gitee.com/bigmagic/riscv32_rtt
代码部分还有一些细节需要完善,但目前第一阶段主要以bring up为首要目标,后续会在第二篇文章专门描述中断、串口输出、系统定时器、三者在不同架构的设计与rt-thread的之间的处理逻辑。也会在近期选择riscv32的硬件平台进行选型以及移植测试。