1###  RV32移植指南
2
3#### 1.概述
4
5为了简化32位RISC-V架构内核移植RT-Thread的流程,RT-Thread提供一分通用代码于common文件夹
6
7|       文件名        |            文件内容            |
8| :-----------------: | :----------------------------: |
9|    context_gcc.S    | 开关全局中断,线程上下文切换等 |
10|      cpuport.c      |  线程栈初始化,软件中断触发等  |
11|      cpuport.h      |        数据加载写入指令        |
12|   interrupt_gcc.S   |        线程间上下文切换        |
13|     riscv-ops.h     |       控制状态寄存器读写       |
14| rt_hw_stack_frame.h |           线程栈格式           |
15|    trap_common.c    |     中断注册,中断查询分发     |
16
17#### 2.移植接口
18
19(1)软件中断触发函数,通常向量管理中断方式需实现该函数,非向量中断管理方式一般不需要
20
21```c
22void rt_trigger_software_interrupt(void)
23```
24
25(2)保存上文后执行函数,该函数向量中断与非向量中断均需实现
26
27```c
28void rt_hw_do_after_save_above(void)
29```
30
31该函数需要实现的工作如下:
32
33步骤1:将函数返回地址(ra)保存栈中
34
35步骤2:加载中断处理函数的入口参数
36
37步骤3:调用中断处理函数(新移植的BSP推荐使用RT-Thread common_trap.c文件中提供的统一中断处理函数:rt_rv32_system_irq_handler)
38
39步骤4:从栈中加载返回地址(ra),返回至SW_handler函数
40
41#### 3.准备工作
42
43- 准备一个基础的裸机工程,需具备以下条件:
44
45  - 明确中断管理方式(向量中断/非向量中断)
46
47  - 实现系统节拍定时器中断
48  - 实现一个串口中断
49
50#### 4.移植步骤
51
52- 步骤一:配置中断管理入口,相关中断入口函数位于**common/interrupt_gcc.S**,入口函数为**SW_handler**
53
54  - 根据使用的中断管理方式,执行下述操作
55
56    - 向量中断管理方式
57
58      > 通常会使用一个软件中断(该中断的优先级被配置为最低)作为在中断中触发上下文切换的入口函数。SW_handler在此仅作为触发软件中断时的入口参数,其他类型中断触发时跳转至各自的中断入口函数。
59      >
60
61      移植方法:修改原有的中断向量表中软件中断所在位置,将原有软件中断函数名修改为**SW_handler**
62
63      示例(ch32系列):
64
65      ```assembly
66      _vector_base:
67          .option norvc;
68          .word   _start
69           ...
70          .word   SW_handler    /* 将这里原来放置的软件中断函数名修改为SW_handler */
71      ```
72
73    - 非向量中断
74
75      > 当有中断触发时,会进入一个统一的中断入口函数进行中断查询分发。SW_handler在此处不仅作为作为在中断中触发上下文切换的入口函数,同时承担中断查询分发与执行。
76      >
77
78      移植方法:将**SW_handler**的地址加载到保存统一中断入口地址的寄存器(通常为mtevc,具体名称需要根据具体的内核指定)
79
80      示例(hpm6750系列):
81
82      ```assembly
83          la t0, SW_handler
84          csrw mtvec, t0
85      ```
86
87- 步骤二:修改链接脚本,在中断栈顶名称后添加示例代码
88
89  - 将下述代码放置于链接脚本中中断栈顶名称之后
90
91    ```assembly
92    PROVIDE( __rt_rvstack = . );
93    ```
94
95  - 示例(core-v-mcu链接脚本):
96
97    ```assembly
98      .stack : ALIGN(16)
99      {
100        stack_start = .;
101        __stack_bottom = .;
102        . += __stack_size;
103        __stack_top = .;
104        PROVIDE( __rt_rvstack = . );//移植时添加
105        stack = .;
106      } > L2
107    ```
108
109> __stack_top为core-v-mcu工程的中断栈顶名  不同工程此处的名称可能不一致 按上述方法将给出的代码放到具体工程链接脚本中断栈顶名称之后即可。
110
111- 步骤三:实现在中断上下文切换的函数接口
112
113  <font color=red>RISC-V架构的内核通常采用非向量中断的管理方式,为了进一步降低难度,针对非向量模式的中断管理方式,common文件夹中的trap_common.c为用户提供了一套统一的中断查询分发、中断入口函数注册以及中断初始化函数,在rthw.h中声明,对于移植一个新的RV32内核,若采用非向量中断管理的方式,推荐使用方式一,若采用向量中断管理方式或针对中断的处理有专门的优化时推荐使用方式二,期望采用原有裸机工程的统一的中断查询与处理函数也可使用方式二。以下是两种实现方式的示例:</font>
114
115  方式一:面向非向量中断管理方式(例:core-v-mcu)
116
117  在RT-Thread的BSP框架中的board文件夹创建一个统一名称的汇编文件:trap_gcc.S,将该文件添加到编译环境即可,该函数的实现如下(用户直接使用,无需修改):
118
119  ```assembly
120    #include "cpuport.h"
121
122    	.globl rt_hw_do_after_save_above
123    	.type rt_hw_do_after_save_above,@function
124    rt_hw_do_after_save_above:
125    	addi  sp, sp,  -4
126        STORE ra,  0 * REGBYTES(sp)
127
128        csrr  a0, mcause
129        csrr  a1, mepc
130        mv    a2, sp
131        call  rt_rv32_system_irq_handler
132
133        LOAD  ra,  0 * REGBYTES(sp)
134        addi  sp, sp,  4
135        ret
136  ```
137
138  随后用户仅需调用rt_hw_interrupt_init进行初始化,再将中断入口函数通过rt_hw_interrupt_install函数注册即可,注册的中断入口函数为裸机原有的中断入口函数,示例代码如下(相关设备的中断入口函数注册之前不可使用该设备):
139
140  ```c
141        rt_hw_interrupt_init();//中断入口函数初始化
142        rt_hw_interrupt_install(0x7, timer_irq_handler, RT_NULL, "timerirq");//注册系统定时器中断入口函数
143        rt_hw_interrupt_install(0xb, fc_soc_event_handler1, RT_NULL, "eventirq");//注册外部中断入口函数
144  ```
145
146  方式二:面向向量中断管理方式(例:CH32)与针对中断管理有专门优化的内核(例:GD32)
147
148  - 向量中断(可参考ch32)
149
150    在RT-Thread的BSP框架中的board文件夹创建需要的文件,实现下述的两个函数:
151
152    - 在void rt_trigger_software_interrupt(void) 中实现触发软件中断的操作
153
154    - 在void rt_hw_do_after_save_above(void) 中实现触发软件中断之后的工作,通常是清除软件中断置位标志位或类似操作
155
156  - 非向量中断(期望采用原有裸机工程的统一的中断查询与处理函数)
157
158  在RT-Thread的BSP框架中的board文件夹创建一个统一名称的汇编文件:trap_gcc.S,将该文件添加到编译环境即可,此步骤与方式一提供的方法相似,仅在调用中断处理函数以及传递的参数不同,需要根据具体的移植工程实现,方式二下该函数的实现如下:
159
160  示例代码:
161
162  ```assembly
163    #include "cpuport.h"
164
165    	.globl rt_hw_do_after_save_above
166    	.type rt_hw_do_after_save_above,@function
167    rt_hw_do_after_save_above:
168    	addi  sp, sp,  -4 // 移动栈指针
169        STORE ra,  0 * REGBYTES(sp) // 将返回地址寄存器值保存至栈中
170
171        csrr  a0, mscratch// 加载函数入口参数
172        call  trap_entry// 调用中断处理函数
173
174        LOAD  ra,  0 * REGBYTES(sp) // 从栈中恢复返回地址寄存器值
175        addi  sp, sp,  4// 移动栈指针
176        ret	// 返回SW_handler
177  ```
178
179    trap_entry为用户实现的中断源查询分发的函数,在移植时仅需要将该函数名修改为用户的中断查询分发函数即可。
180
181
182
183#### 5.验证
184
185- 创建一个静态线程,在线程中调用RT-Thread提供的与系统时基相关函数接口,例如rt_thread_mdelay,调试观察系统是否可以正常运行
186- 移植RT-Thread的shell进一步验证系统是否移植成功
187
188