gem5流程学习-1

执行gem5的命令是:
/mnt/c/work/gem5/build/RISCV/gem5.debug /mnt/c/work/gem5/configs/learning_gem5/part1/simple-riscv.py
gem5.debug是debug版本,正常编出来的是gem5.opt

  1. 入口函数 /mnt/c/work/gem5/src/sim/main.cc
    try {
        py::module_::import("m5").attr("main")();
    } 

这会加载 gem5 的 Python 前端模块 m5,并执行它的 main() 函数,m5.main() 会解析命令行,加载用户的 Python 配置脚本 - simple-riscv.py。

分析下simple-riscv.py
image
process.cmd 接受 gem5.debug的参数,就是binary文件。
m5.simulate()开启仿真。
看一段译码的调用栈:

gem5::RiscvISA::Decoder::decode(gem5::RiscvISA::Decoder * const this, gem5::PCStateBase & _next_pc) (\mnt\c\work\gem5\src\arch\riscv\decoder.cc:137)
gem5::BaseSimpleCPU::preExecute(gem5::BaseSimpleCPU * const this) (\mnt\c\work\gem5\src\cpu\simple\base.cc:369)
gem5::TimingSimpleCPU::completeIfetch(gem5::TimingSimpleCPU * const this, gem5::PacketPtr pkt) (\mnt\c\work\gem5\src\cpu\simple\timing.cc:841)
gem5::TimingSimpleCPU::IcachePort::ITickEvent::process(gem5::TimingSimpleCPU::IcachePort::ITickEvent * const this) (\mnt\c\work\gem5\src\cpu\simple\timing.cc:903)
gem5::EventQueue::serviceOne(gem5::EventQueue * const this) (\mnt\c\work\gem5\src\sim\eventq.cc:249)
gem5::doSimLoop(gem5::EventQueue * eventq) (\mnt\c\work\gem5\src\sim\simulate.cc:339)
gem5::simulate(gem5::Tick num_cycles) (\mnt\c\work\gem5\src\sim\simulate.cc:238)
...

在gem5::simulate之前的调用栈是在pybind11里面,这块目前不容易看,应该不需要改动,目前不需要细看。

2.执行 decode的函数:

StaticInstPtr
Decoder::decode(ExtMachInst mach_inst, Addr addr)
{
    DPRINTF(Decode, "Decoding instruction 0x%08x at address %#x\n",
            mach_inst.instBits, addr);

    StaticInstPtr &si = instMap[mach_inst];
    if (!si)
        si = decodeInst(mach_inst);

    si->size(compressed(mach_inst) ? 2 : 4);

    DPRINTF(Decode, "Decode: Decoded %s instruction: %#x\n",
            si->getName(), mach_inst);
    return si;
}

真正的功能在si = decodeInst(mach_inst);
这个会根据arch的不同,调用不同的函数:

