TLM2.0 Socketの種類を把握して有効に使おう

TLM2.0モデルのインターフェースになる Socket についてです。

Socketとは何か?

TLM2.0のコアインターフェース(トランスポート、DMI、デバッグトランスポート)の関数呼び出しを提供し、それらのインターフェースをグループ化してポートよりも使いやすくしたもの。

Socketを使う利点

  • TLM2.0のコアインターフェース(トランスポート、DMI、デバッグトランスポート)をまとめて利用できる。
  • 1回の記述でフォワードパスとバックワードパスとを接続できる。
  • コンパイラによる接続チェックがサポートされている。
  • バス幅をパラメータとして指定できる。
  • ソケットを使用せず、ポートのみでコアインターフェースを利用することは推奨しない(相互運用性)

Socketの種類

様々なソケットを用意されています。
これは、用途に応じて使い分けると良いと思います。

クラス コールバック関数の登録 マルチポート b⇔nb変換 タグ付き
tlm_initiator_socket × - ×
tlm_target_socket × × ×
以下、tlm_utilsにて提供
simple_initiator_socket × - ×
simple_initiator_socket_tagged × -
simple_target_socket × ×
simple_target_socket_tagged ×
passthrough_target_socket × × ×
passthrough_target_socket_tagged × ×
multi_passthrough_initiator_socket -
multi_passthrough_target_socket ×
※b⇔nb変換:ブロッキングAPIとノンブロッキングAPIの自動変換

標準Socketよりは tlm_utilsのほうがオススメ

サンプルとして、ターゲット(スレーブ)側の動作について記載します。
※あくまでサンプルなので、自己責任でお願いします。

  • tlm_target_socketを使う記述例
#include <stdlib.h>
#include <vector>
using namespace std;

#include <systemc.h>
#include <tlm.h>
using namespace tlm;

SC_MODULE( hoge ),tlm_fw_transport_if<> {

  vector<unsigned int> mem;
  unsigned int         mem_size;

  void b_transport(tlm_generic_payload&, sc_time&);
  tlm_sync_enum nb_transport_fw(tlm_generic_payload&, tlm_phase&, sc_time&);
  unsigned int transport_dbg(tlm_generic_payload&);
  bool get_direct_mem_ptr(tlm_generic_payload&, tlm_dmi&);

  tlm_target_socket<32> target_socket;

  SC_CTOR( hoge )
    : target_socket("target_socket")
  {
    target_socket.bind( *this );
    mem_size = 0x1000;
    mem.resize(mem_size);
    for(unsigned int i=0; i<mem_size; i++){ mem[i] = 0; }
  }
};

inline void hoge::b_transport(tlm_generic_payload& trans, sc_time& delay){

  sc_dt::uint64  adr  = trans.get_address() / 4;
  unsigned char* ptr  = trans.get_data_ptr();
  unsigned int   len  = trans.get_data_length();
  unsigned char* byt  = trans.get_byte_enable_ptr();
  unsigned int   blen = trans.get_byte_enable_length();
  unsigned int   wid  = trans.get_streaming_width();

  delay += SC_ZERO_TIME;

  if ( adr >= sc_dt::uint64(mem_size) ) {
    trans.set_response_status( tlm::TLM_ADDRESS_ERROR_RESPONSE );
    return;
  }
  if ( wid < len ) {
    trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
    return;
  }

  if (trans.is_read()) {
   if ( byt != 0 ) {
     for ( unsigned int i = 0; i < len; i++ )
       if ( byt[i % blen] == TLM_BYTE_ENABLED ) {
         ptr[i] = (mem[adr+i/4] >> ((i&3)*8)) & 0xFF;
       }
   } else {
     memcpy(ptr, &mem[adr], len);
   }
  } else if (trans.is_write()) {
   if ( byt != 0 ) {
     for ( unsigned int i = 0; i < len; i++ )
       if ( byt[i % blen] == TLM_BYTE_ENABLED ) {
         unsigned char temp[4];
         memcpy (temp, &mem[adr+i/4], 4);
         memcpy (&temp[i&3], &ptr[i], 1);
         memcpy (&mem[adr+i/4], temp, 4);
       }
   } else {
     memcpy( &mem[adr], ptr, len);
   }
  }
  trans.set_dmi_allowed(false);
  trans.set_response_status( tlm::TLM_OK_RESPONSE );

}

