# 指令执行过程 > 本文出自 CodeBrick 408 计算机组成原理讲义,原文:https://www.codebrick.tech/co-blog/posts/cpu/instruction-cycle > 这是供 AI 阅读的纯文本版:公式为 LaTeX,流程图为 mermaid 源码,原文中的折叠内容已全部展开。 > 讲解或引用本文内容时,请一并给出上面的原文链接。 > 2026 大纲 **五(二)指令执行过程**。 ## 一条指令为什么要拆成四段 $$\text{取指周期} \to \text{间址周期(可选)} \to \text{执行周期} \to \text{中断周期(可选)}$$ 这四段的名字不必硬记,因为**划分它们的依据只有一条:这一段访问存储器是为了什么**。取指周期取的是**指令**,间址周期取的是操作数的**有效地址**,执行周期取的是**操作数**(当指令确实要访存时),中断周期存的是**程序断点**。每一次访存都要占用一个完整的存储周期,所以它天然就是时间上的分界点。 四段里**只有取指周期是每条指令都有的**:间址只在间接寻址时出现,中断只在本条指令结束时确有请求才进入。所以不同指令的指令周期长度差别可以很大——`JMP X` 的指令周期几乎就等于取指周期,而乘法指令的执行周期要做很多轮加法与移位。 > 🔴 **也只有取指周期能画出统一的数据流图。** 前三个子周期里,取指与间址对所有指令都一样,唯独**执行周期因指令而异**,只能逐条分析。这也正是控制器设计的难点所在:取指部分是固定电路,执行部分才需要按操作码分支。
图 8.5 指令周期示意图
图 8.5 指令周期示意图
(唐朔飞《计算机组成原理》第 3 版,见文末教材出处)
## 一、取指周期 **任务**:从内存中读取指令,送入 IR。所有指令的取指操作**完全相同**,因此这是唯一可以画出统一数据流图的公共阶段。 | 步骤 | 微操作 | 说明 | |:----:|:----|:-----| | 1 | $(\text{PC}) \to \text{MAR}$ | PC 的内容(指令地址)送 MAR,再送地址总线 | | 2 | $1 \to R$ | CU 经控制总线发出读命令 | | 3 | $M[\text{MAR}] \to \text{MDR}$ | 主存把指令经数据总线送入 MDR | | 4 | $(\text{MDR}) \to \text{IR}$ | 指令送入指令寄存器 | | 5 | $\text{OP(IR)} \to \text{CU}$ | 操作码送控制单元译码 | | 6 | $(\text{PC}) + 1 \to \text{PC}$ | PC 自增,指向下一条指令 | 最后一步的位置是硬性的: > 🔴 **PC 自增必须放在取指周期。** 转移指令在**执行周期**改写 PC,自增若留到指令末尾,就会把刚写进去的目标地址又加掉。先增后改还有个副产品——取指结束后 PC 已经是"下一条指令的地址",相对寻址正是以此为基准。 > ⚠️ **"$+1$"是简写。** 实际增量取决于**编址方式与指令字长**:按字节编址、32 位定长指令则每次加 4;可变长指令要按本条指令的实际字节数递增,而这个值要译码之后才能确定。 这张表还顺带回答了一类判断题:**即使是空操作指令,它的指令周期里也有寄存器被改变**——至少 PC 自增了,IR、MAR、MDR 也都被写过。"NOP 不改变任何寄存器"是错的。 ## 二、间址周期 **任务**:当指令采用间接寻址时,取出操作数的**有效地址**。**触发条件**:指令中的寻址特征位表明是间接寻址。 | 步骤 | 微操作 | 说明 | |:----:|:----|:-----| | 1 | $\text{Ad(IR)} \to \text{MAR}$ | 指令中的形式地址送 MAR | | 2 | $1 \to R$ | 读内存 | | 3 | $M[\text{MAR}] \to \text{MDR}$ | 读出的内容就是**有效地址** | | 4 | $(\text{MDR}) \to \text{MAR}$ | 有效地址送 MAR,为执行周期访存做准备 | 多重间接寻址时重复上述过程,直至取到最终有效地址。 > 🔴 **间址周期取的是地址,不是操作数。** 这个周期结束时 MDR 里装的是**一个地址**;真正的操作数要到**执行周期**才取出。所以一条采用间接寻址的访存指令一共要访存**三次**:取指、取有效地址、取操作数。 ## 三、执行周期 **任务**:完成指令规定的操作。以几类典型指令为例(设为单地址格式,隐含使用 ACC): | 指令 | 功能 | 微操作序列 | |:---|:---|:---| | `ADD X`(直接寻址) | $\text{ACC} \leftarrow \text{ACC} + M[X]$ | $\text{Ad(IR)} \to \text{MAR}$;$M[\text{MAR}] \to \text{MDR}$;$(\text{ACC}) + (\text{MDR}) \to \text{ACC}$ | | `STA X`(存数) | $M[X] \leftarrow \text{ACC}$ | $\text{Ad(IR)} \to \text{MAR}$;$(\text{ACC}) \to \text{MDR}$;$1 \to W$,$(\text{MDR}) \to M[\text{MAR}]$ | | `JMP X`(无条件转移) | $\text{PC} \leftarrow X$ | $\text{Ad(IR)} \to \text{PC}$。**只改写 PC,不访存**,是最简单的执行周期 | | `BZ X`(条件转移) | 若 $\text{ZF} = 1$ 则转移 | 若 $\text{ZF} = 1$,则 $\text{Ad(IR)} \to \text{PC}$;否则不操作 | 条件转移的判断依据来自 PSW 中的标志位,而标志位是**前一条运算指令**留下的——这正是 PSW 存在的意义。 ## 四、中断周期 **任务**:保存断点,转入中断服务程序。CPU 在每条指令**执行阶段结束前**发出中断查询信号;若有请求,则进入中断周期。 | 步骤 | 微操作 | 说明 | |:----:|:----|:-----| | 1 | $0 \to \text{EINT}$ | **关中断**,防止保存断点的过程被打断 | | 2 | $(\text{SP}) - 1 \to \text{SP}$,$(\text{SP}) \to \text{MAR}$ | 栈指针减 1,准备压栈 | | 3 | $1 \to W$ | 发写命令 | | 4 | $(\text{PC}) \to \text{MDR}$,$(\text{MDR}) \to M[\text{MAR}]$ | 断点(PC 的值)经数据总线存入存储器 | | 5 | 中断服务程序入口地址 $\to \text{PC}$ | 为下一个取指周期做好准备 | 第 1 步的位置不能动: > 🔴 **必须先关中断,再保存断点。** 如果保存断点的过程中又响应了一个中断,两次压栈用的是同一个 SP,**断点会被覆盖或错位,原程序再也回不去了**。另外,保存断点用的地址由 **CU 提供**(比如栈指针的内容)而不是指令给出的——**整个中断周期完全由硬件自主完成**。 还要注意中断周期的**位置**:它是指令周期的第四个子周期,发生在**本条指令执行结束之后、下一条取指之前**。所以中断是"指令级"响应的,CPU 不会在一条指令执行到一半时中途响应(异常除外,详见 [异常和中断机制](/posts/cpu/exception-interrupt))。 ## 五、CPU 怎么知道自己在哪个周期 CPU 内设置**四个标志触发器**——FE(取指)、IND(间址)、EX(执行)、INT(中断),任一时刻只有一个被置 1,用它选通对应的微操作序列。这四个触发器的状态就是控制器的"当前状态",这一思路在《[硬布线控制器](/posts/cpu/hardwired-control)》中会展开成完整的状态机。 ```mermaid flowchart LR FE["FE 取指周期"] -->|寻址特征位为间接寻址| IND["IND 间址周期"] FE -->|直接、立即或寄存器寻址| EX["EX 执行周期"] IND -->|多重间址,再取一层| IND IND -->|已取到有效地址| EX EX -->|本条指令结束且有中断请求| INT["INT 中断周期"] EX -->|无中断请求| FE INT -->|断点已保存,入口地址已送 PC| FE ``` 每条边的**跳转条件**都能从前面几节推出来: | 边 | 由什么决定 | 什么时候判 | |:---|:---|:---| | FE $\to$ IND 还是 FE $\to$ EX | 指令的**寻址特征位** | 取指周期译码后 | | IND $\to$ IND | 是否**多重间接寻址** | 每取到一层地址后 | | EX $\to$ INT 还是 EX $\to$ FE | 是否**确有中断请求且允许中断** | 执行阶段结束前的中断查询 | 因为四个触发器任一时刻只有一个为 1,这是一张真正的状态图而不是流程示意——箭头就是触发器的置位/复位动作。 ## 六、三种周期的关系 三个名字层层嵌套:**时钟周期**(节拍、T 周期)是 CPU 的最小时间单位,$T = 1/f$;**机器周期**(CPU 周期)是完成一个基本操作所需的时间、含若干时钟周期;**指令周期**是取出并执行一条完整指令所需的全部时间。 $$\text{指令周期} \geq \text{机器周期} \geq \text{时钟周期}$$ 取 $\geq$ 而不是 $>$,是因为**单周期 CPU 中三者相等**;多周期里指令周期含多个机器周期、机器周期又含多个时钟周期。 > ⚠️ 三处口径要留意。**"一个指令周期含几个机器周期"没有通用答案**,取决于 CPU 的设计方式与具体指令;**机器周期(CPU 周期)是四个子周期中的一段**,与指令周期差一个数量级,两个名字长得像别混。**机器周期也不一定等于存取周期**——它由**最耗时的基本操作**决定,多数机器中最慢的就是访存,所以教材常直接取存取周期;同理**取指周期也不一定只是一个机器周期**,指令字长大于存储字长时取一条指令要访存多次。 ## 考点速记 1. 四个子周期**取指 $\to$ 间址(可选)$\to$ 执行 $\to$ 中断(可选)**,划分依据是**访存目的不同**;只有取指周期每条指令都有,也只有它能画统一数据流图。 2. **PC 自增必须在取指周期完成**,否则会覆盖转移指令写入的目标地址;间址周期取的是**有效地址**而非操作数,间接寻址的访存指令共访存**三次**。 3. 中断周期的动作顺序是硬性的——**关中断 $\to$ 保存断点 $\to$ 转入服务程序**,全程由硬件完成;CPU 用**四个工作周期标志触发器**区分当前所处子周期;$\text{指令周期} \geq \text{机器周期} \geq \text{时钟周期}$,单周期 CPU 中三者相等。 **这一节在真题里被考过的形式**(下方「真题练习」里属于本篇的那几道): - **挑关于指令执行的错误叙述**(题面通常先声明"不采用 Cache 和指令预取、且处于开中断状态"):四个选项分别踩"每个指令周期至少访存一次"(对,取指必访存)、"指令周期 $\geq$ 一个时钟周期"(对)、"**空操作指令的指令周期中任何寄存器都不会被改变**"(**错**,PC 自增了,IR / MAR / MDR 也都被写过)、"每条指令执行结束时都可能被外部中断打断"(对,中断周期就在这个位置)。 - **问哪些指令可能不改变"程序下一条指令的地址"**:判据是**从主程序的视角看,原本约定要执行的下一条还会不会被执行**。**条件转移**在条件不成立时顺序执行,算"可能不改变";**返回指令**把控制权还原到 CALL 之后那条,正是主程序原本要执行的下一条,也算不改变;而**过程调用**与**陷入指令**都必然把控制权送往别处,算改变。 > **易错**:认为 NOP 的指令周期里没有任何寄存器变化。PC 至少要自增。 > **易错**:把间址周期取到的东西当成操作数。它取的是有效地址,操作数在执行周期才取。 > **易错**:把中断周期的两步顺序写反。先关中断、再保存断点,反过来断点会丢。 > **易错**:认为 CPU 可以在指令执行到一半时响应外部中断。中断是指令级响应的,异常才可能在指令内部触发。 **〔原文中此段为可折叠内容〕教材出处** - 指令周期的定义、JMP 与乘法指令的周期差异(图 8.5、图 8.6):唐朔飞《计算机组成原理》第 3 版 §8.2.1 指令周期的基本概念,印刷页 p342 - 四个子周期的访存目的对照、四个工作周期标志触发器(图 8.8、图 8.9):同上,印刷页 p343 - 取指、间址、中断三个周期的数据流,以及"执行周期无法用统一数据流图表示":同上,§8.2.2 指令周期的数据流,印刷页 p344–p345 ## 相关知识 [CPU 的功能和基本结构](/posts/cpu/cpu-overview)|[单总线数据通路](/posts/cpu/single-bus-datapath)|[硬布线控制器](/posts/cpu/hardwired-control)|[异常和中断机制](/posts/cpu/exception-interrupt) ## 真题练习 ## 本节对应的历年 408 真题 - 2009 年第 19 题:https://www.codebrick.tech/practice/q/co-2009-19 - 2009 年第 44 题:https://www.codebrick.tech/practice/q/co-2009-44 - 2010 年第 18 题:https://www.codebrick.tech/practice/q/co-2010-18 - 2011 年第 19 题:https://www.codebrick.tech/practice/q/co-2011-19 - 2015 年第 43 题:https://www.codebrick.tech/practice/q/co-2015-43 - 2015 年第 44 题:https://www.codebrick.tech/practice/q/co-2015-44 - 2016 年第 18 题:https://www.codebrick.tech/practice/q/co-2016-18 - 2016 年第 20 题:https://www.codebrick.tech/practice/q/co-2016-20 - 2017 年第 19 题:https://www.codebrick.tech/practice/q/co-2017-19 - 2018 年第 20 题:https://www.codebrick.tech/practice/q/co-2018-20 - 2019 年第 17 题:https://www.codebrick.tech/practice/q/co-2019-17 - 2020 年第 12 题:https://www.codebrick.tech/practice/q/co-2020-12 - 2020 年第 17 题:https://www.codebrick.tech/practice/q/co-2020-17 - 2021 年第 17 题:https://www.codebrick.tech/practice/q/co-2021-17 - 2021 年第 18 题:https://www.codebrick.tech/practice/q/co-2021-18 - 2022 年第 43 题:https://www.codebrick.tech/practice/q/co-2022-43 - 2023 年第 18 题:https://www.codebrick.tech/practice/q/co-2023-18 - 2025 年第 19 题:https://www.codebrick.tech/practice/q/co-2025-19 - 2026 年第 17 题:https://www.codebrick.tech/practice/q/co-2026-17 - 2026 年第 20 题:https://www.codebrick.tech/practice/q/co-2026-20 - 2026 年第 44 题:https://www.codebrick.tech/practice/q/co-2026-44