これは何か
PYNQ-Z1 を使って遊んでみます。
今回は、下記ができるようになる事を目指します。
- BRAM を使ってみる
- (1)AXI BRAM Controller を使って python から読み書きしてみる
- (2)自作 IP を接続して、FPGA 内部で生成したデータを python から読み出してみる
- (本記事) BRAM を ROM として使ってみる
関連記事:
- PYNQ で遊ぶ : 1. LED を点滅させる
- PYNQ で遊ぶ : 2. Verilog HDL を使ってみる
- PYNQ で遊ぶ : 3. BRAM を使う (1)
- PYNQ で遊ぶ : 4. BRAM を使う (2)
5-1. プロジェクトの作成
-
PYNQ や Vivado のセットアップがまだな場合
- 手順 1-2. 開発の準備 を参考に済ませておきます
-
手順 1-3. プロジェクトの作成 を参考に、新規プロジェクトを作成します
- Project name : asobu05
- Project location : ~/vivado/asobu
- にしました。
-
手順 1-4-1. ZYNQ の IP を配置する を参考に、Block Design を作ります
- 名前は design_1 のままにしました
- ZYNQ も配置しておきます
5-2. 自作モジュールの作成
今回は、次の構成をゴールにします。
- BRAM を ROM として設置
- 自作モジュールで、
- GPIO から、要求するアドレスを受け取り、
- BRAM-ROM から要求アドレスのデータを取り出し、
- GPIO へ返す
5-2-1. 自作モジュールを編集する
-
手順 2-2-1. Verilog HDL のファイルを追加します を参考に verilog HDL ファイルを追加します。
- ファイル名は、bram_loader.v にしました。
-
手順 2-2-2. Verilog HDL のファイルを編集します を参考に、ファイルを編集します
bram_loader.v
module bram_loader
(
input wire CLK,
input wire [31:0] req_addr,
output reg [31:0] data,
output wire bram_clk,
output reg [31:0] bram_addr,
input wire [31:0] bram_data
);
assign bram_clk = CLK;
always @(posedge CLK)
begin
bram_addr <= req_addr;
// 今回は、アドレス指示値に、末尾の 2'b00 を付けません。
// 今回は、Block Memory Generator で、Stand Alone を選択し、
// かつ、Generate address interface with 32 bits のチェックボックスを OFF にしました。
// この場合、アドレスは連続する整数で指定します。
data <= bram_data;
// 実際は、データは 1 CLK 遅れてやってきます。
// 連続してデータを取り続ける必要がある場合などは、気をつけてください。
end
endmodule
5-3. IP を配置し配線する
5-3-1. IP を配置する
- IP Integrator で IP を配置します。
- 2-3-1. 自作モジュールを配置する の要領で、bram_loader.v を配置します。
- 3-2-1. Block Memory Generator IP を配置する の要領で、Block Memory Generator を配置します。
- 1-4-2. AXI GPIO の IP を配置する の要領で、AXI GPIO を配置します。
5-3-2. Block Memory Generator を ROM で設定する
- Block Memory Generator をダブルクリックして設定を開き、
- Mode を、Stand Alone に
- Memory Type を、Single Port ROM にします。
- メモリを設定します
- データ個数 = 8 、
- Always Enabled : en ピンが不要になります
- Registor を使用しない : レイテンシが短くなります
- Reset を使わない : rst ピンが不要になります
- ROM データを設定します
- ROM データは外部ファイルで管理されます
- 今回は、読み込めるものがないので、新規作成します
- ファイル名はとりあえず、適当です
- 編集します
- 記述方式は 10 進数にしました
- Depth を 8 に、メモリを設定しておいたので、データを 8 個書きます。スペース区切りです。
- BRAM の設定状況を確認しておきましょう
- レイテンシは 1 CLK
- アドレス幅は 3 bit です
- ちなみに、生成された ROM データのファイルは以下のような感じです
- テキストなので、事前に python などで準備しておくと便利でしょう
design_1_blk_mem_gen_0_0.coe
memory_initialization_radix=10;
memory_initialization_vector=0 10 20 30 40 50 60 70;
- 設定後の BRAM はピンが減っています
5-3-3. IP を配線する
- 次の配線を行います:
- axi_gpio_0.gpio_io_i -- bram_loader_0.data
- axi_gpio_0.gpio_io_o -- bram_loader_0.req_addr
- blk_mem_gen_0.addra -- bram_loader_0.bram_addr
- blk_mem_gen_0.clka -- bram_loader_0.bram_clk
- blk_mem_gen_0.douta -- bram_loader_0.bram_data
- Run Connection Automation (チェックボックスを全て選択) と、Run Block Automation を実行します。
こんな感じになりました。
5-4. 後仕上げ
5-4-1. HDL Wrapper を生成する
手順 1-5. HDL Wrapper を生成する と同様の手順です
5-4-2. 生成する
手順 1-7. 生成する と同様の手順です
こんな実装になりました。
5-5. PYNQ で実行する
5-5-1. ファイルのアップロード
- 手順 1-8-1. ファイルのアップロード を参考に PYNQ へアップロードします
- 例えばこんなコマンドになります
scp ~/vivado/asobu/asobu05/asobu05.runs/impl_1/design_1_wrapper.bit xilinx@192.168.2.99:pynq/overlays/asobu05/asobu05.bit
scp ~/vivado/asobu/asobu05/asobu05.srcs/sources_1/bd/design_1/hw_handoff/design_1.hwh xilinx@192.168.2.99:pynq/overlays/asobu05/asobu05.hwh
5-5-2. jupyter で実行
- 手順 1-8-2. jupyter で実行 を参考に実行します
asobu05.ipynb
import pynq
fpga = pynq.Overlay('asobu05.bit')
gpio = pynq.MMIO(fpga.ip_dict['axi_gpio_0']['phys_addr'], length=0x1000)
# 読み込んでみます。
gpio.read()
# >> 0
# 初期値は 0 でした。
# req_addr に 1 を入れてみます。
gpio.write(offset=0, data=1)
gpio.read()
# >> 10
# アドレス 1 番に格納した 10 が取り出せました。
# req_addr に 5 を入れてみます。
gpio.write(offset=0, data=5)
gpio.read()
# >> 50
# アドレス 5 番に格納した 50 が取り出せました。