For Good FPGA Design

UVMのTLMとは?(4) analysis_portとanalysis_export

第2回~第4回では、UVMにおける、TLMによるコンポーネント間の通信の例を示します。今回は、analysis_port/analysis_exportを使用した例を示します。TLMの解説は、第1回 解説編をご覧ください。

関連記事

なお、ソースコードはGitHubに公開しています。

目次

トランザクション

トランザクションはuvm_sequence_itemを継承して作成します。第1回 解説編 の記事で解説したものと同じものを使います。

(1) simple_transaction.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class simple_transaction extends uvm_sequence_item;
rand bit[7:0] data_e;
rand bit[7:0] data_o;
 
constraint CE { data_e[0] == 1'b0; }    // Even number
constraint CO { data_o[0] == 1'b1; }    // Odd number
 
`uvm_object_utils_begin(simple_transaction)
    `uvm_field_int(data_e, UVM_DEFAULT)
    `uvm_field_int(data_o, UVM_DEFAULT)
`uvm_component_utils_end
 
function new (string name = "simple_transaction");
    super.new(name);
endfunction
 
endclass

コンポーネント

UVMのコンポーネントは3種類作成します。

  • analysis_producer: Analysis portを持ちます。write( )を呼び、Write動作を発生させます。Analysis portは1対Nの接続で使います。analysis_producerは1側です
  • analysis_subscriber: Analysis exportを持ちます。write( )の処理を定義します。analysis_subscriberは1対N接続のN側です。今回は、2個のインスタンスを生成して接続してみます
  • analysis_parent: analysis_producerのAnalysis portと、analysis_subscriberのAnalysis exportを接続します
図1 analysis_port/analysis_export

(2) analysis_producer.sv

uvm_analysis_port#(T)により、analysis_portを定義します。トランザクションを生成し、analysis_port.write( )を呼び、Write動作を発生させます。19行目です。

実際のUVM環境では、collectorやmonitorに対してanalysis_portを実装することになります。これらは、DUT (検証対象の回路) の信号を収集してトランザクションを生成します。今回は簡単のため、analysis_producer内でrandomizeによりトランザクションの内容をつくります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class analysis_producer extends uvm_component;
    uvm_analysis_port #(simple_transaction) analysis_port;
    `uvm_component_utils(analysis_producer)
    function new(string name, uvm_component parent);
        super.new(name, parent);
        analysis_port = new("analysis_port", this);
    endfunction
    task run_phase(uvm_phase phase);
        simple_transaction data;
        for (int i = 0; i < 5; i++) begin
            `uvm_info("PRODUCER",
                $sformatf("Producing data... i = %d", i),
                UVM_LOW)
            data = simple_transaction::type_id::create("data");
            assert (data.randomize());
            `uvm_info("PRODUCER", 
                $sformatf("Write: data_e = %d, data_o = %d", data.data_e, data.data_o),
                UVM_LOW)
            analysis_port.write(data);             // Initiate writing a transaction
        end
    endtask
endclass

(3) analysis_subscriber.sv

uvm_analysis_imp#(T, IMP)により、analysis_exportを定義します。function void write( )に、Writeされた時の動作を定義します。今回の”動作”は、単に受け取ったトランザクションの内容をプリント(`uvm_info)するだけです。subscriberは、1対N接続のN側なので、時間を消費できません。したがって、functionでwrite( )が定義されていることに注意してください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class analysis_subscriber extends uvm_component;
    uvm_analysis_imp #(simple_transaction, analysis_subscriber) analysis_export;
 
    `uvm_component_utils(analysis_subscriber)
 
    function new(string name, uvm_component parent);
        super.new(name, parent);
        analysis_export = new("analysis_export", this);
    endfunction
 
    function void write(simple_transaction data);    // Define behavior of write() in function
        `uvm_info(get_name(),                        // Just print received data with name of the object
            $sformatf("Got: data_e = %d, data_o = %d", data.data_e, data.data_o),
            UVM_LOW)
    endfunction
 
endclass

(4) analysis_parent.sv

build_phaseでanalysis_producerとanalysis_subscriberをインスタンスし、connect_phaseでanalysis_portとanalysis_exportを接続します。24, 25行目です。

今回は、subscriber1とsubscriber2の2つのサブスクライバーをインスタンスします。そして、1つのAnalysis portに対して、2つのAnalysis exportを接続します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class analysis_parent extends uvm_component;
    analysis_producer producer;
    analysis_subscriber subscriber1;
    analysis_subscriber subscriber2;
 
    `uvm_component_utils(analysis_parent)
 
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
 
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
 
        producer = analysis_producer::type_id::create("producer", this);
        subscriber1 = analysis_subscriber::type_id::create("subscriber1", this);
        subscriber2 = analysis_subscriber::type_id::create("subscriber2", this);
 
    endfunction
 
    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
 
        producer.analysis_port.connect(subscriber1.analysis_export);
        producer.analysis_port.connect(subscriber2.analysis_export);
    endfunction
 
endclass

パッケージ

(5) pkg.sv

作成したすべてのclassをpackageにしておきます。次のtop.svのように、importして使うことで、すっきりした記述になります。

1
2
3
4
5
6
7
8
9
10
11
12
13
package pkg;
    `include "uvm_macros.svh"
    import uvm_pkg::*;
 
    /* Include transactions */
    `include "simple_transaction.sv"
 
    /* Include classes */
    `include "analysis_producer.sv"
    `include "analysis_subscriber.sv"
    `include "analysis_parent.sv"
 
endpackage

トップモジュール

(6) top.sv

analysis_parentのインスタンスを生成し、run_testします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
`include "uvm_macros.svh"
`include "pkg.sv"
 
module top();
import uvm_pkg::*;
import pkg::*;
 
analysis_parent parent;
 
initial begin
    `uvm_info("INFO", "get port test", UVM_LOW)
 
    parent = analysis_parent::type_id::create("parent", null);
 
    run_test();
end
endmodule

実行

Vivado Simulatorで動作します。実行の手順はこちらの記事をご覧ください。QuestaとModelSimで行うためには有料版が必要です。randomize( )機能は有料版でないと使えないようです。

結果

Printされた内容はTcl Consoleに出力されます。analysis_producerがwrite( )したトランザクションを、subscriber1とsubscriber2がそれぞれ受け取り、プリントしています。実際のUVM環境では、subscriber1は期待値との比較によるチェック、subscriber2はカバレッジ計算…というように使います。


UVM_INFO C:/sim_1/analysis_producer.sv(21) @ 0: parent.producer [PRODUCER] Producing data… i = 0
UVM_INFO C:/sim_1/analysis_producer.sv(28) @ 0: parent.producer [PRODUCER] Write: data_e = 248, data_o = 223
UVM_INFO C:/sim_1/analysis_subscriber.sv(18) @ 0: parent.subscriber1 [subscriber1] Got: data_e = 248, data_o = 223
UVM_INFO C:/sim_1/analysis_subscriber.sv(18) @ 0: parent.subscriber2 [subscriber2] Got: data_e = 248, data_o = 223
UVM_INFO C:/sim_1/analysis_producer.sv(21) @ 0: parent.producer [PRODUCER] Producing data… i = 1
UVM_INFO C:/sim_1/analysis_producer.sv(28) @ 0: parent.producer [PRODUCER] Write: data_e = 170, data_o = 145
UVM_INFO C:/sim_1/analysis_subscriber.sv(18) @ 0: parent.subscriber1 [subscriber1] Got: data_e = 170, data_o = 145
UVM_INFO C:/sim_1/analysis_subscriber.sv(18) @ 0: parent.subscriber2 [subscriber2] Got: data_e = 170, data_o = 145
UVM_INFO C:/sim_1/analysis_producer.sv(21) @ 0: parent.producer [PRODUCER] Producing data… i = 2
UVM_INFO C:/sim_1/analysis_producer.sv(28) @ 0: parent.producer [PRODUCER] Write: data_e = 114, data_o = 119
UVM_INFO C:/sim_1/analysis_subscriber.sv(18) @ 0: parent.subscriber1 [subscriber1] Got: data_e = 114, data_o = 119
UVM_INFO C:/sim_1/analysis_subscriber.sv(18) @ 0: parent.subscriber2 [subscriber2] Got: data_e = 114, data_o = 119

…略…


まとめ

今回は、analysis_port/analysis_exportの使用例を示しました。ポートの生成、write( )メソッドの定義、ポートの接続については、参考にしていただけると思います。

アバター画像
この記事を書いた人
ジーノ。大手電機メーカーで、基板設計の全般と、FPGAの設計に従事した経験を活かし、FPGAについて情報発信中。
RTL設計、シミュレーション、タイミング・クロージャ、FPGAまわりのハードウェア開発まで、幅広く取り扱っております。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA