文章目录


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, rs2 tmp = rs1[31:0] << rs2[4:0]
tmp = tmp[31:0]
rd = signed(tmp)
其中 rs2 只取最低 5 位数值
srlw 逻辑右移 (低32位版本) srlw rd, rs1, rs2 tmp = rs1[31:0] >> rs2[4:0]
rd = signed(tmp)
其中 rs2 只取最低 5 位数值
sraw 算术右移 (低32位版本) sraw rd, rs1, rs2 tmp = rs1[31:0] >> rs2[4:0]
rd = Signed_with_rs1(tmp)
其中 rs2 只取最低 5 位数值, 另外结果需要以 rs1 的 Bit[31] 为符号位来做符号扩展
slliw 立即数逻辑左移 (低32位版本) slliw rd, rs1, shamt tmp = rs1[31:0] << shamt
tmp = tmp[31:0]
rd = signed(tmp)
其中 shamt 为 6 位无符号数
srliw 立即数逻辑右移 (低32位版本) srliw rd, rs1, shamt tmp = rs1[31:0] >> shamt
tmp = tmp[31:0]
rd = signed(tmp)
其中 shamt 为 6 位无符号数
sraiw 立即数算术右移 (低32位版本) sraiw rd, rs1, shamt tmp = 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, rs2 rd = rs1 & rs2
xor 异或操作 xor rd, rs1, rs2 rd = rs1 ^ rs2
or 或操作 or rd, rs1, rs2 rd = rs1 | rs2

2.2 立即数位操作指令

• 立即数位操作指令

指令助记符 中文描述 指令格式 操作说明
xori 立即数异或操作 xori rd, rs1, imm rd = rs1 ^ imm
其中 imm 为 12 位有符号数
ori 立即数或操作 ori rd, rs1, imm rd = rs1 | imm
其中 imm 为 12 位有符号数
andi 立即数与操作 andi rd, rs1, imm rd = 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) 交换两个数

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

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

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

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

(4) 判断两个是否相等

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

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

3. 算术运算指令

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

指令助记符 中文描述 指令格式 操作说明
add 加法指令 add rd, rs1, rs2 rd = rs1 + rs2
sub 减法指令 sub rd, rs1, rs2 rd = rs1 - rs2
addi 立即数加法 addi rd, rs1, imm rd = rs1 + imm
其中 imm 为 12 位有符号数
addiw 立即数加法 (低32位版本) addi rd, rs1, imm tmp = rs1[31:0] + imm
tmp = tmp[31:0]
rd = signed (tmp)
其中 imm 为 12 位有符号数
addw 加法 (低32位版本) addw rd, rs1, rs2 tmp = rs1[31:0] + rs2[31:0]
tmp = tmp[31:0]
rd = signed (tmp)
subw 减法 (低32位版本) subw rd, rs1, rs2 tmp = 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, rs2 rd = (rs1 < rs2) ? 1 : 0 其中 rs1 和 rs2 为有符号数
sltu 小于比较指令 sltu rd, rs1, rs2 rd = (rs1 < rs2) ? 1 : 0 其中 rs1 和 rs2 为无符号数
slti 小于比较指令 slti rd, rs1, imm rd = (rs1 < imm) ? 1 : 0 其中 imm 为 12 位有符号数, rs1 为有符号数
sltiu 立即数小于比较 sltiu rd, rs1, imm rd = (rs1 < imm) ? 1 : 0 其中 imm 为 12 位有符号数, rs1 为无符号数

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

RV64I指令集提供比较伪指令

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

5. 无条件跳转指令

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

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

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

