Vengineerの妄想(準備期間)

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

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

はじめに

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

今回は、テストベンチの中がどうなっているかをみてみます。

テストベンチの中は?どんな感じになっているの?

zynqmpsoc.srcs/sim_1/imports/mpsoc_preset/mpsoc_tb.v がテストベンチです。

中をみてみましょう

/* # ########################################################################
# Copyright (C) 2019, Xilinx Inc - All rights reserved

# Licensed under the Apache License, Version 2.0 (the "License"). You may
# not use this file except in compliance with the License. A copy of the
# License is located at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# ######################################################################## */

`timescale 1ns / 1ps

module tb;
    reg tb_ACLK;
    reg tb_ARESETn;

    wire temp_clk;
    wire temp_rstn;

    reg [31:0] read_data;
    wire [3:0] leds;
    reg resp;

    initial
    begin
        tb_ACLK = 1'b0;
    end

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

    always #10 tb_ACLK = !tb_ACLK;

    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

    assign temp_clk = tb_ACLK;
    assign temp_rstn = tb_ARESETn;

mpsoc_preset_wrapper mpsoc_sys
 (
 .led_4bits_tri_o(leds)
);
endmodule

timescale

timescale は下記のように、1ns / 1ps です。

`timescale 1ns / 1ps

モジュール名

モジュール名は、tb です。

module tb;

クロックおよびリセット

クロックは、tb_ACLK、リセットは、tb_ARESETn です。

    reg tb_ACLK;
    reg tb_ARESETn;

initial 文で tb_ACLK を 1'b0 に初期化後、always 文で #10 (10ns) で tb_ACLK を反転しています。

    initial
    begin
        tb_ACLK = 1'b0;
    end

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

    always #10 tb_ACLK = !tb_ACLK;

テストベンチ本体

テストベンチの本体は、下記の initial 文で行っています。最初に、$displayシステムタスクでメッセージを表示した後に、tb_ARESETn を 1'b0 にし、リセットをかけます。tb_ACLKを20クロック待って、tb_ARESETn を 1'b1 にして、リセットを解除します。その後、tb_ACLK を 5クロック待って、いよいよZynq MPSoC VIP の登場です。

    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 が Zynq MPSoC VIP のインスタンスになっています。

        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 ;
  • tb => zynqmpsoc.srcs/sim_1/imports/mpsoc_preset/mpsoc_tb.v
  • tb.mpsoc_sys => zynqmpsoc.srcs/sources_1/imports/hdl/mpsoc_preset_wrapper.v
  • tb.mpsoc_sys.mpsoc_preset_i => zynqmpsoc_x.gen/sources_1/bd/mpsoc_preset/sim/mpsoc_preset.v
  • tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0 => zynqmpsoc.srcs/sources_1/bd/mpsoc_preset/ip/mpsoc_preset_zynq_ultra_ps_e_0_0

になります。

Zynq MPSoC VIP は、zynqmpsoc_x.gen/sources_1/bd/mpsoc_preset/sim/mpsoc_preset.v の中で下記のようにインスタンスされています。

  mpsoc_preset_zynq_ultra_ps_e_0_0 zynq_ultra_ps_e_0

mpsoc_preset_zynq_ultra_ps_e_0_0 は、zynqmpsoc.srcs/sources_1/bd/mpsoc_preset/ip/mpsoc_preset_zynq_ultra_ps_e_0_0/mpsoc_preset_zynq_ultra_ps_e_0_0.xci です。

下記のコードが Zynq MPSoC VIP のAPIを実行しています。

  • por_srstb_reset(1'b1)
  • 200

  • por_srstb_reset(1'b0)
  • fpga_soft_reset(32'h1)
  • 2000 // / This delay depends on your clock frequency. It should be at least 16 clock cycles.

  • por_srstb_reset(1'b1)
  • fpga_soft_reset(32'h0);
  • 2000

APIは、下記のようにドキュメントに説明があります。

por_srstb_reset は、Soft reset for VIP. It is a mandatory reset. See por_srstb_reset API for more information. で、por_reset_ctrl: 1-bit input reset to VIP. とあります。つまり、VIPのリセットです。最初に1'b1でリセットし、#200 (200ns) 経ってから、1'b0 でリセットを解除します。

fpga_soft_reset は、Issue/Generate soft reset for PL. で、[3:0] reset_ctrl : 4-bit input indicating the reset o/p to be asserted for PL. (Details same as FPGA_RST_CTRL register defined in PS) とあります。FPGA部のリセットです。ここでは、4ビット中下位1ビットのみ1にしています。#2000 (2000ns) 待って、また、VIPをリセット(por_srstb_reset(1'b1))し、fpga_soft_reset(32'h0)でFPGA部のリセットを解除し、#2000 (2000ns) 待ちます。

por_srstb_resetへの引数、1'b1 がリセットなのか? 1'b0 がリセットなのか?どうなんでしょうかね。上記のコードだと、1'b1 が最後なので、1'b0 がリセットなんでしょうか?

どうやら上記のコードはおまじないということで、Zynq MPSoC VIP を使うときはこれをそのまま使うみたいです。

やっとテストプログラム

ここからやっとテストプログラムです。下記の アドレス 0xa0000000 に 0xffffffff をライト => リードしています。この部分で、GPIOへのライト => リードが実行され、

        //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");

アドレスマップは下記のようになっているので、0xa0000000 は GPIO です。GPIOは、4ビットなので 0xffffffff を書いても、リード値は 0x0000000f になるはずです。

次は、BlockRAMへのアクセスです。0xa0010000 に 0xDEADBEEF をライトし、リードします。この時、0xDEADBEEF が読めるはずです。

        //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

シミュレーションの実行

左にある Simulation => RTL Simulation => Run Behavioral Simulation を実行します。何故か、下記のコマンドを実行してもうまく波形表示できなかったので、生成されたシミュレーション用 shell を実行して、波形ファイル tb_(behav.wdb) を再生成しました。

cd zynqmpsoc.sim/sim_1/behav/xsim
./simulate.sh

tb_ARESETn が 0 => 1 は、tb_ACLK が 20クロック後になっています。

その後、s_axi_aresetn が ちょっとだけ、1 になり、その後、0 => 1 になっています。この部分が下記の部分になっているんだと思います。

ということは、por_srstb_reset は 0 がリセットということになりますね。

        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 ;

下記の部分が GPIO への ライト => リード です。何故か? 4バイト x 4回のアクセスになっています。

次にBlockRAMへの ライト => リード です。こちらは 4バイト x 1回のアクセスになっています。

おわりに

GPIOへのアクセスが、4バイト x 4回なのは謎ですが、とりあえずシミュレーションすることができました。