学习目标

  • 理解指令格式
  • 实现指令译码器
  • 提取指令字段

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;
}

下一步

第 6 章:指令执行

更新时间: