軟件版本: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一致,所以代碼正確。