はじめに
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
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回なのは謎ですが、とりあえずシミュレーションすることができました。