1. 前言
RISC-V 体系结构定义了以下寄存器:
通用寄存器 (General Purpose Registers):
x0- 硬编码为零x1- 返回地址 (ra)x2- 栈指针 (sp)x3- 全局指针 (gp)x4- 线程指针 (tp)x5- 临时寄存器 (t0)x6- 临时寄存器 (t1)x7- 临时寄存器 (t2)x8- 保存寄存器/帧指针 (s0/fp)x9- 保存寄存器 (s1)x10- 函数参数/返回值 (a0)x11- 函数参数/返回值 (a1)x12- 函数参数 (a2)x13- 函数参数 (a3)x14- 函数参数 (a4)x15- 函数参数 (a5)x16- 函数参数 (a6)x17- 函数参数 (a7)x18- 保存寄存器 (s2)x19- 保存寄存器 (s3)x20- 保存寄存器 (s4)x21- 保存寄存器 (s5)x22- 保存寄存器 (s6)x23- 保存寄存器 (s7)x24- 保存寄存器 (s8)x25- 保存寄存器 (s9)x26- 保存寄存器 (s10)x27- 保存寄存器 (s11)x28- 临时寄存器 (t3)x29- 临时寄存器 (t4)x30- 临时寄存器 (t5)x31- 临时寄存器 (t6)
浮点寄存器 (Floating Point Registers):
f0-f31- 浮点寄存器
特殊寄存器 (Special Registers):
pc- 程序计数器 (Program Counter)
这些寄存器在 RISC-V 指令集中被广泛使用,用于各种计算和数据存储操作。
2. 汇编指令——sd/ld
在RISC-V汇编语言中,sd和ld是用于存储和加载数据的指令。
sd(Store Doubleword): 将一个64位的寄存器值存储到内存中。语法:
sd source_register, offset(base_register)例如:
sd s0, 8(a0)将寄存器s0的值存储到a0寄存器地址加上偏移量 8 的内存位置。
ld(Load Doubleword): 从内存中加载一个64位的值到寄存器中。语法:
ld destination_register, offset(base_register)例如:
ld s0, 8(a0)从a0寄存器地址加上偏移量 8 的内存位置加载一个值到寄存器s0。
3. 切换函数
3.1 汇编内容
.altmacro
.macro SAVE_SN n
sd s\n, (\n+2)*8(a0)
.endm
.macro LOAD_SN n
ld s\n, (\n+2)*8(a1)
.endm
.section .text
.globl __switch
__switch:
# __switch(
# current_task_cx_ptr: *mut TaskContext,
# next_task_cx_ptr: *const TaskContext
# )
# save kernel stack of current task
sd sp, 8(a0)
# save ra & s0~s11 of current execution
sd ra, 0(a0)
.set n, 0
.rept 12
SAVE_SN %n
.set n, n + 1
.endr
# restore ra & s0~s11 of next execution
ld ra, 0(a1)
.set n, 0
.rept 12
LOAD_SN %n
.set n, n + 1
.endr
# restore kernel stack of next task
ld sp, 8(a1)
ret3.2 文件解析
在 RISC-V 中,a 寄存器(a0 到 a7)用于传递函数参数和返回值,而 s 寄存器(s0 到 s11)用于保存寄存器。虽然 a 寄存器只有 8 个,但 s 寄存器有 12 个(s0 到 s11)。
在代码中,SAVE_SN 和 LOAD_SN 宏用于保存和恢复 s 寄存器。具体来说,SAVE_SN 宏将 s 寄存器的值存储到内存中,而 LOAD_SN 宏从内存中加载 s 寄存器的值。
以下是代码的详细解释:
保存当前任务的上下文:
sd sp, 8(a0):将当前任务的栈指针 (sp) 保存到a0指向的内存地址的偏移量 8 处。sd ra, 0(a0):将返回地址 (ra) 保存到a0指向的内存地址的偏移量 0 处。使用
SAVE_SN宏保存s0到s11寄存器的值到a0指向的内存地址中。
恢复下一个任务的上下文:
ld ra, 0(a1):从a1指向的内存地址的偏移量 0 处加载返回地址 (ra)。使用
LOAD_SN宏从a1指向的内存地址中加载s0到s11寄存器的值。ld sp, 8(a1):从a1指向的内存地址的偏移量 8 处加载栈指针 (sp)。
以下是代码的详细解释:
altmacro
.macro SAVE_SN n
sd s\n, (\n+2)*8(a0) # 将 s 寄存器的值存储到 a0 指向的内存地址的偏移量 (\n+2)*8 处
.endm
.macro LOAD_SN n
ld s\n, (\n+2)*8(a1) # 从 a1 指向的内存地址的偏移量 (\n+2)*8 处加载 s 寄存器的值
.endm
.section .text
.globl __switch
__switch:
# __switch(
# current_task_cx_ptr: *mut TaskContext,
# next_task_cx_ptr: *const TaskContext
# )
# 保存当前任务的内核栈
sd sp, 8(a0)
# 保存当前执行的 ra 和 s0~s11
sd ra, 0(a0)
.set n, 0
.rept 12
SAVE_SN %n
.set n, n + 1
.endr
# 恢复下一个执行的 ra 和 s0~s11
ld ra, 0(a1)
.set n, 0
.rept 12
LOAD_SN %n
.set n, n + 1
.endr
# 恢复下一个任务的内核栈
ld sp, 8(a1)
ret通过使用宏和循环,代码能够有效地保存和恢复所有 12 个 s寄存器的值。