这是 计算机组成原理 Notes 系列的第二期。
数据通路设计 (Data Path)
数据通路是 CPU 中执行数据操作的路径,包含以下关键组件:
基本组件
1. 程序计数器 (PC)
- 存储下一条要执行指令的地址
- 每周期自动 +4(字节寻址,32位指令)
- 分支/跳转时加载新地址
2. 指令存储器 (Instruction Memory)
- 根据 PC 地址读取指令
- 只读,通常在取指阶段访问
3. 寄存器堆 (Register File)
- 32个通用寄存器(x0-x31)
- 2个读端口、1个写端口
- x0 硬连线为0
4. 算术逻辑单元 (ALU)
- 执行算术运算(加、减)
- 执行逻辑运算(与、或、异或)
- 执行比较运算(SLT)
- 计算内存地址
5. 数据存储器 (Data Memory)
- 存储程序数据
- 读写操作在访存阶段进行
6. 控制单元 (Control Unit)
- 解析指令 opcode
- 生成控制信号
单周期数据通路
单周期设计中,每条指令在一个时钟周期内完成:
指令执行阶段:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 取指 │ -> │ 译码 │ -> │ 执行 │ -> │ 访存 │ -> │ 写回 │
│ (Fetch) │ │ (Decode)│ │(Execute)│ │(Memory) │ │(WriteBack)│
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
PC+4 读寄存器 ALU运算 读写内存 写寄存器
读指令 立即数扩展 分支判断
关键路径:取指 -> 寄存器读 -> ALU -> 内存访问 -> 寄存器写
缺点:时钟周期由最长指令(Load)决定,效率低下
多周期数据通路
将指令执行分为多个时钟周期,资源共享:
| 周期 | 操作 | 活跃组件 |
|---|---|---|
| 1 | 取指 | PC, Inst Memory |
| 2 | 译码/读寄存器 | Register File |
| 3 | 执行/地址计算 | ALU |
| 4 | 访存/分支完成 | Data Memory |
| 5 | 写回 | Register File |
优点:
- 功能部件复用(节省硬件)
- 不同指令执行周期数不同
- 更短的时钟周期
流水线 (Pipeline)
流水线通过重叠执行多条指令来提高吞吐量。
经典五级流水线
时间 ->
指令1: [IF][ID][EX][MEM][WB]
指令2: [IF][ID][EX][MEM][WB]
指令3: [IF][ID][EX][MEM][WB]
指令4: [IF][ID][EX][MEM][WB]
| 阶段 | 名称 | 功能 |
|---|---|---|
| IF | 取指 (Instruction Fetch) | 从内存读取指令,PC+4 |
| ID | 译码 (Instruction Decode) | 解析指令,读取寄存器 |
| EX | 执行 (Execute) | ALU运算,计算地址 |
| MEM | 访存 (Memory Access) | 读写数据内存 |
| WB | 写回 (Write Back) | 结果写回寄存器 |
流水线冒险 (Hazards)
1. 结构冒险 (Structural Hazard)
- 原因:硬件资源冲突
- 示例:单端口内存同时需要取指和访存
- 解决:
- 硬件:增加端口(哈佛结构)
- 软件:插入气泡(Bubble)
2. 数据冒险 (Data Hazard)
- 原因:指令间数据依赖
add x1, x2, x3 # I1
sub x4, x1, x5 # I2 需要 I1 的结果
and x6, x1, x7 # I3 需要 I1 的结果
or x8, x1, x9 # I4 需要 I1 的结果
类型:
- RAW(写后读):I2 依赖 I1 的结果
- WAR(读后写):通常不会在流水线中发生
- WAW(写后写):不会在五级流水线中发生
解决方法:
-
转发/旁路 (Forwarding/Bypassing):
EX阶段结果直接传递给下一条指令的EX阶段 [IF][ID][EX]-->[MEM][WB] [IF][ID]-->[EX][MEM][WB] ^^ 转发路径 -
加载-使用冒险 (Load-Use Hazard):
lw x1, 0(x2) # I1 add x3, x1, x4 # I2 需要 I1 的加载结果(无法转发)解决:插入1个气泡,或使用编译器调度
3. 控制冒险 (Control Hazard)
- 原因:分支指令改变PC,但下一条指令已在流水线中
beq x1, x2, label # 分支指令
add x3, x4, x5 # 无论是否分支,此指令已进入流水线
解决方法:
- 停顿 (Stall):等待分支结果(性能损失大)
- 预测不跳转 (Predict Not Taken):假设分支不成立
- 延迟分支 (Delayed Branch):编译器调度安全指令填充延迟槽
- 分支预测 (Branch Prediction):硬件预测分支方向
分支预测 (Branch Prediction)
分支预测用于减少控制冒险带来的性能损失。
静态预测
| 策略 | 描述 | 准确率 |
|---|---|---|
| 总是不跳转 | 假设分支总是不成立 | ~50% |
| 总是跳转 | 假设分支总是成立 | ~60% |
| 向后跳转预测为真 | 循环中常用(loop) | ~70% |
| 编译器提示 | 指令中附加预测提示 | 可变 |
动态预测
1. 一位分支预测器
状态机:
0 (Not Taken)
^ |
| v
1 (Taken)
预测与实际结果一致:保持状态
预测与实际结果不一致:翻转状态
缺点:循环边界预测错误两次(进入和退出)
2. 两位分支预测器(饱和计数器)
状态机:
00: 强不跳转 (Strongly Not Taken)
01: 弱不跳转 (Weakly Not Taken)
10: 弱跳转 (Weakly Taken)
11: 强跳转 (Strongly Taken)
预测错误时移动一位,正确时继续强化
优点:对偶尔不成立的循环表现更好
3. 分支历史表 (BHT - Branch History Table)
PC低位索引 -> BHT -> 2位饱和计数器
BHT结构:
┌─────────────────┬──────────────────┐
│ 分支指令PC低位 │ 2位饱和计数器 │
├─────────────────┼──────────────────┤
│ ... │ ... │
└─────────────────┴──────────────────┘
4. 两级自适应预测器 (PAg/PAp/GAp)
- 全局历史寄存器 (GHR):记录最近N次分支结果
- 模式历史表 (PHT):根据历史模式选择预测器
GHR: 1010 (最近4次分支:跳、不跳、跳、不跳)
│
v
PHT索引 -> 选择对应的2位计数器
分支目标缓冲 (BTB - Branch Target Buffer)
缓存分支指令的目标地址,避免计算延迟:
BTB结构:
┌──────────────┬──────────────┬──────────────┐
│ 分支指令PC │ 目标地址 │ 预测信息 │
├──────────────┼──────────────┼──────────────┤
│ 0x1000 │ 0x2000 │ 11 (强跳转) │
│ 0x1050 │ 0x1800 │ 01 (弱不跳) │
└──────────────┴──────────────┴──────────────┘
Cache (高速缓存)
Cache 利用程序访问的局部性原理,在 CPU 和主存之间提供快速数据访问。
局部性原理
时间局部性:最近访问的数据很可能再次被访问
循环中的计数器变量
空间局部性:访问某个地址后,其邻近地址很可能被访问
顺序访问数组元素
Cache 基本结构
┌─────────────────────────────────────────┐
│ Cache 结构 │
├─────────────┬─────────────┬─────────────┤
│ Index │ Tag │ Data │
│ (索引位) │ (标记位) │ (数据块) │
└─────────────┴─────────────┴─────────────┘
地址划分:
32位物理地址:
┌─────────────────┬─────────────┬─────────────┐
│ Tag │ Index │ Offset │
│ (标记位) │ (索引位) │ (块内偏移) │
└─────────────────┴─────────────┴─────────────┘
直接映射 Cache (Direct Mapped)
每个内存块只能映射到唯一的 Cache 行:
Cache 行号 = (内存块地址) mod (Cache 行数)
示例:4行 Cache,块大小16字节
地址0x0000 -> Cache 行0
地址0x0010 -> Cache 行1
地址0x0020 -> Cache 行2
地址0x0030 -> Cache 行3
地址0x0040 -> Cache 行0 (冲突!)
优点:实现简单,查找快 缺点:容易产生冲突失效(Conflict Miss)
全相联 Cache (Fully Associative)
内存块可以映射到任意 Cache 行:
查找:比较所有行的 Tag
优点:无冲突失效 缺点:硬件复杂度高(需要并行比较器),不适合大容量
组相联 Cache (Set Associative)
Cache 分成若干组,每组包含多行:
2路组相联示例:
┌─────────────────────────────────────┐
│ 组0 │ 行0-0 │ 行0-1 │ │
│ 组1 │ 行1-0 │ 行1-1 │ │
│ 组2 │ 行2-0 │ 行2-1 │ │
│ 组3 │ 行3-0 │ 行3-1 │ │
└─────────────────────────────────────┘
组号 = (内存块地址) mod (组数)
组内任意行可存放该块
优点:平衡了冲突率和硬件复杂度
Cache 失效类型
| 类型 | 原因 | 解决策略 |
|---|---|---|
| 强制失效 (Compulsory) | 首次访问该块 | 预取 (Prefetching) |
| 容量失效 (Capacity) | Cache 容量不足 | 增大 Cache |
| 冲突失效 (Conflict) | 映射冲突 | 增加相联度 |
替换策略
| 策略 | 描述 | 特点 |
|---|---|---|
| LRU (Least Recently Used) | 替换最久未使用的行 | 实现复杂,命中率高 |
| FIFO (First In First Out) | 替换最早进入的行 | 实现简单 |
| Random | 随机选择 | 实现最简单 |
| LFU (Least Frequently Used) | 替换访问次数最少的 | 需要计数器 |
写策略
写命中 (Write Hit):
| 策略 | 操作 | 特点 |
|---|---|---|
| 写直达 (Write Through) | 同时写 Cache 和内存 | 数据一致,速度慢 |
| 写回 (Write Back) | 只写 Cache,替换时写内存 | 速度快,需要脏位 |
写不命中 (Write Miss):
| 策略 | 操作 |
|---|---|
| 写分配 (Write Allocate) | 加载块到 Cache,再写入 |
| 非写分配 (No Write Allocate) | 直接写内存,不加载到 Cache |
常见组合:
- 写直达 + 非写分配
- 写回 + 写分配
多级 Cache
CPU -> L1 Cache -> L2 Cache -> L3 Cache -> 主存
(32-64KB) (256KB-1MB) (4-64MB)
1-3周期 8-15周期 20-50周期 200+周期
| 级别 | 特点 | 设计目标 |
|---|---|---|
| L1 | 分指令和数据 (Harvard) | 最小延迟 |
| L2 | 统一缓存 | 平衡延迟和命中率 |
| L3 | 多核共享 | 高命中率 |
包含策略:
- Inclusive:L1 内容一定在 L2 中
- Exclusive:L1、L2 内容互不重复
- Non-inclusive:介于两者之间
BUS (总线)
总线是连接 CPU、内存、I/O 设备的共享通信通路。
总线类型
| 总线 | 功能 | 示例 |
|---|---|---|
| 数据总线 | 传输数据 | 64位宽 |
| 地址总线 | 传输地址 | 32/64位宽 |
| 控制总线 | 传输控制信号 | 读写信号、中断等 |
总线仲裁
集中式仲裁:
- 链式查询:优先级固定,结构简单
- 计数器定时查询:优先级可编程
- 独立请求:每个设备独立请求,响应快
分布式仲裁:
- 设备自行竞争总线使用权
总线事务
典型读事务:
1. 主设备发送地址和读命令
2. 从设备准备数据
3. 从设备发送数据
4. 主设备接收数据
典型写事务:
1. 主设备发送地址和写命令
2. 主设备发送数据
3. 从设备接收并存储数据
计算机的性能指标
IC与Clock
指令数 (IC - Instruction Count):程序执行的总指令条数
时钟周期 (Clock Cycle):CPU 操作的最小时间单位
时钟频率 (Clock Rate):
其中 为时钟周期, 为时钟频率(单位:Hz)
CPI
CPI (Cycles Per Instruction):每条指令的平均时钟周期数
不同指令的 CPI:
| 指令类型 | 典型 CPI | 说明 |
|---|---|---|
| ALU 指令 | 1 | 单周期完成 |
| Load | 2-5 | 需要访存 |
| Store | 1-2 | 无需写回寄存器 |
| 分支 | 1-3 | 取决于预测成功率 |
| 乘法/除法 | 3-32 | 复杂运算 |
CPU Time
CPU 执行时间:
或:
性能优化方向:
| 方向 | 方法 | 影响 |
|---|---|---|
| 减少指令数 | 更好的算法/编译器 | 软件优化 |
| 降低 CPI | 流水线、超标量、更好的预测 | 硬件架构 |
| 提高时钟频率 | 更好的工艺、更短的流水线 | 物理实现 |
Amdahl 定律:
其中:
- :可优化部分在原执行时间中的比例
- :该部分的加速倍数
Comments