1. 移位指令

移位指令是RISC-V指令集中用于对数据进行位移操作的重要指令类型。移位操作在计算机中广泛用于乘法、除法、位操作等场景。

1.1 逻辑左移指令 (sll)

sll: 逻辑左移 (Shift Left Logical) 指令

  • 功能: 将操作数向左移动指定的位数
  • 特点: 最高位会被丢弃,最低位补0
  • 用途: 常用于实现乘法运算(左移1位相当于乘以2)
指令格式
sll rd, rs1, rs2
操作说明
  • rd: 目标寄存器,存储移位结果
  • rs1: 源寄存器,包含要移位的操作数
  • rs2: 移位量寄存器,指定左移的位数

1.2 逻辑右移指令 (srl)

srl: 逻辑右移 (Shift Right Logical) 指令

  • 功能: 将操作数向右移动指定的位数
  • 特点: 最高位补0,最低位会被丢弃
  • 用途: 常用于实现除法运算(右移1位相当于除以2)
指令格式
srl rd, rs1, rs2
操作说明
  • rd: 目标寄存器,存储移位结果
  • rs1: 源寄存器,包含要移位的操作数
  • rs2: 移位量寄存器,指定右移的位数

1.3 算术右移指令 (sra)

sra: 算术右移 (Shift Right Arithmetic) 指令

  • 功能: 将操作数向右移动指定的位数
  • 特点: 最低位会被丢弃,最高位会按照符号进行扩展
  • 用途: 用于有符号数的除法运算,保持符号位不变
指令格式
sra rd, rs1, rs2
操作说明
  • rd: 目标寄存器,存储移位结果
  • rs1: 源寄存器,包含要移位的操作数
  • rs2: 移位量寄存器,指定右移的位数
  • 符号扩展: 如果原数的最高位是1(负数),则右移时高位补1;如果最高位是0(正数),则右移时高位补0

1.4 带立即数的移位指令

带立即数的移位指令是移位指令的扩展形式,它们使用立即数作为移位量,而不是从寄存器中读取。这种设计提高了指令执行效率,因为移位量直接编码在指令中。

1.4.1 立即数逻辑左移指令 (slli)

slli: 立即数逻辑左移 (Shift Left Logical Immediate) 指令

  • 功能: 将操作数向左移动指定的立即数位数
  • 特点: 最高位会被丢弃,最低位补0
  • 优势: 移位量直接编码在指令中,执行效率更高
指令格式
slli rd, rs1, shamt
操作说明
  • rd: 目标寄存器,存储移位结果
  • rs1: 源寄存器,包含要移位的操作数
  • shamt: 移位量,为6位无符号立即数(0-63)
  • 操作: rd = rs1 << shamt
1.4.2 立即数逻辑右移指令 (srli)

srli: 立即数逻辑右移 (Shift Right Logical Immediate) 指令

  • 功能: 将操作数向右移动指定的立即数位数
  • 特点: 最高位补0,最低位会被丢弃
  • 优势: 移位量直接编码在指令中,执行效率更高
指令格式
srli rd, rs1, shamt
操作说明
  • rd: 目标寄存器,存储移位结果
  • rs1: 源寄存器,包含要移位的操作数
  • shamt: 移位量,为6位无符号立即数(0-63)
  • 操作: rd = rs1 >> shamt
1.4.3 立即数算术右移指令 (srai)

srai: 立即数算术右移 (Shift Right Arithmetic Immediate) 指令

  • 功能: 将操作数向右移动指定的立即数位数
  • 特点: 最低位会被丢弃,最高位会按照符号进行扩展
  • 优势: 移位量直接编码在指令中,执行效率更高
指令格式
srai rd, rs1, shamt
操作说明
  • rd: 目标寄存器,存储移位结果
  • rs1: 源寄存器,包含要移位的操作数
  • shamt: 移位量,为6位无符号立即数(0-63)
  • 操作:
    • tmp = rs1 >> shamt
    • rd = Signed_with_rs1(tmp)
  • 符号扩展: 根据rs1的符号位进行符号扩展

