04AXI4匯流排axi-full-master(AXI4匯流排篇)
軟體版本:vitis2020.2(vivado2020.2)
作業系統:WIN10 64bit
硬體平臺:適用XILINX A7/K7/Z7/ZU/KU系列FPGA(本文使用米聯客(milianke)MZU07A-EG開發板)
登入“米聯客”FPGA社群-www.uisrc.com影片課程、答疑解惑!
4.1概述
使用XILINX 的軟體工具VIVADO以及XILINX的7代以上的FPGA或者SOC掌握AXI-4匯流排結束,並且可以靈活使用AXI-4匯流排技術完成資料的交換,可以讓我們在構建強大的FPGA內部匯流排資料互聯通訊方面取得高效、高速、標準化的優勢。
關於AXI4匯流排協議的部分介紹請閱讀“01AXI4匯流排axi-lite-slave”。
本文實驗目的:
1:掌握基於VIVADO工具產生AXI協議模板
2:掌握通過VIVADO工具產生AXI-full-master程式碼
3:理解AXI-full-master中自定義暫存器的地址分配
4:掌握通過VIVADO封裝AXI-full-slave圖形化IP
5:通過模擬驗證AXI-full-master IP的工作是否正常。
4.2建立axi4-full-master匯流排介面IP
新建fpga工程,過程省略
新建完成工程後,單擊選單欄Tools->Create and Package New IP,開始建立一個AXI4-Full介面匯流排IP
選擇使用vivado自帶的AXI匯流排模板建立一個AXI4-FULL介面IP
設定IP的名字為maxi_full
模板支援3中協議,分別是AXI4-Full AXI4-Lite AXI4-Stream,這選擇Full;匯流排包括Master和Slave兩種模式,這裡選擇Master模式
這裡選擇Verify Peripheral IP using AXI4 VIP 可以對AXI4-Lite快速驗證
單擊Finish 後展開VIVADO自動產生的demo,單擊Block Design的工程,可以看到如下2個IP。其中maxi_full_0就是我們自定義的IP,另外一個slave_0是用來驗證maxi_full_0正確性。
採用預設地址分配即可
繼續站看程式碼看看裡面有什麼東西
路徑uisrc/03_ip/ maxi_full_1.0/hdl路徑下的maxi_full_v1_0_M00_AXI.v就是我們的原始碼。另外一個maxi_full_v1_0.v是軟體產生了一個介面檔案,如果我們自己定義IP可有可無。
4.3程式分析
axi匯流排訊號的關鍵無非是地址和資料,而寫地址的有效取決於AXI_AWVALID和AXI_AWREADY,寫資料的有效取決於S_AXI_WVALID和S_AXI_WREADY。同理,讀地址的有效取決於AXI_ARVALID和AXI_ARREADY,讀資料的有效取決於S_AXI_RVALID和S_AXI_RREADY。所以以下程式碼的閱讀分析注意也是圍繞以上4個訊號的有效時序。
以下程式我們把關鍵訊號的程式碼拆分閱讀
1:產生初始化訊號
//Generate a pulse to initiate AXI transaction. 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-full-master的axi_awvalid
當(~axi_awvalid && start_single_burst_write)==1條件滿足,開始一次寫傳輸,設定axi_awvalid有效。
always @(posedge M_AXI_ACLK) begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_awvalid <= 1'b0; end // If previously not valid , start next transaction else if (~axi_awvalid && start_single_burst_write) begin axi_awvalid <= 1'b1; end /* Once asserted, VALIDs cannot be deasserted, so axi_awvalid must wait until transaction is accepted */ else if (M_AXI_AWREADY && axi_awvalid) begin axi_awvalid <= 1'b0; end else axi_awvalid <= axi_awvalid; end |
3:axi-full-slave的axi_awaddr
寫通道地址每當M_AXI_AWREADY && axi_awvalid地址加1
// Next address after AWREADY indicates previous address acceptance always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awaddr <= 'b0; end else if (M_AXI_AWREADY && axi_awvalid) begin axi_awaddr <= axi_awaddr + burst_size_bytes; end else axi_awaddr <= axi_awaddr; end |
4:axi-full-master的axi_wvalid
設定axi_wvalid <= 1'b1開始寫資料。wnext訊號有效程式碼axi_full_master寫資料有效。
assign wnext = M_AXI_WREADY & axi_wvalid;
// WVALID logic, similar to the axi_awvalid always block above always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_wvalid <= 1'b0; end // If previously not valid, start next transaction else if (~axi_wvalid && start_single_burst_write) begin axi_wvalid <= 1'b1; end /* If WREADY and too many writes, throttle WVALID Once asserted, VALIDs cannot be deasserted, so WVALID must wait until burst is complete with WLAST */ else if (wnext && axi_wlast) axi_wvalid <= 1'b0; else axi_wvalid <= axi_wvalid; end |
5:axi-full-master的axi_master_last
axi_master_last訊號,當條件滿足(((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))==1的時候,axi_wlast <= 1'b1。這是VIVADO自帶的模板,但是這裡有個bug,那就是必須確保slave可以連續接收資料,假設傳送last的時候wnext==0,這樣就不能把最後一個數據正確寫入到slave中了。
//WLAST generation on the MSB of a counter underflow // WVALID logic, similar to the axi_awvalid always block above always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_wlast <= 1'b0; end // axi_wlast is asserted when the write index // count reaches the penultimate count to synchronize // with the last write data when write_index is b1111 // else if (&(write_index[C_TRANSACTIONS_NUM-1:1])&& ~write_index[0] && wnext) else if (((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 )) begin axi_wlast <= 1'b1; end // Deassrt axi_wlast when the last write data has been // accepted by the slave with a valid response else if (wnext) axi_wlast <= 1'b0; else if (axi_wlast && C_M_AXI_BURST_LEN == 1) axi_wlast <= 1'b0; else axi_wlast <= axi_wlast; end |
刪除以上程式碼,並且新增以下程式碼:
wire wlast = (write_index == C_M_AXI_BURST_LEN-1) && wnext; reg wlast_r1 = 1'b0; always @(posedge M_AXI_ACLK) wlast_r <= wlast;
assign axi_wlast = (wlast_r==1'b0)&&(wlast==1'b1); |
另外需要修改reg axi_wlast;為wire axi_wlast;
這樣就可以確保傳送wlast的時候資料肯定是有效的。
6:寫次數記錄write_index計數器
/* Burst length counter. Uses extra counter register bit to indicate terminal count to reduce decode logic */ always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_write == 1'b1) begin write_index <= 0; end else if (wnext && (write_index != C_M_AXI_BURST_LEN-1)) begin write_index <= write_index + 1; end else write_index <= write_index; end |
7:axi-full-master的axi_wdata
axi_full_master寫資料計數寫資料
/* Write Data Generator Data pattern is only a simple incrementing count from 0 for each burst */ always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) axi_wdata <= 'b1; //else if (wnext && axi_wlast) // axi_wdata <= 'b0; else if (wnext) axi_wdata <= axi_wdata + 1; else axi_wdata <= axi_wdata; end |
8:axi-full-master的axi_bready
設定axi_bready訊號
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 |
9:axi-full-slave的axi_arvalid
Axi_full_master讀通道的分析非常類似,程式碼是對稱的。
當(~axi_arvalid && start_single_burst_read)==1條件滿足,開始一次寫傳輸,設定axi_arvalid有效。
always @(posedge M_AXI_ACLK) begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_arvalid <= 1'b0; end // If previously not valid , start next transaction else if (~axi_arvalid && start_single_burst_read) begin axi_arvalid <= 1'b1; end else if (M_AXI_ARREADY && axi_arvalid) begin axi_arvalid <= 1'b0; end else axi_arvalid <= axi_arvalid; end
|
10:axi-full-slave的axi_araddr
讀地址計算
// Next address after ARREADY indicates previous address acceptance always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_araddr <= 'b0; end else if (M_AXI_ARREADY && axi_arvalid) begin axi_araddr <= axi_araddr + burst_size_bytes; end else axi_araddr <= axi_araddr; end |
11:axi-full-master的axi_rready
讀資料準備好
/* The Read Data channel returns the results of the read request In this example the data checker is always able to accept more data, so no need to throttle the RREADY signal */ 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) begin if (M_AXI_RLAST && axi_rready) begin axi_rready <= 1'b0; end else begin axi_rready <= 1'b1; end end // retain the previous value end |
12:讀次數記錄read_index計數器
讀資料索引計數
assign rnext = M_AXI_RVALID && axi_rready; // Burst length counter. Uses extra counter register bit to indicate // terminal count to reduce decode logic always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_read) begin read_index <= 0; end else if (rnext && (read_index != C_M_AXI_BURST_LEN-1)) begin read_index <= read_index + 1; end else read_index <= read_index; end |
13:產生對比資料expected_rdata
資料expected_rdata用於和讀出的M_AXI_RDATA進行對比以此驗證資料的正確性。
//Generate expected read data to check against actual read data always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)// || M_AXI_RLAST) expected_rdata <= 'b1; else if (M_AXI_RVALID && axi_rready) expected_rdata <= expected_rdata + 1; else expected_rdata <= expected_rdata; end |
14:比對資料正確性
讀寫資料比較
//Check received read data against data generator always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin read_mismatch <= 1'b0; end //Only check data when RVALID is active else if (rnext && (M_AXI_RDATA != expected_rdata)) begin read_mismatch <= 1'b1; end else read_mismatch <= 1'b0; end |
15:讀寫狀態機
讀寫狀態機原始碼
//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_burst_write <= 1'b0; start_single_burst_read <= 1'b0; compare_done <= 1'b0; ERROR <= 1'b0; end else begin // state transition case (mst_exec_state) IDLE: // This state is responsible to wait for user defined C_M_START_COUNT // number of clock cycles. 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 burst_write_active 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 && ~start_single_burst_write && ~burst_write_active) begin start_single_burst_write <= 1'b1; end else begin start_single_burst_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 burst_read_active 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 && ~burst_read_active && ~start_single_burst_read) begin start_single_burst_read <= 1'b1; end else begin start_single_burst_read <= 1'b0; //Negate to generate a pulse end end INIT_COMPARE: // 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. //if (~error_reg) begin 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 |
整理成流程圖,更加容易理解:
16:正在寫burst_write_active
burst_write_active代表正在寫操作
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) burst_write_active <= 1'b0;
//The burst_write_active is asserted when a write burst transaction is initiated else if (start_single_burst_write) burst_write_active <= 1'b1; else if (M_AXI_BVALID && axi_bready) burst_write_active <= 0; end |
17:寫完成writes_done
寫資料完成writes_done訊號
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 (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast) else if (M_AXI_BVALID && (write_burst_counter[C_NO_BURSTS_REQ]) && axi_bready) writes_done <= 1'b1; else writes_done <= writes_done; end |
18:正在讀burst_read_active
讀burst_read_active代表正在讀資料
// burst_read_active signal is asserted when there is a burst write transaction // is initiated by the assertion of start_single_burst_write. start_single_burst_read // signal remains asserted until the burst read is accepted by the master always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) burst_read_active <= 1'b0;
//The burst_write_active is asserted when a write burst transaction is initiated else if (start_single_burst_read) burst_read_active <= 1'b1; else if (M_AXI_RVALID && axi_rready && M_AXI_RLAST) burst_read_active <= 0; end |
19:讀完成reads_done
讀資料完成reads_done訊號
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 rready response //else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast) else if (M_AXI_RVALID && axi_rready && (read_index == C_M_AXI_BURST_LEN-1) && (read_burst_counter[C_NO_BURSTS_REQ])) reads_done <= 1'b1; else reads_done <= reads_done; end |
20:IP的更新
由於修改了程式碼,需要先更新IP狀態,完成IP更新
4.4實驗結果
模擬結果
一次axi4寫操作burst lenth=16如下圖所示,由於WREADY訊號不是連續的,所以可以傳輸效率不是最高的
一共進行64次burst共計寫了1024個32bit資料
一次讀操作的burst lenth也是16如下圖,但是可以看到讀資料時連續的,所以效率最高
一共進行64次burst共計讀了1024個32bit資料
可以看到讀出的資料RDATA和expected_rdata一致,所以程式碼正確。