第一次夏休みも終了し、コミケまでのひとときで「SystemVerilogによるテストベンチ実践会(2017夏)」に向けてお勉強です。
(コミケの準備ってのもあるんだけど、現実逃避・・・)
Vivado 2017.2のDPI-Cではexport taskができないということでしたので出来るように工夫する検討はすでにやってみました。
あとは本当にできるかどうかですね。
って、「VivadoでZynqのSystemVerilogができなぁ〜い」って叫んでたんですが夏休みが明けたらできちゃいました。
なんだったんでしょうねぇ〜(^-^;
まずはテスト用プロジェクトを作成しましょう。
回路構成は簡単にZynqとAXI GPIO(Address:0x4000_0000)のみです。
予め、ブロックはZynq7000.tcl(一番、最後に添付)で作成しています。
プロジェクトを作成するtclファイルを示します。
# プロジェクトの生成
create_project Zynq7000 ./Zynq7000 -part xc7z010clg400-1
cd Zynq7000
# ブロックデザインの生成
source ../Zynq7000.tcl
make_wrapper -files [get_files ./Zynq7000.srcs/sources_1/bd/Zynq7000/Zynq7000.bd] -top
add_files -norecurse ./Zynq7000.srcs/sources_1/bd/Zynq7000/hdl/Zynq7000_wrapper.v
# シミュレーションファイルの読込み
add_files -fileset sim_1 -norecurse -scan_for_includes ../tb_Zynq7000.sv
set_property top tb_Zynq7000 [get_filesets sim_1]
# シミュレーション環境の生成
generate_target Simulation [get_files ./Zynq7000.srcs/sources_1/bd/Zynq7000/Zynq7000.bd]
export_ip_user_files -of_objects [get_files ./Zynq7000.srcs/sources_1/bd/Zynq7000/Zynq7000.bd] -no_script -force -quiet
launch_simulation
exit
次のように実行してプロジェクトを作成します。
$ vivado -mode batch -source make.tcl
テストベンチでは最初にVerilog HDL側からLEDやメモリに書込みを行い、cFuncStartを実行します。
cFuncStartからsvPlWriteでCプログラムからPLのLEDにデータを書き込みます。
SystemVerilog側のトップ階層のテストベンチです。
`timescale 1ns / 1ps
module tb_Zynq7000;
import "DPI-C" function int cFuncStart() ;
reg tb_ACLK;
reg tb_ARESETn;
wire temp_clk;
wire temp_rstn;
reg [31:0] read_data;
wire [31:0] LED;
reg resp;
initial
begin
tb_ACLK = 1'b0;
end
//------------------------------------------------------------------------
// Simple Clock Generator
//------------------------------------------------------------------------
always #10 tb_ACLK = !tb_ACLK;
initial
begin
`ifndef XILINX_SIMULATOR
tb_Zynq7000.zynq_sys.base_zynq_i.processing_system7_0.inst.M_AXI_GP0.master.IF.PC.fatal_to_warnings=1;
#40;
tb_Zynq7000.zynq_sys.base_zynq_i.processing_system7_0.inst.M_AXI_GP0.master.IF.PC.fatal_to_warnings=0;
`endif
end
reg [31:0] mem_data;
initial
begin
$display ("running the tb");
tb_ARESETn = 1'b0;
repeat(2)@(posedge tb_ACLK);
tb_ARESETn = 1'b1;
@(posedge tb_ACLK);
repeat(5) @(posedge tb_ACLK);
//Reset the PL
tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.fpga_soft_reset(32'h1);
tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.fpga_soft_reset(32'h0);
//This drives the LEDs on the GPIO output
tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.write_data(32'h40000000,4, 32'hDEADA5A5, resp);
tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.read_data(32'h40000008,4,read_data,resp);
$display ("%t, running the testbench, data read from GPIO was 32'h%x",$time, read_data);
if(read_data[3:0] == 4'h5) begin
$display ("AXI VIP Test PASSED");
end
else begin
$display ("AXI VIP Test FAILED");
end
$display ("Simulation completed");
tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.write_mem(32'hDEADBEEF, 32'h10000000, 4);
tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.read_mem(32'h10000000, 4, mem_data);
$display ("%t, running the testbench, data read from MEM was 32'h%x",$time, mem_data);
cFuncStart();
// $stop;
end
assign temp_clk = tb_ACLK;
assign temp_rstn = tb_ARESETn;
Zynq7000_wrapper zynq_sys
(.DDR_addr(),
.DDR_ba(),
.DDR_cas_n(),
.DDR_ck_n(),
.DDR_ck_p(),
.DDR_cke(),
.DDR_cs_n(),
.DDR_dm(),
.DDR_dq(),
.DDR_dqs_n(),
.DDR_dqs_p(),
.DDR_odt(),
.DDR_ras_n(),
.DDR_reset_n(),
.DDR_we_n(),
.FIXED_IO_ddr_vrn(),
.FIXED_IO_ddr_vrp(),
.FIXED_IO_mio(),
.FIXED_IO_ps_clk(temp_clk),
.FIXED_IO_ps_porb(temp_rstn ),
.FIXED_IO_ps_srstb(temp_rstn),
.led_tri_o(LED),
.gpi_tri_i(LED)
);
//
initial begin
$monitor($stime, " : [LED] 0x%08x", LED);
end
// svPlWrite
export "DPI-C" function svPlWrite ;
reg GP0WriteTaskEnable = 0;
reg [31:0] GP0Address;
reg [31:0] GP0WriteData;
reg GP0Resp;
function int svPlWrite(input int address, input int data) ;
GP0Address = address;
GP0WriteData = data;
GP0WriteTaskEnable = 1;
return 0;
endfunction
always begin
wait(GP0WriteTaskEnable == 1);
GP0WriteTaskEnable = 0;
$display("svPlWrite(%x,%x)", GP0Address, GP0WriteData);
tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.write_data(GP0Address, 4, GP0WriteData, GP0Resp);
end
// svStopSim
export "DPI-C" function svStopSim ;
reg StopSimTaskEnable = 0;
function int svStopSim() ;
StopSimTaskEnable = 1;
return 0;
endfunction
always begin
wait(StopSimTaskEnable == 1);
StopSimTaskEnable = 0;
#1000;
$display("svStopSim()");
$stop;
end
endmodule
#include "dpi.h"
int cFuncStart()
{
int rslt;
printf("[cFuncStart]\n");
rslt = svPlWrite(0x40000000, 0x01234567);
printf("Check LED\n");
svStopSim();
return 0 ;
}
実行手順は次のとおりです。
cd Zynq7000/Zynq7000.sim/sim_1/behav/
xvlog -m64 --relax -L smartconnect_v1_0 -L axi_protocol_checker_v1_1_14 -L xil_common_vip_v1_0_0 -L axi_vip_v1_0_2 -L axi_vip_v1_0_1 -L xil_defaultlib -prj tb_Zynq7000_vlog.prj
xvhdl -m64 --relax -prj tb_Zynq7000_vhdl.prj
xelab -L axi_lite_ipif_v3_0_4 -L lib_cdc_v1_0_2 -L interrupt_control_v3_1_4 -L axi_gpio_v2_0_15 -L xil_defaultlib -L proc_sys_reset_v5_0_11 -L axi_infrastructure_v1_1_0 -L xil_common_vip_v1_0_0 -L smartconnect_v1_0 -L axi_protocol_checker_v1_1_14 -L axi_vip_v1_0_2 -L axi_vip_v1_0_1 -L xlconstant_v1_1_3 -L unisims_ver -L unimacro_ver -L secureip -L xpm xil_defaultlib.tb_Zynq7000 -dpiheader dpi.h
cp ../../../../function.c .
xsc function.c
xelab -sv_lib dpi -L axi_lite_ipif_v3_0_4 -L lib_cdc_v1_0_2 -L interrupt_control_v3_1_4 -L axi_gpio_v2_0_15 -L xil_defaultlib -L proc_sys_reset_v5_0_11 -L axi_infrastructure_v1_1_0 -L xil_common_vip_v1_0_0 -L smartconnect_v1_0 -L axi_protocol_checker_v1_1_14 -L axi_vip_v1_0_2 -L axi_vip_v1_0_1 -L xlconstant_v1_1_3 -L unisims_ver -L unimacro_ver -L secureip -L xpm --snapshot tb_Zynq7000 xil_defaultlib.tb_Zynq7000 xil_defaultlib.glbl -R
なんだよぉ〜、できじゃないかぁ〜!!!
だれだぁ〜、VivadoでZynqをSystemVerilog難しいって言った奴はぁ〜!!!
<<<ログがクソ長いので省略>>>
Built simulation snapshot tb_Zynq7000
****** xsim v2017.2 (64-bit)
**** SW Build 1909853 on Thu Jun 15 18:39:10 MDT 2017
**** IP Build 1909766 on Thu Jun 15 19:58:00 MDT 2017
** Copyright 1986-2017 Xilinx, Inc. All Rights Reserved.
source xsim.dir/tb_Zynq7000/xsim_script.tcl
# xsim {tb_Zynq7000} -autoloadwcfg -runall
Vivado Simulator 2017.2
Time resolution is 1 ps
run -all
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.M_AXI_GP0.master
[0] : *ZYNQ_BFM_INFO : M_AXI_GP0 : Port is ENABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.M_AXI_GP1.master
[0] : *ZYNQ_BFM_INFO : M_AXI_GP1 : Port is DISABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.S_AXI_GP0.slave
[0] : *ZYNQ_BFM_INFO : S_AXI_GP0 : Port is DISABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.S_AXI_GP1.slave
[0] : *ZYNQ_BFM_INFO : S_AXI_GP1 : Port is DISABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.S_AXI_HP0.slave
[0] : *ZYNQ_BFM_INFO : S_AXI_HP0 : Port is DISABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.S_AXI_HP1.slave
[0] : *ZYNQ_BFM_INFO : S_AXI_HP1 : Port is DISABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.S_AXI_HP2.slave
[0] : *ZYNQ_BFM_INFO : S_AXI_HP2 : Port is DISABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.S_AXI_HP3.slave
[0] : *ZYNQ_BFM_INFO : S_AXI_HP3 : Port is DISABLED.
XilinxAXIVIP: Found at Path: tb_Zynq7000.zynq_sys.Zynq7000_i.processing_system7_0.inst.S_AXI_ACP.slave
[0] : *ZYNQ_BFM_INFO : S_AXI_ACP : Port is DISABLED.
running the tb
0 : [LED] 0x00000000
[150] : *ZYNQ_BFM_INFO : FPGA Soft Reset called for 0x1
[150] : *ZYNQ_BFM_INFO : FPGA Soft Reset called for 0x0
[150] : M_AXI_GP0 : *ZYNQ_BFM_INFO : Starting Address(0x40000000) -> AXI Write -> 4 bytes
2250 : [LED] 0xdeada5a5
[2450] : M_AXI_GP0 : *ZYNQ_BFM_INFO : Done AXI Write for Starting Address(0x40000000) with Response 'OKAY'
[2450] : M_AXI_GP0 : *ZYNQ_BFM_INFO : Starting Address(0x40000008) -> AXI Read -> 4 bytes
[2870] : M_AXI_GP0 : *ZYNQ_BFM_INFO : Done AXI Read for Starting Address(0x40000008) with Response 'OKAY'
2870000, running the testbench, data read from GPIO was 32'hdeada5a5
[cFuncStart]
Check LED
AXI VIP Test PASSED
Simulation completed
[2870] : *ZYNQ_BFM_INFO : Starting Address(0x10000000) -> Write 4 bytes of data to DDR Memory
[2870] : *ZYNQ_BFM_INFO : Starting Address(0x10000000) -> Read 4 bytes of data from DDR Memory
2870000, running the testbench, data read from MEM was 32'hdeadbeef
svPlWrite(40000000,01234567)
[2870] : M_AXI_GP0 : *ZYNQ_BFM_INFO : Starting Address(0x40000000) -> AXI Write -> 4 bytes
3150 : [LED] 0x01234567
[3350] : M_AXI_GP0 : *ZYNQ_BFM_INFO : Done AXI Write for Starting Address(0x40000000) with Response 'OKAY'
svStopSim()
exit
INFO: [Common 17-206] Exiting xsim at Mon Aug 7 12:11:56 2017...
$
最後にZynqの回路を貼り付けておく。
################################################################
# This is a generated script based on design: Zynq7000
#
# Though there are limitations about the generated script,
# the main purpose of this utility is to make learning
# IP Integrator Tcl commands easier.
################################################################
namespace eval _tcl {
proc get_script_folder {} {
set script_path [file normalize [info script]]
set script_folder [file dirname $script_path]
return $script_folder
}
}
variable script_folder
set script_folder [_tcl::get_script_folder]
################################################################
# Check if script is running in correct Vivado version.
################################################################
set scripts_vivado_version 2017.2
set current_vivado_version [version -short]
if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
puts ""
catch {common::send_msg_id "BD_TCL-109" "ERROR" "This script was generated using Vivado <$scripts_vivado_version> and is being run in <$current_vivado_version> of Vivado. Please run the script in Vivado <$scripts_vivado_version> then open the design in Vivado <$current_vivado_version>. Upgrade the design by running \"Tools => Report => Report IP Status...\", then run write_bd_tcl to create an updated script."}
return 1
}
################################################################
# START
################################################################
# To test this script, run the following commands from Vivado Tcl console:
# source Zynq7000_script.tcl
# If there is no project opened, this script will create a
# project, but make sure you do not have an existing project
# <./myproj/project_1.xpr> in the current working folder.
set list_projs [get_projects -quiet]
if { $list_projs eq "" } {
create_project project_1 myproj -part xc7z010clg400-1
}
# CHANGE DESIGN NAME HERE
set design_name Zynq7000
# If you do not already have an existing IP Integrator design open,
# you can create a design using the following command:
# create_bd_design $design_name
# Creating design if needed
set errMsg ""
set nRet 0
set cur_design [current_bd_design -quiet]
set list_cells [get_bd_cells -quiet]
if { ${design_name} eq "" } {
# USE CASES:
# 1) Design_name not set
set errMsg "Please set the variable <design_name> to a non-empty value."
set nRet 1
} elseif { ${cur_design} ne "" && ${list_cells} eq "" } {
# USE CASES:
# 2): Current design opened AND is empty AND names same.
# 3): Current design opened AND is empty AND names diff; design_name NOT in project.
# 4): Current design opened AND is empty AND names diff; design_name exists in project.
if { $cur_design ne $design_name } {
common::send_msg_id "BD_TCL-001" "INFO" "Changing value of <design_name> from <$design_name> to <$cur_design> since current design is empty."
set design_name [get_property NAME $cur_design]
}
common::send_msg_id "BD_TCL-002" "INFO" "Constructing design in IPI design <$cur_design>..."
} elseif { ${cur_design} ne "" && $list_cells ne "" && $cur_design eq $design_name } {
# USE CASES:
# 5) Current design opened AND has components AND same names.
set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
set nRet 1
} elseif { [get_files -quiet ${design_name}.bd] ne "" } {
# USE CASES:
# 6) Current opened design, has components, but diff names, design_name exists in project.
# 7) No opened design, design_name exists in project.
set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
set nRet 2
} else {
# USE CASES:
# 8) No opened design, design_name not in project.
# 9) Current opened design, has components, but diff names, design_name not in project.
common::send_msg_id "BD_TCL-003" "INFO" "Currently there is no design <$design_name> in project, so creating one..."
create_bd_design $design_name
common::send_msg_id "BD_TCL-004" "INFO" "Making design <$design_name> as current_bd_design."
current_bd_design $design_name
}
common::send_msg_id "BD_TCL-005" "INFO" "Currently the variable <design_name> is equal to \"$design_name\"."
if { $nRet != 0 } {
catch {common::send_msg_id "BD_TCL-114" "ERROR" $errMsg}
return $nRet
}
##################################################################
# DESIGN PROCs
##################################################################
# Procedure to create entire design; Provide argument to make
# procedure reusable. If parentCell is "", will use root.
proc create_root_design { parentCell } {
variable script_folder
if { $parentCell eq "" } {
set parentCell [get_bd_cells /]
}
# Get object for parentCell
set parentObj [get_bd_cells $parentCell]
if { $parentObj == "" } {
catch {common::send_msg_id "BD_TCL-100" "ERROR" "Unable to find parent cell <$parentCell>!"}
return
}
# Make sure parentObj is hier blk
set parentType [get_property TYPE $parentObj]
if { $parentType ne "hier" } {
catch {common::send_msg_id "BD_TCL-101" "ERROR" "Parent <$parentObj> has TYPE = <$parentType>. Expected to be <hier>."}
return
}
# Save current instance; Restore later
set oldCurInst [current_bd_instance .]
# Set parent object as current
current_bd_instance $parentObj
# Create interface ports
set DDR [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:ddrx_rtl:1.0 DDR ]
set FIXED_IO [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_processing_system7:fixedio_rtl:1.0 FIXED_IO ]
set GPI [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:gpio_rtl:1.0 GPI ]
set LED [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:gpio_rtl:1.0 LED ]
# Create ports
# Create instance: axi_gpio_0, and set properties
set axi_gpio_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 axi_gpio_0 ]
set_property -dict [ list \
CONFIG.C_ALL_INPUTS_2 {1} \
CONFIG.C_ALL_OUTPUTS {1} \
CONFIG.C_IS_DUAL {1} \
] $axi_gpio_0
# Create instance: proc_sys_reset_0, and set properties
set proc_sys_reset_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset:5.0 proc_sys_reset_0 ]
# Create instance: processing_system7_0, and set properties
set processing_system7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0 ]
# Create instance: smartconnect_0, and set properties
set smartconnect_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:smartconnect:1.0 smartconnect_0 ]
set_property -dict [ list \
CONFIG.NUM_SI {1} \
] $smartconnect_0
# Create interface connections
connect_bd_intf_net -intf_net axi_gpio_0_GPIO [get_bd_intf_ports LED] [get_bd_intf_pins axi_gpio_0/GPIO]
connect_bd_intf_net -intf_net axi_gpio_0_GPIO2 [get_bd_intf_ports GPI] [get_bd_intf_pins axi_gpio_0/GPIO2]
connect_bd_intf_net -intf_net processing_system7_0_DDR [get_bd_intf_ports DDR] [get_bd_intf_pins processing_system7_0/DDR]
connect_bd_intf_net -intf_net processing_system7_0_FIXED_IO [get_bd_intf_ports FIXED_IO] [get_bd_intf_pins processing_system7_0/FIXED_IO]
connect_bd_intf_net -intf_net processing_system7_0_M_AXI_GP0 [get_bd_intf_pins processing_system7_0/M_AXI_GP0] [get_bd_intf_pins smartconnect_0/S00_AXI]
connect_bd_intf_net -intf_net smartconnect_0_M00_AXI [get_bd_intf_pins axi_gpio_0/S_AXI] [get_bd_intf_pins smartconnect_0/M00_AXI]
# Create port connections
connect_bd_net -net proc_sys_reset_0_peripheral_aresetn [get_bd_pins axi_gpio_0/s_axi_aresetn] [get_bd_pins proc_sys_reset_0/peripheral_aresetn] [get_bd_pins smartconnect_0/aresetn]
connect_bd_net -net processing_system7_0_FCLK_CLK0 [get_bd_pins axi_gpio_0/s_axi_aclk] [get_bd_pins proc_sys_reset_0/slowest_sync_clk] [get_bd_pins processing_system7_0/FCLK_CLK0] [get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] [get_bd_pins smartconnect_0/aclk]
connect_bd_net -net processing_system7_0_FCLK_RESET0_N [get_bd_pins proc_sys_reset_0/ext_reset_in] [get_bd_pins processing_system7_0/FCLK_RESET0_N]
# Create address segments
create_bd_addr_seg -range 0x00010000 -offset 0x40000000 [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_gpio_0/S_AXI/Reg] SEG_axi_gpio_0_Reg
# Restore current instance
current_bd_instance $oldCurInst
save_bd_design
}
# End of create_root_design()
##################################################################
# MAIN FLOW
##################################################################
create_root_design ""
writed: 2017/08/08/ 01:11:07