目录
官网教程:gem5: Event-driven programming
官方教程是基于HelloObject开始,创建和调度事件。
一、官网教程
1、创建简单的事件回调
gem5中的事件驱动模型中,每个事件都有一个回调函数,用于处理时间。
- 通常都是从:cppEvent继承的类。
- gem5中提供了一个用于创建简单事件的包装函数。
(1)在HelloObject的.hh头文件中,需要声明一个新的函数processEvent(),每次事件触发时都会执行该函数。【该函数不带有参数,不返回任何内容】
(2)增加一个Event实例。使用EventFunctionWrapper,允许执行任何函数。
(3)添加了一个startup() 函数
class HelloObject : public SimObject
{
private:
void processEvent();
EventFunctionWrapper event;
public:
HelloObject(const HelloObjectParams &p);
void startup() override;
};
(4)在HelloObject构造函数中构造这个事件。EventFunctionWrapper 接受两个参数:要执行的函数(function to execute)和一个名称(name)。
-
第一个参数是一个没有参数和返回值的函数(std::function<void(void)>),通常情况是一个简单的lambda函数,调用一个成员函数。
例如:下面这段代码主要是通过调用SimObject的构造函数实现了HelloObject 的构造函数。
HelloObject::HelloObject(const HelloObjectParams ¶ms) : SimObject(params), event([this]{processEvent();}, name()) { DPRINTF(HelloExample, "Created the hello object\\n"); } //HelloObject::HelloObject(const HelloObjectParams ¶ms):这是HelloObject类的构造函数的定义,它接受一个类型为HelloObjectParams的参数引用。 //SimObject(params):在构造函数的成员初始化列表中,调用了基类SimObject的构造函数,传递了params参数。这表明HelloObject类是SimObject类的派生类,且通过构造函数参数初始化了基类。 //event([this]{processEvent();}, name()):这是另一个成员初始化,初始化了event成员变量。event是gem5中的一个事件对象,其构造函数接受两个参数:一个Lambda表达式和一个字符串。 //DPRINTF(HelloExample, "Created the hello object\\n");:这是gem5中的调试输出宏,用于打印调试信息。
-
第二个参数name通常是拥有该事件的 SimObject 的名称,当打印名称时,名称的末尾会自动添加 ".wrapped_function_event"。【主要是用来标记事件所属的SimObject】
例如:有一个名为 "ExampleObject" 的 SimObject,并在其中创建了一个名为 "processEvent" 的事件,那么事件的完整名称将是 "ExampleObject.processEvent.wrapped_function_event"。
(5)定义 processEvent 函数的实现。
void
HelloObject::processEvent()
{
DPRINTF(HelloExample, "Hello world! Processing the event!\\n");
}
//使用了条件编译指令 #ifdef DEBUG
//这表示代码块只有在定义了 DEBUG 宏的情况下才会被编译
这段代码会简单打印一段文字。同时说明processEvent 函数是 HelloObject 类的成员函数,用于处理事件的逻辑。
2、调度事件
使用:cppschedule 函数完成事件执行时间的调度。
事件驱动模型不允许事件在过去执行,所以:cppschedule 函数可以在未来的某个时间安排一个Event实例完成执行。
(1)在HelloObject 类中的添加startup() 函数中进行事件的初始调度。startup() 函数用于在 SimObject 内部安排事件。它在模拟开始之前不会执行(即从 Python 配置文件中调用 simulate() 函数时)。
void HelloObject::startup()
{
schedule(event, 100);
}
#在 startup() 函数中使用 schedule 函数来安排事件的触发时间。
#参数包括要调度的事件(event)和触发时间(当前时钟周期 curTick() 加上 100 个周期)
#事件将在指定的时间点执行 processEvent() 函数中定义的逻辑。
(2)schedule()的第2个参数为未来时刻的偏移量。即事件的触发时间为“当前时钟周期 curTick() 加上 100 个周期”。
3、More event scheduling
为 HelloObject 添加一个延迟参数和一个控制触发事件次数的参数。
(1)在 HelloObject 类的声明中,添加一个成员变量用于存储延迟和触发事件次数。
class HelloObject : public SimObject
{
private:
void processEvent();
EventFunctionWrapper event;
const Tick latency;
int timesLeft;
public:
HelloObject(const HelloObjectParams &p);
void startup() override;
};
(2)在构造函数中为延迟(latency)和剩余触发次数(timesLeft)添加默认值。
HelloObject::HelloObject(const HelloObjectParams ¶ms) :
SimObject(params), event([this]{processEvent();}, name()),
latency(100), timesLeft(10)
{
DPRINTF(HelloExample, "Created the hello object\\n");
}
(3)更新startup()
and processEvent()
。
void HelloObject::startup()
{
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);
}
}
此时运行gem5时,事件会触发10次,模拟将在1000个时钟周期后结束。
二、我的测试代码
这个阶段中我使用的是自建的新类ZylObject,如果大家想重新命名可以直接按照这个思路新建属于自己的简单类。
1、ZylObject.py
from m5.params import *
from m5.SimObject import SimObject
class ZylObject(SimObject):
type = 'ZylObject'
cxx_header = "dsic/zyl_object.hh"
cxx_class = "gem5::ZylObject"
2、zyl_object.hh
#ifndef __DSIC_ZYL_OBJECT_HH__
#define __DSIC_ZYL_OBJECT_HH__
#include "params/ZylObject.hh"
#include "sim/sim_object.hh"
namespace gem5
{
class ZylObject : public SimObject
{
private:
void processEvent();
EventFunctionWrapper event;
const Tick latency;
int timesLeft;
public:
ZylObject(const ZylObjectParams &p);
void startup() override;
};
} // namespace gem5
#endif // __DSIC_ZYL_OBJECT_HH__
3、zyl_object.cc
#include "dsic/zyl_object.hh"
#include "debug/ZylObject.hh"
#include <iostream>
namespace gem5
{
ZylObject::ZylObject(const ZylObjectParams ¶ms) :
SimObject(params), event([this]{processEvent();},name()),
latency(100), timesLeft(10)
{
std::cout << "function[1]: Hello World! From a ylZha's SimObject!" << std::endl;
DPRINTF(ZylObject, "function[2]: Created the test object\\n");
{
DPRINTF(ZylObject, "function[2]: text object\\n");
}
}
void
ZylObject::processEvent()
{
timesLeft--;
DPRINTF(ZylObject, "Hello world! step[%d] Processing the event! %d left\\n", timesLeft,timesLeft);
if (timesLeft <= 0) {
DPRINTF(ZylObject, "Finish! Done firing!\\n");
} else {
schedule(event, curTick() + latency);
}
}
void
ZylObject::startup()
{
schedule(event, latency);
}
} // namespace gem5
4、SConscript
Import('*')
SimObject('FzzObject.py', sim_objects=['FzzObject'])
SimObject('RouterObject.py', sim_objects=['RouterObject'])
SimObject('FucoreObject.py', sim_objects=['FucoreObject'])
SimObject('ZylObject.py', sim_objects=['ZylObject'])
Source('fzz_object.cc')
Source('router_object.cc')
Source('fucore_object.cc')
Source('zyl_object.cc')
DebugFlag('FzzObject')
DebugFlag('RouterObject')
DebugFlag('FucoreObject')
DebugFlag('ZylObject')
5、run_zyl.py
import m5
from m5.objects import *
root = Root(full_system = False)
root.zyl = ZylObject()
m5.instantiate()
print("Beginning zylobject simulation!")
exit_event = m5.simulate()
print('Exiting @ tick {} because {}'
.format(m5.curTick(), exit_event.getCause()))