GEM5教程--修改和拓展gem5(三)

五、向SimObjects和更多事件添加参数

GEM5的Python接口最强大的部分之一是将参数从Python传递到GEM5中的C++对象的能力。在本部分中,我们将探讨SimObjects的一些参数类型,以及如何在前面部分的简单HelloObject的基础上使用它们。

1、简单参数

首先,我们将为延迟和在HelloObject中触发事件的次数添加参数。要添加参数,请修改SimObject Python文件(src/learning-gem5/HelloObject.py)中的HelloObject类。通过向包含Param类型的Python类添加新语句来设置参数。

(1)例如,下面的代码作为参数time_to_wait,它是一个“Latency”参数,而number_of_fires是一个整数参数。

class HelloObject(SimObject):
    type = 'HelloObject'
    cxx_header = "learning_gem5/hello_object.hh"

    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")

Param.声明类型为的参数。常见的类型包括整数的Int类型、浮点的Float类型等等。这些类型的行为类似于常规的Python类。

每个参数声明接受一个或两个参数。当给定两个参数(如上面的number_of_fires)时,第一个参数是参数的默认值。在这种情况下,如果在Python配置文件中实例化HelloObject而没有为number of fires指定任何值,那么它将采用默认值1。 参数声明的第二个参数是参数的简短描述。这一定是一个Python字符串。如果您只为参数声明指定一个参数,那么它就是描述(关于time_to_wait)。

gem5还支持许多复杂的参数类型,这些类型不仅仅是内置类型。例如,time_to_wait就是一个延迟。延迟将值作为时间值作为字符串,并将其转换为模拟器信号。例如,如果默认滴答率为1皮秒(每秒10^12滴答或1T赫兹),“1ns”将自动转换为1000。还有其他方便的参数,如百分比、周期、内存大小等等。

(2)一旦在 SimObject文件中声明这些参数,就需要在构造函数中将它们的值复制到C++类。以下代码显示对HelloObject构造函数的更改。

HelloObject::HelloObject(HelloObjectParams *params) :
    SimObject(params),
    event(*this),
    myName(params->name),
    latency(params->time_to_wait),
    timesLeft(params->number_of_fires)
{
    DPRINTF(Hello, "Created the hello object with the name %s\n", myName);
}

(3)这里,我们使用参数的值作为延迟和剩余时间的默认值。此外,我们存储参数对象中的名称,以便稍后在成员变量myName中使用。每个params实例化都有一个名称,该名称在实例化时来自Python配置文件。

但是,在这里指定名称只是使用params对象的一个例子。对于所有SimObjects,都有一个始终返回名称的name()函数。因此,永远不需要存储上述名称。

在HelloObject类声明中,为名称添加一个成员变量。

class HelloObject : public SimObject
{
  private:
    void processEvent();

    EventWrapper<HelloObject, &HelloObject::processEvent> event;

    std::string myName;

    Tick latency;

    int timesLeft;

  public:
    HelloObject(HelloObjectParams *p);

    void startup();
};

(4)time_to_wait参数没有默认值。因此,我们需要更新Python配置文件(run_hello.py)来指定这个值。

root.hello = HelloObject(time_to_wait = '2us')

或者,我们可以指定time_to_wait作为成员变量。两个选项完全相同,因为C++对象直到 m5.instantiate() 才被创建。

root.hello = HelloObject()
root.hello.time_to_wait = '2us'
2、其他SimObjects作为参数

也可以将其他SimObjects指定为参数。为了演示这一点,我们将创建一个新的SimObject,GoodbyeObject。这个对象将有一个简单的函数来和另一个SimObject说“再见”。为了让它更有趣,GoodbyeObject将有一个缓冲区来写消息,并且有一个有限的带宽来写消息。

(1)首先,在SConscript文件中声明SimObject:

Import('*')

SimObject('HelloObject.py')
Source('hello_object.cc')
Source('goodbye_object.cc')

DebugFlag('Hello')

(2)接下来,需要在SimObject Python文件中声明新的SimObject。由于GoodbyeObject与HelloObject高度相关,我们将使用相同的文件。您可以将以下代码添加到HelloObject.py。 此对象有两个参数,都具有默认值。第一个参数是缓冲区的大小,是一个MemorySize参数。第二个是写带宽,它指定填充缓冲区的速度。一旦缓冲区已满,模拟将退出。

class GoodbyeObject(SimObject):
    type = 'GoodbyeObject'
    cxx_header = "learning_gem5/goodbye_object.hh"

    buffer_size = Param.MemorySize('1kB',
                                   "Size of buffer to fill with goodbye")
    write_bandwidth = Param.MemoryBandwidth('100MB/s', "Bandwidth to fill "
                                            "the buffer")

