gem5中已经命名了“SimObject”,如果用户再次实现SimObject将会导致编译器混淆而出错。
gem5中几乎所有的对象都继承自SimObject基类,SimObjects将其主要接口开放给gem5中所有的对象使用,SimObjects是可以通过配置脚本访问的封装对象。
SimObjects可能有许多参数,这些参数通过使用配置文件来设定。它除了使用类似于整数和浮点数这类简单参数外,也可以使用其他的SimObjects作为参数。故而用户可以使用它来创建类似真实机器的复杂系统层次结构。
这里我们一步步地创建一个展示“HelloWorld”的简单SimObjects,展示SimObjects的创建方法和所有SimObject所需的样板代码。另外,还创建一个简单的配置脚本,用于实例化我们创建的SimObjects。
使用Git分支
通常为gem5添加新特性时,使用新的git分支,即用户使用gem5添加新功能或修改某些内容时首先要创建一个新的分支来存储用户更改。
git checkout -b hello-simobject
Step 1:为自定义的SimObject创建一个Python类
每个SimObject都有一个与之关联的Python类,这个Python类描述了SimObject的参数,这些参数可以通过Python配置文件控制。
我们首先使用简单的SimObject,从没有参数开始。也就是说只需要为SimObject:
- 1、声明一个新类并设置
- 2、设置它的名称
- 3、设置用于SimObject定义的C++类的C++头文件。
我们可以在src/learning_gem5/part2中创建文件HelloObject.py。如果是使用克隆的GEM5的仓库,我们将在src/learning_gem5/part2和configs/learning_gem5/part2目录下发现这些已经完成了的文件,继续本教程的学习之前用户可以删除或者将它们移动到别处。
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"
没有强制要求这里'type'的设置必须与类的名称相同,但一般都遵循这样的作法。'type'是使用此Python SimObject封装的C++类,只有在一些特殊情况下,'type'才应该与类名不同。
cxx_header是用作'type'参数的类的声明文件,约定SimObject的名称全部使用小写或者下划线(不强制),用户可以用它指定任意头文件。
cxx_class指明在gem5命名空间中声明的SimObject的属性,gem5代码中的大多数SimObject基类在gem5命名空间中声明!
Step 2:使用C++实现用户自定义的SimObject
接下来,在 src/learning_gem5/part2/目录下创建hello_object.hh和hello_object.cc文件,这两个文件是用于HelloObject具体实现的。
首先,考虑C++对象的头文件,GEM5将#ifndef和#endif对之间的所有头文件使用名字和相应的路径进行封装,因此不存在循环包含的问题。
SimObjects应该在gem5命名空间中声明,所以这里使用了namespace gem5名称域。
这里需要做的唯一的一件事情就是声明自定义的类,由于HelloObject是SimObject的一种,所以它必须从SimObject的C++类派生。绝大多数情况下,用户自定义的SimObject的父类是SimObject的子类,而不是SimObject类本身。
SimObject类定义了许多虚函数,但全部都不是纯虚函数,因此在最简单的案例下,除了构造函数以外,派生类可以不做任何函数实现。
所有的SimObject类构造函数都假定采用一个参数对象,构建(build)系统基于Python的SimObject类(如第一步中所创建)自动生成该参数对象,并基于对象名称自动生成参数类型名称。对于这里的 “HelloObject” 参数类型的名称为“HelloObjectParams”。
头文件的代码如下:
#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__
接下来,需要在后缀为.cc的C++源文件中实现两个函数,注意是两个函数,不是一个函数。第一个是HelloObject的构造函数,这里简单地将参数对象传递给父类SimObject并打印“Hello World!”。
正常情况下,用户都是使用调试标志,几乎从不使用gem5的std::cout。这里,为了简便我们使用了std::cout,下一节在修改后再使用调试标志。
#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
注意:如果用户使用这种形式的构造函数:Foo(const FooParams &)
gem5将会自动定义一个方法:FooParams::create(),这个create()方法的目的是调用 SimObject构造函数并返回一个SimObject的实例,大多数SimObject都遵循这种模式;如果用户定义的SimObject不遵循此模式,则参考gem5的SimObject文档,该文档提供有关手动实现这个create()方法的详细信息。
Step 3:注册SimObject和C++文件
构建系统需要了解C++和Python文件的信息以实现C++文件的编译和Python文件的解释,GEM5使用Scons作为构建系统,只需要在包含用户自定义的SimObject代码的目录下创建一个SCons脚本文件(SConscript)即可,如果该目录下已经存在SConscript脚本文件,则将有关声明添加到该SConscript脚本文件中。
这个脚本文件只是一个普通的Python文件,因此用户可以在该文件中编写符合Python语法的所需代码。一些脚本可能会变得非常复杂,GEM5因此为SimObjects自动创建代码并编译特定于领域的语言,如SLICC和ISA语言。
在SConscript文件中,有许多函数在用户导入后自动定义,请参阅有关该部分的说明…
这里,要编译我们定义的SimObject,只需要在src/learning_gem5/part2目录中创建一个名为“SConscript”的文件。在这个文件中,必须声明SimObject和.cc文件。下面是有关代码:
Import('*')
SimObject('HelloObject.py', sim_objects=['HelloObject'])
Source('hello_object.cc')
Step 4:(重)构建gem5
要编译和链接新文件,只需要重新编译gem5。下例假设使用X86指令集,实际上本例我们的目标中无需特定指令集,它能在gem5中任意指令集上运行。
scons build/X86/gem5.opt
Step 5:创建配置脚本以使用自定义的SimObject
至此,我们就已经实现了自定义的SimObject,并将其编译到了gem5中。接下来需要创建或修改配置SConscript文件来实例化自定义对象。这个示例对象很简单,甚至不需要系统对象(system object)!不需要CPU、高速缓存等除了一个被称之为root之外的任何其他对象。注意:所有gem5实例都需要一个root对象。
首先通过创建一个简单的配置脚本,导入m5以及所有已经被编译过的对象
import m5
from m5.objects import *
接下来按照所有GEM5实例的要求来实例化root对象,
root = Root(full_system = False)
现在,我们要实例化自己创建的HelloObject对象,所需要做的只是调用Python构造函数,稍后,我们将研究如何通过Python构造函数指定参数。除了将自定义对象实例化以外,还需要确保它是root对象的子对象(child)。使用C++实例化的只有root的子对象SimObjects。
root.hello = HelloObject()
最后通过m5模块调用实例来实际运行仿真!
m5.instantiate()
print("Beginning simulation!")
exit_event = m5.simulate()
print('Exiting @ tick {} because {}'
.format(m5.curTick(), exit_event.getCause()))
注意:在修改src/目录下的文件后要重新编译gem5。运行配置文件的命令行命令在下方的“command line:”后面,如下所示。
注意:如果为未来章节“Adding parameters to SimObjects and more events”准备的代码,(goodbye_object)存在于src/learning_gem5/part2目录下,run_hello.py将发生错误。如果删除这些文件或者将它们移出gem5目录,run_hello.py运行的输出如下:
gem5 Simulator System. http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.
gem5 compiled May 4 2016 11:37:41
gem5 started May 4 2016 11:44:28
gem5 executing on mustardseed.cs.wisc.edu, pid 22480
command line: build/X86/gem5.opt configs/learning_gem5/part2/run_hello.py
Global frequency set at 1000000000000 ticks per second
Hello World! From a SimObject!
Beginning simulation!
info: Entering event queue @ 0. Starting simulation...
Exiting @ tick 18446744073709551615 because simulate() limit reached
恭喜!您已经成功编写了第一个SimObject。在接下来的章节中,我们将扩展这个SimObject并探索SimObjects有哪些功能可以使用。