上篇文章《GEM5入门学习1》介绍了如何使用GEM5来仿真,本文讲介绍如何在GEM5中添加一个自己的模块,并且调试GEM5。
1 添加一个简单模块
添加一个简单的模块需要添加如下几个文件(新添加的demo位于src/learning_gem5/part2目录下):
-
HelloObject.py:Python的模块接口文件,用于导出接口给仿真脚本使用。
-
hello_object.hh:自己实现的hello_object模块的头文件。
-
hello_object.cc:自己实现的hello_object模块的函数定义。
-
SConscript:编译脚本,在gem5编译系统中添加进去自己的模块。
先添加一个Python的文件,用于将C++模块导出,在python文件中指明新添加对象的名称以及对象的文件位置:
# file: src/learning_gem5/part2/HelloObject.py
from m5.params import *
from m5.SimObject import SimObject
class HelloObject(SimObject):
type = 'HelloObject'
cxx_header = "learning_gem5/part2/hello_object.hh"
cxx_class = "gem5::HelloObject"
其次要实现hello_object.hh文件:
// file: src/learning_gem5/part2/hello_object.hh
#ifndef __LEARNING_GEM5_HELLO_OBJECT_HH__
#define __LEARNING_GEM5_HELLO_OBJECT_HH__
#include "params/HelloObject.hh"
#include "sim/sim_object.hh"
namespace gem5
{
class HelloObject : public SimObject
{
public:
HelloObject(const HelloObjectParams &p);
};
} // namespace gem5
#endif // __LEARNING_GEM5_HELLO_OBJECT_HH__
以及模块的实现hello_object.cc文件:
// file src/learning_gem5/part2/hello_object.cc
#include "learning_gem5/part2/hello_object.hh"
#include <iostream>
namespace gem5
{
HelloObject::HelloObject(const HelloObjectParams ¶ms) : SimObject(params)
{
std::cout << "Hello World! From a SimObject!" << std::endl;
}
} // namespace gem5
添加构建脚本:
# file: src/learning_gem5/part2/SConscript
Import('*')
SimObject('HelloObject.py', sim_objects=['HelloObject'])
Source('hello_object.cc')
上面四个文件时添加一个模块必要的文件,完成之后就可以重新构建gem5,新添加的文件就会被编译到gem的二进制文件中去
scons build/X86/gem5.opt
重新编译之后就可以写仿真脚本来运行我们自己写的模块了:
# file: configs/tutorial/part2/run_hello.py
import m5
from m5.objects import *
# 由于我们写了一个简单的验证模块,所以系统配置时不用指定CPU、内存、Cache等其他模块,单独的helloObject模块就可运行
root = Root(full_system = False)
root.hello = HelloObject()
m5.instantiate()
print("Beginning simulation!")
exit_event = m5.simulate()
print('Exiting @ tick {} because {}'.format(m5.curTick(), exit_event.getCause()))
运行:
./build/X86/gem5.opt configs/tutorial/part2/run_hello.py
运行结果显示为:
qihangkong@ubuntu:~/git/gem5$ ./build/X86/gem5.opt ./configs/tutorial/part2/run_hello.py
gem5 Simulator System. https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 version 22.1.0.0
gem5 compiled Mar 6 2023 14:24:51
gem5 started Mar 6 2023 14:35:10
gem5 executing on ubuntu, pid 48426
command line: ./build/X86/gem5.opt './configs/tutorial/part2/run_hello.py'
Global frequency set at 1000000000000 ticks per second
Hello World! From a SimObject!
Beginning simulation!
build/X86/sim/simulate.cc:192: info: Entering event queue @ 0. Starting simulation...
Exiting @ tick 18446744073709551615 because simulate() limit reached
2 添加调试接口
GEM5中包含一套调试信息的实现接口,其支持自定义的log信息打印,可以在运行时指定 --debug-flags=XXX 来指定输出log的信息:
如输出DRAM的日志信息可以使用:
build/X86/gem5.opt --debug-flags=DRAM configs/learning_gem5/part1/simple.py | head -n 50
输出执行过程的日志信息可以使用:
build/X86/gem5.opt --debug-flags=Exec configs/learning_gem5/part1/simple.py | head -n 50
可以通过 --debug-help 来获取所有的debug信息:
build/X86/gem5.opt --debug-help
接下来我们在新添加的模块里面添加一个新的调试标签,就可以替换print输出。
实现需要先在前面创建的SConstruct文件添加申明一个Debug标签:
# file: src/learning_gem5/part2/SConstruct
Import("*")
DebugFlag("HelloExample") # 这个声明必须在Import("*")后面
在SConstruct中声明了标签之后,构建系统将会自动生成一个 debug/HelloExample.hh 的头文件,在使用的时候直接包含这个头文件,就可以使用DPRINT宏来打印日志:
// file: src/learning_gem5/part2/hello_object.cc
#include "base/trace.hh"
#include "debug/HelloExample.hh"
#include "learning_gem5/part2/hello_object.hh"
#include <iostream>
namespace gem5
{
HelloObject::HelloObject(const HelloObjectParams ¶ms) : SimObject(params)
{
DPRINTF(HelloExample, "Created the hello object\n");
}
} // namespace gem5
编译后运行命令:
./build/X86/gem5.opt --debug-flags=HelloExample configs/tutorial/part2/run_hello.py
就会打印出来上面的日志:
gem5 Simulator System. https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 version 22.1.0.0
gem5 compiled Mar 7 2023 11:27:08
gem5 started Mar 7 2023 11:41:32
gem5 executing on ubuntu, pid 66785
command line: ./build/X86/gem5.opt --debug-flags=HelloExample 'configs/tutorial/part2/run_hello.py'
Global frequency set at 1000000000000 ticks per second
0: hello: Create the hello object
Beginning simulation!
build/X86/sim/simulate.cc:192: info: Entering event queue @ 0. Starting simulation...
Exiting @ tick 18446744073709551615 because simulate() limit reached
如果不加 --debug-flags就没有打印信息的输出。
3 事件驱动编程
GEM5是一个事件驱动的模型,在本节中将在上一节的demo基础上扩展为事件处理模型。
对于事件模型,每个事件将会触发钩子函数的执行,Gem5中提供一个简单的接口来创建事件模型。
首先要定义事件的钩子函数:
-
processEvent()函数:用来执行在每次事件发生的响应函数
-
EventFunctionWrapper event:执行任意事件函数的接口
-
startup()函数:用来使SimObjects调度内部事件。
// file: src/learning_gem5/part2/hello_object.hh
#ifndef __LEARNING_GEM5_HELLO_OBJECT_HH__
#define __LEARNING_GEM5_HELLO_OBJECT_HH__
#include "params/HelloObject.hh"
#include "sim/sim_object.hh"
namespace gem5
{
class HelloObject : public SimObject
{
public:
HelloObject(const HelloObjectParams &p);
void startup();
private:
void processEvent();
EventFunctionWrapper event;
};
} // namespace gem5
#endif // __LEARNING_GEM5_HELLO_OBJECT_HH__
hello_object.cc文件中实现如下:
// file: src/learning_gem5/part2/hello_object.cc
#include "base/trace.hh"
#include "debug/HelloExample.hh"
#include "learning_gem5/part2/hello_object.hh"
#include <iostream>
namespace gem5
{
HelloObject::HelloObject(const HelloObjectParams ¶ms) :
SimObject(params),
event([this]{processEvent();}, name())
{
DPRINTF(HelloExample, "this is function HelloObject\n");
}
void HelloObject::processEvent() {
DPRINTF(HelloExample, "this is function processEvent()\n");
}
void HelloObject::startup() {
DPRINTF(HelloExample, "this is function startup()\n");
schedule(event, 100);
}
} // namespace gem5
运行命令:
./build/X86/gem5.opt --debug-flags=HelloExample configs/tutorial/part2/run_hello.py
执行结果如下:
gem5 Simulator System. https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 version 22.1.0.0
gem5 compiled Mar 7 2023 11:59:40
gem5 started Mar 7 2023 12:01:39
gem5 executing on ubuntu, pid 67893
command line: ./build/X86/gem5.opt --debug-flags=HelloExample 'configs/tutorial/part2/run_hello.py'
Global frequency set at 1000000000000 ticks per second
0: hello: this is function HelloObject
Beginning simulation!
0: hello: this is function startup()
build/X86/sim/simulate.cc:192: info: Entering event queue @ 0. Starting simulation...
100: hello: this is function processEvent()
Exiting @ tick 18446744073709551615 because simulate() limit reached
现在可以添加一些变量来控制程序的Tick时延,在hello_object.hh文件中:
// file: src/learning_gem5/part2/hello_object.hh
#ifndef __LEARNING_GEM5_HELLO_OBJECT_HH__
#define __LEARNING_GEM5_HELLO_OBJECT_HH__
#include "params/HelloObject.hh"
#include "sim/sim_object.hh"
namespace gem5
{
class HelloObject : public SimObject
{
public:
HelloObject(const HelloObjectParams &p);
void startup();
private:
void processEvent();
EventFunctionWrapper event;
const Tick latency;
int timeLeft;
};
} // namespace gem5
#endif // __LEARNING_GEM5_HELLO_OBJECT_HH__
hello_object.cc:
// file src/learning_gem5/part2/hello_object.cc
#include "base/trace.hh"
#include "debug/HelloExample.hh"
#include "learning_gem5/part2/hello_object.hh"
#include <iostream>
namespace gem5
{
HelloObject::HelloObject(const HelloObjectParams ¶ms) :
SimObject(params), event([this]{processEvent();}, name()), latency(100), timeLeft(10)
{
DPRINTF(HelloExample, "this is function HelloObject\n");
}
void HelloObject::processEvent() {
DPRINTF(HelloExample, "this is function processEvent()\n");
timeLeft --;
if (timeLeft <= 0) {
DPRINTF(HelloExample, "Done\n");
} else {
schedule(event, curTick() + latency);
}
}
void HelloObject::startup() {
DPRINTF(HelloExample, "this is function startup()\n");
schedule(event, latency);
}
} // namespace gem5
运行输出如下:
qihangkong@ubuntu:~/git/gem5$ ./build/X86/gem5.opt --debug-flags=HelloExample configs/tutorial/part2/run_hello.py
gem5 Simulator System. https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 version 22.1.0.0
gem5 compiled Mar 7 2023 15:02:47
gem5 started Mar 7 2023 15:22:11
gem5 executing on ubuntu, pid 73107
command line: ./build/X86/gem5.opt --debug-flags=HelloExample 'configs/tutorial/part2/run_hello.py'
Global frequency set at 1000000000000 ticks per second
0: hello: this is function HelloObject
Beginning simulation!
0: hello: this is function startup()
build/X86/sim/simulate.cc:192: info: Entering event queue @ 0. Starting simulation...
100: hello: this is function processEvent()
200: hello: this is function processEvent()
300: hello: this is function processEvent()
400: hello: this is function processEvent()
500: hello: this is function processEvent()
600: hello: this is function processEvent()
700: hello: this is function processEvent()
800: hello: this is function processEvent()
900: hello: this is function processEvent()
1000: hello: this is function processEvent()
1000: hello: Done
Exiting @ tick 18446744073709551615 because simulate() limit reached
4 从Python传参数
上面的例子中,参数是定义在程序中的,本节介绍如何通过python脚本传递参数。
首先要修改src/learning_gem5/part2/HelloObject.py添加参数:
# file: src/learning_gem5/part2/HelloObject.py
from m5.params import *
from m5.SimObject import SimObject
class HelloObject(SimObject):
type = 'HelloObject'
cxx_header = "learning_gem5/part2/hello_object.hh"
cxx_class = "gem5::HelloObject"
time_to_wait = Param.Latency("time before firing the event")
number_of_fires = Param.Int(1, "Number of times to fire the event before goodbye")
修改hello_object.cc文件中的变量初始化方式,从params.time_to_wait和params.number_of_fires中初始化:
// file src/learning_gem5/part2/hello_object.cc
#include "base/trace.hh"
#include "debug/HelloExample.hh"
#include "learning_gem5/part2/hello_object.hh"
#include <iostream>
namespace gem5
{
HelloObject::HelloObject(const HelloObjectParams ¶ms) :
SimObject(params), event([this]{processEvent();}, name()), latency(params.time_to_wait), timeLeft(params.number_of_fires)
{
DPRINTF(HelloExample, "this is function HelloObject\n");
}
void HelloObject::processEvent() {
DPRINTF(HelloExample, "this is function processEvent()\n");
timeLeft --;
if (timeLeft <= 0) {
DPRINTF(HelloExample, "Done\n");
} else {
schedule(event, curTick() + latency);
}
}
void HelloObject::startup() {
DPRINTF(HelloExample, "this is function startup()\n");
schedule(event, latency);
}
} // namespace gem5
因为是从python脚本中传的参数进来,所以要修改python脚本:
# file: configs/tutorial/part2/run_hello.py
import m5
from m5.objects import *
# 由于我们写了一个间的验证模块,所以系统配置时不用指定CPU、内存、Cache等其他模块,helloObject就可运行
root = Root(full_system = False)
root.hello = HelloObject()
root.hello.time_to_wait = '1ns'
m5.instantiate()
print("Beginning simulation!")
exit_event = m5.simulate()
print('Exiting @ tick {} because {}'.format(m5.curTick(), exit_event.getCause()))
运行结果如下:
qihangkong@ubuntu:~/git/gem5$ ./build/X86/gem5.opt --debug-flags=HelloExample configs/tutorial/part2/run_hello.py
gem5 Simulator System. https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 version 22.1.0.0
gem5 compiled Mar 7 2023 15:57:18
gem5 started Mar 7 2023 15:59:05
gem5 executing on ubuntu, pid 77889
command line: ./build/X86/gem5.opt --debug-flags=HelloExample 'configs/tutorial/part2/run_hello.py'
Global frequency set at 1000000000000 ticks per second
0: hello: this is function HelloObject
Beginning simulation!
0: hello: this is function startup()
build/X86/sim/simulate.cc:192: info: Entering event queue @ 0. Starting simulation...
1000: hello: this is function processEvent()
1000: hello: Done
Exiting @ tick 18446744073709551615 because simulate() limit reached
5 传更多的参数
还可以传递其他的SimObject作为参数,在这个例子中设置了一个HelloObject,HelloObject中包含一个GoodbyeObject的对象,其中GoodbyeObject通过参数传递给HelloObject。
实现HelloObject执行倒计时之后给GoodbyeObject发送消息,直到消息填满GoodbyeObject的缓冲区为止。
实现包含文件:
-
src/learning_gem5/part2/SConscript:构建工程,包含两个对象HelloObject和GoodbyeObject
-
src/learning_gem5/part2/goodbye_object:新增加一个goodbye_object对象的实现
-
src/learning_gem5/part2/hello_object:更新hello_object,给goodbye发送消息
-
src/learning_gem5/part2/HelloObject.py:导出GoodbyeObject
首先创建一个新的GoodbyeObject的实现goodbye_object.hh:
// file: src/learning_gem5/part2/goodbye_object.hh
#ifndef __LEARNING_GEM5_GOODBYE_OBJECT_HH__
#define __LEARNING_GEM5_GOODBYE_OBJECT_HH__
#include <string>
#include "params/GoodbyeObject.hh"
#include "sim/sim_object.hh"
namespace gem5
{
class GoodbyeObject : public SimObject
{
private:
void processEvent();
// 在每一次迭代填充缓冲区,如果缓冲区未满,此函数将调用其他的事件,继续填充
void fillBuffer();
EventWrapper<GoodbyeObject, &GoodbyeObject::processEvent> event;
float bandwidth;
int bufferSize;
char *buffer;
std::string message;
int bufferUsed;
public:
GoodbyeObject(const GoodbyeObjectParams ¶ms);
~GoodbyeObject();
void sayGoodBye(std::string name);
};
}
#endif
goodbye_object.cc:
// file: src/learning_gem5/part2/goodbye_object.cc
#include "learning_gem5/part2/goodbye_object.hh"
#include "base/trace.hh"
#include "debug/Hello.hh"
#include "sim/sim_exit.hh"
namespace gem5 {
GoodbyeObject::GoodbyeObject(const GoodbyeObjectParams ¶ms) :
SimObject(params),
event(*this),
bandwidth(params.write_bandwidth),
bufferSize(params.buffer_size),
buffer(nullptr),
bufferUsed(0)
{
buffer = new char[bufferSize];
DPRINTF(Hello, "Create the goodbye object\n");
}
GoodbyeObject::~GoodbyeObject()
{
delete[] buffer;
}
void GoodbyeObject::processEvent()
{
DPRINTF(Hello, "Processing the event\n");
fillBuffer();
}
void GoodbyeObject::sayGoodBye(std::string other_name)
{
DPRINTF(Hello, "Saying Goodbye to %s\n", other_name);
message = "Goodbye " + other_name + "!!";
fillBuffer();
}
void GoodbyeObject::fillBuffer()
{
assert(message.length() > 0);
int bytes_copied = 0;
for (auto it = message.begin(); it<message.end() && bufferUsed < bufferSize - 1; it++, bufferUsed++, bytes_copied++) {
buffer[bufferUsed] = *it;
}
if (bufferUsed < bufferSize -1) {
// buffer 未填充满
DPRINTF(Hello, "Scheduing another fillbuffer in %d ticks\n", bandwidth * bytes_copied);
schedule(event, curTick() + bandwidth * bytes_copied);
} else {
DPRINTF(Hello, "Goodbye done copying~\n");
exitSimLoop(buffer, 0, curTick() + bandwidth * bytes_copied);
}
}
} // namespace gem5
修改HelloObject.py,声明GoodbyeObject,并在HelloObject中通过参数传递:
# file: src/learning_gem5/part2/HelloObject.py
from m5.params import *
from m5.SimObject import SimObject
class GoodbyeObject(SimObject):
type = "GoodbyeObject"
cxx_header = "learning_gem5/part2/goodbye_object.hh"
cxx_class = "gem5::GoodbyeObject"
buffer_size = Param.MemorySize('1kB', "size of buffer to fill with goodbye")
write_bandwidth = Param.MemoryBandwidth('100MB/s', "Bandwidth to fill the buffer")
class HelloObject(SimObject):
type = "HelloObject"
cxx_header = "learning_gem5/part2/hello_object.hh"
cxx_class = "gem5::HelloObject"
time_to_wait = Param.Latency("2us", "Time before firing the event")
number_of_fires = Param.Int(
10, "Number of times to fire the event before googbye"
)
goodbye_object = Param.GoodbyeObject("A goodbye object")
对应的hello_object.hh也需要修改:
// file: src/learning_gem5/part2/hello_object.hh
#ifndef __LEARNING_GEM5_HELLO_OBJECT_HH__
#define __LEARNING_GEM5_HELLO_OBJECT_HH__
#include "learning_gem5/part2/goodbye_object.hh"
#include "params/HelloObject.hh"
#include "sim/sim_object.hh"
namespace gem5
{
class HelloObject : public SimObject
{
public:
HelloObject(const HelloObjectParams &p);
void startup() override;
private:
void processEvent();
EventFunctionWrapper event;
GoodbyeObject *goodbye;
const std::string myName;
const Tick latency;
int timesLeft;
};
} // namespace gem5
#endif // __LEARNING_GEM5_HELLO_OBJECT_HH__
hello_object.cc:
// file: src/learning_gem5/part2/hello_object.cc
#include "base/trace.hh"
#include "debug/Hello.hh"
#include "learning_gem5/part2/hello_object.hh"
#include <iostream>
namespace gem5
{
HelloObject::HelloObject(const HelloObjectParams ¶ms) :
SimObject(params),
event([this]{processEvent();}, name()),
goodbye(params.goodbye_object),
myName(params.name),
latency(params.time_to_wait),
timesLeft(params.number_of_fires)
{
DPRINTF(Hello, "this is function HelloObject()\n");
}
void HelloObject::processEvent() {
timesLeft --;
DPRINTF(Hello, "Hello world! Processing the event! %d left\n",
timesLeft);
if (timesLeft <= 0) {
DPRINTF(Hello, "Done Firing!\n");
goodbye->sayGoodBye(myName);
} else {
schedule(event, curTick() + latency);
}
}
void HelloObject::startup() {
DPRINTF(Hello, "this is function startup()\n");
schedule(event, latency);
}
} // namespace gem5
SConscript构建脚本修改如下:
# file: src/learning_gem5/part2/SConstruct
Import('*')
DebugFlag('Hello')
SimObject('HelloObject.py', sim_objects=['HelloObject', 'GoodbyeObject'])
Source('hello_object.cc')
Source('goodbye_object.cc')
最后还需要修改 configs/tutorial/part2/run_hello.py 文件:
# file: configs/tutorial/part2/run_hello.py
import m5
from m5.objects import *
# 由于我们写了一个简单的验证模块,所以系统配置时不用指定CPU、内存、Cache等其他模块,单独的helloObject模块就可运行
root = Root(full_system=False)
root.hello = HelloObject(time_to_wait = '2ns', number_of_fires = 5)
root.hello.goodbye_object = GoodbyeObject(buffer_size='100B')
m5.instantiate()
print("Beginning simulation!")
exit_event = m5.simulate()
print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}")
添加完成之后编译运行:
scons build/X86/gem5.opt -j 32
./build/X86/gem5.opt --debug-flags=Hello configs/tutorial/part2/run_hello.py
打印信息如下:
zac@zac-us:~/rworkdir/gem5/gem5$ ./build/X86/gem5.opt --debug-flags=Hello configs/tutorial/part2/run_hello.py
gem5 Simulator System. https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 version 23.1.0.0
gem5 compiled Mar 22 2024 11:32:46
gem5 started Mar 22 2024 12:04:09
gem5 executing on zac-us, pid 60993
command line: ./build/X86/gem5.opt --debug-flags=Hello configs/tutorial/part2/run_hello.py
Global frequency set at 1000000000000 ticks per second
warn: No dot file generated. Please install pydot to generate the dot file and pdf.
0: hello.goodbye_object: Create the goodbye object
0: hello: this is function HelloObject()
Beginning simulation!
0: hello: this is function startup()
src/sim/simulate.cc:199: info: Entering event queue @ 0. Starting simulation...
2000: hello: Hello world! Processing the event! 4 left
4000: hello: Hello world! Processing the event! 3 left
6000: hello: Hello world! Processing the event! 2 left
8000: hello: Hello world! Processing the event! 1 left
10000: hello: Hello world! Processing the event! 0 left
10000: hello: Done Firing!
10000: hello.goodbye_object: Saying Goodbye to hello
10000: hello.goodbye_object: Scheduing another fillbuffer in 143055 ticks
153055: hello.goodbye_object: Processing the event
153055: hello.goodbye_object: Scheduing another fillbuffer in 143055 ticks
296110: hello.goodbye_object: Processing the event
296110: hello.goodbye_object: Scheduing another fillbuffer in 143055 ticks
439165: hello.goodbye_object: Processing the event
439165: hello.goodbye_object: Scheduing another fillbuffer in 143055 ticks
582220: hello.goodbye_object: Processing the event
582220: hello.goodbye_object: Scheduing another fillbuffer in 143055 ticks
725275: hello.goodbye_object: Processing the event
725275: hello.goodbye_object: Scheduing another fillbuffer in 143055 ticks
868330: hello.goodbye_object: Processing the event
868330: hello.goodbye_object: Goodbye done copying~
Exiting @ tick 954163 because Goodbye hello!!Goodbye hello!!Goodbye hello!!Goodbye hello!!Goodbye hello!!Goodbye hello!!Goodbye h