Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

Dynamic Scheduler版のVerilatorの中を調べる(その3)

@Vengineerの戯言 : Twitter SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに

Dynamic Scheduler版 Verilatorの中を調べる、その3。今回は、wait がどのように実装されているのかをみていきます。

Verilog HDLコード

Verilog HDLコード (examples/wait/wait.v) を以下に示します。wait の中で 変数の比較をやっています。

module t(/*AUTOARG*/);
   int a = 0;
   int b = 0;
   int c = 0;

   initial begin
     $write("starting with a == 0, b == 0, c == 0\n");
     #2;
     $write("assigning 1 to b.\n");
     b = 1;
     #1;
     $write("assigning 2 to a.\n");
     a = 2;
     #1;
     $write("assigning 3 to c.\n");
     c = 3;
     #1;
     $write("assigning 4 to c.\n");
     c = 4;
     #1;
     $write("assigning 5 to b.\n");
     b = 5;
   end

   initial begin
     #1;
     $write("waiting for a > b...\n");
     wait(a > b);
     $write("waited for a > b.\n");
     $write("waiting for a + b < c...\n");
     wait(a + b < c);
     $write("waited for a + b < c.\n");
     $write("waiting for a < b && b > c...\n");
     wait(a < b && b > c);
     $write("waited for a < b && b > c.\n");
     $write("*-* All Finished *-*\n");
     $finish;
   end
endmodule

生成されたC++コード

initial 文は、Vtop__Slow.cpp の _initial__TOP__? メソッドです。

_initial__TOP__1, _initial__TOP__2, _initial__TOP__3 メソッドは、Verilog HDLコードの下記の部分に対応します。

   int a = 0;
   int b = 0;
   int c = 0;

_initia\l__TOP__4 メソッドは、最初の initial 文に、_initial__TOP__5 メソッドは、2番目の initial 文に対応します。最初の initial 文は #遅延のコードなので、(その1)にて説明しましたので、ここでは省略します。wait に対応するコードは、wait_until メソッドの部分です。各wait_until メソッドの部分の return のコードが wait の中の条件を表しています。

2番目の initial 文に対応するC++コードである _initial__TOP__5 メソッドは、次のようになっています。

void Vtop::_initial__TOP__5(Vtop__Syms* __restrict vlSymsp, VerilatedThread* self) {
    VL_DEBUG_IF(VL_DBG_MSGF("+    Vtop::_initial__TOP__5\n"); );
    Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
    // Body
    self->wait_for_time(vlSymsp, VL_TIME_Q() + 1U);
    if (self->should_exit()) return;
    VL_WRITEF("waiting for a > b...\n");
    /* [wait statement] */
    self->wait_until([](auto&& values) -> bool {
            return VL_GTS_III(1,32,32, std::get<0>(values),
                              std::get<1>(values));
        }, vlTOPp->t__DOT__b, vlTOPp->t__DOT__a);
    if (self->should_exit()) return;
    VL_WRITEF("waited for a > b.\nwaiting for a + b < c...\n");
    /* [wait statement] */
    self->wait_until([](auto&& values) -> bool {
            return VL_LTS_III(1,32,32, ((IData)(std::get<0>(values))
                                        + (IData)(std::get<1>(values))),
                              std::get<2>(values));
        }, vlTOPp->t__DOT__c, vlTOPp->t__DOT__b, vlTOPp->t__DOT__a);
    if (self->should_exit()) return;
    VL_WRITEF("waited for a + b < c.\nwaiting for a < b && b > c...\n");
    /* [wait statement] */
    self->wait_until([](auto&& values) -> bool {
            return (VL_LTS_III(1,32,32, std::get<0>(values),
                               std::get<1>(values))
                    & VL_GTS_III(1,32,32, std::get<1>(values),
                                 std::get<2>(values)));
        }, vlTOPp->t__DOT__c, vlTOPp->t__DOT__b, vlTOPp->t__DOT__a);
    if (self->should_exit()) return;
    VL_WRITEF("waited for a < b && b > c.\n*-* All Finished *-*\n");
    VL_FINISH_MT("/home/vengineer/home/verilator/verilator-dynamic-scheduler-examples/examples/wait/wait.sv", 37, "")
;
    return;
}
  • wait( a> b) は、VL_GTS_III(1,32,32, std::get<0>(values), std::get<1>(values))
  • wait(a + b < c) は、VL_LTS_III(1,32,32, *1 + (IData)(std::get<1>(values))), std::get<2>(values));
  • wait(a < b && b > c ) は、VL_LTS_III(1,32,32, std::get<0>(values), std::get<1>(values)) & VL_GTS_III(1,32,32, std::get<1>(values), std::get<2>(values)));

のようになっています。LV_XXX_IIIマクロを使って、wait のカッコ内の条件を表しています。

おわりに

今回は、wait の実装についてみてみました。wait の実装は、event の @(event) と同じ wait_until メソッドを使っていることがわかりました。

次回は、fork/join について調べてみます。

*1:IData)(std::get<0>(values