1.5 32位移位指令 (RV64专用)

32位移位指令是RISC-V 64位架构(RV64)的专用指令,用于处理32位数据的移位操作。这些指令只对操作数的低32位进行移位,然后将结果符号扩展到64位。

32位移位指令对比表
指令助记符中文描述指令格式操作说明
sllw逻辑左移 (低32位版本)sllw rd, rs1, rs2tmp = rs1[31:0] << rs2[4:0]
tmp = tmp[31:0]
rd = signed(tmp)
其中 rs2 只取最低 5 位数值
srlw逻辑右移 (低32位版本)srlw rd, rs1, rs2tmp = rs1[31:0] >> rs2[4:0]
rd = signed(tmp)
其中 rs2 只取最低 5 位数值
sraw算术右移 (低32位版本)sraw rd, rs1, rs2tmp = rs1[31:0] >> rs2[4:0]
rd = Signed_with_rs1(tmp)
其中 rs2 只取最低 5 位数值, 另外结果需要以 rs1 的 Bit[31] 为符号位来做符号扩展
slliw立即数逻辑左移 (低32位版本)slliw rd, rs1, shamttmp = rs1[31:0] << shamt
tmp = tmp[31:0]
rd = signed(tmp)
其中 shamt 为 6 位无符号数
srliw立即数逻辑右移 (低32位版本)srliw rd, rs1, shamttmp = rs1[31:0] >> shamt
tmp = tmp[31:0]
rd = signed(tmp)
其中 shamt 为 6 位无符号数
sraiw立即数算术右移 (低32位版本)sraiw rd, rs1, shamttmp = rs1[31:0] >> shamt
tmp = tmp[31:0]
rd = Signed_with_rs1(tmp)
其中 shamt 为 6 位无符号数, 另外结果需要以 rs1 的 Bit[31] 为符号位来做符号扩展
.global shift_test
shift_test:
	li t0, 0x8000008a00000000
	srai a1, t0, 1             #  0xC000004500000000
	srli t1, t0, 1             #  0x4000004500000000

	li t0, 0x128000008a        # 0x128000008a
	sraiw a2, t0, 1            # 0xffffffffC0000045
	srliw t1, t0, 1            # 0x40000045
	slliw a3, t0, 1            # 0x114

	li t0, 0x124000008a
	sraiw a2, t0, 1            # 0x20000045
	srliw t1, t0, 1            # 0x20000045
	slliw a4, t0, 1            # 0xffffffff80000114

2. 位操作指令

2.1 寄存器到寄存器位操作指令

RV64指令集提供与(and)、或(or)以及异或(xor)三种位操作指令

指令助记符中文描述指令格式操作说明
and与操作and rd, rs1, rs2rd = rs1 & rs2
xor异或操作xor rd, rs1, rs2rd = rs1 ^ rs2
or或操作or rd, rs1, rs2rd = rs1 | rs2

2.2 立即数位操作指令

• 立即数位操作指令

指令助记符中文描述指令格式操作说明
xori立即数异或操作xori rd, rs1, immrd = rs1 ^ imm
其中 imm 为 12 位有符号数
ori立即数或操作ori rd, rs1, immrd = rs1 | imm
其中 imm 为 12 位有符号数
andi立即数与操作andi rd, rs1, immrd = rs1 & imm
其中 imm 为 12 位有符号数

异或操作的妙用

异或的3个小特点

(1) 0异或任何数=任何数
  • 0^0=0
  • 0^1=1
  • 0异或任何数=任何数
(2) 1异或任何数 = 任何数取反
  • 1^0=1
  • 1^1=0
  • 1异或任何数 = 任何数取反
(3) 任何数异或自己 = 把自己置0
  • 任何数异或自己 = 把自己置0

异或的几个小妙用

(1) 使某些特定的位翻转

说明: 要使数字10100001的第1位和第2位翻转,可以将其与00000110进行异或运算。

示例: 10100001 ^ 00000110 = 10100111

(2) 交换两个数

