一条 load/add/store 如何贯穿机器¶
场景与初始状态¶
源程序为:
编译器约定 x10 指向三个连续 32 位整数,生成:
假设:
采用 RV32I 风格,通用寄存器宽度 XLEN = 32
x10 = 0x1000
a = 20, b = 22
页表映射有效
指令已在 L1 I-cache
目标数据行不在任一级数据 cache,但页面已驻留主存
最终可观察目标是 Mem[0x1008] = 42。下面所有预测、停顿和缓冲都必须保持这个架构结果。
1. 从源代码到机器码¶
编译器完成类型选择、寄存器分配和指令选择。汇编器把助记符变成机器码,把标签解析为位移;链接器安排代码与数据地址,装载器建立进程虚拟地址空间。
此时 lw 的语义已经固定,但“有几级流水、cache 多大、是否乱序”仍未决定,这正是 ISA 与微体系结构的边界。
2. 取第一条 lw¶
PC 给出虚拟指令地址。前端查询指令 TLB,得到物理页号后访问 L1 I-cache。因为假设命中,指令位进入译码器,PC 同时准备顺序增加 4。
若有分支预测器,它此时提供后续取指方向;本例没有分支,前端连续取四条指令。
3. 译码与读寄存器¶
译码器识别 load 格式,提取 rs1=x10、rd=x5 和立即数 0。寄存器堆读出:
控制器设置 ALU 输入选择为立即数、存储读有效、写回来源为内存、寄存器写有效。写使能还不会立刻提交,它随指令沿流水线向后传递。
4. EX 计算虚拟有效地址¶
ALU 做普通整数加法:
这一步只得到虚拟地址,不是最终物理位置。还要检查对齐:32 位 load 通常要求地址是 4 的倍数。
5. 地址翻译与 L1 miss¶
数据 TLB 用 VPN 查到 PPN并验证读权限,组合出物理地址。L1 D-cache 用组索引查 tag,发现 miss:
- miss 状态缓冲记录尚未完成的请求;
- 请求发往 L2、末级 cache,最终到内存控制器;
- DRAM 激活相应 bank/row 并返回整个 cache line;
- cache line 填入 L1,目标 4 B 由块内偏移选出。
第一次 miss 的等待可能远长于一次 ALU 加法。若处理器支持非阻塞 cache,其他独立指令可能继续;简单顺序核心会冻结相关流水阶段。
6. 第一条 load 写回¶
数据 20 沿写回通路进入 x5。若采用精确异常,只有确认翻译、权限和访存都成功后,寄存器更新才可成为架构状态。
第二条 lw 访问 0x1004。因为与 a 常位于同一 cache line,第一次 miss 带回的相邻数据让它很可能 L1 命中。这就是空间局部性带来的实际收益。
7. add 的数据相关¶
add x7, x5, x6 依赖两条 load。若第二条 load 的数据直到 MEM 末尾才可用,紧随其后的 add 可能出现 load-use 冒险。冒险检测器让 add 停顿,或在值可用时从 MEM/WB 转发到 ALU。
ALU 最终计算:
补码加法器执行模 \(2^{32}\) 运算。本例没有溢出;在 RV32I 中,整数 add 即使溢出也不触发算术异常,只保留结果的低 32 位。其他 ISA 或语言运行时可以规定不同的溢出处理。
8. sw 的地址与数据路径¶
store 同时需要:
x10与立即数 8 计算地址0x1008;x7提供数据 42。
x7 可通过 store-data 转发直接送来,不必等它先写回再读。地址翻译检查写权限,D-cache 定位目标行。
若使用写回、写分配 cache,cache 行被更新并置 dirty,下层内存可能暂时仍是旧值。ISA 关注的是之后合法 load 应观察到正确结果,而不是要求 DRAM 立刻写成 42。
9. store buffer 与提交¶
高性能处理器可在执行阶段先为 store 分配队列或 buffer 项并准备地址、数据;只有在异常检查完成且按程序顺序提交后,它才可向 cache 排空并对外可见。后续对同地址的 load 必须从尚未排空的 store 转发最新值,避免读到旧 cache 数据。
在多核系统中,store 获得该 cache 行所有权并使其他核心副本失效。何时对其他核心可见还受内存一致性模型约束。
10. 从晶体管角度看¶
上述每个寄存器位由存储单元保持;ALU 加法由门网络传播进位;tag 比较由并行比较器完成;控制信号驱动多路选择器和写使能;时钟边沿让流水寄存器同步采样。更底层的晶体管尺寸、电压和连线延迟共同限制频率与功耗。
组成原理的价值就在这里:不必逐管分析,也能知道某次等待发生在 ALU、翻译、cache、DRAM 还是一致性协议。
失败路径¶
这组指令还可能遇到:
- 非法或未对齐地址;
- TLB miss 或 page fault;
- cache 替换需要写回 dirty 行;
- 多核所有权请求等待;
- ECC 检测到内存错误。
精确异常要求更早指令已完成,出错指令和更晚指令不留下架构副作用。正常路径和失败路径必须共同设计。
自测¶
- 为什么第二个 load 可能受益于第一个 load 的 cache line 填充?
- 这四条指令中至少有哪些 RAW 相关?分别可用转发还是停顿处理?
- store 已对本核后续 load 可见,是否意味着 DRAM 和其他核心已立刻看到?