KR260でSystemVerilogでLEDチカしてみる
以前、KV260でSystemVerilogでLEDチカしてみるを書きましたが、今回は KR260 を扱える機会を得たので KR260版 を作ってみます。
KR260 は KV260 と違い、PL部に繋がる クロック も LED もボード上に実装されており、PS からクロックを貰ったり、PMOD に別途 LED を用意したりしなくてよい分、扱いやすいように思います。
なお本記事の一部で KV260 で作成したスクリーンショットを流用している箇所がありますので、都度読み替えて頂ければ幸いです。
はじめに
さまざまなプログラミング
いわゆる「プログラミング」を行う環境や言語にはいろいろなものがあります。
- CPU をターゲットに Rust/Python/C++ などの言語で行うプログラミング
- GPU をターゲットに CUDA/SYCL/GLSL などの言語で行うプログラミング
- Programmable Logic をターゲットに HLS/SystemVerilog/VHDL/Chisel などの言語で行うプログラミング
いずれも、それぞれ良し悪しのあるの楽しいプログラミングの世界であり、すべての言語でチューリング完全なプログラミングができるという点であらゆる計算を記述することができます。
中でも今回は KR260を使った 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開発は色んな所に情報がありますので、ここでは KR260 RTL プログラミングを SystemVerilog を用いて行う方法の最初の一歩について紹介したいと思います。
RTL プログラミングの適した領域としては
- CPUやキャッシュシステムなど、独自のプロセッサを作るなど
- 独自のAIエンジンや、Videoコーデックなどで、高効率なエンジンブロックを作るなど
- イメージセンサやLCD、モーターなど外部デバイスとのインターフェースのプログラム
- サイクル単位でタイミング制御したい/同期したいリアルタイムアプリケーション
などなど、いろいろな分野があります。
環境を揃えよう
Xilinx認定Ubuntu と Kria KR260 の紹介
ZynqMP では Linux を動かす事が可能で、いろいろなディストリビューションが適用可能なのですが、今回は、認定Ubuntu を使って説明したいと思います。
現在こちらで配布されている Ubuntu が使える ZynqMP のボードは
の5種類あります。このなかで現時点で2番目に安く手に入るのが KR260 となります。
個人でも購入できる、購入先の候補としては
などいろいろあります。
ボードが用意出来たら SDカードに Ubuntu 22.04 のイメージを書き込んで電源を入れれば起動します。
本記事で使っているイメージは iot-limerick-kria-classic-desktop-2204-20240304-165.img.xz です。
開発ツール Vitis/Vivado
AMD(Xilinx)ではユーザー登録をすると開発ツールがダウンロードできるようになっています。
そしてなんと、規模の小さいデバイス向けにはこれらを無償で利用することができ、KR260 も無償で開発することができます。
今回は Vivado というツールを使うのですが、Vivado は Vitis という上位のツールにも含まれており、Vivado のみをインストールするよりも Vitis をインストールすることをオススメしておきます。
筆者は Windows 11 の WSL2 環境に Ubuntu 20.04 を入れており、ここに Vitis 2022.2 を入れております。
サイズは大きいのですが AMD(Xilinx)のダウンロードサイトからダウンロードできますので、頑張ってダウンロードとインストールを行ってください。
LEDチカをやってみよう
今回は SystemVerilog を使って、Lチカを行おうと思います。
SystemVerilog を少し勉強すれば、クロックをカウントして、LEDを点滅させるプログラムの書き方は容易に想像がつくと思います。
しかし 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を探し出して選びます。
新規プロジェクトが出来上がり初期状態となります。
ブロックデザイナを利用してファン制御を接続しておく
KR260 は KV260 と違い、PL部に繋がる クロック も LED もボード上に実装されております。
クロックは PS からもらう事も出来るのですが、今回は折角ですので FPGA らしくボード上のクロックを使う例を示したいと思います(PS のクロックを使いたい方は KV260 の例を参考にしてください)。
しかしながら KR260 で Linux を使う場合の注意点として PS から冷却ファンを制御する信号が出ており、これを PL 経由で適切に繋いでおかないと Linux 側のファン制御しているソフト部分に影響が出る場合があるようですので、今回はそこもケアしていきます。
こういった部分がしばし初見泣かせになりかねないので、しっかりと説明していきます。
物理的には 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 はいろんな設定がありますが、プロジェクト作成時に KR260 を選んでいますので、適した設定が行われます。
次に ファンを制御する PWM 信号を出すため、先ほど生成した ZYNQ ブロックをダブルクリックして、設定を行います。
ファン制御に関してこちらにも記事を書いておりますので、合わせて参考にしてください。
下図のように 「I/O Configuration」 の 「Low Speed」 の 「Processing Unit」 の 「TTC」の「TTC0」の「Waveout」のチェックを付け、I/O に EMIO を選びます。設定出来たら OK を押します。
TTCとはタイマのコアであり、ファンの速度を制御する PWM 信号を発生する事が出来るコアです。
下記のように ttc0 の wave 出力ピンが表示されるようになります。
ここでまたツールバーの + ボタンを押し今度は「Slice」を探して追加します。
TTC0 の 3bit の出力のうち、ファンの制御に使われるのは最上位の bit2 ですのでこれを取り出す設定とします。
ハードウェア記述言語などは 3ビット幅は [2:0]
のように表記して 下位ビットから順に 0 から数える事が多いので注意してください。
Zynqコアの TTC0 の出力と Slice の入力をマウスで繋いだ後、Slice の出力を右クリックして 「Make External」を選ぶと外部ポートが出来ます。
ファンへのイネーブル信号として外部ポートの名前を fan_en
にリネームしておいてください。
そうすると Block Design の外に信号が引き出されます。
最後に、今回は利用しないのですが maxihpm0_fpd_aclk
と maxihpm1_fpd_aclk
にクロックが供給されていないとエラーになるので、マウスで pl_clk0
と接続します。
下図のようになれば完成です。
いよいよ 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 のエディタだと日本語は文字化けすると思いますので悪しからずご了承ください。
ここで初めて「SystemVerilog の本で勉強した世界」になります。教科書レベルのことをやろうとしても、メーカーやツールごとに教科書に書いてないことが前準備としていろいろあるわけです。
`timescale 1ns / 1ps
module top
(
input var logic clk ,
output var logic [1:0] led ,
output var logic fan_en
);
// Block design
design_1
u_design_1
(
.fan_en (fan_en)
);
logic [24:0] counter = 0;
always_ff @(posedge clk) begin
// 25MHz で 1秒間隔でカウントアップ
if ( counter >= 25000000 - 1 ) begin
counter <= 0;
led <= led + 1;
end
else begin
counter <= counter + 1;
end
end
endmodule
すこし、解説しておくと。先ほどブロックデザイナで作成したブロックを design_1 という名前で利用しています。先ほど作成した fan_en というポートからファンの制御信号をそのままtopモジュールのポートに接続しています。
クロックは KR260 では 25MHz が供給されていますので、25bit のカウンタ変数を定義し、クロックの立上りの度にインクリメントして 1秒ごとに LED への 2bit の出力値をインクリメントしています。
以上で、SystemVerilog 言語のお話はおしまいです。
以降はこのプログラムが実機で動くようにしていきます。
「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 ピンに
- led[1] : E8 ピン
- led[0] : F8 ピン
- clk : C3 ピン
- fan_en : A12 ピン
と設定します。これはグラフィカルに表示されている通り LSIデバイスのピン番号と対応します。各ピンがどこに繋がっているかは後述の回路図で確認可能です。
また、I/O Std に 「LVCMOS18」を設定しています。
設定して保存しようとすると、制約ファイルの名前を聞いてきます。ここでは top.xdc という名前で保存しています。
xdc は Xilinx の制約ファイルの拡張子の慣例のようです。
top.xdc というファイル名で、制約ファイルが出来上がったと思います。
さらにここで、25MHz のクロック入力にタイミング制約も加えておきましょう。
Edit Timing Constrains
を選ぶことでタイミング制約も編集できます。
Cratate Clock
を選びます。
clk
ポートに対して 25MHz を示す 40ns 周期を指定します。この時、デューティー比 50% の綺麗なクロックを想定していますので、 Fall at
に 20ns を指定します。
Source objects
のところは、ダイアログを出して信号を検索しても構いません。
保存すると xdc ファイルに反映されます。
クロックを指定することで、このクロックで動作する回路の箇所が、40ns 以内に計算の終わる必要があることを指示する事が可能となり、以降タイミング制約を満たすように回路がチェックされるようになります。
なお、GUI を使わずに、初めから xdc ファイルをテキストエディタで直接書くことも出来きます。
以下は私がテキストファイルで書いた時の xdc ファイルです。
GUI からも指定可能ですが、create_clock で、25MHz のタイミング制約も加えています。
# 25MHz
create_clock -period 40.000 -name clk -waveform {0.000 20.000} [get_ports clk]
# fan enable
set_property PACKAGE_PIN A12 [get_ports fan_en]
set_property IOSTANDARD LVCMOS18 [get_ports fan_en]
# clock
set_property PACKAGE_PIN C3 [get_ports clk]
set_property IOSTANDARD LVCMOS18 [get_ports {clk}]
# LED
set_property PACKAGE_PIN F8 [get_ports {led[0]}]
set_property PACKAGE_PIN E8 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS18 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS18 [get_ports {led[1]}]
あとは、Generate Bitstream ボタンを押して、bitstream を生成するところまで一気にやってしまいます。
完了すると、このようなダイアログが出ます。
OK を押して閉じて構いません。
プロジェクトファイルの下に
kr260_led_blinking.runs/impl_1/top.bit
というファイルが生成されているはずです。
クロック入力や LED 出力の配線を調べてみる
ここから先は情報の調べ方など、少し上級者的な話です。
AMD のサイトを検索するとユーザーガイドや回路図を取得できます。
回路図はユーザー登録しないとダウンロードできないのでKR260のページの下の方にあるリンクなどから各自でダウンロードください。
さて、先ほど、LED などのピン番号を指定しました。これらはユーザーガイドや回路図に記載があります。
またクロックについても 25MHz のオシレータのクロックが分配されているのを見つける事が出来ます。
追いかけていくと、SOM240_1 のピンに繋がっていることがわかります。
ここから先、K26 SOM モジュールになるのですが、現時点ではなんと
- K26 SOM モジュールの回路図は公開されていない
- Vivado のツールでは K26 SOM のピンではなく、モジュール上の ZynqMP LSI のピンを指定させる
という、少しめんどくさいことになっています。
AMD(Xilinx)としては、K26 SOM をモジュールとして商品にしたいようなので、いずれは Vivado で直接モジュールのコネクタピンが指定できるようになるのではないかと思いますが、現時点ではツールが追い付いていないようです。
ですので、こちら の資料のところから XTP685 - Kria K26 SOM XDC File (v1.0) を探し出して、著名したうえでダウンロードしてきます。
そして、ダウンロードしてきた xdc を検索すると例えば led[0] の繋がる som240_1_d13 が F8 ピンであることをようやく知ることができるわけです。
次に、F8 ピンのI/O設定をどうするべきか調べます。
ダウンロードした XTP685 のファイルから F8 は I/Oバンク66 に属しており VCCO は som240_1_d1 から供給されていることがわかります。
そして再び回路図に戻ってくることで PL_1V8(1.8V) が供給されていることがわかるわけです。
I/O ピンの設定は、VCCO の電圧に応じて選択できるものが決まってきますが、LED の点滅をさせる場合、単なるデジタルレベルで H/L の制御ができればよいですので LVCMOS18 を設定しています。
これで、出力値が 0 の時は 0V、出力値が 1 の時は 1.8V が出力されます。
もちろん、回路図など追わなくとも説明書通りにピンを設定すれば済むケースも多いのですが、「なぜマニュアルでは F8 を設定するように書かれているの?」という疑問まで解消しようとするとこのような手順で資料を読み解いていく事になります。
マイコンにせよFPGAにせよ LEDチカチカをしたい方々は IoT として、現実世界の何かをセンシングしたり制御したりしたいと考えているかと思いますので、プログラムと電気信号がどう繋がっているかを理解しておくことは、無駄な事ではありません。
作った回路を実行する
ではいよいよ動かしていきましょう。kr260_led_blinking.runs/impl_1/top.bit を、KR260 の Ubuntu 環境にコピーします。
scp なり、Samba を使うなりいろいろな方法があると思いますので、PC と エッジボードで動く Linux(Ubuntu) の間でファイルコピーできるようになりましょう。
また 最近の Ubuntu は、起動時に初期回路が焼き込まれているのでこれを最初にアンロードしなければなりません。
sudo xmutil unloadapp
とすることで取り外せます。
その後、ファイルをコピーした先で下記のように実行すれば動きます。
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"
下記のLEDが点滅すればOKです。
配置配線を覗いてみる(おまけ)
最後に余談ですが、今回の回路の中を覗いてみます。
プログラミング言語で宣言した変数が実際のフリップフロップに割り当てられて、配線が外部のピンに繋がっているところまでビジュアルに見ることができてとても楽しいです。
下記は KV260 での画像ですが KR260 でも同じように Vivado で中がビジュアライズされてみることが出来ます。
おわりに
余力があれば、xsim でのシミュレーションや ILA を使った実機デバッグの話も書きたかったのですが、力尽きたので今回はここまでです。
Verilog などの教科書をみてプログラミング言語を理解しても、いざ動かそうとすると言語以外に覚えないといけないことがたくさんあるかと思います。
KV260 のようにダウンロードした Ubuntu イメージを SDカードに書き込むだけで起動するボードが出てきたおかげで、ZynqMP を IoT プログラミングのプラットフォームとして選ぶ際のとっつきやすさは格段に高まったように思います。
一方で、「Ubuntu は立ち上げたし、Verilog 等の RTLプログラミングの入門書も読み始めてるよ」という方が、「どうやってこの RTL のコード動かせばいいの?」となってしまったときに Lチカ は最初の一歩として非常に重要と思います。
逆にLチカをしっかり理解すれば、端子を増やして実際にいろいろな RTL をプログラムしながら学んでいくことが可能になるのではないでしょうか。
本記事が何かの参考になれば幸いに思います。
参考コード
本記事と全く同じではありませんが、筆者のオープンソースの中にKR260でLチカを行うサンプルを追加しております。
よろしければ参考にされてください。
Discussion