load 指令的执行
发射: 从 instQueue 到 loadPipeline
Gem5 执行到 IEW::executeInsts()
函数时,根据 fromIssue->size
得知此时允许发射的指令有多少条,然后通过循环次从 instQueue 中取出可以发射的指令: instQueue.getInstToExecute()
若是此时指令已经被冲刷 (inst->isSquashed()
) : 标记该指令已执行并且可以提交 (由 commit 阶段来处理被冲刷指令) 后继续处理下一条指令
对于访存指令 (inst->isMemRef()
) , 如果是 load 指令 (inst->isLoad()
) , 则将指令加入 load pipeline: ldstQueue.issueToLoadPipe(inst)
。随后执行流水线: ldstQueue.executePipeSx()
ldstQueue.issueToLoadPipe()
调用该指令对应硬件线程号的 LSQUnit::issueToLoadPipe()
, 主要是将该 load 指令加入 loadPipeSx[0]
的尾部
执行: loadPipeline
ldstQueue.executePipeSx()
调用所有线程对应的 LSQUnit::executePipeSx()
, 分别执行 load 流水线和 store 流水线: executeLoadPipeSx()
, executeStorePipeSx()
.
对于 load 流水线:如果指令被冲刷,则将指令标记已执行和可以提交,以在 commit 阶段处理。
- 第一级: 进行地址翻译
loadDoTranslate()
- 第二级
- 通知调度器对 load 指令预测唤醒:
iewStage->getScheduler()->specWakeUpFromLoadPipe(inst)
- 向访存系统发送 load 请求:
loadDoSendRequest()
- 通知调度器对 load 指令预测唤醒:
- 第三级:
- 接收数据:
loadDoRecvData()
- 对于预取指令,忽略 fault.
- 接收数据:
- 第四级: 将 load 数据写回:
loadDoWriteback()
(实际上什么也没做, 相当于空函数)
在 load 流水线中,如果 load 指令需要 replay :
- needSTLFReplay() || needCacheBlockedReplay() || needRescheduleReplay()
- 将指令取消发射(
iewStage->loadCancel()
) - 从 load 流水线中清空(
inst->endPipelining()
)
- 将指令取消发射(
- 第三级流水线上如果检查需要 replay ,则
- 如果是 bank conflict 导致: 发射队列重新发射指令
inst->issueQue->retryMem(inst)
- 如果是 cache miss 导致:
iewStage->cacheMissLdReplay()
- 如果是 tlb miss 导致:
iewStage->deferMemInst()
- 随后将指令取消发射并从 load 流水线中清空
- 如果是 bank conflict 导致: 发射队列重新发射指令
load 指令走完流水线后如果不需要 replay 则标记指令可以写回(iewStage->readyToFinish(inst)
) 并将该指令踢出流水线。 (正常退出)
stage1: address translation
在 xs-gem5 中,指令的 initiateAcc()
API 仅仅用于进行 TLB 访问, 在此处被调用
- inst->initiateAcc()
- xc->initiateMemRead()
- cpu->pushRequest()
- iew.ldstQueue.pushRequest()
- LSQ::pushRequest()
- 计算是否需要突发传输:计算地址访问范围是否跨 cache block
- 如果跨 cache block: 则将请求设置为
SplitDataRequest
- 如果不跨 cache block: 则将请求设置为
SingleDataRequest
- 如果跨 cache block: 则将请求设置为
- 设置请求并开始地址翻译:
request->initiateTranslation()
- 将要写入的数据放入 inst->memData
- 计算是否需要突发传输:计算地址访问范围是否跨 cache block
SplitDataRequest
根据 cacheline 大小,将整个需要访问的内存区间划分成多个对 cacheline 的请求(头部和尾部需要单独处理以排除不需要访问的区间), 压入 _reqs
,对其中的每一个请求设置一些标志位后调用 MMU 开始地址翻译:sendFragmentToTranslation()->translateTiming()