Mas0n
to be reverse engineer🐧
翻车鱼

RISC-V base architecture

RISC-V base architecture

了解RISCV体系结构以及其基础指令集RV32I指令格式组成。

Programmers’ Model for Base Integer ISA

RISC-V的体系结构是模块化的。即可以在基础指令之上添加扩展指令,这一点相比于ARM32以及x86-32体系具有绝大的优势。

RISC-V最基础的指令是RV32I,即32位指令。这是所有RISC-V处理器都需实现的指令。RISC-V体系结构可以在这个基础指令集上进行扩展,例如:RV64I,64位的基础指令扩展;RV32M,乘法指令扩展;RV32F,单精度浮点扩展;RV32D,双精度浮点扩展等。下表列出了RISC-V体系的一些指令集。

NameTypeDescrtion
RV32I基础指令整数指令,包含算术、分支、逻辑、访存指令,有32个32位寄存器,能寻址32位地址空间
RV32E基础指令与RV32I一样,只不过只能使用前16个32位寄存器
RV64I基础指令整数指令,包含算术、分支、逻辑、访存指令,有32个64位寄存器,能寻址64位地址空间
RV128I基础指令整数指令,包含算术、分支、逻辑、访存指令,有32个128位寄存器,能寻址128位地址空间
M扩展指令包含乘法、除法、求模取余指令
F扩展指令单精度(32bit)浮点指令
D扩展指令双精度(32bit)浮点指令,必须要同时支持F扩展指令
Q扩展指令四倍精度浮点指令
A扩展指令存储器原子操作指令,比如比较并交换,读-改-写等指令
C扩展指令压缩指令,指令长度为16位,主要用于改善程序大小
P扩展指令单指令多数据(Packed-SIMD)指令
B扩展指令位操作指令
H扩展指令支持 Hypervisor 管理指令
J扩展指令动态翻译语言的指令
L扩展指令十进制浮点指令
N扩展指令用户中断指令
G通用指令包含 I、M、A、F、D指令

Register

将RISC-V通用寄存器(RV32I)作为典型。

对于RV32I而言,CPU拥有32个通用寄存器,每个寄存器的宽度为32bit(XLEN=32)。其中x0寄存器硬编码恒为0,即x0寄存器上的值恒为0。另外,RISC-V还有一个pc寄存器用于保存当前指令的地址。

https://cdn.shi1011.cn/2022/08/e6a70cf8797c622faffa3d41eeb1473d.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

标准软件调用约定规定x1用于保存调用的返回地址,x2作为堆栈指针,x5用作临时寄存器或备用链接寄存器。下表列出了标准调用约定下的寄存器使用规范。(可能存在勘误)

这里需要注意Saver有以下定义

  • Caller:由调用者保存
  • Callee:由被调用者保存
RegisterABI NameDescrtionSaver
x0zero0值寄存器,硬编码为0,写入数据忽略,读取永远为0(Hardwired Zero)
x1ra返回地址(Return Address)Caller
x2sp栈指针(Stack Pointer)Callee
x3gp全局指针(Global Pointer)
x4tp线程指针(Thread Pointer)
x5t0临时寄存器或备用链接寄存器(Temporary / alternate Link Register)Caller
x6-x7t1-t2临时寄存器(Temporary)Caller
x8s0/fp需要保存的寄存器或者帧指针寄存器(Saved Register / Frame Pointer)Callee
x9s1需要保存的寄存器,保存原进程中的关键数据,避免在函数调用过程中被破坏(Saved Register)Callee
x10-x11a0-a1函数参数/返回值(Function Arguments / Return Value)Caller
x12-x17a2-a7函数参数(Function Arguments)Caller
x18-x27s2-s11需要保存的寄存器(Saved Register)Callee
x28-x31t3-t6临时寄存器(Temporary)Caller
    
pc   

Base Instruction Formats

在RV32I ISA中,有四种核心指令格式(R/I/S/U),如下图所示。

https://cdn.shi1011.cn/2022/08/7a35e48a0035c90c344da51a8c3a976e.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

