- 本帖最后由 Stupid_bird 于 2020-7-1 20:20 编辑 *
PicoRV32 是使用verilog实现一个简单的RISC-V CPU
只需要很少的资源消耗便可以移植到FPGA中运行起来。
几个月之前忽然想尝试将RT-Thread 内核移植到 PicoRV32 CPU上运行起来的想法,于是有了目前这个帖子。
第一次写帖子如有问题望大家见谅 ,记录一下移植的方法 欢迎大家交流。
PicoRV32 内核的github地址 : https://github.com/cliffordwolf/picorv32
目前情况 已经基本完成RT-Thread的移植工作。工程连接:https://github.com/flyfishR/rtthread-nanoFPGA工程 :https://gitee.com/Stupid_bird/picorv32_EG4S20.git
移植方法
1、使用的工具链是 gcc 在picorv32 GitHub上有安装方法
2、调试的时候使用的是modelsim (:L 好麻烦。。。。)移植主要需要实现下面几个函数
1、启动代码 start.s
2、上下文切换函数
rt_hw_context_switch_to
rt_hw_context_switch
rt_hw_context_switch_interrupt
3、全局中断开关
rt_hw_interrupt_disable
rt_hw_interrupt_enable
4、中断管理函数
rt_hw_interrupt_init
rt_hw_interrupt_mask
rt_hw_interrupt_umask
rt_hw_interrupt_install
启动代码1、主要完成了CPU寄存器、数据段和BSS段的初始化工作 提供好C语言的运行环境
/* zero-initialize all registers */
addi x1, zero, 0
addi x2, zero, 0
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
/* set stack pointer */
lui sp, %hi(_riscv_sp)
addi sp, sp, %lo(_riscv_sp)
addi sp,sp,-16
sw zero,0(sp)
sw zero,4(sp)
sw zero,8(sp)
sw zero,12(sp)
// picorv32_waitirq_insn(zero)
picorv32_maskirq_insn(zero, zero)
j _start
ebreak
_start:
# Initialize global pointer
1: auipc gp, %pcrel_hi(__global_pointer$)
addi gp, gp, %pcrel_lo(1b)
# Clear the bss segment
la a0, _edata
la a1, _end
_bss_init:
addi a0,a0,4
sw zero,-4(a0)
bgeu a1,a0,_bss_init
# Init the data segment
la a0, _data
la a1, _edata
la a2, _data_lma
_data_init:
addi a2,a2,4
lw a5,-4(a2)
addi a0,a0,4
sw a5,-4(a0)
bgeu a1,a0,_data_init
# call entry
li a0, 0
call entry
ebreak
riscv_maskirq:
picorv32_maskirq_insn(a0, a0)
ret
riscv_timer:
picorv32_timer_insn(a0, a0)
ret
riscv_getirq:
picorv32_getq_insn(a0, q1)
ret
2、进程切换
rt_hw_context_switch_interrupt
```void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to)
{
if (rt_thread_switch_interrupt_flag == 0)
rt_interrupt_from_thread = from;
rt_interrupt_to_thread = to;
rt_thread_switch_interrupt_flag = 1;
return ;
}rt_hw_context_switch
这个函数要说明一下 我这边使用的触发中断的方式来实现进程切换的 使用的rt_thread_switch_interrupt_flag 这个变量来标记 是否要进入中断状态 ,实际进入中断是在 rt_hw_interrupt_enable函数中后面会看到实现方法。
void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to)
{
if (rt_thread_switch_interrupt_flag == 0)
rt_interrupt_from_thread = from;
rt_interrupt_to_thread = to;
rt_thread_switch_interrupt_flag = 1;
rt_hw_context_switch_flag=1 ;
return ;
}```rt_hw_context_switch_to加载第一个线程的时候使用。
* void rt_hw_context_switch_to(rt_ubase_t to);
* a0 --> to
*/
.globl rt_hw_context_switch_to
rt_hw_context_switch_to:
LOAD sp, (a0)
/* resw ra to mepc */
LOAD tp, 0 * 4(sp) // 加载PC指针 到线程指针中 待用
LOAD x1, 1 * REGBYTES(sp) /* x1 - ra - return address for jumps */
LOAD x5, 5 * REGBYTES(sp)
LOAD x6, 6 * REGBYTES(sp)
LOAD x7, 7 * REGBYTES(sp)
LOAD x8, 8 * REGBYTES(sp)
LOAD x9, 9 * REGBYTES(sp)
LOAD x10, 10 * REGBYTES(sp)
LOAD x11, 11 * REGBYTES(sp)
LOAD x12, 12 * REGBYTES(sp)
LOAD x13, 13 * REGBYTES(sp)
LOAD x14, 14 * REGBYTES(sp)
LOAD x15, 15 * REGBYTES(sp)
LOAD x16, 16 * REGBYTES(sp)
LOAD x17, 17 * REGBYTES(sp)
LOAD x18, 18 * REGBYTES(sp)
LOAD x19, 19 * REGBYTES(sp)
LOAD x20, 20 * REGBYTES(sp)
LOAD x21, 21 * REGBYTES(sp)
LOAD x22, 22 * REGBYTES(sp)
LOAD x23, 23 * REGBYTES(sp)
LOAD x24, 24 * REGBYTES(sp)
LOAD x25, 25 * REGBYTES(sp)
LOAD x26, 26 * REGBYTES(sp)
LOAD x27, 27 * REGBYTES(sp)
LOAD x28, 28 * REGBYTES(sp)
LOAD x29, 29 * REGBYTES(sp)
LOAD x30, 30 * REGBYTES(sp)
LOAD x31, 31 * REGBYTES(sp)
addi sp, sp, 32 * REGBYTES
jr tp // 跳转至线程指针```3、中断开关
```rt_base_t rt_hw_interrupt_disable(void)
{
return riscv_maskirq(0xffffffff);
}
void rt_hw_interrupt_enable(rt_base_t level)
{
riscv_maskirq(level);
if(rt_hw_context_switch_flag)
{
rt_hw_context_switch_flag =0;
if((level & 0x0002)==0)
{
/* 判断是否要触发系统中断*/
if(rt_thread_switch_interrupt_flag)
{
__asm("ecall");
}
}
}
return;
}
中断入口 实际进行线程切换的地方
```irq_entry:
/ save registers /
addi sp, sp, -32 * REGBYTES
picorv32_setq_insn(q2, x1)
picorv32_getq_insn(x1, q0)
sw x1, 0*REGBYTES(sp) // 保存 epc 指针
picorv32_getq_insn(x1, q2) // 保存 return address
sw x1, 1 * REGBYTES(sp)
sw x5, 5 * REGBYTES(sp)
sw x6, 6 * REGBYTES(sp)
sw x7, 7 * REGBYTES(sp)
sw x8, 8 * REGBYTES(sp)
sw x9, 9 * REGBYTES(sp)
sw x10, 10 * REGBYTES(sp)
sw x11, 11 * REGBYTES(sp)
sw x12, 12 * REGBYTES(sp)
sw x13, 13 * REGBYTES(sp)
sw x14, 14 * REGBYTES(sp)
sw x15, 15 * REGBYTES(sp)
sw x16, 16 * REGBYTES(sp)
sw x17, 17 * REGBYTES(sp)
sw x18, 18 * REGBYTES(sp)
sw x19, 19 * REGBYTES(sp)
sw x20, 20 * REGBYTES(sp)
sw x21, 21 * REGBYTES(sp)
sw x22, 22 * REGBYTES(sp)
sw x23, 23 * REGBYTES(sp)
sw x24, 24 * REGBYTES(sp)
sw x25, 25 * REGBYTES(sp)
sw x26, 26 * REGBYTES(sp)
sw x27, 27 * REGBYTES(sp)
sw x28, 28 * REGBYTES(sp)
sw x29, 29 * REGBYTES(sp)
sw x30, 30 * REGBYTES(sp)
sw x31, 31 * REGBYTES(sp)
picorv32_setq_insn(q3, x2) // 保存sp 至q3 寄存器
/* switch to interrupt stack */
la sp , irq_stack // 加载 irq 堆栈
/* interrupt handle */
call rt_interrupt_enter
/* call interrupt handler C function */
picorv32_getq_insn(a1, q1)
// call to C function
jal ra, irq
call rt_interrupt_leave
/* switch to from thread stack */
picorv32_getq_insn(sp, q3)
/* need to switch new thread */
la s0, rt_thread_switch_interrupt_flag
lw s2, 0(s0)
beqz s2, rt_hw_context_switch_interrupt_exit
/* clear switch interrupt flag */
sw zero, 0(s0)
la s0, rt_interrupt_from_thread
lw s1, 0(s0)
sw sp, 0(s1)
la s0, rt_interrupt_to_thread
lw s1, 0(s0)
lw sp, 0(s1)
lw a0, 0 * REGBYTES(sp)
picorv32_setq_insn(q0, a0)
/ restore registers /
rt_hw_context_switch_interrupt_exit:
lw x1, 0 * REGBYTES(sp)
picorv32_setq_insn(q0, x1)
lw x1, 1 * REGBYTES(sp)
picorv32_setq_insn(q2, x1)
lw x5, 5 * REGBYTES(sp)
lw x6, 6 * REGBYTES(sp)
lw x7, 7 * REGBYTES(sp)
lw x8, 8 * REGBYTES(sp)
lw x9, 9 * REGBYTES(sp)
lw x10, 10 * REGBYTES(sp)
lw x11, 11 * REGBYTES(sp)
lw x12, 12 * REGBYTES(sp)
lw x13, 13 * REGBYTES(sp)
lw x14, 14 * REGBYTES(sp)
lw x15, 15 * REGBYTES(sp)
lw x16, 16 * REGBYTES(sp)
lw x17, 17 * REGBYTES(sp)
lw x18, 18 * REGBYTES(sp)
lw x19, 19 * REGBYTES(sp)
lw x20, 20 * REGBYTES(sp)
lw x21, 21 * REGBYTES(sp)
lw x22, 22 * REGBYTES(sp)
lw x23, 23 * REGBYTES(sp)
lw x24, 24 * REGBYTES(sp)
lw x25, 25 * REGBYTES(sp)
lw x26, 26 * REGBYTES(sp)
lw x27, 27 * REGBYTES(sp)
lw x28, 28 * REGBYTES(sp)
lw x29, 29 * REGBYTES(sp)
lw x30, 30 * REGBYTES(sp)
lw x31, 31 * REGBYTES(sp)
picorv32_getq_insn(x1, q2)
addi sp, sp, 32 * REGBYTES
picorv32_retirq_insn()
```运行效果
安路FPGA上运行效果

可以的后面添加进去