RV32I指令集及其编码方式解读

2024-05-09 20:28

本文主要是介绍RV32I指令集及其编码方式解读,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • RISCV指令集的模块化
  • RV32I
    • R型指令
    • I型指令
      • load指令 (I型)
    • S型指令
    • B型指令 (S型指令变体)
        • PC相对地址
        • B型指令
        • 简单举例
    • U型指令
      • LUI指令
        • li伪指令
      • AUIPC指令
    • J型指令(U型指令变体)
      • JAL指令
      • JALR指令(I型指令)
  • 参考


前言

R I S C − V RISC-V RISCV 表示精简指令集计算机 R I S C ( R e d u c e d I n s t r u c t i o n S e t C o m p u t e r ) RISC(Reduced \ Instruction \ Set \ Computer) RISC(Reduced Instruction Set Computer) 的第五代指令集。其主要特点在于:指令长度固定,指令数量精简,通常在一个时钟周期内完成,且 R I S C V RISCV RISCV 完全开源。

而与其相对的 C I S C ( C o m p l e x I n s t r u c t i o n S e t C o m p u t e r ) CISC(Complex \ Instruction \ Set \ Computer) CISC(Complex Instruction Set Computer) 相比更加简洁高效。我们熟知的 x 86 x86 x86 其经过多年发展以及向前兼容的要求使得指令集非常繁杂,且授权费用高昂。

面向 32 32 32 位的 R I S C V RISCV RISCV 指令集称为 R V 32 RV32 RV32 ,另外对应有 64 64 64 位的 R V 64 RV64 RV64 128 128 128 位的 R V 128 RV128 RV128。 本文将以 R V 32 RV32 RV32 为主体,向大家详细介绍 32 32 32 位基础指令集 R V 32 I RV32I RV32I 的指令编码及其具体格式以及一些指令编码时立即数位置奇怪的原因。

RISCV指令集的模块化

R I S C V RISCV RISCV 的指令集按照不同的功能分为不同的子模块,以 R V 32 RV32 RV32 为例,模块用 R V 32 RV32 RV32 +字母命名,一个 R I S C V RISCV RISCV 指令集必须包含基础指令集 R V 32 I RV32I RV32I 以及可选的扩展部分,扩展部分包括标准扩展和用户自定义扩展。基础以及部分标准扩展指令集如下表所示:

RV32模块全称用途
RV32IBase Integer Instruction Set加减法,访问内存,控制转移分支指令,环境调用断点,内存屏障
RV32MInteger Multiplication and Division整数乘法除法
RV32FSingle-Precision Floating Point单精度浮点数
RV32DDouble-Precision Floating Point双精度浮点数
RV32QQuad-Precision Floating Point四倍精度浮点数
RV32CCompressed Instruction压缩指令,指令字长16bit,用于对指令大小有限制的环境
RV32AAtomic Instruction原子指令,用于OS支持
RV32VVector Operation向量运算
RV32E通用寄存器变为16个,用作简单嵌入式设备,其余和RV32I基本一致

表中并未包含全部标准扩展,仅列出部分作为参考。

RV32I

R V 32 I RV32I RV32I 中共有指令格式 6 6 6 种,核心四种为 R I S U R \ I \ S \ U R I S U。六种指令格式分别是:
1 1 1 R R R 型指令,用于寄存器和寄存器之间的算术运算。
2 2 2 I I I 型指令,用于寄存器和立即数之间的算术运算以及用于从内存中加载数据。
3 3 3 S S S 型指令,用于向内存存储数据。
4 4 4 B B B 型指令,用于短距离分支指令。 S S S 型指令的变体。
5 5 5 U U U 型指令,用于立即数高 20 b i t 20bit 20bit 操作指令。
6 6 6 J J J 型指令,用于长距离跳转。 U U U 型指令的变体。

其中,通用寄存器 32 32 32 个,从 x 0 − x 31 x0-x31 x0x31,共需 5 b i t 5bit 5bit 表示。 其中, x 0 x0 x0 被硬连线到 0 0 0
另有一个寄存器: p c pc pc ,用于存储当前正在执行的指令的地址。

四种主要的指令编码方式如下图所示:
在这里插入图片描述
可以看到,为了方便硬件设计,所有指令都保持 o p c o d e r s r d f u n c t opcode \ rs \ rd \ funct opcode rs rd funct 字段在寄存器位置的一致 (有例外,但 R V 32 I RV32I RV32I 中没有)。 f u n c t 3 funct3 funct3 字段主要用于区分具体的指令。

