VMM : TLM

PortとExport

PortとExportは、SystemC 2.1で導入されたもの。VMM 1.2では、OSCIのTLM-2.0をサポートしているので、PortとExportもサポートしています。

では、どのような記述になるのか?それを説明しているのが、 The Curious World of Ports and Exports in VMM 1.2です。

Port : vmm_tlm_b_transport_port #(producer, my_tx) m_port;

Export : vmm_tlm_b_transport_export #(consumer, my_tx) m_export;

のようになります。

Port/Exportのバインドは、connect_ph関数(VMM 1.2で関数名が変わりました)で行います。

virtual function void connect_ph;

m_producer.m_port.tlm_bind( m_consumer.m_export );

endfunction

SystemCでTLM-2.0を使っているのであれば、それほど問題は無いでしょう!

また、VMM 1.2のTLM-2.0をマスターすれば、SystemCのTLM-2.0も使えるようになるわけです。

b_transportタスク

Port/Export間でのデータ転送には、b_transportタスクを使います。

Port側のtransportタスクとExport側のb_transportタスクでは、引数が違います。

Port側は、

m_port.b_transport(tx, delay);

で、Portのインスタンス(m_port)のタスクとして呼び出されます。

引数は、トランザクションデータ(Tx)とディレイ(delay)です。

一方、Export側は、

task b_transport(int id = -1, my_tx trans, ref int delay);

で、タスクとして実装します。第1引数として(id)があります。

このidはどこから来たトランザクションデータであるかを区別するために使います。

b_transportタスクは、引数が違うということを覚えておきましょう!

tlm_bind

VMMでのPortとExportでは、vmm_tlm_b_transport_portクラスとvmm_tlm_b_transport_exportクラスを

tlm_bindで接続することを説明しました。

virtual function void connect_ph;

m_producer.m_port.tlm_bind( m_consumer.m_export );

endfunction

このtlm_bindの定義を見てみましょう。

function void vmm_tlm_port_base::tlm_bind(

vmm_tlm_export_base#(DATA,PHASE) peer,

int id = -1,

string fname = "",

int lineno = 0);

第一引数のpeerは、vmm_tlm_export_base#(DATA,PHASE)型です。

先に説明したイニシエータのPortとターゲットのExportを接続するときのターゲットのExportになります。

第二引数のidは、ターゲットに接続するイニシエータを区別するための識別子(id)になります。

通常はデフォルト値(-1)が設定されているので、指定する必要はありません。

第三引数と第四引数はデバック用に使われるものです。

第二引数のidを使って、複数のイニシエータを1つのターゲットに接続し、ターゲット側では送られてくるトランザクションをこのidを使って区別します。

VMM 1.2 User GuideのPage.A-868の例題を見てみましょう。

class producer extends vmm_xactor;

vmm_tlm_b_transport_port#(producer) b_port = new(this,"producer port");

endclass

class consumer extends vmm_xactor;

vmm_tlm_b_transport_export#(consumer) b_export = new(this,"consumer export");

endclass

class my_env extends vmm_group;

producer p[4];

consumer c;

function void connect_ph();

foreach(p[i]) begin

p[i].b_port.tlm_bind(c.b_export, i);

end

endfunction

endclass

my_envクラスのconnect_ph関数で4つのproducerのインスタンス(p[4])を

1つのconsumerのインスタンス(c)にtlm_bindを使ってバインドしています。

このとき、第二引数に各producerのidを割り当てています。

第二引数に割り当てられる値は正の整数です。負の整数を与えると、デフォルト値と同じ動きをします。

そして、このidは、b_transportの第一引数になります。

task b_transport(int id = -1, my_tx trans, ref int delay);

tlm_unbind

tlm_bindは、PortとExportを接続するための関数です。

一方、tlm_unbindは既に接続してあるものを解放するための関数です。

tlm_unbindを使うことで動的にPortとExportの接続ができることになります。

VMM 1.2 User GuideのPage.A-871の例題を見てみましょう。

class my_env extends vmm_group;

producer p1;

consumer c1, c2;

function void connect_ph();

p1.b_port.tlm_bind(c1.b_export);

endfunction

endclass

class test2 extends vmm_test;

function void configure_test_ph();

env.p1.b_port.tlm_unbind();

env.p1.b_port.tlm_bind(c2.b_export);

endfunction

endclass

test2クラスのconfigure_test_ph関数でtlm_unbindとtlm_bindでc1.b_exportからc2.b_exportに接続し直しています。

