HDL/VivadoでAXIバスを利用 のバックアップソース(No.3)
更新- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- バックアップ を表示
- 電気回路/HDL/VivadoでAXIバスを利用 へ行く。
[[公開メモ]] #contents * AXI バス [#zf8590a4] Xilinx の資料によれば、 > AXI は、[[AMBA (Advanced Microcontroller Bus Architecture) 4 仕様>http://www.amba.com/]] > に基づいて標準化 された IP インターフェイスプロトコルです。 とのことで、例えば Zynq に内蔵された ARM プロセッサとユーザーロジックの間などが AXI バスで繋がれていますので、IP を自作したならば AXI バスに繋がなければ、 CPU から利用できません。 でも逆に、一旦 IP を AXI バス互換にしてしまえば Vivado 上の GUI を用いて 容易に接続を行えるなど、利点も多いようです。 * Vivado の IP ひな形生成機構を利用する [#u6d63940] Xilinx の AXI バスに関するちゃんとしたリファレンスはこのあたりになります。 (英語版は v14.3、日本語版は v13.4) - http://japan.xilinx.com/support/documentation/ip_documentation/axi_ref_guide/v13_4/j_ug761_axi_reference_guide.pdf - http://japan.xilinx.com/support/documentation/ip_documentation/ug761_axi_reference_guide.pdf でも、これらを端から端まで読んで仕様をコードに落とし込むのは片手間ではできない感じです。 より手っ取り早く理解するには Vivado を使って IP のひな形を作成するのが良いようです。 以下、その手順を追ってみます。 * プロジェクトを作成 [#j4df389e] IP を利用するプロジェクトを作成します。 &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" という名前にしました。 &ref(lite_slave_config.png,,50%); 設定・読み出し可能なレジスタを4つ持つ AXI4 Lite Slave IP を作成します。 IP のひな形の他、IP を ARM から使うためのドライバのひな形まで生成してくれます。 &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] を選択することで 右側にいろいろな設定項目が現れます。 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; ... のように、Ctrl+K で作成した aclk, arstn, init, done などのポートが見えています。 これを 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 からの出力を書き出すようにしたりすれば、 既存 IP を AXI4 Lite バスに接続することができます。 ** AXI4 Lite Master [#s82d83fb] AXI4 バス経由で、Slave のレジスタに順に値を書き込むコードが生成されています。 書き込み手順を変えれば任意の IP と繋げられるはずですが、 ちょっと複雑なので理解するのに時間が掛かります。。。 * AXI4 Lite Master を読む [#za72c469] シミュレーション時に AXI4 Lite バスから IP にアクセスしたいので、 AXI4 Lite Master の動作を理解するためソースをしっかり読んでみようと思います。 ** メインのステートマシン [#a85b463d] :IDLE|init_txn_pulse で INIT_WRITE へ :INIT_WRITE|writes_done で INIT_READ へ :INIT_READ|reads_done で INIT_COMPARE へ :INIT_COMPARE|無条件に IDLE へ という簡単なものでした。 INIT_WRITE フェーズで書き込んだデータと同じ値が INIT_READ フェーズで読み出せることを確認するようになっています。 ** INIT_WRITE の動作 [#lbcf8890] READ の動作も WRITE の動作と似ているので、 WRITE が理解できればほぼ全てを理解できるようでした。 *** メインロジック [#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 * シミュレーション用の Master を作成する [#h72e26ea] 上記と同様の手順で AXI4_Lite_Master_BFM を作成しました。 ** 書き込みを行うタスク [#md21ae06] LANG:verilog task write(addr, data); input [C_M_AXI_ADDR_WIDTH-1:0] addr; input [C_M_AXI_DATA_WIDTH-1:0] data; begin reg error; M_AXI_AWVALID = 0; M_AXI_WVALID = 0; M_AXI_BREADY = 0; @(posedge M_AXI_ACLK) #1 fork begin // アドレスを出力し AWVALID を立てて AWREADY を待つ M_AXI_AWADDR = addr; M_AXI_AWVALID = 1; while(!M_AXI_AWREADY) @(posedge M_AXI_ACLK) #1; @(posedge M_AXI_ACLK) #1; M_AXI_AWVALID = 0; end begin // データを出力し WVALID を立てて WREADY を待つ M_AXI_WDATA = data; M_AXI_WVALID = 1; while(!M_AXI_WREADY) @(posedge M_AXI_ACLK) #1; @(posedge M_AXI_ACLK) #1; M_AXI_WVALID = 0; end begin // BVALID が立ったら BREADY を返しエラーを読む while(!M_AXI_BVALID) @(posedge M_AXI_ACLK) #1; @(posedge M_AXI_ACLK) #1; M_AXI_BREADY = 1; error = M_AXI_BRESP[1]; @(posedge M_AXI_ACLK) #1; M_AXI_BREADY = 0; end join // エラーがなければ 1 を返す write = !error; end 動作未確認。 *** 読み出しを行うタスク [#u1e7645b] LANG:verilog task [C_M_AXI_DATA_WIDTH-1:0] read(addr); input [C_M_AXI_ADDR_WIDTH-1:0] addr; begin reg error; M_AXI_ARVALID = 0; M_AXI_RREADY = 0; @(posedge M_AXI_ACLK) #1 fork begin // アドレスを出力し ARVALID を立てて ARREADY を待つ M_AXI_ARADDR = addr; M_AXI_ARVALID = 1; while(!M_AXI_ARREADY) @(posedge M_AXI_ACLK) #1; @(posedge M_AXI_ACLK) #1; M_AXI_ARVALID = 0; end begin // RVALID が立ったら RREADY を返しエラーとデータを読む while(!M_AXI_RVALID) @(posedge M_AXI_ACLK) #1; @(posedge M_AXI_ACLK) #1; M_AXI_RREADY = 1; error = M_AXI_RRESP[1]; if(!error) read = M_AXI_RDATA; @(posedge M_AXI_ACLK) #1; M_AXI_RREADY = 0; end join end 動作未確認。 * コメント・質問 [#r43d5138] #article_kcaptcha
Counter: 104367 (from 2010/06/03),
today: 27,
yesterday: 0