跳转至

调度器相关代码介绍

1. 架构概述与演进历程

1.1 从集中式到分布式的演进

XS-GEM5的调度器架构体现了现代超标量处理器设计的重要演进:从传统的集中式指令队列(Monolithic IQ)分布式发射队列(Distributed IssueQueue)架构的转变。这一转变不仅降低了硬件复杂性和功耗,还为提高时钟频率创造了条件。

传统集中式架构的限制

// 传统设计:单一庞大的指令队列
class MonolithicInstructionQueue {
    std::vector<DynInstPtr> entries[128];    // 大容量单一队列
    std::vector<std::vector<bool>> depMatrix; // NxN依赖矩阵,复杂度O(N²)
    ComplexSelector selector;                 // 复杂的全局选择逻辑
    // 问题:随着队列增大,硬件复杂度呈指数增长
};

现代分布式架构的优势

// 现代设计:多个小规模专用队列
class DistributedScheduler {
    std::vector<IssueQue*> issueQues;        // 多个小队列
    // 每个队列: 复杂度O(N/k),总复杂度降为O(N)
    WakeupNetwork specWakeupChannels;         // 灵活的唤醒网络
    GlobalArbitrator portArbitrator;          // 全局资源仲裁
};

1.2 XS-GEM5调度器主要组件

  • Scheduler: 中央协调者,负责指令分发、全局仲裁和唤醒协调
  • IssueQue: 分布式发射队列,每个专门处理特定类型指令
  • InstructionQueue: 兼容性包装器,提供对旧接口的支持
  • SpecWakeupChannel: 推测性唤醒通道,实现跨队列性能优化

1.3 指令生命周期流程

Rename Stage → Dispatch Queues → Scheduler Distribution
       |              |                    |
       V              V                    V
   指令重命名 → 两阶段分发缓冲 → 分布式队列分配
       |              |                    |
       V              V                    V
   依赖分析   →   类型分类   →   局部依赖图构建
       |              |                    |
       V              V                    V
┌─────────────────────────────────────────────────┐
│              分布式发射逻辑                        │
│   ┌─────────┐ ┌─────────┐ ┌─────────┐           │
│   │ IntIQ0  │ │ FpIQ0   │ │ MemIQ0  │ ...       │
│   │ Ready   │ │ Ready   │ │ Ready   │           │
│   │ Select  │ │ Select  │ │ Select  │           │
│   └─────────┘ └─────────┘ └─────────┘           │
│            ↓        ↓        ↓                  │
│         Global Port Arbitration                │
│                    ↓                           │
│            Issue to Function Units             │
└─────────────────────────────────────────────────┘
       |              |                    |
       V              V                    V
   功能单元执行 → 推测性唤醒网络 → 依赖链传播
       |              |                    |
       V              V                    V
   结果写回    →   正式唤醒    →   下一轮发射

2. 核心数据结构深度解析

2.1 Scheduler 类:中央协调器 (src/cpu/o3/issue_queue.hh:231)

Scheduler作为分布式调度器的大脑,其设计体现了现代处理器"分治思想"的精髓。

2.1.1 核心成员变量详解

class Scheduler : public SimObject {
    // === 发射队列管理 ===
    std::vector<IssueQue*> issueQues;              // 管理的所有发射队列
    std::vector<DispPolicy> dispTable;             // OpClass→IssueQue映射表
    std::vector<int> dispSeqVec;                    // 分发序列缓存

    // === 三级记分板系统 ===
    std::vector<bool> scoreboard;                  // 正式记分板:真实数据就绪
    std::vector<bool> bypassScoreboard;            // 旁路记分板:旁路数据就绪  
    std::vector<bool> earlyScoreboard;             // 早期记分板:推测数据就绪

    // === 推测唤醒网络 ===
    std::vector<std::vector<IssueQue*>> wakeMatrix; // 唤醒矩阵[srcIQ][dstIQs]
    PendingWakeEventsType specWakeEvents;          // 待处理的推测唤醒事件

    // === 寄存器文件端口仲裁 ===
    std::vector<std::pair<DynInstPtr, int>> rfPortOccupancy; // 端口占用状态
    std::vector<DynInstPtr> arbFailedInsts;        // 仲裁失败的指令列表
    int rfMaxTypePortId;                           // 最大端口类型ID

    // === 执行控制 ===
    std::vector<DynInstPtr> instsToFu;             // 发射到功能单元的指令
    std::vector<bool> opPipelined;                 // 操作是否流水化
    std::vector<int> opExecTimeTable;              // 操作执行延迟表

    // === 性能统计 ===
    SchedulerStats stats;                          // 调度器性能统计
};

2.1.2 三级记分板系统的设计哲学

TODO: 似乎有问题,没有在代码中找到对应函数!

/*
 * 三级记分板系统实现了精确的数据依赖跟踪:
 * 
 * Level 1 - Early Scoreboard (推测就绪):
 *   当指令发射时立即设置,用于激进的推测唤醒
 *   风险:可能被取消,需要回滚机制
 * 
 * Level 2 - Bypass Scoreboard (旁路就绪):
 *   当指令开始执行、结果可通过旁路网络获取时设置
 *   用于支持back-to-back指令执行
 * 
 * Level 3 - Regular Scoreboard (正式就绪):
 *   当指令完全执行完毕、结果写入寄存器文件时设置
 *   最保守但最可靠的就绪状态
 */

void Scheduler::updateScoreboard(const DynInstPtr& inst, ScoreboardLevel level) {
    for (int i = 0; i < inst->numDestRegs(); i++) {
        auto dst = inst->renamedDestIdx(i);
        if (dst->isFixedMapping()) continue;

        switch (level) {
        case EARLY_READY:
            earlyScoreboard[dst->flatIndex()] = true;
            DPRINTF(Schedule, "[sn:%lli] Early ready: p%d\n", 
                    inst->seqNum, dst->flatIndex());
            break;
        case BYPASS_READY:
            bypassScoreboard[dst->flatIndex()] = true;
            DPRINTF(Schedule, "[sn:%lli] Bypass ready: p%d\n", 
                    inst->seqNum, dst->flatIndex());
            break;
        case FINAL_READY:
            scoreboard[dst->flatIndex()] = true;
            DPRINTF(Schedule, "[sn:%lli] Final ready: p%d\n", 
                    inst->seqNum, dst->flatIndex());
            break;
        }
    }
}

2.2 IssueQue 类:分布式微调度器 (src/cpu/o3/issue_queue.hh:102)

每个IssueQue实际上是一个完整的小型调度器,具备独立的指令管理、依赖跟踪和选择逻辑能力。

2.2.1 指令存储与管理架构

class IssueQue : public SimObject {
    // === 指令存储层次 ===
    std::list<DynInstPtr> instList;                    // 主存储:所有指令
    std::vector<ReadyQue*> readyQs;                     // 就绪队列:按输出端口分组
    SelectQue selectQ;                                  // 选择队列:仲裁后的候选
    std::queue<DynInstPtr> replayQ;                     // 重放队列:需重试的指令

    // === 多级发射流水线 ===
    TimeBuffer<IssueStream> inflightIssues;            // 发射流水线缓冲
    TimeBuffer<IssueStream>::wire toIssue;              // S0: 选择输出
    TimeBuffer<IssueStream>::wire toFu;                 // Sn: 发射到FU
    int scheduleToExecDelay;                            // 发射流水线深度

    // === 局部依赖图 ===
    // subDepGraph[physRegId] = [(srcOpIdx, dependentInst), ...]
    std::vector<std::vector<std::pair<uint8_t, DynInstPtr>>> subDepGraph;

