XilinxのAXI Verification IPを試す。

## はじめに
先日、Vivado 2017.1が[リリースされました。](https://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2017_1/ug973-vivado-release-notes-install-license.pdf)(PDF)
その中で、AXI Verification IP(AXI VIP)が正式かつ無償でリリースされたので早速試してみることにしました。

## AXI VIPの構成
概要は、[marseeさんが書かれております](http://marsee101.blog19.fc2.com/blog-entry-3781.html)ので、そちらをご覧下さい。
AXI VIPは、SystemVerilogを使用し、`class`や`interface`をふんだんに使ったTransactionベースの検証コンポーネントで構成されております。よって、ModelSim ASEのような他社の無償ツールでは使用できません。Vivado Simulatorを使用することになります。
[AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017](https://japan.xilinx.com/support/documentation/ip_documentation/axi_vip/v1_0/pg267-axi-vip.pdf)(PDF)の56ページのAXI Master Agentの図を引用します。
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/32925/c19aa1ef-6ca7-932d-6f2c-5ccc914b272f.png)
これはMasterの図になりますが、上図のように、AXI VIPはMaste/Slave/Pass-ThroughでそれぞれAgentと呼ばれる単位でまとめられており、それぞれライト用のドライバ(Driver)、リード用のドライバ、モニター(Monitor)機能が内包されています。
ドライバによってAXIバス経由で検証対象(DUT)へアクセス、モニタによってDUTのプロトコルチェック等を行っているようです。

## AXI VIPの使用
CentOS 7環境にVivado 2017.1をインストールし、試してみました。
###Example Designを生成してみる
Vivado 2017.1を起動し、任意の空プロジェクトを作成し、IP Catalogから「AXI Verification IP」を選択して、デフォルトのままDesign Sourceに登録し、「Open IP Example Design」を選択します。
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/32925/a65f24ec-0a49-0f60-48ce-3de94e140091.png)

選択すると、Example Designのプロジェクトが立ち上がります。Example DesignはBlock DesignにMaste/Slave/Pass-ThroughのAXI VIPをつながった構成となっています。
プロジェクトおよびはBlock Design名は「ex_sim」となっており、これが各ファイルのプリフィックスとなっているようです。
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/32925/5a57f19a-5f40-a931-7da8-9bfcb8193d4d.png)

プロジェクトによって生成されたファイルを調べると、axi_vip_0_ex.ip_user_files/sim_scripts/ex_sim にVivado Simulatorをはじめ、各社のシミュレータに対応したスクリプトファイルが生成されます。
ただしこのスクリプトは、Block Designをコンパイル・エラボレーションまでのスクリプトのようです。
また、imports ディレクトリには、上記Block Designに合わせた様々なテストベンチサンプルが置いてあります。~~個人的には雑多な印象を受けるので、Product Guideを確認して目的に合ったサンプルを調べてみて下さい。~~

[Product Guide](https://japan.xilinx.com/support/documentation/ip_documentation/axi_vip/v1_0/pg267-axi-vip.pdf)(PDF)のChapter 6に、Example Designに関して説明がされております。
Example Designでは、下図のように、「sim_basic～」と「sim_adv～」という名前で計10セットのシミュレーションセットが生成されます。

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/32925/cfa57398-ce36-4479-93ff-a38ca2d35ec1.png)

これは、Maste/Slave/Pass-ThroughのAXI VIPを組み合わせたシミュレーションセットのようです。ここではその内の一つである「sim_basic_mst_active_pt_slv_slv_passive」を使ってシミュレーションを実施してみます。
構成はProduct Guideから引用した下図の通りで、Master VIPがPass-Through VIPにライトリードアクセスを行います。Slave VIPは使用しません。

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/32925/047c1377-0b12-15b4-bd6c-12f61adb580c.png)

目的のシミュレーションセットを右クリックして「Run Simulation」→「Run Behavioral Simulation」を選択します。
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/32925/c09eb262-3a17-da68-d2cc-150bc7690e6e.png)

Tclコンソールには以下のようなログが流れ、メッセージの最後を見ると、AXI VIPが動作していることがわかります。