很明显,指令格式有:

  • major opcode位于0-6bit上
  • 当存在目标寄存器时,其保存于7-11bit上
  • 当存在第一个源寄存器时,其保存在15-19bit上
  • 当存在第二个源寄存器时,其保存在20-24bit上
  • 余下的位用于minor opcode或指令的其他数据(func3位于12-14bit,func7位于25-31bit)

而多少位用于立即数取决于指令中存在的寄存器号个数:

  • R-type:存在一个目标寄存器和两个源寄存器的指令时没有立即数。例如两个寄存器相加(ADD
  • L-type:存在一个目标寄存器和一个源寄存器的指令拥有12bits的立即数,例如一个寄存器相加(ADDI
  • S-type:存在两个源寄存器且没有目标寄存器的指令,例如STORE指令,也有12bits的立即数,但因为各个寄存器位于不同的位置,立即数也必须位于不同的位置。
  • U-type:只存在一个目标寄存器,没有minor opcode的指令,例如LUI指令,拥有20bits的立即数(major opcode和目标寄存器号需要12bits)

Immediate Encoding Variants

额外的,RV32I还有两种变种指令格式(B/J),如下图所示。

https://cdn.shi1011.cn/2022/08/4dc7380c92057ed4bd383ce065710c39.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

S与B之间的唯一区别在于B-type将12bits的立即数,左移了一位。而不是将整条指令移位。按照惯例,中间的imm[10:1]和符号位imm[12]保持不变,而S-type中的最低位inst[7]在B中将作为最高位进行编码。

类似,J-type的立即数字段是在U-type基础上左移了12位。

因为B-type和J-type的立即数都没有bit 0位,所以它们的立即数都是2的整数倍。

RISC-V实际上只有四种基本格式,但可以保守的认为有六种格式。

  • 源寄存器和目标寄存器都设计固定在所有 RISC-V 指令同样的位置上,指令译码相对简单,所以指令在 CPU 流水线中执行时,可以先开始访问寄存器,然后再完成指令解码。
  • 所有立即数的符号位总是在指令的最高位。这么做的好处是,有可能成为关键路径的立即数符号扩展可以在指令解码前进行,有利于 CPU 流水线的时序优化。

RV32I Instruction Formats

如下图所示,对于每幅图,将有下划线的字母从左往右连接起来,就组成了完整的RV32I指令集。对于每一个图,集合标志{}内列举了指令的所有变体,变体用加下划线的字母或下划线字符_表示。

https://cdn.shi1011.cn/2022/08/f8ec5a9beeff83e3b1a1d1296323c8fb.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

下图展示了RV32I的基础指令组成。

https://cdn.shi1011.cn/2022/08/600bbf09afbae193d271cf5e0fe71900.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

Integer Operate Instructions

Register – Register

下图展示了寄存器-寄存器的整数运算指令布局,操作码,格式类型和名称的操作码映射。

https://cdn.shi1011.cn/2022/08/eb24ea584431dd501b1a2f350d210fef.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

相应的指令汇编格式如下表所示

Instruction NameasmPseudo C
addadd rd, rs1, rs2rd = rs1 + rs2
subsub rd, rs1, rs2rd = rs1 - rs2
andand rd, rs1, rs2rd = rs1 & rs2
oror rd, rs1, rs2rd = rs1 | rs2
xorxor rd, rs1, rs2rd = rs1 ^ rs2
sltslt rd, rs1, rs2rd = rs1 < rs2(signed)
sltusltu rd, rs1, rs2rd = rs1 < rs2(unsigned)
sllsll rd, rs1, rs2rd = rs1 << rs2(Logical)
srlsrl rd, rs1, rs2rd = rs1 >> rs2(Logical)
srasra rd, rs1, rs2rd = rs1 >> rs2(Arithmetic)

Immediate

下图展示了立即数的整数运算指令布局,操作码,格式类型和名称的操作码映射。

https://cdn.shi1011.cn/2022/08/4101318a0e0024c08b18b533c87548a6.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

注:在立即数算术指令中,没有减法运算。

相应的指令汇编格式如下表所示

Instruction NameasmPseudo C
addiaddi rd, rs1, imm[11:0]rd = rs1 + imm[11:0]
andiandi rd, rs1, imm[11:0]rd = rs1 & imm[11:0]
oriori rd, rs1, imm[11:0]rd = rs1 | imm[11:0]
xorixori rd, rs1, imm[11:0]rd = rs1 ^ imm[11:0]
sltislti rd, rs1, imm[11:0]rd = rs1 < imm[11:0](signed)
sltiusltiu rd, rs1, imm[11:0]rd = rs1 < imm[11:0](unsigned)
sllisll rd, rs1, shamt[4:0]rd = rs1 << shamt[4:0](Logical)
srlisrl rd, rs1, shamt[4:0]rd = rs1 >> shamt[4:0](Logical)
sraisra rd, rs1, shamt[4:0]rd = rs1 >> shamt[4:0](Arithmetic)

Upper Immediate

下图展示了U-type 32bits指令布局,操作码,格式类型和名称的操作码映射。

https://cdn.shi1011.cn/2022/08/6d4f82afdf0b9fe822d1dff8aa740249.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

lui(Load Upper Immediate)

lui rd, imm # 将20bits的立即数左移12位,低12位补零写回rd中

配合addi指令(设置低12bit)可将寄存器设置为任意32bits的立即数

lui x12, 0x12345     # x12 = 0x12345000
addi x12, x12, 0x321 # x12 = 0x12345321

但是,当这个12bits的立即数为负数(即最高比特位是1)时,得到的结果是高20位减1后与低12位拼接

lui x10, 0x12345     # x10 = 0x12345000
addi x10, x10, 0xeef # x10 = 0x12344eef

解决这个问题的方法之一:如果低12位的立即数的符号位是1,我们可以使用li 指令预先给高20位的数加 1。

auipc(Add Upper Immediate to PC)

auipc rd, imm # 将20bits的立即数左移12位,低12位补零得到的32bits数与pc的值相加写到寄存器rd

例如

Label: auipc x10, 0 # 将Label的地址保存在x10寄存器中

Load / Store

下图展示了Load/Store 指令布局,操作码,格式类型和名称的操作码映射。

https://cdn.shi1011.cn/2022/08/0eb302d3de5c3ae5cfa50888cb003813.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

Load和Store的寻址模式是符号扩展 12 位立即数加上基地址寄存器的形式,这在 x86-32 中被称为位偏移寻址模式,不同于x86-32或ARM-32,RV32I省略了ARM-32和x86-32的复杂寻址模式。使得CPU流水线可以对数据冲突提前做出判断,并通过流水线各级之间的转送加以处理,而不需要插入空操作(NOP),极大提高了代码的执行效率。

补充:Load指令属于 I 型指令,而 Store 指令属于 S 型指令。

相应的指令汇编格式如下表所示

Instruction NameasmDescription
lwlw rd, offset[11:0](rs1)字加载(load word)
lhlh rd, offset[11:0](rs1)半字加载(load halfword)
lhulhu rd, offset[11:0](rs1)无符号半字加载(load halfword unsigned)
lblb rd, offset[11:0](rs1)字节加载(load byte)
lbulbu rd, offset[11:0](rs1)无符号字节加载(load byte unsigned)
swsw rs2, offset[11:0](rs1)字存储(store word)
shsh rs2, offset[11:0](rs1)半字存储(store halfword)
sbsb rs2, offset[11:0](rs1)字节存储(store byte)

Control Transfer Instructions

Unconditional Jumps

无条件跳转分为直接跳转和相对跳转。

注:直接跳转是 J 型指令,而相对跳转是 I 型指令。

直接跳转

https://cdn.shi1011.cn/2022/08/71988e949e63e03982105fd752db149a.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

jal指令的执行过程:

  1. 将20bits的立即数作符号扩展并左移一位,产生一个32bits的符号数
  2. 将该32bits符号数和pc相加得到跳转地址(这样jal可以作为短跳转指令,跳转至 pc±1mb 范围内)
  3. 同时,jal会把紧随其后的那条指令的地址,即pc+4,存入目标寄存器(通常为ra)中。如果目标寄存器为0,则jal等同于 goto 指令;如果目标寄存器不为0,jal可以实现函数调用的功能。

相对跳转

https://cdn.shi1011.cn/2022/08/1a6f7ad3ee7ca314908b95ce74e73975.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

jalr指令会把12bits的立即数和源寄存器相加(通常ra作为源寄存器,x0作为目标寄存器),并把相加的结果末位清零,作为新的跳转地址。和jal指令一样,jalr也会把紧随其后的那条指令的地址,存入目标寄存器中。

相应的指令汇编格式如下所示

jal rd, label # rd=pc+4, pc+=offset

伪指令 j 实际上是jal指令的变体,rd被设置为x0,即丢弃返回地址。

jalr rd, rs1, imm # rd=pc+4, pc=rs1+imm

跳转到任意32位绝对地址

lui x1, <hi 20 bits>
jalr ra, x1, <lo 12 bits>

相对pc的偏移跳转

auipc x1, <hi 20 bits>
jalr x0, x1, <lo 12 bits>  # 无条件跳转

Conditional Branches

有条件跳转

https://cdn.shi1011.cn/2022/08/6ecfd5c9006b0db0b17f8e8604c8c088.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

相应的指令汇编格式如下表所示

Instruction NameasmPseudo C
beqbeq rs1, rs2, label相等跳转
bnebne rs1, rs2, label不等跳转
bltblt rs1, rs2, label小于跳转
bltubltu rs1, rs2, label无符号小于跳转
bgebge rs1, rs2, label大于等于跳转
bgeubgeu rs1, rs2, label无符号大于等于跳转

CSR Instructions

除了内存地址空间和通用寄存器地址空间外,RISC-V 中还定义了一个独立的控制与状态寄存器(CSR)地址空间。

https://cdn.shi1011.cn/2022/08/9627d145d668dd859a51136aeed4c0da.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

专用的CSR指令

https://cdn.shi1011.cn/2022/08/6f454e6a3a587ce60dc2bd4b00ce0732.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

这6条CSR指令又可以分为两类

  • 寄存器操作:CSRRW,CSRRS,CSRRC
  • 立即数操作:CSRRWI,CSRRSI,CSRRCI

对于立即数操作的3个指令,5bits的立即数被放在了rs1的位置,即上图所示的imm,同时也被称为uimm[4:0],因为这些立即数是无符号(unsigned immediate)的。

CSRRW

CSRRS

CSRRC

CSRRWI

CSRRSI

CSRRCI

Other Instructions

  • 系统调用 ecall 指令
  • 调试时用于将控制转移到调试环境的 ebreak 指令

Refenrence

[1] https://lab.cs.tsinghua.edu.cn/cod-lab-docs/labs/4-riscv-inst

[2] https://github.com/riscv/riscv-isa-manual/releases/download/draft-20200727-8088ba4/riscv-spec.pdf

[3] http://riscvbook.com/chinese/RISC-V-Reader-Chinese-v2p1.pdf

[4] https://suda-morris.github.io/blog/cs/risc-v.html

[6] https://www.cs.cornell.edu/courses/cs3410/2019sp/riscv/interpreter

[7] D. Patterson and A. Waterman, The RISC-V reader. Berkeley: Strawberry Canyon LLC, 2018.

本文链接:https://blog.shi1011.cn/learn/2354
本文采用 CC BY-NC-SA 4.0 Unported 协议进行许可

Mas0n

文章作者

发表回复

textsms
account_circle
email

翻车鱼

RISC-V base architecture
了解RISCV体系结构以及其基础指令集RV32I指令格式组成。 Programmers' Model for Base Integer ISA RISC-V的体系结构是模块化的。即可以在基础指令之上添加扩展指令,这一点相比于A…
扫描二维码继续阅读
2022-08-31