gem5系列笔记(Part II)

code style
  • 使用4个空格而不是Tab

  • 变量和函数用小驼峰命名(第一个单词首字母小写),类名用大驼峰,局部变量加下划线

创建简单Simobject
  1. 创建python脚本,定义Simobject的类,并可以配置Simobject的一些参数,这些参数可以从python脚本中控制
  2. 在C++中实现Simobject类
  3. 添加SConscript来编译新创建的Simobject
Debug

在命令行中使用debug标志,如

 build/X86/gem5.opt --debug-flags=DRAM

创建debug flag:

  1. 在SConscript中声明
DebugFlag('Hello')
  1. 之后编译时在debug文件下会自动生成同名头文件Hello.hh,将其包含到.cc文件中。
#include "debug/Hello.hh
  1. 用debugging function代替打印输出,通常用DPRINTF(Flag, VA_ARGS), 其中第一个参数是同名标志,第二个是打印出来的字符串。当然还有很多其他函数,可以参考learning_gem5
DPRINTF(Hello, "Created the hello object\n");

4.当DPRINTF执行时,会产生三种数据打印到stdout, 分别是执行标志时的tick, 调用DPRINTF的 Simobject 以及其中的字符串参数。

事件驱动编程

每当有事件要处理时,才会调用CPU,其他时间事件请求在队列中,从而不需像之前那样CPU不断轮询

  1. 创建事件回调函数
    事件都继承自 Event 类,gem5中通过封装的函数来实现
    头文件中定义processEvent() 实现需要处理的事件,并添加Event实例。
class HelloObject : public SimObject
{
  private:
    void processEvent();

    EventFunctionWrapper event;

  public:
    HelloObject(HelloObjectParams *p);

    void startup();
};

接下来实现构建函数。EventFuntionWrapper 需要两个参数,要执行的函数和Simobject 的名字。其中要执行的函数通常用一个简单的lambda 函数(C++中的lambda)来调用成员函数 processEvent().

HelloObject::HelloObject(HelloObjectParams *params) :
    SimObject(params), event([this]{processEvent();}, name())
{
    DPRINTF(Hello, "Created the hello object\n");
}

this 指针
接下来实现我们要处理的事件

void
HelloObject::processEvent()
{
    DPRINTF(Hello, "Hello world! Processing the event!\n");
}
  1. 调度事件
    需要用到以下函数,而之前的startup 函数就是Simobject 允许调度内在事件的地方。
    *schedule(Event event, Tick when)
    Schedules the event (event) to execute at time when. This function places the event into the event queue and will execute the event at tick when.
void
HelloObject::startup()
{
    schedule(event, 100);
}

其中100一般使用偏移加curTick()

3.多次调度
通过添加参数latency

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

    EventFunctionWrapper event;

    Tick latency;

    int timesLeft;

  public:
    HelloObject(HelloObjectParams *p);

    void startup();
};

在构造函数中

HelloObject::HelloObject(HelloObjectParams *params) :
    SimObject(params), event([this]{processEvent();}, name()),
    latency(100), timesLeft(10)
{
    DPRINTF(Hello, "Created the hello object\n");
}

更新其他

void
HelloObject::startup()
{
    schedule(event, latency);
}

void
HelloObject::processEvent()
{
    timesLeft--;
    DPRINTF(Hello, "Hello world! Processing the event! %d left\n", timesLeft);

    if (timesLeft <= 0) {
        DPRINTF(Hello, "Done firing!\n");
    } else {
        schedule(event, curTick() + latency);
    }
}
向 Simobject 增加更多参数和事件

添加简单参数

  1. 在python文件创建的类中添加
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后面的名字命名,如time_to_wait的类型是Latency, 另一个是Int

  1. 头文件中添加参数
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();
};
  1. 将参数添加进源文件的构造函数
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);
}

注意到这里似乎Event 封装函数的用法似乎和前一节不同??
而且之后的用法基本上都是这样

  1. 最后需要在Python 配置文件中指定新加参数的值
root.hello = HelloObject(time_to_wait = '2us')
或者是下面的方法
root.hello = HelloObject()
root.hello.time_to_wait = '2us'

因为在m5.instantiate()调用之前C++对象并没有被创建

添加其他Simobject 作为参数

  1. 首先在SConsctipt文件中声明新创建的对象
Import('*')

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

DebugFlag('Hello')

2.同样要创建python 类,这里增加了带宽和缓冲两个参数

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.实现C++对象

#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);
}

通过事件来模拟写入缓冲的延迟,同时接口函数就是sayGoodbye

创建完对象之后。我们将新对象作为参数添加到HelloObject

  1. 首先添加到HelloObject 的python 类中, 注意要将新对象的名字作为Param 的后缀
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")
  1. 修改C++文件
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();
};

更改构造函数,增加了检查goodbye是否是空指针的函数

#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");
}

当我们处理完了之前hello的事件,我们要添加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);
    }

最后更新配置文件

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())
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值