1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zephyr on MicroBlaze V (Arty A7-100T)

Last updated at Posted at 2025-08-24

はじめに

AMDがFPGA用に提供するRISC-VコアのMicroBlaze V上でZephyr RTOSを動作させる手順について記述します。

ターゲットボードはDigilent社のArty A7-100Tです。Zephyr RTOSを動作させる手順はZephyr Getting Started Guideに記載されている通りです。

ZephyrはArty A7に実装されているDDR Memory上で動作させます。DDR Memoryを利用したBlock Designの作成には少しクセがあり、そこの作業手順が主な内容になります。

参考記事

今回の作業ではMIG(Memory Interface Generator)の使い方について以下の記事を参考にさせて頂きました。

Arty A7 100TボードとMicroBlazeとMIGでHello Worldと時間計測
Tutorial: MicroBlaze with DDR3 SDRAM

システム概要

以下は実装するBlock Designです。

BD-1.png

周辺デバイスはInterrupt Controller, Timer, UARTの3つとなっています。

開発環境

Board
Arty A7-100T
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スクリプトと制約ファイルを使い、プロジェクトの作成を行います。arty-a7-mbv32.tclの最初に定義しているパラメータは環境に合わせて変更して下さい。

TCLスクリプトで作成されたプロジェクトは配置配線でエラーになります。プロジェクトを作成した後は、後に記述するGUI操作を実行してMIGの設定を変更する必要があります。

arty-a7-mbv32.tcl - プロジェクト作成
# parameters
set PROJ_NAME arty-a7-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

# import the local functions
source $SCRIPT_DIR/local-funcs.tcl

# create the project
if {! [local::is_installed digilentinc.com:arty-a7-100:part0:1.1]} {
    xhub::refresh_catalog [xhub::get_xstores xilinx_board_store]
    xhub::install [xhub::get_xitems digilentinc.com:xilinx_board_store:arty-a7-100:1.1]
}
create_project $PROJ_NAME $PROJ_DIR -part xc7a100tcsg324-1
set_property board_part digilentinc.com:arty-a7-100:part0:1.1 [current_project]

# create the block design
create_bd_design design_1
source $SCRIPT_DIR/$BD_FILE
save_bd_design

# create the wrapper file
make_wrapper -files [get_files $PROJ_DIR/$PROJ_NAME.srcs/sources_1/bd/design_1/design_1.bd] -top
add_files -norecurse $PROJ_DIR/$PROJ_NAME.gen/sources_1/bd/design_1/hdl/design_1_wrapper.v

# add the constraint file
add_files -fileset constrs_1 -norecurse $XDC_FILE
import_files -fileset constrs_1 $XDC_FILE
update_compile_order -fileset sources_1
arty-a7-mbv32-bd.tcl - Block Design作成
# MIG
create_bd_cell -type ip -vlnv xilinx.com:ip:mig_7series:4.2 mig_7series_0
apply_board_connection -board_interface "ddr3_sdram" -ip_intf "mig_7series_0/mig_ddr_interface" -diagram "design_1" 
delete_bd_objs [get_bd_nets clk_ref_i_1] [get_bd_ports clk_ref_i]
delete_bd_objs [get_bd_nets sys_clk_i_1] [get_bd_ports sys_clk_i]
local::connect_pins mig_7series_0/ui_addn_clk_0 mig_7series_0/clk_ref_i
local::make_pin_external mig_7series_0/sys_rst sys_resetn

# MIG Clock and Buffer
local::create_xip_cell util_ds_buf:2.2 sys_clock_bufg {
    CONFIG.C_BUF_TYPE   BUFG
}
local::make_pin_external sys_clock_bufg/BUFG_I sys_clock
local::connect_pins sys_clock_bufg/BUFG_O mig_7series_0/sys_clk_i

# MIG Reset
local::create_xip proc_sys_reset:5.0 mig_reset
local::connect_pins mig_reset mig_7series_0 {
    {slowest_sync_clk ui_clk}
    {dcm_locked mmcm_locked}
    {peripheral_aresetn aresetn}
}
local::create_inline_hdl ilvector_logic:1.0 inv_ui_reset {
    CONFIG.C_OPERATION  not
    CONFIG.C_SIZE       1
}
local::connect_pins mig_7series_0/ui_clk_sync_rst inv_ui_reset/Op1
local::connect_pins mig_reset/ext_reset_in inv_ui_reset/Res 

