0%

xs-gem5: load 指令的前世今生

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 阶段处理。

  1. 第一级: 进行地址翻译 loadDoTranslate()
  2. 第二级
    • 通知调度器对 load 指令预测唤醒: iewStage->getScheduler()->specWakeUpFromLoadPipe(inst)
    • 向访存系统发送 load 请求: loadDoSendRequest()
  3. 第三级:
    • 接收数据: loadDoRecvData()
    • 对于预取指令,忽略 fault.
  4. 第四级: 将 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 流水线中清空

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
    • 设置请求并开始地址翻译:request->initiateTranslation()
    • 将要写入的数据放入 inst->memData

SplitDataRequest

根据 cacheline 大小,将整个需要访问的内存区间划分成多个对 cacheline 的请求(头部和尾部需要单独处理以排除不需要访问的区间), 压入 _reqs ,对其中的每一个请求设置一些标志位后调用 MMU 开始地址翻译:sendFragmentToTranslation()->translateTiming()

stage2: todo

stage3: todo

stage4: todo