O3CPU

目录

  1. 流水线阶段
  2. Execute-in-execute 模型
  3. 模板策略
  4. ISA 独立性
  5. 与 ThreadContext 交互

O3CPU 是我们在 v2.0 版本中的新详细模型。它是一个乱序 CPU 模型,大致基于 Alpha 21264。此页面将为您提供 O3CPU 模型、流水线阶段和流水线资源的概述。我们已努力保持代码有良好的文档,因此请浏览代码以了解 O3CPU 各部分如何工作的确切细节。

流水线阶段

Execute-in-execute 模型

对于 O3CPU,我们努力使其具有高度的时序准确性。为此,我们使用了一个在流水线执行阶段实际执行指令的模型。大多数模拟器模型会在流水线的开头或结尾执行指令;SimpleScalar 和我们的旧详细 CPU 模型都在流水线开头执行指令,然后将其传递给时序后端。这带来了两个潜在问题:首先,时序后端中可能存在错误,这些错误不会显示在程序结果中。其次,通过在流水线开头执行,指令都是按顺序执行的,乱序加载交互丢失。我们的模型能够避免这些缺陷并提供准确的时序模型。

模板策略

O3CPU 大量使用模板策略来获得一定程度的多态性,而无需使用虚函数。它使用模板策略将 “Impl” 传递给 O3CPU 中使用的几乎所有类。此 Impl 在其中定义了流水线的所有重要类,例如特定的 Fetch 类、Decode 类、特定的 DynInst 类型、CPU 类等。它允许任何将其用作模板参数的类能够获取 Impl 中定义的任何类的完整类型信息。通过获取完整的类型信息,不需要通常用于提供多态性的传统虚函数/基类。主要缺点是 CPU 必须在编译时完全定义,并且模板化类需要手动实例化。有关示例 Impl 类,请参见 src/cpu/o3/impl.hh src/cpu/o3/cpu_policy.hh

ISA 独立性

O3CPU 的设计旨在尝试分离依赖于 ISA 的代码和独立于 ISA 的代码。流水线阶段和资源主要是 ISA 独立的,以及低级 CPU 代码。ISA 依赖代码实现特定于 ISA 的函数。例如,AlphaO3CPU 实现特定于 Alpha 的函数,例如从错误中断硬件返回 (hwrei()) 或读取中断标志。低级 CPU,FullO3CPU,负责协调所有流水线阶段并处理其他独立于 ISA 的操作。我们希望这种分离使得实现未来的 ISA 变得更容易,因为希望只需重新定义高级类。

与 ThreadContext 交互

ThreadContext 为外部对象提供了访问 CPU 内线程状态的接口。但是,由于 O3CPU 是乱序 CPU,这稍微复杂一些。虽然在任何给定周期定义架构状态是明确的,但如果更改该架构状态会发生什么并没有明确定义。因此,可以毫不费力地对 ThreadContext 进行读取,但是对 ThreadContext 进行写入和更改寄存器状态需要 CPU 刷新整个流水线。这是因为可能有在飞指令依赖于已更改的寄存器,并且不清楚它们是否应该查看寄存器更新。因此,访问 ThreadContext 可能会导致 CPU 模拟变慢。

后端流水线

计算指令

计算指令更简单,因为它们不访问内存并且不与 LSQ 交互。下面包含一个高级调用链(仅重要函数)以及对每个函数功能的描述。

Rename::tick()->Rename::RenameInsts()
IEW::tick()->IEW::dispatchInsts()
IEW::tick()->InstructionQueue::scheduleReadyInsts()
IEW::tick()->IEW::executeInsts()
IEW::tick()->IEW::writebackInsts()
Commit::tick()->Commit::commitInsts()->Commit::commitHead()

加载指令

加载指令在执行之前与计算指令共享相同的路径。

IEW::tick()->IEW::executeInsts()
  ->LSQUnit::executeLoad()
    ->StaticInst::initiateAcc()
      ->LSQ::pushRequest()
        ->LSQUnit::read()
          ->LSQRequest::buildPackets()
          ->LSQRequest::sendPacketToCache()
    ->LSQUnit::checkViolation()
DcachePort::recvTimingResp()->LSQRequest::recvTimingResp()
  ->LSQUnit::completeDataAccess()
    ->LSQUnit::writeback()
      ->StaticInst::completeAcc()
      ->IEW::instToCommit()
IEW::tick()->IEW::writebackInsts()

存储指令

存储指令类似于加载指令,但只有在提交后才写回到缓存。

IEW::tick()->IEW::executeInsts()
  ->LSQUnit::executeStore()
    ->StaticInst::initiateAcc()
      ->LSQ::pushRequest()
        ->LSQUnit::write()
    ->LSQUnit::checkViolation()
Commit::tick()->Commit::commitInsts()->Commit::commitHead()
IEW::tick()->LSQUnit::commitStores()
IEW::tick()->LSQUnit::writebackStores()
  ->LSQRequest::buildPackets()
  ->LSQRequest::sendPacketToCache()
  ->LSQUnit::storePostSend()
DcachePort::recvTimingResp()->LSQRequest::recvTimingResp()
  ->LSQUnit::completeDataAccess()
    ->LSQUnit::completeStore()

分支预测错误

分支预测错误在 IEW::executeInsts() 中处理。它将通知提交阶段开始挤压 ROB 中预测错误分支上的所有指令。

IEW::tick()->IEW::executeInsts()->IEW::squashDueToBranch()

内存顺序预测错误

InstructionQueue 有一个 MemDepUnit 来跟踪内存顺序依赖性。如果 MemDepUnit 声明存在依赖性,IQ 将不会调度指令。

LSQUnit::read() 中,LSQ 将搜索可能的别名存储并在可能的情况下转发。否则,加载被阻止,并在阻止存储完成时通过通知 MemDepUnit 重新调度。

LSQUnit::executeLoad/Store() 都会调用 LSQUnit::checkViolation() 来搜索 LQ 以寻找可能的预测错误。如果找到,它将设置 LSQUnit::memDepViolator,稍后 IEW::executeInsts() 将启动以挤压预测错误的指令。

IEW::tick()->IEW::executeInsts()
  ->LSQUnit::executeLoad()
    ->StaticInst::initiateAcc()
    ->LSQUnit::checkViolation()
  ->IEW::squashDueToMemOrder()