计算机组成原理作业

第一章作业

1

假设同一套指令集用不同的方法设计了两种机器 M1 和 M2。机器 M1 的时钟周期为 0.8ns,机器 M2 的时钟周期为 1.2ns。某个程序 P 在机器 M1 上运行时的 CPI 为 4,在 M2 上的 CPI 为 2。对于程序 P 来说,哪台机器的执行速度更快?快多少?

image-20251011140015756

2

假定编译器对某段高级语言程序编译生成两种不同的指令序列 S1 和 S2,在时钟频率为 500MHz 的机器 M 上运行,目标指令序列中用到的指令类型有 A、B、C 和 D 四类。每类指令在 M 上的 CPI 和两个指令序列所用的各类指令条数如下表所示。

指令类型 A B C D
各指令的 CPI 1 2 3 4
S1 的指令条数 5 2 2 1
S2 的指令条数 1 1 1 5
image-20251011140100523

3

假定机器 M 在运行程序 P 的过程中,共执行了 500×10⁶ 条浮点数指令、4000×10⁶ 条整数指令、3000×10⁶ 条访存指令、1000×10⁶ 条分支指令,这 4 种指令的 CPI 分别是 2、1、4、1。若要使程序 P 的执行时间减少一半,浮点指令的 CPI 应如何改进?若要使程序 P 的执行时间减少一半,访存指令和分支指令的 CPI 应如何改进?若浮点指令和整数指令的 CPI 减少 20%,访存指令和分支指令的 CPI 减少 40%,则程序 P 的执行时间会减少多少?

image-20251011140106600
image-20251011140123204

第二章作业

1

假定某计算机的总线采用奇校验,每8位数据有一位校验位,若在32位数据线上传输的信息是 8F 3C AB 96H,则对应的4个校验位应为什么? 若接收方收到的数据信息和校验位分别为87 3C AB 96H0101B,则说明发生了什么情况,并给出验证过程。.0

第一部分:计算原始数据 8F 3C AB 96H 对应的 4 个校验位

前提条件: - 使用奇校验:每个字节(8位)中,1的个数必须是奇数。 - 每8位数据配1位校验位,所以32位数据分成4个字节,对应4个校验位。 - 数据是十六进制:8F 3C AB 96H

我们需要对每一个字节单独计算其奇校验位。

第一步:把每个十六进制字节转换成二进制

  • 8F H = 1000 1111
  • 3C H = 0011 1100
  • AB H = 1010 1011
  • 96 H = 1001 0110

第二步:数每个字节中“1”的个数,然后确定校验位

奇校验规则: 如果当前字节中“1”的个数是偶数,则校验位设为 1,使总数变为奇数;如果是奇数,则校验位设为 0,保持奇数。

我们逐个来看:

  1. 字节 8F = 1000 1111
    • 数“1”:位置0, 4,5,6,7 → 共 5个1 → 是奇数
    • 所以校验位 = 0
  2. 字节 3C = 0011 1100
    • 数“1”:位置2,3,4,5 → 共 4个1 → 是偶数
    • 所以校验位 = 1
  3. 字节 AB = 1010 1011
    • 数“1”:位置0,2,4,6,7 → 共 5个1 → 是奇数
    • 所以校验位 = 0
  4. 字节 96 = 1001 0110
    • 数“1”:位置0,3,5,6 → 共 4个1 → 是偶数
    • 所以校验位 = 1

所以,对应的4个校验位是:0 1 0 1,即 0101B

第二部分:接收方收到的数据是 87 3C AB 96H 和校验位 0101B,发生了什么?验证过程

现在接收方收到: - 数据:87 3C AB 96H - 校验位:0101B

我们怀疑有错误,因为原始发送的是 8F,但收到的是 87 —— 很可能第一个字节出错了!

我们来逐字节验证奇校验是否成立

第一步:将接收到的数据转为二进制

  • 87 H = 1000 0111
  • 3C H = 0011 1100 (没变)
  • AB H = 1010 1011 (没变)
  • 96 H = 1001 0110 (没变)

校验位:0101B → 对应四个字节的校验位分别是:第1字节 0,第2字节 1,第3字节 0,第4字节 1

第二步:验证每个字节 + 校验位 是否满足奇校验

注意:我们验证的是“数据位 + 校验位”一共9位中,1的个数是否为奇数。

  1. 第一字节 87 + 校验位 0
    • 数据位:1000 0111 → “1”的个数:位置0, 5,6,7 → 4个1
    • 加上校验位 0 → 总共还是 4个1 → 是偶数
    • 不满足奇校验!→ 出错!
  2. 第二字节 3C + 校验位 1
    • 数据位:0011 1100 → “1”的个数:4个(偶数)
    • 加上校验位 1 → 总共 4+1=5 → 奇数
    • 正确
  3. 第三字节 AB + 校验位 0
    • 数据位:1010 1011 → “1”的个数:5个(奇数)
    • 加上校验位 0 → 总共 5 → 奇数
    • 正确
  4. 第四字节 96 + 校验位 1
    • 数据位:1001 0110 → “1”的个数:4个(偶数)
    • 加上校验位 1 → 总共 5 → 奇数
    • 正确