f u n c t 7 funct7 funct7 字段主要用于以后的扩展,指令中只使用很少一部分。例如: S U B , S U B W , S R A , S R A I , S R A I W SUB, SUBW, SRA, SRAI, SRAIW SUB,SUBW,SRA,SRAI,SRAIW 指令使用 f u n c t 7 = 0 b 0100000 funct7=0b0100000 funct7=0b0100000 相对于指令 A D D , A D D W , S R L , S R L I , S R L I W ADD, ADDW, SRL, SRLI, SRLIW ADD,ADDW,SRL,SRLI,SRLIW f u n c t 7 = 0 b 0000000 funct7=0b0000000 funct7=0b0000000 只有很小的改动,该位主要用于对结果进行符号扩展。

其中, o p c o d e opcode opcode 长度为 7 7 7,占据指令 i n s t [ 6 : 0 ] inst[6:0] inst[6:0] 。一种 o p c o d e opcode opcode 代表了一种类别的操作, o p c o d e opcode opcode 具体映射如下表所示:

在这里插入图片描述
1: o p c o d e opcode opcode 最低两位 i n s t [ 1 : 0 ] inst[1:0] inst[1:0] 必须为 1 1 1 则指令有效。
2: o p c o d e opcode opcode 并不完全由 R I S U J B RISUJB RISUJB 这些指令格式决定,同样是 I I I 型指令, l o a d a d d i load \ \ addi load  addi 有着不同的 o p c o d e opcode opcode

另外,标准原话:"There is no dedicated stack pointer or subroutine return address link register in the Base Integer ISA; the instruction encoding allows any x register to be used for these purposes." 标准基础指令集中并未规定某一个特定寄存器用于堆栈指针和函数返回值的存储。但是按照调用约定, x 1 x1 x1 用于存储返回值,而 x 2 x2 x2 用于作为堆栈指针使用。具体如下图所示:

在这里插入图片描述

R型指令

R R R 型指令主要用于寄存器-寄存器之间的算术运算,不包含立即数。
例如: a d d x 18 , x 19 , x 10 add \ x18, x19, x10 add x18,x19,x10,其中, x 18 x18 x18 r d ( D e s t i n a t i o n R e g i s t e r ) rd(Destination \ Register) rd(Destination Register) 目的寄存器, x 19 x19 x19 r s 1 ( s o u r c e r e g i s t e r ) rs1(source \ register) rs1(source register) 源寄存器, x 10 x10 x10 r s 2 ( s o u r c e r e g i s t e r ) rs2(source \ register) rs2(source register) 源寄存器。

R R R 型指令的编码方式如下图所示:
在这里插入图片描述所有 R R R 型指令的 o p c o d e opcode opcode 一样,都为 i n s t [ 6 : 0 ] = 0 b 0110011 inst[6:0]=0b0110011 inst[6:0]=0b0110011,对应上述 o p c o d e opcode opcode 映射表中 O P OP OP 表项。
所有 R R R 型指令的编码方式及其各字段的值如下表所示:

inst[31:25]inst[24:20]inst[19:15]inst[14:12]inst[11:7]inst[6:0]指令助记符
0000000rs2rs1000rd0110011add
0100000rs2rs1000rd0110011sub
0000000rs2rs1001rd0110011sll
0000000rs2rs1010rd0110011slt
0000000rs2rs1011rd0110011sltu
0000000rs2rs1100rd0110011xor
0000000rs2rs1101rd0110011srl
0100000rs2rs1101rd0110011sra
0000000rs2rs1110rd0110011or
0000000rs2rs1111rd0110011and

s r a ( s h i f t r i g h t a r i t h m e t i c ) sra(shift \ right \ arithmetic) sra(shift right arithmetic) 算术右移指令需要高位补符号位。
位运算中没有逐位取反操作,若要对寄存器 x 10 x10 x10 的内容逐位取反,只需: x o r x 10 , x 10 , x 0 xor \ x10,x10,x0 xor x10,x10,x0。此时, r d = 1 0 10 = 0 b 01010 rd=10_{10}=0b01010 rd=1010=0b01010 r s 1 = 1 0 10 = 0 b 01010 rs1=10_{10}=0b01010 rs1=1010=0b01010 r s 2 = 0 10 = 0 b 00000 rs2=0_{10}=0b00000 rs2=010=0b00000,因此指令二进制表示为: 0 b 0000000 00000 01010 100 01010 0110011 0b0000000 \ 00000 \ 01010 \ 100 \ 01010 \ 0110011 0b0000000 00000 01010 100 01010 0110011

I型指令