    // === 端口配置与管理 ===
    std::vector<IssuePort*> oports;                     // 输出端口配置
    std::vector<int64_t> portBusy;                      // 端口占用状态
    int inports;                                        // 输入端口数量
    int outports;                                       // 输出端口数量

    // === 指令选择策略 ===
    BaseSelector* selector;                             // 选择器策略对象

    // === 容量管理 ===
    const int iqsize;                                   // 队列最大容量
    uint64_t instNum;                                   // 当前指令数
    std::vector<uint8_t> opNum;                         // 按OpClass的指令计数
};

2.2.2 依赖图的精巧实现

TODO: 有问题,没有在代码中找到对应函数!

/*
 * 局部依赖图设计:
 * - 索引方式:按物理寄存器索引 subDepGraph[physRegId]
 * - 存储内容:依赖该寄存器的所有指令及其源操作数位置
 * - 优势:O(1)查找依赖者,高效的唤醒传播
 */

void IssueQue::buildDependencyGraph(const DynInstPtr& inst) {
    DPRINTF(Schedule, "[sn:%lli] Building dependency graph\n", inst->seqNum);

    bool hasUnresolvedDeps = false;

    // 遍历所有源操作数
    for (int srcIdx = 0; srcIdx < inst->numSrcRegs(); srcIdx++) {
        auto srcReg = inst->renamedSrcIdx(srcIdx);

        if (srcReg->isFixedMapping()) {
            // 架构寄存器,总是就绪
            inst->markSrcRegReady(srcIdx);
            continue;
        }

        // 检查三级记分板状态
        if (scheduler->scoreboard[srcReg->flatIndex()]) {
            // 数据已在寄存器文件中
            inst->markSrcRegReady(srcIdx);
        } else if (scheduler->earlyScoreboard[srcReg->flatIndex()]) {
            // 可能通过推测获得数据
            inst->markSrcRegReady(srcIdx);
        } else {
            // 需要等待,建立依赖关系
            subDepGraph[srcReg->flatIndex()].emplace_back(srcIdx, inst);
            hasUnresolvedDeps = true;

            DPRINTF(Schedule, "[sn:%lli] Depends on p%d (src%d)\n", 
                    inst->seqNum, srcReg->flatIndex(), srcIdx);
        }
    }

    // 如果所有依赖都解决,加入就绪队列
    if (!hasUnresolvedDeps) {
        addToReadyQueue(inst);
    }
}

2.3 InstructionQueue 类:兼容性适配器 (src/cpu/o3/inst_queue.hh:106)

InstructionQueue现在主要作为向后兼容的适配器,将旧的集中式接口转换为新的分布式调度器调用。

2.3.1 适配器模式的实现

class InstructionQueue {
    // === 核心组件委托 ===
    Scheduler* scheduler;                               // 委托给分布式调度器
    MemDepUnit memDepUnit[MaxThreads];                  // 内存依赖单元

    // === 兼容性接口维护 ===
    std::list<DynInstPtr> instsToExecute;               // 执行就绪指令队列
    std::list<DynInstPtr> deferredMemInsts;             // 延迟的内存指令
    std::unordered_set<DynInstPtr> cacheMissLdInsts;    // 缓存缺失的Load
    std::list<STLFFailLdInst> stlfFailLdInsts;          // Store-to-Load转发失败
    std::list<DynInstPtr> blockedMemInsts;              // 被阻塞的内存指令
    std::list<DynInstPtr> retryMemInsts;                // 重试内存指令

    // === 非推测指令管理 ===
    std::map<InstSeqNum, DynInstPtr> nonSpecInsts;      // 非推测指令映射

    // === 统计信息 ===
    IQStats iqStats;                                    // 指令队列统计
    IQIOStats iqIOStats;                                // IO访问统计

public:
    // === 主要适配接口 ===
    void insert(const DynInstPtr &new_inst, int disp_seq) {
        scheduler->insert(new_inst, disp_seq);          // 委托给Scheduler
        ++iqStats.instsAdded;
    }

    bool hasReadyInsts() {
        return scheduler->hasReadyInsts();               // 委托查询
    }

    DynInstPtr getInstToExecute() {
        assert(!instsToExecute.empty());
        DynInstPtr inst = std::move(instsToExecute.front());
        instsToExecute.pop_front();
        return inst;
    }

    int wakeDependents(const DynInstPtr &completed_inst) {
        // 处理物理寄存器的释放逻辑(兼容性需要)
        completed_inst->lastWakeDependents = curTick();

        // 实际唤醒由Scheduler处理
        return 0;  // 分布式架构中,依赖计数不在此维护
    }
};

2.4 推测唤醒网络:性能加速的核心 (src/cpu/o3/issue_queue.hh:220)

推测唤醒是现代超标量处理器的关键性能优化技术,通过预测指令完成时间实现提前唤醒。

2.4.1 唤醒通道的定义与配置

class SpecWakeupChannel : public SimObject {
public:
    std::vector<std::string> srcIQs;                    // 源发射队列列表
    std::vector<std::string> dstIQs;                    // 目标发射队列列表

    SpecWakeupChannel(const SpecWakeupChannelParams& params)
        : SimObject(params), srcIQs(params.srcs), dstIQs(params.dsts) {}
};

/*
 * 香山V3处理器的典型唤醒网络配置:
 */
class KMHV3Scheduler : public Scheduler {
    // 定义各功能域的队列
    std::vector<std::string> intBank = {"intIQ0", "intIQ1", "intIQ2", "intIQ3", "intIQ4", "intIQ5"};
    std::vector<std::string> memBank = {"ld0", "ld1", "ld2", "sta0", "sta1", "std0", "std1"};
    std::vector<std::string> fpBank = {"fpIQ0", "fpIQ1", "fpIQ2", "fpIQ3"};

    // 配置唤醒网络
    std::vector<SpecWakeupChannel*> specWakeupNetwork = {
        // 整数域内部唤醒 + 整数到内存域唤醒
        new SpecWakeupChannel({.srcs = intBank + memBank, .dsts = intBank + memBank}),

        // 浮点域内部唤醒(隔离设计避免跨域干扰)
        new SpecWakeupChannel({.srcs = fpBank, .dsts = fpBank})
    };
};

对应scheduler 中wakeMatrix的构建

2.4.2 推测唤醒事件的生命周期管理

class SpecWakeupCompletion : public Event {
    DynInstPtr inst;                                    // 触发唤醒的指令
    IssueQue* to_issue_queue;                          // 目标发射队列
    PendingWakeEventsType* owner;                       // 事件管理器引用

public:
    SpecWakeupCompletion(const DynInstPtr& _inst, IssueQue* to, 
                        PendingWakeEventsType* _owner)
        : Event(Stat_Event_Pri, AutoDelete), inst(_inst), 
          to_issue_queue(to), owner(_owner) {}

    void process() override {
        // 执行推测唤醒
        to_issue_queue->wakeUpDependents(inst, true);

        // 清理事件记录
        (*owner)[inst->seqNum].erase(this);
        if ((*owner)[inst->seqNum].empty()) {
            owner->erase(inst->seqNum);
        }

        DPRINTF(Schedule, "[sn:%lli] Speculative wakeup completed\n", 
                inst->seqNum);
    }

    const char* description() const override {
        return "Speculative wakeup completion";
    }
};

/*
 * 推测唤醒的风险管理:
 * 如果推测失败(指令被squash),需要取消所有相关的推测唤醒事件
 */
void Scheduler::cancelSpeculativeWakeups(const DynInstPtr& inst) {
    auto it = specWakeEvents.find(inst->seqNum);
    if (it != specWakeEvents.end()) {
        for (auto* event : it->second) {
            cpu->deschedule(event);  // 取消事件调度
            delete event;            // 释放事件对象
        }
        specWakeEvents.erase(it);

        DPRINTF(Schedule, "[sn:%lli] Cancelled %d speculative wakeup events\n", 
                inst->seqNum, it->second.size());
    }
}
TODO: 没有在代码中找到对应函数!cancelSpeculativeWakeups

3. 分布式调度核心流程深度解析

3.1 两阶段指令分发机制

XS-GEM5实现了创新的两阶段分发机制,解耦了指令接收和队列分配,提高了流水线弹性。

3.1.1 阶段一:分类到分发队列 (IEW::classifyInstToDispQue)

/*
 * 第一阶段:预处理和缓冲
 * 目标:将来自Rename的指令按类型分类到缓冲队列
 * 优势:提供缓冲层,避免下游阻塞影响上游
 */
void IEW::classifyInstToDispQue(ThreadID tid) {
    std::deque<DynInstPtr> &insts_to_dispatch = 
        dispatchStatus[tid] == Unblocking ? skidBuffer[tid] : insts[tid];

    bool emptyROB = fromCommit->commitInfo[tid].emptyROB;
    unsigned dispatched = 0;

    while (!insts_to_dispatch.empty()) {
        auto& inst = insts_to_dispatch.front();

        // 1. 指令类型分类
        DQType dqType = getInstDQType(inst);  // IntDQ/FVDQ/MemDQ

        // 2. 容量检查
        if (dispQue[dqType].size() >= dqSize[dqType]) {
            // 对应类型的分发队列已满,暂停分发
            DPRINTF(Dispatch, "DispQueue[%d] full, blocking dispatch\n", dqType);
            break;
        }

        // 3. 序列化指令特殊处理
        if ((inst->isSerializeBefore() && !inst->isSerializeHandled()) && !emptyROB) {
            // 序列化指令需要等待ROB为空
            DPRINTF(Dispatch, "[sn:%lli] Serialize before, waiting for empty ROB\n", 
                    inst->seqNum);
            break;
        }

        // 4. 添加到分发队列
        dispQue[dqType].push_back(inst);
        inst->enterDQTick = curTick();  // 记录进入分发队列的时间

        // 5. 生产者注册(用于依赖图构建)
        if (!inst->isNop() && !inst->isEliminated()) {
            scheduler->addProducer(inst);  // 标记为数据生产者
        }

        insts_to_dispatch.pop_front();
        dispatched++;
    }

    // 更新分发统计
    iewStats.dispatchedInst[tid] += dispatched;

    // 处理阻塞情况
    if (!insts_to_dispatch.empty()) {
        block(tid);  // 阻塞当前线程的分发
    }
}

/*
 * 指令类型分类逻辑
 */
IEW::DQType IEW::getInstDQType(const DynInstPtr &inst) {
    // 内存相关指令进入MemDQ
    if (inst->isMemRef() || inst->isReadBarrier() || 
        inst->isWriteBarrier() || inst->isNonSpeculative()) {
        return MemDQ;
    }

    // 浮点和向量指令进入FVDQ
    if (inst->isFloating() || inst->isVector()) {
        return FVDQ;
    }

    // 其他指令进入IntDQ
    return IntDQ;
}

3.1.2 阶段二:从分发队列到调度器 (IEW::dispatchInstFromDispQue)

/*
 * 第二阶段:正式分发到调度器
 * 目标:将缓冲队列中的指令分发到具体的IssueQue
 * 特点:支持负载均衡和智能分发策略
 */
void IEW::dispatchInstFromDispQue(ThreadID tid) {
    bool add_to_iq = false;
    int totalDispatched = 0;

    // 遍历所有分发队列类型
    for (int dqType = 0; dqType < NumDQ; dqType++) {
        int dispatched = 0;
        int disp_seq = -1;

        // === 关键优化:预调度分析 ===
        // lookahead()会分析即将分发的指令,提前计算分发策略
        scheduler->lookahead(dispQue[dqType]);

        // 按分发带宽限制处理
        while (!dispQue[dqType].empty() && dispatched < dispWidth[dqType]) {
            DynInstPtr inst = dispQue[dqType].front();
            disp_seq++;  // 分发序列号,用于负载均衡

            // 跳过已废除指令
            if (inst->isSquashed()) {
                dispQue[dqType].pop_front();
                continue;
            }

            // === 关键检查:调度器就绪状态 ===
            if (!scheduler->ready(inst, disp_seq)) {
                // 目标IssueQue没有空间,停止当前类型的分发
                DPRINTF(Dispatch, "[sn:%lli] Scheduler not ready for %s\n", 
                        inst->seqNum, enums::OpClassStrings[inst->opClass()]);
                break;
            }

            // === LSQ容量检查(内存指令特有) ===
            if (checkLSQCapacity(inst, tid)) {
                DPRINTF(Dispatch, "[sn:%lli] LSQ capacity exhausted\n", inst->seqNum);
                break;
            }

            // === 指令分类处理 ===
            add_to_iq = classifyAndHandleInst(inst, tid);

            // === 非推测指令特殊处理 ===
            if (add_to_iq && inst->isNonSpeculative()) {
                inst->setCanCommit();
                instQueue.insertNonSpec(inst);  // 非推测指令专用队列
                add_to_iq = false;
            }

            // === 正式插入调度器 ===
            if (add_to_iq) {
                instQueue.insert(inst, disp_seq);  // 委托给调度器
            }

            // 清理和统计
            inst->exitDQTick = curTick();
            ppDispatch->notify(inst);  // 性能探针通知
            dispQue[dqType].pop_front();
            dispatched++;
            totalDispatched++;
        }
    }

    // 更新整体分发统计  
    iewStats.dispDist.sample(totalDispatched);
}

3.2 Scheduler智能分发策略

3.2.1 预测性负载均衡 (Scheduler::lookahead)

/*
 * lookahead机制:预测性分发策略
 * 目标:在实际分发前分析指令序列,优化队列分配
 * 核心思想:避免某个IssueQue成为瓶颈
 */
void Scheduler::lookahead(std::deque<DynInstPtr>& insts) {
    if (old_disp) {
        return;  // 传统分发模式,不使用预测
    }

    // 按OpClass统计即将分发的指令数量
    uint8_t disp_op_num[Num_OpClasses];
    std::memset(disp_op_num, 0, sizeof(disp_op_num));

    int i = 0;
    for (auto& inst : insts) {
        OpClass opClass = inst->opClass();

        // 获取该OpClass对应的IssueQue列表
        auto& iqs = dispTable[opClass];

        // === 关键策略:按负载排序 ===
        // 根据当前队列中该OpClass的指令数量排序,负载小的优先
        // 由于iqs 是引用,这里排序后已经修改了dispTable中的顺序!之后ready 就会按负载顺序放进去!
        std::sort(iqs.begin(), iqs.end(), disp_policy(opClass));

        // === Split Store特殊处理 ===
        if (inst->isSplitStoreAddr()) {
            // Store指令会被拆分为地址和数据两部分
            auto& stdIqs = dispTable[StoreDataOp];
            std::sort(stdIqs.begin(), stdIqs.end(), disp_policy(StoreDataOp));
        }

        // 计算该指令应该分发到哪个IssueQue(轮询策略)
        dispSeqVec[i] = disp_op_num[opClass] % iqs.size();
        disp_op_num[opClass]++;
        i++;

        DPRINTF(Schedule, "[sn:%lli] %s will go to IQ[%d] (load balancing)\n", 
                inst->seqNum, enums::OpClassStrings[opClass], dispSeqVec[i-1]);
    }
}

/*
 * 分发策略比较器:负载均衡核心算法
 */
bool Scheduler::disp_policy::operator()(IssueQue* a, IssueQue* b) const {
    // 比较两个IssueQue中指定OpClass的指令数量
    int loadA = a->opNum[disp_op];  // 队列A中该OpClass的指令数
    int loadB = b->opNum[disp_op];  // 队列B中该OpClass的指令数

    // 负载小的队列优先级更高
    return loadA < loadB;
}

3.2.2 智能分发决策 (Scheduler::insert)

/*
 * 主分发接口:将指令插入到最优的IssueQue
 */
void Scheduler::insert(const DynInstPtr& inst, int disp_seq) {
    DPRINTF(Schedule, "[sn:%lli] Inserting %s instruction\n", 
            inst->seqNum, enums::OpClassStrings[inst->opClass()]);

    // === Split Store指令特殊处理 ===
    if (inst->isSplitStoreAddr()) {
        // 1. 创建Store数据微操作
        auto stduop = inst->createStoreDataUop();
        this->insert(stduop, disp_seq);  // 递归插入数据部分

        // 2. 将原指令转换为Store地址微操作
        inst->buildStoreAddrUop();
        DPRINTF(Schedule, "[sn:%lli] Split store: addr=%lli, data=%lli\n", 
                inst->seqNum, inst->seqNum, stduop->seqNum);
    }

    // 获取目标IssueQue列表
    auto& iqs = dispTable[inst->opClass()];
    assert(!iqs.empty());

    if (old_disp) {
        // === 传统分发:遍历查找可用队列 ===
        bool inserted = false;
        std::sort(iqs.begin(), iqs.end(), disp_policy(inst->opClass()));

        for (auto iq : iqs) { // 遍历所有IssueQue,找到一个ready的IssueQue,将指令插入进去,RTL开销大
            if (iq->ready()) {
                iq->insert(inst);
                inserted = true;
                break;
            }
        }
        panic_if(!inserted, "No available IQ for opClass %s", 
                 enums::OpClassStrings[inst->opClass()]);
    } else {
        // === 现代分发:基于预测的精确分配 ===
        IssueQue* targetIQ = iqs[dispSeqVec.at(disp_seq)]; // 按照lookahead 的顺序,将指令插入到对应的IssueQue中
        assert(targetIQ->ready());
        targetIQ->insert(inst);

        DPRINTF(Schedule, "[sn:%lli] Assigned to %s (seq=%d)\n", 
                inst->seqNum, targetIQ->getName().c_str(), disp_seq);
    }
}

3.3 分布式发射与选择机制

3.3.1 "局部选择,全局仲裁"架构

/*
 * 核心调度函数:协调所有IssueQue的发射过程
 * 实现"局部选择,全局仲裁"的分布式调度策略
 */
void Scheduler::issueAndSelect() {
    DPRINTF(Schedule, "=== Starting distributed issue and select cycle ===\n");

    // === 第一步:局部选择阶段 ===
    // 每个IssueQue独立选择其最佳候选指令
    for (auto iq : issueQues) {
        iq->selectInst();  // 局部选择,结果存储在iq->selectQ中
    }

    // === 第二步:全局仲裁阶段 ===
    // 处理跨队列的资源冲突(主要是寄存器文件端口冲突)
    globalPortArbitration();

    // === 第三步:正式发射阶段 ===  
    // 通过仲裁的指令正式发射到功能单元
    for (auto iq : issueQues) {
        iq->issueToFu();  // 将selectQ中的获胜指令发射出去
    }

    // === 第四步:性能统计更新 ===
    updateIssueStats();
}

/*
 * 全局寄存器文件端口仲裁
 * 解决多个队列同时请求相同读端口的冲突
 */
void Scheduler::globalPortArbitration() {
    // 清空仲裁失败列表
    arbFailedInsts.clear();

    // 重置端口占用状态
    std::fill(rfPortOccupancy.begin(), rfPortOccupancy.end(), 
              std::make_pair(nullptr, 0));

    // 收集所有候选指令的端口需求
    for (auto iq : issueQues) {
        for (auto& [portId, inst] : iq->selectQ) {
            // 检查每个源操作数的端口需求
            for (int srcIdx = 0; srcIdx < inst->numSrcRegs(); srcIdx++) {
                auto srcReg = inst->renamedSrcIdx(srcIdx);
                if (srcReg->isFixedMapping()) continue;

                // 获取端口配置
                int typePortId = getRegFilePortId(iq, portId, srcIdx, srcReg);
                int priority = getPortPriority(iq, portId, srcIdx);

                // 尝试分配端口
                if (!allocateRegFilePort(inst, srcReg, typePortId, priority)) {
                    // 仲裁失败,将指令加入失败列表
                    arbFailedInsts.push_back(inst);
                    DPRINTF(Schedule, "[sn:%lli] Port arbitration failed for p%d\n", 
                            inst->seqNum, srcReg->flatIndex());
                    break;  // 该指令失败,检查下一条指令
                }
            }
        }
    }

    // 通知各IssueQue仲裁结果
    for (auto& inst : arbFailedInsts) {
        inst->setArbFailed();  // 标记仲裁失败,下周期重试
    }

    DPRINTF(Schedule, "Port arbitration: %d instructions failed\n", 
            arbFailedInsts.size());
}

3.3.2 IssueQue局部选择逻辑

/*
 * 单个IssueQue的指令选择过程
 * 目标:从就绪队列中选出最优候选指令
 */
void IssueQue::selectInst() {
    selectQ.clear();  // 清空上一周期的选择结果

    // 遍历所有输出端口
    for (int portId = 0; portId < outports; portId++) {
        auto readyQ = readyQs[portId];
        if (readyQ->empty()) continue;

        // 使用选择器策略选择最佳指令
        selector->begin(readyQ);  // 设置选择范围

        // 按照选择器策略,逐个选择最佳指令
        for (auto it = selector->select(readyQ->begin(), portId); 
             it != readyQ->end(); 
             it = selector->select(++it, portId)) {

            auto& inst = *it;

            // 跳过已取消的指令
            if (inst->canceled()) {
                inst->clearInReadyQ();
                it = readyQ->erase(it);
                continue;
            }

            // === 关键检查:端口可用性 ===
            // 检查该端口在未来几个周期是否被占用
            uint64_t futureBusyMask = portBusy[portId];
            int instOpLatency = scheduler->getCorrectedOpLat(inst);

            if (!(futureBusyMask & (1llu << instOpLatency))) {
                // 端口可用,选择该指令
                DPRINTF(Schedule, "[sn:%lli] Selected for port %d\n", 
                        inst->seqNum, portId);

                // 预分配寄存器文件读端口
                bool portAllocSuccess = preAllocateRegFilePorts(inst, portId);
                if (portAllocSuccess) {
                    selectQ.push_back(std::make_pair(portId, inst));
                    inst->clearInReadyQ();
                    readyQ->erase(it);
                    break;  // 该端口已选定指令,选择下一个端口
                } else {
                    // 端口预分配失败,尝试下一条指令
                    continue;
                }
            } else {
                // 端口忙,记录统计信息
                iqstats->portBusy[portId]++;
                DPRINTF(Schedule, "[sn:%lli] Port %d busy, mask=0x%llx\n", 
                        inst->seqNum, portId, futureBusyMask);
            }
        }
    }

    DPRINTF(Schedule, "%s selected %d instructions this cycle\n", 
            iqname.c_str(), selectQ.size());
}

/*
 * 寄存器文件端口预分配
 * 在全局仲裁前,先进行本地端口需求分析
 */
bool IssueQue::preAllocateRegFilePorts(const DynInstPtr& inst, int portId) {
    // 获取该端口的寄存器文件配置
    auto& intPorts = intRfTypePortId[portId];
    auto& fpPorts = fpRfTypePortId[portId];

    // 检查每个源操作数
    for (int srcIdx = 0; srcIdx < inst->numSrcRegs(); srcIdx++) {
        auto srcReg = inst->srcRegIdx(srcIdx);
        auto physReg = inst->renamedSrcIdx(srcIdx);

        if (physReg->isFixedMapping()) continue;

        std::pair<int, int> typePortIdPri;
        bool portFound = false;

        // 根据寄存器类型选择端口
        if (srcReg.isIntReg() && srcIdx < intPorts.size()) {
            typePortIdPri = intPorts[srcIdx];
            portFound = true;
        } else if (srcReg.isFloatReg() && srcIdx < fpPorts.size()) {
            typePortIdPri = fpPorts[srcIdx];
            portFound = true;
        }

        if (portFound) {
            // 通知调度器进行端口分配
            scheduler->useRegfilePort(inst, physReg, 
                                    typePortIdPri.first,   // typePortId
                                    typePortIdPri.second); // priority
        }
    }

    return true;  // 预分配总是成功,实际冲突由全局仲裁解决
}

3.4 多级唤醒机制深度解析

3.4.1 推测性唤醒:性能优化的核心

/*
 * 推测性唤醒:现代超标量处理器的关键技术
 * 原理:在指令发射时就预测其完成时间,提前唤醒依赖指令
 * 优势:减少依赖链延迟,提高指令级并行度
 * 风险:推测错误需要回滚机制
 */
void Scheduler::specWakeUpDependents(const DynInstPtr& inst, IssueQue* from_issue_queue) {
    // 只有流水化且有目标寄存器的非Load指令才进行推测唤醒
    if (!opPipelined[inst->opClass()] || inst->numDestRegs() == 0 || inst->isLoad()) {
        DPRINTF(Schedule, "[sn:%lli] Skip spec wakeup: not eligible\n", inst->seqNum);
        return;
    }

    DPRINTF(Schedule, "[sn:%lli] Starting speculative wakeup from %s\n", 
            inst->seqNum, from_issue_queue->getName().c_str());

    // 遍历推测唤醒网络,找到所有目标队列
    for (auto to : wakeMatrix[from_issue_queue->getId()]) {
        // 计算唤醒延迟
        int baseOpLatency = getCorrectedOpLat(inst);
        int wakeDelay = baseOpLatency - 1;  // 提前一个周期唤醒

        // === 跨队列延迟调整 ===
        // 不同深度的发射流水线需要调整唤醒时机
        int stagesDiff = std::abs(from_issue_queue->getIssueStages() - 
                                 to->getIssueStages());

        if (from_issue_queue->getIssueStages() > to->getIssueStages()) {
            wakeDelay += stagesDiff;  // 目标队列更浅,延迟增加
        } else if (wakeDelay >= stagesDiff) {
            wakeDelay -= stagesDiff;  // 目标队列更深,延迟减少
        }

        DPRINTF(Schedule, "[sn:%lli] Wakeup %s->%s, delay=%d cycles\n", 
                inst->seqNum, from_issue_queue->getName().c_str(), 
                to->getName().c_str(), wakeDelay);

        if (wakeDelay == 0) {
            // === 立即推测唤醒 ===
            to->wakeUpDependents(inst, true);  // 推测性标志为true

            // 更新早期记分板(仅限整数指令)
            if (!(inst->isFloating() || inst->isVector())) {
                for (int i = 0; i < inst->numDestRegs(); i++) {
                    auto dst = inst->renamedDestIdx(i);
                    if (!dst->isFixedMapping()) {
                        earlyScoreboard[dst->flatIndex()] = true;
                        DPRINTF(Schedule, "[sn:%lli] Early scoreboard set: p%d\n", 
                                inst->seqNum, dst->flatIndex());
                    }
                }
            }
        } else {
            // === 延迟推测唤醒 ===
            // 创建推测唤醒事件
            auto wakeEvent = new SpecWakeupCompletion(inst, to, &specWakeEvents);

            // 记录事件用于可能的取消操作
            specWakeEvents[inst->seqNum].insert(wakeEvent);

            // 调度事件
            Tick whenToWake = cpu->clockEdge(Cycles(wakeDelay)) - 1;
            cpu->schedule(wakeEvent, whenToWake);

            DPRINTF(Schedule, "[sn:%lli] Scheduled spec wakeup event at tick %llu\n", 
                    inst->seqNum, whenToWake);
        }
    }
}

/*
 * Load指令专用的推测唤醒
 * Load的特殊性:延迟不确定,但可以在流水线中提前唤醒依赖者
 */
void Scheduler::specWakeUpFromLoadPipe(const DynInstPtr& inst) {
    assert(inst->isLoad());

    auto from_issue_queue = inst->issueQue;
    DPRINTF(Schedule, "[sn:%lli] Load pipe spec wakeup from %s\n", 
            inst->seqNum, from_issue_queue->getName().c_str());

    // Load指令的推测唤醒总是立即进行(在Load流水线中)
    for (auto to : wakeMatrix[from_issue_queue->getId()]) {
        to->wakeUpDependents(inst, true);

        // 设置早期记分板
        for (int i = 0; i < inst->numDestRegs(); i++) {
            auto dst = inst->renamedDestIdx(i);
            if (!dst->isFixedMapping()) {
                earlyScoreboard[dst->flatIndex()] = true;
            }
        }
    }
}

3.4.2 正式唤醒:可靠的依赖解除

/*
 * 写回阶段的正式唤醒
 * 特点:数据已经真实可用,唤醒是安全和可靠的
 */
void Scheduler::writebackWakeup(const DynInstPtr& inst) {
    DPRINTF(Schedule, "[sn:%lli] Writeback wakeup started\n", inst->seqNum);

    inst->setWriteback();  // 标记指令已写回
    cpu->perfCCT->updateInstPos(inst->seqNum, PerfRecord::AtWriteVal);

    // 更新正式记分板
    for (int i = 0; i < inst->numDestRegs(); i++) {
        auto dst = inst->renamedDestIdx(i);
        if (!dst->isFixedMapping()) {
            scoreboard[dst->flatIndex()] = true;
            DPRINTF(Schedule, "[sn:%lli] Scoreboard set: p%d\n", 
                    inst->seqNum, dst->flatIndex());
        }
    }

    // 通知所有IssueQue进行正式唤醒
    for (auto iq : issueQues) {
        iq->wakeUpDependents(inst, false);  // 非推测性唤醒
    }
}

/*
 * IssueQue内的依赖唤醒实现
 */
void IssueQue::wakeUpDependents(const DynInstPtr& inst, bool speculative) {
    if (speculative && inst->canceled()) {
        // 推测性唤醒时,如果指令已被取消,则跳过
        return;
    }

    DPRINTF(Schedule, "[sn:%lli] %s wakeup in %s\n", 
            inst->seqNum, speculative ? "Speculative" : "Writeback", iqname.c_str());

    // 遍历该指令的所有目标寄存器
    for (int i = 0; i < inst->numDestRegs(); i++) {
        auto dst = inst->renamedDestIdx(i);
        if (dst->isFixedMapping() || dst->getNumPinnedWritesToComplete() != 1) {
            continue;  // 跳过架构寄存器和多写端口寄存器
        }

        // 寄存器缓存更新(整数寄存器优化)
        if (dst->is(IntRegClass)) {
            scheduler->regCache.insert(dst->flatIndex(), {});
        }

        // 查找依赖该寄存器的所有指令
        auto& depList = subDepGraph[dst->flatIndex()];
        for (auto it = depList.begin(); it != depList.end(); ) {
            int srcIdx = it->first;
            auto& consumer = it->second;

            // 检查依赖指令是否已被废弃
            if (consumer->isSquashed()) {
                it = depList.erase(it);
                continue;
            }

            // 检查该源操作数是否已经就绪
            if (consumer->readySrcIdx(srcIdx)) {
                ++it;  // 已经就绪,跳过
                continue;
            }

            // === 关键步骤:标记源操作数就绪 ===
            consumer->markSrcRegReady(srcIdx);
            DPRINTF(Schedule, "[sn:%lli] src%d woken by [sn:%lli]\n", 
                    consumer->seqNum, srcIdx, inst->seqNum);

            // 检查指令是否所有依赖都已解决
            addIfReady(consumer);

            ++it;
        }

        // 正式唤醒时清理依赖图
        if (!speculative) {
            depList.clear();
        }
    }
}

/*
 * 检查指令是否就绪并加入就绪队列
 */
void IssueQue::addIfReady(const DynInstPtr& inst) {
    if (!inst->readyToIssue()) {
        return;  // 还有未解决的依赖
    }

    // 记录就绪时间戳
    if (inst->readyTick == -1) {
        inst->readyTick = curTick();
        DPRINTF(Counters, "[sn:%lli] Ready at tick %llu\n", 
                inst->seqNum, curTick());
    }

    // 内存指令需要额外检查内存依赖
    if (inst->isMemRef() && !inst->memDepSolved()) {
        DPRINTF(Schedule, "[sn:%lli] Memory dependency not solved\n", inst->seqNum);
        return;
    }

    // 清除取消标志并加入就绪队列
    inst->clearCancel();
    if (!inst->inReadyQ()) {
        READYQ_PUSH(inst);  // 宏定义的就绪队列插入操作
        DPRINTF(Schedule, "[sn:%lli] Added to ready queue\n", inst->seqNum);
    }
}

4. Dispatch与Execute深度交互机制

4.1 调度器与执行单元的精密协调

4.1.1 指令发射到功能单元的完整流程

/*
 * 从调度器到功能单元的完整路径
 * 体现了现代处理器精确的资源管理和时序控制
 */
DynInstPtr Scheduler::getInstToFU() {
    if (instsToFu.empty()) {
        return DynInstPtr(nullptr);  // 没有待发射指令
    }

    // 从发射队列取出指令
    auto inst = instsToFu.back();
    instsToFu.pop_back();

    DPRINTF(Schedule, "[sn:%lli] Issued to FU: %s\n", 
            inst->seqNum, enums::OpClassStrings[inst->opClass()]);

    // 更新指令状态和时间戳
    inst->setIssued();
    inst->issueTick = curTick();

    // 性能跟踪点
    cpu->perfCCT->updateInstPos(inst->seqNum, PerfRecord::AtFU);

    return inst;
}

/*
 * 执行单元延迟管理:动态延迟预测
 * 某些指令的执行延迟取决于操作数值,需要动态计算
 */
uint32_t Scheduler::getOpLatency(const DynInstPtr& inst) {
    OpClass opClass = inst->opClass();

    // 浮点转换指令的特殊处理
    if (opClass == FloatCvtOp) {
        if (inst->destRegIdx(0).isFloatReg()) {
            // 整数到浮点转换需要额外延迟
            return 2 + opExecTimeTable[opClass];
        }
    }

    // 查表获取基础延迟
    uint32_t baseLatency = opExecTimeTable[opClass];

    DPRINTF(Schedule, "[sn:%lli] Op latency for %s: %d cycles\n", 
            inst->seqNum, enums::OpClassStrings[opClass], baseLatency);

    return baseLatency;
}

/*
 * 修正后的操作延迟:考虑流水线和旁路因素
 */
uint32_t Scheduler::getCorrectedOpLat(const DynInstPtr& inst) {
    uint32_t baseLatency = getOpLatency(inst);

    // 未来可以在这里添加更多修正因素:
    // - 操作数准备时间
    // - 旁路网络延迟
    // - 功能单元流水线深度

    return baseLatency;
}

4.1.2 功能单元完成事件处理

/*
 * 功能单元完成事件类:处理长延迟操作
 * 对于延迟超过1个周期的操作,使用事件驱动机制
 */
class InstructionQueue::FUCompletion : public Event {
    DynInstPtr inst;                    // 执行完成的指令 
    int fuIdx;                         // 功能单元索引
    InstructionQueue *iqPtr;           // 指令队列指针
    bool freeFU;                       // 是否释放功能单元

public:
    FUCompletion(const DynInstPtr &_inst, int fu_idx, InstructionQueue *iq_ptr)
        : Event(Stat_Event_Pri, AutoDelete), inst(_inst), fuIdx(fu_idx), 
          iqPtr(iq_ptr), freeFU(false) {}

    void process() override {
        DPRINTF(IEW, "[sn:%lli] FU completion event processed\n", inst->seqNum);

        // 处理功能单元完成
        iqPtr->processFUCompletion(inst, fuIdx);

        // 清理指令引用
        inst = nullptr;
    }

    const char* description() const override {
        return "Functional unit completion";
    }

    void setFreeFU() { freeFU = true; }
};

/*
 * 处理功能单元完成的核心逻辑
 */
void InstructionQueue::processFUCompletion(const DynInstPtr &inst, int fu_idx) {
    DPRINTF(IEW, "[sn:%lli] Processing FU completion\n", inst->seqNum);

    // 检查CPU是否处于睡眠状态
    assert(!cpu->switchedOut());

    // 减少未完成的写回操作计数
    --wbOutstanding;

    // 唤醒可能睡眠的CPU
    iewStage->wakeCPU();

    // 将指令添加到写回队列
    // 注:这些FU完成事件应该在周期开始时处理,避免时序问题
    issueToExecuteQueue->access(0)->size++;
    instsToExecute.push_back(inst);

    DPRINTF(IEW, "[sn:%lli] Added to execute queue, size=%d\n", 
            inst->seqNum, issueToExecuteQueue->access(0)->size);
}

4.2 执行延迟的精确建模

4.2.1 动态延迟计算:真实硬件的精确建模

/*
 * 执行延迟检查:基于操作数值的动态延迟预测
 * 体现了现代处理器对执行延迟的精确建模
 */
bool InstructionQueue::execLatencyCheck(const DynInstPtr& inst, uint32_t& op_latency) {
    // Leading zero count:用于除法延迟计算的辅助函数
    auto clz = [](RegVal val) -> int {
#if defined(__GNUC__) || defined(__clang__)
        return val == 0 ? 64 : __builtin_clzll(val);
#else
        // 软件实现的前导零计数
        for (int i = 0; i < 64; i++) {
            if (val & (0x1lu << 63)) return i;
            val <<= 1;
        }
        return 64;
#endif
    };

    RegVal rs1, rs2;
    int delay_;

    switch (inst->opClass()) {
    case OpClass::IntDiv: {
        // 整数除法:延迟取决于操作数的位宽差异
        rs1 = cpu->readArchIntReg(inst->srcRegIdx(0).index(), inst->threadNumber);
        rs2 = cpu->readArchIntReg(inst->srcRegIdx(1).index(), inst->threadNumber);

        // 计算前导零差异:rs1和rs2的有效位宽差异
        delay_ = std::max(clz(rs2) - clz(rs1), 0);

        if (rs2 == 1) {
            // 除以1的特殊情况:rs1 / 1 = rs1
            op_latency = 5;
        } else if (rs1 == rs2) {
            // 相等除法:rs1 / rs2 = 1 余 0
            op_latency = 7;
        } else if (clz(rs2) - clz(rs1) < 0) {
            // 被除数小于除数:rs1/rs2 = 0 余 rs1
            op_latency = 5;
        } else {
            // 一般情况:基础延迟 + 动态延迟
            op_latency = 7 + delay_ / 4;
        }

        DPRINTF(IEW, "[sn:%lli] IntDiv latency: %d (rs1=0x%llx, rs2=0x%llx)\n", 
                inst->seqNum, op_latency, rs1, rs2);
        return true;
    }

    case OpClass::FloatSqrt: {
        // 浮点平方根:延迟取决于精度和特殊值
        rs1 = cpu->readArchFloatReg(inst->srcRegIdx(0).index(), inst->threadNumber);
        rs2 = cpu->readArchFloatReg(inst->srcRegIdx(1).index(), inst->threadNumber);

        switch (inst->staticInst->operWid()) {
        case 32: {  // 单精度
            float* f1 = reinterpret_cast<float*>(&rs1);
            float* f2 = reinterpret_cast<float*>(&rs2);

            if (__isnanf(*f1) || __isnanf(*f2) || __isinff(*f1) || __isinff(*f2)) {
                // 特殊值(NaN, Inf)快速完成
                op_latency = 2;
            } else {
                // 正常单精度平方根
                op_latency = 8;
            }
            break;
        }
        case 64: {  // 双精度
            double* d1 = reinterpret_cast<double*>(&rs1);
            double* d2 = reinterpret_cast<double*>(&rs2);

            if (__isnan(*d1) || __isnan(*d2) || __isinf(*d1) || __isinf(*d2)) {
                // 特殊值快速完成
                op_latency = 2;
            } else {
                // 正常双精度平方根
                op_latency = 15;
            }
            break;
        }
        default:
            panic("Unsupported float width: %d\n", inst->staticInst->operWid());
            return false;
        }

        DPRINTF(IEW, "[sn:%lli] FloatSqrt latency: %d (%d-bit)\n", 
                inst->seqNum, op_latency, inst->staticInst->operWid());
        return true;
    }

    case OpClass::FloatDiv: {
        // 浮点除法:类似平方根的处理
        rs1 = cpu->readArchFloatReg(inst->srcRegIdx(0).index(), inst->threadNumber);
        rs2 = cpu->readArchFloatReg(inst->srcRegIdx(1).index(), inst->threadNumber);

        switch (inst->staticInst->operWid()) {
        case 32: {
            float* f1 = reinterpret_cast<float*>(&rs1);
            float* f2 = reinterpret_cast<float*>(&rs2);

            op_latency = (__isnanf(*f1) || __isnanf(*f2) || 
                         __isinff(*f1) || __isinff(*f2)) ? 2 : 7;
            break;
        }
        case 64: {
            double* d1 = reinterpret_cast<double*>(&rs1);
            double* d2 = reinterpret_cast<double*>(&rs2);

            op_latency = (__isnan(*d1) || __isnan(*d2) || 
                         __isinf(*d1) || __isinf(*d2)) ? 2 : 12;
            break;
        }
        default:
            panic("Unsupported float width: %d\n", inst->staticInst->operWid());
            return false;
        }

        DPRINTF(IEW, "[sn:%lli] FloatDiv latency: %d (%d-bit)\n", 
                inst->seqNum, op_latency, inst->staticInst->operWid());
        return true;
    }

    default:
        // 其他指令使用固定延迟
        return false;
    }
}

4.3 执行流水线的精确时序控制

4.3.1 多级发射流水线管理

/*
 * IssueQue的多级发射流水线实现
 * 支持不同深度的发射流水线,精确控制指令时序
 */
void IssueQue::scheduleInst() {
    DPRINTF(Schedule, "%s: Scheduling %d instructions\n", iqname.c_str(), selectQ.size());

    // 处理选择队列中的每条指令
    for (auto& [portId, inst] : selectQ) {
        if (inst->canceled()) {
            DPRINTF(Schedule, "[sn:%lli] Canceled during schedule\n", inst->seqNum);
            continue;
        }

        if (inst->arbFailed()) {
            // 仲裁失败,放回就绪队列重试
            DPRINTF(Schedule, "[sn:%lli] Arbitration failed, retry\n", inst->seqNum);
            iqstats->arbFailed++;

            assert(inst->readyToIssue());
            READYQ_PUSH(inst);  // 重新加入就绪队列
        } else {
            // 成功调度
            DPRINTF(Schedule, "[sn:%lli] Successfully scheduled to port %d\n", 
                    inst->seqNum, portId);

            iqstats->portissued[portId]++;
            inst->setScheduled();
            inst->issueportid = portId;

            // === 关键:加入发射流水线 ===
            toIssue->push(inst);  // 进入发射流水线的第一级

            // 端口占用管理
            if (!opPipelined[inst->opClass()]) {
                // 非流水化操作:完全占用端口
                portBusy[portId] = -1ll;  // 全部位设置为1
            } else if (scheduler->getCorrectedOpLat(inst) > 1) {
                // 流水化操作:按延迟设置占用位
                uint32_t latency = scheduler->getCorrectedOpLat(inst);
                portBusy[portId] |= (1ll << latency);
            }

            // === 启动推测唤醒 ===
            scheduler->specWakeUpDependents(inst, this);

            // 性能跟踪
            cpu->perfCCT->updateInstPos(inst->seqNum, PerfRecord::AtIssueArb);
        }

        // 清理仲裁失败标志
        inst->clearArbFailed();
    }
}

/*
 * 发射流水线推进:从选择到功能单元的多级缓冲
 */
void IssueQue::tick() {
    // 更新统计信息
    iqstats->avgInsts = instNum;

    if (instNumInsert > 0) {
        iqstats->insertDist[instNumInsert]++;
        instNumInsert = 0;
    }

    // === 核心:调度当前周期选中的指令 ===
    scheduleInst();

    // === 推进发射流水线 ===
    inflightIssues.advance();  // 所有级都向前推进一级

    // === 更新端口占用状态 ===
    for (auto& busyMask : portBusy) {
        busyMask = busyMask >> 1;  // 所有占用位右移一位
    }

    DPRINTF(Schedule, "%s tick complete, %d insts in flight\n", 
            iqname.c_str(), getTotalInflightInsts());
}

/*
 * 从发射流水线末端发射到功能单元
 */
void IssueQue::issueToFu() {
    int size = toFu->size;  // 本周期可发射的指令数
    int replayed = 0;       // 重放指令计数
    int issued = 0;         // 总发射指令计数

    int issuedLoad = 0;     // 发射的Load指令数
    int issuedStore = 0;    // 发射的Store指令数

    // === 第一优先级:处理重放队列 ===
    // 重放指令通常是因为缓存缺失等原因需要重试的内存指令
    while (!replayQ.empty() && replayed < outports) {
        auto& inst = replayQ.front();

        // 检查Load/Store流水线容量限制
        if (inst->isLoad() && issuedLoad >= numLoadPipe) break;
        if (inst->isStore() && issuedStore >= numStorePipe) break;

        // 直接发射到功能单元,无需再次检查记分板
        scheduler->addToFU(inst);
        DPRINTF(Schedule, "[sn:%lli] Replayed to FU\n", inst->seqNum);

        replayQ.pop();
        issued++;
        replayed++;

        if (inst->isLoad()) issuedLoad++;
        if (inst->isStore()) issuedStore++;
    }

    // === 第二优先级:处理发射流水线输出 ===
    for (int i = 0; i < size; i++) {
        auto inst = toFu->pop();
        if (!inst) continue;

        // 检查发射端口和流水线容量
        if ((i + replayed >= outports) ||
            (inst->isLoad() && issuedLoad >= numLoadPipe) ||
            (inst->isStore() && issuedStore >= numStorePipe)) {

            // 容量不足,将指令放回就绪队列
            inst->clearScheduled();
            READYQ_PUSH(inst);

            DPRINTF(Schedule, "[sn:%lli] Issue port/pipe occupied, retry\n", inst->seqNum);
            iqstats->issueOccupy++;
            continue;
        }

        // === 关键检查:记分板状态验证 ===
        if (!checkScoreboard(inst)) {
            // 记分板检查失败(通常是推测唤醒错误)
            DPRINTF(Schedule, "[sn:%lli] Scoreboard check failed\n", inst->seqNum);
            continue;  // 指令被取消,不计入发射统计
        }

        // 成功发射
        if (inst->isLoad()) issuedLoad++;
        if (inst->isStore()) issuedStore++;

        addToFu(inst);  // 实际发射到功能单元
        cpu->perfCCT->updateInstPos(inst->seqNum, PerfRecord::AtIssueReadReg);
        issued++;
    }

    // 更新统计信息
    if (issued > 0) {
        iqstats->issueDist[issued]++;
    }
    if (replayed > 0) {
        iqstats->issueOccupy += replayed;  // 记录重放开销
    }

    DPRINTF(Schedule, "%s issued %d insts (%d replayed) this cycle\n", 
            iqname.c_str(), issued, replayed);
}

4.4 记分板验证与推测恢复

4.4.1 精确的记分板检查机制

/*
 * 记分板检查:验证推测唤醒的正确性
 * 这是防止错误推测导致程序错误的关键机制
 */
bool IssueQue::checkScoreboard(const DynInstPtr& inst) {
    DPRINTF(Schedule, "[sn:%lli] Checking scoreboard\n", inst->seqNum);

    for (int i = 0; i < inst->numSrcRegs(); i++) {
        auto src = inst->renamedSrcIdx(i);

        if (src->isFixedMapping()) {
            // 架构寄存器总是可用
            continue;
        }

        // === 关键检查:旁路数据可用性 ===
        if (!scheduler->bypassScoreboard[src->flatIndex()]) {
            // 旁路数据不可用,需要找到生产者指令
            auto dst_inst = scheduler->getInstByDstReg(src->flatIndex());
            // 如果dst_inst 是null,或者不是load,则说明这个指令的源操作数没有被任何指令生产,panic
            if (!dst_inst || !dst_inst->isLoad()) {
                panic("Invalid dependency: expected load producer for p%d", 
                      src->flatIndex());
            }

            DPRINTF(Schedule, "[sn:%lli] Cannot get bypass data from [sn:%lli], canceling\n", 
                    inst->seqNum, dst_inst->seqNum);

            // === 推测错误处理:Load取消传播 ===
            scheduler->loadCancel(dst_inst);
            return false;  // 检查失败
        }
    }

    DPRINTF(Schedule, "[sn:%lli] Scoreboard check passed\n", inst->seqNum);
    return true;  // 所有源操作数都可用
}

/*
 * Load取消传播:处理推测错误的级联影响
 * 当Load指令缓存缺失时,需要取消所有推测唤醒的依赖指令
 */
void Scheduler::loadCancel(const DynInstPtr& inst) {
    DPRINTF(Schedule, "[sn:%lli] Load miss, starting cancel propagation\n", inst->seqNum);

    if (inst->issueQue) {
        inst->issueQue->iqstats->loadmiss++;
    }

    // 使用DFS遍历依赖链,取消所有受影响的指令
    std::stack<DynInstPtr> cancelStack;
    cancelStack.push(inst);

    while (!cancelStack.empty()) {
        auto cancelInst = cancelStack.top();
        cancelStack.pop();

        // === 清理推测唤醒事件 ===
        auto eventIt = specWakeEvents.find(cancelInst->seqNum);
        if (eventIt != specWakeEvents.end()) {
            for (auto* event : eventIt->second) {
                cpu->deschedule(event);  // 取消调度的事件
                delete event;            // 释放事件对象
            }
            specWakeEvents.erase(eventIt);

            DPRINTF(Schedule, "[sn:%lli] Cancelled %d spec wakeup events\n", 
                    cancelInst->seqNum, eventIt->second.size());
        }

        // === 更新记分板状态 ===
        for (int i = 0; i < cancelInst->numDestRegs(); i++) {
            auto dst = cancelInst->renamedDestIdx(i);
            if (dst->isFixedMapping()) continue;

            // 清除早期记分板标记
            earlyScoreboard[dst->flatIndex()] = false;

            // === 递归取消依赖者 ===
            for (auto iq : issueQues) {
                auto& depList = iq->subDepGraph[dst->flatIndex()];

                for (auto& [srcIdx, depInst] : depList) {
                    if (depInst->readySrcIdx(srcIdx)) {
                        // 该依赖指令被错误唤醒,需要取消
                        DPRINTF(Schedule, "[sn:%lli] Canceling dependent inst [sn:%lli]\n", 
                                cancelInst->seqNum, depInst->seqNum);

                        depInst->issueQue->cancel(depInst);  // 取消指令
                        depInst->clearSrcRegReady(srcIdx);   // 清除就绪标记
                        cancelStack.push(depInst);           // 递归取消
                    }
                }
            }
        }
    }

    // === 清理流水线中的取消指令 ===
    // 从发射流水线中移除已取消的指令
    for (auto iq : issueQues) {
        for (int i = 0; i <= iq->getIssueStages(); i++) {
            auto& stage = iq->inflightIssues[-i];

            for (int j = 0; j < stage.size; j++) {
                if (stage.insts[j] && stage.insts[j]->canceled()) {
                    DPRINTF(Schedule, "[sn:%lli] Removed from issue pipeline stage %d\n", 
                            stage.insts[j]->seqNum, i);
                    stage.insts[j] = nullptr;
                }
            }
        }
    }
}

5. 配置和参数化

5.1 调度器配置 (configs/common/FUScheduler.py)

支持多种调度器配置: - ECoreScheduler: 基础E-Core配置 - KunminghuScheduler: 昆明湖V3配置 - KMHV3Scheduler: V3优化配置 - IdealScheduler: 理想化配置

5.2 寄存器文件端口配置

通过 rp 参数配置寄存器读端口:

# 整数寄存器读端口: [type_id(2bit)] [port_id(4bit)] [priority(2bit)]
def IntRD(id, p):
    return (0 << 6) | (id << 2) | (p)

# 浮点寄存器读端口
def FpRD(id, p):
    return (1 << 6) | (id << 2) | (p)

6. 关键代码位置

6.1 主要文件

  • src/cpu/o3/issue_queue.hh/cc: 分布式发射队列实现
  • src/cpu/o3/inst_queue.hh/cc: 兼容性包装器
  • configs/common/FUScheduler.py: 调度器配置

6.2 重要函数

  • Scheduler::insert(): 指令分发入口
  • IssueQue::selectInst(): 指令选择逻辑
  • Scheduler::specWakeUpDependents(): 投机唤醒机制
  • IssueQue::checkScoreboard(): 记分板检查
  • Scheduler::lookahead(): 分发预测

6.3 数据流关键点

  • dispTable[opClass]: OpClass到IssueQue的映射
  • wakeMatrix[srcIQ][dstIQ]: 唤醒通道配置
  • subDepGraph[regIdx]: 寄存器依赖关系
  • portBusy[]: 端口占用管理

7. 调试和统计

7.1 调试标志

  • Debug::Schedule: 调度相关调试信息
  • Debug::Dispatch: 分发阶段调试信息

7.2 性能统计

  • iqstats->issueDist: 发射指令分布
  • iqstats->portBusy: 端口忙碌统计
  • stats.exec_stall_cycle: 执行停顿周期
  • stats.memstall_*: 内存停顿统计