结论:只有第一个字节校验失败!说明在传输过程中,第一个字节发生了错误。

进一步分析:哪里出错了?

原始发送的是 8F H = 1000 1111

接收的是 87 H = 1000 0111

对比:

1
2
3
4
原始: 1 0 0 0  1 1 1 1
接收: 1 0 0 0 0 1 1 1

第5位(从左数第5位,或从右数第4位)由1变成了0

所以,第5位发生了翻转错误(bit flip)

🧠 总结

  1. 原始数据 8F 3C AB 96H 的校验位是 0101B
  2. 接收方收到 87 3C AB 96H0101B 后,发现第一个字节校验失败,说明该字节在传输中发生了错误(具体是第5位由1变0)。
  3. 奇校验能检测单个比特错误,但不能纠正它,也不能检测偶数个比特错误。
IMG_20251029_093747

第三章作业

已知 x = 10, y = -6,采用 6位机器数表示。请按如下要求计算并把结果还原成真值。

(1)求 [x + y]补[x - y]补 (2)用原码一位乘法计算 [x × y]原 (3)用布斯算法计算 [x × y]补 (4)用加减交替法计算 [x / y]原 的商和余数

image-20251029194504411
image-20251029185146990
IMG_20251031_171709

第七章作业

1

image-20251126152551121

DRAM 的全称是 Dynamic Random Access Memory,中文叫 动态随机存取存储器

它是计算机中最常用的主存储器(Main Memory / RAM),也就是我们平时常说的“内存条”上的核心芯片。

为什么 DRAM 单元要刷新?(物理原理)

核心原因:漏电。

你可以把 DRAM 的存储单元想象成一个底部有个小针眼的“水桶”(电容),而数据就是“水”(电荷)。

  • 存入数据 “1”: 你给桶里倒满水。
  • 存入数据 “0”: 你把桶倒空。
  • 问题来了: 因为那个小针眼(晶体管的漏电流),满桶的水会慢慢往下漏。
    • 如果你不管它,过一会儿(比如 2ms 后),桶里的水漏干了,原本的 “1” 就变成了 “0”,数据就丢了。

这里最容易混淆的是两个时间概念,必须分清楚:

  1. 最大刷新时间(题目中的 2ms):
    • 这是死线 (Deadline)
    • 意思是:对于每一个水桶,管理员最迟必须每隔 2ms 回来检查一次。如果超过 2ms 没管它,水就漏光了。
  2. 产生刷新信号的间隔(题目要求的答案):
    • 这是管理员处理下一行水桶的频率
    • 关键点: 管理员不能一次性同时刷新几百万个水桶(那样电路会过载,电流太大)。他必须一批一批地(一行一行地)轮流刷新。
adf3c730cec214bcbd0b690a8632d0e1

2

image-20251127234744026

ROM 的全称是 Read-Only Memory,中文叫 只读存储器

核心特点:

  • 字面意思: “Read-Only” 意味着只能读,不能写(或者说在正常工作状态下不能随意写入)。
  • 实际特性(最重要):非易失性 (Non-Volatile)
    • RAM (DRAM/SRAM): 一断电,数据立马消失。
    • ROM: 断电后,数据依然保存。哪怕你把电脑关机放一年,ROM 里的数据也不会丢。

为什么 ROM 总是放在内存地址的最前面(从 0 开始)?

  1. 开机第一件事: 当你按下电脑电源键时,RAM(内存条)里是空的(全是乱码或 0),CPU 无法从 RAM 里读取指令。
  2. 启动引导: CPU 设计为通电后自动去地址 0000H(或其他固定地址)找第一条指令。
  3. 固化程序: 我们必须在这个位置放一个断电也不丢数据的存储器(ROM),里面存着电脑的启动程序(BIOS/UEFI)
    • 它负责检测硬件、初始化系统,然后把操作系统(Windows/Linux)从硬盘搬到 RAM 里。

$\overline{\text{MREQ}} = 0$(即处于低电平/有效状态)时,它的主要作用是通知系统,CPU 当前正打算对内存(RAM 或 ROM)进行访问。

$R/\overline{W}$ (Read / Write Control)

  • 角色:这是 CPU 发出的控制信号(总线信号)。
  • 含义:它告诉整个系统,CPU 当前想要控制数据流向的方向。
  • 状态逻辑
    • 高电平 (1) = Read (读):CPU 准备从总线上接收数据(数据流向:内存 CPU)。
    • 低电平 (0) = Write (写):CPU 准备向总线上发送数据(数据流向:CPU 内存)。

