RISC-V指令简介
RISC-V指令的长度是固定的,由处理器的位数决定。
R型指令
RISC-V中的R型指令是用于寄存器-寄存器操作的基本指令格式,主要用于执行算术运算、逻辑运算和移位操作等计算密集型任务。
R型指令采用固定的32位编码格式,具体字段分布如下:
| 字段位置 | 31-25 | 24-20 | 19-15 | 14-12 | 11-7 | 6-0 |
|---|---|---|---|---|---|---|
| 字段名称 | funct7 | rs2 | rs1 | funct3 | rd | opcode |
| 位宽 | 7位 | 5位 | 5位 | 3位 | 5位 | 7位 |
各字段功能说明:
- opcode :7位操作码,固定为
0110011,标识该指令为R型指令 - rd:5位目的寄存器编号,用于存放运算结果
- funct3:3位功能码,进一步区分不同类型的R型指令
- rs1:5位第一个源操作数寄存器编号
- rs2:5位第二个源操作数寄存器编号
- funct7:7位扩展功能码,与funct3配合使用,更精确地定义操作类型
算术运算指令
- ADD rd, rs1, rs2 :寄存器加法,
rd = rs1 + rs2 - SUB rd, rs1, rs2 :寄存器减法,
rd = rs1 - rs2
逻辑运算指令
- AND rd, rs1, rs2 :按位与操作,
rd = rs1 & rs2 - OR rd, rs1, rs2 :按位或操作,
rd = rs1 | rs2 - XOR rd, rs1, rs2 :按位异或操作,
rd = rs1 ^ rs2
移位操作指令
- SLL rd, rs1, rs2 :逻辑左移,
rd = rs1 << rs2 - SRL rd, rs1, rs2 :逻辑右移,
rd = rs1 >> rs2(高位补0) - SRA rd, rs1, rs2 :算术右移,
rd = rs1 >> rs2(高位补符号位)
比较指令
- SLT rd, rs1, rs2 :有符号比较,如果
rs1 < rs2则rd = 1,否则rd = 0 - SLTU rd, rs1, rs2 :无符号比较,如果
rs1 < rs2则rd = 1,否则rd = 0
编码示例
以add x5, x6, x7指令为例:
- 二进制编码:
0000000 | 00111 | 00110 | 000 | 00101 | 0110011 - 功能:将x6和x7寄存器中的值相加,结果存入x5寄存器
I型指令
I型指令是RISC-V指令集中非常重要的一类,主要用于立即数操作 、数据加载 和无条件跳转等功能。
| 字段位置(高位到低位) | 字段名称 | 位宽 | 功能描述 |
|---|---|---|---|
| 31-20 | imm[11:0] | 12位 | 12位立即数,在使用时会进行符号扩展至32位 |
| 19-15 | rs1 | 5位 | 第一个源操作数寄存器地址 |
| 14-12 | funct3 | 3位 | 功能码,与opcode配合指定具体操作(如ADD、LOAD等) |
| 11-7 | rd | 5位 | 目的寄存器地址,用于存放操作结果 |
| 6-0 | opcode | 7位 | 操作码,标识指令类型(如I型指令的操作码为0010011或0000011等) |
主要类型与功能
I型指令根据其具体功能,主要可以分为以下三类:
-
寄存器-立即数运算指令
这是最基础的I型指令,其基本操作为:
Reg[rd] = Reg[rs1] op Imm。最常见的例子是ADDI(立即数加法)指令,例如ADDI x5, x6, 10表示将寄存器x6的值加上10,结果存入寄存器x5。除了加法,这类指令还包括按位逻辑操作(如ANDI,ORI,XORI)、移位操作(如SLLI,SRLI,SRAI)和比较操作(如SLTI,SLTIU)等。 -
加载指令
加载指令用于从内存中读取数据到寄存器,属于I型指令。其地址由寄存器rs1的值加上12位的符号扩展偏移量(offset)形成。例如
LW(加载字)指令LW x10, 0(x1),表示从以寄存器x1的值为基地址、偏移量为0的内存位置读取一个32位的字,然后存入寄存器x10。类似的指令还有加载字节(LB/LBU)和加载半字(LH/LHU)等。 -
跳转链接寄存器指令
JALR(跳转并链接寄存器)指令也采用I型格式。它用于子程序调用或跳转到由寄存器指定的地址,其操作通常为x[rd] = PC + 4; PC = x[rs1] + imm。例如JALR ra, 40(x10)会跳转到地址为x10 + 40的地方执行,同时将返回地址(下一条指令的地址PC+4)存入ra寄存器。
S型指令
S型指令是RISC-V指令集中专门用于写存储器(Store)操作的一类指令,其核心功能是将寄存器中的数据存储到内存中。
| 核心特征 | S型指令的对应设计 |
|---|---|
| 主要功能 | 将寄存器的值写入内存(Store操作) |
| 操作码 (opcode) | 0100011 |
| 指令格式字段 (从高位到低位) | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode |
| 操作的寄存器 | 两个源寄存器:rs1(基地址寄存器)、rs2(要存储的数据来源寄存器) |
| 立即数特点 | 12位立即数(作为地址偏移量),但被拆分编码在指令的不同位置 |
| 常见指令示例 | sb (存储字节), sh (存储半字), sw (存储字) |
指令格式详解
S型指令的32位编码格式颇具巧思,其设计旨在与其他指令格式保持规整性同时,优化指令解码。它的12位立即数(imm)被分为两部分,分散在指令中:
imm[11:5]:位于指令的31-25位。imm[4:0]:位于指令的11-7位。
当CPU执行S型指令时,需要先将这两个部分组合起来,并进行符号扩展 ,生成一个32位的有效地址偏移量。存储操作的内存地址由公式 内存地址 = Reg[rs1] + 符号扩展后的立即数 计算得出。
常见指令与操作过程
常见的S型指令通过funct3字段区分具体操作类型,例如:
sw rs2, offset(rs1):存储字(32位)。将寄存器rs2中的32位数据写入内存。sh rs2, offset(rs1):存储半字(16位)。仅将寄存器rs2的低16位数据写入内存。sb rs2, offset(rs1):存储字节(8位)。仅将寄存器rs2的低8位数据写入内存。
其数据通路的关键操作可概括为:计算有效地址,然后根据funct3指示的数据宽度,将rs2寄存器中的相应数据写入该内存地址。
SB型指令
SB型指令是RISC-V指令集中用于条件分支跳转的一类重要指令,它根据两个寄存器的值比较结果来决定程序的执行流程。
| 核心特征 | SB型指令的对应设计 |
|---|---|
| 主要功能 | 条件分支跳转(Conditional Branch) |
| 操作码 (opcode) | 1100011 |
| 指令格式字段 (从高位到低位) | imm[12,10:5] | rs2 | rs1 | funct3 | imm[4:1,11] | opcode |
| 操作的寄存器 | 两个源寄存器:rs1、rs2(用于比较) |
| 立即数特点 | 12位立即数(作为偏移量),但被拆分并旋转编码在指令中 |
| 跳转地址计算 | 目标地址 = 当前指令地址 + 符号扩展后的立即数左移1位后的值 |
常见指令与功能
SB型指令通过funct3字段来区分不同的比较条件。以下是RV32I基础指令集中的所有SB型指令:
| 指令 | 语法 | 功能描述(伪代码) | 比较类型 |
|---|---|---|---|
| BEQ | beq rs1, rs2, imm |
若 rs1 == rs2,则 PC += imm << 1 |
相等 |
| BNE | bne rs1, rs2, imm |
若 rs1 != rs2,则 PC += imm << 1 |
不相等 |
| BLT | blt rs1, rs2, imm |
若 rs1 < rs2,则 PC += imm << 1 |
有符号数小于 |
| BGE | bge rs1, rs2, imm |
若 rs1 >= rs2,则 PC += imm << 1 |
有符号数大于等于 |
| BLTU | bltu rs1, rs2, imm |
若 rs1 < rs2,则 PC += imm << 1 |
无符号数小于 |
| BGEU | bgeu rs1, rs2, imm |
若 rs1 >= rs2,则 PC += imm << 1 |
无符号数大于等于 |
关键点说明:
- 跳转范围 :由于12位立即数经过符号扩展后左移1位,SB型指令的跳转范围是相对于当前指令的 ±4KB 地址空间。
- 有符号与无符号 :
BLT/BGE将寄存器值当作有符号整数 进行比较,而BLTU/BGEU则当作无符号整数进行比较。这在处理如地址或特定编码的数值时至关重要。
U型指令
RISC-V指令集中的U型指令(Upper Immediate Instructions)主要用于处理20位的大立即数,这些指令的特点是将立即数放置在寄存器的高位,低位则进行补零或与其他地址组合。
| 核心特征 | U型指令的对应设计 |
| :--- | :--- | :--- |
| 主要功能 | 处理20位大立即数,用于构建32位常数或绝对地址 |
| 操作码 (opcode) | 0110111 (LUI) 或 0010111 (AUIPC) |
| 指令格式字段 (从高位到低位) | imm[31:12] | rd | opcode |
| 操作的寄存器 | 一个目的寄存器:rd(用于存放结果) |
| 立即数特点 | 20位立即数(位于指令的31-12位),加载到寄存器的高20位 |
U型指令的编码格式非常规整,仅包含三个部分:
imm[31:12]:20位的立即数,占据指令的最高20位(31-12位)。rd:5位的目的寄存器编号,用于存放操作结果。opcode:7位的操作码,标识指令的具体类型。
常见的U型指令主要有两条:
-
LUI(Load Upper Immediate)- 操作 :将20位的立即数左移12位(即填充低12位为0),然后符号扩展至32位,结果写入目的寄存器
rd。 - 语法 :
lui rd, imm20 - 示例 :
lui x10, 0x12345执行后,寄存器x10中的值为0x12345000。这条指令常用于构建大的常数或绝对地址的高位部分。
- 操作 :将20位的立即数左移12位(即填充低12位为0),然后符号扩展至32位,结果写入目的寄存器
-
AUIPC(Add Upper Immediate to PC)- 操作 :将20位的立即数左移12位并进行符号扩展,然后与当前程序计数器(PC)的值相加 ,结果写入目的寄存器
rd。 - 语法 :
auipc rd, imm20 - 示例 :
auipc x10, 0可以用于获取当前PC的值(因为立即数为0,结果就是PC的值)。这条指令在与I型指令(如addi)或J型指令(如jalr)结合使用时,可以实现相对于PC的远距离跳转或构建位置无关代码的地址。
- 操作 :将20位的立即数左移12位并进行符号扩展,然后与当前程序计数器(PC)的值相加 ,结果写入目的寄存器
UJ型指令
UJ型指令是RISC-V指令集中用于实现长距离无条件跳转 的关键指令格式,最主要的就是JAL(Jump and Link)指令。
| 核心特征 | UJ型指令的对应设计 |
|---|---|
| 主要功能 | 长距离无条件跳转(Jump),主要用于函数调用和远距离跳转 |
| 操作码 (opcode) | 1101111 |
| 指令格式字段 (从高位到低位) | imm[20,10:1,11,19:12] | rd | opcode |
| 操作的寄存器 | 一个目的寄存器:rd(用于保存返回地址) |
| 立即数特点 | 20位立即数(作为偏移量),但被拆分并旋转编码在指令中 |
| 跳转地址计算 | 目标地址 = 当前指令地址 + 符号扩展后的立即数左移1位后的值 |
当CPU执行UJ型指令时,硬件会按照 imm[20] | imm[10:1] | imm[11] | imm[19:12] 的顺序取出这些比特位,然后进行符号扩展 生成一个21位的有符号立即数。这个立即数会左移1位 (即乘以2),因为RISC-V的所有指令地址都必须是2字节对齐的,这样能保证目标地址的有效性。最终,跳转目标地址由公式 目标地址 = PC + 偏移量 计算得出。
JAL
UJ型指令中最核心的就是JAL(Jump and Link)指令,它的操作可以概括为以下两步:
- 保存返回地址 :将下一条指令的地址(即
PC + 4)写入目的寄存器rd中。 - 跳转:将程序计数器(PC)设置为计算出的目标地址。
JAL指令的语法是:jal rd, offset
- 一个常见的用法是
jal ra, target,其中ra(即x1寄存器)通常用作链接寄存器,专门用于存放函数调用后的返回地址。 - 如果跳转后不需要返回(比如单纯的跳转),可以将
rd设置为x0(零寄存器),因为写入x0的值会被丢弃,例如jal x0, target。
寻址方式
- 立即数寻址
- 寄存器寻址
- PC相对寻址
- 基址寻址