香山处理器 IFU 取指单元深度解读
作者:翁贞华
代码版本:Kunminghu V3
适用对象:芯片验证工程师、CPU 架构学习者
1. 概述
1.1 IFU 在香山流水线中的位置
IFU(Instruction Fetch Unit,取指单元)是香山处理器前端流水线的最后一环,位于 BPU 分支预测和后端 Decode 之间:
1 | BPU → FTQ → ICache → IFU → IBuffer → Backend (Decode) |
香山的前端取指采用双发射(FetchPorts=2),每个周期最多可以从两个 fetch block 中取出 32 条指令(每块 16 条,每条 2 字节)。
1.2 IFU 的核心功能
IFU 负责以下工作:
- 接收 FTQ 的取指请求,解析 fetch block 信息
- 向 ICache 发起取指请求,获取 64B 的指令数据
- 指令定界与边界检测,识别 RVC(压缩指令)和 RVI(普通指令)的边界
- 跨块指令处理,处理跨越两个 fetch block 的半条指令
- 分支预测验证,通过 PredChecker 检验 BPU 预测是否正确
- RVC 展开,将 16-bit RVC 指令扩展为 32-bit
- 指令入 IBuffer,将处理好的指令发送给后端
2. IFU 接口详解
IFU 位于 src/main/scala/xiangshan/frontend/ifu/Ifu.scala,继承自 IfuModule,主要接口定义在 IfuBundle 中。
2.1 完整 IO 接口
1 | class IfuIO(implicit p: Parameters) extends IfuBundle { |
2.2 与 FTQ 的接口详解
输入(fromFtq):
| 信号 | 类型 | 说明 |
|---|---|---|
req.valid |
Bool | 取指请求有效 |
req.bits.fetch[i] |
FtqFetchRequest | 第 i 个 fetch block 的请求(i=0,1) |
redirect.valid |
Bool | 后端重定向信号 |
flushFromBpu |
BpuFlushInfo | BPU 冲刷信号 |
输出(toFtq):
| 信号 | 类型 | 说明 |
|---|---|---|
req.ready |
Bool | IFU 可以接收新的取指请求 |
wbRedirect.valid |
Bool | IFU 产生的重定向(预测错误) |
resp |
FtqIfuWbBundle | 写回信息(taken、cfiPosition、target 等) |
2.3 与 ICache 的接口详解
输出(toICache):
| 信号 | 类型 | 说明 |
|---|---|---|
req.valid |
Bool | 取指请求有效 |
req.bits.addr |
UInt(PAddrBits.W) | 取指的物理地址 |
stall |
Bool | 反压信号,告诉 ICache 不要发送新的 resp |
输入(fromICache):
| 信号 | 类型 | 说明 |
|---|---|---|
fetchResp.valid |
Bool | ICache 返回的响应有效 |
fetchResp.bits.data |
Vec(FetchBlockSize, UInt(16.W)) | 128 个 half-word(256 字节,实际用 64B) |
fetchResp.bits.maybeRvcMap |
Vec(FetchBlockInstNum, Bool) | 哪些位置可能是 RVC |
3. IFU 子模块详解
3.1 PreDecode(预译码)
文件: PreDecode.scala
功能: 在 IFU 流水线的 S2 阶段,PreDecode 模块对每条指令进行基础的预译码:
- 检测分支/jump 指令:识别 jal、jalr、条件分支等
- 计算 jump offset:根据指令类型(jal/r/jalr)计算目标偏移
- 指令类型分类:区分 RVC 和 RVI 指令
1 | class PreDecode extends Module { |
3.2 InstrBoundary(指令边界检测)
文件: InstrBoundary.scala
功能: 这是 IFU 中最关键的组合逻辑之一。香山的 fetch block 大小是 64 字节,其中可能包含 RVC(16-bit)和 RVI(32-bit)混合的指令。InstrBoundary 的任务是:
- 识别每条指令的边界(instrEndVec)
- 判断哪些位置是有效指令(instrValid)
- 识别 RVC 指令(isRvc)
- 处理半条 RVI 指令:当 fetch block 边界落在一条 RVI 指令中间时
1 | class InstrBoundary extends Module { |
3.3 PredChecker(预测检查器)
文件: PredChecker.scala
功能: 验证 BPU 的预测是否正确。这是香山前端实现精确异常的关键模块。
PredChecker 采用两级流水线:
Stage 1(S2 阶段):
检测以下错误类型:
jalFault:jal 指令预测错误retFault:ret 指令预测错误notCFIFault:预测跳转到某个位置但实际不是 CFIinvalidTakenFault:不该跳转但 BPU 预测了跳转
Stage 2(S3 阶段,写回阶段):
检测目标地址错误:
targetFault:跳转的目标地址与 BPU 预测不一致
1 | val checkerRedirect = Wire(new Redirect) |
3.4 RvcExpander × 8(8个 RVC 展开器)
文件: RvcExpander.scala
功能: 将 16-bit RVC 指令展开为 32-bit。香山每周期最多处理 8 条 RVC 展开。
1 | private val rvcExpanders = Seq.fill(IBufferEnqueueWidth)(Module(new RvcExpander)) |
RVC 展开规则(部分):
c.addi→addic.j→jal x0, offsetc.jal→jal x1, offsetc.ld→ldc.sd→sd- …共 44 条 RVC 指令
3.5 IfuUncacheUnit(MMIO 取指单元)
文件: IfuUncacheUnit.scala
功能: 处理 MMIO(Memory-Mapped I/O)区域的取指。MMIO 地址不能缓存,需要通过特殊的 uncache 路径访问。
状态机:
1 | IDLE ──▶ REQ_SENT ──▶ WAIT_COMMIT ──▶ DONE |
- IDLE:等待 uncache 请求
- REQ_SENT:发送 MMIO 请求到 InstrUncache,等待响应
- WAIT_COMMIT:等待该 MMIO 指令 commit(保证精确异常)
- DONE:指令已提交,清除状态
关键设计点:
- uncache 指令必须等待 commit 后才能发送下一个 uncache 请求(保证异常顺序)
- uncache 区域没有缓存,所以每次都要重新请求
4. IFU 4 级流水线详解
IFU 采用 4 级流水线设计(S0/S1/S2/S3),每级有 fire、ready、flush 信号控制流动。
流水线概览图
1 | ┌─────────────────────────────────────────────────────┐ |
S0: 发起取指请求
主要工作:
- 从
fromFtq.req接收取指请求 - 生成两个 fetch block 的
FetchBlockInfo - 检测
crossCacheline - 向 ICache 发送请求
1 | io.toICache.req.valid := s0_fire && !s0_flush |
S1: 接收 ICache 响应
主要工作:
- 接收 ICache 的
fetchResp - ValidHold 保持 S0 的有效信号
- 调用
InstrBoundary计算指令边界 - 更新
prevLastIsHalfRvi寄存器
S2: 预译码和 RVC 展开准备
主要工作:
- PreDecode 进行指令预译码
- InstrCompact 合并两个 block 的指令
- PredChecker Stage1(错误检测)
- 计算
instrCountBeforeCurrent
S3: IBuffer 入队和预测验证
主要工作:
- MMIO 处理(通过 IfuUncacheUnit)
- RVC 展开(8 个 RvcExpander)
- PredChecker Stage2(targetFault 检测)
- 写回 FTQ(
toFtq.resp) - IBuffer 入队(
io.toIBuffer)
5. 关键设计细节
5.1 prevLastIsHalfRvi 处理
这是香山处理 RVC/RVI 混合指令边界的最巧妙设计之一。
1 | private val s1_prevLastIsHalfRvi = RegInit(false.B) |
5.2 flush 和 redirect 优先级
1 | backendRedirect := fromFtq.redirect.valid // 最高:后端重定向 |
优先级(从高到低):
backendRedirect(后端分支 mispred / 例外)wbRedirect(IFU 自己产生的重定向)uncacheRedirect(Uncache 重定向)flushFromBpu(BPU 冲刷)
6. 验证要点
从验证工程师的角度,以下是需要重点关注的场景:
6.1 crossCacheline 场景
- fetch block 跨越两个 cache line 时,IFU 正确请求两个 line
- 两个 line 的 hit/miss 组合(4 种)都正确处理
isDoubleLine信号正确设置
6.2 RVC 与非 RVC 混合
- 纯 RVC block:所有指令都是 2 字节
- 纯 RVI block:所有指令都是 4 字节
- 混合 block:RVC 和 RVI 交替出现
- 边界情况:RVC 指令在 block 最后一个位置
6.3 跨块半 RVI 指令
- 前一个 block 最后一条是半条 RVI
- 后一个 block 第一条指令正确拼接
prevLastIsHalfRvi寄存器正确更新
6.4 分支预测验证(PredChecker)
- jalFault:jal 指令的目标与预测不一致
- retFault:ret 指令的目标与预测不一致
- notCFIFault:预测跳转但实际不是 CFI
- targetFault:跳转目标地址错误
6.5 MMIO 路径
- MMIO 指令进入 Uncache FSM
- 等待最老指令 commit 后才发送请求
- 收到响应后正确生成 flush 请求
7. IFU 目录结构
1 | src/main/scala/xiangshan/frontend/ifu/ |
8. 总结
IFU 是香山处理器前端流水线的核心,承担着从 ICache 取指令、将指令边界定界、验证分支预测、处理 RVC 展开、检测预测错误等多重职责。
IFU 设计的关键亮点:
- 4 级流水线:S0 取指请求 → S1 ICache 响应 → S2 预译码/合并 → S3 入 IBuffer/验证,每级职责清晰
- 双发射支持:两个 fetch block 并行处理,每个 block 最多 16 条指令
- 跨块半 RVI 处理:
prevLastIsHalfRvi寄存器和拼接逻辑解决了指令跨越 block 边界的问题 - 两级 PredChecker:Stage1 在 S3 同一周期检测大部分错误,Stage2 延迟到写回阶段检测 targetFault
- MMIO 特殊路径:IfuUncacheUnit FSM 保证 MMIO 指令按序 commit,实现精确异常
- 性能监控完整:IfuPerfAnalysis 追踪所有关键事件,支持 top-down 性能分析
理解 IFU 的工作原理,对于验证香山前端功能、调试取指相关 bug、以及优化前端性能都至关重要。
文档版本:香山处理器 IFU 深度解读 v1.0
代码版本:Kunminghu V3 (kunminghu-v3 分支)