通俗理解:这是 CPU 在喊话:“大家都听好了,我现在是要‘收东西’(Read)还是要‘发东西’(Write)。”

$\overline{WE}$ (Write Enable)

  • 角色:这是 RAM 芯片上的输入引脚(控制端)。
  • 含义:允许写入信号。它决定了内存芯片是否允许将数据总线上的电平记录到存储单元中。
  • 符号含义:顶部的横线表示低电平有效 (Active Low)
  • 状态逻辑
    • 低电平 (0) = 允许写入:内存芯片打开“大门”,将数据总线上的数据存入当前地址指向的单元。
    • 高电平 (1) = 禁止写入:内存芯片处于读取模式(配合其他信号)或待机模式,不会修改内部存储的数据。

通俗理解:这是内存芯片身上的一个开关。只有把这个开关拉下来(变低),内存才会乖乖地把数据“记下来”。

d11ce94cd3db7edfb1cd4a3fa6dadf39

3

image-20251127234735106
91e3e3a11a15b2854641380b834e3326

4

image-20251127234807626
DECFBD2ECFC7C36C6D9BB49DFD0C15C2

5

image-20251127234908094
bc48f7ff158bdc49537455d0a2d5efbd
d4cd56bf63b45d5da02ce2f13eb06edf

6

image-20251127234930226
90b6c6f2d8d12022fa0bfa2d8bbcf775

第四章作业

1

哪些寻址方式下的操作数在寄存器中?哪些在存储器中?

  • 操作数(Operand):指令执行时要处理的数据。
  • 寻址方式(Addressing Mode):指明操作数在哪里(寄存器?内存?立即数?)以及如何找到它。
  • 寄存器(Register):CPU内部的高速存储单元。
  • 存储器(Memory):主存(RAM),速度比寄存器慢,但容量大。
寻址方式 操作数位置 举例
寄存器寻址 ✅ 寄存器 MOV AX, BX
立即寻址 ❌ 不在寄存器/存储器(在指令中) MOV AX, 5
直接寻址 ✅ 存储器 MOV AX, [2000H]
寄存器间接寻址 ✅ 存储器 MOV AX, [BX]
寄存器相对寻址 ✅ 存储器 MOV AX, [BX + 10]
基址变址寻址 ✅ 存储器 MOV AX, [BX + SI]
相对基址变址寻址 ✅ 存储器 MOV AX, [BX + SI + 5]

⚠️ 注意:立即数(Immediate) 不是寄存器也不是存储器,它直接嵌入在指令中。

2

什么是 RISC?

RISC = Reduced Instruction Set Computer(精简指令集计算机)

与之相对的是 CISC(Complex Instruction Set Computer,复杂指令集计算机),比如 Intel x86。

1️⃣ 指令集精简(Reduced Instruction Set)

  • 指令数量少(通常几十到几百条),每条指令功能单一。
  • 指令长度固定(如 32 位),便于流水线处理。

💡 类比:就像厨房里只有几把多功能刀具,每把刀只做一件事,但做得又快又好。

例子:

  • RISC:ADD R1, R2, R3 → 把 R2 和 R3 相加,结果存入 R1
  • CISC:MOV [BX+SI+10], AX → 一条指令完成地址计算+内存写入

2️⃣ 指令执行周期短(Single-Cycle Execution)

  • 大多数指令在一个时钟周期内完成。
  • 通过简化指令和硬件设计实现。

3️⃣ 大量通用寄存器(Large Register File)

  • 寄存器数量多(如 ARM 有 16 个通用寄存器,RISC-V 有 32 个)。
  • 减少对内存的访问,提高速度。

4️⃣ 采用流水线技术(Pipelining)

  • 指令执行分为多个阶段(取指、译码、执行、访存、写回),并行处理。
  • RISC 指令简单统一,非常适合流水线。

5️⃣ 加载/存储架构(Load/Store Architecture)

  • 只有 LOADSTORE 指令可以访问内存。
  • 其他运算指令只能在寄存器之间进行。

3

假定某计算机中有一条转移指令,采用相对寻址方式,共占两字节,第一字节是操作码,第二字节是 相对位移量(用补码表示),CPU每次从内存只能取一字节。假设执行到某转移指令时 PC的内容为200, 执行该转移指令后要求转移到100开始的一段程序执行,则该转移指令第二字节的内容应该是多少?

QQ20251206-170833

4

4.假设地址为1200H的内存单元中的内容为12FCH,地址为12FCH的内存单元的内容为38B8H,而 38B8H单元的内容为88F9H。说明以下各情况下操作数的有效地址是多少? (1)操作数采用变址寻址,变址寄存器的内容为252,指令中给出的形式地址为1200H。 (2)操作数采用一次间接寻址,指令中给出的地址码为1200H。 (3)操作数采用寄存器间接寻址,指令中给出的寄存器编号为8,8号寄存器的内容为1200H。

QQ20251206-170838