(3)现在,我们需要实现 GoodbyeObject

#ifndef __LEARNING_GEM5_GOODBYE_OBJECT_HH__
#define __LEARNING_GEM5_GOODBYE_OBJECT_HH__

#include <string>

#include "params/GoodbyeObject.hh"
#include "sim/sim_object.hh"

class GoodbyeObject : public SimObject
{
  private:
    void processEvent();

    /**
     * Fills the buffer for one iteration. If the buffer isn't full, this
     * function will enqueue another event to continue filling.
     */
    void fillBuffer();

    EventWrapper<GoodbyeObject, &GoodbyeObject::processEvent> event;

    /// The bytes processed per tick
    float bandwidth;

    /// The size of the buffer we are going to fill
    int bufferSize;

    /// The buffer we are putting our message in
    char *buffer;

    /// The message to put into the buffer.
    std::string message;

    /// The amount of the buffer we've used so far.
    int bufferUsed;

  public:
    GoodbyeObject(GoodbyeObjectParams *p);
    ~GoodbyeObject();

    /**
     * Called by an outside object. Starts off the events to fill the buffer
     * with a goodbye message.
     *
     * @param name the name of the object we are saying goodbye to.
     */
    void sayGoodbye(std::string name);
};

#endif // __LEARNING_GEM5_GOODBYE_OBJECT_HH__
#include "learning_gem5/goodbye_object.hh"

#include "debug/Hello.hh"
#include "sim/sim_exit.hh"

GoodbyeObject::GoodbyeObject(GoodbyeObjectParams *params) :
    SimObject(params), event(*this), bandwidth(params->write_bandwidth),
    bufferSize(params->buffer_size), buffer(nullptr), bufferUsed(0)
{
    buffer = new char[bufferSize];
    DPRINTF(Hello, "Created 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()
{
    // There better be a message
    assert(message.length() > 0);

    // Copy from the message to the buffer per byte.
    int bytes_copied = 0;
    for (auto it = message.begin();
         it < message.end() && bufferUsed < bufferSize - 1;
         it++, bufferUsed++, bytes_copied++) {
        // Copy the character into the buffer
        buffer[bufferUsed] = *it;
    }

    if (bufferUsed < bufferSize - 1) {
        // Wait for the next copy for as long as it would have taken
        DPRINTF(Hello, "Scheduling another fillBuffer in %d ticks\n",
                bandwidth * bytes_copied);
        schedule(event, curTick() + bandwidth * bytes_copied);
    } else {
        DPRINTF(Hello, "Goodbye done copying!\n");
        // Be sure to take into account the time for the last bytes
        exitSimLoop(buffer, 0, curTick() + bandwidth * bytes_copied);
    }
}

GoodbyeObject*
GoodbyeObjectParams::create()
{
    return new GoodbyeObject(this);
}

这个GoodbyeObject的接口是一个简单的函数sayGoodbye,它接受一个字符串作为参数。调用此函数时,模拟器将生成消息并将其保存在成员变量中。然后,我们开始填充缓冲区。

为了模拟有限的带宽,每次我们将消息写入缓冲区时,都会暂停等待写入消息所需的延迟。我们使用一个简单的事件来模拟这个暂停。

因为我们在SimObject声明中使用了MemoryBandwidth参数,所以带宽变量会自动转换为每字节的滴答数,所以计算延迟只是带宽乘以要写入缓冲区的字节数。

最后,当缓冲区已满时,我们调用函数exitSimLoop,它将退出模拟。这个函数有三个参数,第一个是返回Python配置脚本的消息(exit_event.getCause()),第二个是退出代码,第三个是何时退出。

3、将GoodbyeObject作为参数添加到HelloObject

(1)首先,我们还将向HelloObject添加一个GoodbyeObject作为参数。为此,只需指定SimObject类名作为参数的类型名。你可以有一个默认的,或者没有,就像一个普通的参数。

class HelloObject(SimObject):
    type = 'HelloObject'
    cxx_header = "learning_gem5/hello_object.hh"

    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")

    goodbye_object = Param.GoodbyeObject("A goodbye object")

(2)其次,我们将向HelloObject类添加对GoodbyeObject的引用。

class HelloObject : public SimObject
{
  private:
    void processEvent();

    EventWrapper<HelloObject, &HelloObject::processEvent> event;

    /// Pointer to the corresponding GoodbyeObject. Set via Python
    const GoodbyeObject* goodbye;

    /// The name of this object in the Python config file
    const std::string myName;

    /// Latency between calling the event (in ticks)
    const Tick latency;

    /// Number of times left to fire the event before goodbye
    int timesLeft;

  public:
    HelloObject(HelloObjectParams *p);

    void startup();
};

(3)然后,我们需要更新HelloObject的构造函数和流程事件函数。我们还在构造函数中添加一个检查,以确保goodbye指针有效。通过使用空的特殊Python SimObject,可以通过参数将空指针作为SimObject传递。当这种情况发生时,我们应该惊慌失措,因为这个对象不是被编码为接受的情况。

#include "learning_gem5/part2/hello_object.hh"

#include "base/misc.hh"
#include "debug/Hello.hh"

HelloObject::HelloObject(HelloObjectParams *params) :
    SimObject(params),
    event(*this),
    goodbye(params->goodbye_object),
    myName(params->name),
    latency(params->time_to_wait),
    timesLeft(params->number_of_fires)
{
    DPRINTF(Hello, "Created the hello object with the name %s\n", myName);
    panic_if(!goodbye, "HelloObject must have a non-null GoodbyeObject");
}

(4)一旦我们处理了参数指定的事件数,就应该调用GoodbyeObject中的sayGoodbye函数。

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);
    }
}
4、升级配置脚本

