PYNQ で遊ぶ : 3. BRAM を使う (1)

# これは何か

PYNQ-Z1 を使って遊んでみます。

今回は、下記ができるようになる事を目指します。

- BRAM を使ってみる
    - （本記事） AXI BRAM Controller を使って python から読み書きしてみる
    - （次の記事） 自作 IP を接続して、FPGA 内部で生成したデータを python から読み出してみる


##### 関連記事：

- [PYNQ で遊ぶ : 1. LED を点滅させる
](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8)
- [PYNQ で遊ぶ : 2. Verilog HDL を使ってみる](https://qiita.com/nishimuraatsushi/items/e05b7185de463c0b006a)


# 関連するリンク
- 公式ドキュメント
    - [UG473 : 7 シリーズ FPGA メモリリソース ユーザーガイド](https://japan.xilinx.com/support/documentation/user_guides/j_ug473_7Series_Memory_Resources.pdf)
    - [PG58 : Block Memory Generator Product Guide](https://japan.xilinx.com/support/documentation/ip_documentation/blk_mem_gen/v8_4/pg058-blk-mem-gen.pdf)
        - BRAM を使うために使う IP
    - [PG78 : AXI Bloack RAM Contnroller Product Guide](https://japan.xilinx.com/support/documentation/ip_documentation/axi_bram_ctrl/v4_1/pg078-axi-bram-ctrl.pdf)
        - BRAM を AXI から接続して使う IP
- 解説
    - [ACRi : PYNQ を使って Python で手軽に FPGA を活用 (5)](https://www.acri.c.titech.ac.jp/wordpress/archives/36)


# BRAM の概要

- BRAM は、FPGA 内部に実装されたメモリです
- 7 シリーズの場合、BRAM 1 つで、18 Kbit x2、または 36 Kbit x1 として使えます 
- PYNQ-Z1 に搭載されている XC7Z020-1CLG400C の場合、140 個乗っています (DS190 参照)

<img width=400 alt="DS190 表1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/feb80c47-a16c-9b9b-71e5-aa1b0150c47f.png">

- また、出力レジスタなし (1CLK) で取り出す場合 288 MHz、出力レジスタあり (2CLK) で取り出す場合 382 MHz で駆動可能です (PG58 参照)

<img width=500 alt="PG58 Table 2-1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/42f45231-8bd6-3822-db1d-ff8bdcc15ef1.png">

- 実際の FPGA 上での配置は下のような雰囲気です

<img width=500 alt="スクリーンショット 2020-11-02 10.44.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/6f459102-fdfe-602c-54fa-8dbe1c5bd7b2.png">

# 3-1. プロジェクトの作成
- PYNQ や Vivado のセットアップがまだな場合
    - [手順 1-2. 開発の準備](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8#1-2-%E9%96%8B%E7%99%BA%E3%81%AE%E6%BA%96%E5%82%99) を参考に済ませておきます

- [手順 1-3. プロジェクトの作成](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8#1-3-%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90) を参考に、新規プロジェクトを作成します
    - Project name : asobu03
    - Project location : ~/vivado/asobu
    - にしました。

- [手順 1-4-1. ZYNQ の IP を配置する](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8#1-4-1-zynq-%E3%81%AE-ip-%E3%82%92%E9%85%8D%E7%BD%AE%E3%81%99%E3%82%8B) を参考に、Block Design を作ります
    - 名前は design_1 のままにしました
    - ZYNQ も配置しておきます

# 3-2. BRAM 関連の IP を配置する
## 3-2-1. Block Memory Generator IP を配置する

IP を検索し、追加。

<img width="350" alt="スクリーンショット 2020-11-02 12.04.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/94e27b92-4fd9-4e65-01ba-b10b185cf770.png">

Block Memory Generator が追加されました。

<img width="350" alt="スクリーンショット 2020-11-02 12.06.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/6f2f1e3b-7df3-fce7-5362-3332043a00e7.png">

今回はデフォルトのまま使用します。


## 3-2-2. AXI BRAM Controller IP を配置する

IP を検索し、追加。

<img width="350" alt="スクリーンショット 2020-11-02 12.07.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/e9035f70-0856-4271-82ab-4760e79afe48.png">


配置されました。

<img width="350" alt="スクリーンショット 2020-11-02 12.09.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/6cba27d9-fdeb-cbbb-7e0a-baabc5b53293.png">

デフォルトでは PORT A, PORT B が利用可能です。
PORT A だけを使用するように設定してみます。他のパラメータはデフォルトのままです。

<img width="350" alt="スクリーンショット 2020-11-02 12.14.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/b5d71605-b47f-bcc4-e1c3-df5d1c6357ff.png">

<img width="350" alt="スクリーンショット 2020-11-02 12.15.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/c00daff5-3195-ee3f-d756-e24f19cfb4a3.png">


## 3-2-3. 配線する

axi_bram_ctrl_0.BRAM_PORTA を blk_mem_gen_0.BRAM_PORTA に接続します。

<img width="350" alt="スクリーンショット 2020-11-02 12.24.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/9503c75b-ac13-5b1e-9870-ba32d24de45f.png">

配線されました。

<img width="350" alt="スクリーンショット 2020-11-02 12.25.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/cbb2197d-7523-77f1-654b-71a945b5db36.png">

Run Connection Automation を実行します。

<img width="350" alt="スクリーンショット 2020-11-02 12.27.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/50d89bee-45b7-c5f8-5ce5-ecdf9f99be38.png">

配線されました。

<img width="350" alt="スクリーンショット 2020-11-02 12.28.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/26abd6d0-dd67-eb3e-da84-d8f9504745b6.png">

Run Block Automation を実行します。

<img width="350" alt="スクリーンショット 2020-11-02 12.28.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/b62d3fa4-35b5-6e94-04b8-29be2907771b.png">


最終的にこうなりました。

<img width="350" alt="スクリーンショット 2020-11-02 12.35.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/b7ca41c5-b169-54d3-6c23-61daca19ccb0.png">

BRAM の設定を確認してみましょう。

<img width="350" alt="スクリーンショット 2020-11-02 12.36.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/187a1a03-1d2e-d671-dfd8-791b824b5b42.png">

8 BRAM 使用して、1 クロックでデータを取り出せるようです。


# 3-3. 後仕上げ
## 3-3-1. HDL Wrapper を生成する
[手順 1-5. HDL Wrapper を生成する](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8#1-5-hdl-wrapper-%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B) と同様の手順です

## 3-3-2. 生成する
[手順 1-7. 生成する](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8#1-7-%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B) と同様の手順です

こんな実装になりました。

<img width="500" alt="スクリーンショット 2020-11-02 13.50.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/1dee3550-a884-cadb-88d4-e4a4e3b8d9f9.png">


# 3-4. PYNQ で実行する
## 3-4-1. ファイルのアップロード

- [手順 1-8-1. ファイルのアップロード](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8#1-8-1-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89) を参考に PYNQ へアップロードします
- 例えばこんなコマンドになります

```sh
scp ~/vivado/asobu/asobu03/asobu03.runs/impl_1/design_1_wrapper.bit xilinx@192.168.2.99:pynq/overlays/asobu03/asobu03.bit
scp ~/vivado/asobu/asobu03/asobu03.srcs/sources_1/bd/design_1/hw_handoff/design_1.hwh xilinx@192.168.2.99:pynq/overlays/asobu03/asobu03.hwh
```

## 3-4-2. jupyter で実行

- [手順 1-8-2. jupyter で実行](https://qiita.com/nishimuraatsushi/items/3e5f161eca4b2a0f06c8#1-8-2-jupyter-%E3%81%A7%E5%AE%9F%E8%A1%8C) を参考に実行します

<img width="400" alt="スクリーンショット 2020-11-02 13.57.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107797/63de2a06-3189-df20-ac9e-66ba4e25ff04.png">

```python:asobu03.ipynb
import pynq
fpga = pynq.Overlay('asobu03.bit')

bram = pynq.MMIO(fpga.ip_dict['axi_bram_ctrl_0']['phys_addr'], length=8*1024)

# 読み込んでみます。
bram.read(0)
# >> 0
# 初期値は 0 が入っていました。

# 別の値を入れてみます。
bram.write(0, 100)
bram.read(0)
# >> 100

# 別のアドレスに値を入れます。
# read, write は 4 byte 単位でアクセスします。
# アドレス (offset) は、byte 単位で入れます。
[bram.write(i*4, i) for i in range(100)]

# 読み出してみましょう。
d = [bram.read(i*4) for i in range(100)]

d[20:30]
# >> [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
# 取り出せました。
```