5

6.某计算机指令系统采用定长指令字格式,指令字长16位,每个操作数的地址码长6位。指令分二 地址、单地址和零地址3类。若二地址指令有k2条,零地址指令有k0条,则单地址指令最多有多少条?

QQ20251206-170843

第五章作业

1.

CPU的基本组成和基本功能各是什么?

1. CPU 的基本组成 (Basic Composition)

CPU 在硬件上主要由 数据通路 (Datapath)控制器 (Control Unit) 两大部分组成。

  • 数据通路 (Datapath)
    • 定义:它是 CPU 中负责数据处理和信号传递的路径,“干活的肌肉”。
    • 包含的核心部件
      • 运算部件 (ALU):负责算术运算(加减)和逻辑运算(与或)。
      • 寄存器堆 (Register File):CPU 内部的高速存储单元,用于暂存操作数和运算结果(如 rs, rt, rd)。
      • 程序计数器 (PC):存着当前或下一条指令的地址,是指令流动的“指针”。
      • 其他连接部件:包括多路选择器 (MUX)、加法器 (Adder)、符号扩展单元 (SignExt) 等,用于连通电路和辅助计算。
  • 控制器 (Control Unit)
    • 定义:它是 CPU 的“大脑”,指挥数据通路工作。
    • 功能:根据指令的操作码 (Opcode) 和功能码 (Func),生成各种控制信号(如 RegDst, ALUSrc, MemWr 等),控制 MUX 的选通和寄存器的读写。

2. CPU 的基本功能 (Basic Functions)

CPU 的功能就是执行指令,这一过程通常被称为 “指令周期”。结合 PPT 中的流程,基本功能可以概括为以下四点:

  1. 指令控制 (Instruction Control)
    • 取指令:控制程序按照顺序执行 (PC + 4) 或进行跳转 (beq/j)。确保 CPU 能自动地、连续地从存储器中取出指令。
  2. 操作控制 (Operation Control)
    • 译码与指挥:对取出的指令进行译码(分析 Opcode),产生相应的控制信号,指挥各个部件按规定的动作工作(例如:看到 lw 就打开内存读开关,看到 add 就指挥 ALU 做加法)。
  3. 时间控制 (Time Control)
    • 时序同步:对各种操作实施时间上的定时。通过 时钟信号 (Clk) 来同步所有部件的动作(例如:规定在时钟上升沿更新 PC,在下降沿写入寄存器),保证计算机有条不紊地工作。
  4. 数据加工 (Data Processing)
    • 运算:利用 ALU 对数据进行算术运算(如 add)和逻辑运算(如 ori),这是 CPU 最根本的功能。

2. 取指令部件的功能是什么?

根据 PPT image_373184.png,取指令部件 (Instruction Fetch Unit) 主要有两个核心功能:

  1. 取指令:根据程序计数器 (PC) 中的地址,从指令存储器 (Instruction Memory) 中读取出当前的指令字 (M[PC])。
  2. 更新 PC:计算下一条指令的地址。通常情况下是顺序执行 (PC <- PC + 4);如果是跳转或分支指令,则更新为目标跳转地址。

3. 控制器的功能是什么?

根据 PPT image_2f0f80.png 和 image_2f0cd7.png 中展示的真值表逻辑:

控制器的功能是“翻译”和“指挥”。它接收指令中的 操作码 (op) 和 功能码 (func),通过译码逻辑,产生一系列的 控制信号 (如 RegDst, ALUSrc, RegWr, MemtoReg 等)。这些信号控制着数据通路中各个多路选择器 (MUX)、ALU 和寄存器的动作,确保指令按预定逻辑执行。


4. 单周期处理器相关问题

  • 单周期处理器的 CPI 是多少?
    • CPI = 1
    • 原因:顾名思义,单周期处理器每条指令都在一个时钟周期内完成。
  • 时钟周期如何确定?
    • 时钟周期取决于 最复杂指令(通常是 lw)的执行时间,也就是数据通路中的 关键路径 (Critical Path)
    • 计算公式Tcycle 取指时间 + 译码/读寄存器时间 + ALU运算时间 + 读数据存储器时间 + 写回时间。必须保证最慢的指令也能在一个周期内跑完。
  • 为什么单周期处理器的性能差?
    • 因为 时钟周期被“最慢的指令”拖累了
    • 解释:虽然像 addbeq 这样的简单指令本来可以跑得很快(比如只要 400ps),但因为时钟周期必须迁就最慢的 lw 指令(比如 600ps),所以简单指令执行完后必须 空转等待,直到周期结束。这就造成了巨大的时间浪费,导致整体效率低下。