# Memory Interconnect
local::create_xip axi_interconnect:2.1 mem_interconnect {
    CONFIG.NUM_MI   1
    CONFIG.NUM_SI   2
}
local::connect_ifs mem_interconnect/M00_AXI mig_7series_0/S_AXI
local::connect_pins mem_interconnect/M00_ACLK mig_7series_0/ui_clk
local::connect_pins mem_interconnect/M00_ARESETN mig_reset/peripheral_aresetn

# System Reset and Clock
local::create_xip proc_sys_reset:5.0 system_reset
local::connect_bd_port sys_resetn system_reset/ext_reset_in

local::create_xip clk_wiz:6.0 system_clock {
    CONFIG.USE_RESET    false
}
local::connect_pins system_clock/clk_in1 sys_clock_bufg/BUFG_O
local::connect_pins system_clock/clk_out1 system_reset/slowest_sync_clk
local::connect_pins system_clock/locked system_reset/dcm_locked

# MicroBlaze V
set mbv [local::create_xip microblaze_riscv:1.0 microblaze_riscv_0]
local::connect_pins microblaze_riscv_0/Clk system_clock/clk_out1
local::connect_pins microblaze_riscv_0/Reset system_reset/mb_reset
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       {16KB}
    preset          {Real-time}
} $mbv
local::connect_ifs microblaze_riscv_0/M_AXI_DC mem_interconnect/S00_AXI
local::connect_ifs microblaze_riscv_0/M_AXI_IC mem_interconnect/S01_AXI
local::connect_pins mem_interconnect system_clock {
    {ACLK clk_out1}
    {S00_ACLK clk_out1}
    {S01_ACLK clk_out1}
}
local::connect_pins mem_interconnect system_reset {
    {ARESETN peripheral_aresetn}
    {S00_ARESETN peripheral_aresetn}
    {S01_ARESETN peripheral_aresetn}
}

# 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 {3} [get_bd_cells microblaze_riscv_0_axi_periph]
local::connect_pins microblaze_riscv_0_axi_periph/M01_ACLK system_clock/clk_out1
local::connect_pins microblaze_riscv_0_axi_periph/M02_ACLK system_clock/clk_out1
local::connect_pins microblaze_riscv_0_axi_periph/M01_ARESETN system_reset/peripheral_aresetn
local::connect_pins microblaze_riscv_0_axi_periph/M02_ARESETN system_reset/peripheral_aresetn
local::connect_pins mdm_1/Debug_SYS_Rst mig_reset/mb_debug_sys_rst

# AXI Timer
local::create_xip axi_timer:2.0 axi_timer_0
local::connect_pins axi_timer_0/s_axi_aclk system_clock/clk_out1
local::connect_pins axi_timer_0/s_axi_aresetn system_reset/peripheral_aresetn
local::connect_pins axi_timer_0/interrupt microblaze_riscv_0_xlconcat/In0
local::connect_ifs axi_timer_0/S_AXI microblaze_riscv_0_axi_periph/M01_AXI

# AXI UART Lite
local::create_xip axi_uartlite:2.0 axi_uartlite_0 {
    CONFIG.C_BAUDRATE   115200
}
local::connect_pins axi_uartlite_0/s_axi_aclk system_clock/clk_out1
local::connect_pins axi_uartlite_0/s_axi_aresetn system_reset/peripheral_aresetn
local::connect_pins axi_uartlite_0/interrupt microblaze_riscv_0_xlconcat/In1
local::connect_ifs axi_uartlite_0/S_AXI microblaze_riscv_0_axi_periph/M02_AXI
local::make_if_external axi_uartlite_0/UART uart_0

# Epilogue
assign_bd_address
regenerate_bd_layout
arty-a7-mbv32.xdc - 制約ファイル
set_property PACKAGE_PIN C2 [get_ports sys_resetn]
set_property IOSTANDARD LVCMOS33 [get_ports sys_resetn]
set_property PACKAGE_PIN E3 [get_ports {sys_clock[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sys_clock[0]}]
set_property PACKAGE_PIN A9 [get_ports uart_0_rxd]
set_property PACKAGE_PIN D10 [get_ports uart_0_txd]
set_property IOSTANDARD LVCMOS33 [get_ports uart_0_rxd]
set_property IOSTANDARD LVCMOS33 [get_ports uart_0_txd]
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
    }
}

全てのファイルを同じフォルダ配下に置いたら、下記コマンドを実行します。実行するとVivadoが立ち上がり、プロジェクトとBlock Designが作成されます。Vivadoの実行環境にArty A7-100Tのボード定義ファイルがインストールされていない場合、ボード情報のダウンロードが行われて多少時間が掛かります。

