rt-thread-official/libcpu/risc-v/readme.md

213 lines
7.7 KiB
Markdown
Raw Normal View History

[libcpu][riscv]整合libcpu/riscv中的移植文件 提供一份公共代码于common (#6941) 整合libcpu/riscv中的移植文件 提供一份公共代码于common 在提交本pr时,除hpmicro的内核,rv32内核bsp已完成去除大部分的冗余,大部分代码采用common中的实现。本pr的作用是进一步统一common中的文件,从而提供一份公用代码,新移植的RV32内核的BSP可以全部使用common代码。 - 在common中提供一份公用文件:interrupt_gcc.S - 修改原有的文件,将原有的中断中上下文切换代码替换为interrupt_gcc.S - 基于上述修改,修改仓库中risc-v内核的BSP与移植相关的部分 (主要包含中断入口函数 中断栈等) - 在common中提供一份公用文件:trap_common.c;提供统一中断入口函数,中断入口函数初始化,中断入口注册等函数,并完善异常时的信息输出 - 在common中提供一份公用文件:rt_hw_stack_frame.h;将栈帧结构体剥离,供用户使用 - 在上述工作完成后,在上述工作的基础上测试仓库中risc-v内核的BSP - 完善函数中的命名,完善中断栈的获取 - 提供一份详细的基于现有common文件的移植指南 #### 在什么测试环境下测试通过 - 1.CH32V307V-R1-R0 - 2.CH32V208W-R0-1V4 - 3.HPM6750EVKMINI - 4.GD32VF103V-EVAL - 5.qemu(CORE-V-MCU ) > 与上述开发板使用同样芯片的BSP均测试通过 在CH32V307V-R1-R0与HPM6750EVKMINI上基于现有移植文件进行多线程复杂场景下的长时间测试,测试过程系统运行正常。
2023-03-01 14:32:43 +08:00
### RV32移植指南
#### 1.概述
为了简化RISC-V架构内核移植RT-Thread的流程RT-Thread提供一分通用代码于common文件夹
| 文件名 | 文件内容 |
| :-----------------: | :----------------------------: |
| context_gcc.S | 开关全局中断,线程上下文切换等 |
| cpuport.c | 线程栈初始化,软件中断触发等 |
| cpuport.h | 数据加载写入指令 |
| interrupt_gcc.S | 线程间上下文切换 |
| riscv-ops.h | 控制状态寄存器读写 |
| rt_hw_stack_frame.h | 线程栈格式 |
| trap_common.c | 中断注册,中断查询分发 |
#### 2.移植接口
1软件中断触发函数通常向量管理中断需实现该函数非向量中断管理方式一般不需要
```c
void rt_trigger_software_interrupt(void)
```
2保存上文后执行函数该函数向量中断与非向量中断均需实现
```c
void rt_hw_do_after_save_above(void)
```
该函数需要实现的工作如下:
步骤1将函数返回地址(ra)保存栈中
步骤2加载中断处理函数的入口参数
步骤3调用中断处理函数
步骤4从栈中加载返回地址(ra)返回至SW_handler函数
#### 3.准备工作
- 准备一个基础的裸机工程,需具备以下条件:
- 明确中断管理方式(向量中断/非向量中断)
- 实现系统节拍定时器中断
- 实现一个串口中断
#### 4.移植步骤
<font color=red>对于新移植的RISC-V 32位内核若采用非向量中断管理方式推荐使用common中的文件除了下述步骤中必要的修改以及中断初始化与注册步骤外用户仅需拷贝一份cv32e40p移植文件重命名为具体内核名称即可 。</font>
- 步骤一:配置中断管理入口,相关中断入口函数位于**common/interrupt_gcc.S**,入口函数为**SW_handler**
- 根据使用的中断管理方式,执行下述操作
- 向量中断管理方式
> 通常会使用一个软件中断该中断的优先级被配置为最低作为在中断中触发上下文切换的入口函数。SW_handler在此仅作为触发软件中断时的入口参数其他类型中断触发时跳转至各自的中断入口函数。
>
移植方法:修改原有的中断向量表中软件中断所在位置,将原有软件中断函数名修改为**SW_handler**
示例ch32系列
```assembly
_vector_base:
.option norvc;
.word _start
...
.word SW_handler /* 将这里原来放置的软件中断函数名修改为SW_handler */
```
- 非向量中断
> 当有中断触发时会进入一个统一的中断入口函数进行中断查询分发。SW_handler在此处不仅作为作为在中断中触发上下文切换的入口函数同时承担中断查询分发与执行。
>
移植方法:将**SW_handler**的地址加载到保存统一中断入口地址的寄存器通常为mtevc具体名称需要根据具体的内核指定
示例(hpm6750系列):
```assembly
la t0, SW_handler
csrw mtvec, t0
```
- 步骤二:修改链接脚本,在中断栈顶名称后添加示例代码
- 将下述代码放置于链接脚本中中断栈顶名之后
```assembly
PROVIDE( __rt_rvstack = . );
```
- 示例core-v-mcu链接脚本
```assembly
.stack : ALIGN(16)
{
stack_start = .;
__stack_bottom = .;
. += __stack_size;
__stack_top = .;
PROVIDE( __rt_rvstack = . );//移植时添加
stack = .;
} > L2
```
> __stack_top为core-v-mcu工程的中断栈顶名 不同工程此处的名称可能不一致 按上述方法将给出的代码放到具体工程链接脚本中断栈顶名称之后即可。
- 步骤三:实现在中断上下文切换的函数接口
<font color=red>RISC-V架构的内核通常采用非向量中断的管理方式为了进一步降低难度为用户提供了统一的中断查询分发函数以及相关函数 该部分内容位于common文件夹的trap_common.c文件中对于移植一个新的RV32内核推荐使用RT-Thread提供的统一函数接口。以下是两种实现方式的示例</font>
采用RT-Thread通用中断分发函数
采用RT-Thread提供的通用中断查询分发函数时移植文件直接拷贝一份cv32e40p内核移植文件重命名为具体内核名称即可移植代码如下
```assembly
#include "cpuport.h"
.globl rt_hw_do_after_save_above
.type rt_hw_do_after_save_above,@function
rt_hw_do_after_save_above:
addi sp, sp, -4
STORE ra, 0 * REGBYTES(sp)
csrr a0, mcause
csrr a1, mepc
mv a2, sp
call rt_rv32_system_irq_handler
LOAD ra, 0 * REGBYTES(sp)
addi sp, sp, 4
ret
```
RT-Thread提供的通用中断分发函数如下:
```c
rt_weak void rt_rv32_system_irq_handler(rt_uint32_t mcause)
{
rt_uint32_t mscratch = read_csr(0x340);
rt_uint32_t irq_id = (mcause & 0x1F);
rt_uint32_t exception = !(mcause & 0x80000000);
if(exception)
{
s_stack_frame = (rt_hw_stack_frame_t *)mscratch;
rt_show_stack_frame();
}
else
{
rv32irq_table[irq_id].handler(irq_id, rv32irq_table[irq_id].param);
}
}
```
随后用户仅需调用rt_hw_interrupt_init进行初始化然后将中断入口函数通过rt_hw_interrupt_install函数注册即可注册的中断入口函数为裸机原有的中断入口函数示例代码如下:
```c
rt_hw_interrupt_init();
rt_hw_interrupt_install(0x7, timer_irq_handler, RT_NULL, "timerirq");
rt_hw_interrupt_install(0xb, fc_soc_event_handler1, RT_NULL, "eventirq");
```
(二)采用原有裸机的中断入口处理函数
新建一个文件夹,并以移植的内核的名称命名:
- 向量中断(可参考ch32)
1在刚才新建的文件夹下创建一个c文件用于实现上文提供的两个函数接口
2实现上述两个函数接口
- 在void rt_trigger_software_interrupt(void) 中实现触发软件中断的操作
- 在void rt_hw_do_after_save_above(void)中实现触发软件中断之后的工作,通常是清除软件中断置位标志位或类似操作
- 非向量中断(可参考cv32e40p)
- 在刚才新建的文件夹下,创建一个汇编文件,实现上述提供的函数接口
- 对于非向量中断的管理方式仅需实现rt_hw_do_after_save_above函数即可
示例代码:
```assembly
#include "cpuport.h"
.globl rt_hw_do_after_save_above
.type rt_hw_do_after_save_above,@function
rt_hw_do_after_save_above:
addi sp, sp, -4 // 移动栈指针
STORE ra, 0 * REGBYTES(sp) // 将返回地址寄存器值保存至栈中
csrr a0, mscratch // 加载函数入口参数
call trap_entry // 调用中断处理函数
LOAD ra, 0 * REGBYTES(sp) // 从栈中恢复返回地址寄存器值
addi sp, sp, 4 // 移动栈指针
ret // 返回SW_handler
```
trap_entry为用户实现的中断源查询分发的函数在移植时仅需要将该函数名修改为用户的中断查询分发函数即可。
#### 5.验证
- 创建一个静态线程在线程中调用RT-Thread提供的与系统时基相关函数接口例如rt_thread_mdelay调试观察系统是否可以正常运行
- 移植RT-Thread的shell进一步验证系统是否移植成功