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 | ○ | ○ | × | ○ |
標準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)すれば良い。
などがあると思います。
個人的には、記述する際にはこちらのほうが色々とお得かと思います。