Vivado SimulatorによるDPI-Cシミュレーション
UG900 Vivado Logic Simulation*1 のAppendix E: Direct Programming Interface (DPI) in Vivado Simulatorを参考にC言語で書いたCSVファイルの読込み関数をAXI4 Lite のマスタIPで呼び出して,このCSVに従ってマスタが動作するようにします.
なお,環境は下記の通りです.
公式サンプルの実行
公式のサンプルがインストールされているので,まずはこれが実行できるか確認します.
simple_import
サンプルディレクトリ(simple_import
)をコピーしてきて,run.csh
に記述されているコマンドを実行します.
cp -r /opt/Xilinx/Vivado/2014.4/examples/xsim/systemverilog/dpi/simple_import . cd simple_import xsc function.c xvlog -svlog file.sv xelab work.m -sv_lib dpi -R
このサンプルでは,function.c
に記述されたmyFunction()
をfile.sv
から呼び出すサンプルとなっています.
正しく呼び出していれば,シミュレーション出力としてPASSED
が表示されます.
simple_export
サンプルディレクトリ(simple_export
)をコピーしてきて,run.csh
に記述されているコマンドを実行します.
cp -r /opt/Xilinx/Vivado/2014.4/examples/xsim/systemverilog/dpi/simple_export . cd simple_export xvlog -sv file.sv xelab TOP -dpiheader dpi.h xsc function.c xelab TOP -sv_lib dpi -R
このサンプルでは,function.c
に記述されたcFunc()
をfile.sv
から呼び出し,
さらにcFunc()
内でfile.sv
内のsvFunc()
を呼び出すというインポートとエクスポートが使われています.
正しく呼び出していれば,先ほどと同様にシミュレーション出力としてPASSED
が表示されます.
AXI4 Lite マスタのシナリオファイル(CSV)の読込み
手順としては,
- SystemVerilogから呼び出すシナリオファイルのオープン,クローズ,データの読出し関数
open_scn()
,close_scn()
,read_scn()
を作成 - AXI4-Lite Master IP内でトランザクションの制御を
read_scn()
で得られたパラメータに従って行うようにする.
C言語側(CSVの読込み関数)
CSVのフォーマットとしては,とりあえず下記のように,トランザクション間のディレイ,トランザクションのモード,アドレス,データ(Wの場合のみ)とします.
10, W, 44A00000, 00000001 20, R, 44A00004
あまり,C言語得意じゃないですが,このCSVを読み込んで,それぞれを返すread_scn()
を作ります.戻り値は適当に後でEOLとかが判別できるように付けておきます.
DPI_DLLESPEC int read_scn( int * const delay, int * const mode, int * const addr, int * const data ){ char readline[32]; char *find; char c_mode; if (fgets(readline, sizeof(readline), fp) == NULL){ return 3; } if ((find = strchr(readline,'\n')) != NULL){ *find = '\0'; } if (sscanf(readline, " %d , %c , %x , %x ", delay, &c_mode, addr, data) == EOF){ return 1; } if (c_mode == 'W'){ *mode = 1; }else if(c_mode == 'R'){ *mode = 0; }else{ return 2; } return 0; }
他にファイルのオープン,クローズを作ります(省略).
SystemVerilog側(AXI4-LiteマスタIP)
AXI4-Liteの回路動作をSystemVerilogで記述します.
マスタモジュールaxi4l_mst
は,
run
信号の立上りパルスrun_pulse
でファイルをオープンし,読み込んだデータがファイルのEOLになるまでライト/リードトランザクションを行う.
トランザクションは前回のトランザクションが終わってから行う(AWチャンネルとARチャンネルは独立して動作可能ですが,今回のマスタは同時に行いません).
トランザクションはtrans_wr
アサートされたらmst_awaddr
とmst_wdata
のアドレス・データをライトし,trans_rd
がアサートされたらmst_araddr
のアドレスをリードするような回路となっています.
コードの一部(C言語の関数を呼び出している部分)を下記に示します.
typedef enum reg [2:0] { ST_IDLE, ST_OPEN_SCN, ST_READ_SCN, ST_TRANS_WR, ST_TRANS_WR_WAIT, ST_TRANS_RD, ST_TRANS_RD_WAIT, ST_CLOSE_SCN } state_t; state_t state; reg [31:0] mst_awaddr, mst_wdata, mst_araddr; integer ret, delay, mode, addr, data; time trans_start; import "DPI-C" pure function int open_scn(); import "DPI-C" pure function int read_scn( output int delay, output int mode, output int addr, output int data); import "DPI-C" pure function int close_scn(); always @(posedge axi_aclk) begin if (axi_aresetn == 1'b0) begin state <= ST_IDLE; trans_wr <= 1'b0; trans_rd <= 1'b0; error <= 1'b0; finish <= 1'b0; mst_awaddr <= 32'b0; mst_wdata <= 32'b0; mst_araddr <= 32'b0; trans_idx <= 32'b0; end else begin case (state) ST_IDLE: begin state <= ST_IDLE; trans_wr <= 1'b0; trans_rd <= 1'b0; error <= 1'b0; finish <= 1'b0; mst_awaddr <= 32'b0; mst_wdata <= 32'b0; mst_araddr <= 32'b0; trans_idx <= 32'b0; if (run_pulse == 1'b1) begin state <= ST_OPEN_SCN; end end ST_OPEN_SCN: begin state <= ST_READ_SCN; open_scn(); end ST_READ_SCN: begin ret = read_scn(delay, mode, addr, data); if (ret == 0) begin trans_idx <= trans_idx + 1'b1; trans_start <= $time + delay; if (mode == 0) begin state <= ST_TRANS_RD; mst_araddr <= addr; end else begin state <= ST_TRANS_WR; mst_awaddr <= addr; mst_wdata <= data; end end else if (ret == 3) begin state <= ST_FINISH; finish <= 1'b1; end else begin state <= ST_FINISH; finish <= 1'b1; error <= 1'b1; end end ST_TRANS_WR: begin if ($time >= trans_start) begin state <= ST_TRANS_WR_WAIT; trans_wr <= 1'b1; $strobe("%t: [%s] WR ( ADDR=%H, DATA=%H )", $time, INST_NAME, m_axi_awaddr, m_axi_wdata); end end ST_TRANS_WR_WAIT: begin trans_wr <= 1'b0; if (m_axi_bvalid && m_axi_bready) begin state <= ST_READ_SCN; error <= m_axi_bresp[1]; end end ST_TRANS_RD: begin if ($time >= trans_start) begin state <= ST_TRANS_RD_WAIT; trans_rd <= 1'b1; $strobe("%t: [%s] RD ( ADDR=%H )", $time, INST_NAME, m_axi_araddr); end end ST_TRANS_RD_WAIT: begin trans_rd <= 1'b0; if (m_axi_rvalid && m_axi_rready) begin state <= ST_READ_SCN; error <= m_axi_rresp[1]; $strobe("%t: [%s] RE ( DATA=%H )", $time, INST_NAME, m_axi_rdata); end end ST_CLOSE_SCN: begin state <= ST_IDLE; finish <= 1'b0; close_scn(); end default: begin state <= ST_IDLE; trans_wr <= 1'b0; trans_rd <= 1'b0; finish <= 1'b0; mst_awaddr <= 32'b0; mst_wdata <= 32'b0; mst_araddr <= 32'b0; end endcase end end
シミュレーション結果
作ったマスタとテスト用のスレーブ(WRは常に受け入れ,RDはランダム値を返すモジュールaxi4l_slv
)を繋いでシミュレーションします.
マスタのシナリオを下記のように設定しました.
50, W, 00000000, 11111111 60, R, 00000004 50, R, 00000008 30, W, 0000000C, 22222222
実行コマンドは次のようになります.
xsc read_scn.c xvlog -sv test.v axi4l_mst.sv axi4l_slv.sv xelab work.test -sv_lib dpi xsim work.test -R
シミュレーション結果は次の用になりました.
168000: [MST] WR ( ADDR=00000000, DATA=11111111 ) 183000: [SLV] WR ( ADDR=00000000, DATA=11111111 ) 258000: [MST] RD ( ADDR=00000004 ) 273000: [SLV] RD ( ADDR=00000004, DATA=7d3599fa ) 283000: [MST] RE ( DATA=7d3599fa ) 338000: [MST] RD ( ADDR=00000008 ) 353000: [SLV] RD ( ADDR=00000008, DATA=5b6fb9b6 ) 363000: [MST] RE ( DATA=5b6fb9b6 ) 398000: [MST] WR ( ADDR=0000000C, DATA=22222222 ) 413000: [SLV] WR ( ADDR=0000000C, DATA=22222222 )
左から時刻(ps),マスタ/スレーブ,タイプ(WR=ライトトランザクション,RD=リードトランザクション,RE=レスポンス結果),アドレス・データとなっている. クロックは5ns(200MHz)なので,トランザクション(マスタ→スレーブ)15ns(3cyc),レスポンスに10ns(2cyc)掛かっている. 正しくマスタからライトトランザクションをおこなったことがスレーブに伝わっている.また,リードトランザクションの場合は,レスポンスがスレーブから返ってきているのが確認できる.
波形は次のようになります.
正しく動作していることが確認できます.
まとめ
Vivado Simulatorを用いてDPI-Cシミュレーションを行えることを確認した.