gem5 使用记录,对例子中helloobject的理解
gem5中有一个 hello的例子,不是hello world那个,在src/learning-gem5/part2里面,这是虽然是个简单的例子但包含的要素挺多挺全。
整个结构是src下面有一个hello_object和goodbye_object这两个源码,前者会调用后者,同时还有有相应的python文件,这些是一一对应的。每个C++都对应一个PY文件。
PY文件可以理解对C++这个类的描述,例子中把hello_object goodbye_object两个C++写到一个PY里去了,这样也行,同样的目录下还有scons这个文件,这是编译时通过他去找哪些需要编译,里面就是把写的前面的文件加进去其实。
另外在config里面就是仿真启动,相当于是主程序的入口。也是py文件。
下面是C++配套的PY文件代码
1 from m5.params import *
2 from m5.SimObject import SimObject
3
4 class HelloObject(SimObject):
5 type = 'HelloObject'
6 cxx_header = "learning_gem5/part2/hello_object.hh"
7 cxx_class = 'gem5::HelloObject'
8
9 time_to_wait = Param.Latency("Time before firing the event")
10 number_of_fires = Param.Int(1, "Number of times to fire the event before "
11 "goodbye")
12
13 goodbye_object = Param.GoodbyeObject("A goodbye object")
14
15 class GoodbyeObject(SimObject):
16 type = 'GoodbyeObject'
17 cxx_header = "learning_gem5/part2/goodbye_object.hh"
18 cxx_class = 'gem5::GoodbyeObject'
19
20 buffer_size = Param.MemorySize('1kB',
21 "Size of buffer to fill with goodbye")
22 write_bandwidth = Param.MemoryBandwidth('100MB/s', "Bandwidth to fill "
23 "the buffer")
来理解一下这些代码,gem5有很多现有的类我们会用到, gem5所有的类都要继承自simobject这是固定的,而每个simobject都有一个params 的参数,所以开始把params和simobject导入进来。
然后是声明hello的类,继承simobject,里面是类的说明,名字,路径之类的,然后是参数第9行开始,左边是C++中的参数名字右边是他的类型,这些类型是来自第一行导入的params这些文件中已经定义好的了,比如去把parama找到打开能找到对于Latency的定义。
13行是我们要调用的子类,大型设计会有很多小类,最后在顶层把它们整合起来,这里的HELLO相当于顶层,GOODBYE相当于子功能模块,我们要把他加到主模块里来。
后面是对GOODBYE这个类的描述都差不多。
然后再理解下C++文件,
1 HelloObject::HelloObject(const HelloObjectParams ¶ms) :
2 SimObject(params),
3 // This is a C++ lambda. When the event is triggered, it will call the
4 // processEvent() function. (this must be captured)
5 event([this]{ processEvent(); }, name() + ".event"),
6 goodbye(params.goodbye_object),
7 // Note: This is not needed as you can *always* reference this->name()
8 myName(params.name),
9 latency(params.time_to_wait),
10 timesLeft(params.number_of_fires)
11 {
12 DPRINTF(HelloExample, "Created the hello object\n");
13 panic_if(!goodbye, "HelloObject must have a non-null GoodbyeObject");
14 }
这个是HELLO的构造函数,他有一个params的参数需要传进来,来源于上面的文字有说过,然后传给simobject,5行是事件的加入,这个是GEM5的机制,因为我们要模拟数字电路的工作方式,这里相当于是把要做的事加到event里面,然后拿去在某个时间执行一次,再间隔时间再执行一次这样就相当于时钟一样, 后面是GOODBYE的传参,MYname后面直接用了name这个变量,但是没有声明或者传递啥的,研究了下,是因为前面有个name()这个函数是返回例化名字的,前面用了这个函数会返回一个name变量,所以后面可以直接用,如果前面也直接用name的话就会报错。再后面是参数传递,花括号里面是构造函数要做的事情。
1 void
2 HelloObject::startup()
3 {
5 // Before simulation starts, we need to schedule the event
6 schedule(event, latency);
7 }
8
9 void
10 HelloObject::processEvent()
11 {
13 timesLeft--;
14 DPRINTF(HelloExample, "Hello world! Processing the event! %d left\n",
15 timesLeft);
16
17 if (timesLeft <= 0) {
18 DPRINTF(HelloExample, "Done firing!\n");
19 goodbye->sayGoodbye(myName);
20 } else {
21 schedule(event, curTick() + latency);
22 }
23 }
上面第一个函数是startup可以看到没有其他哪里在调用它,这好像也是gem5本身的在开始仿真前会自动调用的一个函数,暂时不研究怎么调的了,里面的内容是一个安排事件的函数,这个函数也是gem5里面的实现时钟模拟的函数,之前event是把事件封起来,这里就是真正决定他好久执行了,第二个参数就是等这么久之后就执行event,而event里面又包了我们的普通函数process,这样整个类就运行起来了。
每二个函数就是普通的函数,他被封在了event里面,同时里面也有调用子类GOODBYE的功能,也有再次安排事件的调用。这样就可以实现时钟工作模式。
下面是config/learning/part2里面的。
1 # set up the root SimObject and start the simulation
2 root = Root(full_system = False)
3
4 # Create an instantiation of the simobject you created
5 root.hello = HelloObject(time_to_wait = '2us', number_of_fires = 5)
6 root.hello.goodbye_object = GoodbyeObject(buffer_size='100B')
7
8 # instantiate all of the objects we've created above
9 m5.instantiate()
10
11 print("Beginning simulation!")
12 exit_event = m5.simulate()
13 print('Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause()))
这其实就是主程序的入口相当于,第2行是格式要求吧理解为,每个仿真都有个最顶层的类就是root,后面参数是表示是不是全系统模拟,我们这当然不是。第5行是在root下再例化一个之前我们写的类,并把我们想要的参数传进去。这样程序就真正的完整了,可以用了,第9行是初始化上面的东西,再后面就是开始跑起来了,都是较固定。不再细说。