文章目录
- 前言
- 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 RISC−V 表示精简指令集计算机 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模块 | 全称 | 用途 |
---|---|---|
RV32I | Base Integer Instruction Set | 加减法,访问内存,控制转移分支指令,环境调用断点,内存屏障 |
RV32M | Integer Multiplication and Division | 整数乘法除法 |
RV32F | Single-Precision Floating Point | 单精度浮点数 |
RV32D | Double-Precision Floating Point | 双精度浮点数 |
RV32Q | Quad-Precision Floating Point | 四倍精度浮点数 |
RV32C | Compressed Instruction | 压缩指令,指令字长16bit,用于对指令大小有限制的环境 |
RV32A | Atomic Instruction | 原子指令,用于OS支持 |
RV32V | Vector 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
x0−x31,共需
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] | 指令助记符 |
---|---|---|---|---|---|---|
0000000 | rs2 | rs1 | 000 | rd | 0110011 | add |
0100000 | rs2 | rs1 | 000 | rd | 0110011 | sub |
0000000 | rs2 | rs1 | 001 | rd | 0110011 | sll |
0000000 | rs2 | rs1 | 010 | rd | 0110011 | slt |
0000000 | rs2 | rs1 | 011 | rd | 0110011 | sltu |
0000000 | rs2 | rs1 | 100 | rd | 0110011 | xor |
0000000 | rs2 | rs1 | 101 | rd | 0110011 | srl |
0100000 | rs2 | rs1 | 101 | rd | 0110011 | sra |
0000000 | rs2 | rs1 | 110 | rd | 0110011 | or |
0000000 | rs2 | rs1 | 111 | rd | 0110011 | and |
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
OP−IMM 表项。
其中,立即数用
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] | rs1 | 000 | rd | 0010011 | addi |
imm[11:0] | rs1 | 010 | rd | 0010011 | slti |
imm[11:0] | rs1 | 011 | rd | 0010011 | sltiu |
imm[11:0] | rs1 | 100 | rd | 0010011 | xori |
imm[11:0] | rs1 | 110 | rd | 0010011 | ori |
imm[11:0] | rs1 | 111 | rd | 0010011 | andi |
0000000+shamt[4:0] | rs1 | 001 | rd | 0010011 | slli |
0000000+shamt[4:0] | rs1 | 101 | rd | 0010011 | srli |
0100000+shamt[4:0] | rs1 | 101 | rd | 0010011 | srai |
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] | rs1 | 000 | rd | 0000011 | lb |
imm[11:0] | rs1 | 001 | rd | 0000011 | lh |
imm[11:0] | rs1 | 010 | rd | 0000011 | lw |
imm[11:0] | rs1 | 100 | rd | 0000011 | lbu |
imm[11:0] | rs1 | 101 | rd | 0000011 | lhu |
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] | rs2 | rs1 | 000 | imm[4:0] | 0100011 | sb |
imm[11:5] | rs2 | rs1 | 001 | imm[4:0] | 0100011 | sh |
imm[11:5] | rs2 | rs1 | 010 | imm[4:0] | 0100011 | sw |
以上文例子 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]
[−(2∗211),2∗(211−1)]=[−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] | rs2 | rs1 | 000 | imm[4:1 | 11] | 1100011 | beq |
imm[12 |10:5] | rs2 | rs1 | 001 | imm[4:1 | 11] | 1100011 | bne |
imm[12 |10:5] | rs2 | rs1 | 100 | imm[4:1 | 11] | 1100011 | blt |
imm[12 |10:5] | rs2 | rs1 | 101 | imm[4:1 | 11] | 1100011 | bge |
imm[12 |10:5] | rs2 | rs1 | 110 | imm[4:1 | 11] | 1100011 | bltu |
imm[12 |10:5] | rs2 | rs1 | 111 | imm[4:1 | 11] | 1100011 | bgeu |
简单举例
以下面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)]
[−(2∗219),2∗(219−1)],范围约为
±
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
完结撒花。