HDL/VivadoでAXIバスを利用 のバックアップソース(No.2)

更新

[[公開メモ]]

#contents

* プロジェクトを作成 [#j4df389e]

&ref(new-project.png,,50%);

[Create New Project] からプロジェクトを作成します。

&ref(project-name.png,,50%);

testing_axi4_lite という名前にしました。

&ref(rtl-project.png,,50%);

RTL Project として、

&ref(main-sv.png,,50%);

[Create File] から "main.sv" を追加します。

[Add Existing IP]、[Add Constraint] は飛ばして、

&ref(zynq-7020.png,,50%);

Zynq 7020 を選択しました。

&ref(project-summary.png,,50%);

[Finish] でプロジェクトが生成されます。

&ref(no-ports.png,,50%);

ここではモジュール main にはポートを定義しません。

* AXI4 Lite スレーブとなる IP を作成 [#cc35574c]

&ref(create-ip-menu.png,,50%);

[Tools]-[Create and Package IP...] を選択

&ref(create-ip.png,,50%);

AXI4 ペリフェラル専用の Wizard があると書かれています。

&ref(create-axi4-peripheral.png,,50%);

当然そちらを選択して Next

&ref(test_lite_slave.png,,50%);

"test_lite_slave" という IP を作成します。

&ref(lite_slave_config.png,,50%);

AXI4 Lite Slave として設定可能なレジスタを4つ持つ IP を作成します。

&ref(lite_slave_creation.png,,50%);

AXI4 BFM Simulation はライセンスを別途購入しないと使えません。

[Edit IP] とすることで IP 用のプロジェクトが別途で作成されます。

&ref(lite_slave_project.png,,50%);

このような IP 用のプロジェクトでは、
[Flow Navigator] の [Project Manager] から [Package IP] を選択することで
右側にいろいろな設定項目が現れます。

設定を変更したら、最後に [Review and Package] を押すと、IP が更新されます。

* AXI4 Lite マスターとなる IP を作成する [#oc593fbf]

上と同様にして、"test_lite_master" という IP を作成しました。

[Flow Navigator]-[Project Manager] で [IP Catalog] を選ぶと、

&ref(lite_sm_generated.png,,50%);

2つの IP が追加されていることを確認できます。

* GUI を使って配置する [#n430d13d]

[Flow Navigator]-[IP Integrator]-[Create Block Design] から、

&ref(create_block_design.png,,50%);

design_1 という Design を作成します。

&ref(add-lite-ips.png,,50%);

[Add IP] から2つの IP を選んで Enter を押すと、

&ref(lite-ips-placed.png,,50%);

並びが逆な感じですが、2つの IP が配置されました。

S_AXI から M_AXI までマウスカーソルをドラッグすると、
2つのポートの間を配線できます。

Ctrl+K で aclk, arstn, init を入力ポートとして、
done を出力ポートとして作成し、それぞれ適切に配線します。

&ref(design-routed.png);

ごちゃごちゃしているので、
[Regenerate Layout] したところ、

&ref(layout-regenerated.png);

見やすく配置されました。

* "Address block is not mapped" というエラー [#ebb49d4a]

ところが、これで [Validate Design (F6)] したところ、

 CRITICAL WARNING: [BD 41-1356] Address block </test_lite_slave_0/S_AXI/S_AXI_reg> 
 is not mapped into </test_lite_master_0/M_AXI>. Please use Address Editor to either 
 map or exclude it.

というエラーが出てしまいました。

言われたとおり、Address Editor でアドレスを割り当てます。

&ref(assign-address.png,,50%);

右クリックから [Assign Address] を呼び、Range を最小の 4k にしました。

&ref(address-assigned.png,,50%);

Diagram に戻り、 F6 したところ、

&ref(validation-successful.png,,50%);

うまくいきました。

* Verilog ソースとの統合 [#i7215ace]

[Flow navigator]-[IP Integrator]-[Generate Block Design] から、

&ref(generate-block-design.png,,50%);

[Out of context per Block Design] を選ぶと、

&ref(verilog-source-generated.png,,50%);

design_1.v が生成されます。

中身は、

design_1.v
 LANGUAGE:verilog
 module design_1
    (aclk,
     arstn,
     done,
     init);
   input aclk;
   input arstn;
   output done;
   input init;
 ...

のようになっているので、これを main.v でインスタンス化することで利用できます。

* シミュレーションしてみる [#o8d662e3]

[Flow Navigator]-[Project Manager]-[Add Sources] から

&ref(add-simulation-source.png,,50%);

[Add or create simulation sources] を選んで、

&ref(create-design_1_test.png,,50%);

"design_1_test.sv" を作成します。

中身は次のようにしました。

design_1_test.sv
 LANGUAGE:verilog
 `timescale 1ns / 1ps
 
 module design_1_test();
 
   reg aclk = 0;
   reg arstn = 0;
   wire done;
   reg init = 0;
 
   design_1 uut (.*);
 
   always #10
     aclk <= !aclk;
   
   initial begin
     repeat(10) @(posedge aclk);
     arstn <= 1;
 
     repeat(10) @(posedge aclk);
     init <= 1;
     @(posedge aclk);
     init <= 0;
 
     @(posedge done);
     @(posedge aclk);
     $stop;
   end
 endmodule

このファイルをトップレベルに指定して、

&ref(set-as-top.png,,50%);

[Run Simulation] すると、

&ref(axi4-lite-simulated.png);

正しくシミュレーションできていることが分かります。

* 自動生成される IP の中身 [#h0e78358]

** AXI4 Lite Slave [#w41173ad]

4つのレジスタ slv_reg1, slv_reg2, slv_reg3, slv_reg4 を含んでいて、
AXI4 Lite インタフェースを用いて内容を読み書きできます。

これらのレジスタの値を既存の IP で読み取れば、
既存 IP への設定値を AXI4 バス経由で書き込めることになります。

レジスタの値の代わりに既存 IP からの出力を繋げば、
既存 IP の状態を AXI4 バス経由で読み出せることになります。

** AXI4 Lite Master [#s82d83fb]

AXI4 バス経由で、Slave のレジスタに順に値を書き込むコードが生成されています。

書き込み手順を変えれば任意の IP と繋げられるはずです。

* AXI4 Lite Master を読む [#za72c469]

シミュレーション時に使いたいのでちゃんと読んでみる

 LANGUAGE:verilog(linenumber)
 `timescale 1 ns / 1 ps


** メインのステートマシン [#a85b463d]
:IDLE|init_txn_pulse で INIT_WRITE へ
:INIT_WRITE|writes_done で INIT_READ へ
:INIT_READ|reads_done で INIT_COMPARE へ
:INIT_COMPARE|無条件に IDLE へ

という簡単なもの

** INIT_WRITE の動作 [#lbcf8890]

*** メインロジック [#t562517a]

 LANGUAGE:verilog
 if (writes_done) begin
   mst_exec_state <= INIT_READ;
 end else
 if (~axi_awvalid && ~axi_wvalid && ~M_AXI_BVALID  && 
         ~start_single_write && ~write_issued && 
         ~last_write) begin
   start_single_write <= 1;
   write_issued  <= 1;
 end else 
 if (axi_bready) begin
   write_issued  <= 0;
 end else begin
   start_single_write <= 0;
 end

- axi_bready が立てば次のクロックで write_issued が降りる
- axi_bready が立っていなければ次のクロックで start_single_write が降りる
- 通常は start_single_write が立った次のクロックで axi_bready が立つことは無いので、
start_single_write のパルスは必ず1クロック幅になる

write_issued はここでしか参照されておらず、
start_single_write を立てた後、
まだ axi_bready が検出されていないことを表すフラグになっている。

last_write は続きの書き込み要求があるかどうかを表すフラグ。

- アドレス線 = AWVALID を立てて AWREADY を待つ
- データ線 = WVALID を立てて WREADY を待つ
- 書き込み完了 = M_AXI_BVALID が立ったら BREADY を返す

と言う動作なので、上記の大きな if は
- アドレス線出力中でなく
- データ選出力中でなく
- 書き込み完了待ちでなく
- 書き込み開始フラグが立っておらず
- 引き続きの書き込み要求がない

という条件を表している。

*** アドレス線 [#icb83e02]

awvalid を立てて awready を待つ。

awready が立ったら4増やす。

 LANGUAGE:verilog
  // axi_awvalid の設定
  // 有効な書き込みアドレスが出力されていることを示す
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1) begin
      axi_awvalid <= 0;
    end else 
    if (start_single_write) begin
      axi_awvalid <= 1;
    end else 
    if (axi_awvalid && M_AXI_AWREADY) begin
      axi_awvalid <= 0;
    end
  end
  
  // 書き込みアドレス
  assign M_AXI_AWADDR   = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr;
  
  // axi_awaddr の設定
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0  || init_txn_pulse == 1) begin
      axi_awaddr <= 0;
    end else 
    if (axi_awvalid && M_AXI_AWREADY) begin
      axi_awaddr <= axi_awaddr + 32'h00000004;
    end
  end

*** データ線 [#p0fb2a6a]

WVALID を立てて WREADY を待つ

 LANGUAGE:verilog
  // axi_wvalid の設定
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0  || init_txn_pulse == 1) begin
      axi_wvalid <= 0;
    end else 
    if (start_single_write) begin
      axi_wvalid <= 1;
    end else 
    if (axi_wvalid && M_AXI_WREADY) begin
      axi_wvalid <= 0;
    end
  end
  
  // axi_wdata の設定
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1 ) begin
      axi_wdata <= C_M_START_DATA_VALUE;
    end else
    if (M_AXI_WREADY && axi_wvalid) begin
      axi_wdata <= C_M_START_DATA_VALUE + write_index;
    end
  end

*** 書き込み完了 [#f996255d]

 LANGUAGE:verilog
  // axi_bready の設定
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1) begin
      axi_bready <= 0;
    end else 
    if (~axi_bready && M_AXI_BVALID) begin
      axi_bready <= 1;
    end else begin
      axi_bready <= 0;
    end
  end
  
  // Flag write errors
  assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]);

*** 書き込みデータ数のカウント [#v349921b]

 LANGUAGE:verilog
  // write_index の設定
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1) begin
      write_index <= 0;
    end else 
    if (start_single_write) begin
      write_index <= write_index + 1;
    end
  end
  
  // last_write の設定
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1) begin
      last_write <= 0;
    end else 
    if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY) begin
      last_write <= 1;
    end
  end
  
  // writes_done の設定
  // 書き込み完了は M_AXI_BVALID && axi_bready で判定する
  always @(posedge M_AXI_ACLK) begin
    if (M_AXI_ARESETN == 0 || init_txn_pulse == 1) begin
      writes_done <= 0;
    end else 
    if (last_write && M_AXI_BVALID && axi_bready) begin
      writes_done <= 1;
    end
  end

*** 改造指針 [#kaf0a56c]

last_write に write_command_empty を割り当てる

アドレスとデータを適切に切り替える

とすれば良さそう。

** INIT_READ の動作 [#u9217895]

*** メインロジック [#i99b9e66]
 LANGUAGE:verilog
      INIT_READ:
        if (reads_done) begin
          mst_exec_state <= INIT_COMPARE;
        end else
        if (~axi_arvalid && ~M_AXI_RVALID &&
                ~start_single_read && ~read_issued &&
                ~last_read) begin
          start_single_read <= 1;
          read_issued  <= 1;
        end else 
        if (axi_rready) begin
          read_issued  <= 0;
        end else begin
          start_single_read <= 0;
        end

*** [#d9c4db99]

* コメント・質問 [#r43d5138]

#article_kcaptcha

Counter: 105110 (from 2010/06/03), today: 9, yesterday: 0