I I I 型指令主要用于寄存器-立即数之间的算术运算,也用于 l o a d load load 指令的指令格式。
例如: a d d x 18 , x 19 , − 50 add \ x18, x19, -50 add x18,x19,50,其中, x 18 x18 x18 r d ( D e s t i n a t i o n R e g i s t e r ) rd(Destination \ Register) rd(Destination Register) 目的寄存器, x 19 x19 x19 r s 1 ( s o u r c e r e g i s t e r ) rs1(source \ register) rs1(source register) 源寄存器, − 50 -50 50 为立即数 ( i m m e d i a t e ) (immediate) (immediate)
I I I 型指令的编码方式对比 R R R 型指令如下图所示:
在这里插入图片描述 I I I 型指令中用于算术运算的指令 o p c o d e opcode opcode 一样,都为 i n s t [ 6 : 0 ] = 0 b 0010011 inst[6:0]=0b0010011 inst[6:0]=0b0010011,对应上述 o p c o d e opcode opcode 映射表中 O P − I M M OP-IMM OPIMM 表项。
其中,立即数用 12 b i t 12bit 12bit 补码表示,其具体表示范围为: [ − 2048 , 2047 ] [-2048,2047] [2048,2047]。所有立即数通过符号扩展到 32 b i t 32bit 32bit 之后再进行运算。
所有 I I I 型指令的编码方式及其各字段的值如下表所示:

inst[31:20]inst[19:15]inst[14:12]inst[11:7]inst[6:0]指令助记符
imm[11:0]rs1000rd0010011addi
imm[11:0]rs1010rd0010011slti
imm[11:0]rs1011rd0010011sltiu
imm[11:0]rs1100rd0010011xori
imm[11:0]rs1110rd0010011ori
imm[11:0]rs1111rd0010011andi
0000000+shamt[4:0]rs1001rd0010011slli
0000000+shamt[4:0]rs1101rd0010011srli
0100000+shamt[4:0]rs1101rd0010011srai

1:伪指令 m v mv mv 用于寄存器值的拷贝, m v x 10 , x 19 mv \ x10, x19 mv x10,x19 指令具体实现方式为: a d d i x 10 , x 19 , 0 addi \ x10,x19,0 addi x10,x19,0
2:伪指令 n o p nop nop 用于 n o o p e r a t i o n no \ operation no operation n o p nop nop 指令具体实现方式为: a d d i x 0 , x 0 , 0 addi \ x0,x0,0 addi x0,x0,0
3:立即数逻辑运算中,最高有效位保持和 R R R 型指令 f u n c t 7 funct7 funct7 字段一致的 7 7 7 位宽度,方便硬件设计。
4:同时,由于寄存器字长为 32 32 32,故移位量 ( s h a m t = s h i f t a m o u n t ) (shamt=shift \ amount) (shamt=shift amount) 只需要考虑在 [ 0 , 31 ] [0,31] [0,31] 即可,只需要 5 b i t 5bit 5bit 编码。关于在具体设计中移位长度大于 32 32 32 则直接结果置 0 0 0 还是对移位量按照 32 32 32 取余需要根据具体的架构设计。我在这里有过简要分析:移位案例。

load指令 (I型)

l o a d load load 指令用于从内存中读取数据到寄存器。
R V 32 I RV32I RV32I 提供了一个 32 b i t 32bit 32bit 的内存地址空间,其根据字节寻址。一个可能的 l o a d load load 指令: l w x 10 , 2 ( x 2 ) lw \ x10,2(x2) lw x10,2(x2),用于从 ( x 2 ) + 2 (x2)+2 (x2)+2 所指向的内存地址取一个字。其中, r d rd rd 寄存器为 x 10 x10 x10 ,数据宽度为字长 w o r d word word,基地址寄存器 b a s e / r s 1 base/rs1 base/rs1 x 2 x2 x2,偏移量 o f f s e t offset offset 2 2 2
l o a d load load 指令格式和 I I I 型指令一致。具体如下图所示:
在这里插入图片描述其中, l o a d load load 指令的 o p c o d e opcode opcode o p c o d e opcode opcode 映射表中第一项 l o a d load load o p c o d e = 0 b 0000011 opcode=0b0000011 opcode=0b0000011
其中,基址偏移量 o f f s e t offset offset 12 b i t 12bit 12bit 补码表示,其具体表示范围为: [ − 2048 , 2047 ] [-2048,2047] [2048,2047]。通过符号扩展到 32 b i t 32bit 32bit 之后和基地址相加。
所有 l o a d load load 指令编码方式及个字段的值如下表所示:

inst[31:20]inst[19:15]inst[14:12]inst[11:7]inst[6:0]指令助记符
imm[11:0]rs1000rd0000011lb
imm[11:0]rs1001rd0000011lh
imm[11:0]rs1010rd0000011lw
imm[11:0]rs1100rd0000011lbu
imm[11:0]rs1101rd0000011lhu

