Appearance
一条指令在CPU里的一生——模拟器使用指南
大纲定位
五(二)指令执行过程 · 五(三)数据通路的功能和基本结构
要求:能把一条指令在数据通路上的完整流转讲清楚——每一步用到哪个部件、数据从哪来到哪去。
这个模拟器解决什么问题
指令执行的五个阶段(IF→ID→EX→MEM→WB)和控制信号是这一章的骨架。数据通路图能看懂、控制信号表能列出,但两者合在一起时常常对不上——"这条指令到底走了哪些部件?哪一拍是哪个信号在起作用?"
这个模拟器让你跟着一条指令"走一遍" CPU,每一步都能看到哪些部件被点亮、哪些信号被激活。按顺序走完三个场景大约 15 分钟。
界面速览
| 位置 | 控件 | 作用 |
|---|---|---|
| 顶部 | 认识 CPU / 跟着指令走 / 换条指令试试 | 三个场景,建议按此顺序 |
| 场景三 | 指令下拉框 | lw $t0,0($t1) 取数 / sw $t0,0($t1) 存数 / beq $t1,$t2,+4 分支 |
| 中部 | 数据通路图 | 场景一里灰色等你点;场景二三里随阶段逐个点亮 |
| 右侧 | 信息面板 | 当前部件/阶段的说明与激活的控制信号 |
| 底部 | ◀ | ▶/⏸ | ▶ | 上一步 | 播放暂停 | 下一步 |
| 底部 | 速度下拉 |「重新播放」 | 0.5x / 1x / 2x | 跑到末尾才出现 |
建议一律用单步 ▶,别连播。 这个模拟器的价值在于"停下来看当前这一拍激活了哪些信号",连播会把它变成一段看不清的动画。
教学模型说明
模拟器采用 MIPS 风格信号命名(ALUSrc/RegWrite/MemToReg)。另一种常见写法是单总线结构 + 微操作传送语句,信号名各不相同,见 单总线数据通路。模拟器负责建立"指令流经部件"的画面感,这个直觉在两套体系里是通用的。
场景一:认识 CPU
打开模拟器后默认进入这个场景。你看到的是一个简化的单周期数据通路图——所有部件灰色,等你点击。
你应该点击的七个部件
1. PC(程序计数器)
点击后弹出说明:"我记住下一条指令的地址。每执行一条,自动 +4。" PC 自增是硬件自动完成的,不需要控制信号。
2. 指令存储器(IM)
存放所有指令。PC 给地址,IM 返回对应的 32 位指令编码。
哈佛结构
指令存储器和数据存储器分开——这就是"哈佛结构"的思想。分开的好处是取指和访存可以同时进行(流水线需要这一点)。
3. 控制器
指挥官。根据 opcode(和 funct)生成所有控制信号。控制信号在译码阶段就全部确定,但各信号在不同阶段才起作用。
4. 寄存器堆
32 个寄存器($0–$31),可同时读两个(rs、rt 端口)、写一个。$0 永远为 0。
5. ALU
做加减与或比较等运算。输出运算结果 + Zero 标志(结果为 0 时 Zero=1)。
6. 数据存储器(DM)
主存。lw 从这里读数据,sw 往这里写数据。R 型指令不用它。
7. MUX(多路选择器)
数据通路上有两个关键 MUX:
- ALU 输入 MUX:ALUSrc 控制,=0 选寄存器,=1 选立即数
- 写回 MUX:MemToReg 控制,=0 选 ALU 结果,=1 选内存值
右侧信息面板
始终显示当前指令的:
- 汇编格式和二进制编码
- 指令字段分解(op/rs/rt/rd/shamt/funct 或 op/rs/rt/imm)
- 关键寄存器的当前值
场景二:跟着一条指令走
点击"跟着指令走"标签,进入核心场景。默认指令是 add $t0, $t1, $t2($t1=5, $t2=3)。
动画的五个阶段
每一步都可以暂停(⏸)、单步推进(▶)、回退(◀):
阶段 ① 取指(IF)
| 你能看到什么 | 对应知识点 |
|---|---|
| PC 高亮 → 地址流向指令存储器 → 指令编码取出 | 取指过程 |
| +4 加法器高亮 → PC 更新为 PC+4 | PC 自增 |
取指阶段的两件事
取指阶段做两件事:①取指令 ②PC+4。这两件事是硬件自动完成的,所有指令都一样,不需要控制信号参与。
阶段 ② 译码(ID)
| 你能看到什么 | 对应知识点 |
|---|---|
| 指令编码拆成 6 个字段,每个字段不同颜色 | 指令格式 |
| opcode 送入控制器 → 控制信号逐个亮起(绿=1,红=0) | 控制信号生成 |
rs/rt 字段送入寄存器堆 → 读出 $t1=5, $t2=3 | 寄存器读取 |
记忆要点
控制器在译码阶段就一次性生成所有控制信号。比如 RegWrite=1 虽然在 ID 就确定了,但它在 WB 阶段才真正控制寄存器堆写入。
阶段 ③ 执行(EX)
| 你能看到什么 | 对应知识点 |
|---|---|
| ALUSrc MUX 高亮 → ALUSrc=0 → 选寄存器值 | ALU 输入选择 |
| 两个操作数(5, 3)进入 ALU → 输出 ALU Result=8 | ALU 运算 |
| Zero 信号输出(8≠0 → Zero=0) | Zero 标志 |
易混
R 型指令 ALUSrc=0(两个输入都来自寄存器)。lw/sw 的 ALUSrc=1(第二个输入是立即数)。但 beq 虽然是 I 型,ALUSrc=0(做寄存器比较,不用立即数做 ALU 运算)。
阶段 ④ 访存(MEM)
| 你能看到什么 | 对应知识点 |
|---|---|
| 数据存储器保持灰色 → MemRead=0, MemWrite=0 | add 不访存 |
| ALU 结果直接穿过 | 只有 lw/sw 用数据存储器 |
WARNING
add 不需要数据存储器,但阶段不能跳过——硬件是固定连线的,只是控制信号让数据存储器"什么都不做"。这个理解对后面学流水线非常重要。
阶段 ⑤ 写回(WB)
| 你能看到什么 | 对应知识点 |
|---|---|
| MemToReg MUX 高亮 → MemToReg=0 → 选 ALU 结果 | 写回数据来源 |
结果 8 写回寄存器堆 → $t0 从 0 变为 8 | RegWrite=1 |
完成后出现绿色汇总面板,列出五个阶段的关键动作和信号。
场景三:换条指令试试
这是最有价值的场景。从下拉菜单选择不同指令,看它走的路径和 add 有什么不同。
三条可选指令
| 指令 | 和 add 的关键差异 |
|---|---|
lw $t0, 0($t1) | ALUSrc=1(用立即数算地址),MemRead=1(要访存),MemToReg=1(写回内存值) |
sw $t0, 0($t1) | MemWrite=1(写内存),RegWrite=0(没有写回阶段) |
beq $t1, $t2, +4 | ALU 做减法比较,Branch=1,Zero=1 时 PC 跳转,RegWrite=0(没有写回) |
对比表(核心差异)
右侧面板会自动展示和 add 的控制信号对比表。差异项高亮显示:
| 信号 | add | lw | sw | beq |
|---|---|---|---|---|
| ALUSrc | 0 | 1 | 1 | 0 |
| MemRead | 0 | 1 | 0 | 0 |
| MemWrite | 0 | 0 | 1 | 0 |
| RegWrite | 1 | 1 | 0 | 0 |
| MemToReg | 0 | 1 | × | × |
| Branch | 0 | 0 | 0 | 1 |
| RegDst | 1 | 0 | × | × |
记忆技巧
只需要记住 add 的控制信号(全部都是"默认值"),然后记住每种指令和 add 哪里不一样:
- lw:ALUSrc、MemRead、MemToReg 变 1,RegDst 变 0(写 rt 不是 rd)
- sw:ALUSrc 变 1,MemWrite 变 1,RegWrite 变 0(不写回)
- beq:Branch 变 1,RegWrite 变 0,ALU 做减法
推荐使用流程
第一轮:建立直觉(5 分钟)
- 认识 CPU:点击每个部件,搞清楚它的功能
- 跟着 add 指令走:0.5x 慢速看完五个阶段
- 展开每个阶段的说明,画面 + 文字对照理解
第二轮:对比差异(10 分钟)
- 切到"换条指令试试"
- 依次选 lw → sw → beq,看每条指令的数据通路
- 重点看右侧对比表:哪些信号不同?为什么不同?
- 自己试着默写四种指令的控制信号表,然后用模拟器验证
第三轮:自己复现(按需)
- 合上模拟器,凭记忆手画一遍数据通路,标出某条指令激活的部件
- 再打开模拟器逐阶段对照,看漏了哪一段
- 重点检查两处:控制信号是不是在 ID 段一次性生成的,以及不用的部件是"跳过"还是"空过"
概念去哪里学
本文只讲怎么用这个模拟器。它演示到的知识点都有专文: