PYNQ-Z1でカスタムIP作成してLEDチカ


アジェンダ
0. Vivado WebPACK Editionのインストール
1. プロジェクトの作成
2. IP配置
3. カスタムIPの作成
4. 回路の追加
5. bit streamの生成
6. Jupyter notebookからの実行
#0. Vivado WebPACK Editionのインストール

Vivado WebPACK Editionを下記から、アカウント作ってインストールしておく。
https://www.xilinx.com/support/download.html
Vivado Design Suite - HLx Edition - xxxx.x  Full Product Installation

以下説明は、下記環境の実行例です。
-OS: CentOS 7.4
-Vivado version: Vivado v2017.3
-FPGA評価ボード: PYNQ-Z1

vivadoを起動
sudo /opt/Xilinx/Vivado/2017.3/bin/vivado

#1. プロジェクトの作成

Vivadoを起動して、Quick StartのCreate Projectをクリック。Project Nameに適当な名前と、Project Locationに保存ディレクトリを入力。create project subdirectoryは余計なディレクトリを作らないようにチェックを外したまま
<img width="696" alt="Screen Shot 2018-02-10 at 23.54r.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/e39f4031-c4b9-a45a-9959-95991e364a47.png">

Project TypeはRTL Projectを選択。Do not specify sources at this timeはチェックにしたまま
<img width="696" alt="Screen Shot 2018-02-10 at 23.54.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/086a2e57-198b-cae1-de96-2fe3109417cf.png">
Default Partでは利用するFPGAボードを選択。
PYNQ-Z1では下図のようにxc7z020clg400-1を選択
Family: Zynq-7000
Package: clg400
Speed grade: -1
<img width="696" alt="Screen Shot 2018-02-10 at 23.55.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/eed4854d-bf81-faea-dd4c-845f40d7ef16.png">

#2. IP配置
Flow Navidator->IP Integrator->Create Block Designをクリック。とりあえず、デフォルトのままOK
<img width="696" alt="Screen Shot 2018-02-11 at 00.02.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/996604e3-d78d-585c-c11c-9eced0bc4d0e.png">
Diagramのキャンバス内のAdd IPの+ボタンをクリック
<img width="1392" alt="Screen Shot 2018-02-11 at 00.02r.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/f9cc23dc-352a-3fef-de32-1cb8c7c3bdfc.png">
ZYNQ7 Processing Systemを見つけダブルクリックしてIPをキャンバス内に配置
<img width="696" alt="Screen Shot 2018-02-11 at 00.03.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/840524e4-e875-7c27-fde8-e60e337b761e.png">
配置されたIP(水色のZYNQと書かれた箱)をダブルクリック
<img width="696" alt="Screen Shot 2018-02-11 at 00.03.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/4be7bc8e-23be-77bd-ca9c-3fb02a8b8bd1.png">
メニューバーのPresets->Apply Configurationを選択。
<img width="1392" alt="Screen Shot 2018-02-11 at 00.04.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/72cf198c-41b1-aae2-306b-58147aa6fa0d.png">
PYNQ-Z1用のPreset TCL File(pynq_revC.tcl)を選択。(事前に、下記からDesign ResourcesのZynq Presets(pynq_revC.zip)をダウンロード/解凍しておく。)
https://reference.digilentinc.com/reference/programmable-logic/pynq-z1/start
<img width="696" alt="Screen Shot 2018-09-20 at 19.53.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/bc0f3435-01c2-27d3-63b7-e05040a59872.png">
上記pynq_revC.tclでは、GP0が無効化されているので、有効化する。PS-PL Configurationを選択。AXI Non Secure Enablement->GP Master AXI Interfaceを展開し、M AXI GP0 Interfaceにチェック
<img width="696" alt="Screen Shot 2018-02-11 at 00.06.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/e2f59df3-1da5-9816-6b49-652e812504d0.png">

#3. カスタムIPの作成
メニューバーのTools->Create and Package IP...を選択
<img width="696" alt="Screen Shot 2018-02-11 at 00.06.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/cd16cd0c-221f-7b03-cfdb-4c82a955d912.png">
Create a new AXI4 peripheralを選択
<img width="696" alt="Screen Shot 2018-02-11 at 00.07.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/e173a2cf-452c-78c1-2e23-93e6805c5e9d.png">
Name等はデフォルトのまま、IP Locationにプロジェクトのディレクトリを設定
<img width="696" alt="Screen Shot 2018-02-11 at 23.40.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/61d861d2-4e66-f403-06a4-78e077037502.png">
AXI4インタフェース設定は、デフォルトのまま
<img width="696" alt="Screen Shot 2018-02-11 at 23.41.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/9f15bb91-83a1-5073-7a5e-9dc42ecb957a.png">
Edit IPを選択
<img width="696" alt="Screen Shot 2018-02-11 at 23.41.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/c406fbec-7557-b304-5f55-298721151184.png">

#4. 回路の追加
<img width="696" alt="Screen Shot 2018-02-11 at 23.44.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/04b5687c-c48e-4976-b827-acc0e4d24639.png">
Sources->Design Sourcesのmyip_v1_0を展開、myip_v1_0_S00_AXI_inst:myip_v1_0_S00_AXIをダブルクリック、myip_v1_0_S00_AXI.vを編集
今回は、LEDを出力ポートとして追加します。
<img width="696" alt="Screen Shot 2018-02-11 at 23.45.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/13f499cb-6e7c-c5f4-6d19-98e10772c9e1.png">
18行目くらいに下記のように出力ポートを追加

```
//Users to add ports here
output wire [3:0] LED,
```
同様に400行目くらい下記のようにレジスタをポート信号へ接続

```
//Add user logic here
assign LED = slv_reg0[3:0];
```
編集したら、フロッピーディスクアイコンをクリックしてSaveします。
Sources->Design Sourcesのmyip_v1_0をダブルクリック、上位階層のmyip_v1_0.vを編集
<img width="696" alt="Screen Shot 2018-09-20 at 20.17.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/9f17293c-f5d7-3dd7-55dc-04a58deb7f80.png">
同様に、18行目くらいに下記のように出力ポートを追加

```
//Users to add ports here
output wire [3:0] LED,
```
51行目くらいに下記のように下位階層のポートを追加

```
) myip_v1_0_S00_AXI_inst (
	.LED	(LED),
	.S_AXI_ACLK(s00_axi_aclk),
```
<img width="696" alt="Screen Shot 2018-02-11 at 23.49.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/6e9962ee-3d6b-0b54-4f0c-e9f8f03e5672.png">
Package IPペインから、Customization Parametersを選択し、右上ののMerge changes from~をクリック
<img width="696" alt="Screen Shot 2018-02-11 at 23.50.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/ccd0608b-bd74-4cab-d1bf-328a1e4bf34a.png">
Cutomization GUIを選択すると、LED端子が追加されている。
<img width="696" alt="Screen Shot 2018-02-11 at 23.51.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/c03ec17f-be9b-721d-4226-a7e910ae6ac2.png">
Review and Packageを選択し、Re Package IPをクリック
<img width="696" alt="Screen Shot 2018-02-11 at 23.55.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/e097793b-fc31-ffc2-aa7d-d54d696ae1a3.png">
Diagramウィンドウに戻り、ペイン上部の+アイコン(Add IP)をクリックし、先ほど作ったmyip_v1.0をダブルクリックして配置
<img width="696" alt="Screen Shot 2018-02-11 at 23.56.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/02beab3a-32e8-ce4a-246c-1ae430443671.png">
ペイン上部のRun Connection Automationをクリック(その横のRun Block Automationもやっとく)
<img width="696" alt="Screen Shot 2018-02-11 at 23.56.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/37c88592-367c-f721-33ab-f1b4fe6ee97d.png">
そのままOKすると結線される。
<img width="696" alt="Screen Shot 2018-02-11 at 23.57.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/73eace02-00f4-88b0-e5b7-6e7fc74045c4.png">
myip_0の端子LED[3:0]の上で右クリックメニューからMake Externalを実行
<img width="1392" alt="Screen Shot 2018-02-11 at 23.58r.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/ec5250dd-1b68-2f2e-628e-b54eddeb1fcd.png">
出力ポートが接続される。
<img width="696" alt="Screen Shot 2018-09-20 at 23.21r.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/1a744127-6524-2eb9-93d2-3770eeaf0946.png">
出力先がLEDでなくLED_0に接続となる時は、LED_0をクリックするとExternal Port PropertiesがLED_0のプロパティを表示するので、NameをLEDに修正する。
<img width="696" alt="Screen Shot 2018-02-11 at 23.58.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/b9565323-92a6-de3c-71ba-190f9a319b53.png">
右クリックメニューからValidate Designを実行し、デザインをチェック
<img width="696" alt="Screen Shot 2018-02-11 at 23.59.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/c941ee74-920e-70a3-094e-846401303107.png">
Sources->Design Sources->design_1を右クリックし、Create HDL Wrapperを実行
<img width="696" alt="Screen Shot 2018-02-12 at 00.00.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/acd16999-8476-816a-4b98-9eff6c502ae3.png">
Sources->Constraints->constrs_1を右クリックし、Add sourcesを実行
<img width="696" alt="Screen Shot 2018-02-12 at 00.00.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/05968fbe-1b69-da28-ccce-1382fdb5a245.png">
Add or creates constraintsを選択
<img width="696" alt="Screen Shot 2018-09-20 at 21.40.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/93dbaf2b-873f-0f60-de56-45cbc7cf1015.png">
制約ファイルを読み込み
事前に、上記digilentのサイトからDesign ResourcesのMaster XDC(pynq-z1_c.zip)をダウンロード/解凍、LEDsの部分を下記のようにコメントアウト/修正しておく(ledをLEDに)。

```
##LEDs

set_property -dict { PACKAGE_PIN R14   IOSTANDARD LVCMOS33 } [get_ports { LED[0] }]; #IO_L6N_T0_VREF_34 Sch=led[0]
set_property -dict { PACKAGE_PIN P14   IOSTANDARD LVCMOS33 } [get_ports { LED[1] }]; #IO_L6P_T0_34 Sch=led[1]
set_property -dict { PACKAGE_PIN N16   IOSTANDARD LVCMOS33 } [get_ports { LED[2] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=led[2]
set_property -dict { PACKAGE_PIN M14   IOSTANDARD LVCMOS33 } [get_ports { LED[3] }]; #IO_L23P_T3_35 Sch=led[3]
```
<img width="1392" alt="Screen Shot 2018-02-12 at 00.01.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/e9ada269-6574-0999-8ad2-f6bf27400831.png">

#5. bit streamの生成
<img width="1392" alt="Screen Shot 2018-02-12 at 00.01.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/ff256117-02f9-76b7-b51e-f161e1d39615.png">
Flow Navigator->PROGRAM AND DEBUG->Generate Bitstreamをクリック。
ウィンドウの右上で緑の風車?がくるくる回るので終わるまで待つと、下部のTCL Consoleに下記のようなログが出るので、プロジェクトディレクトリ内の当該ファイルdesign_1_bd.tclとdesign_1_wrapper.bit を、PYNQ-Z1上の/home/xilinx/pynq/overlays/baseディレクトリにscp等でコピーしておく。(PYNQ-Z1はデフォルトの構成です。)
design_1_bd.tclはdesign_1_wrapper.tclにリネームして置く。

```
...
Generated Block Design Tcl file /home/kenichi/blink_zq/blink_zq.srcs/sources_1/bd/design_1/hw_handoff/design_1_bd.tcl
...
Run output will be captured here: /home/kenichi/blink_zq/blink_zq.runs/impl_1/runme.log
Writing bitstream ./design_1_wrapper.bit...
```


#6. Jupyter notebookからの実行
PYNQ-Z1上のJupyter notebookで下記のようにpython scriptを書く。
Jupyter notebookの使い方は、この辺を見てください。https://pynq.readthedocs.io/en/v2.0/jupyter_notebooks.html

<img width="696" alt="Untitled.png" src="https://qiita-image-store.s3.amazonaws.com/0/117100/f0330865-51f9-1e2d-7117-6fbc596b200b.png">
In[1]:

```
from pynq import Overlay
from pynq import PL
OL = Overlay("/home/xilinx/pynq/overlays/base/design_1_wrapper.bit")
```
bitstreamファイルを指定

In[2]:

```
OL.download()
PL.ip_dict
```
bitstreamを読み込んで、デバイスファイルに書き込み

In[3]:

```
from pynq import MMIO
mmio=MMIO(int(PL.ip_dict["myip_0"]["phys_addr"]), 4, True)
mmio.write(0,15)
```
MMIOクラスを用いて、FPGAのレジスタにアクセス

In[4]:

```
import time
while(True):
    for i in range(16):
        mmio.write(0,i)
        time.sleep(0.5)
```

LEDチカ
![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/117100/c642f530-5af3-72ba-d9d2-e3e734886beb.gif)


#参考
[PYNQ-Z1のOverlay読み込みとPythonからのFPGA PLの制御](http://todotani.cocolog-nifty.com/blog/2017/01/pynq-z1overlayp.html)
[FPGAプログラミング大全 Xilinx編](https://www.shuwasystem.co.jp/products/7980html/4753.html)