一起学RISC-V汇编第5讲之常用指令及伪指令列表
这一篇介绍一下RISC-V常用的汇编指令,整理成表,便于查阅。
1 RISC-V指令命名
以slt指令为例,如下示意图:大括号{ }内列举了每组指令的所有变体,这些变体通过带下滑线的字母(单独的下划线_表示空字段),从左到右连接带下滑线的字母即可组成完整的指令集,比如slt意思是set less than,相当于是一种缩写,完整语句方便我们快速清晰的理解指令的作用。
下图表示:slt、slti、sltu、sltiu 这4条RVI指令。
指令示意:
$$
\mathrm{\underline{s}et;\underline{l}ess;\underline{t}han}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{i}mmediate}
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{u}nsigned}
\end{aligned}
\right}
$$
注意:后续指令示意中的加粗部分表示RV64指令集,将RV64与RV32放在一起可以看出RV64只需在RV32的基础上加入少数指令:32 位的指令字(word),双字(doubleword)和长字(long)版本。
后续指令列表中使用了一些符号,这里提前进行说明:
符号 | 说明 |
---|---|
寄存器 | 源寄存器rs1, rs2 目的寄存器 rd |
符号扩展 | RV32I/RV64I 的立即数总是进行符号扩展,sign-extend,使用sext()表示 |
内存访问 | 使用M()表示访问某处内存 |
下面列举如下指令集:
- RVI(包括RV32I与RV64I)
- RVM(包括RV32M与RV64M)
- RVFD(包括RV32FD与RV64FD)
- RVA(包括RV32A与RV64A)
2 RVI指令集
2.1 内存操作指令
指令示意:
$$
\left{
\begin{aligned}
& \mathrm{\underline{l}oad} \
& \mathrm{\underline{s}tore}
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{b}yte} \
& \mathrm{\underline{h}alfword} \
& \mathrm{\underline{w}ord} \
& \mathrm{\mathbf{\underline{d}oubleword}} \
\end{aligned}
\right}
$$
$$
\mathrm{\underline{l}oad}
\left{
\begin{aligned}
& \mathrm{\underline{b}yte} \
& \mathrm{\underline{h}alfword} \
& \mathrm{\mathbf{\underline{w}ord}} \
\end{aligned}
\right}
\mathrm{\underline{u}nsigned}
$$
RISC-V中访存指令唯一支持的寻址模式是将12位立即数符号扩展后与寄存器相加,即寄存器相对寻址,后面第7讲RISC-V的寻址模式。
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
lb | I | lb rd,offset(rs1) | 取字节 x[rd] = sext( M[x[rs1] + sext(offset)][7:0] ) 从地址 x[rs1] + sext(offset) 读取 1 字节,符号扩展后写入 x[rd]。 |
lh | I | lh rd,offset(rs1) | 取半字 x[rd] = sext( M[x[rs1] + sext(offset)][15:0] ) 从地址 x[rs1] + sext(offset) 读取 2 字节,符号扩展后写入 x[rd]。 |
lw | I | lw rd,offset(rs1) | 取字 x[rd] = sext( M[x[rs1] + sext(offset)][31:0] ) 从地址 x[rs1] + sext(offset) 读取 4 字节,写入 x[rd],在RV64I中要进行符号扩展。 |
lbu | I | lbu rd,offset(rs1) | 取无符号字节 x[rd] = M[x[rs1] + sext(offset)][7:0] 从地址 x[rs1] + sext(offset) 读取 1 字节,0扩展后写入 x[rd]。 |
lhu | I | lhu rd,offset(rs1) | 取无符号半字 x[rd] = M[x[rs1] + sext(offset)][15:0] 从地址 x[rs1] + sext(offset) 读取 2 字节,0扩展后写入 x[rd]。 |
sb | S | sb rs2,offset(rs1) | 存字节 M[x[rs1] + sext(offset)] = x[rs2][7:0] 将x[rs2]的最低字节写入内存 x[rs1] + sext(offset) 处。 |
sh | S | sh rs2,offset(rs1) | 存半字 M[x[rs1] + sext(offset)] = x[rs2][15:0] 将x[rs2]的最低2字节写入内存 x[rs1] + sext(offset) 处。 |
sw | S | sw rs2,offset(rs1) | 存字 M[x[rs1] + sext(offset)] = x[rs2][31:0] 将x[rs2]的最低4字节写入内存 x[rs1] + sext(offset) 处。 |
RV64I指令:
指令 | 类型 | RV64I | 作用 |
---|---|---|---|
ld | I | ld rd,offset(rs1) | 取双字 x[rd] = M[x[rs1] + sext(offset)][63:0] 从地址 x[rs1] + sext(offset) 读取 8 字节,写入 x[rd]。 |
lwu | I | lwu rd,offset(rs1) | 取无符号字 x[rd] = M[x[rs1] + sext(offset)][31:0] 从地址 x[rs1] + sext(offset) 读取 4 字节,0扩展后写入 x[rd]。 |
sd | S | sd rd,offset(rs1) | 存双字 M[x[rs1] + sext(offset)] = x[rs2][63:0] 将x[rs2]中的8字节写入内存 x[rs1] + sext(offset) 处。 |
2.1 算术指令
指令示意:
$$
\mathrm{\underline{add}}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{i}mmediate}
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\mathbf{\underline{w}ord}}
\end{aligned}
\right}
$$
$$
\mathrm{\underline{sub}tract}
\left{
\begin{aligned}
& _ \
& \mathrm{\mathbf{\underline{w}ord}}
\end{aligned}
\right}
$$
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
add | R | add rd,rs1,rs2 | 加法 x[rd] = x[rs1] + x[rs2] 将 x[rs2] 与 x[rs1] 相加,结果写入 x[rd]。忽略算术溢出。 |
addi | I | addi rd,rs1,imm | 加立即数 x[rd] = x[rs1] + sext(imm) 对 imm 符号扩展后与 x[rs1] 相加,结果写入 x[rd]。忽略算术溢出。 |
sub | R | sub rd,rs1,rs2 | 减法 x[rd] = x[rs1] - x[rs2] 将 x[rs1] 减去 x[rs2],结果写入 x[rd]。忽略算术溢出。 |
注意:RISC-V中没有SUBI指令,RVI的立即数总是进行符号扩展,因此它们也能表示负数,SUBI可以由ADDI来实现(减一个数等于加一个负数),故RVI中无须包含立即数版本的subi指令。
RV64I指令:
指令 | 类型 | 仅RV64I | 作用 |
---|---|---|---|
addw | R | addw rd,rs1,rs2 | 加字 x[rd] = sext( (x[rs1] + x[rs2])[31:0] ) 将 x[rs2] 与 x[rs1] 相加,结果截为 32 位,符号扩展后写入 x[rd],忽略算术溢出。 |
addiw | I | addiw rd,rs1,imm | 加立即数字 x[rd] = sext( (x[rs1] + sext(imm))[31:0] ) 对 imm 符号扩展后与 x[rs1] 相加,结果截为 32 位,符号扩展后写入 x[rd],忽略算术溢出。 |
subw | R | subw rd,rs1,rs2 | 减字 x[rd] = sext( (x[rs1] - x[rs2])[31:0] ) 将 x[rs1] 减去 x[rs2],结果截为 32 位,符号扩展后写入 x[rd]。忽略算术溢出。 |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
mv | mv rd,rs | addi rd,rs,0 | move指令,支持RV32I 和 RV64I x[rd] = x[rs1] 将 x[rs1] 复制到 x[rd] 中。 |
nop | nop | addi x0,x0,0 | 空操作,支持 RV32I 和 RV64I 仅让 pc指向下一条指令。 |
neg | neg rd, rs | sub rd,x0,rs | 取负,支持 RV32I 和 RV64I x[rd] = -x[rs2] 将 x[rs2] 的相反数写入 x[rd]。 |
negw | negw rd, rs | subw rd,rs1,rs2 | 取负字,仅支持 RV64I x[rd] = sext( (-x[rs2])[31:0] ) 将 x[rs2] 的相反数截为 32 位,符号扩展后写入 x[rd]。 |
sext.w | sext.w rd, rs | addiw rd, rs, 0 | 符号扩展字,仅支持 RV64I x[rd] = sext( x[rs1][31:0] ) 将 x[rs1] 低 32 位的符号扩展结果写入 x[rd] |
lui与auipc指令:
注意:有两条指令lui与auipc指令,不好归类,绿卡将其归到算术指令,这里也放到这一类吧。
指令示意:
$$
\mathrm{\underline{l}oad;\underline{u}pper;\underline{i}mmediate}
$$
$$
\mathrm{\underline{a}dd;\underline{u}pper;\underline{i}mmediate;to;\underline{p}c}
$$
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
lui | U | lui rd, imm | 装入高位立即数 x[rd] = sext( imm )<< 12 将 20 位 imm 符号扩展后左移 12 位,低 12 位置零,结果写入 x[rd]。 |
auipc | U | auipc rd, imm | pc 加高位立即数 x[rd] = pc + (sext(imm)<< 12) 对 20 位 imm 符号扩展后左移 12 位,与 pc相加,结果写入 x[rd]。 |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
li | li rd,imm | 多种指令序列 | 装入立即数,支持RV32I 和 RV64I x[rd] = imm 使用尽可能少的指令将常量装入 x[rd]。 在 RV32I 中,它展开为 lui 和 addi组合; 在RV64I 中的展开结果较长:lui, addi, slli, addi, slli, addi, slli, addi |
la | la rd, symbol | 多种指令序列 | 装入地址,支持RV32I 和 RV64I x[rd] = &symbol 将 symbol 的地址装入 x[rd]。 当汇编位置无关代码时,它展开为对全局偏移量表(Global Offset Table)的读入操作:在 RV32I 中展开为 auipc rd, offsetHi 和 addi rd, rd, offsetLo 否则与lla等价 |
lla | lla rd, symbol | auipc rd, symbol[31:12] addi rd, rd, symbol[11:0] |
装入本地地址,支持RV32I 和 RV64I x[rd] = &symbol 将 symbol 的地址装入 x[rd]。展开为 auipc rd, offsetHi 和 addi rd, rd, offsetLo |
2.2 移位指令
指令示意:
$$
\left{
\begin{aligned}
& \mathrm{\underline{s}hift; \underline{l}eft;\underline{l}ogical} \
& \mathrm{\underline{s}hift;\underline{r}ight;\underline{a}rithmetic}\
& \mathrm{\underline{s}hift;\underline{r}ight;\underline{l}ogical}
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{i}mmediate}\
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\mathbf{\underline{w}ord}}
\end{aligned}
\right}
$$
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
sll | R | sll rd,rs1,rs2 | 逻辑左移 x[rd] = x[rs1] << x[rs2] 将 x[rs1] 左移 x[rs2] 位,空位补零,结果写入 x[rd] x[rs2] 的低 5 位(在 RV64I 中是低 6 位)为移位位数,高位忽略。 |
slli | I | slli rd,rs1,shamt | 逻辑左移立即数 x[rd] = x[rs1] << shamt 将 x[rs1] 左移 shamt 位,空位补零,结果写入 x[rd] 在 RV32I 中仅当 shamt[5]=0时该指令合法。 |
srl | R | srl rd,rs1,rs2 | 逻辑右移 x[rd] = x[rs1] >> x[rs2] 将 x[rs1] 右移 x[rs2] 位,空位补零,结果写入 x[rd] x[rs2] 的低 5 位(在 RV64I 中是低 6 位)为移位位数,高位忽略。 |
srli | I | srli rd,rs1,shamt | 逻辑右移立即数 x[rd] = x[rs1] >> shamt 将 x[rs1] 右移 shamt 位,空位补零,结果写入 x[rd] 在 RV32I 中,仅当 shamt[5]=0时该指令合法。 |
sra | R | sra rd,rs1,rs2 | 算术右移 x[rd] = x[rs1] >> x[rs2] 将 x[rs1] 右移 x[rs2] 位,空位用 x[rs1] 的最高位填充,结果写入 x[rd] x[rs2] 的低 5 位(在 RV64I 中是低 6 位)为移位位数,高位忽略。 |
srai | I | srai rd,rs1,shamt | 算术右移立即数 x[rd] = x[rs1] >> shamt 将 x[rs1] 右移 shamt 位,空位用 x[rs1] 的最高位填充,结果写入 x[rd] 在 RV32I 中,仅当 shamt[5]=0时该指令合法。 |
RV64I指令:
指令 | 类型 | RV64I | 作用 |
---|---|---|---|
sllw | R | sllw rd,rs1,rs2 | 逻辑左移字 x[rd] = sext((x[rs1] << x[rs2][4:0])[31:0]) 将 x[rs1] 左移 x[rs2] 位,空位补零,结果写入 x[rd] x[rs2] 的低 5 位为移位位数,高位忽略。 |
slliw | I | slliw rd,rs1,shamt | 逻辑左移立即数字 x[rd] = sext((x[rs1] << shamt)[31:0]) 将 x[rs1] 左移 shamt 位,空位补零,结果截为 32 位,符号扩展后写入 x[rd] 仅当 shamt[5]=0时该指令合法。 |
srlw | R | srlw rd,rs1,rs2 | 逻辑右移字 x[rd] = sext(x[rs1][31:0] >>x[rs2][4:0]) 将 x[rs1] 右移 x[rs2] 位,空位补零,符号扩展后写入 x[rd] x[rs2] 的低 5 位为移位位数,高位忽略。 |
srliw | I | srliw rd,rs1,shamt | 逻辑右移立即数 x[rd] = x[rs1] >> shamt 将 x[rs1] 右移 shamt 位,空位补零,结果写入 x[rd] 在 RV32I 中,仅当 shamt[5]=0时该指令合法。 |
sraw | R | sraw rd,rs1,rs2 | 算术右移字 x[rd] = sext(x[rs1][31:0] >> x[rs2][4:0]) 将 x[rs1] 的低 32 位右移 x[rs2] 位,空位用 x[rs1][31] 填充,将 32 位结果符号扩展后写入 x[rd] x[rs2] 的低 5 位为移位位数,高位忽略。 |
sraiw | I | sraiw rd,rs1,shamt | 算术右移立即字 x[rd] = sext(x[rs1][31:0] >> shamt) 将 x[rs1] 的低 32 位右移 shamt 位,空位用 x[rs1][31] 填充,将 32 位结果符号扩展后写入 x[rd] 仅当 shamt[5]=0时该指令合法。 |
2.3 逻辑指令
指令示意:
$$
\left{
\begin{aligned}
& \mathrm{\underline{and}} \
& \mathrm{\underline{or}} \
& \mathrm{e\underline{x}clusive;\underline{or}} \
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{i}mmediate}
\end{aligned}
\right}
$$
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
xor | R | xor rd,rs1,rs2 | 异或 x[rd] = x[rs1] ^ x[rs2] 将 x[rs1] 和 x[rs2] 按位异或,结果写入 x[rd] |
xori | I | xori rd,rs1,imm | 异或立即数 x[rd] = x[rs1] ^ sext(imm) 将 x[rs1] 和符号扩展后的 imm 按位异或,结果写入 x[rd]。 |
or | R | or rd,rs1,rs2 | 或 x[rd] = x[rs1] | x[rs2] 将 x[rs1] 和 x[rs2] 按位或的结果写入 x[rd] |
ori | I | ori rd,rs1,imm | 或立即数 x[rd] = x[rs1] | sext(imm) 将 x[rs1] 和符号扩展后的 imm 按位或的结果写入 x[rd]。 |
and | R | and rd,rs1,rs2 | 与 x[rd] = x[rs1] & x[rs2] 将 x[rs1] 和 x[rs2] 的按位与结果写入 x[rd] |
andi | I | andi rd,rs1,imm | 与立即数 x[rd] = x[rs1] & sext(imm) 对 imm 符号扩展后和 x[rs1] 进行按位与,结果写入 x[rd]。 |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
not | not rd, rs1 | xori rd, rs1, -1 | 取反,在 RV32I 和 RV64I 中 x[rd]=~x[rs1] 将 x[rs1] 按位取反后写入 x[rd]。 |
neg | neg rd, rs2 | sub rd, x0, rs2 | 取负,在 RV32I 和 RV64I 中 x[rd]=-x[rs2] 将 x[rs2] 的相反数写入 x[rd] |
negw | negw rd, rs2 | subw rd, x0, rs2 | 取负,在 RV64I 中 x[rd] = sext((-x[rs2])[31:0]) 将 x[rs2] 的相反数截为 32 位,符号扩展后写入 x[rd] |
2.4 比较-置位指令
比较-置位指令指令本身并不直接涉及控制流的改变,但它经常配合分支指令使用。
指令示意:
$$
\mathrm{\underline{s}et;\underline{l}ess;\underline{t}han}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{i}mmediate}
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{u}nsigned}
\end{aligned}
\right}
$$
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
slt | R | slt rd,rs1,rs2 | 小于则置位 x[rd] = x[rs1] < x[rs2] 比较 x[rs1] 和 x[rs2](视为补码),若 x[rs1] 更小,则向 x[rd] 写入 1,否则写入 0 |
slti | I | slti rd,rs1,imm | 小于立即数则置位 x[rd] = x[rs1] < sext(imm) 比较 x[rs1] 和符号扩展后的 imm(视为补码),若 x[rs1] 更小,则向 x[rd] 写入 1,否则写入 0 |
sltu | R | sltu rd,rs1,rs2 | 无符号小于则置位 x[rd] = x[rs1] < x[rs2] 比较 x[rs1] 和 x[rs2](视为无符号数),若 x[rs1] 更小,则向 x[rd] 写入 1,否则写入 0 |
sltiu | I | sltiu rd,rs1,imm | 小于无符号立即数则置位 x[rd] = x[rs1] < sext(imm) 比较 x[rs1] 和符号扩展后的 imm(视为无符号数),若 x[rs1] 更小,则向 x[rd] 写入 1,否则写入 0 |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
seqz | seqz rd, rs | sltiu rd, rs, 1 | 等于零时置位,在 RV32I 和 RV64I 中 x[rd] = (x[rs1] == 0) 若 x[rs1] 等于 0,则向 x[rd] 写入 1,否则写入 0 |
snez | snez rd, rs | sltu rd, x0, rs | 不等于零则置位,在 RV32I 和 RV64I 中 x[rd] = (x[rs2] != 0) 若 x[rs1] 不等于 0,则向 x[rd] 写入 1,否则写入 0 |
sltz | sltz rd, rs | slt rd, rs, x0 | 小于0则置位,在 RV32I 和 RV64I 中 x[rd] = x[rs1] < 0 若 x[rs1] 小于0,则向 x[rd] 写入 1,否则写入 0 |
sgtz | sgtz rd, rs | slt rd, x0, rs | 大于0则置位,在 RV32I 和 RV64I 中 x[rd] = x[rs1] > 0 若 x[rs1] 大于0,则向 x[rd] 写入 1,否则写入 0 |
2.5 分支指令
指令示意:
$$
\mathrm{\underline{b}ranch}
\left{
\begin{aligned}
& \mathrm{\underline{eq}ual} \
& \mathrm{\underline{n}ot;\underline{e}qual}
\end{aligned}
\right}
$$
$$
\mathrm{\underline{b}ranch}
\left{
\begin{aligned}
& \mathrm{\underline{g}reater;than;or;\underline{e}qual} \
& \mathrm{\underline{l}ess;\underline{t}han}
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{u}nsigned}
\end{aligned}
\right}
$$
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
beq | B | beq rs1,rs2,offset | 相等时分支 if (rs1 == rs2) pc += sext(offset) 若 x[rs1] 和 x[rs2] 相等,将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bne | B | bne rs1,rs2,offset | 不等时分支 if (rs1 != rs2) pc += sext(offset) 若 x[rs1] 和 x[rs2] 不相等,将 pc 设为当前 pc 加上符号扩展后的 offset。 |
blt | B | blt rs1,rs2,offset | 小于时分支 if (rs1 < rs2) pc += sext(offset) 若 x[rs1] 小于 x[rs2](补码比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bge | B | bge rs1,rs2,offset | 大于等于时分支 if (rs1 >= rs2) pc += sext(offset) 若 x[rs1] 大于等于 x[rs2](补码比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bltu | B | bltu rs1,rs2,offset | 无符号小于时分支 if (rs1 < rs2) pc += sext(offset) 若 x[rs1] 小于 x[rs2](无符号比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bgeu | B | bgeu rs1,rs2,offset | 无符号大于等于时分支 if (rs1 >= rs2) pc += sext(offset) 若 x[rs1] 大于等于 x[rs2](无符号比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
beqz | beqz rs1, offset | beq rs1,x0,offset | 等于0时分支,在 RV32I 和 RV64I 中 if (rs1 == 0) pc += sext(offset) 若 x[rs1] 等于0,将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bnez | bnez rs1, offset | bne rs1,x0,offset | 不等于0时分支,在 RV32I 和 RV64I 中 if (rs1 != 0) pc += sext(offset) 若 x[rs1] 等于0,将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bgez | bgez rs1, offset | bge rs1, x0, offset | 大于等于0时分支,在 RV32I 和 RV64I 中 if (rs1 >=0) pc += sext(offset) 若 x[rs1] 大于等于0,将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bgt | bgt rs1, rs2, offset | blt rs2, rs1, offset | 大于时分支,在 RV32I 和 RV64I 中 if (rs1 > rs2) pc += sext(offset) 若 x[rs1] 大于x[rs2],(补码比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bgtu | bgtu rs1, rs2, offset | bltu rs2, rs1, offset | 无符号大于时分支,在 RV32I 和 RV64I 中 if (rs1 > rs2) pc += sext(offset) 若 x[rs1] 大于x[rs2](无符号比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bgtz | bgtz rs2, offset | blt x0, rs2, offset | 大于0时分支,在 RV32I 和 RV64I 中 if (rs2 > 0) pc += sext(offset) 若x[rs2] 大于0,(补码比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
ble | ble rs1, rs2, offset | bge rs2, rs1, offset | 小于等于时分支,在 RV32I 和 RV64I 中 if (rs1 <= rs2) pc += sext(offset) 若 x[rs1] 小于等于 x[rs2](补码比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bleu | bleu rs1, rs2, offset | bgeu rs2, rs1, offset | 无符号小于等于时分支,在 RV32I 和 RV64I 中 if (rs1 <= rs2) pc += sext(offset) 若 x[rs1] 小于等于 x[rs2](无符号比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
blez | blez rs2, offset | bge x0, rs2, offset | 小于等于0时分支,在 RV32I 和 RV64I 中 if (rs2 <= 0) pc += sext(offset) 若 x[rs2] 小于0(补码比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
bltz | bltz rs1, offset | blt rs1,x0,offset | 小于0时分支,在 RV32I 和 RV64I 中 if (rs1 < 0) pc += sext(offset) 若 x[rs1] 小于0(补码比较),将 pc 设为当前 pc 加上符号扩展后的 offset。 |
2.6 跳转指令
指令示意:
$$
\mathrm{\underline{j}ump;\underline{a}nd;\underline{l}ink}
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\underline{r}egister}
\end{aligned}
\right}
$$
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
jal | J | jal rd, offset | 跳转并链接 x[rd] = pc+4; pc += sext(offset) 将下一条指令的地址(pc+4)写入 x[rd],然后将 pc 设为当前 pc 加上符号扩展后的 |
jalr | I | jalr rd,offset(rs1) | 寄存器跳转并链接 t=pc+4; pc=(x[rs1]+sext(offset))&~1; x[rd]=t 将 pc 设为 x[rs1] + sext(offset),将跳转地址的最低位清零,并将原 pc+4 写入 x[rd]。若省略 rd,则默认为 x1 |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
j | j offset | jal x0, offset | 跳转,在 RV32I 和 RV64I 中 pc += sext(offset) 将 pc 设为当前 pc 加上符号扩展后的 offset。 |
jr | jr rs1 | jalr x0, 0(rs1) | 寄存器跳转,在 RV32I 和 RV64I 中 pc = x[rs1] 将 pc 设为 x[rs1]。展开为 jalr x0, 0(rs1) |
ret | ret | jalr x0, 0(x1) | 从子过程返回,在 RV32I 和 RV64I 中 pc = x[1] 从子过程返回 |
2.7 同步指令
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
fence | I | fence pred, succ | 内存和 I/O 屏障 Fence(pred, succ) |
fence.i | I | fence.i | 指令流屏障 Fence(Store, Fetch) 使内存指令区域的写入对后续取指操作可见 |
2.8 环境指令
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
ecall | I | ecall | 环境调用 RaiseException(EnvironmentCall) 通过抛出环境调用异常调用执行环境 |
ebreak | I | ebreak | 环境断点 RaiseException(Breakpoint) 通过抛出断点异常调用调试器 |
2.9 控制状态寄存器指令
指令示意:
$$
\mathrm{\underline{c}ontrol;\underline{s}tatus;\underline{r}egister}
\left{
\begin{aligned}
& \mathrm{\underline{r}ead;& ;\underline{c}lear;bit} \
& \mathrm{\underline{r}ead;& ;\underline{s}et;bit}\
& \mathrm{\underline{r}ead;& ;\underline{w}rite}
\end{aligned}
\right}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{i}mmediate}\
\end{aligned}
\right}
$$
RV32I/RV64I指令:
指令 | 类型 | RV32I/RV64I | 作用 |
---|---|---|---|
csrrw | I | csrrw rd, csr, rs1 | 控制状态寄存器读后写 t = CSRs[csr]; CSRs[csr] = x[rs1]; x[rd] = t 记控制状态寄存器 csr 的值为 t。将 x[rs1] 写入 csr,再将 t 写入 x[rd]。 |
csrrs | I | csrrs rd, csr, rs1 | 控制状态寄存器读后置位 t = CSRs[csr]; CSRs[csr] = t | x[rs1]; x[rd] = t 记控制状态寄存器 csr 的值为 t。将 t 和 x[rs1] 的按位或结果写入 csr,再将 t 写入x[rd]。 |
csrrc | I | csrrc rd, csr, rs1 | 控制状态寄存器读后清位 t = CSRs[csr]; CSRs[csr] = t &∼x[rs1]; x[rd] = t 记控制状态寄存器 csr 的值为 t。将 x[rs1] 的反码和 t 按位与,结果写入 csr,再将 t 写入 x[rd]。 |
csrrwi | I | csrrwi rd, csr, zimm[4:0] | 控制状态寄存器读后写立即数 x[rd] = CSRs[csr]; CSRs[csr] = zimm 将控制状态寄存器 csr 的值复制到 x[rd] 中,再将 5 位立即数 zimm 的零扩展结果写入csr |
csrrsi | I | csrrsi rd, csr, zimm[4:0] | 控制状态寄存器读后置位立即数 t = CSRs[csr]; CSRs[csr] = t | zimm; x[rd] = t 记控制状态寄存器 csr 的值为 t。将 5 位立即数 zimm 零扩展后,和 t 按位或,结果写入 csr,再将 t 写入 x[rd](csr 中的第 5 及更高的位不变) |
csrrci | I | csrrci rd, csr, zimm[4:0] | 控制状态寄存器读后清位立即数 t = CSRs[csr]; CSRs[csr] = t &∼zimm; x[rd] = t 记控制状态寄存器 csr 的值为 t。将 5 位立即数 zimm 零扩展后的反码和 t 按位与结果写入 csr,再将 t 写入 x[rd](csr 中的第 5 及更高的位不变) |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
csrc | csrc csr, rs1 | csrrc x0, csr, rs1 | 控制状态寄存器清位,在RV32I和RV64I中 CSRs[csr] &= ∼x[rs1] 对于 x[rs1] 中每一个为 1 的位,将控制状态寄存器 csr 的对应位清零 |
csrci | csrci csr, zimm[4:0] | csrrci x0, csr, zimm | 控制状态寄存器清位立即数,在RV32I和RV64I中 CSRs[csr] &= ∼zimm 对于 5 位立即数零扩展后中每一个为 1 的位,将控制状态寄存器 csr 的对应位清零 |
csrr | csrr rd, csr | csrrs rd, csr, x0 | 控制状态寄存器读,在RV32I和RV64I中 x[rd] = CSRs[csr] 将控制状态寄存器 csr 写入 x[rd] |
csrs | csrs csr, rs1 | csrrs x0, csr, rs1 | 控制状态寄存器置位,在RV32I和RV64I中 CSRs[csr] |= x[rs1] 对于 x[rs1] 中每一个为 1 的位,将控制状态寄存器 csr 的对应位置 1 |
csrsi | csrsi csr, zimm[4:0] | csrrsi x0, csr, zimm | 控制状态寄存器置位立即数,在RV32I和RV64I中 CSRs[csr] |= zimm 对于 5 位立即数 zimm 的零扩展结果中每一个为 1 的位,将控制状态寄存器 csr 的对应位置 1 |
csrw | csrw csr, rs1 | csrrw x0, csr, rs1 | 控制状态寄存器写,在RV32I和RV64I中 CSRs[csr] = x[rs1] 将 x[rs1] 的值写入控制状态寄存器 csr |
csrwi | csrwi csr, zimm[4:0] | csrrwi x0, csr,zimm | 控制状态寄存器写立即数,在RV32I和RV64I中 CSRs[csr] = zimm 将 5 位立即数 zimm 的零扩展结果写入控制状态寄存器 csr |
3 RVM指令集
指令示意:
$$
\mathrm{\underline{mul}tiply}
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\mathbf{\underline{w}ord}}
\end{aligned}
\right}
$$
$$
\mathrm{\underline{mul}tiply;\underline{h}igh}
\left{
\begin{aligned}
& _ \
& \mathrm{\underline{u}nsigned} \
& \mathrm{\underline{s}igned;\underline{u}nsigned}
\end{aligned}
\right}
$$
$$
\left{
\begin{aligned}
& \mathrm{\underline{div}ide} \
& \mathrm{\underline{rem}ainder}
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\underline{u}nsigned}
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\mathbf{\underline{w}ord}}
\end{aligned}
\right}
$$
RV32M/RV64M指令:
指令 | 类型 | RV32M/RV64M | 作用 |
---|---|---|---|
mul | R | mul rd, rs1, rs2 | 乘 x[rd] = x[rs1] × x[rs2] 将 x[rs2] 与 x[rs1] 相乘,乘积写入 x[rd]。忽略算术溢出。 |
mulh | R | mulh rd, rs1, rs2 | 高位乘 x[rd] = (x[rs1] × x[rs2]) >> XLEN 将 x[rs2] 与 x[rs1] 视为补码并相乘,乘积的高位写入 x[rd]。 |
mulhsu | R | mulhsu rd, rs1, rs2 | 高位有符号-无符号乘 x[rd] = (x[rs1] × x[rs2]) >> XLEN 将 x[rs1](视为补码)与 x[rs1](视为无符号数)相乘,乘积的高位写入 x[rd] |
mulhu | R | mulhu rd, rs1, rs2 | 高位有无符号乘 x[rd] = (x[rs1] × x[rs2]) >> XLEN 将 x[rs1]与 x[rs1](视为无符号数)相乘,乘积的高位写入x[rd] |
div | R | div rd, rs1, rs2 | 除 x[rd] = x[rs1] ÷ x[rs2] x[rs1] 除以 x[rs2](补码除法),结果向零舍入,将商写入 x[rd] |
divu | R | divu rd, rs1, rs2 | 无符号除 x[rd] = x[rs1] ÷ x[rs2] x[rs1] 除以 x[rs2](无符号除法),结果向零舍入,将商写入 x[rd] |
rem | R | rem rd, rs1, rs2 | 求余数 x[rd] = x[rs1] % x[rs2] 将 x[rs1] 和 x[rs2] 视为补码并相除,向 0 舍入,将余数写入 x[rd]。 |
remu | R | remu rd, rs1, rs2 | 求无符号余数 x[rd] = x[rs1] % x[rs2] 将 x[rs1] 和 x[rs2] 视为无符号数并相除,向 0 舍入,将余数写入 x[rd]。 |
RV64M指令:
指令 | 类型 | RV64M | 作用 |
---|---|---|---|
mulw | R | mulw rd, rs1, rs2 | 乘字 x[rd] = sext((x[rs1] × x[rs2])[31:0]) 将 x[rs2] 与 x[rs1] 相乘,乘积截为 32 位,符号扩展后写入 x[rd]。忽略算术溢出。 |
divw | R | divw rd, rs1, rs2 | 除字 x[rd] = sext(x[rs1][31:0] ÷ x[rs2][31:0]) x[rs1] 的低 32 位除以 x[rs2] 的低 32 位(补码除法),结果向零舍入,将 32 位商的符号扩展结果写入 x[rd]。 |
divuw | R | divuw rd, rs1, rs2 | 无符号除字 x[rd] = sext(x[rs1][31:0] ÷ x[rs2][31:0])) x[rs1] 的低 32 位除以 x[rs2] 的低 32 位(无符号除法),结果向零舍入,将 32 位商的符号扩展结果写入 x[rd]。 |
remw | R | remw rd, rs1, rs2 | 求余数字 x[rd] = sext(x[rs1][31:0] % x[rs2][31:0]) 将 x[rs1] 和 x[rs2] 的低 32 位视为补码并相除,向 0 舍入,将 32 位余数符号扩展后写入 x[rd] |
remuw | R | remuw rd, rs1, rs2 | 求余数字 x[rd] = sext(x[rs1][31:0] % x[rs2][31:0]) 将 x[rs1] 和 x[rs2] 的低 32 位视为无符号数并相除,向 0 舍入,将 32 位余数符号扩展后写入 x[rd] |
4 RVFD指令集
4.1 访存指令
用于浮点数的取数存数操作。
指令示意:
$$
\mathrm{\underline{f}loat}
\left{
\begin{aligned}
& \mathrm{\underline{l}oad} \
& \mathrm{\underline{s}tore}
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{w}ord} \
& \mathrm{\underline{d}oubleword} \
\end{aligned}
\right}
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
flw | I | flw rd, offset(rs1) | 取浮点字 f[rd] = M[x[rs1] + sext(offset)][31:0] 从内存地址 x[rs1] + sext(offset) 中读取单精度浮点数,并写入 f[rd] |
fsw | S | fsw rs2, offset(rs1) | 存浮点字 M[x[rs1] + sext(offset)] = f[rs2][31:0] 将 f[rs2] 中的单精度浮点数写入内存地址 x[rs1] + sext(offset) 中 |
RV32D/RV64D指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
fld | I | fld rd, offset(rs1) | 取浮点双字 f[rd] = M[x[rs1] + sext(offset)][63:0] 从内存地址 x[rs1] + sext(offset) 中读取双精度浮点数,并写入 f[rd] |
fsd | S | fsd rs2, offset(rs1) | 存浮点双字 M[x[rs1] + sext(offset)] = f[rs2][63:0] 将 f[rs2] 中的双精度浮点数写入内存地址 x[rs1] + sext(offset) 中 |
4.2 RVFD 算术指令
指令示意:
$$
\mathrm{\underline{f}loat}
\left{
\begin{aligned}
& \mathrm{\underline{add}} \
& \mathrm{\underline{sub}tract} \
& \mathrm{\underline{mul}tiply} \
& \mathrm{\underline{div}ide} \
& \mathrm{\underline{sq}uare\ \underline{r}oo\underline{t}} \
& \mathrm{\underline{min}imum} \
& \mathrm{\underline{max}imum}
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{.s}ingle} \
& \mathrm{\underline{.d}ouble} \
\end{aligned}
\right}
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
fadd.s | R | fadd.s rd, rs1, rs2 | 单精度浮点加 f[rd] = f[rs1] + f[rs2] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相加,将舍入后的单精度结果写入 f[rd] |
fsub.s | R | fsub.s rd, rs1, rs2 | 单精度浮点减 f[rd] = f[rs1] - f[rs2] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相减,将舍入后的单精度结果写入 f[rd] |
fmul.s | R | fmul.s rd, rs1, rs2 | 单精度浮点乘 f[rd] = f[rs1] × f[rs2] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相乘,将舍入后的单精度结果写入 f[rd]。 |
fdiv.s | R | fdiv.s rd, rs1, rs2 | 单精度浮点除 f[rd] = f[rs1] ÷ f[rs2] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相除,并将舍入后的商写入 f[rd]。 |
fsqrt.s | R | fsqrt.s rd, rs1 | 单精度浮点求平方根 f[rd] = √ f[rs1] 计算 f[rs1] 中的单精度浮点数的平方根,将舍入后的单精度结果写入 f[rd] |
fmin.s | R | fmin.s rd, rs1, rs2 | 单精度浮点最小值 f[rd] = min(f[rs1], f[rs2]) 将 f[rs1] 和 f[rs2] 中的单精度浮点数较小者写入 f[rd] |
fmax.s | R | fmax.s rd, rs1, rs2 | 单精度浮点最大值 f[rd] = max(f[rs1], f[rs2]) 将 f[rs1] 和 f[rs2] 中的单精度浮点数较大者写入 f[rd]。 |
RV32D/RV64D指令:
指令 | 类型 | RV32D/RV64D | 作用 |
---|---|---|---|
fadd.d | R | fadd.d rd, rs1, rs2 | 双精度浮点加 f[rd] = f[rs1] + f[rs2] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相加,将舍入后的双精度结果写入 f[rd]。 |
fsub.d | R | fsub.d rd, rs1, rs2 | 双精度浮点减 f[rd] = f[rs1] - f[rs2] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相减,将舍入后的双精度结果写入 f[rd] |
fmul.d | R | fmul.d rd, rs1, rs2 | 双精度浮点乘 f[rd] = f[rs1] × f[rs2] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相乘,将舍入后的双精度结果写入 f[rd]。 |
fdiv.d | R | fdiv.d rd, rs1, rs2 | 双精度浮点除法 f[rd] = f[rs1] ÷ f[rs2] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相除,并将舍入后的商写入 f[rd]。 |
fsqrt.d | R | fsqrt.d rd, rs1 | 双精度浮点求平方根 f[rd] = √ f[rs1] 计算 f[rs1] 中的双精度浮点数的平方根,将舍入后的双精度结果写入 f[rd]。 |
fmin.d | R | fmin.d rd, rs1, rs2 | 双精度浮点最小值 f[rd] = min(f[rs1], f[rs2]) 将 f[rs1] 和 f[rs2] 中的双精度浮点数较小者写入 f[rd] |
fmax.d | R | fmax.d rd, rs1, rs2 | 双精度浮点最大值 f[rd] = max(f[rs1], f[rs2]) 将 f[rs1] 和 f[rs2] 中的双精度浮点数较大者写入 f[rd] |
4.3 RVFD 乘加指令
指令示意:
$$
\mathrm{\underline{f}loat}
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\underline{n}egative}
\end{aligned}
\right}
\mathrm{\underline{m}ultiply}
\left{
\begin{aligned}
& \mathrm{\underline{add}} \
& \mathrm{\underline{sub}tract}
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{.s}ingle} \
& \mathrm{\underline{.d}ouble} \
\end{aligned}
\right}
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
fmadd.s | R4 | fmadd.s rd, rs1, rs2, rs3 | 单精度浮点乘加 f[rd] = f[rs1]×f[rs2]+f[rs3] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相乘,并将未舍入的积与 f[rs3] 中的单精度浮点数相加,将舍入后的单精度结果写入 f[rd] |
fmsub.s | R4 | fmsub.s rd, rs1, rs2, rs3 | 单精度浮点乘减 f[rd] = f[rs1]×f[rs2]-f[rs3] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相乘,并将未舍入的积与 f[rs3] 中的单精度浮点数相减,将舍入后的单精度结果写入 f[rd] |
fnmadd.s | R4 | fnmadd.s rd, rs1, rs2, rs3 | 单精度浮点乘加取负 f[rd] = -f[rs1]×f[rs2]-f[rs3] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相乘,结果取负,并将未舍入的积与 f[rs3] 中的单精度浮点数相减,将舍入后的单精度结果写入 f[rd]。 |
fnmsub.s | R4 | fnmsub.s rd, rs1, rs2, rs3 | 单精度浮点乘减取负 f[rd] = -f[rs1]×f[rs2]+f[rs3] 将 f[rs1] 与 f[rs2] 中的单精度浮点数相乘,结果取负,并将未舍入的积与 f[rs3] 中的单精度浮点数相加,将舍入后的单精度结果写入 f[rd] |
RV32D/RV64D指令:
指令 | 类型 | RV32D/RV64D | 作用 |
---|---|---|---|
fmadd.d | R4 | fmadd.d rd, rs1, rs2, rs3 | 双精度浮点乘加 f[rd] = f[rs1]×f[rs2]+f[rs3] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相乘,并将未舍入的积与 f[rs3] 中的双精度浮点数相加,将舍入后的双精度结果写入 f[rd] |
fmsub.d | R4 | fmsub.d rd, rs1, rs2, rs3 | 双精度浮点乘减 f[rd] = f[rs1]×f[rs2]-f[rs3] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相乘,并将未舍入的积与 f[rs3] 中的双精度浮点数相减,将舍入后的双精度结果写入 f[rd]。 |
fnmadd.d | R4 | fnmsub.d rd, rs1, rs2, rs3 | 双精度浮点乘减取负 f[rd] = -f[rs1]×f[rs2]-f[rs3] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相乘,结果取负,并将未舍入的积与 f[rs3] 中的双精度浮点数相减,将舍入后的双精度结果写入 f[rd]。 |
fnmsub.d | R4 | fnmsub.d rd, rs1, rs2, rs3 | 双精度浮点乘减取负 f[rd] = -f[rs1]×f[rs2]+f[rs3] 将 f[rs1] 与 f[rs2] 中的双精度浮点数相乘,结果取负,并将未舍入的积与 f[rs3] 中的双精度浮点数相加,将舍入后的双精度结果写入 f[rd] |
4.4 RVFD传送指令
用于标量寄存器与浮点寄存器之间数据mv操作。
指令示意:
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{m}o\underline{v}e}\
\mathrm{to}\
\mathrm{\underline{.s}ingle} \
\mathrm{from}\
\mathrm{\underline{.x}\ register} \
$$
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{m}o\underline{v}e}\
\mathrm{to}\
\mathrm{\underline{.x}\ register} \
\mathrm{from}\
\mathrm{\underline{.s}ingle} \
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
fmv.w.x | R | fmv.w.x rd, rs1 | 从整数传送字到浮点 f[rd] = x[rs1][31:0] 将 x[rs1] 中的单精度浮点数复制到 f[rd]。 |
fmv.x.w | R | fmv.x.w rd, rs1 | 从浮点传送字到整数 x[rd] = sext(f[rs1][31:0]) 将 f[rs1] 中的单精度浮点数复制到 x[rd],在 RV64F 中额外对结果进行符号扩展 |
RV64D指令:
指令 | 类型 | RV64D | 作用 |
---|---|---|---|
fmv.d.x | R | fmv.d.x rd, rs1 | 从整数传送双字到浮点 f[rd] = x[rs1][63:0] 将 x[rs1] 中的双精度浮点数复制到 f[rd] |
fmv.x.d | R | fmv.x.d rd, rs1 | 从浮点传送双字到整数 x[rd] = f[rs1][63:0] 将 f[rs1] 中的双精度浮点数复制到 x[rd]。 |
4.5 RVFD 转换指令
指令示意:
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{c}on\underline{v}er\underline{t}\ to}
\left{
\begin{aligned}
& \mathrm{\underline{.s}ingle} \
& \mathrm{\underline{.d}ouble} \
\end{aligned}
\right}
\mathrm{from}\
\mathrm{\underline{.w}ord} \
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\underline{u}nsigned}
\end{aligned}
\right}
$$
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{c}on\underline{v}er\underline{t} \ to\ \underline{.w}ord}
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\underline{u}nsigned}
\end{aligned}
\right}
\mathrm{from}
\left{
\begin{aligned}
& \mathrm{\underline{.s}ingle} \
& \mathrm{\underline{.d}ouble} \
\end{aligned}
\right}
$$
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{c}on\underline{v}er\underline{t} \ to\ \underline{.s}ingle}
\mathrm{from} \
\mathrm{\underline{.d}ouble}
$$
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{c}on\underline{v}er\underline{t} \ to\ \underline{.d}ouble}
\mathrm{from} \
\mathrm{\underline{.s}ingle}
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F | 作用 |
---|---|---|---|
fcvt.s.w | R | fcvt.s.w rd, rs1 | 字转换为单精度浮点 f[rd] = f32(x[rs1]) 将 x[rs1] 中的 32 位有符号整数(补码表示)转换为单精度浮点数,并写入 f[rd] |
fcvt.w.s | R | fcvt.w.s rd, rs1 | 单精度浮点转换为字 x[rd] = sext(s32(f[rs1])) 将 f[rs1] 中的单精度浮点数转换为 32 位有符号整数(补码表示),符号扩展后写入x[rd]。 |
fcvt.s.wu | R | fcvt.s.wu rd, rs1 | 无符号字转换为单精度浮点 f[rd] = f32(x[rs1]) 将 x[rs1] 中的 32 位无符号整数转换为单精度浮点数,并写入 f[rd]。 |
fcvt.wu.s | R | fcvt.wu.s rd, rs1 | 单精度浮点转换为无符号字 x[rd] = sext(u32(f[rs1])) 将 f[rs1] 中的单精度浮点数转换为 32 位无符号整数,符号扩展后写入 x[rd] |
RV32D/RV64D指令:
指令 | 类型 | RV64D | 作用 |
---|---|---|---|
fcvt.s.d | R | fcvt.s.d rd, rs1 | 双精度转换为单精度浮点 f[rd] = f32(f[rs1]) 将 f[rs1] 中的双精度浮点数转换为单精度浮点数,并写入 f[rd]。 |
fcvt.d.s | R | fcvt.d.s rd, rs1 | 单精度浮点转换为双精度浮点 f[rd] = f64(f[rs1]) 将 f[rs1] 中的单精度浮点数转换为双精度浮点数,并写入 f[rd] |
fcvt.d.w | R | fcvt.d.w rd, rs1 | 字转换为双精度浮点 f[rd] = f64(x[rs1]) 将 x[rs1] 中的 32 位有符号整数(补码表示)转换为双精度浮点数,并写入 f[rd] |
fcvt.w.d | R | fcvt.w.d rd, rs1 | 双精度浮点转换为字 x[rd] = sext(s32(f[rs1])) 将 f[rs1] 中的双精度浮点数转换为 32 位有符号整数(补码表示),符号扩展后写入x[rd]。 |
fcvt.d.wu | R | fcvt.d.wu rd, rs1 | 无符号字转换为双精度浮点 f[rd] = f64(x[rs1]) 将 x[rs1] 中的 32 位无符号整数转换为双精度浮点数,并写入 f[rd] |
fcvt.wu.d | R | fcvt.wu.d rd, rs1 | 双精度浮点转换为无符号字 x[rd] = sext(u32(f[rs1])) 将 f[rs1] 中的双精度浮点数转换为 32 位无符号整数,符号扩展后写入 x[rd] |
RV64F指令:
指令 | 类型 | RV64F | 作用 |
---|---|---|---|
fcvt.l.s | R | fcvt.l.s rd, rs1 | 单精度浮点转换为长字 x[rd] = s64(f[rs1]) 将 f[rs1] 中的单精度浮点数转换为 64 位有符号整数(补码表示),并写入 x[rd] |
fcvt.s.l | R | fcvt.s.l rd, rs1 | 长字转换为单精度浮点转换 f[rd] = f32(x[rs1]) 将 x[rs1] 中的 64 位有符号整数(补码表示)转换为单精度浮点数,并写入 f[rd]。 |
fcvt.s.lu | R | fcvt.s.lu rd, rs1 | 无符号长字转换为单精度浮点 f[rd] = f32(x[rs1]) 将 x[rs1] 中的 64 位无符号整数转换为单精度浮点数,并写入 f[rd]。 |
fcvt.lu.s | R | fcvt.lu.s rd, rs1 | 单精度浮点转换为无符号长字 x[rd] = u64f32(f[rs1]) 将 f[rs1] 中的单精度浮点数转换为 64 位无符号整数,并写入 x[rd]。 |
RV64D指令:
指令 | 类型 | RV64D | 作用 |
---|---|---|---|
fcvt.d.l | R | fcvt.d.l rd, rs1 | 长字转换为双精度浮点 f[rd] = f64(x[rs1]) 将 x[rs1] 中的 64 位有符号整数(补码表示)转换为双精度浮点数,并写入 f[rd] |
fcvt.l.d | R | fcvt.l.d rd, rs1 | 双精度浮点转长字 x[rd] = s64(f[rs1]) 将 f[rs1] 中的双精度浮点数转换为 64 位有符号整数(补码表示),并写入 x[rd]。 |
fcvt.d.lu | R | fcvt.d.lu rd, rs1 | 无符号长字转换为双精度浮点 f[rd] = f64(x[rs1]) 将 x[rs1] 中的 64 位无符号整数转换为双精度浮点数,并写入 f[rd]。 |
fcvt.lu.d | R | fcvt.lu.d rd, rs1 | 双精度浮点转换为无符号长字 x[rd] = u64(f[rs1]) 将 f[rs1] 中的双精度浮点数转换为 64 位无符号整数,并写入 x[rd]。 |
4.6 RVFD 符号注入指令
指令示意:
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{s}i\underline{gn} \ in\underline{j}ection}
\left{
\begin{aligned}
& \mathrm{_} \
& \mathrm{\underline{n}egative} \
& \mathrm{e\underline{x}clusive \ or} \
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{.s}ingle} \
& \mathrm{\underline{.d}ouble} \
\end{aligned}
\right}
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
fsgnj.s | R | fsgnj.s rd, rs1, rs2 | 单精度浮点符号注入 f[rd] = {f[rs2][31], f[rs1][30:0]} 用 f[rs1] 的阶码和尾数,以及 f[rs2] 的符号位,组成一个新的单精度浮点数,并将其写入 f[rd]。 |
fsgnjn.s | R | fsgnjn.s rd, rs1, rs2 | 单精度浮点符号取反注入 f[rd] = {∼f[rs2][31], f[rs1][30:0]} 用 f[rs1] 的阶码和尾数,以及 f[rs2] 的符号位取反,组成一个新的单精度浮点数,并将其写入 f[rd] |
fsgnjx.s | R | fsgnjx.s rd, rs1, rs2 | 单精度浮点符号异或注入 f[rd] = {f[rs1][31] ˆ f[rs2][31], f[rs1][30:0]} 用 f[rs1] 的阶码和尾数,以及 f[rs1] 和 f[rs2] 符号位的异或,组成一个新的单精度浮点数,并将其写入 f[rd] |
RV32D/RV64D指令:
指令 | 类型 | RV32D/RV64D | 作用 |
---|---|---|---|
fsgnj.d | R | fsgnj.d rd, rs1, rs2 | 双精度浮点符号注入 f[rd] = {f[rs2][63], f[rs1][62:0]} 用 f[rs1] 的阶码和尾数,以及 f[rs2] 的符号位,组成一个新的双精度浮点数,并将其写入 f[rd] |
fsgnjn.d | R | fsgnjn.d rd, rs1, rs2 | 双精度浮点符号取反注入 f[rd] = {∼f[rs2][63], f[rs1][62:0]} 用 f[rs1] 的阶码和尾数,以及 f[rs2] 的符号位取反,组成一个新的双精度浮点数,并将其写入 f[rd] |
fsgnjx.d | R | fsgnjx.d rd, rs1, rs2 | 双精度浮点符号异或注入 f[rd] = {f[rs1][63] ˆ f[rs2][63], f[rs1][62:0]} 用 f[rs1] 的阶码和尾数,以及 f[rs1] 和 f[rs2] 符号位的异或,组成一个新的双精度浮点数,并将其写入 f[rd]。 |
伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
fabs.s | fabs.s rd, rs1 | fsgnjx.s rd, rs1, rs1 | 单精度浮点数绝对值,在RV32F/RV64F中 f[rd] = |f[rs1]| 将双精度浮点数 f[rs1] 的绝对值写入 f[rd] |
fmv.s | fmv.s rd, rs1 | fsgnj.s rd, rs1, rs1 | 单精度浮点数据传送,在RV32F/RV64F中 f[rd] = f[rs1] 将双精度浮点数 f[rs1] 复制到 f[rd] 中 |
fneg.s | fneg.s rd, rs1 | fsgnjn.s rd, rs1, rs1 | 单精度浮点取负,在RV32F/RV64F中 f[rd] = -f[rs1] 将双精度浮点数 f[rs1] 取负后写入 f[rd] |
fabs.d | fabs.d rd, rs1 | fsgnjx.d rd, rs1, rs1 | 双精度浮点数绝对值,在RV32D/RV64D中 f[rd] = |f[rs1]| 将双精度浮点数 f[rs1] 的绝对值写入 f[rd] |
fmv.d | fmv.d rd, rs1 | fsgnj.d rd, rs1, rs1 | 双精度浮点数据传送,在RV32D/RV64D中 f[rd] = f[rs1] 将双精度浮点数 f[rs1] 复制到 f[rd] 中 |
fneg.d | fneg.d rd, rs1 | fsgnjn.d rd, rs1, rs1 | 双精度浮点取负,在RV32D/RV64D中 f[rd] = -f[rs1] 将双精度浮点数 f[rs1] 取负后写入 f[rd] |
4.7 RVFD 比较指令
指令示意:
$$
\mathrm{compare} \
\mathrm{\underline{f}loat}\
\left{
\begin{aligned}
& \mathrm{\underline{eq}uals} \
& \mathrm{\underline{l}ess \ \underline{t}han} \
& \mathrm{\underline{l}ess \ \underline{t}han \ or \ \underline{eq}uals} \
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{.s}ingle} \
& \mathrm{\underline{.d}ouble} \
\end{aligned}
\right}
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
feq.s | R | feq.s rd, rs1, rs2 | 单精度浮点相等 x[rd] = (f[rs1] == f[rs2]) 若 f[rs1] 和 f[rs2] 中的单精度浮点数相等,则向 x[rd] 写 1,否则写 0 |
flt.s | R | fle.s rd, rs1, rs2 | 单精度浮点小于 x[rd] = (f[rs1] < f[rs2]) 若 f[rs1] 中的单精度浮点数小于 f[rs2],则向 x[rd] 中写 1,否则写 0 |
fle.s | R | fle.s rd, rs1, rs2 | 单精度浮点小于等于 x[rd] = (f[rs1] ≤ f[rs2]) 若 f[rs1] 中的单精度浮点数小于等于 f[rs2],则向 x[rd] 中写 1,否则写 0。 |
RV32D/RV64D指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
feq.d | R | feq.d rd, rs1, rs2 | 双精度浮点相等 x[rd] = (f[rs1] == f[rs2]) 若 f[rs1] 和 f[rs2] 中的双精度浮点数相等,则向 x[rd] 写 1,否则写 0 |
flt.d | R | flt.d rd, rs1, rs2 | 双精度浮点小于 x[rd] = (f[rs1] < f[rs2]) 若 f[rs1] 中的双精度浮点数小于 f[rs2],则向 x[rd] 写 1,否则写 0 |
fle.d | R | fle.d rd, rs1, rs2 | 双精度浮点小于等于 x[rd] = (f[rs1] ≤ f[rs2]) 若 f[rs1] 中的双精度浮点数小于等于 f[rs2],则向 x[rd] 中写 1,否则写 0。 |
4.8 RVFD 分类指令
指令示意:
$$
\mathrm{\underline{f}loat}\
\mathrm{\underline{class}ify}\
\left{
\begin{aligned}
& \mathrm{\underline{.s}ingle} \
& \mathrm{\underline{.d}ouble} \
\end{aligned}
\right}
$$
RV32F/RV64F指令:
指令 | 类型 | RV32F/RV64F | 作用 |
---|---|---|---|
fclass.s | R | fclass.s rd, rs1 | 单精度浮点分类 x[rd] = classifys(f[rs1]) 将一个表示 f[rs1] 中单精度浮点数类别的掩码写入 x[rd]。 |
RV32D/RV64D指令:
指令 | 类型 | RV32D/RV64D | 作用 |
---|---|---|---|
fclass.d | R | fclass.d rd, rs1 | 双精度浮点分类 x[rd] = classifys(f[rs1]) 将一个表示 f[rs1] 中双精度浮点数类别的掩码写入 x[rd]。 |
4.9 RVFD 配置指令
RV32F/RV64F伪指令:
指令 | 伪指令 | 实际指令 | 作用 |
---|---|---|---|
frcsr | frcsr rd | csrrs rd, fcsr, x0 | 读浮点控制状态寄存器,在 RV32F 和 RV64F 中 x[rd] = CSRs[fcsr] 将浮点控制状态寄存器写入 x[rd] |
frflags | frflags rd | csrrs rd, fflags, x0 | 读浮点异常标志,在 RV32F 和 RV64F 中 x[rd] = CSRs[fflags] 将浮点异常标志写入 x[rd] |
frrm | frrm rd | csrrs rd, frm, x0 | 读浮点舍入模式,在 RV32F 和 RV64F 中 x[rd] = CSRs[frm] 将浮点舍入模式写入 x[rd] |
fscsr | fscsr rd, rs1 | csrrw rd, fcsr, rs1 | 交换浮点控制状态寄存器,在 RV32F 和 RV64F 中 t = CSRs[fcsr]; CSRs[fcsr] = x[rs1]; x[rd] = t 将 x[rs1] 写入浮点控制状态寄存器,并将浮点控制状态寄存器的原值写入 x[rd] 若省略 rd,则默认为 x0 |
fsrm | fsrm rd, rs1 | csrrw rd, frm, rs1 | 交换浮点舍入模式,在 RV32F 和 RV64F 中 csrrw rd, frm, rs1 将 x[rs1] 写入浮点舍入模式寄存器,并将浮点舍入模式寄存器的原值写入 x[rd] 若省略 rd,则默认为 x0 |
fsflags | fsflags rd, rs1 | csrrw rd, fflags, rs1 | 交换浮点异常标志,在 RV32F 和 RV64F 中 t = CSRs[fflags]; CSRs[fflags] = x[rs1]; x[rd] = t 将 x[rs1] 写入浮点异常标志寄存器,并将浮点异常标志寄存器的原值写入 x[rd] 若省略 rd,则默认为 x0 |
5 RVA指令集
RVA 用于同步的原子操作有两种:
-
原子内存操作 (atomic memory operation, AMO)
AMO 指令对内存中的操作数执行一次原子操作,并将原内存值写入目的寄存器。“原子” 表示内存读写之间既不会发生中断,也不会被其他处理器修改内存值。
-
加载保留/条件存储 ( load reserved / store conditional)
加载保留和条件存储保证了它们两条指令之间的操作的原子性。
- 加载保留读取一个内存字,存入目标寄存器中,并留下这个字的保留记录。
- 而如果条件存储的目标地址上存在保留记录,它就把字存入这个地址。
- 如果存入成功,它向目标寄存器中写入 0;否则写入一个非0 的错误代码。
加载保留就是当Load数据时,保留加载这个地址数据的记录。条件存储表示Store并不是总能成功,需要满足一定的条件:1.有LR访问过该地址的记录;2.LR和SC之间没有其它的写操作或中断;这样做的好处就是不用长时间将总线上锁,所以LR/SC指令可以不上锁保证操作的原子性。
指令示意:
$$
\mathrm{\underline{a}tomic \ \underline{m}emory \ \underline{o}peration}\
\left{
\begin{aligned}
& \mathrm{\underline{add}} \
& \mathrm{\underline{and}} \
& \mathrm{\underline{or}} \
& \mathrm{\underline{swap}} \
& \mathrm{\underline{xor}} \
& \mathrm{\underline{maxi}mum} \
& \mathrm{\underline{maxi}mum \ \underline{u}nsigned} \
& \mathrm{\underline{mini}mum} \
& \mathrm{\underline{mini}mum \ \underline{u}nsigned} \
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{.w}ord} \
& \mathrm{\mathbf{\underline{.d}oubleword}} \
\end{aligned}
\right}
$$
$$
\left{
\begin{aligned}
& \mathrm{\underline{l}oad \ \underline{r}eserved} \
& \mathrm{\underline{s}tore \ \underline{c}onditional} \
\end{aligned}
\right}
\left{
\begin{aligned}
& \mathrm{\underline{.w}ord} \
& \mathrm{\mathbf{\underline{.d}oubleword}} \
\end{aligned}
\right}
$$
RV32A/RV64A指令:
指令 | 类型 | RV32A/RV64A | 作用 |
---|---|---|---|
amoadd.w | R | amoadd.w rd, rs2, (rs1) | 原子加字 x[rd] = AMO32(M[x[rs1]] + x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t + x[rs2] 写入该地址,并将 t 的符号扩展结果写入 x[rd] |
amoand.w | R | amoand.w rd, rs2, (rs1) | 原子与字 x[rd] = AMO32(M[x[rs1]] & x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t 和 x[rs2] 的按位与结果写入该地址,并将 t 的符号扩展结果写入 x[rd]。 |
amoor.w | R | amoor.w rd, rs2, (rs1) | 原子或字 x[rd] = AMO32(M[x[rs1]] |x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t 和 x[rs2] 的按位或结果写入该地址,并将 t 的符号扩展结果写入 x[rd] |
amoswap.w | R | amoswap.w rd, rs2, (rs1) | 原子交换字 x[rd] = AMO32(M[x[rs1]] SWAP x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 x[rs2] 写入该地址,并将 t的符号扩展结果写入 x[rd] |
amoxor.w | R | amoxor.w rd, rs2, (rs1) | 原子异或字 x[rd] = AMO32(M[x[rs1]] ^ x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t 和 x[rs2] 的按位异或结果写入该地址,并将 t 的符号扩展结果写入 x[rd] |
amomax.w | R | amomax.w rd, rs2, (rs1) | 原子最大字 x[rd] = AMO32(M[x[rs1]] MAX x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t 和 x[rs2] 中较大者(补码比较)写入该地址,并将 t 的符号扩展结果写入 x[rd]。 |
amomaxu.w | R | amomaxu.w rd, rs2, (rs1) | 原子无符号最大字 x[rd] = AMO32(M[x[rs1]] MAXU x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t 和 x[rs2] 中较大者(无符号比较)写入该地址,并将 t 的符号扩展结果写入 x[rd]。 |
amomin.w | R | amomin.w rd, rs2, (rs1) | 原子最小字 x[rd] = AMO32(M[x[rs1]] MIN x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t 和 x[rs2] 中较小者(补码比较)写入该地址,并将 t 的符号扩展结果写入 x[rd] |
amominu.w | R | amominu.w rd, rs2, (rs1) | 原子无符号最小字 x[rd] = AMO32(M[x[rs1]] MINU x[rs2]) 将内存地址为 x[rs1] 的字记为 t,将 t 和 x[rs2] 中较小者(无符号比较)写入该地址,并将 t 的符号扩展结果写入 x[rd]。 |
lr.w | R | lr.w rd, (rs1) | 预订取字 x[rd] = LoadReserved32(M[x[rs1]]) 从地址 x[rs1] 读取 4 字节,符号扩展后写入 x[rd],并预订该内存字 |
sc.w | R | sc.w rd, rs2, (rs1) | 条件存字 x[rd] = StoreConditional32(M[x[rs1]], x[rs2]) 若内存地址 x[rs1] 被预订,则将 x[rs2] 中的 4 字节写入该地址。若写入成功,则向x[rd] 写入 0,否则向其写入一个非 0 的错误码 |
RV64A指令:
指令 | 类型 | RV64A | 作用 |
---|---|---|---|
amoadd.d | R | amoadd.d rd, rs2, (rs1) | 原子加双字 x[rd] = AMO64(M[x[rs1]] + x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t + x[rs2] 写入该地址,并将 t 写入 x[rd]。 |
amoand.d | R | amoand.d rd, rs2, (rs1) | 原子与双字 x[rd] = AMO64(M[x[rs1]] & x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t 和 x[rs2] 的按位与结果写入该地址,并将 t 写入 x[rd] |
amoor.d | R | amoor.d rd, rs2, (rs1) | 原子或双字 x[rd] = AMO32(M[x[rs1]] | x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t 和 x[rs2] 的按位或结果写入该地址,并将 t 写入 x[rd]。 |
amoswap.d | R | amoswap.d rd, rs2, (rs1) | 原子交换双字 x[rd] = AMO64(M[x[rs1]] SWAP x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 x[rs2] 写入该地址,并将t 写入 x[rd]。 |
amoxor.d | R | amoxor.d rd, rs2, (rs1) | 原子异或双字 x[rd] = AMO32(M[x[rs1]] ^ x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t 和 x[rs2] 的按位异或结果写入该地址,并将 t 写入 x[rd]。 |
amomax.d | R | amomax.d rd, rs2, (rs1) | 原子最大双字 x[rd] = AMO64(M[x[rs1]] MAX x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t 和 x[rs2] 中较大者(补码比较)写入该地址,并将 t 写入 x[rd]。 |
amomaxu.d | R | amomaxu.d rd, rs2, (rs1) | 原子无符号最大双字 x[rd] = AMO64(M[x[rs1]] MAXU x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t 和 x[rs2] 中较大者(无符号比较)写入该地址,并将 t 写入 x[rd] |
amomin.d | R | amomin.d rd, rs2, (rs1) | 原子最小双字 x[rd] = AMO64(M[x[rs1]] MIN x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t 和 x[rs2] 中较小者(补码比较)写入该地址,并将 t 写入 x[rd]。 |
amominu.d | R | amominu.d rd, rs2, (rs1) | 原子无符号最小双字 x[rd] = AMO64(M[x[rs1]] MINU x[rs2]) 将内存地址为 x[rs1] 的双字记为 t,将 t 和 x[rs2] 中较小者(无符号比较)写入该地址,并将 t 写入 x[rd]。 |
lr.d | R | lr.d rd, (rs1) | 预订取双字 x[rd] = LoadReserved64(M[x[rs1]]) 从地址 x[rs1] 读取 8 字节,写入 x[rd],并预订该内存双字 |
sc.d | R | sc.d rd, rs2, (rs1) | 条件存双字 x[rd] = StoreConditional64(M[x[rs1]], x[rs2]) 若内存地址 x[rs1] 被预订,则将 x[rs2] 中的 8 字节写入该地址。若写入成功,则向x[rd] 写入 0,否则向其写入一个非 0 的错误码 |
参考:
- 《riscv-spec-20191213.pdf》
- riscv-asm-manual/riscv-asm.md at master · riscv-non-isa/riscv-asm-manual · GitHub
- 从零手写操作系统之RVOS任务同步和锁实现-07-腾讯云开发者社区-腾讯云 (tencent.com)