vmm_tlm_b_transport_exportマクロ

tlm_bindを使うことでExportに複数のPortを接続することができます。同じようなことは、vmm_tlm_b_transport_exportマクロでも可能です。

vmm_tlm_b_transport_exportマクロは、sv/std_lib/vmm_tlm_macros.svで次のように定義されています。

`define vmm_tlm_b_transport_export(SUFFIX) \

class vmm_tlm_b_transport_export``SUFFIX #(type TARGET = vmm_xactor, type DATA = vmm_data) extends vmm_tlm_export_base#(DATA); \

static local vmm_object _obj; \

local TARGET m_parent; \

\

function new(TARGET parent, string name, int max_binds = 1, int min_binds = 0); \

super.new(($cast(_obj, parent)) ? _obj: null , name, max_binds, min_binds,((parent != null) && $cast(_obj,parent)) ? _obj.get_log() : null); \

this.m_parent = parent; \

endfunction: new \

\

virtual task b_transport(int id = -1, DATA trans, ref int delay); \

if(this.child_export == null) \

this.m_parent.b_transport``SUFFIX(id, trans, delay); \

else \

this.child_export.b_transport(id,trans,delay); \

endtask : b_transport \

\

endclass : vmm_tlm_b_transport_export``SUFFIX

vmm_tlm_b_transport_exportマクロでは、vmm_tlm_b_transport_exportSUFFIXという新しいクラスを定義しています。

つまり、各イニシエータ(Port)に対して接続するためのExportのクラスを定義する訳です。

class consumer extends vmm_xactor;

`vmm_tlm_b_transport_export(_1)

`vmm_tlm_b_transport_export(_2)

vmm_tlm_b_transport_export_1#(consumer) b_export1 = new(this, "export1");

vmm_tlm_b_transport_export_2#(consumer) b_export2 = new(this,"export2");

task b_transport_1(int id = -1, vmm_data trans, ref int delay );

trans.display("From export1");

endtask

task b_transport_2(int id = -1, vmm_data trans, ref int delay);

trans.display("From export2");

endtask

endclass

class producer extends vmm_xactor;

vmm_tlm_b_transport_port#(producer) b_port;

endclass

class my_env extends vmm_group;

producer p1,p2;

consumer c1;

function void connect_ph();

c1.b_export1.tlm_bind(p1.b_port);

c1.b_export2.tlm_bind(p2.b_port);

endfunction

endclass

vmm_tlm_b_transport_exportマクロを使って、2つのExport(b_export1,b_export2)をそれぞれ

p1p2にtlm_bindを使って接続しています。

vmm_tlm_analysis_portクラス

vmm_tlm_analysis_portクラスは、オブザーバークラス(スコアボードやカバレッジ)へのトランザクションをブロードキャストする場合に使います。

vmm_tlm_analysis_portクラスの定義は、次のようになります。

vmm_tlm_analysis_portクラスは、2つのパラメータ(INITIATORDATA)を持ちます。

class vmm_tlm_analysis_port#(type INITIATOR = vmm_tlm_xactor,

type DATA = vmm_data)

extends vmm_tlm_analysis_port_base#(DATA);

vmm_tlm_analysis_portクラスは、次のようにトランザクタクラスの中でインスタンス化します。

class tlm_consumer extends vmm_xactor;

vmm_tlm_analysis_port#(tlm_consumer, my_trans) analysis_port

= new(this,"consumer_analysis");

function b_transport(int id=-1,my_trans trans, ref int delay);

this.analysis_port.write(trans);

endfunction

endclass

この例では、INITIATORをデフォルトのvmm_tlm_xactorクラスからtlm_comsumerクラスに変えています。

また、DATAをデフォルトのvmm_dataクラスからmy_transクラスに変えています。

b_transportタスクの中で、write関数にてトランザクションデータ(trans)をブロードキャストしています。

vmm_tlm_analysis_portクラスは、vmm_tlm_b_transport_portクラスと同じようにtlm_bindとtlm_unbindが使えます。

vmm_tlm_analysis_exportクラス

vmm_tlm_analysis_portクラスのインスタンスと接続できるのは、vmm_tlm_analysis_portクラスのインスタンスです。

このことは、vmm_tlm_b_transport_portクラスとvmm_tlm_b_transport_exportクラスの関係と同じです。

vmm_tlm_analysis_exportクラスの定義は、vmm_tlm_analysis_portクラスと基本的には同じです。

class vmm_tlm_analysis_export#(type T = vmm_tlm_xactor,

type D = vmm_data)

extends vmm_tlm_analysis_export_base#(D);

vmm_tlm_analysis_portクラスと同様に、vmm_tlm_analysis_exportクラスでも、2つのパラメータ(TDを持ちます。

デフォルトでは、Dはvmm_tlm_xactorクラス、Tはvmm_dataクラスです。

vmm_tlm_analysis_exportクラスは、次のように、vmm_tlm_alalysis_portクラスからトランザクションを受け取るクラスの中にインスタンス化します。

class tlm_scoreboard extends vmm_group;

vmm_tlm_analysis_export#(tlm_scoreboard,my_trans) analysis_export

= new(this,"scb_analysis");

function write(int id=-1, my_trans trans);

endfunction

endclass

この例では、Tをデフォルトのvmm_tlm_xactorクラスからtlm_scoreboardクラスに、

また、Dをデフォルトのvmm_dataクラスからmy_transクラスに変えています。

そして、write関数も定義しています。

vmm_tlm_analysis_exportマクロ

オブザーバークラスが複数のvmm_tlm_analysis_exportクラスのインスタンスを持つことはできません。

なぜなら、write関数が複数ポートに対応できないためです。

そこで登場するのは、vmm_tlm_analysis_exportマクロです。クラスではありません。

class tlm_scoreboard2 extends vmm_group;

`vmm_tlm_analysis_export(_1)