1: l b ( l o a d b y t e ) lb \ (load \ byte) lb (load byte) 指令用于从内存中读取一字节内容,将其符号扩展之后放入 r d rd rd 寄存器。而与其对应的 l b u ( l o a d b y t e u n s i g n e d ) lbu \ (load \ byte \ unsigned) lbu (load byte unsigned) 用于从内存读取一字节内容,将其 0 0 0 扩展后放入 r d rd rd 寄存器。 l h ( l o a d h a l f w o r d ) lh \ (load \ half \ word) lh (load half word) l h u ( l o a d h a l f w o r d u n s i g n e d ) lhu \ (load \ half \ word \ unsigned) lhu (load half word unsigned) 同理。
2: w i d t h width width 编码中后两位表示宽度,最高位表示 u n s i g n e d unsigned unsigned

S型指令

s t o r e store store 指令用于把寄存器中的数据放入指定内存位置。
R V 32 I RV32I RV32I 提供了一个 32 b i t 32bit 32bit 的内存地址空间,其根据字节寻址。一个可能的 s t o r e store store 指令: s w x 10 , 8 ( x 2 ) sw \ x10,8(x2) sw x10,8(x2),用于把寄存器 x 10 x10 x10 的内容放入 ( x 2 ) + 8 (x2)+8 (x2)+8 的内存地址空间。其中,不存在目的寄存器 r d rd rd,源寄存器 r s 1 rs1 rs1 寄存器为 x 10 x10 x10 ,存储数据宽度为字长 w o r d word word,源寄存器 r s 2 rs2 rs2 x 2 x2 x2,偏移量 o f f s e t offset offset 8 8 8
s t o r e store store 指令含 r s 1 r s 2 o f f s e t rs1 \ \ rs2 \ \ offset rs1  rs2  offset 但不含 r d rd rd,因此需要一种单独编码格式。
S S S 型指令格式如下图所示:
在这里插入图片描述1:其中, o p c o d e f u n c t 3 r s 1 r s 2 opcode\ \ funct3 \ \ rs1 \ \ rs2 opcode  funct3  rs1  rs2 的位置和前面指令保持一致。立即数位置变动。
2:标准原话:"the instruction format was chosen to keep all register specifiers at the same position in all formats at the expense of having to move immediate bits across formats ",以立即数的编码移动为代价来使得所有寄存器在统一位置。
3:其中, s t o r e store store 指令的 o p c o d e opcode opcode o p c o d e opcode opcode 映射表中 S T O R E STORE STORE o p c o d e = 0 b 0100011 opcode=0b0100011 opcode=0b0100011
4:其中,基址偏移量 o f f s e t offset offset 12 b i t 12bit 12bit 补码表示,其具体表示范围为: [ − 2048 , 2047 ] [-2048,2047] [2048,2047]。通过符号扩展到 32 b i t 32bit 32bit 之后和基地址相加。
所有 s t o r e store store 指令编码方式及个字段的值如下表所示:

inst[31:25]inst[20:24]inst[19:15]inst[14:12]inst[11:7]inst[6:0]指令助记符
imm[11:5]rs2rs1000imm[4:0]0100011sb
imm[11:5]rs2rs1001imm[4:0]0100011sh
imm[11:5]rs2rs1010imm[4:0]0100011sw

以上文例子 s w x 10 , 8 ( x 2 ) sw \ x10,8(x2) sw x10,8(x2) 为例,其 o f f s e t = 0 b 0000000 01000 offset=0b0000000 \ 01000 offset=0b0000000 01000 r s 2 = 0 b 01010 rs2=0b01010 rs2=0b01010 r s 1 = 0 b 00010 rs1=0b00010 rs1=0b00010 f u n c t 3 = 0 b 010 funct3=0b010 funct3=0b010,综上,该指令二进制表示为: 0 b 0000000 01010 00010 010 01000 0100011 0b0000000 \ \ 01010 \ \ 00010 \ \ 010 \ \ 01000 \ \ 0100011 0b0000000  01010  00010  010  01000  0100011

B型指令 (S型指令变体)

B B B 型指令为条件分支指令,用于在条件满足时跳转到指定标签位置。
一个可能的 b r a n c h branch branch 分支指令: b e q x 1 , x 2 , l a b e l beq \ x1,x2,label beq x1,x2,label,用于比较寄存器 x 1 x1 x1 x 2 x2 x2 的值是否相等,相等程序跳转到 l a b e l label label 分支位置。其中,不存在目的寄存器 r d rd rd,源寄存器 r s 1 rs1 rs1 寄存器为 x 10 x10 x10 ,源寄存器 r s 2 rs2 rs2 x 2 x2 x2,条件成立跳转位置为 l a b e l label label

