本篇文章由 VeriMake 旧版论坛中备份出的原帖的 Markdown 源码生成
原帖标题为:为 PicoRV32 适配 rt-thread 操作系统
原帖网址为:https://verimake.com/topics/69 (旧版论坛网址,已失效)
原帖作者为:上丁(旧版论坛 id = 25,注册于 2020-04-11 21:55:20)
原帖由作者初次发表于 2020-04-11 22:00:55,最后编辑于 2020-04-11 22:00:55(编辑时间可能不准确)
截至 2021-12-18 14:27:30 备份数据库时,原帖已获得 1646 次浏览、1 个点赞、0 条回复
为PicoRV32适配rt-thread操作系统
PicoRV32是一款由著名的IC设计师Clifford Wolf开发并开源的一款RISC-V处理器核,它实现RISC-V RV32IMC指令集,这款PicoRV32
的重点在于面积和频率的优化,在作者的github上我们了解到在Xilinx7-Series FPGA上的开销为761-2000 LUTs,并且能够综合到250~450MHz的主频。而我们这次使用的安陆EAGLE_20系列FPGA LUTs资源为19600,足以放入PicoRV32
核心。
rt-thread是一款国产实时操作系统,采用c语言编写,具有体积小,成本低,功耗低、启动快速,实时性高、占用资源小等特点。其NANO版本仅占用3kb的flash资源,1.2kb的RAM资源,非常适合PicoRV32。
目前我们已经成了rt-thread
在PicoRV32
移植的初步工作,使得rt-thread
能够在PicoRV32
核心上正常运行,并实现基本的上下文切换操作。
01
如下是我们使用VSCODE和RISCV-gcc工具链编译rtthread
yang@DESKTOP-3E9KST5 MINGW64 ~/Desktop/work_space/firmware-flash_2
$ make
riscv32-unknown-elf-gcc -I"C:\Users\yang\Desktop\work_space\firmware-flash_2" -I"C:\Users\yang\Desktop\work_space\firmware-flash_2\rt-thread\include" -I"C:\Users\yang\Desktop\work_space\firmware-flash_2\rt-thread\libcpu" -I"C:\Users\yang\Desktop\work_space\firmware-flash_2\rt-thread\finsh" -ffreestanding -nostdlib -o firmware.elf start.S firmware.c ./rt-thread/src/*.c ./rt-thread/libcpu/*.c ./rt-thread/finsh/*.c ./rt-thread/libcpu/*.S -march=RV32I --std=gnu99 -Wl,-Bstatic,-T,sections.lds,-Map,firmware.map,--strip-debug -lgcc
riscv32-unknown-elf-objcopy -O binary firmware.elf firmware.bin
yang@DESKTOP-3E9KST5 MINGW64 ~/Desktop/work_space/firmware-flash_2
$
02
移植的第一步首先是得完成在PicoRV32裸机状态下的运行程序,确保cpu能正常运作和启动代码的有效。然后把入口地址(main)指向操作系统的入口地址(entry)。
#include "custom_ops.S"
.section .text
start:
# zero initialize entire scratchpad memory
li a0, 0x00000000
li a1, 0x00001000
setmemloop:
sw a0, 0(a0)
addi a0, a0, 4
blt a0, a1, setmemloop
# copy data section
la a0, _sidata
la a1, _sdata
la a2, _edata
bge a1, a2, end_init_data
loop_init_data:
lw a3, 0(a0)
sw a3, 0(a1)
addi a0, a0, 4
addi a1, a1, 4
blt a1, a2, loop_init_data
end_init_data:
# zero-init bss section
la a0, _sbss
la a1, _ebss
bge a0, a1, end_init_bss
loop_init_bss:
sw zero, 0(a0)
addi a0, a0, 4
blt a0, a1, loop_init_bss
end_init_bss:
# zero-initialize register file
addi x1, zero, 0
# x2 (sp) is initialized by reset
addi x3, zero, 0
addi x4, zero, 0
addi x5, zero, 0
addi x6, zero, 0
addi x7, zero, 0
addi x8, zero, 0
addi x9, zero, 0
addi x10, zero, 0
addi x11, zero, 0
addi x12, zero, 0
addi x13, zero, 0
addi x14, zero, 0
addi x15, zero, 0
addi x16, zero, 0
addi x17, zero, 0
addi x18, zero, 0
addi x19, zero, 0
addi x20, zero, 0
addi x21, zero, 0
addi x22, zero, 0
addi x23, zero, 0
addi x24, zero, 0
addi x25, zero, 0
addi x26, zero, 0
addi x27, zero, 0
addi x28, zero, 0
addi x29, zero, 0
addi x30, zero, 0
addi x31, zero, 0
lui sp, %hi(0x400);
addi sp, sp, %lo(0x400);
# call main
call entry
loop:
j loop
.balign 4
03
编写上下文切换汇编函数、这里是系统启动执行的第一个任务,关键是要需要将任务的入口函数地址和返回地址复制到通用寄存器中
.globl rt_hw_context_switch_to
rt_hw_context_switch_to:
lw sp, (a0)
/*
lw a0, 0 * 4(sp)
csrw mepc, a0 load epc from stack 机器模式异常pc寄存器,现在把thread1的入口存入
*/
lw x1, 1 * 4(sp) /*ra 返回地址*/
/*
lw a0, 2 * 4(sp) load mstatus from stack
csrw mstatus, a0 机器模式状态寄存器,7880意味着mpp为11强制为机器模式MPIE0,MIE为1,
*/
lw x4, 4 * 4(sp)
lw x5, 5 * 4(sp)
lw x6, 0 * 4(sp) /*把入口保存在临时寄存器t1*/
lw x7, 7 * 4(sp)
lw x8, 8 * 4(sp)
lw x9, 9 * 4(sp)
lw x10, 10 * 4(sp)
lw x11, 11 * 4(sp)
lw x12, 12 * 4(sp)
lw x13, 13 * 4(sp)
lw x14, 14 * 4(sp)
lw x15, 15 * 4(sp)
lw x16, 16 * 4(sp)
lw x17, 17 * 4(sp)
lw x18, 18 * 4(sp)
lw x19, 19 * 4(sp)
lw x20, 20 * 4(sp)
lw x21, 21 * 4(sp)
lw x22, 22 * 4(sp)
lw x23, 23 * 4(sp)
lw x24, 24 * 4(sp)
lw x25, 25 * 4(sp)
lw x26, 26 * 4(sp)
lw x27, 27 * 4(sp)
lw x28, 28 * 4(sp)
lw x29, 29 * 4(sp)
lw x30, 30 * 4(sp)
lw x31, 31 * 4(sp)
addi sp, sp, 32 * 4
jr t1
04
编写任务切换汇编代码
.globl rt_hw_context_switch
rt_hw_context_switch:
/* saved from thread context
* x1/ra -> sp(0)
* x1/ra -> sp(1)
* mstatus.mie -> sp(2)
* x(i) -> sp(i-4)
*/
addi sp, sp, -32 * 4
sw sp, (a0)
sw x1, 0 * 4(sp)
sw x1, 1 * 4(sp)
/* csrr a0, mstatus */
andi a0, a0, 8
beqz a0, save_mpie
li a0, 0x80
save_mpie:
sw a0, 2 * 4(sp)
sw x4, 4 * 4(sp)
sw x5, 5 * 4(sp)
sw x6, 6 * 4(sp)
sw x7, 7 * 4(sp)
sw x8, 8 * 4(sp)
sw x9, 9 * 4(sp)
sw x10, 10 * 4(sp)
sw x11, 11 * 4(sp)
sw x12, 12 * 4(sp)
sw x13, 13 * 4(sp)
sw x14, 14 * 4(sp)
sw x15, 15 * 4(sp)
sw x16, 16 * 4(sp)
sw x17, 17 * 4(sp)
sw x18, 18 * 4(sp)
sw x19, 19 * 4(sp)
sw x20, 20 * 4(sp)
sw x21, 21 * 4(sp)
sw x22, 22 * 4(sp)
sw x23, 23 * 4(sp)
sw x24, 24 * 4(sp)
sw x25, 25 * 4(sp)
sw x26, 26 * 4(sp)
sw x27, 27 * 4(sp)
sw x28, 28 * 4(sp)
sw x29, 29 * 4(sp)
sw x30, 30 * 4(sp)
sw x31, 31 * 4(sp)
/* restore to thread context
* sp(0) -> epc;
* sp(1) -> ra;
* sp(i) -> x(i+2)
*/
lw sp, (a1) /*参数二*/
/* resw ra to mepc */
lw a1, 0 * 4(sp)
/* csrw mepc, a1 有*/
lw x1, 1 * 4(sp)
/* force to machin mode(MPP=11) */
/* li a1, 0x00001800; @*/
/* csrs mstatus, a1 @*/
/* lw a1, 2 * 4(sp) @*/
/* csrs mstatus, a1 @*/
lw x4, 4 * 4(sp)
lw x5, 5 * 4(sp)
lw x6, 0 * 4(sp)
lw x7, 7 * 4(sp)
lw x8, 8 * 4(sp)
lw x9, 9 * 4(sp)
lw x10, 10 * 4(sp)
lw x11, 11 * 4(sp)
lw x12, 12 * 4(sp)
lw x13, 13 * 4(sp)
lw x14, 14 * 4(sp)
lw x15, 15 * 4(sp)
lw x16, 16 * 4(sp)
lw x17, 17 * 4(sp)
lw x18, 18 * 4(sp)
lw x19, 19 * 4(sp)
lw x20, 20 * 4(sp)
lw x21, 21 * 4(sp)
lw x22, 22 * 4(sp)
lw x23, 23 * 4(sp)
lw x24, 24 * 4(sp)
lw x25, 25 * 4(sp)
lw x26, 26 * 4(sp)
lw x27, 27 * 4(sp)
lw x28, 28 * 4(sp)
lw x29, 29 * 4(sp)
lw x30, 30 * 4(sp)
lw x31, 31 * 4(sp)
addi sp, sp, 32 * 4
jr t1
05
创建两个测试任务,由于没有启用内存堆,采用静态创建的方式创建任务。
void main()
{
rt_kprintf("now at main\n\r");
rt_err_t result;
/* 创建线程1 */
tid1 = &thread_1;
rt_kprintf("tid1 = &thread1 done;\n\r");
result = rt_thread_init(tid1, "thread_1", thread1_entry, RT_NULL,
thread1_stack, sizeof(thread1_stack), 5, 20); //优先级,时间片
if(result == RT_EOK){
rt_kprintf("thread1 startup... ");
rt_thread_startup(tid1);
rt_kprintf("done!\r\n\r\n");
}
else{
print("init thread1 fail\n\r");
}
/* 创建线程2 */
tid2 = &thread_2;
rt_kprintf("\r\n\r\nnow init thread2...");
result = rt_thread_init(tid2, "thread2", thread2_entry, RT_NULL,
thread2_stack, sizeof(thread2_stack), 6, 20); //优先级,时间片
if(result == RT_EOK){
print("thread2 startup\n\r");
rt_thread_startup(tid2);
}
else{
print("init thread2 fail\n\r");
}
rt_kprintf("\r\n\r\n\r\nall thing is ok!\r\n");
rt_kprintf("let go to scheduler now!\r\n");
rt_system_scheduler_start();
}
06
编写任务代码,测试任务切换功能。第一个任务运行一段时间后挂起自身,让出cpu的使用权。使得任务2得以执行。
void thread1_entry(void *param)
{
rt_uint32_t t=0;
rt_kprintf("now at thread1\r\n");
while (1) //慢
{
leds = 0x80;
for(uint32_t i=0;i<10000;i++)asm volatile("");
leds = 0x40;
for(uint32_t i=0;i<10000;i++)asm volatile("");
rt_kprintf("t = %d\r\n",t);
t++;
if (3==t)
{
rt_kprintf("rt_thread_suspend....");
if(rt_thread_suspend(tid1) == RT_EOK) //rt_thread_suspend(tid1);
{
rt_kprintf("ok!");
rt_schedule();
t=0;
}
}
}
}
void thread2_entry(void *param)
{
char a=0;
rt_kprintf("now at thread2\r\n");
while (1)
{
leds = 0x80;
for(uint32_t i=0;i<2000;i++)asm volatile("");
leds = 0x40;
for(uint32_t i=0;i<2000;i++)asm volatile("");
a++;
rt_kprintf("a = %d\r\n",a);
if(15==a)
{
rt_thread_resume(tid1);
rt_schedule();
a=0;
}
}
}
07
第一个任务启动前要在调度函数里打印一下栈帧的内容情况确保内容有效。
08
运行的结果:任务在来回切换,串口打印不同的内容。led在两个任务的闪烁频率不同
ok!highest_ready_priority is : 6
local is : 00000780 value :a0520
local is : 00000784 value :a406c
local is : 00000788 value :3888
local is : 0000078c value :deadbeef
local is : 00000790 value :deadbeef
local is : 00000794 value :deadbeef
local is : 00000798 value :deadbeef
local is : 0000079c value :deadbeef
local is : 000007a0 value :deadbeef
local is : 000007a4 value :deadbeef
local is : 000007a8 value :0
local is : 000007ac value :deadbeef
local is : 000007b0 value :deadbeef
local is : 000007b4 value :deadbeef
local is : 000007b8 value :deadbeef
local is : 000007bc value :deadbeef
local is : 000007c0 value :deadbeef
local is : 000007c4 value :deadbeef
local is : 000007c8 value :deadbeef
local is : 000007cc value :deadbeef
local is : 000007d0 value :deadbeef
local is : 000007d4 value :deadbeef
local is : 000007d8 value :deadbeef
local is : 000007dc value :deadbeef
local is : 000007e0 value :deadbeef
local is : 000007e4 value :deadbeef
local is : 000007e8 value :deadbeef
local is : 000007ec value :deadbeef
local is : 000007f0 value :deadbeef
local is : 000007f4 value :deadbeef
local is : 000007f8 value :deadbeef
local is : 000007fc value :deadbeef
rt_hw_context_switch...now at thread2
a = 1
a = 2
a = 3
a = 4
a = 5
a = 6
a = 7
a = 8
a = 9
a = 10
a = 11
a = 12
a = 13
a = 14
a = 15
jump over rt_hw_interrupt_disable();
jump rt_hw_interrupt_enable(temp);
into rt_schedule_insert_thread... jump interrupt disable
insert thread to ready list ...done!jump interrupt enable
done!
highest_ready_priority is : 5
local is : 00000530 value :a3c5c
local is : 00000534 value :a3c5c
local is : 00000538 value :80
local is : 0000053c value :a04
local is : 00000540 value :deadbeef
local is : 00000544 value :a261c
local is : 00000548 value :a0438
local is : 0000054c value :deadbeef
local is : 00000550 value :5d0
local is : 00000554 value :6
local is : 00000558 value :80
local is : 0000055c value :ac4
local is : 00000560 value :a6280
local is : 00000564 value :594
local is : 00000568 value :b3c
local is : 0000056c value :ac4
local is : 00000570 value :0
local is : 00000574 value :deadbeef
local is : 00000578 value :deadbeef
local is : 0000057c value :deadbeef
local is : 00000580 value :deadbeef
local is : 00000584 value :deadbeef
local is : 00000588 value :deadbeef
local is : 0000058c value :deadbeef
local is : 00000590 value :deadbeef
local is : 00000594 value :deadbeef
local is : 00000598 value :deadbeef
local is : 0000059c value :deadbeef
local is : 000005a0 value :deadbeef
local is : 000005a4 value :deadbeef
local is : 000005a8 value :deadbeef
local is : 000005ac value :deadbeef
rt_hw_context_switch...done!
t = 0
t = 1