Vengineerの妄想

人生を妄想しています。

SimpleBus (その10)

Verification Engineerの戯言

今回からは、SimpleATXモデルです。SimpleATXは、Approximately-timed coding styleのモデルです。
まずは、TargetモデルのSimpleATTarget1クラスからです。SimpleATTarger1クラスのコンストラクタはつぎのようになっています。
  SimpleATTarget1(sc_core::sc_module_name name) :
    sc_core::sc_module(name),
    socket("socket"),
    ACCEPT_DELAY(25, sc_core::SC_NS),
    RESPONSE_DELAY(100, sc_core::SC_NS)
  {
    // register nb_transport method
    REGISTER_NBTRANSPORT(socket, myNBTransport);

    SC_METHOD(endRequest)
    sensitive << mEndRequestEvent;
    dont_initialize();

    SC_METHOD(beginResponse)
    sensitive << mBeginResponseEvent;
    dont_initialize();

    SC_METHOD(endResponse)
    sensitive << mEndResponseEvent;
    dont_initialize();
  }
Initiatorクラスから呼び出されるnb_transportメソッドとして、myNBTransportメソッドをREGISTER_NBTRANSPORTマクロで登録しています。
その次に3つのメソッドをSC_METHODマクロでイベント待ちにしています。
各メソッドは、リクエスト終了(mEndRequestEvent)、レスポンス開始(mBeginRequestEvent)、レスポンス終了(mEndResponseEvent)のイベントをトリガにしています。

myNBTtransportメソッドは、次のようになっています。
  sync_enum_type myNBTransport(transaction_type& trans, phase_type& phase, sc_core::sc_time& t)
  {
    if (phase == tlm::BEGIN_REQ) {
      sc_dt::uint64 address = trans.get_address();
      assert(address < 400);

      unsigned int& data = *reinterpret_cast<unsigned int*>(trans.get_data_ptr());
      if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
        std::cout << name() << ": Received write request: A = 0x"
                  << std::hex << (unsigned int)address << ", D = 0x"
                  << data << std::dec
                  << " @ " << sc_core::sc_time_stamp() << std::endl;

        *reinterpret_cast<unsigned int*>(&mMem[address]) = data;

      } else {
        std::cout << name() << ": Received read request: A = 0x"
                  << std::hex << (unsigned int)address << std::dec
                  << " @ " << sc_core::sc_time_stamp() << std::endl;

        data = *reinterpret_cast<unsigned int*>(&mMem[address]);
      }

      // Notify end of request phase after ACCEPT delay
      if (mEndRequestQueue.empty()) {
        mEndRequestEvent.notify(t + ACCEPT_DELAY);
      }
      mEndRequestQueue.push(&trans);

      // AT-noTA target
      // - always return false
      // - seperate call to indicate end of phase (do not update phase or t)
      return tlm::TLM_ACCEPTED;

    } else if (phase == tlm::END_RESP) {

      // response phase ends after t
      mEndResponseEvent.notify(t);

      return tlm::TLM_COMPLETED;
    }

    // Not possible
    assert(0); exit(1);
    return tlm::TLM_REJECTED;
  }
Initiatorクラスからリクエスト開始(tlm::BEGIN_REQ)が来たときは、トランザクションのコマンドに対応して内部メモリにアクセスします。その後、mEndRequestEvent.notify(t+ACCEPT_DELAY)を実行し、
endRequestメソッドを起動します。tはmyNBTransportメソッドに引数でInitiatorクラスからこのTargetクラスまでに加算された時間になり、ACCEPT_DELAYはSimpleATTarget1クラスのリクエスト開始からリクエスト終了までの時間(25ns)になります。つまり、t+25ns時間後にendRequestメソッドが呼び出される。
そして、戻り値としてtlm::TLM_ACCEPTを返します。

endRequestメソッドでは、Initiatorクラスに対して、phaseをtlm::END_REQにしてnb_transportメソッドを実行します。これにより、Initiatorクラスはリクエストが終了することになります。
その後、mBeginResponseEvent.notify(RESPONSE_DELAY)を実行し、RESPONSE_DELAY後にレスポンス開始のためのbeginResponseメソッドが起動されます。

beginResponseメソッドでは、リクエストランザクションがリード(tlm::TLM_READ_COMMAND)のときは、レスポンストランザクションのデータにメモりデータをコピーし、Initiatorクラスに対して、nb_transportメソッドを実行します。戻り値がtlm::TLM_COMPLETEDのときは、mEndResponseEvent.notify(t)を実行し、endResponseメソッドが起動されます。

Initiatorクラスからリクエスト開始(tlm::BEGIN_RES)が来たときは、mEndResponseEvent.notify(t)を実行し、戻り値としてtlm::TLM_COMPLETEDを返します。