PC相对地址

在当今操作系统中,可执行文件执行时由于映射到内存虚拟地址空间的位置的不确定性所以需要类似 w i n d o w s windows windows 可执行文件中的重定位表来对一些地址做重定位。因此编译器在编译源代码过程中会尽量使用PC相对地址,介绍 R V 32 I RV32I RV32I 时有提到除通用寄存器外还有寄存器 p c pc pc 用于存储当前执行指令地址。分支指令中待跳转目的地址在指令编码中会记录为相对当前PC的单元偏移量,减少重定位的开销。

简言之,分支指令的跳转不以字节为单位。而在 R V 32 I RV32I RV32I 中,基本单元为 2 2 2 字节。不以 1 1 1 字节作为单元的单位的原因在于: R V 32 I RV32I RV32I 中所有指令的长度都为 4 4 4 字节,避免分支跳转到一条指令的中间位置。以 2 2 2 字节为基本单元的原因在于: R I S C V RISCV RISCV 中有 R V 32 C RV32C RV32C 扩展,该扩展用于把指令长度缩短到 2 2 2 字节,用于对机器码长度有限制的场景中。为了使得在该扩展用仍旧适用,取基本单元大小为 2 2 2 字节。

B型指令

B B B 型指令的指令编码与 S S S 型指令对比如下图所示:
在这里插入图片描述保持和 S S S 型指令中 o p c o d e f u n c t 3 r s 1 r s 2 opcode \ \ funct3 \ \ rs1 \ \ rs2 opcode  funct3  rs1  rs2 位置相同。合理调整立即数各 b i t bit bit 的位置。
0:立即数 i m m e d i a t e immediate immediate 表示待跳转标签和当前 P C PC PC 内存地址的差值。以字节为单位。 B B B 型指令中跳转基本单元为 2 2 2 字节,故最低位不考虑,指令中不记录该位。
1:立即数的最高位永远位于指令中的最高有效位 M S B MSB MSB。主要原因:"In RISC-V the sign bit for all immediates is always held in bit 31 of the instruction to allow sign-extension to proceed in parallel with instruction decoding.",该标准原话告诉我们立即数的最高位永远位于指令的最高有效位方便在指令解码的同时进行符号扩展。
2:立即数编码奇怪的第二原因:" immediates is chosen to maximize overlap with the other formats and with each other.",立即数需要尽量和其余编码格式中立即数位置重合,方便逻辑的复用。
3: B B B 型指令中首先立即数最高位 i m m [ 12 ] imm[12] imm[12] 位于 i n s t [ 31 ] inst[31] inst[31],其次 i m m [ 10 : 5 ] imm[10:5] imm[10:5] 位于 i n s t [ 30 : 25 ] inst[30:25] inst[30:25] S S S 型保持一致。其次,立即数 i m m [ 4 : 1 ] imm[4:1] imm[4:1] 位于 i n s t [ 11 : 8 ] inst[11:8] inst[11:8] S S S型保持一致。最后一位填充到 i n s t [ 7 ] inst[7] inst[7]
4: B B B 型指令中记录 13 b i t 13bit 13bit 立即数的高 12 b i t 12bit 12bit,最低位一直为 0 0 0,跳转的地址空间范围: [ − ( 2 ∗ 2 11 ) , 2 ∗ ( 2 11 − 1 ) ] = [ − 4096 , 4094 ] [-(2*2^{11}),2*(2^{11}-1)]=[-4096,4094] [(2211),2(2111)]=[4096,4094]
5: B B B 型指令的 o p c o d e opcode opcode 0 b 1100011 0b1100011 0b1100011
所有 B B B 型指令编码及其字段值如下所示:

inst[31:25]inst[20:24]inst[19:15]inst[14:12]inst[11:7]inst[6:0]指令助记符
imm[12 |10:5]rs2rs1000imm[4:1 | 11]1100011beq
imm[12 |10:5]rs2rs1001imm[4:1 | 11]1100011bne
imm[12 |10:5]rs2rs1100imm[4:1 | 11]1100011blt
imm[12 |10:5]rs2rs1101imm[4:1 | 11]1100011bge
imm[12 |10:5]rs2rs1110imm[4:1 | 11]1100011bltu
imm[12 |10:5]rs2rs1111imm[4:1 | 11]1100011bgeu
简单举例

以下面RISCV代码为例,给出其分支指令的二进制表示:

Loop: beq x19,x10,End
add x18,x18,x10
addi x19,x19,-1
j Loop
End: # target instruction

