在本文中,我将探讨在 gem5 模拟器中如何创建、调度事件,并深入理解背后的原理。
Event
gem5 是一个事件驱动(Event-driven)的模拟器。在事件驱动模型中,每个事件(Event)都有一个回调函数用于处理事件。
官方教程是基于HelloObject开始,创建和调度事件。下面我们也以 HelloObject 类代码为例。
1. 创建简单的事件回调
- 在 HelloObject.hh 头文件中,需要声明一个新的函数
processEvent()
,每次事件触发时都会执行该函数(此函数必须不带参数并且不返回任何内容)。 - 添加一个 Event 实例,使用
EventFunctionWrapper
,允许执行任何函数。 - 最后,添加了一个
startup()
函数,其中使用 schedule() 函数开始调度事件,即让该事件安排在未来的某个时刻被触发(事件驱动的模拟不允许事件在过去执行)。
class HelloObject : public SimObject {
private:
void processEvent();
EventFunctionWrapper event;
public:
HelloObject(const HelloObjectParams &p);
void startup() override;
};
- 在 HelloObject 构造函数中构造这个事件。
EventFunctionWrapper
接受两个参数:要执行的函数(function to execute)和一个名称(name)。
- 第一个参数是一个没有参数和返回值的函数(std::function<void(void)>),通常情况是一个简单的lambda函数,调用一个成员函数。例如:下面这段代码主要是通过调用SimObject的构造函数实现了HelloObject 的构造函数。
- 第二个参数name通常是拥有该事件的 SimObject 的名称,当打印名称时,名称的末尾会自动添加 ".wrapped_function_event"。【主要是用来标记事件所属的SimObject】例如:有一个名为 "ExampleObject" 的 SimObject,并在其中创建了一个名为 "processEvent" 的事件,那么事件的完整名称将是 "ExampleObject.processEvent.wrapped_function_event"。
// HelloObject::HelloObject(const HelloObjectParams ¶ms):这是HelloObject类的构造函数的定义,它接受一个类型为HelloObjectParams的参数引用。
// SimObject(params):在构造函数的成员初始化列表中,调用了基类SimObject的构造函数,传递了params参数。这表明HelloObject类是SimObject类的派生类,且通过构造函数参数初始化了基类。
// event([this]{processEvent();}, name()):这是另一个成员初始化,初始化了event成员变量。event是gem5中的一个事件对象,其构造函数接受两个参数:一个Lambda表达式和一个字符串。
HelloObject::HelloObject(const HelloObjectParams & params):
SimObject(params), event([this]{processEvent();}, name())
{
DPRINTF(HelloExample, "Created the hello object\\n");
}
- 定义
processEvent
函数的实现。
这段代码会简单打印一段文字。同时说明processEvent
函数是HelloObject
类的成员函数,用于处理事件的逻辑。
void
HelloObject::processEvent()
{
DPRINTF(HelloExample, "Hello world! Processing the event!\\n");
}
2. event调度事件
使用:cppschedule 函数完成事件执行时间的调度。
事件驱动模型不允许事件在过去执行,所以:cppschedule 函数可以在未来的某个时间安排一个Event实例完成执行。
- 在 HelloObject 类中的添加
startup()
函数中进行事件的初始调度,startup()
函数用在于SimObject
内部安排事件。它在模拟开始之前不会执行(即从 Python 配置文件中调用 simulate() 函数时)。
# 在 startup() 函数中使用 schedule 函数来安排事件的触发时间。
# 参数包括要调度的事件(event)和触发时间(当前时钟周期 curTick() 加上 100 个周期)
# 事件将在指定的时间点执行 processEvent() 函数中定义的逻辑。
void HelloObject::startup()
{
schedule(event, 100);
}
在上面的代码中,事件会在第 100 个 tick 时被触发。通常,需使用 curTick()
加时间偏移来确定事件触发的时间。但在这一简单示例中,开始模拟的函数(即 Python 配置文件中调用 simulate() 函数) 是以 tick = 0 的原点开始执行的,因此这里的 startup()
可以显式地标明要调度的时间点。
当运行 gem5 模拟器后,会得到如下输出,具体如何运行不是本博客的重点,感兴趣的读者可参考官方文档运行。从下面的输出信息可知,我们实现了在 100 tick 时执行 processEvent()
函数。
gem5 Simulator System. http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 compiled Jan 4 2017 11:01:46
gem5 started Jan 4 2017 13:41:38
gem5 executing on chinook, pid 1834
command line: build/X86/gem5.opt --debug-flags=Hello configs/learning_gem5/part2/run_hello.py
Global frequency set at 1000000000000 ticks per second
0: hello: Created the hello object
Beginning simulation!
info: Entering event queue @ 0. Starting simulation...
100: hello: Hello world! Processing the event!
Exiting @ tick 18446744073709551615 because simulate() limit reached
3. More event scheduling
为 HelloObject 添加一个延迟参数和一个控制触发事件次数的参数。
- 在 HelloObject 类的声明中,添加一个成员变量用于存储延迟和触发事件次数。
class HelloObject : public SimObject
{
private:
void processEvent();
EventFunctionWrapper event;
const Tick latency;
int timeLeft;
public:
HelloObject(const HelloObjectParams &p);
void startup() override;
};
- 在构造函数中为延迟(latency)和生育触发次数(timesLeft)添加默认值。
HelloObject::HelloObject(const HelloObjectParams ¶ms) :
SimObjecct(params), event([this{processEvent();}, name()]),
latency(100), timeLeft(10)
{
DPRINTF(HelloExample, "Created the hello object\\n");
}
- 更新
startup()
和processEvent()
。
void HelloObject::starup()
{
schedule(event, latency);
}
void HelloObject::processEvent()
{
timesLeft--;
DPRINTF(HelloExample, "Hello world! Processing the event! %d left\\n", timesLeft);
if (timesLeft <= 0) {
DPRINTF(HelloExample, "Done firing!\\n");
} else {
schedule(event, curTick() + latency);
}
}
上面代码中,在 timeleft 还未小于 0 时,processEvent()
函数每次打印出信息后,都会再次调用 schedule()
函数增加一个事件。注意,此处使用的 schedule()
函数 就利用了 curTick()
生成时间偏移量,进而指定了事件被调度的时间。
4. 测试代码
创建一个HelloObject类
1. hello_object.hh
2. hello_object.cc
3. HelloObject.py
4. SConscript
5. run_hello.py
输出结果
接下来,我会带领大家深入分析以下 gem5 的事件驱动原理,以及其代码实现细节。
标签:函数,gem5,HelloObject,event,学习,processEvent,事件,Event From: https://www.cnblogs.com/sys-123456/p/18523667