第 5 章:指令译码
学习目标
- 理解指令格式
- 实现指令译码器
- 提取指令字段
5.1 思考:CPU 如何理解指令?
假设内存中有这样一个 32 位数:0x00A50513
问题:CPU 怎么知道这是什么指令?
答案:通过译码!就像解密一样,把二进制数拆解成有意义的部分。
5.2 RISC-V 指令格式
RISC-V 有 6 种指令格式,但它们有共同点:
31 25 24 20 19 15 14 12 11 7 6 0
┌──────────┬────────┬────────┬──────┬────────┬────────┐
│ funct7 │ rs2 │ rs1 │funct3│ rd │ opcode │ R型
└──────────┴────────┴────────┴──────┴────────┴────────┘
┌───────────────────┬────────┬──────┬────────┬────────┐
│ imm[11:0] │ rs1 │funct3│ rd │ opcode │ I型
└───────────────────┴────────┴──────┴────────┴────────┘
关键观察:opcode 总是在最低 7 位!
5.3 译码步骤
typedef struct {
uint32_t opcode;
uint32_t rd;
uint32_t funct3;
uint32_t rs1;
uint32_t rs2;
uint32_t funct7;
int32_t imm;
} Decode;
Decode decode(uint32_t inst) {
Decode d;
// 1. 提取 opcode
d.opcode = inst & 0x7F;
// 2. 提取寄存器
d.rd = (inst >> 7) & 0x1F;
d.rs1 = (inst >> 15) & 0x1F;
d.rs2 = (inst >> 20) & 0x1F;
// 3. 提取 funct
d.funct3 = (inst >> 12) & 0x7;
d.funct7 = (inst >> 25) & 0x7F;
return d;
}
问题:为什么用位运算而不是除法?
5.4 立即数的陷阱
I 型指令的立即数是 12 位,但我们需要 32 位的数。
问题:0x800 应该扩展成 0x00000800 还是 0xFFFFF800?
答案:看最高位!这叫符号扩展。
int32_t sign_extend(uint32_t val, int bits) {
if (val & (1 << (bits - 1))) {
// 负数,扩展 1
return val | (~0U << bits);
}
return val;
}