假设当前 P C PC PC 指向分支指令 b e q x 19 , x 10 , E n d beq \ x19,x10,End beq x19,x10,End 的地址, R V 32 I RV32I RV32I 指令定长 32 b i t 32bit 32bit,那么标签 E n d End End 的地址为 P C + 16 PC+16 PC+16。立即数以 2 2 2 字节为单元,所有 i m m = 8 imm=8 imm=8 r s 2 = 0 b 01010 = 1 0 10 rs2=0b01010=10_{10} rs2=0b01010=1010 r s 1 = 0 b 10011 = 1 9 10 rs1=0b10011=19_{10} rs1=0b10011=1910 f u n c t 3 = 0 b 000 funct3=0b000 funct3=0b000,综上,该指令二进制表示为: 0 b 0000000 01010 10011 000 01000 1100011 0b0000000 \ \ 01010 \ \ 10011 \ \ 000 \ \ 01000 \ \ 1100011 0b0000000  01010  10011  000  01000  1100011

U型指令

分支指令的跳转范围有限。考虑标准库 m m a p mmap mmap 的地址位置和当前文件距离较大时则需要更大的跳转范围。例如: b e q x 10 , x 0 , f a r beq \ x10,x0,far beq x10,x0,far f a r far far 距离当前pc距离很大,则考虑以下形式改写指令:

bne x10,x0,next
j far
next:
# next instruction

上述含立即数类型的指令中立即数用 12 b i t 12bit 12bit 表示,对应的, U U U 型指令给出了控制立即数高 20 b i t 20bit 20bit 的指令。
U U U 型指令编码格式如下图所示,具体仅有指令 L U I LUI LUI A U I P C AUIPC AUIPC
在这里插入图片描述

LUI指令

L U I ( l o a d u p p e r i m m e d i a t e ) LUI \ (load \ upper \ immediate) LUI (load upper immediate) 用于把立即数的值写入目的寄存器高 20 b i t 20bit 20bit,并将目的寄存器低 12 b i t 12bit 12bit 清零。
L U I LUI LUI 指令的 o p c o d e opcode opcode 由映射表可知为 0 b 0110111 0b0110111 0b0110111
结合 a d d i addi addi 指令写寄存器的低 12 b i t 12bit 12bit ,可以达到控制寄存器 32 b i t 32bit 32bit 的目的。例如:

lui x10, 0x87654      # x10 = 0x87654000
addi x10, x10, 0x321  # x10 = 0x87654321

其中, r d = 0 b 01010 rd=0b01010 rd=0b01010 i m m = 0 x 1000 0111 0110 0101 0100 imm=0x1000 \ 0111 \ 0110 \ 0101 \ 0100 imm=0x1000 0111 0110 0101 0100,故指令二进制及十六进制表示为: 0 b 10000111011001010100 01010 0110111 = 0 x 87654537 0b10000111011001010100 \ 01010 \ 0110111=0x87654537 0b10000111011001010100 01010 0110111=0x87654537

特殊的点,假如当前需求为设置 x 10 x10 x10 寄存器内容为 0 x D E A D B E E F 0xDEADBEEF 0xDEADBEEF

lui x10, 0xDEADB      # x10 = 0xDEADB000
addi x10, x10, 0xEEF  # x10 = 0xDEADAEEF

由于立即数在加法指令执行前会符号扩展,低 12 b i t 12bit 12bit 相加时直接相加,结果正确。高 20 b i t 20bit 20bit 相加时 0 x D E A D B 0xDEADB 0xDEADB 与符号扩展的全 1 1 1 补码表示为 − 1 -1 1 相加,故而高 20 b i t 20bit 20bit 比预期少 1 1 1
所以:在立即数低 12 b i t 12bit 12bit 的最高有效位为 1 1 1 时,设置高 20 b i t 20bit 20bit 时需要对立即数加 1 1 1 处理。如下所示:

lui x10, 0xDEAADC      # x10 = 0xDEADC000
addi x10, x10, 0xEEF  # x10 = 0xDEADBEEF
li伪指令

l i ( l o a d i m m e d i a t e ) li \ (load \ immediate) li (load immediate) 用于加载立即数到目的寄存器。
l i x 10 , 0 x 87654321 li \ x10, 0x87654321 li x10,0x87654321 即把立即数 0 x 87654321 0x87654321 0x87654321 加载到寄存器 x 10 x10 x10 中。该伪指令最终借由指令 l u i x 10 , 0 x 87654 lui \ x10, 0x87654 lui x10,0x87654 a d d i x 10 , x 10 , 0 x 321 addi \ x10, x10, 0x321 addi x10,x10,0x321 实现。

AUIPC指令