5. 单周期方式下,在一个指令周期内某个部件能否被重复使用多次?为什么?

  • 回答不能
  • 为什么
    • 在单周期设计中,指令的执行像水流一样流过数据通路,每个部件在每个周期内只能处理这一个指令流过的数据。
    • 如果需要重复使用某个功能(例如需要做两次加法:一次算 PC+4,一次算 ALU 运算),硬件上必须 设置多份独立的部件(例如设置专门的加法器用于 PC 更新,见 image_2dc282.png 中的 Adder),而不能复用同一个 ALU。
    • 原因总结:如果复用,会产生 资源冲突 (Structural Hazard),因为在一个时钟周期内,一个部件无法同时处理两个不同的任务。

6

image-20251227162818244
image-20251227162825596
指令 含义 格式 核心动作 关键区分点
add 加法 op rs rt rd shamt func Reg + Reg -> Reg rd
sub 减法 op rs rt rd shamt func Reg - Reg -> Reg rd
ori 逻辑或 op rs rt imm Reg OR ZeroExt(Imm) 零扩展
lw 取数 op rs rt imm Mem[Base+Off] -> Reg 读内存, 写 rt
sw 存数 op rs rt imm Reg -> Mem[Base+Off] 写内存
beq 分支 op rs rt imm if (Reg==Reg) Jump 比较, 条件跳
j 跳转 op target Goto Target 无条件, 拼接地址

1. RegWr (寄存器写使能)

  • 正常功能
    • 1:允许将数据写入寄存器堆(用于 R-type, ori, lw)。
    • 0:禁止写入(用于 sw, beq, j)。
  • 若恒为 0:寄存器堆的大门永远关上了,任何数据都写不进去。
  • 受影响指令:所有需要写回结果的指令,包括 R型指令 (add, sub 等)orilw。它们虽然能算出结果,但存不下来,相当于白忙活。

2. RegDst (写入目标寄存器选择)

  • 正常功能
    • 1:写入目标是 rd (指令 11-15 位),用于 R型指令
    • 0:写入目标是 rt (指令 16-20 位),用于 lw, ori
  • 若恒为 0:写入数据的目的地永远被锁定在 rt
  • 受影响指令R型指令 (add, sub 等)。这些指令本该把结果存入 rd,现在却错误地覆盖了 rt 寄存器里的内容。
    • 注:lwori 本来就要选 0,所以它们不受影响。

3. ALUSrc (ALU 源操作数选择)

  • 正常功能
    • 1:ALU 的第二个输入选 立即数 (用于 lw, sw, ori)。
    • 0:ALU 的第二个输入选 寄存器 B (用于 R-type, beq)。
  • 若恒为 0:ALU 永远看不见立即数,只能看见寄存器 B。
  • 受影响指令lw, sw, ori。这些指令需要用到立即数(算地址或做逻辑运算),信号错误会导致 ALU 使用错误的操作数进行计算。

4. Branch (分支信号)

  • 正常功能
    • 1:表示当前是分支指令,如果 Zero=1 则更新 PC 为跳转目标。
    • 0:表示顺序执行 PC+4。
  • 若恒为 0:与门永远输出 0,PC 永远无法加载分支目标地址。
  • 受影响指令beq。即使比较结果相等(应跳转),CPU 也会无视,继续执行下一条指令,导致分支失效。

5. MemWr (存储器写使能)

  • 正常功能
    • 1:允许向数据存储器写入数据 (用于 sw)。
    • 0:只读不写。
  • 若恒为 0:数据存储器变成了“只读”模式。
  • 受影响指令sw。该指令的功能就是存数,写不进去就完全失效了。

6. ExtOp (扩展模式选择)

  • 正常功能
    • 1:进行 符号扩展 (Sign Extension),用于 lw, sw (处理负偏移量)。
    • 0:进行 零扩展 (Zero Extension),用于 ori
  • 若恒为 0:所有立即数都按“零扩展”处理。
  • 受影响指令lwsw
    • 如果偏移量是正数,运气好还能对;但如果偏移量是 负数,零扩展会把它变成一个巨大的正数,导致计算出的内存地址完全错误。

7. R-type (R型指令标识)

  • 正常功能:这通常是一个译码信号。当它为 1 时,ALU 控制器会去查看 func 字段来决定具体做什么运算(加、减、与、或等)。当它为 0 时,ALU 控制器通常执行默认操作(如加法,服务于 lw/sw)。
  • 若恒为 0:ALU 控制器认为当前“不是 R型指令”,因此忽略 func 字段,直接执行默认操作(通常是 ADD)。
  • 受影响指令除了 add 以外的所有 R型指令 (sub, and, or, slt 等)。它们会被错误地执行成加法。

8. MemtoReg (写回数据来源选择)

  • 正常功能
    • 1:写回寄存器的数据来自 存储器 (用于 lw)。
    • 0:写回寄存器的数据来自 ALU (用于 R-type, ori)。
  • 若恒为 0:写回通路永远连着 ALU,断开了存储器。
  • 受影响指令lw。该指令本想把从内存读出的数据存进寄存器,结果却错误地把内存地址(ALU 计算结果)存进去了。