`vmm_tlm_analysis_export(_2)

vmm_tlm_analysis_export_1#(tlm_scoreboard2,my_trans) scb1;

vmm_tlm_analysis_export_2#(tlm_scoreboard2,my_trans) scb2;

function write_1 (int id=-1,my_trans trans);

`vmm_note(log, $psprintf("Received %s from %0d", Trans.psdisplay(""), id);

endfunction

function write_2 (int id=-1,my_trans trans);

`vmm_note(log, $psprintf("Received %s from %0d", Trans.psdisplay(""), id);

endfunction

endclass

vmm_tlm_analysis_exportマクロを使って、2つのExportを定義します。

定義したExportのクラス名は、vmm_tlm_analysis_export_1とvmm_tlm_analysis_export_2になります。

vmm_tlm_analysis_exportマクロの引数として指定した_1と_2がvmm_tlm_analysis_portの後に追加されたことになります。

この2つのクラスに対して、scb1とscb2というポートをインスタンス化します。

(ここでは、new関数でインスタンス化する部分はありませんが)

各ポートに対応するwrite関数であるwrite_1とwrite_2も定義します。

_1と_2になるのは、vmm_tlm_analysis_exportマクロの引数として_1と_2を指定しているからです。

vmm_tlm_analysis_exportクラスとPeer IDs

オブザーバークラスが複数のAnalysis Exportをサポートするには、vmm_tlm_analysis_exportマクロを使う例では、クラスのインスタンスを持つことはできません。

これに対して、vmm_tlm_analysis_exportマクロを使わない方法があります。

それは、オブザーバークラス内に定義するwrite関数の第1引数のidを使うというものです。

class tlm_scoreboard_x extends vmm_object;

vmm_tlm_analysis_export#( tlm_scoreboard_2, my_trans ) scb_analysis

= new(this,”scoreboard_analysis”, 2, 0);

virtual function write(int id= -1, my_trans trans);

case(id)

0: do_compare_from_port0(trans);

1: do_compare_from_port1(trans);

endcase

endfunction

endclass: scoreboard

write関数の中でdの値によって処理する内容を変えます。

こうすることで、複数のAnalysis Exportをサポートできます。

では、write関数のidの値はどのようにして割り当てるのでしょうか?

それは、次のようにAnalysis PortとAnalysis Exportをバインドするときに割り当てます。

class my_env extends vmm_group;

tlm_monitor mon[2];

tlm_scoreboard scb;

virtual function void connect_ph();

mon[0].analysis_port.tlm_bind(scb.scb_analysis, 0);

mon[1].analysis_port.tlm_bind(scb.scb_analysis, 1);

endfunction : build

endclass: my_env

tlm_bind関数の第2引数への値がwrite関数の第1引数のidに渡されます。

mon[0]からのトランザクションは、id=0になり、

mon[1]からのトランザクションは、id=1になります。