Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

Xilinx ZynqMP SoC VIP の中を調べる(その9)

はじめに

2か月間、寝かしておきました。

vengineer.hatenablog.com

の続きです。

Xilinx ZynqMP SoC VIP の中を調べる(その9)

DPI-C の export task の実装 です。

テストベンチ (mpsoc_tb.v) を振り返る

Xilinx ZynqMP SoC VIP の中を調べる(その2) で説明した、テストベンチ (mpsoc_tv.v) を振り返ってみます。

mpsoc_tb.v の 下記のinitial 文を変更していきます。

変更前

   initial
    begin

        $display ("running the tb");

        tb_ARESETn = 1'b0;
        repeat(20)@(posedge tb_ACLK);
        tb_ARESETn = 1'b1;
        @(posedge tb_ACLK);

        repeat(5) @(posedge tb_ACLK);

            tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.por_srstb_reset(1'b1);
        #200;
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.por_srstb_reset(1'b0);
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.fpga_soft_reset(32'h1);
        #2000 ;  // This delay depends on your clock frequency. It should be at least 16 clock cycles.
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.por_srstb_reset(1'b1);
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.fpga_soft_reset(32'h0);
                #2000 ;

        //This drives the LEDs on the GPIO output
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.write_data(32'hA0000000,4, 32'hFFFFFFFF, resp);
                #200
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.read_data(32'hA0000000,4, read_data, resp);
                #200

        $display ("LEDs are toggled, observe the waveform");

        //Write into the BRAM through GP0 and read back
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.write_data(32'hA0010000,4, 32'hDEADBEEF, resp);
                #200
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.read_data(32'hA0010000,4,read_data,resp);
                #200
        $display ("%t, running the testbench, data read from BRAM was 32'h%x",$time, read_data);

    if(read_data == 32'hDEADBEEF) begin
           $display ("AXI VIP Test PASSED");
        end
        else begin
           $display ("AXI VIP Test FAILED");
        end
        $display ("Simulation completed");
        $stop;
    end

変更後

fpga_soft_reset までは同じで、その後の GPIO/GP0 の部分は、XXXマクロで ifdef/endif で囲み動かないようにします。その後に、dpi_main() を呼ぶようにします。

   initial
    begin

        $display ("running the tb");

        tb_ARESETn = 1'b0;
        repeat(20)@(posedge tb_ACLK);
        tb_ARESETn = 1'b1;
        @(posedge tb_ACLK);

        repeat(5) @(posedge tb_ACLK);

        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.por_srstb_reset(1'b1);
        #200;
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.por_srstb_reset(1'b0);
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.fpga_soft_reset(32'h1);
        #2000 ;  // This delay depends on your clock frequency. It should be at least 16 clock cycles.
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.por_srstb_reset(1'b1);
        tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.fpga_soft_reset(32'h0);
        #2000 ;

        $display("Start dpi_main %t", $time);
        dpi_main();
        $display("Finish dpi_main %t", $time);

        $display ("Simulation completed");
        $stop;
    end

dpi_main は、下記のように、import です。

    import "DPI-C" context task dpi_main();

また、read_data APIに対応する vip_read32、write_data API に対応する vip_write32、指定した回数クロックをウエイトする vip_nop task を下記のように追加し、DPI-C export task とします。

    export "DPI-C" task vip_nop;
    export "DPI-C" task vip_write32;
    export "DPI-C" task vip_read32;

    task vip_nop(input int count);
        for(int n=0 ; n<count; n++) begin
            @(posedge tb_ACLK);
        end
    endtask

    task vip_write32(input int unsigned addr, input int unsigned data);
       logic [31:0] addr_r, data_r;
       reg resp;
       addr_r = addr;
       data_r = data;
       tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.write_data(addr_r, 4, data_r, resp);
    endtask // write

    task vip_read32(input int unsigned addr, output int unsigned data);
       logic [31:0] addr_r, data_r;
       reg resp;
       addr_r = addr;
       tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.read_data(addr_r, 4, data_r, resp);
       data = data_r;
    endtask

このファイルを mpsoc_dpi.svh とします。そして、下記のように、mpsoc_tb.v の initial 文の前で include するようにします。

    //------------------------------------------------------------------------
    // Simple Clock Generator
    //------------------------------------------------------------------------

    always #10 tb_ACLK = !tb_ACLK;

`include "mpsoc_dpi.svh"

    initial
    begin

これで、initial 文の最後の方で実行される dpi_main 経由で c++言語の関数 dpi_main が呼び出されるようになります。

おわりに

次回は、dpi_main の中をみてみます。

関連ブログ:

vengineer.hatenablog.com