1: A U I P C ( a d d u p p e r i m m e d i a t e t o P C ) AUIPC \ (add \ upper \ immediate \ to \ PC) AUIPC (add upper immediate to PC) 用于把立即数左移 12 b i t 12bit 12bit,低位补 0 0 0 形成 o f f s e t offset offset,并将 o f f s e t + p c offset+pc offset+pc 放入目的寄存器, p c pc pc a u i p c auipc auipc 指令所在的地址。
2: A U I P C AUIPC AUIPC 指令用于辅助 P C PC PC 相对寻址。指令可以通过 Label: AUIPC x10, 0 拿到当前 l a b e l label label 的地址(位于 x 10 x10 x10 寄存器中)。
3: A U I P C AUIPC AUIPC 指令的 o p c o d e opcode opcode 由映射表可知为 0 b 0010111 0b0010111 0b0010111

4:指令 A U I P C x 10 , 0 AUIPC \ x10, 0 AUIPC x10,0 r d = 0 b 01010 rd=0b01010 rd=0b01010 i m m = 0 x 0000 0000 0000 0000 0000 imm=0x0000 \ 0000 \ 0000 \ 0000 \ 0000 imm=0x0000 0000 0000 0000 0000,故指令二进制及十六进制表示为: 0 b 00000000000000000000 01010 0010111 = 0 x 00000517 0b00000000000000000000 \ 01010 \ 0010111=0x00000517 0b00000000000000000000 01010 0010111=0x00000517

J型指令(U型指令变体)

远距离跳转指令有: J A L JAL JAL J A L R JALR JALR,不同于分支指令, J J J J A L JAL JAL 指令拥有更远的跳转空间,配合 U U U 型指令可以更加灵活。

JAL指令

J A L JAL JAL 指令通过立即数并以 2 2 2 字节为单位形成有符号立即数和当前 p c pc pc 相加形成目的地址,用于远距离跳转并将跳转指令的下一条指令的地址放入目的寄存器 r d rd rd,方便函数返回。举例见下文。
J A L ( J u m p a n d l i n k ) JAL \ (Jump \ and \ link) JAL (Jump and link) 指令编码对比 I I I 型和 U U U 型如下图所示:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述 J A L JAL JAL 指令 o p c o d e opcode opcode 由映射表可知为: 0 b 1101111 0b1101111 0b1101111。保持最高位位于指令 i n s t [ 31 ] inst[31] inst[31] 位置,立即数部分尽量与已有指令最大重合方便复用。 J A L JAL JAL 指令可用于函数调用,例如:

JAL ra, Func
# next instruction
...
Func:
addi sp, sp, -48 # 一个可能的函数栈帧结构开始位置
...

1: J A L JAL JAL 指令的寻址范围: 20 b i t 20bit 20bit 有符号立即数的 2 2 2 字节寻址单元,最低为为 0 0 0 ,则以字节为单位的寻址范围为: [ − ( 2 ∗ 2 19 ) , 2 ∗ ( 2 19 − 1 ) ] [-(2*2^{19}),2*(2^{19}-1)] [(2219),2(2191)],范围约为 ± 1 M i B \pm1MiB ±1MiB
2:伪指令 j l a b e l j \ label j label 用于无条件跳转到 l a b e l label label 位置,同时不在意返回地址。最终实现方式为: j a l x 0 , l a b e l jal \ x0, label jal x0,label 丢弃返回地址。

JALR指令(I型指令)

J A L R JALR JALR 指令通过立即数给出相对源寄存器rs的偏移量,符号扩展到 32 b i t 32bit 32bit,与 r s rs rs 的值相加之后作为跳转的目的地址,同时记录跳转指令下一条指令地址到目的寄存器 r d rd rd,方便函数返回。指令格式: j a l r r d , r s , i m m e d i a t e jalr \ rd, \ rs, \ immediate jalr rd, rs, immediate,具体示例见下文。
J A L R ( J u m p a n d l i n k r e g i s t e r ) JALR \ (Jump \ and \ link \ register) JALR (Jump and link register) 指令编码对比 I I I 型指令如下图所示:
在这里插入图片描述在这里插入图片描述 J A L JAL JAL 指令 o p c o d e opcode opcode 由映射表可知为: 0 b 1100111 0b1100111 0b1100111。保持最高位位于指令 i n s t [ 31 ] inst[31] inst[31] 位置,立即数部分与 I I I 型指令一样。 J A L R JALR JALR 指令可用于 32 b i t 32bit 32bit 绝对地址函数调用,或已知的 32 b i t P C 32bit \ PC 32bit PC 相对地址的值。例如,已知待跳转函数地址为 0 x 87654321 0x87654321 0x87654321

