Zynq-7000で，独自回路をIPにして，AXI4でPS-PL通信してプログラムから利用する (Vivado)

#やりたいこと
Zed boardを買ってZynqいじりしていたのですが，自分でHDLゴリゴリ書いて作った独自回路をPLに書き込んでPSから利用する方法がよく分からなかったので，まとめてみました．
AXI4インタフェースを持った独自IPを作成し，Zynqのコアとつなぎます．

ここでは単純な加算器をAXI4-Liteインタフェースを利用した独自IPとして作成し，CPUから利用してみます．

使用したVivadoのバージョンは2014.2です．

#プロジェクトの作成
まずは普通にプロジェクトを作ります．
ここではadder_testという名前にしました．
![vivado_create_project2.png](https://qiita-image-store.s3.amazonaws.com/0/65141/0c7896c3-28fd-cc3c-b568-77fa0cd332a1.png)
![vivado_create_project3.png](https://qiita-image-store.s3.amazonaws.com/0/65141/6661f4c1-a6da-7daa-36f9-53f76333a5c4.png)
![vivado_create_project4.png](https://qiita-image-store.s3.amazonaws.com/0/65141/b7d94703-7b34-e97b-4355-27faa370131e.png)
![vivado_create_project.png](https://qiita-image-store.s3.amazonaws.com/0/65141/57acd121-db28-46e7-bb42-54b5c73629d6.png)

#Block design
左側のFlow navigatorから，IP Integrator -> Create Block Design でBlock Designを開きます．名前はここでは`system`にしました．
![vivado_block_design.png](https://qiita-image-store.s3.amazonaws.com/0/65141/abda3bdc-3084-9cb3-81d1-1a5622433def.png)

Add IPをクリックして，`ZYNQ7 Processing System`を選択し，Zynqのコアを追加します．
![vivado_block_design_zynq.png](https://qiita-image-store.s3.amazonaws.com/0/65141/09a43a1c-70bd-ca21-f379-0764201686f8.png)

Zynqコアをダブルクリックしてカスタマイズします．
![vivado_block_design_zynq2.png](https://qiita-image-store.s3.amazonaws.com/0/65141/9ede3a4d-597f-9522-44ee-aa28ce36fed7.png)
PresetsからZed boardを選び，OKします．（IPとの通信のためにAXI Masterインタフェース，テストプログラムのためにUARTがあればよいと思いますが，面倒くさいのでプリセットから選びます．）

#独自IP追加
上のメニューから，Tool -> Create and Package IP を選びます．
こんなダイアログが出ます．
![vivado_create_ip.png](https://qiita-image-store.s3.amazonaws.com/0/65141/71d59bc7-51fe-11dc-268d-ec765515a451.png)
nextを押します．

![vivado_create_ip2.png](https://qiita-image-store.s3.amazonaws.com/0/65141/742c4c76-dac1-d4e1-5b1e-2ab63fe9f294.png)
Create a new AXI4 peripheralを選んでNextを押します．

![vivado_create_ip3.png](https://qiita-image-store.s3.amazonaws.com/0/65141/759b5f7f-aafc-3713-6363-c44e6663044b.png)
IP名を適当に決めて，Nextを押します．今回はsimple_adderにしました．

![vivado_create_ip4.png](https://qiita-image-store.s3.amazonaws.com/0/65141/82f631ef-433c-57d4-e5ed-d1a07bef061f.png)
S00_AXIをS_AXIにリネームしました．リネームする必要は無いと思いますが，一つしかポートが無いので番号は消しました．

![vivado_create_ip5.png](https://qiita-image-store.s3.amazonaws.com/0/65141/372ab8e3-f0c2-b9ac-9b88-839e4658ce02.png)
Edit IPを選択してFinishを押します．

#IP編集
新しいウィンドウが開いて，IPの編集が行えます．
![vivado_edit_ip1.png](https://qiita-image-store.s3.amazonaws.com/0/65141/179352bd-4c86-c4b6-8410-442ebb089860.png)

![vivado_edit_ip2.png](https://qiita-image-store.s3.amazonaws.com/0/65141/1ecab618-9b5f-6fdf-b870-bc9360c4876a.png)
Sourcesペインを見ると，ソースファイルが2つできています．
トップレベルのsimple_adder_v1_0の下にAXI slaveインタフェースのsimple_adder_v1_0_S_AXIがあります．
詳しくは中身を読んでください．

##ソース編集
`simple_adder_v1_0_S_AXI.v`を編集して，加算器にします．
このモジュールは`slv_reg0`, `slv_reg1`, `slv_reg2`, `slv_reg3`というレジスタを持っていて，Writeするとこれらのレジスタにデータが入り，Readするとこれらのレジスタから値が読み出されるようになっています．
ベースアドレスでアクセスすると`slv_reg0`にWrite/Readされ，ベースアドレスにオフセットを足して`slv_reg1`, `slv_reg2`, `slv_reg3`にアクセスします．この辺は詳しくは後述します．

今回は例として`slv_reg0`と`slv_reg1`の和を`slv_reg2`に入れるようにしてみます．

`slv_reg0`, `slv_reg1`, `slv_reg2`, `slv_reg3`をドライブしている`always @( posedge S_AXI_ACLK )`~`end`ブロックを探します．
`if (slv_reg_wren)`~`end`の中で`slv_reg0`, `slv_reg1`, `slv_reg2`, `slv_reg3`への書き込みが行われているのがわかります．（これがWrite処理）
ここに`else`を足して，

```verilog
slv_reg2 <= slv_reg0 + slv_reg1;
```
を書き加えます．（もっとうまいやり方があるのだろうな）

##パッケージ
左側のFlow NavigatorからProject Manager -> Package IPを選びます．
![vivado_edit_ip3.png](https://qiita-image-store.s3.amazonaws.com/0/65141/60f221b3-1df4-4e20-ccb6-f344271af697.png)
全項目に緑色チェックマークがついているのでOKです．Review and Packageタブを選んでRe-Package IPを押します．
これでIPの編集は終了です．

#IP追加
もともとのプロジェクトのウィンドウに戻ってきました．

![vivado_add_ip1.png](https://qiita-image-store.s3.amazonaws.com/0/65141/7854904b-d592-b2f3-edea-c3942ae255c6.png)
Add IPを押して，先ほど作った独自IP，simple_adder_v1.0を追加します．

![vivado_add_ip2.png](https://qiita-image-store.s3.amazonaws.com/0/65141/4a997b05-7bcb-fb96-c145-e3951ac5c1c5.png)
Run Block Automation, Run Connection Automationを実行します．

![vivado_add_ip3.png](https://qiita-image-store.s3.amazonaws.com/0/65141/a4ff31ab-e9dc-3a75-1c0c-9f66c0cc3131.png)
こんな感じで配線されます．（Regenerate Layoutボタンを押すと再配置されて見やすくなります．）

Address Editorタブを選択してみます．
![vivado_add_ip4.png](https://qiita-image-store.s3.amazonaws.com/0/65141/b3caafbe-0d24-6873-02af-d9983b3340d8.png)

追加したsimple_adderにアドレスが割り振られているのが確認できます．

#Bitstream作成
Sourcesペインで`system_i - system (system.bd)`を右クリックして，メニューから`Create HDL Wrapper`を選びます．ダイアログでは`Let Vivado manage wrapper and auto-update`を選んでおくと良いと思います．
これでトップレベルのHDLが生成されます．

また`system_i - system (system.bd)`を右クリックして，メニューから`Generate Output Products`を選びます．ダイアログでGenerateをクリックします．

そうしたら，Generate Bitstreamを押してビットストリームを作成します．ダイアログでsaveするかとかsynthesisとimplementationするかとか聞かれますがyesで．

#ソフトウェアの作成
## SDKへエクスポート
Bitstreamができたら，上部のメニューからFile -> Export -> Export Hardwareを選択します．
ダイアログでInclude bitstreamにチェックが入っていることを確認して，OKを押します．

File -> Launch SDK を選択してSDKを起動します．
これでハードウェアの情報がSDKにも読み込まれます．

![vivado_sdk_1.png](https://qiita-image-store.s3.amazonaws.com/0/65141/d6f2b480-b778-864e-edf1-63d03af7b7a5.png)
SDKの起動直後の画面でsimple_adderのアドレスがあることを確認

##SDKでプロジェクト作成
上部のメニューから File -> New -> Application Projectを選択．
![vivado_sdk_2.png](https://qiita-image-store.s3.amazonaws.com/0/65141/449e65cd-f536-d6b2-5624-8201fa1f1f32.png)
プロジェクト名を適当に決めて，あとはデフォルトのままNext．

![vivado_sdk_3.png](https://qiita-image-store.s3.amazonaws.com/0/65141/45d5befe-6fb0-5ebd-9958-8b5f8d51d86b.png)
Hello World を選んでFinish

##ソース編集
adder_testプロジェクト以下のsrc -> helloworld.c を編集します．

```c
#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "xil_io.h"

#define SIMPLE_ADDER_ADDR_OFFSET1 4
#define SIMPLE_ADDER_ADDR_OFFSET2 8

int main()
{
    u32 result;

    init_platform();

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

    Xil_Out32(XPAR_SIMPLE_ADDER_0_S_AXI_BASEADDR, 123);
    Xil_Out32(XPAR_SIMPLE_ADDER_0_S_AXI_BASEADDR + SIMPLE_ADDER_ADDR_OFFSET1, 456);
    result = Xil_In32(XPAR_SIMPLE_ADDER_0_S_AXI_BASEADDR + SIMPLE_ADDER_ADDR_OFFSET2);

    xil_printf("123 + 456 = %d\r\n", result);

    cleanup_platform();

    return 0;
}
```

```c
#define SIMPLE_ADDER_ADDR_OFFSET1 4
```
はsimple_adder_v1_0.vに定義されている

```verilog
		parameter integer C_S_AXI_ADDR_WIDTH	= 4
```
にもとづいています．


#### simple_adder.h
インクルードファイルはadder_test_bspプロジェクト以下のSrc -> include内にあります．
たまに`simple_adder.h`が生成されてsimple_adderとやりとりするのに便利なマクロが定義されたりするんですが，どうやったらこのファイルが生成されてくれるのかわかりません…
誰か教えてください

##デバッグ実行
adder_testを選択した状態で上部のメニューからRun -> Debug Configurationsを選択します．

ダイアログでXilinx C/C++ application (GDB)をダブルクリックします．

![vivado_sdk_4.png](https://qiita-image-store.s3.amazonaws.com/0/65141/142aca10-ed56-ad5f-8a8d-d9ac1faf4a0f.png)
Reset Entire Systemを選択し，Program FPGAにチェックが入った状態にします．

![vivado_sdk_5.png](https://qiita-image-store.s3.amazonaws.com/0/65141/b19c543b-c769-3ea5-e988-61ea644b911c.png)
STDIO Connectionタブでコンソールの設定を行います．
Tera term等，別のターミナルソフトを使う場合は不要です．

設定が終わったら，Applyして設定を保存し，Debugを押します．パースペクティブを変えますか的なダイアログが出たらOKします．

##確認
デバッグモードなのでステップ実行が始まります．とりあえずここではResumeを押して普通に実行してしまいます．

![vivado_sdk_6.png](https://qiita-image-store.s3.amazonaws.com/0/65141/ad08f832-aae6-863f-6faf-e0d471b2a556.png)
コンソールに

```
Hello World
123 + 456 = 579
```
と表示され，加算回路にAXIを使ってアクセスし利用することができたのがわかります．

#言い訳
以上です．
Zynqを触り始めて1ヶ月くらいの初心者なのでご指摘などなどぜひよろしくお願いします．