```tcl
source /tmp/axi_vip_0_ex/axi_vip_0_ex.sim/sim_basic_mst_active__pt_slv__slv_passive/behav/xsim.dir/axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive_behav/webtalk/xsim_webtalk.tcl -notrace
INFO: [Common 17-186] '/tmp/axi_vip_0_ex/axi_vip_0_ex.sim/sim_basic_mst_active__pt_slv__slv_passive/behav/xsim.dir/axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive_behav/webtalk/usage_statistics_ext_xsim.xml' has been successfully sent to Xilinx on Mon Apr 24 19:02:09 2017. For additional details about this file, please refer to the WebTalk help file at /usr/tools/xilinx/Vivado/2017.1/doc/webtalk_introduction.html.
INFO: [Common 17-206] Exiting Webtalk at Mon Apr 24 19:02:09 2017...
run_program: Time (s): cpu = 00:00:13 ; elapsed = 00:00:07 . Memory (MB): peak = 6089.344 ; gain = 0.000 ; free physical = 9095 ; free virtual = 28142
INFO: [USF-XSim-69] 'elaborate' step finished in '8' seconds
INFO: [USF-XSim-4] XSim::Simulate design
INFO: [USF-XSim-61] Executing 'SIMULATE' step in '/tmp/axi_vip_0_ex/axi_vip_0_ex.sim/sim_basic_mst_active__pt_slv__slv_passive/behav'
INFO: [USF-XSim-98] *** Running xsim
   with args "axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive_behav -key {Behavioral:sim_basic_mst_active__pt_slv__slv_passive:Functional:axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive} -tclbatch {axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive.tcl} -log {simulate.log}"
INFO: [USF-XSim-8] Loading simulator feature
Vivado Simulator 2017.1
Time resolution is 1 ps
source axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive.tcl
# set curr_wave [current_wave_config]
# if { [string length $curr_wave] == 0 } {
#   if { [llength [get_objects]] > 0} {
#     add_wave /
#     set_property needs_save false [current_wave_config]
#   } else {
#      send_msg_id Add_Wave-1 WARNING "No top level signals found. Simulator will start without a wave window. If you want to open a wave window go to 'File->New Waveform Configuration' or type 'create_wave_config' in the TCL console."
#   }
# }
# run 1000ns
XilinxAXIVIP: Found at Path: axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive.DUT.ex_design.axi_vip_mst.inst
XilinxAXIVIP: Found at Path: axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive.DUT.ex_design.axi_vip_passthrough.inst
This AXI VIP is in passthrough mode
XilinxAXIVIP: Found at Path: axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive.DUT.ex_design.axi_vip_slv.inst
INFO: [USF-XSim-96] XSim completed. Design snapshot 'axi_vip_0_exdes_basic_mst_active__pt_slv__slv_passive_behav' loaded.
INFO: [USF-XSim-97] XSim simulation ran for 1000ns
launch_simulation: Time (s): cpu = 00:00:18 ; elapsed = 00:00:11 . Memory (MB): peak = 6182.926 ; gain = 93.582 ; free physical = 9059 ; free virtual = 28115
```
ここでは省きますが、波形を見ると、AXI VIP同士でAXIバスを通じてアクセスしていることがわかりました。


###自分でインスタンスしてみる
Example DesignはBlock Designの中にVIPを3個並べて動かすという個人的にはやや奇異な構成となっており、具体的に理解を深めるため、自分でインスタンスしてテストベンチを作成してみました。

