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


これは何か

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

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

関連記事:

5-1. プロジェクトの作成

5-2. 自作モジュールの作成

今回は、次の構成をゴールにします。

  • BRAM を ROM として設置
  • 自作モジュールで、
    • GPIO から、要求するアドレスを受け取り、
    • BRAM-ROM から要求アドレスのデータを取り出し、
    • GPIO へ返す

5-2-1. 自作モジュールを編集する

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 を配置する

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. ファイルのアップロード

scp ~/vivado/asobu/asobu05/asobu05.runs/impl_1/design_1_wrapper.bit [email protected]:pynq/overlays/asobu05/asobu05.bit
scp ~/vivado/asobu/asobu05/asobu05.srcs/sources_1/bd/design_1/hw_handoff/design_1.hwh [email protected]:pynq/overlays/asobu05/asobu05.hwh

5-5-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 が取り出せました。