MicroBlazeでAXI GPIOの外部ピン割り込みを実装

## 概要
MicroBlazeで外部ピン入力からの割り込みを実装し、AXI GPIOのInterruptとAXI Interrupt Controllerの使い方を学びます。
割り込み処理はこれから書こうと思っているAXI Quad SPIやAXI IICなどを使ったSPIやI2C通信をするために必要となります。

## 環境
* Vivado 2018.3

## Block Designの作成
### MicroBlazeの追加
予めClocking Wizardで100MHzのシステムクロックを容易しておいた状態で、MicroBlazeを追加します。
![000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410452/380435b2-f0cb-c378-f951-b7ab686d2c86.png)
**Run Block Automation**を実行して、下図のように設定をします。Interrupt Controllerのチェックは必須です。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410452/c3813bb0-49aa-d0ff-8dc4-014a6d58e259.png)
**Run Connection Automation**を実行した後の配線です。AXI Interrupt Controllerに接続されたConcatに割り込み信号を入力していきます。
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410452/906d38de-2f93-be5b-6b13-ccbcc96e922e.png)

### AXI GPIOの追加
AXI GPIOを追加して**Run Connection Automation**で配線をしましょう。
GPIOバスはボタンスイッチが接続されます。
Interruptを有効にし、ip2intc_irptピンはAXI Interrupt Controllerと接続します。
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410452/140e4674-1c6f-10e9-a947-e2faf5b874ca.png)

### UARTの追加
デバッグメッセージ等を表示するために、UARTを追加します。
このあたりはボードによって設定が違うと思いますので、説明は省略します。
![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410452/a63c2d4d-6325-3700-a945-24ef39779949.png)

### Interrupt入力の数を修正
今回はGPIO Interruptのみを使うので、AXI Interrupt Controllerに接続されたConcatの入力を1つに修正しましょう。
空いているピンがあるとSDKでプロジェクトを生成するときにエラーが出るので注意です。
![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410452/f9306ff5-f376-6695-f151-c09c4ff16b05.png)

## Bitstreamの生成とSDKプロジェクトの作成
Block Designの作成が終わったら、ピン配置の設定などを行いGenerate Bitstreamを行ってください。
また、Export Hardwareを行ってからXilinx SDKを起動してください。

SDKでは**Hello Worldテンプレートプロジェクト**を作成しておきます。

## サンプルコード
```c++
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xintc.h"
#include "xgpio.h"

XGpio Gpio;
XIntc Intc;

// GPIO Interrupt Handler
void GpioHandler(void *CallbackRef)
{
    XGpio *GpioPtr = (XGpio *)CallbackRef;
    xil_printf("Gpio Interrupt!\r\n");
    // Clear the Interrupt
    XGpio_InterruptClear(GpioPtr, 1);
}

int main()
{
	int Status;

    init_platform();

    // XGpioの初期化
    Status = XGpio_Initialize(&Gpio, XPAR_GPIO_0_DEVICE_ID);
    if(Status != XST_SUCCESS) return XST_FAILURE;
    // Buttonピンを入力に設定
    XGpio_SetDataDirection(&Gpio, 1, 1);

    // XIntcの初期化
    Status = XIntc_Initialize(&Intc, XPAR_INTC_0_DEVICE_ID);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    // XGpioの割り込みで呼び出される関数を指定
    Status = XIntc_Connect(&Intc, XPAR_INTC_0_GPIO_0_VEC_ID,
                (XInterruptHandler)GpioHandler,
                (void *)&Gpio);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    //XIntcをhardware interrupts onlyで開始
    Status = XIntc_Start(&Intc, XIN_REAL_MODE);
    if(Status != XST_SUCCESS) return XST_FAILURE;

    // XGpioの割り込みを有効化
    XIntc_Enable(&Intc, XPAR_INTC_0_GPIO_0_VEC_ID);

    // MicroBlazeの割り込み設定を初期化
    Xil_ExceptionInit();
    // MicroBlazeにXIntcからの割り込みを登録
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                (Xil_ExceptionHandler)XIntc_InterruptHandler,
                &Intc);
    // MicroBlazeの割り込みを有効化
    Xil_ExceptionEnable();

    // GPIO Channel 1の割り込みを有効化
    XGpio_InterruptEnable(&Gpio, 1);
    // GPIOのグローバル割り込みを有効化
    XGpio_InterruptGlobalEnable(&Gpio);

    while(1);

    cleanup_platform();
    return 0;
}
```

## 実行結果
ボタンスイッチを押すと割り込みハンドラ関数が呼び出されました。
![006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410452/60b642fa-5996-8012-3d44-2139ad08fbd4.png)

## 参考
AXI GPIOの使い方にはこちらを参考にしてください。
[Xilinx AXI GPIO の使い方](https://qiita.com/s_nkgw/items/800e0559332495605056)