aster_ismの工作室

FPGAとかマイコンとか

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)の読込み

手順としては,

  1. SystemVerilogから呼び出すシナリオファイルのオープン,クローズ,データの読出し関数open_scn()close_scn()read_scn()を作成
  2. 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_awaddrmst_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)掛かっている. 正しくマスタからライトトランザクションをおこなったことがスレーブに伝わっている.また,リードトランザクションの場合は,レスポンスがスレーブから返ってきているのが確認できる.

波形は次のようになります.

f:id:aster_ism:20150313210306p:plain

正しく動作していることが確認できます.

まとめ

Vivado Simulatorを用いてDPI-Cシミュレーションを行えることを確認した.

ソースコード

https://github.com/a5teri5m/vivado_dpic_axi4lite_mst

*1:http://www.xilinx.com/support/documentation/sw_manuals/xilinx2014_4/ug900-vivado-logic-simulation.pdf

*2:普段使っているopenSUSEだとうまくいかなかったの会社で動くことを確認しているCentOS 6を使った