指令类型/描述 指令格式 操作说明 附加说明
JAL 跳转并链接指令 jal rd, label PC = PC + offsetrd = PC + 4 其中 offset 为 21 位有符号数, 2 字节对齐 (最低位为 0)。其值表示 label 地址与当前 PC 值的偏移量
JALR 使用寄存器跳转并链接指令 jalr rd, offset(rs1) PC = rs1 + offsetrd = 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 label jal x0, offset 跳转到label标签处,不带返回地址
jal label jal ra, offset 跳转到label标签处,返回地址存储在ra寄存器中
jr rs jalr x0, 0(rs) 跳转到rs寄存器地址处,不带返回地址
jalr rs jalr ra, 0(rs) 跳转到rs寄存器地址处,返回地址存储在ra寄存器中
ret jalr x0, 0(ra) 从ra寄存器中获取返回地址,并返回。常用于子函数返回
call func auipc ra, offset[31:12]+offset[11]
jalr ra, offset[11:0](ra)
调用子函数func,返回地址保存到ra寄存器中
tail func auipc x6, offset[31:12]+offset[11]
jalr x0, offset[11:0](x6)
调用子函数func,不保存返回地址

6. 条件跳转指令

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

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

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

指令 指令格式 说明
beq beq rs1, rs2, label 如果rs1和rs2寄存器的值相等,则跳转到label标签处
bne bne rs1, rs2, label 如果rs1和rs2寄存器的值不相等,则跳转到label标签处
blt blt rs1, rs2, label 如果rs1寄存器的值小于rs2,则跳转到label标签处
bltu bltu rs1, rs2, label 与blt指令类似,只不过rs1和rs2的值为无符号数
bgt bgt rs1, rs2, label 如果rs1寄存器的值大于rs2,则跳转到label标签处
bgtu bgtu rs1, rs2, label 与bgt指令类似,只不过rs1和rs2的值为无符号数
bge bge rs1, rs2, label 如果rs1寄存器的值大于等于rs2,则跳转到label标签处
bgeu bgeu 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指令集中基于基础条件跳转指令构建的高级指令,提供了更简洁和易用的编程接口。这些伪指令通过组合基础指令来实现更直观的比较操作。

伪指令 指令组合 判断条件
beqz beq rs, x0, label rs == 0
bnez bne rs, x0, label rs != 0
blez bge x0, rs, label rs <= 0
bgez bge rs, x0, label rs >= 0
bltz blt rs, x0, label rs < 0
bgtz blt x0, rs, label rs > 0
bgt blt rt, rs, label rs > rt
ble bge rt, rs, label rs <= rt
bgtu bltu rt, rs, label rs > rt (无符号数比较)
bleu bgeu rt, rs, label rs <= 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指令 指令格式 说明
csrrw csrrw rd, csr, rs1 原子地交换CSR和rs1寄存器的值。读取在CSR的旧值,将其零扩展到64位,然后写入rd寄存器中,与此同时,rs1寄存器的旧值将被写入CSR中。
csrrs csrrs rd, csr, rs1 原子地读CSR寄存器的值并且设置CSR寄存器中相应的位。指令读取CSR寄存器的旧值,将其零扩展到64位,然后写入rd寄存器中。与此同时,把rs1寄存器的值做作为掩码,设置CSR寄存器相应的位。
csrrc csrrc rd, csr, rs1 原子地读CSR寄存器的值并且清CSR寄存器中相应的位。指令读取CSR寄存器的旧值,将其零扩展到64位,然后写入rd寄存器中。与此同时,把rs1寄存器的值做作为掩码,清CSR寄存器相应的位。
csrrwi csrrwi rd, csr, uimm 作用与csrrw指令类似,区别在于使用5位无符号立即数替代rs1。
csrrsi csrrsi rd, csr, uimm 作用与csrrs指令类似,区别在于使用5位无符号立即数替代rs1。
csrrci csrrci rd, csr, uimm 作用与csrrc指令类似,区别在于使用5位无符号立即数替代rs1。

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

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

伪指令 指令组合 说明
csrr csrrs rd, csr, x0 读取CSR寄存器的值
csrw csrrw x0, csr, rs 写CSR寄存器的值
csrs csrrs x0, csr, rs 设置CSR寄存器的字段 (`csr
csrc csrrc x0, csr, rs 清CSR寄存器的字段 (csr &= ~rs)
csrwi csrrwi x0, csr, imm 把立即数imm写入CSR寄存器中
csrsi csrrsi x0, csr, imm 设置CSR寄存器的字段 (`csr
csrci csrrci x0, csr, imm 清CSR寄存器的字段 (csr &= ~imm)
Logo

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

更多推荐