source /tools/Xilinx/2025.1/Vivado/settings64.sh
vivado -source arty-a7-mbv32.tcl

MIG Configuration

スクリプト実行直後の状態では、まだBitstreamの生成は出来ません。MIGの設定を変更する必要があります。

Block Design右上にあるmig_7series_0をダブルクリックしてMemory Interface Generatorウィンドウを表示します。

BD-2.png

ウィドウが表示されたらNextボタンを7回クリックします。

MIG-1.png

FPGA Optionsが表示されたらSystem Clock[Single-Ended] から [No Buffer] に変更します。変更したらNextボタンを3回クリックします。

MIG-2.png

Pin Selectionが表示されたらValidateボタンをクリックして、表示されたDRC ValidationウィンドウのOKボタンをクリックして下さい。その後、Nextボタンを3回クリックして下さい。

MIG-3.png

Simulation Optionsが表示されたらAcceptをチェックしてNextを2回クリックして下さい。

MIG-4.png

Design Notesが表示されたら、Generateボタンをクリックして終了です。

MIG-5.png

Bitstream作成

メニューバーから [Flow] -> [Generate Bitstream] を選択します。

Save Projectウィンドウが表示されたらSaveボタンをクリックします。

BDSAVE-1.png

No Implementation Results Availableウィンドウが開いたらYesボタンをクリックします。

GEN-1.png

Launch RUnsウィンドウが開きますので、OKをクリックすると論理合成からBitstreamの生成まで実行されます。

Bitstreamが生成されるとBitstream Generation Completedウィンドウが表示されますのでCancelボタンをクリックして閉じて下さい。

GEN-3.png

XSAファイル作成

メニューバーから [File] -> [Export] -> [Export Hardware...] を選択して、ウィンドウが開いたらNextボタンをクリックします。

EXPORT-1.png

Include bitstream/binaryをチェックしてNextボタンをクリックします。

EXPORT-2.png

XSA File Nameを設定したらFinishボタンをクリックしてXSAファイルを作成します。以降ではファイル名をarty-a7-mbv32.xsaとして記述します。

EXPORT-3.png

Zephyrビルド手順

前準備

Zephyr Getting Started Guideに記載されている通り、最初はZephyr 3.7.0 Getting Started Guideの手順を実行してビルド環境を用意して下さい。

Install the Zephyr SDKに記載されている手順まで実行します。最後に記述されているudev rulesのインストールは実行しなくても問題ありません。

次に作業に利用するファイル名、ディレクトリ名を定義します。作業環境に合わせて修正して下さい。

# parameters
PROJ_NAME=arty-a7-mbv32
XSA_FILE=$HOME/ws/vivado/$PROJ_NAME/$PROJ_NAME.xsa
SDT_DIR=$HOME/${PROJ_NAME}_sdt

プロジェクト作成

オリジナルのソースツリーにMicroBlaze V固有のソースツリーを追加します。

本記事では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
west build -p -b mbv32 tests/misc/test_build/

次にHostPCとTarget BoardをUSBで接続した後、下記コマンドを実行します。

cd $HOME/zephyrproject/zephyr
west flash --runner xsdb --elf-file $HOME/zephyrproject/zephyr/build/zephyr/zephyr.elf --bitstream $SDT_DIR/$PROJ_NAME.bit

実行に成功すると、(最初の2024.2の表示が気になりますが)メッセージがシリアルに出力され続けます。

*** Booting Zephyr OS build xilinx_v2024.2-111-g986cf11c05ea ***
threadA: Hello World from riscv!
threadB: Hello World from riscv!
threadA: Hello World from riscv!
threadB: Hello World from riscv!
threadA: Hello World from riscv!
threadB: Hello World from riscv!
threadA: Hello World from riscv!
threadB: Hello World from riscv!
...

その他のテストコードをビルド

Zephyrの開発環境にはHello Worldの他にも色々と試せるテストコードがtestディレクトリ配下に用意されています。

例としてtests/benchmarks/latency_measure/を実行する場合は以下の手順となります。

cd $HOME/zephyrproject/zephyr
west build -p -b mbv32 tests/benchmarks/latency_measure

実行はHello Worldと同じ手順です。

おわりに

初めてZephyrに触れましたが、最近のRTOSはビルドシステム込みで提供されるので、ビルド環境が乱立していたuITRON世代には新鮮でした。

MicroBlaze VはEthernet Driverが未提供なので、早く正式サポートして貰いたいですね。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?