SystemVerilogによるテストベンチ実践会(2017夏)のSystemVerilog勉強会は有意義でした。
Zynq VIPとDPI-Cを使用して、テスト用に作成したCソースコードをそのままコードは変更しないで実機テストでも使用できる確認を取りました。
成果物を誰でも実行できるように環境と整えたので公開します。
さて、いったい何が出来るようになったのか・・・?
それは見てのお楽しみ♪
動作環境は次のようになります。
項目 | 詳細 |
---|---|
OS | Ubuntu 16.04.3LTS |
ツール | Vivado 2017.2 |
ここで実験した環境はgithubからcloneして、実行することができます。
$ git clone git://github.com/aquaxis/svDemo
今回はZynq VIPとDPI-Cの動作確認を行うことが目的なので簡易的なプロジェクトです。
FPGAの基本はLチカでしょう。
対象のプロジェクトは図のようになります。
ZynqとZynqのGPポートに接続されたAXI GPIOの構成で、GPIOの出力はLEDに接続しています。
今回のプロジェクトはZYBOで確認できるように環境を整えています。
Zynqプロジェクトは次のように実行することで生成することができます。
$ /opt/Xilinx/Vivado/2017.2/settings64.sh
$ vivado -mode batch -source make.tcl
実行するとZynq7000というフォルダを作成し、その配下に今回対象としているプロジェクト作成します。
Zynq7000/Zynq7000.xprをVivadで開くとプロジェクトを確認することができます。
ファイル名 | 概要 |
---|---|
make.tcl | Zynqプロジェクトを生成するtclスクリプト |
Zynq7000.tcl | 本プロジェクトのブロックデザイン |
Zynq7000.xdc | 本プロジェクトのピン定義ファイル |
コマンドを実行すると、シミュレーションを行うためのエクスポートをしますがそれは後述します。
シミュレーションの内容は「ZynqのプロジェクトでアプリケーションからLEDを点灯させる」ことです。
つまり、シミュレーションの構成としては下図のようになります。
今まで、Zynq VIPが無い場合はAXIバスを使用したシミュレーションが行いにくいという欠点がありました。
シミュレーションと行うために下図のようにBFMを使用するなり、自前でバスマスターモデルを作成して、Verilog HDLのテストベンチでAXIバスにデータを書き込むというシミュレーションを行っていました。
私はとりわけ、後者側で自前でバスマスターモデルを用意してシミュレーションをしていました。
つまり、シミュレーションを行うためにシミュレーション用のプロジェクトを用意していました。
これがZynq VIPを使用するとシミュレーション用にプロジェクトを作成しなくても、下図のように本使用するプロジェクトでシミュレーションを行えるようになります。
これがZynq VIPを使用する最大の利点になります。
多くのシミュレーションベンチはVerilog HDLやVHDLなど、ハードウェア記述言語でシミュレーションする側を記述してシミュレーションを行います。
DPI-Cをしようするちと下図のようにCソースコードで記述したテストアプリをそのまま使用して、シミュレーションを行うことが可能になります。
DPI-Cを利用する利点は次の点が挙げられるでしょう。
シミュレーションを行うために次のソースを用意しました。
ファイル名 | 概要 |
---|---|
tb_Zynq7000.sv | シミュレーション用のSystemVerilogトップファイル |
function.c | テストアプリ(Cソースコード) |
これらのファイルの構成は大雑把にですが下図のようになります。
Zynqプロジェクトを生成した時に、すでにシミュレーション環境が整っています。
Zynqプロジェクトを作成した時にexportsimというフォルダが同時作成されています。
次のようにフォルダを移動するとtb_Zynq7000.shというファイルがあります。
ただし、この時点ではtb_Zynq7000.svを組み込んでシミュレーションのトップ階層の準備が整っているだけで、テストベンチなどは組み込まれていません。
ここで次のように実行するとシミュレーションを行うことができます。
$ cd exportsim/xsim
$ ./tb_Zynq7000.sh
この時点ではテストベンチが存在しないので次のようにエラー出力されます。
Simulation completed
[1505] : *ZYNQ_BFM_INFO : Starting Address(0x10000000) -> Write 4 bytes of data to DDR Memory
[1505] : *ZYNQ_BFM_INFO : Starting Address(0x10000000) -> Read 4 bytes of data from DDR Memory
1505000, running the testbench, data read from MEM was 32'hdeadbeef
FATAL_ERROR: Vivado Simulator kernel has encounted an exception from DPI C function: cFuncStart(). Please correct.
Time: 1505 ns Iteration: 1 Process: /tb_Zynq7000/Initial40_3
File: /home/hidemi/workspace/svDemo/exportsim/xsim/srcs/tb_Zynq7000.sv
HDL Line: /home/hidemi/workspace/svDemo/exportsim/xsim/srcs/tb_Zynq7000.sv:72
## quit
ここでエラーが出ているのはDPI-Cのコードを埋め込んでいるためです。
DPI-Cを実行できるように./tb_Zynq7000.shを次のように修正します。
# RUN_STEP: <elaborate>
elaborate()
{
$xv_path/bin/xelab --relax --debug typical --mt auto -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 xlconcat_v2_1_1 -L unisims_ver -L unimacro_ver -L secureip -L xpm --snapshot tb_Zynq7000 xil_defaultlib.tb_Zynq7000 xil_defaultlib.glbl -log elaborate.log
}
# RUN_STEP: <simulate>
simulate()
{
$xv_path/bin/xsim tb_Zynq7000 -key {Behavioral:sim_1:Functional:tb_Zynq7000} -tclbatch cmd.tcl -log simulate.log
}
# RUN_STEP: <elaborate>
elaborate()
{
$xv_path/bin/xelab --relax --debug typical --mt auto -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 xlconcat_v2_1_1 -L unisims_ver -L unimacro_ver -L secureip -L xpm --snapshot tb_Zynq7000 xil_defaultlib.tb_Zynq7000 xil_defaultlib.glbl -log elaborate.log
$xv_path/bin/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 xlconcat_v2_1_1 -L unisims_ver -L unimacro_ver -L secureip -L xpm xil_defaultlib.tb_Zynq7000 -dpiheader dpi.h
cp ../../function.c .
$xv_path/bin/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 xlconcat_v2_1_1 -L unisims_ver -L unimacro_ver -L secureip -L xpm --snapshot tb_Zynq7000 xil_defaultlib.tb_Zynq7000 xil_defaultlib.glbl -R
}
# RUN_STEP: <simulate>
simulate()
{
$xv_path/bin/xsim tb_Zynq7000 -key {Behavioral:sim_1:Functional:tb_Zynq7000} -tclbatch cmd.tcl -log simulate.log
# $xv_path/bin/xsim tb_Zynq7000 -g
}
tb_Zynq7000.shを修正後、次のように実行します。
$ ./tb_Zynq7000.sh
そうすると次のように結果が表示されます。
Simulation completed
[1505] : *ZYNQ_BFM_INFO : Starting Address(0x10000000) -> Write 4 bytes of data to DDR Memory
[1505] : *ZYNQ_BFM_INFO : Starting Address(0x10000000) -> Read 4 bytes of data from DDR Memory
1505000, running the testbench, data read from MEM was 32'hdeadbeef
svPlWrite(40000000,01234567)
[1505] : M_AXI_GP0 : *ZYNQ_BFM_INFO : Starting Address(0x40000000) -> AXI Write -> 4 bytes
1645 : [LED] 0xzzzzzzz7
[1745] : 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 Wed Aug 30 01:54:59 2017...
****** 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 -tclbatch {cmd.tcl} -key {Behavioral:sim_1:Functional:tb_Zynq7000}
Vivado Simulator 2017.2
Time resolution is 1 ps
source cmd.tcl
## set curr_wave [current_wave_config]
## if { [string length $curr_wave] == 0 } {
## if { [llength [get_objects]] > 0} {
## add_wave /
## set_property needs_save false [current_wave_config]
## } else {
## send_msg_id Add_Wave-1 WARNING "No top level signals found. Simulator will start without a wave window. If you want to open a wave window go to 'File->New Waveform Configuration' or type 'create_wave_config' in the TCL console."
## }
## }
ERROR: [Simulator 45-9] The current simulation was compiled without trace information. To capture and view waveform data, please recompile the design with -debug all or typical.
これでシミュレーションが完了しました。
最後の行のエラー情報は波形情報が出力されないというエラーなのでシミュレーション自体はこれで完了になります。
シミュレーションは下図のように流れます。
次は実機で動作確認をしてしょう。
今回の目的はテストアプリをそのまま使用して実機でテストすることであり、function.cは変更しないでZYBO上で動作検証を行います。
そこで、tb_Zynq7000.svで呼び出したsvPlWriteファンクションのラッパーファイルを作成して、下図のような構成にします。
これを実現するラッパーソースコードが次のコードになります。
#include "dpi.h"
#define MAP_LENG 0x00010000
int svPlWrite(unsigned int addr, unsigned int data)
{
int fd;
unsigned int *laddr;
fd = open( "/dev/mem", O_RDWR | O_SYNC );
if( fd == -1 ){
printf( "Can't open /dev/mem.\n" );
return 0;
}
laddr = mmap( NULL, MAP_LENG, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & 0xFFFF0000);
if( addr == MAP_FAILED ){
printf( "Error: mmap()\n" );
return 0;
}
laddr[(addr & 0x0000FFFC) / 4] = data;
munmap(laddr, MAP_LENG);
close(fd);
return 0;
}
int main(int argc, char **argv)
{
cFuncStart();
return 0;
}
今回はシミュレーションと実機確認を同じテストアプリ(Cソースアプリ)を使用して確認することなのでラッパーソースコードは簡易になっています。
これを次のようにコンパイルします。
$ gcc -c function.c
$ gcc -c main.c
$ gcc -o FuncTest function.o main.o
もちろん、これらのコンパイルはクロスコンパイルでなければいけません。
私の場合は、ZYBO上のLinuxにgccをインストールしているのでZYBO上でコンパイルしました。
これでFuncTestができあがるので次のように実行してみましょう。
$ ./FuncTest
もちろん、ZYBO上でですよ。
これで図のようにfunction.cを変更せずにLEDが点灯することを確認できました。
シミュレーションで行ったことと同様の結果が同じテストコードを使用して確認できました。
今回はCソースコードで記述したテストアプリを改変すること無く、シミュレーションと実機の両方で実行することが目的であったため定番のLチカで動作確認を行いました。 例えば、画像データを使用したシミュレーションを行う場合、Zynq VIPとDPI-Cの環境が整う前は次のように環境を整備してシミュレーションを行っていました。
シミュレーション後、実機確認ではまた別のテストを行います。
Cソースコードでデータを流し込むテストアプリが組めれば、わざわざ、Verilog HDL用のHexデータを用意してシミュレーションをするなどの工数が一気に減って、次のように検証工数が削減されます。
そして、実機でもそのまま使用することが出来るのです。