プロジェクト名はデフォルトの「project_1」です。
Block Designを以下に示します。
AXIマスターモデルにVIPを使用し、AXIスレーブにVivadoのユーティリティで自動生成できるPeripheralモジュールを使ってみました。
![無題.png
](https://qiita-image-store.s3.amazonaws.com/0/32925/77785035-86bc-dca2-3b77-805797a74ff2.png)

Block Design名は「sys」としました。また、Block Designを含んだ論理合成でおなじみの「Create HDL Wrapper」を使用したため、トップモジュール名は「sys_wrapper」となります。

これらをインスタンスしたテストベンチファイルを以下に示します。

```sv:tb.sv
`timescale 1ns/1ps
import axi_vip_v1_0_1_pkg::*;
import sys_axi_vip_0_0_pkg::*;

module tb();

    localparam int LP_CLK_PERI = 100;
    localparam int LP_RST_PERI = 777;

    // DUT instance
    logic aresetn, aclk;
    sys_wrapper dut(.*);

    task rst_gen();
        aresetn = '0;
        #(LP_RST_PERI);
        aresetn = '1;
    endtask

    task clk_gen();
        aclk = '0;
        forever #(LP_CLK_PERI/2) aclk = ~aclk;
    endtask

    task clk_dly(int n);
        repeat(n) @(posedge aclk);
    endtask

    // VIP decreation
    sys_axi_vip_0_0_mst_t   agent;

    task init_agent();
        agent = new("master vip agent", dut.sys_i.axi_vip_0.inst.IF);
        agent.start_master();
    endtask

    // Transaction method
    task wr_tran();
        axi_transaction  wr_transaction;
        wr_transaction   = agent.wr_driver.create_transaction( "write transaction with randomization");
        WR_TRANSACTION_FAIL: assert(wr_transaction.randomize());
        agent.wr_driver.send(wr_transaction);
    endtask

    task rd_tran();
        axi_transaction  rd_transaction;
        rd_transaction   = agent.rd_driver.create_transaction("read transaction with randomization");
        RD_TRANSACTION_FAIL_1a:assert(rd_transaction.randomize());
        agent.rd_driver.send(rd_transaction);
    endtask

    // Testscenario
    initial begin

        fork
            init_agent();
            clk_gen();
            rst_gen();
        join_none

        clk_dly(100);

        wr_tran();

        clk_dly(100);

        rd_tran();

        clk_dly(100);
        $finish(2);
    end
endmodule
```

まず、AXI VIPも元となっているパッケージの「axi_vip_v1_0_1_pkg」をimportし、Block Design作成で生成されるパッケージ「sys_axi_vip_0_0_pkg」をimportします。
Block Designトップモジュール名「sys_wrapper」は、クロックとリセットを入力信号として持っているため、テストベンチで作成してインスタンスします。

次にAXI VIPのインスタンスですが、project_1.ip_user_files/bd/sys/ip/sys_axi_vip_0_0/sim や project_1.srcs/sources_1/bd/sys/ip/sys_axi_vip_0_0/sim ディレクトリに「sys_axi_vip_0_0_pkg.sv」があり、この中にAgentが定義されております。これが最初の図にあるAXI Master Agentとなります。また繰り返しになりますが「sys」はBlock Design名で使用したものであり、Block Design作成時に名前を変えると、生成されるAXI VIPの名前やディレクトリ名はそれを反映したものになります。

AgentはSystemVerilogのクラスであり、宣言とnewメソッドによるインスタンスが必要となります。
newメソッドの第1引数は任意の文字列、第2引数はドライバI/Fへのインスタンスパスになります。この辺りの手続きはExample Designのコードを参考にしました。

ライト/リードTransactionを発生させるメソッドもExample Designのコードから拝借しました。ここでは、ランダムなリードライトTransactionを作成し、ドライバに与えることで、I/FからAXI Transactionの発生を実施しています。

テストシナリオでは初期化の後、ライトとリードのTransactionを一回実施しています。
今回作成したBlock Designを意識しないランダムなアクセスなので、ライトはトランザクションを発生して終了しますが、リードはスレーブの反応を見るので、ランダムなアドレスを与えるとVIPから対応するスレーブがない旨、エラーメッセージが出力されます。

##おわりに
[AR# 68234](https://japan.xilinx.com/support/answers/68234.html)にある添付ファイルに、AXI VIPのAPIの説明があり、かなり高度な検証が実施できるように作られているようです。
ひとまずは、Zynqを使ったシステムを開発する際にはBFMとしてBlock Design内の検証が追加の費用が発生なくできそうな気がします。なお、このAXI VIPのリリースに伴い、Zynq BFMはEOLとなるようです。
ただ今後、AXI VIPがバージョンアップしてバージョンナンバーが変わったりすると、コードを変更する時間が無視できなくなるのではないかと思ったりしました。