ZYBO (Zynq) 初心者ガイド (4) PLのAXI GPIOでPSから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=WF7jMu78uwc )

# 環境
- 開発用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)を参照。

# PLのAXI GPIOでPSからLチカ
ややこしいタイトルです。前回は、PSのGPIOをPSのCPUから制御しました。今回は、PLのGPIOをPSのCPUから制御して、LEDをチカチカさせます。PLでGPIOを使うには、AXI GPIOというIPを使います。 
まずVivadoでAXI GPIOをPL上に搭載して、それをPSから制御するようなソフトをSDKで実装します。

## 対象のLED
前回説明したように、MIOはPSからのみアクセスできます。今回はPLのGPIOなので、MIO以外のIOにつながっているLEDを使います。LD0～LD3(M14、M15、G14、D18)を使います。これは、Z7-20の場合です。他のボードだと異なるかもしれません。

# ハードウェアの作成 (Vivado)
## プロジェクトの作成
前回、または前々回に作成したプロジェクトをそのまま使います。ZYBO(Z7-20)ボード用のプロジェクトで、PSだけを搭載したブロックデザインが既にあるものとします。

## AXI GPIOの配置
Block DesignのDiagramビューを開きます。現状、PSのみが配置されていると思います。
![01.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/6ac81af7-d8ce-4a78-7a65-ddeaf18d83f2.jpeg)

これに、AXI GPIOを追加します。AXI GPIOはXilinxによって用意されているIPで、AXIをインターフェースに持つGPIOです。AXI GPIOを追加して、それをLED用のIOに接続するようにします。そして、PSとAXIで接続させます。具体的には、以下のようにします。ほぼ自動で行われます。

- Diagramビュー上で右クリック->Add IP->AXI GPIOを選択
- Run Connection Automationをクリック
- 下記の設定画面で、axi_gpio_0にチェックする
- GPIOのSelect Board Part Interfaceに`leds_4bits`を選ぶ

![02.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/1b104aea-9839-4563-601d-d885d11856e5.jpeg)

すると、以下のように自動的に接続してくれます。また、必要なIPを自動的に追加してくれます。AXI InterconnectはAXIバスを調停してくれる人です。今回はマスター(PS)とスレーブ(AXI GPIO)が1つしかありませんが、接続されるIPが増えると重要になります。Processor System Resetは、名前通りリセット管理をしてくれるのだと思います。 
また、GPIOは`leds_4bits`に接続されています。`leds_4bits`は外に出ているピン(Exeternal Interface)になります。プロジェクト作成時にボードを指定しているので、わざわざピン番号を指定しないでも、`leds_4bits`を指定することで適切なピンに割り当てることが出来ます。
![03.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/3f4af3d4-99fa-5b94-351f-b573ebf20873.jpeg)

## 設定を確認してみる (見るだけ)
この状態で、AXI GPIOをダブルクリックすることで、詳細を確認したり設定変更が出来ます。以下のようにGPIO(省略されているが、GPIO1ということ)は、`leds_4bits`に接続されています。また、今回は使用していませんが、GPIO2があることから一つのAXI GPIOで2チャネル持てることが分かります。また、図は張りませんが、隣のIP Configurationタブを見ると、GPIO Widthも自動的に4になっています。
![04.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/2aab8be6-25a6-786c-5ec0-5022029f832a.jpeg)


Address Editorタブで、各IPのレジスタのアドレスを編集出来ます。ここでは確認するだけにします。これを見ると、今回作ったAXI GPIO(axi_gpio_0)のアドレスは0x41200000のようです。
![05.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/db1d8ae7-0739-31d6-46bc-8f6ecca4b146.jpeg)

## ビットストリームとhdfを作る
[2回目](https://qiita.com/take-iwiw/items/24a8a94741fdbb80f62a)でやったように、論理合成、配置配線をして、ビットストリームとhdfを作ります。その後、hdfをエクスポートします。

- Sourcesタブでdesign_1を右クリック、Generate Output Products、その後、Create HDL Wrapper
- Flow Navigator上で、Generate Bitstream
- メニューバー -> File -> Export -> Export Hardware -> include bitstreamにチェックをつけてOK

## 結果を確認してみる (見るだけ)
ここまでやったら、次はSDKでソフトウェアの実装をするのですが、その前に作られたハードウェアの詳細をちょっと見てみようと思います。

Flow Navigator -> RTL ANALYSIS -> Open Elaborated Design をクリックします。すると、以下のように、Packageビューが表示されます。今回は、Block Designの所でピンアサインをしましたが、この画面から割り当てることもできます。
![06.jpg](https://qiita-image-store.s3.amazonaws.com/0/214268/54c3bdf3-294e-70e7-21d2-5b80c0404c54.jpeg)


他にも、Schematicでブロック図を見れたり、Deviceタブでフロアマップを確認することが出来ます。


# ソフトウェアの実装 (SDK)
前回からの続きでやっている場合は、Vivado上でhdfをエクスポート完了した時点で、SDK上で「更新しますか?」みたいなことを聞かれるので、OKをしてください。新しくプロジェクトを始めた場合は、VivadoからLaunch SDKしてください。これで、作成したハードウェアをベースにしたSDKプロジェクトが用意できました。

前回のPSのGPIOと同様に、レジスタ直叩きバージョンと、ドライバAPIを使うバージョンをやってみようと思います。

## レジスタ直叩きでAXI GPIOを制御する
今回使用しているGPIOは、AXI GPIOというXilinxから提供されているIPです。そのため、仕様などはXilinxが提供しているドキュメントにあります。[AXI GPIO v2.0 LogiCORE IP Product Guide](https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf )を確認することで、レジスタオフセットアドレスや、設定すべき値が分かります。ベースアドレスは、Vivadoで自由に決めることが出来ますが、今回はVivadoが自動で割り当てたものを使用します。このベースアドレスは先ほど確認しました。

関係するレジスタを以下に挙げます。今回配置したAXI GPIOでは、Channel 1をLEDに接続しています。Channel2は未使用です。

- AXI GPIOのベースアドレス: 0x41200000 (Vivadoで確認。変更可能)
- GPIO_DATA (Channel 1 AXI GPIO Data Register)のオフセット: 0x0000
- GPIO_TRI (Channel 1 AXI GPIO 3-state Control Register.)のオフセット: 0x0004

### コード
GPIO_TRIに0を設定することで、出力モードにします。GPIO_DATAに0x0F/0x00を出力することで、4つのLEDを全部On/Offさせます。ちなみに、ベースアドレスは値直打ちではなくて、xparameters.hで定義されているXPAR_AXI_GPIO_0_BASEADDRを使用すべきです。

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

#define REG(address) *(volatile unsigned int*)(address)

#define GPIO_BASE (0x41200000)	/* XPAR_AXI_GPIO_0_BASEADDR */
#define GPIO_DATA (GPIO_BASE + 0x0000)
#define GPIO_TRI  (GPIO_BASE + 0x0004)

int main()
{
    init_platform();

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

    /* Set all of 4 pins(LEDs) as output */
    REG(GPIO_TRI) = 0x00;
    while(1) {
    	/* Set all of 4 pins(LEDs) as High */
    	REG(GPIO_DATA) = 0x0F;
    	sleep(1);
    	/* Set all of 4 pins(LEDs) as Low */
    	REG(GPIO_DATA) = 0x00;
    	sleep(1);
    }

    cleanup_platform();
    return 0;
}
```

今回は、PLの実装もあるので、メニューバー -> Xilinx -> Program FPGAでビットストリームを書き込んでから、ソフトウェアをRunします。 すると、4つのLEDが1秒間隔でチカチカするはずです。

## Xilinx SDK Driver APIでAXI GPIOを制御する
前回と同様に、ドライバAPI(関数)でAXI GPIOを制御してみます。今回のプロジェクトではAXI GPIOを使っています(前回はGPIO PSでした)。そのため、SDKでBSPプロジェクトを作った時に、gpioというライブラリも用意されています(前回はgpiops)。

## コード
使い方はほとんどgpiopsと同じですが、微妙に異なります。とはいえ所詮GPIOなので、雰囲気で実装できると思います。LEDはAXI GPIOのChannel 1に接続しているので、XGpio_SetDataDirectionとXGpio_DiscreteWriteの第2引数(Channel)には1を設定しています。

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

int main()
{
    init_platform();

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

 	XGpio instXGpio;
    XGpio_Initialize(&instXGpio, XPAR_AXI_GPIO_0_DEVICE_ID);
    /* Set all of 4 pins(LEDs) as output */
    XGpio_SetDataDirection(&instXGpio, 1, 0);

    while(1) {
    	/* Set all of 4 pins(LEDs) as High */
    	XGpio_DiscreteWrite(&instXGpio, 1, 0x0F);
    	sleep(1);
    	/* Set all of 4 pins(LEDs) as Low */
    	XGpio_DiscreteWrite(&instXGpio, 1, 0x00);
    	sleep(1);
    }

    cleanup_platform();
    return 0;
}
```

# 参考資料
https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf