Arm Fast Models的调度器架构是虚拟原型开发中的核心引擎,其设计充分考虑了多核系统仿真中的时序精确性和执行效率问题。整个调度系统采用分层设计,主要包含三个关键层次:
仿真控制层(Simulation Control):通过scx_simcontrol_if接口实现仿真状态机的全局控制,包括运行(run)、停止(stop)、关闭(shutdown)等基础操作。这一层直接响应调试器的IRIS协议请求,是调试接口的入口点。
调度执行层(Scheduler Core):SchedulerInterfaceForComponents类提供的线程管理、定时器、同步信号等核心功能。这一层实现了时间量子化(Quantum)管理,确保各线程在合理的同步点进行上下文切换。
时钟树管理层(Clock Tree):FrequencySource和FrequencyObserver构成的观察者模式,实现动态频率调整的传播机制。这对于模拟DVFS(动态电压频率调整)等现代芯片特性至关重要。
关键设计原则:调度器API严格遵循"接口与实现分离"的原则,所有关键功能都通过抽象基类暴露。这种设计使得同一套模型可以运行在不同的仿真环境中(SystemC、独立仿真器等),只需替换底层实现即可。
调度器与外围系统的交互主要通过三个关键接口实现:
scx_simcallback_if:仿真引擎向调试器通知状态变化的回调接口。当仿真状态改变时(如运行→停止),通过该接口的notify_running()、notify_stopped()等方法通知所有注册的客户端。
scx_simcontrol_if:调试器控制仿真引擎的主接口。提供获取调度器实例(get_scheduler)、启停控制(run/stop)、回调管理(add_callback)等功能。其实现在$MAXCORE_HOME/lib/template/tpl_scx_simcontroller.{h,cpp}中。
SchedulerInterfaceForComponents:模型组件使用的调度服务接口。包含线程创建(createThread)、定时器管理(createTimer)、同步等待(wait)等核心功能。
典型交互流程示例:
cpp复制// 获取全局接口
eslapi::CAInterface* global_if = scx_get_global_interface();
// 获取调度器实例
sg::SchedulerInterfaceForComponents* scheduler =
sg::obtainComponentInterfacePointer<sg::SchedulerInterfaceForComponents>(
global_if, "scheduler");
// 创建并启动线程
sg::SchedulerThread* thread = scheduler->createThread("worker", &runnable);
thread->start();
Fast Models调度器API设计时充分考虑了与SystemC/TLM的兼容性,其核心功能在SystemC环境下有明确的对应实现:
| Fast Models API | SystemC等效实现 | 注意事项 |
|---|---|---|
| wait(ticks) | sc_core::wait(sc_time) | 需在等待前处理所有pending的异步回调 |
| wait(ThreadSignal*) | sc_core::wait(sc_event) | 需确保ThreadSignal生命周期管理安全 |
| getCurrentSimulatedTime() | sc_core::sc_time_stamp() | 返回值的精度取决于SystemC的时间分辨率设置 |
| addCallback() | 自定义事件队列 | SystemC原生不支持外部线程安全的事件触发,需通过中间层缓冲实现 |
关键集成难点在于处理SystemC的非线程安全特性。当外部线程(如调试器线程)调用addCallback()时,需要通过特定的同步机制将事件传递到SystemC的仿真线程中。参考实现通常采用双缓冲队列加notify机制:
cpp复制// 异步回调处理示例
void SystemCAdapter::addCallback(SchedulerCallback* cb) {
// 加锁保护(外部线程可能调用)
std::lock_guard<std::mutex> lock(m_queueMutex);
m_pendingCallbacks.push_back(cb);
// 通过SystemC事件通知仿真线程
m_triggerEvent.notify();
}
// SystemC线程处理回调
void SystemCAdapter::processCallbacks() {
while(true) {
sc_core::wait(m_triggerEvent); // 等待事件触发
std::vector<SchedulerCallback*> tmp;
{
std::lock_guard<std::mutex> lock(m_queueMutex);
tmp.swap(m_pendingCallbacks); // 快速交换减少锁时间
}
for(auto* cb : tmp) {
cb->schedulerCallback(); // 执行回调
}
}
}
Fast Models调度器采用量子化时间推进(Quantized Time Advance)机制来平衡仿真精度和性能。其核心控制参数包括:
全局量子(Global Quantum):
cpp复制// 设置全局量子为100ns(基于1GHz时间基准)
scheduler->setGlobalQuantum(100, timebase_1GHz);
该值决定了线程允许运行的最大时间片,所有线程必须在此时间间隔内进行同步。
最小同步延迟(Min Sync Latency):
cpp复制// 设置最小同步延迟为10ns
scheduler->setMinSyncLatency(10, timebase_1GHz);
这个参数影响调度器选择同步点的策略,较小的值提高精度但增加同步开销。
同步点预测:
通过addSynchronisationPoint()添加未来的同步点提示,帮助调度器优化线程调度:
cpp复制// 预测500ns后需要同步
scheduler->addSynchronisationPoint(500, timebase_1GHz);
性能调优经验:
Fast Models调度器中的线程通过SchedulerThread和SchedulerRunnable两个抽象类实现:
cpp复制class MyRunnable : public sg::SchedulerRunnable {
public:
void threadProc() override {
while(!m_stopRequested) {
// 执行模型逻辑...
scheduler->wait(ticks); // 主动让出时间片
}
}
// ...其他接口实现...
};
// 创建线程
sg::SchedulerRunnable* runnable = new MyRunnable();
sg::SchedulerThread* thread = scheduler->createThread("model_thread", runnable);
thread->start();
关键注意事项:
调度器提供三种同步原语:
ThreadSignal(线程信号):
cpp复制// 创建信号
sg::ThreadSignal* signal = scheduler->createThreadSignal("irq_trigger");
// 线程A等待信号
scheduler->wait(signal);
// 线程B触发信号
signal->notify();
定时器回调:
cpp复制class MyTimerCallback : public sg::TimerCallback {
void timerCallback() override {
// 定时处理逻辑
}
};
MyTimerCallback cb;
sg::Timer* timer = scheduler->createTimer("periodic_timer", &cb);
timer->set(100, true); // 100ticks周期定时器
异步回调:
cpp复制class MyAsyncCallback : public sg::SchedulerCallback {
void schedulerCallback() override {
// 在仿真线程中异步执行
}
};
MyAsyncCallback cb;
scheduler->addCallback(&cb); // 线程安全调用
调试技巧:
Fast Models通过FrequencySource和FrequencyObserver实现动态频率调整:
cpp复制class ClockConsumer : public sg::FrequencyObserver {
void notifyFrequencyChanged(FrequencySource* src) override {
double newFreq = src->getFrequency();
// 更新内部时钟逻辑...
}
};
// 注册频率观察者
FrequencySource* clockSource = getClockSource();
ClockConsumer consumer;
clockSource->registerFrequencyObserver(&consumer);
// 改变频率(触发通知)
dynamic_cast<GlobalFrequencySource*>(clockSource)->setFrequency(1.5e9);
设计要点:
调度器的时间分辨率通过setSimulatedTimeResolution()设置:
cpp复制// 设置1ps的时间分辨率
scheduler->setSimulatedTimeResolution(1e-12);
重要限制:
最佳实践:
调度器与调试器的交互主要通过scx_simcontrol_if和scx_simcallback_if完成:
cpp复制class DebuggerHook : public scx::scx_simcallback_if {
void notify_stopped() override {
// 仿真停止时更新调试器状态
updateDebuggerUI();
}
// ...其他通知方法...
};
// 注册调试钩子
DebuggerHook hook;
simControl->add_callback(&hook);
典型工作流程:
调度器级优化:
cpp复制// 示例:调整调度参数
scheduler->setGlobalQuantum(1000); // 1us量子
scheduler->setMinSyncLatency(100); // 100ns最小同步间隔
模型级优化技巧:
性能分析工具链:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 仿真挂起 | 线程未调用wait() | 检查所有threadProc()是否包含定期wait() |
| 时间不同步 | GlobalQuantum设置过大 | 减小setGlobalQuantum()值,或增加addSynchronisationPoint()提示 |
| 频率更新未生效 | 观察者未正确注册 | 检查registerFrequencyObserver()调用链 |
| 调试器连接失败 | 回调接口未注册 | 确保实现了scx_simcallback_if并调用add_callback() |
| 性能突然下降 | 微小时间量子导致频繁同步 | 检查模型是否设置了不合理的微小时间请求 |
cpp复制// 在自定义调度器中添加日志
void MyScheduler::wait(ticks_t ticks) {
log("Thread %s waits for %lld ticks", currentThread()->name(), ticks);
// ...原始实现...
}
cpp复制// 在关键组件中添加时间断言
void checkTiming() {
double currentTime = scheduler->getCurrentSimulatedTime();
assert(currentTime >= m_lastUpdateTime);
m_lastUpdateTime = currentTime;
}
对于复杂系统调试,建议采用分阶段验证: