Ultra96V2のPLでLEDチカを行う(Vivadoでのbitstream作成編)

# はじめに
[Ultra96V2](https://www.avnet.com/wps/portal/japan/products/product-highlights/ultra96/) で PL から LED を光らせるいわゆるLチカを行っております。
たどり着くまでにいくつかやる事があるのですが、本記事はまずVivadoでPL用のbitstreamを作るところまでを書きました。

よくある PS からソフトウェアでLチカをするのではなく、PLに繋がっているRADIO_LEDでLチカを行うことを目標にしています。

「FPGAが使ってみたくて、RTL(VerilogやVHDLなど)を勉強しつつ勧められてUltra96を買ってみたけど、何をすればいいかわからない」といった人向けの内容を目指しています。まず Verilog で LEDをチカチカさせてみたいですよね。
実はこれZynqの場合、結構大変なんので、理解の一助になれば幸いに思います。

# 環境
今回は以下の環境で行っています。

- Vivado 2019.2
- Windows10搭載PC

なお、Zynq を本格的に開発する場合は Linux が必要となりますので、Windowsユーザーであっても、VirtualBoxなどを用いて Ubuntu などの Linux マシン上で環境構築することをお勧めしておきます。


# 事前準備

## Vivadoのインストール
  本稿では Vivado2019.2 を使います。しかしながら [Vitis 2019.2](https://japan.xilinx.com/support/download/index.html/content/xilinx/ja/downloadNav/vitis/2019-2.html) をインストールするとその際に一緒にVivado2019.2をインストールできますので、Zynq を扱う場合は Vitis ごとインストールすることをお勧めします。
　どちらも[Xilinxのページ](https://japan.xilinx.com/support/download.html)からダウンロード可能です。

## ボードファイルのインストール
Ultra96に搭載されている Zynq系 のLSIは、まずPS(プロセッサ)部分のソフトウェアが最初に起動する仕組みになっており、ボードに搭載されている LPDDR4(メモリ)など各種のデバイスに合わせてPS部が初期設定される必要があります。
これらの設定は、回路図やデバイスのデータシートを読みながら自力で設定することも不可能ではない筈なのですが、通常とても大変ですのでボードメーカーの設定をそのまま使うようにしましょう。
Zynq の PL(プログラマブルロジック)部分を扱う場合、PS部の設定を前提に設計する必要がありますので、あらかじめPSがどのように設定しているのかを合成ツール(Vivado)に教えてあげる必要があります。
ボードファイルには他にもいろいろな役割があるのですが、ひとまずインストールしておけば、これらの事を簡単に行うことが可能になります。

AVNET社のボードファイルは

[https://github.com/Avnet/bdf](https://github.com/Avnet/bdf)

にあります。gitでcloneしたり、zipファイルでダウンロードすることが可能です。
取得したイメージの ultra96v2 フォルダがボードファイルですので Vivado をインストールした先にコピーすればOKです。

インストール環境に応じて場所は変わりますが、例えば私の環境の場合は下記となりました。
C:\Xilinx\Vivado\2019.2\data\boards\board_files

# Vivado プロジェクトの作成
Vivadoを起動すると以下のような画面が出るかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/65539c66-9657-3ea0-f11c-6ad647130407.png)

Create Project を選んでプロジェクトを作りましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/2ec079ea-13b9-5816-f71f-0810e583d7b3.png)


NEXTを選びます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/17fbb0dd-3778-6fb5-19ae-0ff4e4ac525d.png)


ここでは ultra96v2_led というプロジェクト名にしています。
パスは各自の好きな場所に設定してください。
ただしWindowsではパス名に260文字の制限があり、Vivadoが内部で非常に深いパスを作ることがあるので、なるべく短いパス名の場所に置くことをお勧めします。

引き続き、NEXTを押していくと

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/5b5faca1-c1ad-f571-ba2f-c0564f190987.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/9eb75340-edd0-ee77-530d-aafda536d814.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/6e814a55-7971-70e5-e69e-7d6795e76f6e.png)

デバイス選択のダイアログが出ますので、Boardsのタブを選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/5a9adbd2-15b4-a900-9ce9-e42a2f437d0e.png)


ボードファイルのインストールがうまくいっていればボード一覧の中に Ultra96v2 が現れますので選択します。
表示に時間のかかるケースもあるのでそういう場合は少し待ちましょう。Search欄にultra96と打ち込むと検索できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/0d4189a6-7380-8919-ae61-28c2e0f80ecf.png)