最后,我们需要将GoodbyeObject添加到配置脚本中。创建一个新的配置脚本hello_goodbye.py并实例化hello和goodbye对象。例如,一个可能的脚本如下。

import m5
from m5.objects import *

root = Root(full_system = False)

root.hello = HelloObject(time_to_wait = '2us', number_of_fires = 5)
root.hello.goodbye_object = GoodbyeObject(buffer_size='100B')

m5.instantiate()

print "Beginning simulation!"
exit_event = m5.simulate()
print 'Exiting @ tick %i because %s' % (m5.curTick(), exit_event.getCause())

您可以修改这两个SimObjects的参数,并查看总体执行时间(Exiting@tick *****)的变化。要运行这些测试,您可能需要删除调试标志,以便减少对终端的输出。

GEM5系列教程索引

GEM5教程–gem5开始之旅(一)

GEM5教程–gem5开始之旅(二)

GEM5教程–修改和拓展gem5(一)

GEM5教程–修改和拓展gem5(二)

GEM5教程–修改和拓展gem5(三)

GEM5教程–修改和拓展gem5(四)

GEM5教程-互联网络

GEM5教程-Garnet

gem5学习基础完整版,介绍了gem5环境的安装,以及一些基本概念。 gem5仿真器是用于计算机系统体系结构研究的模块化平台,涵盖系统级体系结构以及处理器微体系结构。1、多个可互换的CPU型号。 gem5提供了四种基于解释的CPU模型:简单的单CPI CPU; 有序CPU的详细模型和无序CPU的详细模型。 这些CPU模型使用通用的高级ISA描述。 此外,gem5具有基于KVM的CPU,该CPU使用虚拟化来加速仿真。 2、完全集成的GPU模型,可以执行真实计算机ISA,并支持与主机CPU共享的虚拟内存。 3、NoMali GPU模型。 gem5带有集成的NoMali GPU模型,该模型与Linux和Android GPU驱动程序堆栈兼容,因此无需进行软件渲染。 NoMali GPU不产生任何输出,但可以确保以CPU为中心的实验产生代表性的结果。 4、事件驱动的内存系统。 gem5具有详细的,事件驱动的内存系统,包括高速缓存,交叉开关,探听过滤器以及快速而准确的DRAM控制器模型,用于捕获当前和新兴内存的影响,例如内存。 LPDDR3 / 4/5,DDR3 / 4,GDDR5,HBM1 / 2/3,HMC,WideIO1 / 2。 可以灵活地布置组件,例如,以具有异构存储器的复杂的多级非均匀高速缓存层次结构来建模。 5、基于跟踪的CPU模型,可播放弹性跟踪,这些跟踪是由附着到乱序CPU模型的探针生成的依赖项和定时注释的跟踪。 跟踪CPU模型的重点是以快速,合理的方式而不是使用详细的CPU模型来实现内存系统(高速缓存层次结构,互连和主内存)的性能探索。 6、异构和异构多核。 可以将CPU模型和缓存组合到任意拓扑中,从而创建同构异构的多核系统。 MOESI侦听缓存一致性协议可保持缓存一致性。 7、多种ISA支持。 gem5将ISA语义与其CPU模型解耦,从而实现对多个ISA的有效支持。 目前gem5支持Alpha,ARM,SPARC,MIPS,POWER,RISC-V和x86 ISA。 有关更多信息,请参见支持的体系结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值