说明: 演示如何在不使用临时变量的情况下交换两个整数a和b。

示例: 对于a=10100001和b=00000110,交换过程如下:

a = a^b;    // a变成 10100111
b = b^a;    // b变成 10100001 (原来的a)
a = a^b;    // a变成 00000110 (原来的b)
(3) 在汇编里让变量设置为0,例如x1寄存器

示例: 汇编指令xor x1, x1, x1将x1寄存器设置为0。

(4) 判断两个是否相等

说明: 如果两个数a和b相等,它们的异或结果将为0。

示例: return ((a^ b) == 0)

3. 算术运算指令

RV64I指令集只提供最基础的加法和减法指令

指令助记符中文描述指令格式操作说明
add加法指令add rd, rs1, rs2rd = rs1 + rs2
sub减法指令sub rd, rs1, rs2rd = rs1 - rs2
addi立即数加法addi rd, rs1, immrd = rs1 + imm
其中 imm 为 12 位有符号数
addiw立即数加法 (低32位版本)addi rd, rs1, immtmp = rs1[31:0] + imm
tmp = tmp[31:0]
rd = signed (tmp)
其中 imm 为 12 位有符号数
addw加法 (低32位版本)addw rd, rs1, rs2tmp = rs1[31:0] + rs2[31:0]
tmp = tmp[31:0]
rd = signed (tmp)
subw减法 (低32位版本)subw rd, rs1, rs2tmp = rs1[31:0] - rs2[31:0]
tmp = tmp[31:0]
rd = signed (tmp)

3.1 addi指令立即数操作数示例

例子:合法与非法指令识别

问题: 下面两条指令哪一条是非法指令?

addi a1, t0, 0x800
addi a1, t0, 0xfffffffffffff800

答案: 第一条指令为非法指令

汇编器错误信息:

src/asm_test.S: Assembler messages:
src/asm_test.S:12: Error: illegal operands 'addi a1,t0,0x800'

解释:
在GNU AS汇编中,0x800被看作是一个数值为2048的无符号数,而不是12位宽的带符号扩展的立即数。如果想表示"-2048"立即数,我们需要使用0xfffffffffffff800。

关键要点:

  • addi指令在RISC-V中期望一个12位有符号立即数
  • GNU AS将0x800解释为正无符号值(2048),这超出了12位有符号立即数的范围(-2048到2047)
  • 要表示负立即数值如-2048,必须使用完整的64位符号扩展表示0xfffffffffffff800
  • 这个值在截断为12位时是0x800,在12位二进制补码中确实是-2048

4. 比较指令

比较指令是RISC-V指令集中用于进行数值比较的重要指令类型,支持有符号和无符号数的比较操作。

4.1 RV64I指令集提供的比较指令

RV64I指令集提供4条比较指令

指令类型/描述指令格式操作说明附加说明
slt 小于比较指令slt rd, rs1, rs2rd = (rs1 < rs2) ? 1 : 0其中 rs1 和 rs2 为有符号数
sltu 小于比较指令sltu rd, rs1, rs2rd = (rs1 < rs2) ? 1 : 0其中 rs1 和 rs2 为无符号数
slti 小于比较指令slti rd, rs1, immrd = (rs1 < imm) ? 1 : 0其中 imm 为 12 位有符号数, rs1 为有符号数
sltiu 立即数小于比较sltiu rd, rs1, immrd = (rs1 < imm) ? 1 : 0其中 imm 为 12 位有符号数, rs1 为无符号数

4.2 RV64I指令集提供的比较伪指令

RV64I指令集提供比较伪指令

伪指令伪指令格式说明
sltzsltz rd, rs1小于0则置位指令。如果rs1的值小于0,向rd寄存器写入1,否则写入0。
snezsnez rd, rs1不等于0则置位指令。如果rs1寄存器的值不等于0,向rd寄存器写入1,否则写入0。
seqzseqz rd, rs1等于0则置位指令。如果rs1寄存器的值等于0,向rd寄存器写入1,否则写入0。
sgtzsgtz rd, rs1大于0则置位指令。如果rs1的值大于0,向rd寄存器写入1,否则写入0。