選択したら NEXT を押します。

最後のダイアログで Finish を押せばプロジェクトが出来上がります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/6b711313-d4aa-e298-091f-bc385253bed4.png)


# Block Design
プロジェクトが完成したら、早速RTLを書きたいところですが、はやる気持ちを抑えて、まず最初に Block Design で PS部分の定義をしてしまいたいと思います。

左側にある Create Block Design を選択してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/c561319d-65ef-320f-a765-5569c1ff5aab.png)

design_1 という名前のデザインを作ろうとしますが、今回はそのまま OK としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/1d2faeff-9b18-34d5-4687-56e22b706092.png)


block design の画面になるので

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/ae9d05d5-c0d8-797d-155f-28bb4cb6c634.png)


Add IP ボタンを押してください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/79072c48-3aff-13f1-1218-b79d0181b3b0.png)

IPを選択する画面が出てくるので Search に zynq と打ち込んで、出てきた 「Zynq UltraScale+ MPSoC」を選択してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/3827cdb7-8377-5e5c-cdb7-59f1a906e968.png)

画面にZYNQ UltraSCALE+ と描かれたIPが現れます。このIPはPS部分を丸々含んだZynq特有のIPでプロジェクトの中で１個だけ生成して使います(LSIの中に物理的に1個しかないので)。
続けて「Run Block Automation」を押してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/ec8efd6b-74d2-7c4b-0191-8b1006e1aa9b.png)

Run Block Automationダイアログが出ますので「Apply Board Preset」にチェックが付いていることを確認してOKを押してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/42ae9ce7-9f76-abe7-66a0-fc013e1dfadf.png)


これで、Ultra96V2のボードにあった設定がボードファイルを元にIPに設定されます。
なお、注意点として、ここで行う設定は「PS部のソフトウェアがこのように設定してくれる前提でPLはこのIP(PS部)を使いますよ」と宣言しているだけで、実際にはこれと一致する設定を別途PSのソフトウェアで行う必要があります(不整合にならないように管理するのはユーザー側の責任となります)。
PSの設定は、FSBL(First Stage Bootloader)で行ったり、Linux起動後にDeviceTree overlay などの機構で行ったり、JTAGデバッグ時にJTAGから行ったり、方法は様々ですがなんらかの方法で行う必要があります(これらの説明はまた機会があれば別の記事にて記載します)。
この設定はexport可能なので、PLの設計に合わせてPSのソフトウェアを作ることもできますし、その逆にPS側の設定を(最大公約数的なものに)決めておいて、それにあわせてPLを設計していくようなやり方も可能です。


さて、今回は、**PS側のソフトウェア設定がボードファイルのデフォルト値でなされていることを前提に**話を進めます。
ここで一度 PSの設定を覗いておきましょう。ZYNQ UltraSCALE+ と描かれたIPをダブルクリックしてみてください。
次のようなダイアログが開いたと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/9e214f1f-b575-1e2d-4186-e2b08bce176a.png)


この膨大な機能を持ったIPはPS部全体を含んだものです。この部分のみでLinuxを起動させて一般的なICT機能を持ったソフトウェアを構築することも可能です。このPSの存在のおかげで事前準備が大変な反面、これからプログラミングしていくPL部分を**インターネットに繋いでIoT器機としての機能を持たせることが簡単になる** という大事な部分でもあります。

さてここで、今回の記事で重要な部分となるPLへのクロック供給設定である「PL Fabric Clocks」の項を見ておきましょう。PL0 に 100MHz が設定されているかと思います。PL部でVerilogなどでRTLプログラミングをする場合にはクロック入力がほぼ必須ですが、Ultra96ではPL部の外部端子に繋がったクロックはありませんので、PS部からクロックをもらうことが必須です。したがってこの部分のクロックの確認は非常に重要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/78022a96-3a74-9b10-333a-d1fd037fb41b.png)

さて、今回は確認だけしたらダイアログは閉じましょう

続けてブロックデザイン中の Zynq IP にある pl_clk0 のところを右クリックして、「Make Extarnal」を選びましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/c0a0236a-f088-704d-c07a-1e5c61490090.png)

pl_clk0_0 という端子が現れたかと思います。これが先ほど確認した PL Fabric Clocks の 100MHz の出力となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/9a3e6f54-3bf3-f022-ca74-d0d36e0e5e5b.png)


