流水线级数不是一个"模块"里写死的,而是前端+后端+缓存子系统共同摊出来的,最终在"顶层连线"时被确定下来 。在香山(以及 XS-GEM5)里,它既不是纯前端概念,也不是纯后端概念,而是系统级结构参数,由各阶段模块各自声明自己的拍数、再在 Top 层拼起来。
一、先纠正一个直觉:没有"流水线级数寄存器"
-
不像
robDepth=256那样有个变量叫pipelineStages=15。 -
真实做法是:每个子模块(IFU、BPU、Decode、Rename、Dispatch、IQ、ALU、LSU、WB、Commit)内部用
RegInit/PipelineRegister链自己延几拍,总级数 = 各模块延迟之和 + 级间握手寄存。 -
所以"几级"是算出来的,不是"设出来的";改某个模块的延迟,总级数就变。
二、前端和后端的分工(以昆明湖 V2/V3 为例)
前端(Frontend,取指→译码完)
-
ICache 访问 + 命中判断:1--2 拍
-
FTB/BTB 查询 + TAGE 预测:1--2 拍
-
指令对齐/解包(decode width 6→多条):1 拍
-
分支预测重定向(BPU 超前 IFU):BPU 本身 2--3 拍,IFU 取指 1 拍
-
前端到后端边界(Fetch Buffer → Decode):Decode 1 拍
-
合计前端约 5--7 拍(从 PC 出去到译码完进重命名)
后端(Backend,重命名→提交)
-
Rename:1 拍
-
Dispatch 到 IQ/ROB/LSQ:1 拍
-
Issue(调度)等待:0 拍(异步,不计入固定级数,但发射选择 1 拍)
-
执行:ALU 1 拍 / FMA 3--4 拍 / Load AGU 1 拍 + D-Cache 2--3 拍 / Store 类似
-
Writeback 到 PRF:1 拍
-
Commit:1 拍(每周期退若干条)
-
后端"固定延迟链"约 6--10 拍 ,但执行单元延迟因指令而异,所以后端不是定长。
整体 :昆明湖 V2 口径常说"约 14--16 级",那是把前端 6 + 重命名/派发 2 + 执行 1--4 + 写回 1 + 提交 1 加起来,且 Load 命中 L1 的典型路径;Miss 到内存就几十上百拍,但那不算"流水线级数",算"阻塞"。
三、在哪个模块"实现"
-
各子模块自己实现自己的流水级 :
IFU.scala里有RegInit做取指级间寄存;BPU.scala里 TAGE 表查询延 2 拍;Decode.scala1 拍;Rename.scala1 拍;IssueQueue.scala选择逻辑组合+1 拍锁;ALU.scala组合输出或 1 拍寄存;WB.scala写回寄存 1 拍。 -
Top 层(
XSTop.scala/XSCore.scala)只做连线:把 IFU.out 接 Decode.in,中间不插额外级数(除非跨时钟域才加流水寄存)。 -
参数文件(
XSCoreParams.scala)声明各模块深度 :icacheParams.latency=2、ftbParams.bankedOn=true、aluLatency=1,这些参数间接决定总级数,但不存在一个中央"流水线控制器"统一发拍。 -
唯一接近"全局流水线控制"的是 :
RobCommit/Redirect逻辑和frontend.stall/backend.stall握手------它们决定"哪拍停",但不决定"有几级"。
四、XS-GEM5 里怎么对应
XS-GEM5 的 O3CPU 把同样结构用 C++ 建模:
-
fetch()函数里循环展开前端拍数 -
decode()1 拍 -
rename()1 拍 -
iew(issue+execute+writeback)里Scheduler和ExecUnit各自tick()推进自己的延迟计数 -
总 CPI 曲线里"流水线深度"体现在从 fetch 一条指令到 commit 它的最少周期数 = 各阶段 delay 求和
-
改
O3CPU::params里的decodeToExecuteDelay之类字段,就等价于改 Chisel 里某段RegInit链长度
五、一句话收口
流水线级数是"系统级 emergent 属性" :前端模块各自延几拍、后端执行单元各自延几拍、级间握手各加 1 拍寄存,Top 层把它们串起来后,数总拍数就是几级。没有一个叫 Pipeline 的模块负责"实现级数" ,级数散落在 IFU/BPU/Decode/Rename/IQ/Exec/WB/Commit 每一处的 RegInit 和 params.latency 里。