blob: d975efc45f2371909be137ed402325fe1ceeefd1 [file] [log] [blame] [view]
---
layout: post
title: "gem5 O3CPU Backend Documentation Update"
author: Zhengrong Wang
date: 2020-07-18
categories: project
---
The documentation about gem5 O3CPU is a little bit abstract and not
closely related to the code. Therefore, this post extracts
key function chains to show how an instruction is handled by the backend,
with some basic description to ease the learning curve of the O3CPU
backend (IEW and Commit stage).
Hopefully this could help more people. Reader should already be
familiar with gem5. This post has also been added to the
[documentation]({{site.url}}/documentation/general_docs/cpu_models/O3CPU#backend-pipeline).
### Compute Instructions
Compute instructions are simpler as they do not access memory and
do not interact with the LSQ. Included below is a high-level calling chain
(only important functions) with a description about the functionality of each.
```cpp
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()
```
- Rename (`Rename::renameInsts()`).
As suggested by the name, registers are renamed and the instruction
is pushed to the IEW stage. It checks that the IQ/LSQ can hold the new
instruction.
- Dispatch (`IEW::dispatchInsts()`).
This function inserts the renamed instruction into the IQ and LSQ.
- Schedule (`InstructionQueue::scheduleReadyInsts()`)
The IQ manages the ready instructions (operands ready) in a ready list,
and schedules them to an available FU. The latency of the FU is set here,
and instructions are sent to execution when the FU done.
- Execute (`IEW::executeInsts()`).
Here `execute()` function of the compute instruction is invoked and
sent to commit. Please note `execute()` will write results to the destiniation
register.
- Writeback (`IEW::writebackInsts()`).
Here `InstructionQueue::wakeDependents()` is invoked. Dependent
instructions will be added to the ready list for scheduling.
- Commit (`Commit::commitInsts()`).
Once the instruction reaches the head of ROB, it will be committed and
released from ROB.
### Load Instruction
Load instructions share the same path as compute instructions until
execution.
```cpp
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()
```
- `LSQUnit::executeLoad()` will initiate the access by invoking the
instruction's `initiateAcc()` function. Through the execution context interface,
`initiateAcc()` will call `initiateMemRead()` and eventually be directed
to `LSQ::pushRequest()`.
- `LSQ::pushRequest()` will allocate a `LSQRequest` to track all states, and
start translation. When the translation completes, it will
record the virtual address and invoke `LSQUnit::read()`.
- `LSQUnit::read()` will check if the load is aliased with any previous
store.
- If can it can forward, then it will schedule `WritebackEvent` for the next
cycle.
- If it is aliased but cannot forward, it calls
`InstructionQueue::rescheduleMemInst()` and `LSQReuqest::discard()`.
- Otherwise, it send packets to the cache.
- `LSQUnit::writeback()` will invoke `StaticInst::completeAcc()`, which
will write a loaded value to the destination register. The
instruction is then pushed to the commit queue. `IEW::writebackInsts()`
will then mark it done and wake up its dependents. Starting from here it
shares same path as compute instructions.
### Store Instruction
Store instructions are similar to load instructions, but only writeback
to cache after committed.
```cpp
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()
```
- Unlike `LSQUnit::read()`, `LSQUnit::write()` will only copy the store
data, but not send the packet to cache, as the store is not committed yet.
- After the store is committed, `LSQUnit::commitStores()` will mark the SQ
entry as `canWB` so that `LSQUnit::writebackStores()` will send
the store request to cache.
- Finally, when the response comes back, `LSQUnit::completeStore()` will
release the SQ entries.
### Branch Misspeculation
Branch misspeculation is handled in `IEW::executeInsts()`. It will
notify the commit stage to start squashing all instructions in the ROB
on the misspeculated branch.
```cpp
IEW::tick()->IEW::executeInsts()->IEW::squashDueToBranch()
```
### Memory Order Misspeculation
The `InstructionQueue` has a `MemDepUnit` to track memory order dependence.
The IQ will not schedule an instruction if MemDepUnit states there is
dependency.
In `LSQUnit::read()`, the LSQ will search for possible aliasing store and
forward if possible. Otherwise, the load is blocked and rescheduled for when
the blocking store completes by notifying the MemDepUnit.
Both `LSQUnit::executeLoad/Store()` will call `LSQUnit::checkViolation()`
to search the LQ for possible misspeculation. If found, it will set
`LSQUnit::memDepViolator` and `IEW::executeInsts()` will start later to
squash the misspeculated instructions.
```cpp
IEW::tick()->IEW::executeInsts()
->LSQUnit::executeLoad()
->StaticInst::initiateAcc()
->LSQUnit::checkViolation()
->IEW::squashDueToMemOrder()
```