信号 (恒为1) 不能正确执行的指令 简要原因
RegWr sw, beq, j 意外修改寄存器内容
RegDst lw, ori 结果存错寄存器 (rd 而非 rt)
ALUSrc R-type, beq 误用立即数代替寄存器运算
Branch 运算结果为0的指令 意外发生跳转
MemWr 除 sw 外所有指令 意外修改内存数据
ExtOp ori 逻辑运算的高位扩展错误
R-type lw, sw, beq, ori ALU 执行了错误的操作
MemtoReg R-type, ori 写入了内存值而非计算结果

第六章作业

2(1)

1. 为什么“一条指令”的执行时间没有缩短?

流水线技术并不是让“单条指令跑得更快”,而是将一条指令拆分成多个步骤(如 IF, ID, EX, MEM, WB)。

  • 寄存器延迟(Overhead):在流水线的每个阶段之间,都需要加入流水线寄存器来保存中间结果。这些寄存器本身有读写延迟(建立时间、保持时间)。
  • 木桶效应:流水线的时钟周期取决于最慢的那一个阶段。
    • 假设非流水线执行一条指令需要 800ps。
    • 将其切分为 5 段流水线,每段 160ps。但因为要加上寄存器延迟(假设 10ps),周期可能变成了 170ps。
    • 那么一条指令走完 5 个阶段总共需要 170 × 5 = 850ps
  • 结论:单条指令从进入流水线到流出,所花费的总时间(潜伏期,Latency)反而因为寄存器的开销而略微增加了。

2. 为什么“程序”的执行时间缩短了?

流水线的核心优势在于并行(Parallelism)*和*吞吐率(Throughput)

  • 并行处理:虽然一条指令要 5 个周期才做完,但当第一条指令在做“第二步”时,第二条指令已经开始做“第一步”了。

  • 吞吐率提升

    • 非流水线:每 800ps 才能完成一条指令。
    • 流水线:一旦流水线填满,每 170ps 就能有一条指令完成(理想情况下)。
  • 数据证明:

    参考你之前的 PPT image_edf843.png:

    • 单周期方式执行 N 条指令:时间 = 600N ps。
    • 流水线方式执行 N 条指令:时间 = 234N ps。
    • 结论234N < 600N,程序的总执行时间被显著缩短了。

2(5)

(1) 为什么要各流水段之间加寄存器?

简单来说,流水线寄存器(Pipeline Registers)是实现“流水线”功能的物理基础。

  • 保存中间结果(记忆功能):每个时钟周期结束时,当前阶段完成的工作(比如计算出的地址、读出的寄存器值)必须被保存下来,才能在下一个时钟周期传给下一阶段使用。如果没有寄存器,信号会瞬间流过整个电路,变成单周期处理器,无法并行。
  • 隔离各个阶段(解耦):寄存器像一堵墙,把复杂的组合逻辑电路切成了 5 小段。这样,第 1 段在取指时,第 2 段可以同时在译码,互不干扰。
  • 同步时钟:所有的流水线寄存器由同一个时钟信号控制,确保所有指令像排队一样,整齐划一地每过一个周期向前挪一步。

(2) 各流水段寄存器的宽度是否都一样?

不一样。

(3) 为什么?

因为每个阶段需要传递给后续阶段的信息量是不同的

流水线寄存器的宽度取决于后面所有的阶段需要用到哪些信息。所有的信息(包括数据信号和控制信号)都必须像接力棒一样,一层一层往下传。

我们可以具体拆解一下(以标准的 MIPS 32位 5级流水线为例):

  1. IF/ID 寄存器(通常最窄)
    • 只需要保存:指令本身(32位) + 下一条指令地址 PC+4(32位)。
    • 总宽约 64位
  2. ID/EX 寄存器(通常最宽)
    • 这一步刚把指令翻译完,需要把所有原材料都带上:读出的两个寄存器数据(32+32位)、扩展后的立即数(32位)、PC+4(32位)、写回的目标寄存器号(5位),再加上一大堆控制信号(WB, MEM, EX 段的信号)。
    • 总宽往往超过 100位
  3. EX/MEM 寄存器
    • ALU用完的数据不需要传了,但算出的结果要传:ALU计算结果(32位)、准备写入内存的数据(32位)、写回目标寄存器号(5位)以及剩下的控制信号。
    • 宽度变小。
  4. MEM/WB 寄存器
    • 内存操作完,只需要传:读取的内存数据(32位)、ALU结果(32位)、写回目标寄存器号(5位)以及最后的 WB 控制信号。
    • 宽度进一步变化。

3

image-20260103173952347
image-20260103173719006

首先,我们根据题目给出的数据,列出当前各个阶段的耗时:

  • IF (取指):使用存储单元 200ps
  • ID (译码):寄存器堆读 50ps
  • EX (执行):ALU 和加法器 150ps
  • MEM (访存):使用存储单元 200ps
  • WB (写回):寄存器堆写 50ps

