皋陶 发表于 2020-10-20 22:01:39

RISC-V 入门 Part3: 指令格式

本帖最后由 皋陶 于 2020-10-20 22:01 编辑

先贴几个图

[*]RV32I 指令图



[*]下面是具体的指令格式





[*]下面是指令格式格式的展开




上面几张图可以在包云岗老师翻译的 RISC-V book 里面找到。
图 2.2 显示了六种基本指令格式,分别是:用于寄存器-寄存器操作的 R 类型指令,用于短立即数和访存 load 操作的 I 型指令,用于访存 store 操作的 S 型指令,用于条件跳转操作的 B 类型指令,用于长立即数的 U 型指令和用于无条件跳转的 J 型指令。图 2.3 使用图 2.2 的指令格式列出了图 2.1 中出现的所有 RV32I 指令的操作码.
为了帮助程序员,所有位全部是 0 是非法的 RV32I 指令。因此, 试图跳转到被清零的 内存区域的错误跳转将会立即触发异常,这可以帮助调试。类似地,所有位全部是 1 的指 令也是非法指令,它将捕获其他常见的错误,诸如未编程的非易失性内存设备、断开连接 的内存总线或者坏掉的内存芯片。
R-Format Instruction
R-Format 用于寄存器-寄存器操作



I-Format Instruction
用于短立即数和访存 load 操作的 I 型指令


很重要的是,RISC-V 所有的立即数都是 signed 的。例如:


除了这种 add imm. load 操作也是 I 型的:


Note: if instruction has immediate, then uses at most 2 registers (one source, one destination)
load 的模式通常是 load rd, rs 或者 load rd, imm(rs).
S-Format
需要保存2个 Register 和一个小的 imm:


RISC-V design decision is move low 5 bits of immediate to where rd field was in other instructions – keep rs1/rs2 fields in same place.因为 store 只是把寄存器的存到内存中,所以没有“目标寄存器”。

以上指令中,你会发现,RISC-V 希望 rs1 rs2 rd 三个寄存器 存在的话即有相同的位置。

[*]By always placing the read sources in the same place, the register file can read without hesitation. If the data ends up being unnecessary (e.g. I-Type), it can be ignored



B-Format 和 Label
BEQx1,x2,Label
上面是一个很正常的跳转指令,但是本身 .label 如何在机器码上存在呢?
在 RISC-V 中,一切都是有内存位置的,包括指令,那么跳转到对应指令的内存即可。在这里,因为 .code 端本身占用内存的大小有限,所以这里使用 PC-Relative Addressing:

[*]使用 imm 字段,表示 PC 的二进制补码偏移量。(Could specify ± 211 addresses offset from the PC)


[*]To improve the reach of a single branch instruction, in principle, could multiply the offset by four bytes before adding to PC (instructions are 4 bytes and word aligned). 因为我们的 RV32I 都是 32bits 的,而且是 4bytes align 的,所以可以把偏移量 * 32


[*]This would allow one branch instruction to reach ± 211 × 32-bit instructions either side of PC
但是,考虑压缩指令,即扩展的 16-bit instructions,RISC-V 需要支持的还有 16bits 的扩展指令。

[*]To enable this, RISC-V always scales the branch offset by 2 bytes - even when there are no 16-bit instructions
所以需要处理 size, 不能 *32,
在 RISC-V 中,conditional branch 逻辑如下:

[*]如果跳转成功了,PC = PC + imm, imm 是二进制补码,是 signed 的
[*]否则,PC = PC + 4

最后,指令的支持形式如下





下面给了一个例子:


以上是 16bytes,而我们有 imm, 偏移量相当于乘了 byte,同时预留了一个虚拟 0 bit, 相当于 *2byte, 实在不够,会使用 branch + jal 的方式支持。
U-Format
用于长立即数的 U 型指令



[*]AUIPC – Add Upper Immediate to PC
[*]LUI – Load Upper Immediate

注意这俩对应的都是高位。可以用 lui + addi 构造 32位的数(毕竟立即数不够大)。
需要注意的是,addi 指令中,写入 imm, 看上去和 lui 没有冲突,但是如果构造 0xDEADBEEF这种,因为这辆都是二进制补码,需要处理低12位中,imm 是 1的问题,这会导致整个结果成一个负数。
这里我们要回到二进制补码的特点:加一个负数的时候加法仍然是按位加的,但是进位是要从高位 -1 的 。这里需要高位再 +1 再处理。



J-Format
下面是 JAL 和 JALR


jal 根据 imm 来跳跃,rd 存储。这个 jal 中遵循和 branch 中一样的测试,即 *2byte


jalr 中,把结果存到寄存器中, 注意,这回儿它就不 *2byte 了

页: [1]
查看完整版本: RISC-V 入门 Part3: 指令格式