軟件版本:vitis2020.2(vivado2020.2)
操作系統:WIN10 64bit
硬件平臺:適用XILINX A7/K7/Z7/ZU/KU系列FPGA(本文開發板採用米聯客(milianke)MZU07A-EG)
登錄“米聯客”FPGA社區-www.uisrc.com視頻課程、答疑解惑!
2.1概述
使用XILINX 的軟件工具VIVADO以及XILINX的7代以上的FPGA或者SOC掌握AXI-4總線結束,並且可以靈活使用AXI-4總線技術完成數據的交換,可以讓我們在構建強大的FPGA內部總線數據互聯通信方面取得高效、高速、標準化的優勢。
關於AXI4總線協議的部分介紹請閱讀“01AXI4總線axi-lite-slave”。
本文實驗目的:
1:掌握基於VIVADO工具產生AXI協議模板
2:掌握通過VIVADO工具產生AXI-lite-master代碼
3:理解AXI-lite-master中自定義寄存器的地址分配
4:掌握通過VIVADO封裝AXI-lite-master圖形化IP
5:通過仿真驗證AXI-lite-master IP的工作是否正常。
2.2創建axi4-lite-master總線接口IP
新建fpga工程,過程省略
新建完成工程後,單擊菜單欄Tools->Create and Package New IP,開始創建一個AXI4-Lite接口總線IP
選擇使用vivado自帶的AXI總線模板創建一個AXI4-Lite接口IP
設置IP的名字爲maxi_lite
模板支持3中協議,分別是AXI4-Full AXI4-Lite AXI4-Stream
總線包括Master和Slave兩種模式,這裏選擇Slave模式
這裏選擇Verify Peripheral IP using AXI4 VIP 可以對AXI4-Lite快速驗證
單擊Finish 後展開VIVADO自動產生的demo,單擊Block Design的工程,可以看到如下2個IP。其中maxi_lite_0就是我們自定義的IP,另外一個slave_0是用來驗證maxi_lite_0正確性。
採用默認地址分配即可
繼續站看代碼看看裏面有什麼東西
路徑uisrc/03_ip/ maxi_lite_1.0/hdl路徑下的maxi_lite_v1_0_M00_AXI.v就是我們的源碼。另外一個maxi_lite_v1_0.v是軟件產生了一個接口文件,如果我們自己定義IP可有可無。
2.3程序分析
axi總線信號的關鍵無非是地址和數據,而寫地址的有效取決於AXI_AWVALID和AXI_AWREADY,寫數據的有效取決於S_AXI_WVALID和S_AXI_WREADY。同理,讀地址的有效取決於AXI_ARVALID和AXI_ARREADY,讀數據的有效取決於S_AXI_RVALID和S_AXI_RREADY。所以以下代碼的閱讀分析注意也是圍繞以上4個信號的有效時序。
以下程序我們把關鍵信號的代碼拆分閱讀
1:產生初始化信號
always @(posedge M_AXI_ACLK) begin // Initiates AXI transaction delay if (M_AXI_ARESETN == 0 ) begin init_txn_ff <= 1'b0; init_txn_ff2 <= 1'b0; end else begin init_txn_ff <= INIT_AXI_TXN; init_txn_ff2 <= init_txn_ff; end end |
2:axi-lite-master的axi_awvalid
當start_single_write有效,開始一次寫傳輸,設置axi_awvalid有效。
always @(posedge M_AXI_ACLK) begin //Only VALID signals must be deasserted during reset per AXI spec //Consider inverting then registering active-low reset for higher fmax if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awvalid <= 1'b0; end //Signal a new address/data command is available by user logic else begin if (start_single_write) begin axi_awvalid <= 1'b1; end //Address accepted by interconnect/slave (issue of M_AXI_AWREADY by slave) else if (M_AXI_AWREADY && axi_awvalid) begin axi_awvalid <= 1'b0; end end end |
3:axi-lite-slave的axi_awaddr
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awaddr <= 0; end // Signals a new write address/ write data is // available by user logic else if (M_AXI_AWREADY && axi_awvalid) begin axi_awaddr <= axi_awaddr + 32'h00000004;
end end |
4:axi-lite-master的axi_wvalid
當M_AXI_WREADY && axi_wvalid同時有效的時候,數據纔是有效的,對於axi-lite_master接口,M_AXI_WREADY && axi_wvalid同時有效的時間窗口是一個時鐘週期。
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_wvalid <= 1'b0; end //Signal a new address/data command is available by user logic else if (start_single_write) begin axi_wvalid <= 1'b1; end //Data accepted by interconnect/slave (issue of M_AXI_WREADY by slave) else if (M_AXI_WREADY && axi_wvalid) begin axi_wvalid <= 1'b0; end end |
5:axi-lite-master的axi_wdata
產生寫測試用的測試數據
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_wdata <= C_M_START_DATA_VALUE; end // Signals a new write address/ write data is // available by user logic else if (M_AXI_WREADY && axi_wvalid) begin axi_wdata <= C_M_START_DATA_VALUE + write_index; end end |
6:寫次數記錄write_index計數器
這個demo中以start_single_wirte信號作爲統計的,如果我們完全自定axi-lite_master代碼可以自行優化,可以寫出更好的代碼。我們這裏是用vivado模板產生的代碼主要線教會大家如何使用現有的手段和軟件工具學習axi4總線。
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin write_index <= 0; end // Signals a new write address/ write data is // available by user logic else if (start_single_write) begin write_index <= write_index + 1; end end |
7:axi-lite-master的axi_bready
當收到寫通道的axi-lite-slave發回的M_AXI_BVALDI應答信號,設置axi_bready爲1,BRESP返回AXI寫操作是否有錯誤。
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_bready <= 1'b0; end // accept/acknowledge bresp with axi_bready by the master // when M_AXI_BVALID is asserted by slave else if (M_AXI_BVALID && ~axi_bready) begin axi_bready <= 1'b1; end // deassert after one clock cycle else if (axi_bready) begin axi_bready <= 1'b0; end // retain the previous value else axi_bready <= axi_bready; end
//Flag write errors assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]); |
8:axi-lite-slave的axi_arvalid
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_arvalid <= 1'b0; end //Signal a new read address command is available by user logic else if (start_single_read) begin axi_arvalid <= 1'b1; end //RAddress accepted by interconnect/slave (issue of M_AXI_ARREADY by slave) else if (M_AXI_ARREADY && axi_arvalid) begin axi_arvalid <= 1'b0; end // retain the previous value end |
9:axi-lite-slave的axi_araddr
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_araddr <= 0; end // Signals a new write address/ write data is // available by user logic else if (M_AXI_ARREADY && axi_arvalid) begin axi_araddr <= axi_araddr + 32'h00000004; end end |
10:axi-lite-master的axi_rready
當M_AXI_RVALID && axi_rready同時有效的時候,數據纔是有效的,對於axi-lite_master接口,M_AXI_RVALID && ~axi_rready==1的時候設置axi_rready=1,當axi_rready==1,再設置axi_rready=0
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_rready <= 1'b0; end // accept/acknowledge rdata/rresp with axi_rready by the master // when M_AXI_RVALID is asserted by slave else if (M_AXI_RVALID && ~axi_rready) begin axi_rready <= 1'b1; end // deassert after one clock cycle else if (axi_rready) begin axi_rready <= 1'b0; end // retain the previous value end
//Flag write errors assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]); |
11:axi-lite-master的M_AXI_RDATA
當M_AXI_RVALID && axi_rready都有效的時候,對讀取的M_AXI_RDATA數據和expected_rdata數據進行比較。
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) read_mismatch <= 1'b0;
//The read data when available (on axi_rready) is compared with the expected data else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata)) read_mismatch <= 1'b1; else read_mismatch <= read_mismatch; end |
12:產生對比數據expected_rdata
數據expected_rdata用於和讀出的M_AXI_RDATA進行對比以此驗證數據的正確性。
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin expected_rdata <= C_M_START_DATA_VALUE; end // Signals a new write address/ write data is // available by user logic else if (M_AXI_RVALID && axi_rready) begin expected_rdata <= C_M_START_DATA_VALUE + read_index; end end |
13:讀次數記錄read_index計數器
這個demo中以start_single_read信號作爲統計的,如果我們完全自定axi-lite_master代碼可以自行優化,可以寫出更好的代碼。我們這裏是用vivado模板產生的代碼主要線教會大家如何使用現有的手段和軟件工具學習axi4總線。
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin read_index <= 0; end // Signals a new read address is // available by user logic else if (start_single_read) begin read_index <= read_index + 1; end end |
14:axi-lite-master的狀態機
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin expected_rdata <= C_M_START_DATA_VALUE; end // Signals a new write address/ write data is // available by user logic else if (M_AXI_RVALID && axi_rready) begin expected_rdata <= C_M_START_DATA_VALUE + read_index; end end //implement master command interface state machine always @ ( posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 1'b0) begin // reset condition // All the signals are assigned default values under reset condition mst_exec_state <= IDLE; start_single_write <= 1'b0; write_issued <= 1'b0; start_single_read <= 1'b0; read_issued <= 1'b0; compare_done <= 1'b0; ERROR <= 1'b0; end else begin // state transition case (mst_exec_state)
IDLE: // This state is responsible to initiate // AXI transaction when init_txn_pulse is asserted if ( init_txn_pulse == 1'b1 ) begin mst_exec_state <= INIT_WRITE; ERROR <= 1'b0; compare_done <= 1'b0; end else begin mst_exec_state <= IDLE; end
INIT_WRITE: // This state is responsible to issue start_single_write pulse to // initiate a write transaction. Write transactions will be // issued until last_write signal is asserted. // write controller if (writes_done) begin mst_exec_state <= INIT_READ;// end else begin mst_exec_state <= INIT_WRITE;
if (~axi_awvalid && ~axi_wvalid && ~M_AXI_BVALID && ~last_write && ~start_single_write && ~write_issued) begin start_single_write <= 1'b1; write_issued <= 1'b1; end else if (axi_bready) begin write_issued <= 1'b0; end else begin start_single_write <= 1'b0; //Negate to generate a pulse end end
INIT_READ: // This state is responsible to issue start_single_read pulse to // initiate a read transaction. Read transactions will be // issued until last_read signal is asserted. // read controller if (reads_done) begin mst_exec_state <= INIT_COMPARE; end else begin mst_exec_state <= INIT_READ;
if (~axi_arvalid && ~M_AXI_RVALID && ~last_read && ~start_single_read && ~read_issued) begin start_single_read <= 1'b1; read_issued <= 1'b1; end else if (axi_rready) begin read_issued <= 1'b0; end else begin start_single_read <= 1'b0; //Negate to generate a pulse end end
INIT_COMPARE: begin // This state is responsible to issue the state of comparison // of written data with the read data. If no error flags are set, // compare_done signal will be asseted to indicate success. ERROR <= error_reg; mst_exec_state <= IDLE; compare_done <= 1'b1; end default : begin mst_exec_state <= IDLE; end endcase end end //MASTER_EXECUTION_PROC |
整理成流程圖,更加容易理解:
15:最後一個寫數據
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) last_write <= 1'b0;
//The last write should be associated with a write address ready response else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY) last_write <= 1'b1; else last_write <= last_write; end
//Check for last write completion.
//This logic is to qualify the last write count with the final write //response. This demonstrates how to confirm that a write has been //committed.
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) writes_done <= 1'b0;
//The writes_done should be associated with a bready response else if (last_write && M_AXI_BVALID && axi_bready) writes_done <= 1'b1; else writes_done <= writes_done; end |
16:最後一個讀數據
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) last_read <= 1'b0;
//The last read should be associated with a read address ready response else if ((read_index == C_M_TRANSACTIONS_NUM) && (M_AXI_ARREADY) ) last_read <= 1'b1; else last_read <= last_read; end
/* Check for last read completion.
This logic is to qualify the last read count with the final read response/data. */ always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) reads_done <= 1'b0;
//The reads_done should be associated with a read ready response else if (last_read && M_AXI_RVALID && axi_rready) reads_done <= 1'b1; else reads_done <= reads_done; end |
2.4實驗結果
具體的仿真方法省略,以下是仿真結果。