0%

xs-gem5: store 指令的生命周期

一条 store 指令的前世今生

发射: 从 instQueue 到 storePipeline

Gem5 执行到 IEW::executeInsts() 函数时,根据 fromIssue->size 得知此时允许发射的指令有多少条,然后通过循环次从 instQueue 中取出可以发射的指令: instQueue.getInstToExecute()

若是此时指令已经被冲刷 (inst->isSquashed()) : 标记该指令已执行并且可以提交 (由 commit 阶段来处理被冲刷指令) 后继续处理下一条指令

对于访存指令 (inst->isMemRef()) , 如果是 store 指令 (inst->isStore()) , 则将指令加入 store pipeline: ldstQueue.issueToStorePipe(inst)。随后执行流水线: ldstQueue.executePipeSx()

ldstQueue.issueToStorePipe() 调用该指令对应硬件线程号的 LSQUnit::issueToStorePipe(), 主要是该 store 指令加入 storePipeSx[0] 的尾部

执行: 从 storePipeline 到 storeQueue

ldstQueue.executePipeSx() 调用所有线程对应的 LSQUnit::executePipeSx(), 分别执行 load 流水线和 store 流水线: executeLoadPipeSx(), executeStorePipeSx().

对于 store 流水线:第一级进行地址翻译 storeDoTranslate(), 第二级写入 StoreQueue storeDoWriteSQ() 并检查是否被冲刷,第二三四级清空流水线 emptyStorePipeSx()。如果指令需要 replay 则终止该指令流水 inst->endPipelining() 并处理下一条指令。特别地,对于发生 TLBmiss 而需要 replay 的,还需要通知发射队列: iewStage->deferMemInst(inst)

stage1: address translation

在 xs-gem5 中,指令的 initiateAcc() API 仅仅用于进行 TLB 访问, 在此处被调用

  • inst->initiateAcc()
  • xc->writeMem()
  • 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: write into StoreQueue

如果指令地址翻译已完成并且需要访存,则调用 LSQUnit::write() ,作用是将要写入的数据和指令信息写入 storeQueue 的 entry

如果没有 fault, 则检查该 store 指令是否和在飞 load 指令存在违例:checkViolations()

提交: 从 storeQueue 到 storeBuffer

指令进入 LSQ::commitStore() 时将 storeQueue 中最年轻的指令标记为可以写回

随后指令进入 LSQ::writebackStoreBuffer()

  • 指令先被写入 storeBuffer: offloadToStoreBuffer()
    • 对于 split 请求
      • 其每个子请求均插入 storeBuffer: insertStoreBuffer()
      • 所有子请求均成功插入后,标记 store 指令完成并从 storeQueue 中移除该指令:completeStore()
  • 指令进入 storeBuffer 之后准备写入 cache : storeBufferEvictToCache()