また、今回は使用しないのですが maxihpm0_fpd_aclk, maxihpm1_fpd_aclk というAXIバスのクロック入力に何もクロックを繋がずにおくとエラーになってしまうので、便宜上ここにも100MHzを入力してしまいましょう。マウスでドラッグすると結線がでいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/f469f583-a636-63dd-221b-fe5646b2f461.png)


ここで、F6 キーを押すと Validation が動き、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/c743c306-55bd-9e3d-ff67-d10303847366.png)


のように successful のダイアログが表示されれば Block Design で行う作業は完了です。

# RTL(verilog)プログラミング
左側にある「PROJECT MANAGER」をクリックしてPROJECT MANAGERに戻ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/4ca1bf60-6cdb-ee02-f5aa-6e5b36fa752d.png)

いろいよここから RTL でプログラミングしていきます。

「Design Sources」を右クリックして「Add Sources」を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/df9fe8d5-0728-5741-30bf-ea54f7a32564.png)

「Add or create design sources」を選んで NEXT を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/bdc3d572-b558-18ef-a4c0-2b9adba7fbed.png)


「Create File」を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/bd77f737-6cdc-70ad-e766-483672056b46.png)


作成するファイル名を聞いてくるので今回は ultra96v2_led.v としてOKを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/253eaf08-5b04-5736-0ebd-25f01796f320.png)

そのまま Finish を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/51c5ed54-6454-e5c8-19d0-2ce310ce474c.png)


ポートを聞いてきますが、OK→OKと進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/bb66d39e-6783-95ce-c37d-263f2e8f58f6.png)


ファイルが出来上がったのでこれを開いて編集します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/aabad727-6513-5b94-df96-6e4ce20882bc.png)


今回書いたコードはこんな感じです。Verilog 2001 の構文で記載しています。

```verilog:ultra96v2_led.v
// -------------------------------------
// Ultra96V2 LED点滅テストRTL
// -------------------------------------


`timescale 1ns / 1ps
`default_nettype none


// top module
module ultra96v2_led(
                output  wire    [1:0]   led
            );
    
    // block design
    wire        clk;
    design_1
        i_design_1
            (
                .pl_clk0_0  (clk)
            );
    
    
    // clock counter
    reg     [25:0]  reg_clk_count = 0;
    always @(posedge clk) begin
        reg_clk_count <= reg_clk_count + 1;
    end
    
    
    // output LED
    assign led[0] = reg_clk_count[23];
    assign led[1] = reg_clk_count[25];
    
endmodule


`default_nettype wire


// end of file
```

先ほど作った ブロックデザインはそのまま design_1 というモジュール名で、出力ポートに pl_clk0_0 という100MHz 出力をもったモジュールとして RTL 内から利用可能です。
ここではそれをreg_clk_counterと命名した 26bit レジスタで分周して、led となずけた 2bit の output ポートに出力するRTLにしています。

RTLプログラムができたところで早速合成してみます。
上部の合成ボタンから 「Run Synthesis」を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/9242dbe7-b36b-8b4f-cd75-29d33655501e.png)


次にLaunch Runs というダイアログが出てきますが、そのままOKで構いません。
合成するPCがマルチコアCPUの場合、Number of jobs の部分で2以上を指定すると並列に処理が行えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/52db1bab-006a-455d-50bd-0a9becd58cdf.png)


合成にはしばらく時間がかかりますが、完了すると次のようなダイアログが出るので
「Open Synthesized Design」を選んでOKを押すと、合成済みのデザインが開けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/40e95683-47be-4b8f-8120-23ca9549634e.png)


なおここで選び損ねた場合でも、vivadoの左端にあるFlow Navigatorから「Open Synthesised Design」を選べばいつでも開けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/f38a4e1a-eecf-4a2e-4c6f-b867fba9c1d7.png)

メニューの「Layout」→「I/O Planning」を選びます。ここからLSIの端子割り当てを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/bd5b9480-dd83-1a22-cab3-ef138bdee46f.png)

Ultra96-V2 Hardware User's Guide(Ultra96-V2-HW-User-Guide-rev-1-0-V1_0_Preliminary_0.pdf)の10ページ目にある A9, B9 端子に RAIDIO_LED が繋がっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/27e03104-6cff-b46b-8f89-2bef3ce31a83.png)

そこでGUIから各端子を図のように設定します。I/O Std のところは LVCMOS18 を選んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/05492893-458f-75ad-5b2f-00f62bdd8365.png)

ここで一度保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/89901b6f-9946-ff5d-441d-bcfad55e7dc5.png)

下記のようなダイアログが出ますが、気にせずOKを押してください。
(端子割り当てにより制約ファイルが新しくなったので合成をやりなおした方が良いかもしれないと言ってきています)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/fa512adc-d99b-a40c-f10e-88498d7facab.png)

今作った端子割り当てなどの制約を保存するファイル名を聞いてきます。今回は ultra96v2_ledt.xdc としてOKを押しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/c2798504-cb2a-df68-17d3-03588c8c18f7.png)


ちなみにこの時、以下のようなファイルが生成されています。

```ultra96_led.xdc
set_property PACKAGE_PIN B9 [get_ports {led[1]}]
set_property PACKAGE_PIN A9 [get_ports {led[0]}]