lui x5,0x87654    # x5 = 0x87654000
jalr ra,x5,0x321  # ra = address of next instruction, pc = 0x87654321
# next instruction

1:伪指令 j r ( j u m p r e g i s t e r ) jr \ (jump \ register) jr (jump register) 用于无条件跳转到目的寄存器地址。其具体实现方式: j r x 5 = j a l r x 0 , x 5 , 0 jr \ x5 = jalr \ x0, x5, 0 jr x5=jalr x0,x5,0
2:伪指令 r e t ( r e t u r n ) ret \ (return) ret (return) 用于函数返回。其具体实现方式: r e t = j r r a = j a l r x 0 , r a , 0 ret = jr \ ra = jalr \ x0, ra, 0 ret=jr ra=jalr x0,ra,0
3:以指令 j a l r r a , x 5 , 0 x 321 jalr \ ra,x5,0x321 jalr ra,x5,0x321 为例,其偏移量 o f f s e t = 0 x 321 = 0 b 001100100001 offset=0x321=0b001100100001 offset=0x321=0b001100100001 ,基地址寄存器 b a s e ( x 5 ) = 0 x 00101 base(x5)=0x00101 base(x5)=0x00101,目的寄存器 r d ( r a / x 1 ) = 0 b 00001 rd(ra/x1)=0b00001 rd(ra/x1)=0b00001,综上,其二进制表示为: 0 b 001100100001 00101 000 00001 1100111 0b001100100001 \ 00101 \ 000 \ 00001 \ 1100111 0b001100100001 00101 000 00001 1100111

另有指令 e c a l l e b r e a k f e n c e ecall \ ebreak \ fence ecall ebreak fence 未详细介绍,读者可详细参考指令集文档。

参考

R I S C V RISCV RISCV 指令集标准 unpriv-isa-asciidoc.pdf

完结撒花。

这篇关于RV32I指令集及其编码方式解读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/974417

相关文章

MySQL中的MVCC底层原理解读

《MySQL中的MVCC底层原理解读》本文详细介绍了MySQL中的多版本并发控制(MVCC)机制,包括版本链、ReadView以及在不同事务隔离级别下MVCC的工作原理,通过一个具体的示例演示了在可重... 目录简介ReadView版本链演示过程总结简介MVCC(Multi-Version Concurr

关于Gateway路由匹配规则解读

《关于Gateway路由匹配规则解读》本文详细介绍了SpringCloudGateway的路由匹配规则,包括基本概念、常用属性、实际应用以及注意事项,路由匹配规则决定了请求如何被转发到目标服务,是Ga... 目录Gateway路由匹配规则一、基本概念二、常用属性三、实际应用四、注意事项总结Gateway路由

解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

《解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)》该文章介绍了使用Redis的阻塞队列和Stream流的消息队列来优化秒杀系统的方案,通过将秒杀流程拆分为两条流水线,使用Redi... 目录Redis秒杀优化方案(阻塞队列+Stream流的消息队列)什么是消息队列?消费者组的工作方式每

解读静态资源访问static-locations和static-path-pattern

《解读静态资源访问static-locations和static-path-pattern》本文主要介绍了SpringBoot中静态资源的配置和访问方式,包括静态资源的默认前缀、默认地址、目录结构、访... 目录静态资源访问static-locations和static-path-pattern静态资源配置

MySQL中时区参数time_zone解读

《MySQL中时区参数time_zone解读》MySQL时区参数time_zone用于控制系统函数和字段的DEFAULTCURRENT_TIMESTAMP属性,修改时区可能会影响timestamp类型... 目录前言1.时区参数影响2.如何设置3.字段类型选择总结前言mysql 时区参数 time_zon

MySQL中的锁和MVCC机制解读

《MySQL中的锁和MVCC机制解读》MySQL事务、锁和MVCC机制是确保数据库操作原子性、一致性和隔离性的关键,事务必须遵循ACID原则,锁的类型包括表级锁、行级锁和意向锁,MVCC通过非锁定读和... 目录mysql的锁和MVCC机制事务的概念与ACID特性锁的类型及其工作机制锁的粒度与性能影响多版本

Redis过期键删除策略解读

《Redis过期键删除策略解读》Redis通过惰性删除策略和定期删除策略来管理过期键,惰性删除策略在键被访问时检查是否过期并删除,节省CPU开销但可能导致过期键滞留,定期删除策略定期扫描并删除过期键,... 目录1.Redis使用两种不同的策略来删除过期键,分别是惰性删除策略和定期删除策略1.1惰性删除策略

Redis与缓存解读

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步... 目录缓存缓存优缺点缓存更新策略超时剔除先删缓存再更新数据库旁路缓存(先更新数据库,再删缓存)先更新数

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据