2025/11/22
basys3-mbv32.tcl修正。
vivadoをホームディレクトリ以外の場所から起動した時に正常動作しない問題を修正。
はじめに
内容は前の記事とほぼ同じでボードがBASYS3に変わっただけです。同じ内容を投稿しても仕方無いので、Lチカの手順を追加しました。
前の記事を投稿後、手元にBASYS3がある事を思い出しました。このボードは特に使う事なく持て余していたので、一度は使いたいという個人的な事情で作成した記事になります。
システム概要
以下は実装するBlock Designです。
周辺デバイスはInterrupt Controller, Timer, UART, GPIOx3 となっています。
RAMサイズは128KiBになります。
開発環境
Board
BASYS3
Tools
Vivado 2025.1
Vitis 2025.1
Zephyr RTOS
Base Version - 3.7.0
Xilinx Repository Tag - 2025.1
Host OS
Ubuntu 24.04 Desktop
今回の作業でVitisは使いませんが、System Device Treeを生成する際にVitisに含まれるツールやデータを利用する為、Vitisのインストールが必要です。
構成
Zephyr RTOS
Zephyr RTOSはZephyr ProjectがLTS版としてリリースしている3.7.0にAMDが提供するソースを組み込んだ環境でビルドします。
Vivado作業手順
プロジェクト作成
最初にTCLスクリプトを使い、プロジェクトの作成を行います。basys3-mbv32.tclの最初に定義しているパラメータは環境に合わせて変更して下さい。
basys3-mbv32.tcl - プロジェクト作成&ビルド実行
# parameters
set JOBS 8
set PROJ_NAME basys3-mbv32
set PROJ_DIR $env(HOME)/ws/vivado/$PROJ_NAME
set SCRIPT_DIR [file dirname [info script]]
set XDC_FILE $SCRIPT_DIR/$PROJ_NAME.xdc
set BD_FILE $SCRIPT_DIR/${PROJ_NAME}-bd.tcl
set BD_DESIGN design_1
# import the local functions
source $SCRIPT_DIR/local-funcs.tcl
# set the board repository path
set_param board.repoPaths $env(HOME)/.Xilinx/Vivado/2025.1/xhub/board_store
# create the project
if {! [local::is_installed digilentinc.com:basys3:part0:1.2]} {
xhub::refresh_catalog [xhub::get_xstores xilinx_board_store]
xhub::install [xhub::get_xitems digilentinc.com:xilinx_board_store:basys3:1.2]
}
create_project $PROJ_NAME $PROJ_DIR -part xc7a35tcpg236-1
set_property board_part digilentinc.com:basys3:part0:1.2 [current_project]
# create the block design
create_bd_design $BD_DESIGN
source $BD_FILE
save_bd_design
# create the wrapper file
make_wrapper -files [get_files $PROJ_DIR/$PROJ_NAME.srcs/sources_1/bd/$BD_DESIGN/$BD_DESIGN.bd] -top
add_files -norecurse $PROJ_DIR/$PROJ_NAME.gen/sources_1/bd/$BD_DESIGN/hdl/${BD_DESIGN}_wrapper.v
# generate the bitstream file
update_compile_order -fileset sources_1
launch_runs impl_1 -to_step write_bitstream -jobs $JOBS
wait_on_run impl_1
# write the XSA file
write_hw_platform -fixed -include_bit -force -file $PROJ_DIR/$PROJ_NAME.xsa
basys3-mbv32-bd.tcl - Block Design作成
# Clock
local::create_xip clk_wiz:6.0 system_clock {
CONFIG.USE_RESET false
CONFIG.CLK_IN1_BOARD_INTERFACE sys_clock
}
apply_bd_automation -rule xilinx.com:bd_rule:board -config { Board_Interface sys_clock } [get_bd_pins system_clock/clk_in1]
# MicroBlaze-V
set mbv [local::create_xip microblaze_riscv:1.0 microblaze_riscv_0]
apply_bd_automation -rule xilinx.com:bd_rule:microblaze_riscv -config {
axi_intc {1}
axi_periph {Enabled}
debug_module {Debug Enabled}
ecc {None}
local_mem {128KB}
preset {Real-time}
} $mbv
set_property -dict {
CONFIG.C_USE_DCACHE 0
CONFIG.C_USE_ICACHE 0
} $mbv
# Configure the AXI Interrupt Controller
set_property CONFIG.C_HAS_FAST {0} [get_bd_cells microblaze_riscv_0_axi_intc]
# Configure the AXI Interconnect
set_property CONFIG.NUM_MI {6} [get_bd_cells microblaze_riscv_0_axi_periph]
# AXI Timer
local::create_xip axi_timer:2.0 axi_timer_0
# AXI UART Lite
local::create_xip axi_uartlite:2.0 axi_uartlite_0 {
CONFIG.C_BAUDRATE 115200
}
apply_board_connection -board_interface usb_uart -ip_intf axi_uartlite_0/UART -diagram $BD_DESIGN
# Connect buttons and slide switches to GPIO
local::create_xip axi_gpio:2.0 axi_gpio_inputs {
CONFIG.C_INTERRUPT_PRESENT 1
}
apply_board_connection -board_interface push_buttons_4bits -ip_intf axi_gpio_inputs/GPIO -diagram $BD_DESIGN
apply_board_connection -board_interface dip_switches_16bits -ip_intf axi_gpio_inputs/GPIO2 -diagram $BD_DESIGN
# Connect LEDs to GPIO
local::create_xip axi_gpio:2.0 axi_gpio_led_16bits
apply_board_connection -board_interface led_16bits -ip_intf axi_gpio_led_16bits/GPIO -diagram $BD_DESIGN
# Connect 7-segment display to GPIO
local::create_xip axi_gpio:2.0 axi_gpio_7seg {
CONFIG.C_INTERRUPT_PRESENT 0
CONFIG.C_DOUT_DEFAULT 0x0000000F
}
apply_board_connection -board_interface seven_seg_led_an -ip_intf axi_gpio_7seg/GPIO -diagram $BD_DESIGN
apply_board_connection -board_interface seven_seg_led_disp -ip_intf axi_gpio_7seg/GPIO2 -diagram $BD_DESIGN
# Interrupt signal
set_property CONFIG.NUM_PORTS {3} [get_bd_cells microblaze_riscv_0_xlconcat]
local::connect_pins axi_timer_0/interrupt microblaze_riscv_0_xlconcat/In0
local::connect_pins axi_uartlite_0/interrupt microblaze_riscv_0_xlconcat/In1
local::connect_pins axi_gpio_inputs/ip2intc_irpt microblaze_riscv_0_xlconcat/In2
# Connect reset signals
apply_board_connection -board_interface reset -ip_intf rst_system_clock_100M/ext_reset -diagram $BD_DESIGN
local::connect_pins rst_system_clock_100M/peripheral_aresetn {
microblaze_riscv_0_axi_periph/M01_ARESETN
microblaze_riscv_0_axi_periph/M02_ARESETN
microblaze_riscv_0_axi_periph/M03_ARESETN
microblaze_riscv_0_axi_periph/M04_ARESETN
microblaze_riscv_0_axi_periph/M05_ARESETN
axi_timer_0/s_axi_aresetn
axi_uartlite_0/s_axi_aresetn
axi_gpio_inputs/s_axi_aresetn
axi_gpio_led_16bits/s_axi_aresetn
axi_gpio_7seg/s_axi_aresetn
}
# Connect clock signals
local::connect_pins system_clock/clk_out1 {
microblaze_riscv_0_axi_periph/M01_ACLK
microblaze_riscv_0_axi_periph/M02_ACLK
microblaze_riscv_0_axi_periph/M03_ACLK
microblaze_riscv_0_axi_periph/M04_ACLK
microblaze_riscv_0_axi_periph/M05_ACLK
axi_timer_0/s_axi_aclk
axi_uartlite_0/s_axi_aclk
axi_gpio_inputs/s_axi_aclk
axi_gpio_led_16bits/s_axi_aclk
axi_gpio_7seg/s_axi_aclk
}
# Connect AXI interfaces
local::connect_ifs microblaze_riscv_0_axi_periph/M01_AXI axi_timer_0/S_AXI
local::connect_ifs microblaze_riscv_0_axi_periph/M02_AXI axi_uartlite_0/S_AXI
local::connect_ifs microblaze_riscv_0_axi_periph/M03_AXI axi_gpio_inputs/S_AXI
local::connect_ifs microblaze_riscv_0_axi_periph/M04_AXI axi_gpio_led_16bits/S_AXI
local::connect_ifs microblaze_riscv_0_axi_periph/M05_AXI axi_gpio_7seg/S_AXI
# Epilogue
assign_bd_address
regenerate_bd_layout
local-funcs.tcl - 内部利用関数の定義
#
# local functions
#
namespace eval local {
proc is_installed {board} {
return [llength [get_board_parts -quiet $board]]
}
proc create_xip_cell {ip name args} {
set cell [create_bd_cell -type ip -vlnv xilinx.com:ip:$ip $name]
if {[llength $args] > 0} {
set_property -dict [lindex $args 0] $cell
}
return $cell
}
proc create_inline_hdl {ihdl name args} {
set cell [create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:$ihdl $name]
if {[llength $args] > 0} {
set_property -dict [lindex $args 0] $cell
}
return $cell
}
proc connect_pins_list {obj_a obj_b pins_list} {
if {[llength $pins_list] > 0} {
set pins [lindex $pins_list 0]
set pin_a $obj_a/[lindex $pins 0]
set pin_b $obj_b/[lindex $pins 1]
connect_pins $pin_a $pin_b
connect_pins_list $obj_a $obj_b [lrange $pins_list 1 end]
}
}
proc connect_pins {src_pin dst_pins args} {
if {[llength $args] > 0} {
connect_pins_list $src_pin $dst_pins [lindex $args 0]
} else {
set len [llength $dst_pins]
if {$len >= 1} {
connect_bd_net [get_bd_pins $src_pin] [get_bd_pins [lindex $dst_pins 0]]
if {$len > 1} {
connect_pins $src_pin [lrange $dst_pins 1 end]
}
}
}
}
proc connect_ifs {if_a if_b} {
connect_bd_intf_net [get_bd_intf_pins $if_a] [get_bd_intf_pins $if_b]
}
proc connect_bd_port {bd_port bd_pins} {
set len [llength $bd_pins]
if {$len >= 1} {
connect_bd_net [get_bd_ports $bd_port] [get_bd_pins [lindex $bd_pins 0]]
if {$len > 1} {
connect_bd_port $bd_port [lrange $bd_pins 1 end]
}
}
}
proc make_pin_external {bd_pin name} {
make_bd_pins_external [get_bd_pins $bd_pin] -name $name
}
proc make_if_external {bd_if name} {
make_bd_intf_pins_external [get_bd_intf_pins $bd_if] -name $name
}
}
全てのファイルを同じフォルダ配下に置いたら、下記コマンドを実行します。実行するとXSAファイルの作成まで実行されます。Vivadoの実行環境にBASYS3のボード定義ファイルがインストールされていない場合、ボード情報のダウンロードが行われて多少時間が掛かります。
source /tools/Xilinx/2025.1/Vivado/settings64.sh
vivado -source basys3-mbv32.tcl
スクリプトの実行が正常終了するとBitstream Generation Completedウィンドウが開きますのでCancelボタンを押して終了して下さい。この時点でプロジェクト配下にXSAファイルが生成されています。
Zephyrビルド手順
前準備
Zephyr Getting Started Guideに記載されている通り、最初はZephyr 3.7.0 Getting Started Guideの手順を実行してビルド環境を用意して下さい。
Install the Zephyr SDKに記載されている手順まで実行します。最後に記述されているudev rulesのインストールは実行しなくても問題ありません。
次に作業に利用するファイル名、ディレクトリ名を定義します。作業環境に合わせて修正して下さい。
# parameters
PROJ_NAME=basys3-mbv32
XSA_FILE=$HOME/ws/vivado/$PROJ_NAME/$PROJ_NAME.xsa
SDT_DIR=$HOME/${PROJ_NAME}_sdt
プロジェクト作成
オリジナルのソースツリーにBASYS3固有のソースツリーを追加します。
本記事ではSDK及びプロジェクトがホームディレクトリ配下にある事を前提に記述します。
最初にphythonのvenvを有効化します。インストール手順の実行直後や実行済みの場合は不要です。
source $HOME/zephyrproject/.venv/bin/activate
次にMicroBlaze-V固有のソースツリーを追加します。
cd $HOME/zephyrproject/
mv zephyr zephyr.upstream
git clone https://github.com/Xilinx/zephyr-amd.git -b xlnx_rel_v2025.1 zephyr
west update
west lopper-install
Devicetree更新
プロジェクト用DevicetreeをXSAファイルから抽出したDevicetreeで更新します。
# create the SDT files
. /tools/Xilinx/2025.1/Vivado/settings64.sh
xsct -eval "sdtgen set_dt_param -dir $SDT_DIR -xsa $XSA_FILE ; sdtgen generate_sdt"
# update the devicetree for Zephyr Project
cd $HOME/zephyrproject/
LOPPER_DTC_FLAGS="-b 0 -@" west lopper-command -p microblaze_riscv_0 -s $SDT_DIR/system-top.dts -w $HOME/zephyrproject/zephyr
これでビルドの準備は完了です。
Hello Worldのビルド
最初に定番のHello Worldのビルドと実行を試します。
ビルド手順は以下の通りです。
cd $HOME/zephyrproject/zephyr/tests/misc/test_build/
west build -p -b mbv32 .
次にHostPCとTarget BoardをUSBで接続した後、下記コマンドを実行します。
cd $HOME/zephyrproject/zephyr/tests/misc/test_build/
west flash --runner xsdb --elf-file ./build/zephyr/zephyr.elf --bitstream $SDT_DIR/$PROJ_NAME.bit
実行に成功すると、(最初の2024.2の表示が気になりますが)メッセージがシリアルに出力され続けます。
その他のテストコードをビルド
Zephyrの開発環境にはHello Worldの他にも色々と試せるテストコードが用意されています。
例としてtests/benchmarks/latency_measure/を実行する場合は以下の手順となります。
cd $HOME/zephyrproject/zephyr/tests/benchmarks/latency_measure/
west build -p -b mbv32 .
west flash --runner xsdb --elf-file ./build/zephyr/zephyr.elf --bitstream $SDT_DIR/$PROJ_NAME.bit
ZephyrでLチカ
概要
Zephyrにはblinkyという名前でLチカのプログラムが用意されています。ここではBASYS3でのblinky実行手順を記述します。
blinkyは$HOME/zephyrproject/zephyr/samples/basic配下にあります。
Devicetree追加
Zephyrの特徴としてDevicetreeの利用があります。実はblinkyはこのままではコンパイルできず、Devicetreeで制御するLEDを定義する必要があります、
$HOME/zephyrproject/zephyr/samples/basic/blinky/README.rstを参照してファイルを追加します。
#include "dt-bindings/gpio/gpio.h"
/ {
aliases {
led0 = &myled0;
};
leds {
compatible = "gpio-leds";
myled0: led_0 {
gpios = <&axi_gpio_led_16bits 0 GPIO_ACTIVE_HIGH>;
};
};
};
プロジェクト配下にboards/<board name>.overlayという名前でファイルを作成すると、ビルド時に自動的に組み込まれます。この機能をうまく使うと、ボード固有の記述をプログラムから分離可能になります。
ビルド&実行
ビルドと実行の手順は同じです。
cd $HOME/zephyrproject/zephyr/samples/basic/blinky
west build -p -b mbv32 .
west flash --runner xsdb --elf-file ./build/zephyr/zephyr.elf --bitstream $SDT_DIR/$PROJ_NAME.bit
実行に成功するとシリアルのメッセージ出力と共にLEDがチカチカします。
*** Booting Zephyr OS build xilinx_v2024.2-111-g986cf11c05ea ***
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
....
簡単にDevicetreeの説明
boards/mbv32.overlayでBASYS3固有の記述は以下の部分です。
myled0: led_0 {
gpios = <&axi_gpio_led_16bits 0 GPIO_ACTIVE_HIGH>;
};
GPIOの制御対象をled_0というNodeで定義しており、具体的な制御対象はaxi_gpio_led_16bitsの0ビットとなります。
led_0にはmyled0というLabelが定義されています。Labelを定義すると以下の様に参照可能になります。
aliases {
led0 = &myled0;
};
Label名を使わない場合、Nodeで記述する形になります。
aliases {
led0 = &{/leds/led_0};
};
Nodeで指定するとファイルパスの様に記述が長くなりますので、通常はLabelが使われます。
axi_gpio_led_16itsは名前の通り16-bit長の出力を持ちますので、gpiosの0の部分は0-15の範囲で指定可能です。適当に変更すると、チカチカするLEDが変わります。
axi_gpio_led_16itsはBoard定義のDTSファイル内で定義されているGPIO deviceのLabelです。Board定義ファイルは$HOME/zephyrproject/zephyr/boards/amd/mbv32/mbv32.dtsになります。
このファイルの中身を見ると、以下の様にaxi_gpio_led_16itsが定義されています。
axi_gpio_led_16bits: gpio@40020000 {
xlnx,all-inputs = <0x0>;
xlnx,tri-default = <0xffffffff>;
xlnx,gpio2-width = <0x20>;
xlnx,dout-default-2 = <0x0>;
gpio-controller;
... 省略 ...
};
mbv32.overlayではここのaxi_gpio_led_16bitsを参照しています。Zephyrのビルド時にはこのmbv32.dtsがプログラムからの参照対象として一緒にコンパイルされるのですが、この時mbv32.overlayの内容がmbv32.dtsに上書きされてからコンパイルが行われます。
上書きなので双方のファイルで同じ定義が存在する場合はmbv32.overlayの内容が使われます。また、mbv32.dtsに存在しない内容がmbv32.overlayに記述されている場合は記述内容が追加される形となります。
mbv32.overlayが上書きされた結果は$HOME/zephyrproject/zephyr/samples/basic/blinky/build/zephyr/zephyr.dtsで確認できます。
おわりに
今回のBlock DesignではLEDの他にPush ButtonやSlide Switch、7-Segment Displayも接続していますので、Zephyrの勉強がてらこれらの制御についても書ければと思っています。