set_property IOSTANDARD LVCMOS18 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS18 [get_ports {led[0]}]
```

今回はGUIで作成しましたが、テキストエディタで編集することも可能です。
この制約ファイルは、端子割り当てだけでなく、タイミング制約や、配置配線の制約など様々なRTL記述以外に必要な制約事項の記述に使われます。

最後に Generate Bitstream ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/dea47e06-7e0d-bcca-157e-18c293673306.png)


また合成時と同じダイアログがでますがOKを押すと bitstream の生成まで走り始めます。

無事合成が終わりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/ae3fab01-8ed5-575b-6e81-7179a1ef9261.png)

一旦 View Reports を選んでOKを押しましょう。

ultra96v2_led.runs\impl_1 の下に ultra96v2_led.bit ができていることが確認できると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/d45837c3-ee43-6d67-39b8-fe8a1926e8a1.png)

この ultra96v2_led.bit がPLにダウンロードするビットストリームとなります。

本記事では一旦ここまでです。
これをPLに書き込む方法はいろいろあるので、また次回以降記事が書ければと思います。


# 回路図の確認(おまけ)

少し余談ですが、LED周りの回路図の確認について、前置きもなくLVCMOS18を指定しましたがその説明です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/4817a63a-a549-c6b7-5091-b330103fa4ee.png)

回路図を追っていくと、まず[HSMY-C191](https://docs.broadcom.com/doc/AV02-0551EN)というLEDが260Ωの抵抗と直列に繋がり、[FDY3000NZ](https://www.onsemi.jp/pub/Collateral/FDY3000NZJP-D.pdf)というFETを介してFPGAから制御されていることが読み解けるかと思います。
そして、FETのゲート端子はFPGAのBANK26に繋がっております。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/e398d765-bc50-6445-c0e5-b6a098706810.png)

BANK26のI/O電源である VCCO_26には +VCCAUX が接続されており

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/b5627d35-0b76-7a87-c53f-d43c9b343016.png)

+VCCAUX は電源回路から 1.8V が供給されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285930/068b5d88-4605-a107-c50b-3bc2e82c4542.png)

BANK26のI/O電源が1.8VですのでLVCMOS18を指定することになりますが、もう少しLED部分を深く読んでおきます。
HSMY-C191 も、FDY3000NZ も検索すればそれぞれのデータシートにたどり着くことができます。
HSMY-C191のデータシートを読むと、このLEDは25mAが絶対最大定格ですが、絶対最大定格というのはデバイスを壊さないために守るべき規定なので、実際にはこれより少ない電流値で必要な明るさになるように電流を設定して使われます。Figure 2 を見ると、色によって違いますが黄色の場合 5mA 以上流したとして概ね 1.7V 程度の順方向降下電圧になることがわかります。

FETのオン抵抗を一旦無視すると、電源が3.3VがLEDで1.7V程度低下するので、残り1.6Vが260Ωの抵抗にかかり、オームの法則の通り 6mA 程度が流れます。LED部分は 6mA× 1.7V = 10mW 程度の電力で発光することになり、各デバイスの定格に収まる範囲で程よい明るさが設計されていることが読み取れます。
なお、ここでのFETはゲート電圧でON/OFFされるスイッチとみなせます。FETのデータシートの Figure 5 を見ると、 1.8V あれば 400mA 程度流せますので、先の 6mA 程度をON/OFFするスイッチにするには1.8Vで十分足りることになります。

ですので、FPGAからの1.8Vのデジタル制御でLEDのON/OFFが制御できるわけです。


# おしらせ
2020/10/30 にて Qrunch がサービス終了したので移転してきた記事となります。
一時退避場所：https://github.com/ryuz/qrunch_blog