5. 无条件跳转指令

无条件跳转指令是RISC-V指令集中用于控制程序流程的重要指令类型,支持函数调用和程序跳转操作。

5.1 RV64I指令集提供的无条件跳转指令

RV64I指令集提供2条无条件跳转指令

指令类型/描述指令格式操作说明附加说明
JAL 跳转并链接指令jal rd, labelPC = PC + offset 和 rd = PC + 4其中 offset 为 21 位有符号数, 2 字节对齐 (最低位为 0)。其值表示 label 地址与当前 PC 值的偏移量
JALR 使用寄存器跳转并链接指令jalr rd, offset(rs1)PC = rs1 + offset 和 rd = PC + 4其中 offset 为 12 位有符号数

5.2 JAL指令详细说明

JAL指令特点
  • JAL (jump and link) 指令使用 J-type 指令编码
  • 其操作数 offset[20:1] 由指令编码的 [31:12] 位组成
  • 默认为 2 的倍数,即其跳转范围约为当前 PC 值的 ±1 MB
  • 返回地址(即 PC + 4)存储在 rd 寄存器中
  • 如果返回地址存储在 ra(return address)寄存器中,则可以实现函数返回

5.3 JALR指令详细说明

JALR指令特点
  • JALR (jump and link register) 指令使用 I-type 指令编码
  • 跳转目标地址由寄存器 rs1 的值加上 12 位有符号立即数 offset 计算得出
  • 返回地址(即 PC + 4)存储在 rd 寄存器中
  • 常用于间接跳转和函数指针调用
应用场景
  1. 函数调用:jal ra, function_name - 调用函数并将返回地址存储在ra寄存器
  2. 间接跳转:jalr t0, 0(t1) - 跳转到t1寄存器指向的地址
  3. 函数返回:jalr zero, 0(ra) - 返回到ra寄存器存储的地址

5.4 RV64I指令集提供的无条件跳转伪指令

无条件跳转伪指令是RISC-V指令集中基于基础跳转指令构建的高级指令,提供了更简洁和易用的编程接口。

伪指令指令组合说明
j labeljal x0, offset跳转到label标签处,不带返回地址
jal labeljal ra, offset跳转到label标签处,返回地址存储在ra寄存器中
jr rsjalr x0, 0(rs)跳转到rs寄存器地址处,不带返回地址
jalr rsjalr ra, 0(rs)跳转到rs寄存器地址处,返回地址存储在ra寄存器中
retjalr x0, 0(ra)从ra寄存器中获取返回地址,并返回。常用于子函数返回
call funcauipc ra, offset[31:12]+offset[11]
jalr ra, offset[11:0](ra)
调用子函数func,返回地址保存到ra寄存器中
tail funcauipc x6, offset[31:12]+offset[11]
jalr x0, offset[11:0](x6)
调用子函数func,不保存返回地址

6. 条件跳转指令

条件跳转指令是RISC-V指令集中用于实现程序控制流的重要指令类型,根据比较结果决定是否跳转到指定位置。

6.1 RV64I指令集提供的条件跳转指令

RV64I指令集提供多条条件跳转指令

指令指令格式说明
beqbeq rs1, rs2, label如果rs1和rs2寄存器的值相等,则跳转到label标签处
bnebne rs1, rs2, label如果rs1和rs2寄存器的值不相等,则跳转到label标签处
bltblt rs1, rs2, label如果rs1寄存器的值小于rs2,则跳转到label标签处
bltubltu rs1, rs2, label与blt指令类似,只不过rs1和rs2的值为无符号数
bgtbgt rs1, rs2, label如果rs1寄存器的值大于rs2,则跳转到label标签处
bgtubgtu rs1, rs2, label与bgt指令类似,只不过rs1和rs2的值为无符号数
bgebge rs1, rs2, label如果rs1寄存器的值大于等于rs2,则跳转到label标签处
bgeubgeu rs1, rs2, label与bge指令类似,只不过rs1和rs2的值为无符号数

