ZYBO (Zynq) 初心者ガイド (6) 自作IPでLチカ

- [1回目: 開発環境の準備](https://qiita.com/take-iwiw/items/966f252f6ca954aff08b)
- [2回目: Hello Worldプロジェクト](https://qiita.com/take-iwiw/items/24a8a94741fdbb80f62a)
- [3回目: PSのGPIOでLチカ](https://qiita.com/take-iwiw/items/39d2ed00fa88a1204fce)
- [4回目: PLのAXI GPIOでPSからLチカ](https://qiita.com/take-iwiw/items/a3b935a4e4dbd41593e1)
- [5回目: PLだけでLチカ](https://qiita.com/take-iwiw/items/efcdfca1866b9941fd2a)
- __[6回目: 自作IPでLチカ](https://qiita.com/take-iwiw/items/b0f2ce7b4653bc6f32b9) <--- 今回の内容__
- [7回目: ブートイメージを作る](https://qiita.com/take-iwiw/items/ece0cdf83e6e1908fad8)
- [8回目: Linux起動する](https://qiita.com/take-iwiw/items/6e6915f7318689818368)
- [9回目: Linuxカーネルを少しカスタマイズする](https://qiita.com/take-iwiw/items/6f2644d16cbfdb021d07)
- [10回目: LinuxのRootFSをカスタマイズする / PythonでHello World](https://qiita.com/take-iwiw/items/94a90047978664159681)
- [11回目: LinuxユーザアプリケーションでLチカ](https://qiita.com/take-iwiw/items/5e4ab9a106acfda2c866)
- [12回目: LinuxカーネルモジュールでLチカ](https://qiita.com/take-iwiw/items/e7efc48d92d7b979efdc)
- [13回目: LAN(Ethernet 0)を使う](https://qiita.com/take-iwiw/items/ac489acaca1ab54d7ce8)
- [14回目: Linuxユーザアプリをデバッグする / RootFSに取り込む](https://qiita.com/take-iwiw/items/8134ca7098622219b177)
- [15回目: Linux起動時にアプリケーションを自動実行させる](https://qiita.com/take-iwiw/items/50065df6cdd1ba8bcbd6)
- [16回目: Linuxから自作IPをUIOで制御する](https://qiita.com/take-iwiw/items/da91ce4dc2a8a8df3c0a)
- [17回目: Linuxで自作IPのデバイスドライバを作る](https://qiita.com/take-iwiw/items/05eb1204cb51ce0a5c03)
- [18回目: IoT化してスマホからLチカ](https://qiita.com/take-iwiw/items/e9311292c5078d785699)

この記事の内容を4分で見る ( https://www.youtube.com/watch?v=JoQePlMliyk )

# 環境
- 開発用PC: Windows 10 64-bit 
	- Vivado 2017.4 WebPACKライセンス
	- Xilinx SDK 2017.4
- ターゲットボード: ZYBO (Z7-20) 

Windows環境は[1回目](https://qiita.com/take-iwiw/items/966f252f6ca954aff08b)を参照。

# 自作IPでLチカ
GPIOの __OUT__ だけの機能を持ったIPを作ります。この自作IPをPS側のCPUから制御してLEDをチカチカさせます。前回の最後に、LED点灯するモジュール(blink)をIP Integratorから呼んでみました。しかし、厳密にはこれはあくまで「モジュール」であって「IP」ではないようです。IPを作るためには、ちゃんとパッケージ化する必要があります。まずはそれをやります。

今回も、最初からVivadoプロジェクトを作ります。ボードにZyboを指定した空のプロジェクトを用意済みとして、話を進めます。

# 自作IPを作る (Vivado)
## 自作IPの仕様
今回のIPはPSから制御できるようにします。PSとPLでデータをやり取りするときには、AXIバスを使います。CPUはAXI経由でIP内部のレジスタに値をR/Wします。IPはこのレジスタの値を使って動作します。今回は、レジスタ値の下位4ビットを反転して出力するようにします。
(このIP単体にとっては、この4bit出力はただの信号線です。なので、「LED」とかは付けないことにします。どこに接続するかは、このIPを使用するIP Integratorで決めます。（という考えはソフト屋さん的な考えなのかな。。。）)

- 名前: myip
- PSとのインターフェース: AXI4-Lite Slave
- レジスタ: 32-bit x 4 (実際に使用するのは1個だけだが、デフォルト設定のまま使うため)
- 出力: 4-bitの信号線 (後でLEDに接続される)

## 自作IPのひな形を作る
空のVivadoプロジェクトを開いたら、まずはメニューバー -> Tools -> Create and Package New IPをクリックします。何をやるか聞かれるので、「Create AXI4 Peripheral」を選びます。OKを押すと、IP名などを設定する画面になります。基本はデフォルトのままで良いですが、僕は保存先だけ変更しました。デフォルトだと、1つ上のフォルダに作られてしまいます。今回作るIPはこのプロジェクトでのみ使うので、「../」を削除しました。

![01.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/4674317e-76ad-2691-c9af-af87756b4637.jpeg)
![02.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/7ac26d4f-f25b-41f5-2c4f-f4340c847a52.jpeg)

続いて、インターフェースを編集できますが、ここもひとまずはデフォルトのままにします。デフォルトでは、AXI4-Lite Slaveで、32-bit x 4個のレジスタが用意されます。OKを押して、次の画面もAdd IP to the repositoryのままでFinishします。(4-bitの出力は、後でコード上で追加します)

![03.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/4035970b-dc6e-e2d9-f114-060387b6c7a2.jpeg)

## 自作IPを編集する
Flow Navigator -> PROJECT MANAGER -> IP Catalogをクリックして、IP Catalogを確認すると、User Repositoryの下に「myip_v1.0」が出来ています。先ほど全てデフォルト設定で作成したので、これから編集します。右クリックして「Edit in IP Packager」を選びます。これで、このIPを編集する新しい一時的なプロジェクトを開きます。そのプロジェクトの保存先を聞かれますが、デフォルトのままOKします。

![04.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/c3c55f73-683c-2630-bd08-99300584cdcb.jpeg)

自作IP(myip)を編集するための、新しいVivadoウィンドウが開かれます。ソースコードは2つあり、myip_v1_0_S00_AXI.vが実体で、myip_v1_0.vはラッパー的な感じです。

![05.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/d31b6d65-3794-d6be-dc58-ff4dff231127.jpeg)

今回は出力として、4-bitの`MY_OUT`を追加します。これに、4個のうち最初のレジスタの下位4-bitを反転して割り当ててみます。レジスタ名は`slv_reg0`のようです。(この記事では簡単のために、例としてこのようなシンプルなロジックにしました。実際のプロジェクトではもっと複雑なロジックになると思います。)
myip_v1_0_S00_AXI.vを開くと、既にかなりのコードが実装されています。これは、AXIインターフェースでレジスタに読み書きするコードになります。「Users to add ...」というところに、コードを追加していきます。追加は2行だけで、ポート定義と、ロジック記述だけです。

```verilog:myip_v1_0_S00_AXI.vの一部
	module myip_v1_0_S00_AXI #
	省略
	(
		// Users to add ports here
		output [3:0] MY_OUT,
		// User ports ends
		// Do not modify the ports beyond this line

		省略
	);

	省略

	// Add user logic here
	assign MY_OUT = ~slv_reg0[3:0];
	// User logic ends

	endmodule
```

myip_v1_0.vにも同様に追加します。`MY_OUT`を接続するコードを追加するだけです。

```verilog:myip_v1_0.vの一部
	module myip_v1_0 #
	省略
	(
		// Users to add ports here
		output [3:0] MY_OUT,
		// User ports ends
		省略
	);
// Instantiation of Axi Bus Interface S00_AXI
	myip_v1_0_S00_AXI # ( 
		省略
	) myip_v1_0_S00_AXI_inst (
		.MY_OUT(MY_OUT),
		省略
	);
	endmodule
```

コードの編集が終わったら、IPを再作成します。Package IPタブで全てのPackaging Stepsが編集マークからチェックマークになるように、更新します。各Stepの所で「Merge changes...」をクリックすればOKです。最後に、Review and Packageを選び、Re-Package IPをクリックします。成功したら、IP編集用のプロジェクトは閉じられます。

![05_5.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/6a45b4a4-1f74-0832-731d-d10533bd7ec0.jpeg)
![06.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/08e0ffe7-1e2b-fe27-6157-ca7b0df3edce.jpeg)

# 自作IPを使う (Vivado)
PSと、作成したIP(myip)を搭載したハードウェアを作ります。元のVivadoプロジェクト上で、Flow Navigator -> IP INTEGRATOR -> Create Block Designで新しいブロックデザインを作ります。作成されたブロックデザインのDiagramビュー上でPSを追加して、Run Block Automationします。ここまでは、2回目、3回目の内容と同じです。同様の手順で、作成したmyipも追加します。Add IPで表示されるIP一覧にmyipが追加されています。配置後、Run Connection Automationで自動接続してもらいます。設定は全てデフォルトでOKです。

![07.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/3c760d44-c0d3-7d75-4e35-6ad522f72276.jpeg)

myipが配置されたら、MY_OUTの出力ポートを用意します。myipのMY_OUT端子付近で右クリックしてMake Externalを選びます。すると、MY_OUT_0ポートが作られます。

![08.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/ffcd7ea4-83bc-e83d-fe0d-70d3505560f6.jpeg)

必要部品の配置が出来たので、このブロックデザインをトップモジュールとして使います。ここから先は前回と同様です。Sourcesのdesign_1の所で右クリック -> Generate Output Productsと Create HDL Wrapperします。その後、MY_OUT_0ポートをLEDの接続されているピンに割り当てます。これも前回と同様に、Flow Navigator -> RTL ANALYSIS -> Open Elaborated Designしてから、以下のようにピン設定をして、適当な名前で制約ファイルを保存します。

![09.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/0a11a483-addb-78cc-edff-c557b1095325.jpeg)

最後に、Generate Bitstreamでビットストリームファイルを作り、Export Hardwareして、Launch SDKします。

## ノート: 後で再びIPを編集する場合
自作IPを既にブロックデザイン上で配置した後に、自作IPを編集した場合は、更新作業が必要になります。自作IPの編集を終えて、元のVivadoプロジェクトに戻ると、以下のようにメッセージが上部に表示されるので、「Report IP Status」をクリックします。

![10.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/38684d04-9d19-970f-eef8-7e56d80ed260.jpeg)
![11.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/1836d304-e76e-71b4-58d4-7bb2234da151.jpeg)

すると、画面下部にIP Statusが表示されます。変更のあったmyipがチェックされていることを確認して、「Upgrade Selected」をクリックします。その後、必要であればデザインを変更し、Generate Output Products、Create HDL Wrapper、ビットストリーム作成をすることで更新完了です。

# 自作IPを呼び出してLチカするソフトを書く (SDK)
起動したSDK上で、適当なプロジェクトを作ります。ここでは名前をblinkとします。 
まず最初に、自動生成されたblink_bspプロジェクトを確認します。xparameters.hを見ると、`#define XPAR_MYIP_0_S00_AXI_BASEADDR 0x43C00000`が定義されています。これが、自作IP内のレジスタのアドレスになります。各レジスタアドレスは、以下のようになります。

- slv_reg0: 0x43C00000
- slv_reg1: 0x43C00004
- slv_reg2: 0x43C00008
- slv_reg3: 0x43C0000C

今回は、0x43C00000にアクセスすることでLED制御できます。いつも通り、0x43C00000をポインタ化してアクセスしてもいいのですが、アクセス関数も自動生成されています。libsrc/myip_v1_0/src/myip.hを見ると、`MYIP_mWriteReg()`関数が用意されています。また、オフセットもマクロ化されて用意されているのでそれを使います。これらを使い、以下のように実装します。

```c:helloworld.c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xparameters.h"
#include "myip.h"

int main()
{
    init_platform();

    print("Hello World\n\r");

    while(1) {
    	MYIP_mWriteReg(XPAR_MYIP_0_S00_AXI_BASEADDR, MYIP_S00_AXI_SLV_REG0_OFFSET, 0x00);
    	sleep(1);
    	MYIP_mWriteReg(XPAR_MYIP_0_S00_AXI_BASEADDR, MYIP_S00_AXI_SLV_REG0_OFFSET, 0x0F);
    	sleep(1);
    }

    cleanup_platform();
    return 0;
}
```

ビルド後、まずはXilinx -> Program FPGAでビットストリームファイルを書き込みます。確認してほしいのは、この時点でLEDが点灯することです。理由は、myipではレジスタ値の反転を出力しているため、初期値(0)の反転でHigh出力になっているためです。その後、ビルドしたプログラムをRunします。すると、1秒間隔でLEDが点滅するはずです。