using namespace gem5;
StaticInstPtr
RiscvISA::Decoder::decodeInst(RiscvISA::ExtMachInst machInst)
{

文件: /mnt/c/work/gem5/build/RISCV/arch/riscv/generated/decoder-ns.cc.inc
在这个函数中,译码完成,返回一个具体指令的对象。
这些inc文件中的代码都是自动生成的,怎么生成的后面再讨论。

先画一张 c_jr 指令的 cycle 级执行流程图:


┌────────────────────────────────────────────────────┐
│                CPU 取指阶段 (Fetch)                │
│────────────────────────────────────────────────────│
│ 1. 从内存/Cache 取 16bit 的 compressed 指令 c_jr    │
│ 2. 更新 PC 到下一条(暂不跳转)                     │
└───────────────┬────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────────────┐
│                译码阶段 (Decode)  /mnt/c/work/gem5/build/RISCV/arch/riscv/generated/decoder-ns.cc.inc
|                                  /mnt/c/work/gem5/build/RISCV/arch/riscv/generated/decode-method.cc.inc
│────────────────────────────────────────────────────│
│ A. 查表匹配到 C_jr 构造函数                         │
│    - 设置 src/dest 寄存器编号                       │
│    - 设置 flags: IsControl, IsReturn 等             │
│    - 记录 OpClass = IntAluOp                        │
│ B. 返回 C_jr 指令对象给 CPU                         │
└───────────────┬────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────────────┐
│           生成微操作 / 执行语义 (Execute)    /mnt/c/work/gem5/build/RISCV/arch/riscv/generated/exec-ns.cc.inc
│────────────────────────────────────────────────────│
│ 1. CPU 调用 C_jr::execute(xc, traceData)            │
│ 2. execute() 来源于 src/arch/riscv/isa/compressed.isa│
│    - PC = GPR[RC1]                                  │
│    - 发信号给分支预测器更新状态                     │
│    - 返回 Fault::NoFault                            │
└───────────────┬────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────────────┐
│             CPU 功能单元调度 (FU)                   │
│────────────────────────────────────────────────────│
│ 1. 根据 OpClass = IntAluOp 分配整数 ALU 单元         │
│ 2. 功能单元延迟来自 CPU 配置 (FuncUnitConfig.py)     │
│    - TimingSimpleCPU: 固定 1 cycle                  │
│    - O3CPU: 根据 FU latency + pipeline hazard 决定   │
└───────────────┬────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────────────┐
│        分支预测 & Pipeline 控制 (Branch)            │
│────────────────────────────────────────────────────│
│ - 如果预测错误:flush pipeline,重新取指             │
│ - flush 会额外浪费几 cycle(取决于流水深度)         │
└───────────────┬────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────────────┐
│             提交阶段 (Commit)                       │
│────────────────────────────────────────────────────│
│ - 将 PC 更新到跳转目标                              │
│ - 清理 ROB 中的该条指令                             │
│ - 下一条指令开始流入流水线                          │
└────────────────────────────────────────────────────┘
  1. 下面看执行的地方,执行也在 TimingSimpleCPU::completeIfetch(PacketPtr pkt) 函数中。

    preExecute();   -> 译码

    // hardware transactional memory
    if (curStaticInst && curStaticInst->isHtmStart()) {
        // if this HtmStart is not within a transaction,
        // then assign it a new htmTransactionUid
        if (!t_info.inHtmTransactionalState())
            t_info.newHtmTransactionUid();
        SimpleThread* thread = t_info.thread;
        thread->htmTransactionStarts++;
        DPRINTF(HtmCpu, "htmTransactionStarts++=%u\n",
            thread->htmTransactionStarts);
    }

    if (curStaticInst && curStaticInst->isMemRef()) {
        // load or store: just send to dcache
        Fault fault = curStaticInst->initiateAcc(&t_info, traceData);

        // If we're not running now the instruction will complete in a dcache
        // response callback or the instruction faulted and has started an
        // ifetch
        if (_status == BaseSimpleCPU::Running) {
            if (fault != NoFault && traceData) {
                traceFault();
            }

            postExecute();
            // @todo remove me after debugging with legion done
            if (curStaticInst && (!curStaticInst->isMicroop() ||
                        curStaticInst->isFirstMicroop()))
                instCnt++;
            advanceInst(fault);
        }
    } else if (curStaticInst) {
        // non-memory instruction: execute completely now
        Fault fault = curStaticInst->execute(&t_info, traceData);   -> 执行

执行函数call stack:

gem5::RiscvISAInst::C_jr::execute(const gem5::RiscvISAInst::C_jr * const this, gem5::ExecContext * xc, gem5::trace::InstRecord * traceData) (\mnt\c\work\gem5\build\RISCV\arch\riscv\generated\exec-ns.cc.inc:2623)
gem5::TimingSimpleCPU::completeIfetch(gem5::TimingSimpleCPU * const this, gem5::PacketPtr pkt) (\mnt\c\work\gem5\src\cpu\simple\timing.cc:876)
gem5::TimingSimpleCPU::IcachePort::ITickEvent::process(gem5::TimingSimpleCPU::IcachePort::ITickEvent * const this) (\mnt\c\work\gem5\src\cpu\simple\timing.cc:903)
gem5::EventQueue::serviceOne(gem5::EventQueue * const this) (\mnt\c\work\gem5\src\sim\eventq.cc:249)
gem5::doSimLoop(gem5::EventQueue * eventq) (\mnt\c\work\gem5\src\sim\simulate.cc:339)
gem5::simulate(gem5::Tick num_cycles) (\mnt\c\work\gem5\src\sim\simulate.cc:238)
pybind11::detail::argument_loader<unsigned long>::call_impl<gem5::GlobalSimLoopExitEvent*, gem5::GlobalSimLoopExitEvent* (*&)(unsigned long), 0ul, pybind11::detail::void_type>(gem5::GlobalSimLoopExitEvent* 
posted @ 2025-08-08 18:48  pch126  阅读(31)  评论(0)    收藏  举报