6.2 跳转范围

跳转范围:

  • offset表示label标签地址基于当前PC地址的偏移量。
  • offset是13位有符号立即数,其中offset[12:1]由指令编码的Bit[31:25]以及Bit[11:7]共同构成,offset[0]为0,它默认是2的倍数,因此它最大寻址范围是[-4KB ~ 4KB]
  • 跳转到当前PC地址±4KB的范围

6.3 条件跳转伪指令

条件跳转伪指令是RISC-V指令集中基于基础条件跳转指令构建的高级指令,提供了更简洁和易用的编程接口。这些伪指令通过组合基础指令来实现更直观的比较操作。

伪指令指令组合判断条件
beqzbeq rs, x0, labelrs == 0
bnezbne rs, x0, labelrs != 0
blezbge x0, rs, labelrs <= 0
bgezbge rs, x0, labelrs >= 0
bltzblt rs, x0, labelrs < 0
bgtzblt x0, rs, labelrs > 0
bgtblt rt, rs, labelrs > rt
blebge rt, rs, labelrs <= rt
bgtubltu rt, rs, labelrs > rt (无符号数比较)
bleubgeu rt, rs, labelrs <= rt (无符号数比较)

7. CSR指令

CSR(Control and Status Register)指令是RISC-V指令集中用于访问和控制状态寄存器的重要指令类型。这些指令允许程序读取和修改处理器的控制状态寄存器,实现系统级操作。

7.1 CSR指令格式

CSR指令使用I-type指令格式,具有以下字段结构:

31-20: csr字段 (12位) - 用于索引CSR寄存器
19-15: rs1字段 (5位)  - 源寄存器或立即数
14-12: funct3字段 (3位) - 指令功能码
11-7:  rd字段 (5位)   - 目标寄存器
6-0:   opcode字段 (7位) - 操作码

7.2 CSR指令详细说明

CSR指令指令格式说明
csrrwcsrrw rd, csr, rs1原子地交换CSR和rs1寄存器的值。读取在CSR的旧值,将其零扩展到64位,然后写入rd寄存器中,与此同时,rs1寄存器的旧值将被写入CSR中。
csrrscsrrs rd, csr, rs1原子地读CSR寄存器的值并且设置CSR寄存器中相应的位。指令读取CSR寄存器的旧值,将其零扩展到64位,然后写入rd寄存器中。与此同时,把rs1寄存器的值做作为掩码,设置CSR寄存器相应的位。
csrrccsrrc rd, csr, rs1原子地读CSR寄存器的值并且清CSR寄存器中相应的位。指令读取CSR寄存器的旧值,将其零扩展到64位,然后写入rd寄存器中。与此同时,把rs1寄存器的值做作为掩码,清CSR寄存器相应的位。
csrrwicsrrwi rd, csr, uimm作用与csrrw指令类似,区别在于使用5位无符号立即数替代rs1。
csrrsicsrrsi rd, csr, uimm作用与csrrs指令类似,区别在于使用5位无符号立即数替代rs1。
csrrcicsrrci rd, csr, uimm作用与csrrc指令类似,区别在于使用5位无符号立即数替代rs1。

7.3 RV64I指令集基于CSR的伪指令

RV64I指令集提供了基于CSR指令的伪指令,这些伪指令通过组合基础CSR指令来实现更简洁和易用的编程接口。

伪指令指令组合说明
csrrcsrrs rd, csr, x0读取CSR寄存器的值
csrwcsrrw x0, csr, rs写CSR寄存器的值
csrscsrrs x0, csr, rs设置CSR寄存器的字段 (`csr
csrccsrrc x0, csr, rs清CSR寄存器的字段 (csr &= ~rs)
csrwicsrrwi x0, csr, imm把立即数imm写入CSR寄存器中
csrsicsrrsi x0, csr, imm设置CSR寄存器的字段 (`csr
csrcicsrrci x0, csr, imm清CSR寄存器的字段 (csr &= ~imm)
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