当前基准时钟周期:

Tclk = max (200, 50, 150, 200, 50) = 200ps

目前的瓶颈是 IF 和 MEM 阶段。

(1) 若 EX 阶段的 ALU 时间缩短 20%

  • 计算:新的 ALU 时间 = 150ps × (1 − 20%) = 120ps
  • 分析:此时流水线各段最长时间依然是 IF 和 MEM 的 200ps。EX 阶段本来就不是瓶颈(150ps < 200ps),缩短它只会让它等待的时间更长(气泡更多)。
  • 结论不能加快流水线执行速度。时钟周期仍然维持在 200ps

(2) 若 ALU 操作时间增加 20%

  • 计算:新的 ALU 时间 = 150ps × (1 + 20%) = 180ps
  • 分析:虽然 ALU 变慢了,但 180ps 仍然小于最慢的存储器访问时间(200ps)。流水线的“短板”依然是存储器。
  • 结论:对流水线性能没有影响。时钟周期仍然维持在 200ps

(3) 若 ALU 操作时间增加 40%

  • 计算:新的 ALU 时间 = 150ps × (1 + 40%) = 210ps
  • 分析:此时 EX 阶段的时间(210ps)超过了原本的瓶颈(200ps)。
    • 旧瓶颈:200ps
    • 新瓶颈:210ps
    • 根据木桶效应,时钟周期必须迁就最慢的阶段。
  • 结论:流水线性能会下降。时钟周期必须调整为 210ps,导致整体运行速度变慢。

4

image-20260103175033363

(1) 在非流水线处理器上执行该程序需要花多长时间?

非流水线处理器采用串行执行方式,上一条指令完全做完,下一条才开始。

  • 计算公式总时间 = 指令条数 × 单条指令执行时间

  • 计算过程:

    Tnon-pipe = 106 × 100ps = 108ps

  • 单位换算:

    108ps = 100, 000ns = 100μs

答案: 需要花 100μs(或者写 108ps)。

(2) 若新 CPU 采用 20 级流水线,理想情况下,它比非流水线处理器快多少?

这道题可以从两个角度回答,一个是计算加速比,一个是计算具体时间。题目问的是“快多少”(倍数),通常指加速比。

  • 理想流水线假设

    1. 流水线各段 perfectly balanced(完全平衡),每段耗时相等。
    2. 没有流水线填充时间的损耗(当 N 很大时,k − 1 个填充周期可忽略)。
  • 流水线时钟周期:

    $$T_{\text{clk}} = \frac{\text{非流水线指令时间}}{\text{流水线级数}} = \frac{100\text{ps}}{20} = 5\text{ps}$$

  • 流水线总执行时间:

    Tpipe ≈ N × Tclk = 106 × 5ps = 5 × 106ps

    (注:精确公式为 (N + k − 1) × Tclk,但 106 ≫ 19,故忽略不计)

  • 计算加速比 (Speedup):

    $$\text{Speedup} = \frac{T_{\text{non-pipe}}}{T_{\text{pipe}}} = \frac{100 \times 10^6\text{ps}}{5 \times 10^6\text{ps}} = 20$$

答案: 理想情况下,它比非流水线处理器快 20 倍。

(一句话结论:理想情况下,多少级流水线,就获得多少倍的加速比)

(3) 实际流水线并是不是理想的,流水段之间的数据传送会有额外开销。这些开销是否会影响指令执行时间和指令吞吐率?

这是一个考察对流水线“潜伏期(Latency)”和“吞吐率(Throughput)”概念理解的问题。

回答:是的,会有影响。 具体分析如下:

  1. 对“指令执行时间”(Latency)的影响: 会变长。
    • 定义:指一条指令从进入流水线第一段到最后一段完成所需要的总时间。
    • 分析:在非流水线中,时间纯粹是逻辑电路的操作时间(100ps)。在流水线中,为了保存中间结果,每一级之间都要加入流水线寄存器。这些寄存器有建立时间、保持时间等物理延迟(Overhead)。
    • 结果:一条指令在流水线中走完 20 级,总耗时 = 20 × (理想每级时间 + 寄存器延迟)。这个总和一定大于原来的 100ps
  2. 对“指令吞吐率”(Throughput)的影响: 会降低(相比理想流水线)。
    • 定义:指单位时间内完成的指令数量($\text{TP} = \frac{1}{T_{\text{clk}}}$)。
    • 分析:流水线的时钟周期 Tclk 必须足够长,以容纳最慢的一段逻辑操作加上寄存器的延迟开销。
    • 结果:实际时钟周期 Tactual = 5ps + Overhead。因为分母变大了,所以吞吐率 $\frac{1}{T_{\text{actual}}}$ 会比理想吞吐率 $\frac{1}{5\text{ps}}$ 要低。

