LaTeX 渲染有问题。将就着看吧。
# 指令周期
指令周期:处理单个指令的过程(时间)
取指周期:从内存中提取一条指令
执行周期:执行所提取的指令
还可能有中断周期、间址周期
# 间址周期
间址周期:把间接地址的读取看成是一个额外的指令子周期
# CPU 的任务与需求
取指令(从特定地方获取),解释指令(译码),取数据(从特定地方获取),处理数据(如进行算术运算),写数据(写入特定地方)
# 数据流
# 取值周期
控制器下达指令:取指周期的开始。
通过 MAR 将地址传入地址总线
控制器通过控制线通知存储器地址就绪
存储器读取地址
存储器通过数据总线将数据发送给 MBR
如果是异步总线,存储器提供反馈
指令取回来后,PC+“1”
# 间址周期
将 MBR 中的地址引用送入 MAR 得到地址
MAR 将地址传入地址总线
控制器通知存储器取地址
存储器通过数据总线将有效地址发送给 MBR
# 中断周期
处理中断前,将下一条指令放到 MBR 中,再放到数据总线上
控制器将下一条指令的取值地址通过 MAR 放到地址总线上
控制器通知存储器获得数据
存储器从地址线获得地址
存储器从数据线获得数据,并将数据写入到获得的地址
# 指令流水线
# 两阶段方法
将指令处理分成两个阶段:取指令和执行指令
# 六阶段方法
为了进一步的加速,流水线必须有更多的阶段
• 取指令(Fetch instruction,FI):读下一条预期的指令到缓冲器
• 译码指令(Decode instruction,DI):确定操作码和操作数指定符
• 计算操作数(Calculate operands,CO):计算每个源操作数的有效地址
• 取操作数(Fetch operands,FO):从存储器取出每个操作数,寄存器 中的操作数不需要取
• 执行指令(Execute instruction,EI):完成指定的操作。若有指定的目 的操作数位置,则将结果写入此位置
• 写操作数(Write operand,WO):将结果存入存储器
# 流水线性能
加速比:
S_k = \frac{T_{1,n}}{T_{k,n}} = \frac{nkt}{[k+(n-1)]t}=\frac{nk}
# 冒险
# 数据冒险
数据依赖性
有些数据要等前序计算完成
解决方案 1:插入 nop 指令(软件)(不进行任何操作)
解决方案 2:插入 bubble(硬件)
解决方案 3:前递(forwarding)/ 旁路(bypassing)(快速传递数据)
解决方案 4:交换指令顺序
# 控制冒险
指令的执行顺序被更改
解决方案 1:提早确定分支
解决方案 2:取多条指令
解决方案 3:分支预测
静态预测(规则不变)
动态预测(规则变化)
解决方案 4:交换指令顺序
# 结构冒险
已进入流水线的不同指令在同一时刻访问相同的硬件资源(内存或寄存器)
解决:使用多个不同的硬件资源,或者分时使用同一个硬件资源
解决方案 1:流水线停顿(stall),插入空泡(bubble)
解决方案 2:使用不同用途的多个存储器
解决方案 3:同一个存储器提供分时处理
下面是 GPT 写的:
在计算机体系结构中,** 冒险(Hazard)** 是指在指令流水线(Pipeline)中,由于某些原因,指令无法按预期执行,导致执行暂停或结果错误。冒险通常分为三种类型:结构冒险、数据冒险和控制冒险。下面分别介绍这三种冒险:
# 1. 结构冒险(Structural Hazard)
定义:结构冒险是由于硬件资源冲突导致的流水线停顿,通常发生在多个指令需要同时使用相同硬件资源时,导致资源无法同时满足所有指令的需求。
原因:
指令流水线中的不同阶段可能会共享相同的硬件资源,例如 ALU(算术逻辑单元)、寄存器堆、内存总线等。
如果两个或多个指令在同一时刻需要访问相同的硬件资源,硬件资源就无法同时满足所有指令的需求,从而引发结构冒险。
例子:
在一个简单的流水线中,如果同时有两条指令需要访问内存(例如一条加载指令和一条存储指令),而只有一个内存访问单元时,就会发生结构冒险。
类似地,如果同一时刻多个指令需要访问 ALU,但 ALU 资源有限,也可能发生结构冒险。
解决方法:
增加硬件资源(如多路复用器、独立的内存访问端口等)。
使用流水线阶段的资源调度技术,确保指令不同时访问相同的资源。
# 2. 数据冒险(Data Hazard)
定义:数据冒险发生在指令间存在数据依赖时,前序指令的结果尚未计算出来,后续指令需要依赖该结果,从而导致停顿或错误。
数据冒险类型:
读后写(RAW,Read After Write):也称为真正的数据依赖。后续指令需要读取前序指令计算的结果,但该结果尚未写回寄存器或内存,导致后续指令无法执行。
例子:
ADD R1, R2, R3 // R1 = R2 + R3 SUB R4, R1, R5 // 需要R1的值,但R1尚未计算出来
- 在此例中,第二条指令需要依赖第一条指令计算的 R1 的值,但由于第一条指令尚未完成,第二条指令不能立即执行。
写后读(WAR,Write After Read):后续指令写入寄存器的值会覆盖前序指令的结果,导致数据依赖错误。
例子:
ADD R1, R2, R3 // R1 = R2 + R3 SUB R2, R1, R5 // 需要R1的值,但R2被后续指令修改
写后写(WAW,Write After Write):前序指令和后续指令都试图向同一寄存器写值,顺序不一致会导致数据错误。
例子:
ADD R1, R2, R3 // R1 = R2 + R3 SUB R1, R4, R5 // R1被修改
解决方法:
流水线插入气泡(Pipeline Stalls):通过插入 “空指令”(气泡)来等待读取数据,减少数据依赖冲突。
数据前推(Data Forwarding/Bypassing):将计算结果直接从一个流水线阶段传送到需要的后续阶段,避免等待数据写回寄存器。
重排序(Instruction Reordering):重新排列指令的顺序,避免数据依赖冲突。
# 3. 控制冒险(Control Hazard)
定义:控制冒险是由于程序中的控制指令(如跳转、分支等)导致流水线不能确定下一条指令是哪一条,从而发生停顿或错误。
原因:
当程序遇到分支指令时,流水线无法提前知道分支的目标地址,因此必须等待分支指令执行完成以确定跳转的目标。
这通常发生在条件分支指令(如
if
语句、循环等)和无条件跳转指令(如goto
、return
等)之后。
例子:
BEQ R1, R2, LABEL // 如果R1 == R2,跳转到LABEL
ADD R3, R4, R5 // 该指令要等BEQ完成才能执行,因为BEQ决定了跳转
- 在此例中,
BEQ
指令的执行结果不确定,直到它被完全执行后,流水线才能知道是否跳转以及跳转到哪个地址,从而导致控制冒险。
解决方法:
分支预测(Branch Prediction):根据历史执行模式预测分支的方向,提前加载指令。如果预测正确,可以减少控制冒险的影响。
延迟分支(Delayed Branch):在分支指令后插入一条可以执行的指令,即使分支未确定,流水线仍然可以继续执行。
动态分支预测(Dynamic Branch Prediction):使用硬件动态调整分支预测策略,通过复杂的算法来预测跳转指令的目标。
提早确定分支(?)
总结来说,冒险的出现是因为流水线中的不同阶段存在相互依赖和硬件资源竞争。通过优化硬件和采用各种技术(如数据前推、分支预测等),可以最大限度地减少冒险对流水线性能的影响。