FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Gem5のチュートリアル "Learning Gem5"をやってみる(10. イベントドリブンプログラミング)

gem5を構成するSimObjectを追加するためのチュートリアルをやってみる。

www.gem5.org

  • gem5記事一覧インデックス

msyksphinz.hatenablog.com


gem5はイベントドリブンのシミュレータである。本章では、イベントの作成とスケジューリングの方法を探る。hello-simobject-chapterのシンプルなHelloObjectを元に作っていく。

単純なイベントコールバックの作成

gem5のイベントドリブンモデルでは、各イベントにイベントが処理されるコールバック関数がある。一般的に、これは :cppEvent を継承したクラスである。しかし、gem5には単純なイベントを作成するためのラッパー関数が用意されている。

HelloObjectのヘッダーファイルで、イベントが発生するたびに実行する新しい関数(processEvent())を宣言する必要がある。この関数はパラメーターを取らず、何も返さない。

次に、Eventインスタンスを追加する。今回は、任意の関数を実行できるEventFunctionWrapperを使用する。

また、以下で説明するstartup()関数も追加する。

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

    EventFunctionWrapper event;

  public:
    HelloObject(HelloObjectParams *p);

    void startup();
};

次に、HelloObjectのコンストラクタでこのイベントを構築しなければならない。EventFuntionWrapperは、実行する関数と名前の2つのパラメータを取る。名前は通常、イベントを所有するSimObjectの名前である。名前を表示する際には、自動的に".wrapped_function_event "が名前の最後に付加される。

最初のパラメータは、パラメータを取らず戻り値を持たない関数(std::function<void(void)>)である。通常、これはメンバ関数を呼び出す単純なラムダ関数である。しかし、どんな関数でも構わない。以下では、thisをラムダ([this])に取り込んで、クラスのインスタンスメンバ関数を呼び出せるようにしている。

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

プロセス関数の実装も定義しなければならない。この場合、デバッグをするのであれば、単純に何かを表示することになる。

void
HelloObject::processEvent()
{
    DPRINTF(HelloExample, "Hello world! Processing the event!\n");
}

イベントのスケジューリング

最後に、イベントを処理するために、まずイベントをスケジュールする必要がある。そのために :cppschedule 関数を使用する。イベント・ドリブン・シミュレーションでは、イベントを過去に実行することはできない)。

最初は、HelloObjectクラスに追加したstartup()関数でイベントをスケジュールする。startup()関数は、SimObjectが内部イベントをスケジュールするための関数である。この関数は、シミュレーションが初めて開始されるまで実行されない(すなわち、Python設定ファイルからsimulate()関数が呼び出される)。

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

ここでは、単純に100tickでイベントが実行されるようにスケジュールする。通常はcurTick()からのオフセットを使用するが、startup()関数は現在時刻が0のときに呼び出されることがわかっているので、明示的なtick値を使用することができる。

HelloExample"デバッグフラグを付けてgem5を実行したときの出力は以下のようになる。

gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled Jan  4 2017 11:01:46
gem5 started Jan  4 2017 13:41:38
gem5 executing on chinook, pid 1834
command line: build/X86/gem5.opt --debug-flags=HelloExample configs/learning_gem5/part2/run_hello.py

Global frequency set at 1000000000000 ticks per second
      0: hello: Created the hello object
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
    100: hello: Hello world! Processing the event!
Exiting @ tick 18446744073709551615 because simulate() limit reached

より多くのイベントスケジューリング

イベント・プロセス・アクションの中で新しいイベントをスケジュールすることもできる。例えば、HelloObjectにレイテンシ・パラメーターを追加し、イベントを何回発生させるかのパラメーターを追加する。次の章では、これらのパラメータをPythonの設定ファイルからアクセスできるようにする。 HelloObjectクラスの宣言に、レイテンシとイベント発生回数のメンバ変数を追加する。

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

    EventFunctionWrapper event;

    const Tick latency;

    int timesLeft;

  public:
    HelloObject(const HelloObjectParams &p);

    void startup();
};

次に、コンストラクタでlatencytimesLeftの値を設定する。

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

最後に、startup()processEvent()をアップデートする。

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

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

    if (timesLeft <= 0) {
        DPRINTF(HelloExample, "Done firing!\n");
    } else {
        schedule(event, curTick() + latency);
    }
}

これでgem5を実行すると、イベントが10回発生し、1000 tick 後にシミュレーションが終了するはずだ。出力は以下のようになるはずだ。

gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled Jan  4 2017 13:53:35
gem5 started Jan  4 2017 13:54:11
gem5 executing on chinook, pid 2326
command line: build/X86/gem5.opt --debug-flags=HelloExample configs/learning_gem5/part2/run_hello.py

Global frequency set at 1000000000000 ticks per second
      0: hello: Created the hello object
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
    100: hello: Hello world! Processing the event! 9 left
    200: hello: Hello world! Processing the event! 8 left
    300: hello: Hello world! Processing the event! 7 left
    400: hello: Hello world! Processing the event! 6 left
    500: hello: Hello world! Processing the event! 5 left
    600: hello: Hello world! Processing the event! 4 left
    700: hello: Hello world! Processing the event! 3 left
    800: hello: Hello world! Processing the event! 2 left
    900: hello: Hello world! Processing the event! 1 left
   1000: hello: Hello world! Processing the event! 0 left
   1000: hello: Done firing!
Exiting @ tick 18446744073709551615 because simulate() limit reached