总结答案:

会影响。

  • 指令执行时间(单条延迟):会增加(因为加入了流水线寄存器的延迟开销)。
  • 指令吞吐率:会降低(相比于没有开销的理想流水线,因为时钟周期被迫变长了)。

6

image-20260103175023088

1. 哪些指令对之间发生数据相关(Data Dependency)?

我们要找出“写后读”(RAW, Read After Write)的依赖关系。

  • 第一组相关:$s3
    • 产生者:第 1 条指令 addu $s3, $s1, $s0 (写入 $s3
    • 消费者:第 2 条指令 addu $t2, $s3, $s3 (读取 $s3
    • 描述:第 2 条指令依赖第 1 条指令的结果。
  • 第二组相关:$t2
    • 产生者:第 2 条指令 addu $t2, $s3, $s3 (写入 $t2
    • 消费者:第 3 条指令 lw $t1, 0($t2) (读取 $t2 作为基地址)
    • 以及:第 4 条指令 add $t3, $t1, $t2 (读取 $t2
    • 描述:第 3 条和第 4 条指令都依赖第 2 条指令的结果。
  • 第三组相关:$t1
    • 产生者:第 3 条指令 lw $t1, 0($t2) (写入 $t1
    • 消费者:第 4 条指令 add $t3, $t1, $t2 (读取 $t1
    • 描述:第 4 条指令依赖第 3 条指令的结果。

2. 如果不用“转发”技术,需要加几条 NOP?

规则分析: 在没有转发机制的标准 5 段流水线中,数据必须在 WB(写回) 阶段写入寄存器堆后,才能在 ID(译码) 阶段被后续指令读取。

  • 写操作发生在第 5 个时钟周期(WB)。
  • 读操作发生在第 2 个时钟周期(ID)。
  • 这意味着,产生数据的指令必须先完成 WB 阶段,使用数据的指令才能进入 ID 阶段。对于相邻的两条相关指令,中间通常需要间隔 2 个时钟周期(即插入 2 条 NOP)。

具体插入情况

  1. 在第 1 条和第 2 条之间(解决 $s3 相关): 需要插入 2 条 NOP

    Plaintext

    1
    2
    3
    4
    addu $s3, $s1, $s0
    nop
    nop
    addu $t2, $s3, $s3
  2. 在第 2 条和第 3 条之间(解决 $t2 相关): 需要插入 2 条 NOP

    Plaintext

    1
    2
    3
    4
    addu $t2, $s3, $s3
    nop
    nop
    lw $t1, 0($t2)
  3. 在第 3 条和第 4 条之间(解决 $t1 相关): 需要插入 2 条 NOP

    Plaintext

    1
    2
    3
    4
    lw   $t1, 0($t2)
    nop
    nop
    add $t3, $t1, $t2

    (注:虽然第 4 条也依赖第 2 条的 $t2,但因为中间已经隔了第 3 条指令和它的 NOP,所以 $t2 的数据早就准备好了,不需要额外处理)

结论(无转发): 需要在每组发生数据相关的相邻指令前加入 2 条 NOP 指令。

3. 如果采用“转发”,是否可以完全解决数据冒险?

回答:不可以。

原因分析(Load-Use 冒险)

  • ALU 到 ALU 的冒险(如第 1、2 条之间,第 2、3 条之间)可以通过转发完全解决。数据在 EX 阶段算出后,可以直接从流水线寄存器转发给下一条指令的 EX 阶段输入。
  • Load 到 ALU 的冒险(即第 3 条 lw 和第 4 条 add 之间)无法完全解决。
    • lw 指令的数据是在 MEM(访存) 阶段结束时才从内存读出的(时钟周期 4)。
    • add 指令需要在 EX(执行) 阶段开始时使用数据进行计算(时钟周期 3)。
    • 时间悖论:数据在未来(第 4 周期)才产生,但现在(第 3 周期)就要用。这是物理上做不到的,这被称为 Load-Use Hazard

4. 如果不行,需要加入几条 NOP?

针对上述的 Load-Use 冒险,必须让流水线暂停一个周期,等待数据从内存读出。

  • 位置:在第 3 条 lw 和第 4 条 add 之间。
  • 数量1 条 NOP
    • 加了 1 条 NOP 后,add 指令晚一个周期进入 EX 阶段,此时 lw 刚好走完 MEM 阶段,数据可以通过 MEM/WB 寄存器转发给 add

最终代码序列(采用转发)

MIPS Assembler

1
2
3
4
5
addu $s3, $s1, $s0
addu $t2, $s3, $s3 # 转发解决,无 NOP
lw $t1, 0($t2) # 转发解决,无 NOP
nop # 【必须加 1 条 NOP 解决 Load-Use 冒险】
add $t3, $t1, $t2

结论(有转发): 需要在发生数据相关的第 4 条指令(add)之前加入 1 条 NOP 指令。