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

更新


公開メモ

プロジェクトを作成

new-project.png

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

project-name.png

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

rtl-project.png

RTL Project として、

main-sv.png

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

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

zynq-7020.png

Zynq 7020 を選択しました。

project-summary.png

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

no-ports.png

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

AXI4 Lite スレーブとなる IP を作成

create-ip-menu.png

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

create-ip.png

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

create-axi4-peripheral.png

当然そちらを選択して Next

test_lite_slave.png

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

lite_slave_config.png

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

lite_slave_creation.png

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

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

lite_slave_project.png

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

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

AXI4 Lite マスターとなる IP を作成する

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

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

lite_sm_generated.png

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

GUI を使って配置する

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

create_block_design.png

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

add-lite-ips.png

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

lite-ips-placed.png

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

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

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

design-routed.png

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

layout-regenerated.png

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

"Address block is not mapped" というエラー

ところが、これで [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 でアドレスを割り当てます。

assign-address.png

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

address-assigned.png

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

validation-successful.png

うまくいきました。

Verilog ソースとの統合

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

generate-block-design.png

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

verilog-source-generated.png

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 でインスタンス化することで利用できます。

シミュレーションしてみる

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

add-simulation-source.png

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

create-design_1_test.png

"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

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

set-as-top.png

[Run Simulation] すると、

axi4-lite-simulated.png

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

自動生成される IP の中身

AXI4 Lite Slave

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

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

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

AXI4 Lite Master

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

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

AXI4 Lite Master を読む

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

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

メインのステートマシン

IDLE
init_txn_pulse で INIT_WRITE へ
INIT_WRITE
writes_done で INIT_READ へ
INIT_READ
reads_done で INIT_COMPARE へ
INIT_COMPARE
無条件に IDLE へ

という簡単なもの

INIT_WRITE の動作

メインロジック

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 は

  • アドレス線出力中でなく
  • データ選出力中でなく
  • 書き込み完了待ちでなく
  • 書き込み開始フラグが立っておらず
  • 引き続きの書き込み要求がない

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

アドレス線

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

データ線

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

書き込み完了

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]);

書き込みデータ数のカウント

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

改造指針

last_write に write_command_empty を割り当てる

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

とすれば良さそう。

INIT_READ の動作

メインロジック

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

コメント・質問





Counter: 105057 (from 2010/06/03), today: 30, yesterday: 0