KV260でSystemVerilogでLEDチカしてみる
以前、Ultra96V2 向けにLチカ記事を書きましたが、今回は KV260 版です。
はじめに
さまざまなプログラミング
いわゆる「プログラミング」を行う環境や言語にはいろいろなものがあります。
- CPU をターゲットに Rust/Python/C++ などの言語で行うプログラミング
- GPU をターゲットに CUDA/SYCL/GLSL などの言語で行うプログラミング
- Programmable Logic をターゲットに HLS/SystemVerilog/VHDL/Chisel などの言語で行うプログラミング
いずれも、それぞれ良し悪しのあるの楽しいプログラミングの世界であり、すべての言語でチューリング完全なプログラミングができるという点であらゆる計算を記述することができます。
中でも今回は KV260を使った Programmable Logic でのプログラミングの話しです。
ここで今回題材とする AMD(Xilinx) の ZynqMP というプロセッサは、APU(Cortex-A53)、RPU(Cortex-R5)、GPU(Mali-400)、PL(Programmable Logic)が全部入ったプロセッサであり、これらを連携させて利用できます。
- いろいろなプログラミングを楽しめる
- プログラミングの向き不向きに対して適材適所でプログラムできる
というとても魅力的なプロセッサです。
PL(Programmable Logic) のプログラミング
Programmable Logic に対するプログラミングはさらに大きくわけて
- HLSなどの高位合成ツールを使ったデータフローレベルでのプログラミング
- SystemVerilog/VHDL/Chisel などを用いた RTL(Register Transfer Level) と呼ばれるサイクル単位の挙動を記述できるプログラミング
の2種類あります。
HLS開発は色んな所に情報がありますので、ここでは KV260 RTL プログラミングを SystemVerilog を用いて行う方法の最初の一歩について紹介したいと思います。
RTL プログラミングの適した領域としては
- CPUやキャッシュシステムなど、独自のプロセッサを作るなど
- 独自のAIエンジンや、Videoコーデックなどで、高効率なエンジンブロックを作るなど
- イメージセンサやLCD、モーターなど外部デバイスとのインターフェースのプログラム
- サイクル単位でタイミング制御したい/同期したいリアルタイムアプリケーション
などなど、いろいろな分野があります。
環境を揃えよう
Xilinx認定Ubuntu と Kria KV260 の紹介
ZynqMP では Linux を動かす事が可能で、いろいろなディストリビューションが適用可能なのですが、今回は、認定Ubuntu を使って説明したいと思います。
現在こちらで配布されている Ubuntu が使える ZynqMP のボードは
の5種類あるようですが、このなかで一番安くて手を出しやすいのが KV260 となります。
個人でも購入できる、購入先の候補としては
などいろいろありますが、半導体不足もあり在庫のあるところを探すのが良いかと思います。
私は個人にも販売が開始されて割と早い時期に AVNETさん のページから購入しましたが、当時は納品まで何か月も待つことになりました。
ボードが用意出来たら SDカードに Ubuntu 22.04 のイメージを書き込んで電源を入れれば起動します。
本記事で使っているイメージは iot-limerick-kria-classic-desktop-2204-x06-20220614-78.img.xz です。
今回は普通に初期イメージを起動するだけでOKですが、もう少し詳しい環境構築記事もこちらに書いておりますので、よろしければご参照ください。
開発ツール Vitis/Vivado
AMD(Xilinx)ではユーザー登録をすると開発ツールがダウンロードできるようになっています。
そしてなんと、規模の小さいデバイス向けにはこれらを無償で利用することができ、KV260 も無償で開発することができます。
今回は Vivado というツールを使うのですが、Vivado は Vitis という上位のツールにも含まれており、Vivado のみをインストールするよりも Vitis をインストールすることをオススメしておきます。
筆者は Windows 11 の WSL2 環境に Ubuntu 20.04 を入れており、ここに Vitis 2022.2 を入れております。
サイズは大きいのですが AMD(Xilinx)のダウンロードサイトからダウンロードできますので、頑張ってダウンロードとインストールを行ってください。
LEDを光らせる部品
しばし LEDチカチカ(略してLEDチカ)は IoT(Internet of Things) 開発においての Hello world とされるのですが、困ったことに KV260 はプログラムから操作できるようなLEDが実装されていません(KR260など他のはボード上にLEDを持っているようですが)。
似たようなことは、同じくIoT開発ボードとして有名な Rspberry PI でもあるようで、ブレッドボードなどを用いて LED を別途用意するというのが定番のようです。
私は下記の写真のような構成で LED を準備してみました。
とりあえずそのあたりに転がっていたものをかき集めて
- LED
- 100Ω の抵抗
- ブレッドボード
- ブレッドボード用の配線
を用意して接続しました。
これらのものが手元に転がってない方は、秋月電子さんなど、電子部品を扱っておられるお店を探せばきっと揃うと思います。
他にも Raspberry PI 用の Lチカ記事などを検索してみても参考になる情報は多いかと思います。
また、要は電圧の上下が見たいだけですので、LEDでなくとも、テスターとかロジアナとかオシロスコープなどで観測するという手もあるかもしれません。
LEDチカをやってみよう
今回は SystemVerilog を使って、Lチカを行おうと思います。
SystemVerilog を少し勉強すれば、クロックをカウントして、LEDを点滅させるプログラムの書き方は容易に想像がつくと思います。
問題は
- クロックってどこから供給されるの?
- LEDへの出力ってどう指定すればいいの?
- 書いたプログラムはどうやってコンパイル(合成)すればいいの?
- コンパイルしたバイナリ(bitstream)はどうやって実行すればいいの?
という、プログラミングをはじめたくてもはじめられない、プログラミング以前の各種疑問を解消する必要があります。
今回の記事はここにフォーカスしていきたいと思います。
Vivado でプロジェクトを作る
さて、様々な疑問を解決するには、開発環境が無視できません。
なのでまず最初に RTL合成ツールである Vivado を用いてプロジェクトを作ってしまいたいと思います。
Vivado は Verilog/SystemVerilog や VHDL などの RTL を記述できる言語で FPGA 用のプログラムを開発できるツールですが、ZynqMP の PL 部の開発にも利用できます。
Windows 版をインストールされた方はスタートメニューから Vivado が起動できると思います。
Linux 版をインストールされた方は、ターミナルから
$ source /tools/Xilinx/Vitis/2022.2/settings64.sh
$ vivado
のように、settings64.sh を読み込んだ後に vivado と打ち込めば起動できます。
Vivado を起動すると下記のような画面が出てくるので Create Project を選びましょう。
Next を選びます。
プロジェクト名を入力します
RTL Project を選びます
ここでは設定せずに Next で進みます。
ここでボードを選びます。
ここでボードをKV260を探し出して選びます。
新規プロジェクトが出来上がり初期状態となります。
ブロックデザイナを利用して PS からクロックを引き出す
KV260 は PL(Programmable Logic)に繋がる外部端子にクロックはありません。
しかしクロックに同期して動くロジックを記述するRTLプログラミングではまずクロックが必要です。
ではどうするかというと、PS(Processor System)からクロックを貰うということが必要になります。
こういった部分がしばし初見泣かせになりかねないので、しっかりと説明していきます。
物理的には ZynqMP の LSI は PS と PL の2つの部分に分けられます。
PLはLSIの一部分に過ぎないのですが、PLのプログラミング上の概念としては、
「LSI全体の中にまずPLがあり、その中にPSがある」
というプログラミングモデルになっていますので、注意が必要です。
ですので、RTL プログラミングの中で定義する1モジュールとしてPSが登場します。
次に Vivado には、Block Designer という、予め用意したコアを GUI で接続することでプログラミングを行うツールがあります。
今流行のローコード開発と言えなくもないですが、「RTLプログラミングを楽しむ」のが本記事の目的です。
一方で、PSからクロックを取り出すだけに使うには大変便利なツールですので、ここだけは Block Designer を使うことにします。
まず SystemVerilog でプログラミングを始める前に Create Block Design を選びます。
Diagram が開きますので + ボタンを押して Zynq UltraScale MPSoC を選びます。
「Run Block Automation」を選びます。
ここで Apply Board Preset にチェックが入っていることを確認します。
PS はいろんな設定がありますが、プロジェクト作成時に KV260 を選んでいますので、適した設定が行われます。
pl_clk0 というクロックが出てきている端子があるので、右クリックして 「Make External」を選びます。
そうすると Block Design の外に信号が引き出されます。
また、このままの状態だと、今回は未使用ではあるのですが PS から PL へAXIバスでアクセスとして初期設定されている M_AXI_HPM0_FPD と M_AXI_HPM1_FPD のクロックである maxihpm0_aclk と maxihpm1_aclk にクロックが供給されていないとしてエラーが出てしまうので、クロックを繋いでおきます。
マウスでドラッグするだけで結線できます。
Block Design はこれで完成です。
いよいよ SystemVerilog でプログラムを書く
プログラムを書くためにはソースコードを新規作成する必要があります。
先ほど作ったブロックデザイナを配下に持つトップネットを SystemVerilog で作成してみたいと思います。
ソースファイルは予め別のエディタで作成しておいて追加したり Vivado で新規作成したりできます。今回は後者の方法です。
Sources タブに切り替えて、Design Source のところで右クリックして 「Add Source...」 を選びます。
ダイアログが出ますので「Add or create design source」にチェックを付けて Next を押します。
今回は新規に作成するので 「Create File」を押します。
新規作成するソースコードの種類とファイル名を指定します。
ここでは SystemVerilog を設定し、top.sv という名前にしています。
Verilog は知ってるけど、SystemVerilog はわからない、という方も基本的にはほぼ上位互換ですので、SystemVerilog を選んでおくことをオススメしておきます。
Finsh を押します。
ここで、ソースコードの初期状態としてポート宣言などができます。ここでは何もせずに OK を押して先に進みます。
新規のソースコードが追加されます。
ひな型としてソースコードが出来上がります。
このままここに書き足しても良いですし、一度すべて削除して真っ白にしてから書き直しても構いません。
さっそく下記のような LED を点滅させるプログラムを書いてみました。
なお、コメントは日本語で書いておりますが、Vivado のエディタだと日本語は文字化けすると思いますので悪しからずご了承ください。
ここで初めて「Verilog の本で勉強した世界」になります。教科書に書かれていることを教科書通りやろうとすると、その前段階にいろいろあるわけです。
`timescale 1ns / 1ps
module top(
output var logic [0:0] led // LED用に出力を 1bit 定義
);
// ブロックデザインからクロックを引き出す
logic clk;
design_1 i_design1(.pl_clk0_0(clk));
// クロックで動作する26bitのカウンタを作成
logic [25:0] counter = 0;
always_ff @(posedge clk) begin
counter <= counter + 1;
end
// カウンタの25bit目をLEDに出力
assign led = counter[25];
endmodule
すこし、解説しておくと。先ほどブロックデザイナで作成したブロックを design_1 という名前で利用しています。先ほど作成した pl_clk0_0 というポートからクロックを取り出しています。初期状態で 100MHz が供給されています。
その後、26bit のカウンタ変数を定義し、クロックの立上りの度にインクリメントしています。最後に led ポートにカウンタの 25bit 目の 0 or 1 を出力しています。カウンタはオーバーフローすると 0 に戻りますので、led は点滅を繰り返すはずです。
以上で、Verilog の 教科書に書いてある話はおしまいです。
以降はこのプログラムが実機で動くようにしていきます。
「LED のポートがどこにつながるかまだ何の設定もしてないのでは?」と思われるかとは思いますが、後で行うことにしてここで一旦論理合成を行います。
Programmable Logic のコンパイルは、論理合成(Synthesis) と 配置配線(Implementation) の2段階で行われるのですが、論理合成までは「実際にLEDが何番ピンに繋がってるのか?」などの情報は不要ですので、RTLとして正しいソースコードになっていれば論理合成を行うことができます。
C/C++ での組み込みプログラム開発で、メモリマップ(リンカスクリプト)を与えなくてもソース単体のコンパイルだけはできてしまうのに似ていますね。
論理合成(Synthesis)を行うには F11 を押すか、ツールバーから「Run Synthesis」を選んでください。
合成にはそれなりの時間がかかりますが、エラーにならなければ、しばらく待つと合成が完了して下記のようなダイアログが出ると思います。
Open Systhesized Design を選んで、論理合成済みのデザインを開いてみましょう。
もしこのダイアログで開き損ねても、Vivado のウィンドウの左側にある Flow Navigator から「Open Systhesized Design」を選べば開くことができます。
LED 出力を接続する
さて、無事に 論理合成(Synthesis)結果を開くことができたら、配置配線の制約として LED を設定します。
画面下で「I/O Ports」というタブを選ぶと、下記のように作成したプログラムのポートを実際のI/Oピンに接続する画面が出てきます。
ここでは I/O ピンに 「H12」、I/O Std に 「LVCMOS33」、Drive Strength に 16mA を指定しています。これがどうしてこうなるのかはこの後に説明します。
設定して保存しようとすると、制約ファイルの名前を聞いてきます。ここでは top.xdc という名前で保存しています。
xdc は Xilinx の制約ファイルの拡張子の慣例のようです。
top.xdc というファイル名で、下記のようなファイルが出来上がったと思います。
set_property DRIVE 16 [get_ports {led[0]}]
set_property PACKAGE_PIN H12 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
GUI を使わずに、初めから xdc ファイルをテキストエディタで直接書くことも出来きます。
あとは、Generate Bitstream ボタンを押して、bitstream を生成するところまで一気にやってしまいます。
完了すると、このようなダイアログが出ます。
OK を押して閉じて構いません。
プロジェクトファイルの下に
kv260_led_blinking.runs/impl_1/top.bit
というファイルが生成されているはずです。
LED 出力の配線を調べる
さて、先ほど、「H12」、I/O Std に 「LVCMOS33」という設定をしましたがどういう事でしょうか?
まず、Xilinx のサイトから KV260 の回路図がダウンロードできますので、見てみましょう。
今回は KV260 の PMOD(J2) の1番ピンと GND である5番ピンの間に LED と抵抗を直列に接続されるようにブレッドボードを結線しています。
回路図を見ると J2 の 1番ピンは PMOD_HDA11 という信号名です。
追いかけていくと、R276 を経由して HDA11 という信号名 に変わった後、SOM240_1 の A17 ピンに繋がっていることがわかります。
ここから先、K26 SOM モジュールになるのですが、現時点ではなんと
- K26 SOM モジュールの回路図は公開されていない
- Vivado のツールでは K26 SOM のピンではなく、モジュール上の ZynqMP LSI のピンを指定させる
という、少しめんどくさいことになっています。
AMD(Xilinx)としては、K26 SOM をモジュールとして商品にしたいようなので、いずれは Vivado で直接モジュールのコネクタピンが指定できるようになるのではないかと思いますが、現時点ではツールが追い付いていないようです。
ですので、こちら の資料のところから XTP685 - Kria K26 SOM XDC File (v1.0) を探し出して、著名したうえでダウンロードしてきます。
そして、ダウンロードしてきた xdc を検索すると som240_1_a17 が H12 ピンであることをようやく知ることができるわけです。
次に、H12 ピンのI/O設定をどうするべきか調べます。
ダウンロードした XTP685 のファイルから H12 は I/Oバンク45 に属しており VCCO は som240_1_b13 から供給されていることがわかります。
そして再び回路図に戻ってくることで PL_3V3(3.3V) が供給されていることがわかりますね。
I/O ピンの設定は、VCCO の電圧に応じて選択できるものが決まってきますが、LED の点滅をさせるばあい、単なるデジタルレベルで H/L の制御ができればよいですので LVCMOS33 を設定しています。
これで、出力値が 0 の時は 0V、出力値が 1 の時は 3.3V が出力され、電流値としては最大で 16mA まで流すことができます。
よく電子工作で見かけるオーソドックスな赤や緑などのLEDの順方向電圧がだいたい 2.1V とかですので
(3.3V - 2.1V) / 100Ω = 12mA
となりますので、16mA のドライブ能力で何とかなる計算です。
作った回路を実行する
kv260_led_blinking.runs/impl_1/top.bit を、KV260 の Ubuntu 環境にコピーします。
scp なり、Samba を使うなりいろいろな方法があると思いますので、PC と エッジボードで動く Linux(Ubuntu) の間でファイルコピーできるようになりましょう。
ファイルをコピーした後は、ターミナルで下記のように実行すれば動きます。
sudo sh -c "echo 0 > /sys/class/fpga_manager/fpga0/flags"
sudo mkdir -p /lib/firmware
sudo cp top.bit /lib/firmware/
sudo sh -c "echo top.bit > /sys/class/fpga_manager/fpga0/firmware"
下記のような感じで点滅すればOKです。
急にファンが煩くなったかと思いますが、もとからあったファン制御の回路が、Lチカ回路に上書きされたためです。
もしファン制御もしたい方はこちらの記事を参照ください。
配置配線を覗いてみる(おまけ)
最後に余談ですが、今回の回路の中を覗いてみます。
プログラミング言語で宣言した変数が実際のフリップフロップに割り当てられて、配線が外部のピンに繋がっているところまでビジュアルに見ることができてとても楽しいです。
おわりに
余力があれば、xsim でのシミュレーションや ILA を使った実機デバッグの話も書きたかったのですが、力尽きたので今回はここまでです。
Verilog などの教科書をみてプログラミング言語を理解しても、いざ動かそうとすると言語以外に覚えないといけないことがたくさんあるかと思います。
KV260 のようにダウンロードした Ubuntu イメージを SDカードに書き込むだけで起動するボードが出てきたおかげで、ZynqMP を IoT プログラミングのプラットフォームとして選ぶ際のとっつきやすさは格段に高まったように思います。
一方で、「Ubuntu は立ち上げたし、Verilog 等の RTLプログラミングの入門書も読み始めてるよ」という方が、「どうやってこの RTL のコード動かせばいいの?」となってしまったときに Lチカ は最初の一歩として非常に重要と思います。
逆にLチカをしっかり理解すれば、端子を増やして実際にいろいろな RTL をプログラムしながら学んでいくことが可能になるのではないでしょうか。
本記事が何かの参考になれば幸いに思います。
参考コード
本記事と全く同じではありませんが、筆者のオープンソースの中にKV260でLチカを行うサンプルを追加しております。
よろしければ参考にされてください。
Discussion