Skip to content

指令周期

指令执行流程解析

  1. 取指(IF)
    CPU首先根据程序计数器(PC)的值,从内存中取出指令(二进制指令码)。PC实际上是一个指向内存中当前指令位置的指针,确保下一条指令能够正确加载到CPU内部。

  2. 译码(ID)
    取出的二进制指令需要被解析为CPU能理解的操作信息。译码阶段通过查看指令中的操作码(Opcode)和操作数(Operands)字段,将指令转换为相应的操作。常用方式包括利用查找表、硬连线逻辑和多路选择器来区分不同操作类型。

  3. 执行(EX)
    执行阶段中,CPU调用相应的运算单元(如加法器)进行数据处理。例如,遇到加法操作时,从寄存器或内存中获取操作数相加,并将结果存储回目标寄存器或内存。

  4. 更新PC
    指令执行完成后,CPU更新程序计数器(PC)的值,确保下一条指令从正确的位置取出。对于普通指令,PC通常递增指令长度;而对于跳转或分支指令,PC将被更新为指令中特定指定的地址。

下面给出了一个简单的C语言模拟示例,该示例展示了取指、译码、执行以及更新PC的过程:

#include <stdint.h>
#include <stdio.h>

#define NREG 4
#define NMEM 16

// 定义指令格式
typedef union {
  struct { uint8_t rs : 2, rt : 2, op : 4; } rtype;
  struct { uint8_t addr : 4, op : 4; } mtype;
  uint8_t inst;
} inst_t;

#define DECODE_R(inst) uint8_t rt = (inst).rtype.rt, rs = (inst).rtype.rs
#define DECODE_M(inst) uint8_t addr = (inst).mtype.addr

uint8_t pc = 0;       // 程序计数器 (PC) ,采用8位表示
uint8_t R[NREG] = {}; // 寄存器数组
uint8_t M[NMEM] = {   // 内存,包含一个计算 z = x + y 的小程序
  0b11100110,  // load  6#     | R[0] <- M[y]
  0b00000100,  // mov   r1, r0 | R[1] <- R[0]
  0b11100101,  // load  5#     | R[0] <- M[x]
  0b00010001,  // add   r0, r1 | R[0] <- R[0] + R[1]
  0b11110111,  // store 7#     | M[z] <- R[0]
  0b00010000,  // x = 16
  0b00100001,  // y = 33
  0b00000000,  // z = 0
};

int halt = 0; // 结束标志

// 执行一条指令
void exec_once() {
  // 取指过程:从内存中取出当前PC位置的指令
  inst_t this;
  this.inst = M[pc];

  // 根据操作码译码并执行对应的操作
  switch (this.rtype.op) {
    // 操作码为0b0000时,执行寄存器传送操作:R[rt] <- R[rs]
    case 0b0000: { DECODE_R(this); R[rt] = R[rs]; break; }
    // 操作码为0b0001时,执行加法操作:R[rt] <- R[rt] + R[rs]
    case 0b0001: { DECODE_R(this); R[rt] += R[rs]; break; }
    // 操作码为0b1110时,从内存加载数据到R[0]:R[0] <- M[addr]
    case 0b1110: { DECODE_M(this); R[0] = M[addr]; break; }
    // 操作码为0b1111时,将R[0]中的数据存储到内存:M[addr] <- R[0]
    case 0b1111: { DECODE_M(this); M[addr] = R[0]; break; }
    default:
      printf("Invalid instruction with opcode = %x, halting...\n", this.rtype.op);
      halt = 1;
      break;
  }

  // 更新PC,指向下一条指令
  pc++;
}

int main() {
  while (1) {
    exec_once();
    if (halt) break;
  }
  printf("The result of 16 + 33 is %d\n", M[7]);
  return 0;
}

这段代码模拟了一条简单指令执行过程: - 取指:从内存 M[pc] 中取出指令。 - 译码:运用宏 DECODE_RDECODE_M 分析指令格式,解析出操作码和操作数。 - 执行:依照指令类型(如移动、加法、加载、存储)调用对应的操作。 - 更新PC:指令执行后,PC自增,指向下一条指令。

整个流程形成一个不断循环的“取指–译码–执行–更新PC”流程,确保程序被顺序(或按跳转逻辑)执行。