これは何か
PYNQ-Z1 を使って遊んでみます。
今回は、下記ができるようになる事を目指します。
- BRAM を使ってみる
- (本記事) AXI BRAM Controller を使って python から読み書きしてみる
- (次の記事) 自作 IP を接続して、FPGA 内部で生成したデータを python から読み出してみる
関連記事:
関連するリンク
- 公式ドキュメント
- UG473 : 7 シリーズ FPGA メモリリソース ユーザーガイド
-
PG58 : Block Memory Generator Product Guide
- BRAM を使うために使う IP
-
PG78 : AXI Bloack RAM Contnroller Product Guide
- BRAM を AXI から接続して使う IP
- 解説
BRAM の概要
- BRAM は、FPGA 内部に実装されたメモリです
- 7 シリーズの場合、BRAM 1 つで、18 Kbit x2、または 36 Kbit x1 として使えます
- PYNQ-Z1 に搭載されている XC7Z020-1CLG400C の場合、140 個乗っています (DS190 参照)
- また、出力レジスタなし (1CLK) で取り出す場合 288 MHz、出力レジスタあり (2CLK) で取り出す場合 382 MHz で駆動可能です (PG58 参照)
- 実際の FPGA 上での配置は下のような雰囲気です
3-1. プロジェクトの作成
-
PYNQ や Vivado のセットアップがまだな場合
- 手順 1-2. 開発の準備 を参考に済ませておきます
-
手順 1-3. プロジェクトの作成 を参考に、新規プロジェクトを作成します
- Project name : asobu03
- Project location : ~/vivado/asobu
- にしました。
-
手順 1-4-1. ZYNQ の IP を配置する を参考に、Block Design を作ります
- 名前は design_1 のままにしました
- ZYNQ も配置しておきます
3-2. BRAM 関連の IP を配置する
3-2-1. Block Memory Generator IP を配置する
IP を検索し、追加。
Block Memory Generator が追加されました。
今回はデフォルトのまま使用します。
3-2-2. AXI BRAM Controller IP を配置する
IP を検索し、追加。
配置されました。
デフォルトでは PORT A, PORT B が利用可能です。
PORT A だけを使用するように設定してみます。他のパラメータはデフォルトのままです。
3-2-3. 配線する
axi_bram_ctrl_0.BRAM_PORTA を blk_mem_gen_0.BRAM_PORTA に接続します。
配線されました。
Run Connection Automation を実行します。
配線されました。
Run Block Automation を実行します。
最終的にこうなりました。
BRAM の設定を確認してみましょう。
8 BRAM 使用して、1 クロックでデータを取り出せるようです。
3-3. 後仕上げ
3-3-1. HDL Wrapper を生成する
手順 1-5. HDL Wrapper を生成する と同様の手順です
3-3-2. 生成する
手順 1-7. 生成する と同様の手順です
こんな実装になりました。
3-4. PYNQ で実行する
3-4-1. ファイルのアップロード
- 手順 1-8-1. ファイルのアップロード を参考に PYNQ へアップロードします
- 例えばこんなコマンドになります
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 で実行 を参考に実行します
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]
# 取り出せました。