Vivado/Vitis 2025.2を使ったデザインでは,いろいろなファイルが生成されます.そのため,何がビルドを再現するために最小のリソースがなのかわかりづらく,バージョン管理にも手間がかかります.
このメモでは,Zybo Z7-20向けの簡単なデザインを例に,PL/PSの両方を使ったデザインをプロジェクトモードで管理することを想定して,必要なリソースを整理し,簡易ビルドスクリプトとしてまとめたものです.Vivado向けスクリプトはtclで,Vitis向けスクリプトはPythonで記述します.
目次
作成するプロジェクト
作成するプロジェクトは,Zybo Z7-20の上でPS上のソフトウェアとPL上のロジックを動作させるものです.
ブロックデザインは次のような極めてシンプルなものを用意します.

このブロックデザインの外側に,独自にLチカするトップモジュール(top.sv)を用意します.ブロックデザインから作成されたファイルをベースにFCLK_{CLK0, RESET0_N}_0を内部で使うように変更するとともに,LED出力とカウンタを追加した,というだけのものです.
`timescale 1ps / 1ps
module top (
inout wire DDR_cas_n,
inout wire DDR_cke,
inout wire DDR_ck_n,
inout wire DDR_ck_p,
inout wire DDR_cs_n,
inout wire DDR_reset_n,
inout wire DDR_odt,
inout wire DDR_ras_n,
inout wire DDR_we_n,
inout wire [2:0] DDR_ba,
inout wire [14:0] DDR_addr,
inout wire [3:0] DDR_dm,
inout wire [31:0] DDR_dq,
inout wire [3:0] DDR_dqs_n,
inout wire [3:0] DDR_dqs_p,
inout wire [53:0] FIXED_IO_mio,
inout wire FIXED_IO_ddr_vrn,
inout wire FIXED_IO_ddr_vrp,
inout wire FIXED_IO_ps_srstb,
inout wire FIXED_IO_ps_clk,
inout wire FIXED_IO_ps_porb,
output wire [3:0] LED
);
wire FCLK_RESET0_N_0;
wire FCLK_CLK0_0;
design_1 inst (
.DDR_cas_n(DDR_cas_n),
.DDR_cke(DDR_cke),
.DDR_ck_n(DDR_ck_n),
.DDR_ck_p(DDR_ck_p),
.DDR_cs_n(DDR_cs_n),
.DDR_reset_n(DDR_reset_n),
.DDR_odt(DDR_odt),
.DDR_ras_n(DDR_ras_n),
.DDR_we_n(DDR_we_n),
.DDR_ba(DDR_ba),
.DDR_addr(DDR_addr),
.DDR_dm(DDR_dm),
.DDR_dq(DDR_dq),
.DDR_dqs_n(DDR_dqs_n),
.DDR_dqs_p(DDR_dqs_p),
.FIXED_IO_mio(FIXED_IO_mio),
.FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
.FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
.FIXED_IO_ps_srstb(FIXED_IO_ps_srstb),
.FIXED_IO_ps_clk(FIXED_IO_ps_clk),
.FIXED_IO_ps_porb(FIXED_IO_ps_porb),
.FCLK_RESET0_N_0(FCLK_RESET0_N_0),
.FCLK_CLK0_0(FCLK_CLK0_0)
);
wire clk200M;
clk_wiz_0 clk_wiz_0_i (
.clk_out1(clk200M),
.reset(~FCLK_RESET0_N_0),
.locked(),
.clk_in1(FCLK_CLK0_0)
);
reg [31:0] counter;
always_ff @(posedge clk200M) begin
counter <= counter + 1;
end
assign LED = counter[23:20];
endmodule // topまた,制約ファイルとして次のようなtop.xdcを用意します.
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { LED[0] }];
set_property -dict { PACKAGE_PIN M15 IOSTANDARD LVCMOS33 } [get_ports { LED[1] }];
set_property -dict { PACKAGE_PIN G14 IOSTANDARD LVCMOS33 } [get_ports { LED[2] }];
set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { LED[3] }];top.svの中で利用しているクロック生成のIPコアは,VivadoのIP Catalogから作成しました.ここでは,FCLK_CLK0_0から200MHzを生成しています(普通にFCLK_CLK0_0を使ってもいいのですが,サンプルとして).
必要なファイル一式を準備したら,ビルドできることを確認します.
必要なリソースを集める
以下のように必要なリソースを集めることにします.create_prj.tclと,build_sw.pyは後で作成するビルドスクリプトで,それ以外は,既に作成した,あるいは,作成されたリソースです.
.
├── bd
│ └── design_1.tcl
├── ipcores
│ └── clk_wiz_0.xci
├── prj
│ └── top.xsa
├── sources
│ ├── top.sv
│ └── top.xdc
├── create_prj.tcl # あとで作成する
└── build_sw.py # あとで作成するtop.sv と top.xdc は自分で作成したファイルで,design_1.tclとclk_wiz_0.xciはVivadoが作成したファイルです.
clk_wiz_0.xci は,作成したプロジェクトディレククトリ内のソースファイルが格納されたディレクトリにあります.たとえば,デフォルト名でプロジェクトを作成していれば, project_1/project_1.srcs/sources_1/ip/clk_wiz_0/clk_wiz_0.xci にあります.
design_1.tclは,Vivadoで,Open Block Designでブロックデザインを開いた状態でFile -> Export-> Export Block Design... とするか,あるいは,下のようなtclスクリプトで出力できます.
open_bd_design {./project_1/project_1.srcs/sources_1/bd/design_1/design_1.bd}
write_bd_tcl -force ./project_1/design_1.tclこれで,必要なファイルは揃いました.
ハードウェアビルドスクリプトの作成
Vivadoで,プロジェクトを復元するためのtclスクリプト(create_prj.tcl)は次のようなものです.ここでは,prjの下にプロジェクトを作成し,bitファイルを作成します..また,この後のVitisを使ったソフトウェア開発で必要になる,xsaファイルの作成も行います.
set project_dir prj
set project_name prj
set part_name xc7z020clg400-1
set board_name digilentinc.com:zybo-z7-20:part0:1.2
set top_module top
set block_design ./bd/design_1.tcl
set source_files { \
./sources/top.sv \
}
set constraint_files { \
./sources/top.xdc \
}
set ipcore_files { \
./ipcores/clk_wiz_0.xci
}
create_project -force $project_name $project_dir -part $part_name
set_property board_part $board_name [current_project]
add_files -norecurse $source_files
add_files -fileset constrs_1 -norecurse $constraint_files
import_ip -files $ipcore_files
source $block_design
set_property top $top_module [current_fileset]
update_compile_order -fileset sources_1
reset_project
launch_runs synth_1 -jobs 8
wait_on_run synth_1
launch_runs impl_1 -jobs 8
wait_on_run impl_1
launch_runs impl_1 -to_step write_bitstream -jobs 8
wait_on_run impl_1
write_hw_platform -fixed -include_bit -force -file prj/${top_module}.xsa
close_project
quit
実行するには,コマンドラインで,
$ vivado -mode batch -source ./create_prj.tclとします.無事に完了すると,prjの下にVivadoプロジェクト prj.xpr や,最終生成物の top.xsa ができあがっています.
ソフトウェアビルドスクリプトの作成
PS上のソフトウェアはVitisを使って開発をするのが一般的な方法の一つです.Vitisでは,大きく,
- ワークスペースを作成する
- プラットフォームを作成する
- アプリケーションプロジェクトを作成する
という手順で開発をはじめます.
その後,作成したプラットフォームデザインおよびアプリケーションをビルドして,ZYNQをブートイメージを作成すれば,ZYNQ上でデザインを実行可能な BOOT.BINを得ることができます.
これらの手順は,VitisのGUIで作成することもできますが,Pythonスクリプトにやらせることもできます.
次のスクリプト( build_sw.py )が一連の手順を実行する Python スクリプトです.
import os
import vitis
import shutil
import subprocess
client = vitis.create_client()
os.makedirs("./workspace")
client.set_workspace(path="workspace")
platform = client.create_platform_component(name = "platform",
hw_design = "$COMPONENT_LOCATION/../../prj/top.xsa",
os = "standalone",
cpu = "ps7_cortexa9_0",
domain_name = "standalone_ps7_cortexa9_0",
compiler = "gcc")
comp = client.create_app_component(name="hello_world",
platform = "$COMPONENT_LOCATION/../platform/export/platform/platform.xpfm",
domain = "standalone_ps7_cortexa9_0",
template = "hello_world")
platform = client.get_component(name="platform")
status = platform.build()
#shutil.copyfile("./software/helloworld.c", "./workspace/hello_world/src/helloworld.c")
#shutil.copyfile("./software/lscript.ld", "./workspace/hello_world/src/lscript.ld")
comp = client.get_component(name="hello_world")
comp.build()
bif = """
the_ROM_image:
{
[bootloader] ./workspace/platform/export/platform/sw/boot/fsbl.elf
./workspace/platform/export/platform/hw/sdt/top.bit
./workspace/hello_world/build/hello_world.elf
}
"""
with open("boot.bif", "w") as f:
f.write(bif)
VITIS = os.environ["XILINX_VITIS"]
BOOTGEN = os.path.join(VITIS, "bin", "bootgen")
cmd = [
BOOTGEN,
"-arch", "zynq",
"-image", "boot.bif",
"-o", "BOOT.BIN",
"-w"
]
subprocess.run(cmd, check=True)
vitis.dispose()このPythonスクリプトをVitisに同梱されているPythonを使って実行します.現状では,Bashで動作することが想定されているようです(Zshではだめだった).なお,Vitisは,/tools/Xilinx/2025.2 以下にインストールされていることを想定しています.
$ export XILINX_VITIS=/tools/Xilinx/2025.2/Vitis
$ source /tools/Xilinx/2025.2/Vitis/cli/examples/customer_python_utils/setup_vitis_env.sh
$ /tools/Xilinx/2025.2/Vitis/tps/lnx64/python-3.13.0/bin/python build_sw.py作成されたBOOT.BINをMicroSDカードに書き込んでZybo Z7-20にセット,電源を投入すると,LチカとシリアルターミナルへのHello Worldの出力が確認できます.
ワークスペースは,workspace 以下に作成されているので,Vitisを開いて,Set Workspaceでworkspaceを開けば,開発の継続が可能です.
なお,ここでは,VitisのHello World テンプレートを使ってアプリケーションプロジェクトを作成しました.workspace/hello_world/src以下にソースコード,リンカスクリプト,CMake用のファイルなどがありますので,必要があれば,これらのファイルを適当なフォルダ,(たとえば,software)にコピーして編集,ビルド手順中に上書きすることで(スクリプト内でコメントアウトしてるshutil.copyfileの部分),編集したソフトウェアのビルドを再現することができます.
おまけ: ZYNQのPLをさくっと使う
ZYNQのPLだけ使ってロジックを設計したいときにPSのクロックを利用する場合は,PSの初期化をしないとクロックが有効になりません.そのため,Vivadoで作成したbitファイルをHardware ManagerでFPGAに書き込んでも,ロジックは所望の動作をしません.
とりあえず,Hello Wolrdアプリをビルドすれば,それでも事足りるのですが,どうしてもVitisを使いたくない場合には,以下のように,自分でPSの初期化をすることもできます.
- ハードウェアビルド手順で作成した top.xsa を解凍して,ps7_init.tclを取り出す
- xsctでターゲットFPGAに接続する
- ps7_init.tclを読み込み,ps7_initとps7_post_configを実行する
具体的には,
$ unzip top.xsa
$ xsct
xsct% connect
tcfchan#0
xsct% ta 1
xsct% source ps7_init.tcl
xsct% ps7_init
xsct% ps7_post_configと,します.これで,PSが初期化されてクロックの出力が開始,ロジックが所望の動作(今回の例だとLチカ)を開始します.
…すなおに,Vitisアプリを作ってBOOT.BINは作成しないにしてもJTAG経由でアプリとして動かす方が手軽かもしれませんが.