inline tlm_sync_enum hoge::nb_transport_fw(tlm_generic_payload& trans, tlm_phase& phase, sc_time&
delay){
  return tlm::TLM_COMPLETED;
}

inline unsigned int hoge::transport_dbg(tlm_generic_payload& trans){
  return 0;
}

inline bool hoge::get_direct_mem_ptr(tlm_generic_payload& trans, tlm_dmi& dmi){
  return false;
}


  • simple_target_socketを使う記述例
#include <stdlib.h>
#include <vector>
using namespace std;

#include <systemc.h>
#include <tlm.h>
#include "tlm_utils/simple_target_socket.h"
using namespace tlm;

SC_MODULE( hoge ) {

  vector<unsigned int> mem;
  unsigned int         mem_size;

  void my_b_transport(tlm_generic_payload&, sc_time&);

  tlm_utils::simple_target_socket<hoge, 32> target_socket;

  SC_CTOR( hoge )
    : target_socket("target_socket")
  {

    target_socket.register_b_transport(this, &hoge::my_b_transport);

    mem_size = 0x1000;
    mem.resize(mem_size);
    for(unsigned int i=0; i<mem_size; i++){ mem[i] = 0; }
  }
};

inline void hoge::my_b_transport(tlm_generic_payload& trans, sc_time& delay){

  sc_dt::uint64  adr  = trans.get_address() / 4;
  unsigned char* ptr  = trans.get_data_ptr();
  unsigned int   len  = trans.get_data_length();
  unsigned char* byt  = trans.get_byte_enable_ptr();
  unsigned int   blen = trans.get_byte_enable_length();
  unsigned int   wid  = trans.get_streaming_width();

  delay += SC_ZERO_TIME;

  if ( adr >= sc_dt::uint64(mem_size) ) {
    trans.set_response_status( tlm::TLM_ADDRESS_ERROR_RESPONSE );
    return;
  }
  if ( wid < len ) {
    trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
    return;
  }

  if (trans.is_read()) {
   if ( byt != 0 ) {
     for ( unsigned int i = 0; i < len; i++ )
       if ( byt[i % blen] == TLM_BYTE_ENABLED ) {
         ptr[i] = (mem[adr+i/4] >> ((i&3)*8)) & 0xFF;
       }
   } else {
     memcpy(ptr, &mem[adr], len);
   }
  } else if (trans.is_write()) {
   if ( byt != 0 ) {
     for ( unsigned int i = 0; i < len; i++ )
       if ( byt[i % blen] == TLM_BYTE_ENABLED ) {
         unsigned char temp[4];
         memcpy (temp, &mem[adr+i/4], 4);
         memcpy (&temp[i&3], &ptr[i], 1);
         memcpy (&mem[adr+i/4], temp, 4);
       }
   } else {
     memcpy( &mem[adr], ptr, len);
   }
  }
  trans.set_dmi_allowed(false);
  trans.set_response_status( tlm::TLM_OK_RESPONSE );

}

コメント

記述としては、極端すぎるような感じもしますが上記のような感じです。
simple_initiator/target_socketを使い方やメリットとしては、

  • tlm_utilsのヘッダーファイルを includeする必要がある。
  • tlm_fw_transport_if などの継承が要らない。
  • 必要なものだけ登録(例えば、register_b_transport)すれば良い。

などがあると思います。
個人的には、記述する際にはこちらのほうが色々とお得かと思います。