分派与提交
分派阶段会将到达此处的指令根据指令的func字段,分派到不同的发射队列中。在此阶段,重排序缓存(ROB)会跟踪所有进入后端的指令状态,并维护指令的原程序序,使得程序的提交顺序与原程序序一致。当指令在后端完成执行时,它会使得ROB中对应表项的complete状态变为有效。一旦队列头部的指令完成执行,ROB头部的表项就会被提交。此外,分支数据缓存(BDB)会暂存所有的分支预测和执行信息。如果这条指令出现了分支预测失败的情况,那么提交模块还会通知前端重定向PC。
ROB组织
ROB逻辑上上是一个循环队列,用头指针(读指针)和尾指针(写指针)来管理。提交段最旧的指令将由头指针指向,而在分派段的最新指令将会由尾指针索引写入ROB。实际上,ROB是一个使用Cluster实现的子队列集合,子队列数量是译码宽度与提交宽度的最大值。
ROB负责管理了所有进入后端的指令,目前指令的所有信息都将由ROB来维护。每一个ROB表项包含了以下信息:
字段 | 位宽 | 含义 |
---|---|---|
rdVld | 1 | 目的寄存器是否有效 |
rd | 5 | 逻辑目的寄存器编号 |
prd | wpreg | 物理目的寄存器编号 |
pprd | wpreg | 重命名前的物理目的寄存器编号 |
pc | 32 | 指令的PC值 |
isBranch | 1 | 指令是否为分支指令 |
isStore | 1 | 指令是否为存储指令 |
complete | 1 | 指令是否完成执行 |
nxtCmtEn | 1 | 同一周期下一条指令是否允许提交 |
其中,wpreg
表示物理寄存器地址的宽度。
BDB组织
BDB负责管理进入后端的所有分支指令信息。在先前的设计中,这一部分信息由ROB维护,但由于Zircon-2024采用了提交段处理分支预测失败的策略,因此ROB的每一个表项还需要保存大量和分支相关的结果信息。但在绝大部分程序中,分支指令的数量往往只会占到总指令数的30%左右,这使得ROB的表项利用率非常低。因此Zircon-2024设计了一个表项数远小于ROB的BDB来保存这一部分分支指令信息。
BDB的表项组织如下:
字段 | 位宽 | 含义 |
---|---|---|
predType | 2 | 指令所属的分支类型(普通分支、函数调用、函数返回) |
jumpEn | 1 | 该指令是否跳转 |
predFail | 1 | 分支预测是否失败 |
offset | 32 | 保存预测偏移和跳转地址 |
其中offset字段在分派阶段保存预测偏移,这个预测偏移会在寄存器堆读取阶段被读取,并在写回阶段被写入为跳转地址,实现了字段的复用。
分派阶段
ROB分配索引
当指令到达分派器时,ROB会为每一条指令分配一个robIndex,作为该指令在后端中的唯一标识。该标识由3部分构成:
- qidx:写入的ROB子队列索引;
- offset:写入的ROB子队列中的表项号;
- high:顺序最高位,用于指示指令的年龄信息。
其中,high位将ROB索引的顺序标号扩展一位,从而避免了循环队列标号在到达最大值时需要归零,导致大于比较运算无法准确年龄的问题(参考《超标量处理器设计》提交阶段设计)。
分派逻辑
分派器会译码器给出的func字段,来将指令分派到不同的发射队列中。由于每个周期到达分派阶段的若干指令各自都可能为任何类型,因此每一个发射队列的单周期写入宽度能力都必须为译码宽度。
寄存器就绪表
寄存器就绪表是一个表项数和物理寄存器数量相同的布尔数组,每个元素表示该物理寄存器是否就绪。在分派阶段,每个指令都会读取这个表,并将就绪信息写入后续的发射队列中。有关该表的唤醒逻辑,请参看发射队列与仲裁。
提交阶段
当一条指令被执行完成且处于ROB队列头部时,ROB将会从队列中移除该指令,并将该指令的提交信息广播到全流水线。这些信息包括但不限于:
- 将寄存器信息发送到重命名模块,使其更新提交的映射表状态;
- 将分支信息发送到分支预测模块,使其更新预测状态;
- 将写提交信息发送到写缓冲,使其提交一条store到DCache中;
- 将冲刷信号广播到全流水线,使得流水线清空、PC重定向。
其中,由于分支预测器和写缓冲的设计要求,如果队头为一条store或跳转指令,那么当前周期ROB最多仅能提交1条指令,这种设计简化了分支预测器和写缓冲的单周期写入实现。
谨防扇出与线延迟
在Zircon-2023的初版设计中,ROB段的flush信号由于要接入全流水线,因此存在扇出问题,这使得时序变得非常差。在后续设计中,作者对从ROB输出的所有信号都做了缓冲处理,并复制多个寄存器来优化扇出。