带你快速入门AXI4总线--AXI4-Lite篇(3)----XILINX AXI4-Lite接口IP源码仿真分析(Master接口)
寫在前面
? ? ? ? 在帶你快速入門AXI4總線--AXI4-Lite篇(2)----XILINX AXI4-Lite接口IP源碼仿真分析(Slave接口)中我們已經對Slave接口的代碼做了分析,并觀察了其仿真波形,在本文我們將生成AXI4-Lite_Master接口的IP來對其解析。
1、調用IP
? ? ? ? 具體步驟不講,請參看Slave接口的文章,只需要將IP的接口類型改為Master即可,其他一致。
2、Slave接口的源碼分析
????????打開上節生成的源碼(注意:我刪除了源碼的注釋,不然太長了。再優化了一下格式,主要是對齊。順便再吐槽一下CSDN不能折疊代碼):
? ? ? ? 代碼較長,我將其分成NO.1-13共13個部分來進行講解。只講大體思路,其他內容請看代碼注釋。
NO.1:
`timescale 1 ns / 1 ps //NO.1--------------------------------輸入輸出端口-------------------------------------------module myip_axi_lite_master_v1_0_M00_AXI #(parameter C_M_START_DATA_VALUE = 32'hAA000000, //初始寫入數據的值parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000, //寫入地址的基地址parameter integer C_M_AXI_ADDR_WIDTH = 32, //地址位寬parameter integer C_M_AXI_DATA_WIDTH = 32, //數據位寬parameter integer C_M_TRANSACTIONS_NUM = 4 //傳輸的最大事務個數)( //工具人型信號 input wire INIT_AXI_TXN, //傳輸開始信號output reg ERROR, //錯誤output wire TXN_DONE, //傳輸完成 //全局信號 input wire M_AXI_ACLK,input wire M_AXI_ARESETN, //寫地址通道信號 output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,output wire [2 : 0] M_AXI_AWPROT,output wire M_AXI_AWVALID,input wire M_AXI_AWREADY, //寫數據通道信號 output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA,output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,output wire M_AXI_WVALID,input wire M_AXI_WREADY, //寫響應通道信號 input wire [1 : 0] M_AXI_BRESP,input wire M_AXI_BVALID,output wire M_AXI_BREADY, //讀地址通道信號 output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR,output wire [2 : 0] M_AXI_ARPROT,output wire M_AXI_ARVALID,input wire M_AXI_ARREADY, //讀數據通道信號 input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA,input wire [1 : 0] M_AXI_RRESP,input wire M_AXI_RVALID,output wire M_AXI_RREADY);? ? ? ? 這部分主要是模塊端口及參數例化。? ? ? ? ? ? ? ?
????????????????參數例化:初始寫入數據的值;寫入地址的基地址;數據位寬32位;地址位寬4位;傳輸的最大事務個數
? ? ? ? ? ? ? ? 模塊端口:
????????????????????????AXI4-Lite協議的端口。不記得可以看這里:帶你快速入門AXI4總線--AXI4-Lite篇(1)----AXI4-Lite總線
? ? ? ? ? ? ? ? ? ? ? ? 其他工具人型信號
NO.2:
//NO.2--------------------------------以2為底取對數的function------------------------------------------- //以2為底取對數function integer clogb2 (input integer bit_depth);beginfor(clogb2=0; bit_depth>0; clogb2=clogb2+1)bit_depth = bit_depth >> 1;endendfunctionlocalparam integer TRANS_NUM_BITS = clogb2(C_M_TRANSACTIONS_NUM-1); //計算最大傳輸事務計數的位寬? ? ? ? 這部分主要是寫了一個求2為底的對數的FUNCTION,這個function一般用來計算變量的位寬;計算了傳輸事務最大個數的位寬。
NO.3:
//NO.3--------------------------------reg、wire、參數定義-------------------------------------------//狀態機狀態定義parameter [1:0] IDLE = 2'b00, //初始狀態INIT_WRITE = 2'b01, //寫事務狀態INIT_READ = 2'b10, //讀事務狀態INIT_COMPARE = 2'b11; //比較讀、寫結果狀態//axi總線信號reg [1:0] mst_exec_state;reg axi_awvalid;reg axi_wvalid;reg axi_arvalid;reg axi_rready;reg axi_bready;reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;reg [C_M_AXI_DATA_WIDTH-1 : 0] axi_wdata;reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_araddr;wire write_resp_error; //寫響應錯誤wire read_resp_error; //讀響應錯誤reg start_single_write; //單次寫事務觸發信號reg start_single_read; //單次讀事務觸發信號reg write_issued; //拉高表示此時正在發起進行寫事務reg read_issued; //拉高表示此時正在發起進行讀事務reg writes_done; //所有寫事務完成reg reads_done; //所有讀事務完成reg error_reg; //錯誤寄存器,拉高表示此模塊讀寫有誤reg [TRANS_NUM_BITS : 0] write_index; //寫事務計數reg [TRANS_NUM_BITS : 0] read_index; //讀事務計數reg [C_M_AXI_DATA_WIDTH-1 : 0] expected_rdata; //理論上應該讀取的數據(即被寫入的數據)reg compare_done; //比較讀寫結果完成reg read_mismatch; //讀、寫結果不匹配reg last_write; //最后一次寫事務reg last_read; //最后一次讀事務reg init_txn_ff; //傳輸開始信號打1拍reg init_txn_ff2; //傳輸開始信號打2拍reg init_txn_edge; //沒用到的信號wire init_txn_pulse; //傳輸開始信號的上升沿捕獲信號? ? ? ? 這部分定義了一系列的變量,具體作用請看注釋。
NO.4:
//NO.4--------------------------------部分賦值模塊-------------------------------------------assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr; //寫地址 = 基地址 + 偏移地址assign M_AXI_WDATA = axi_wdata;assign M_AXI_AWPROT = 3'b000; //保護類型assign M_AXI_AWVALID = axi_awvalid;assign M_AXI_WVALID = axi_wvalid;assign M_AXI_WSTRB = 4'b1111; //寫入數據全部選中assign M_AXI_BREADY = axi_bready;assign M_AXI_ARADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_araddr; //讀地址 = 基地址 + 偏移地址assign M_AXI_ARVALID = axi_arvalid;assign M_AXI_ARPROT = 3'b001; //保護類型assign M_AXI_RREADY = axi_rready;assign TXN_DONE = compare_done; //比較完成信號賦值給傳輸完成信號? ? ? ? 這部分對部分變量進行了賦值,避免了輸出信號的直接操作。
NO.5:
//NO.5--------------------------------對傳輸開始信號打拍并求其上升沿(異步信號)-------------------------------------------assign init_txn_pulse = (!init_txn_ff2) && init_txn_ff; //求上升沿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? ? ? ? 這部分對INIT_AXI_TXN這個信號進行了上升沿捕捉,這是控制主機模塊開始寫事務的使能信號,由外部傳入,屬于異步信號。
NO.6:
//NO.6--------------------------------狀態機實現流程控制------------------------------------------- always @ ( posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 1'b0) begin 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 case (mst_exec_state) IDLE: //初始狀態 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: //寫傳輸事務狀態 if (writes_done) // 全部4次寫事務完成 begin mst_exec_state <= INIT_READ; //跳轉到讀事務狀態 end else begin mst_exec_state <= INIT_WRITE; //空閑狀態下生成start_single_write、write_issued 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; //寫入響應后表示寫事務可以結束(有1個周期的時延) end else begin start_single_write <= 1'b0; //其他情況不進行寫事務 end end INIT_READ: //讀傳輸事務狀態 if (reads_done) // 全部4次讀事務完成 begin mst_exec_state <= INIT_COMPARE; //跳轉到比較讀寫結果狀態 end else begin mst_exec_state <= INIT_READ; //start_single_read、read_issued 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) //讀取響應后表示讀事務可以結束(有1個周期的時延) begin read_issued <= 1'b0; end else begin start_single_read <= 1'b0; //其他情況不進行讀事務 end end INIT_COMPARE: //比較讀、寫數據結果 beginmst_exec_state <= IDLE; ERROR <= error_reg; compare_done <= 1'b1; end default : begin mst_exec_state <= IDLE; end endcase end end? ? ? ? 這個部分使用一個狀態機實現了整個代碼的流程控制,狀態流程大致如下:
? ? ? ? 整個流程想實現的功能是:首先進入初始狀態,等外部發送可以開始進行傳輸信號后(init_txn_pulse),進入寫事務狀態。在寫事務狀態如果完成了所有的寫事務(4個),則跳轉到讀事務狀態。若沒有完成則繼續保持在該狀態。在讀事務狀態如果完成了所有的讀事務(4個),則跳轉到比較狀態。若沒有完成則繼續保持在該狀態。在比較狀態,對讀、寫數據進行比較,觀察是否由錯誤。
NO.7:
//NO.7--------------------------------寫地址通道------------------------------------------- //寫地址有效 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awvalid <= 1'b0; end else begin if (start_single_write) //單次寫入有效 begin axi_awvalid <= 1'b1; end else if (M_AXI_AWREADY && axi_awvalid) //寫地址通道握手完成 begin axi_awvalid <= 1'b0; end end end //寫次數計數 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin write_index <= 0; end else if (start_single_write) begin write_index <= write_index + 1; end end //寫地址 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awaddr <= 0; end else if (M_AXI_AWREADY && axi_awvalid) begin axi_awaddr <= axi_awaddr + 32'h00000004; end end? ? ? ? 這個部分主要對三個信號賦值:
????????????????寫地址有效信號axi_awvalid:當單次寫事務使能信號有效時,拉高axi_awvalid。當完成握手后,拉低axi_awvalid。
????????????????寫次數計數信號write_index:每當單次寫事務使能信號有效時(表示進行了一次寫事務),其值+1,用來統計寫事務的次數。
????????????????寫地址信號axi_awaddr:每當握手完成后,其值加4。即兩次寫事務的寫入地址相差4
NO.8:
//NO.8--------------------------------寫數據通道------------------------------------------- //寫數據有效always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_wvalid <= 1'b0; end else if (start_single_write) begin axi_wvalid <= 1'b1; end else if (M_AXI_WREADY && axi_wvalid) begin axi_wvalid <= 1'b0; end end //寫數據 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 else if (M_AXI_WREADY && axi_wvalid) begin axi_wdata <= C_M_START_DATA_VALUE + write_index; end end? ? ? ? 這個部分主要對2個信號賦值:
????????????????寫數據有效信號axi_wvalid:當單次寫事務使能信號有效時,拉高axi_wvalid。當完成握手后,拉低axi_wvalid。
????????????????寫數據信號axi_wdata:每當握手完成后,其值在首次寫入值的基礎上 + 當前寫事務的次數。
NO.9:
//NO.9--------------------------------寫響應通道------------------------------------------- //準備接收寫響應always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_bready <= 1'b0; end else if (M_AXI_BVALID && ~axi_bready) begin axi_bready <= 1'b1; end else if (axi_bready) begin axi_bready <= 1'b0; end else axi_bready <= axi_bready; end assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]); //判斷響應是否有效? ? ? ? 這個部分主要對2個信號賦值:
????????????????寫響應準備信號axi_bready:當從機發送的響應有效信號有效時,拉高axi_bready。當完成握手后,拉低axi_bready。
????????????????寫響應錯誤信號write_resp_error:根據響應值對響應結果進行正確性判斷
NO.10:
//讀次數計數 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin read_index <= 0; end else if (start_single_read) begin read_index <= read_index + 1; end end //讀地址有效 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_arvalid <= 1'b0; end else if (start_single_read) begin axi_arvalid <= 1'b1; end else if (M_AXI_ARREADY && axi_arvalid) begin axi_arvalid <= 1'b0; end end //讀地址 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_araddr <= 0; end else if (M_AXI_ARREADY && axi_arvalid) begin axi_araddr <= axi_araddr + 32'h00000004; end end? ? ? ? 這個部分主要對三個信號賦值:
? ? ? ? ? ? ? ? 讀地址有效信號axi_arvalid:當單次讀事務使能信號有效時,拉高axi_arvalid。當完成握手后,拉低axi_arvalid。
????????????????讀次數計數信號read_index:每當單次讀事務使能信號有效時(表示進行了一次讀事務),其值+1,用來統計讀事務的次數。
? ? ? ? ? ? ? ? 讀地址信號axi_araddr:每當握手完成后,其值加4。即兩次讀事務的讀地址相差4(這一點與寫地址一致)
NO.11:
//NO.11--------------------------------讀數據通道(含讀響應)------------------------------------------- //準備讀數據always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_rready <= 1'b0; end else if (M_AXI_RVALID && ~axi_rready) begin axi_rready <= 1'b1; end else if (axi_rready) begin axi_rready <= 1'b0; end end assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]); //判斷響應是否有效 //生成理論上應該讀到的值,后續與實際讀出的值做對比 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 else if (M_AXI_RVALID && axi_rready) begin expected_rdata <= C_M_START_DATA_VALUE + read_index;end end? ? ? ? 這個部分主要對3個信號賦值:
? ? ? ? ? ? ? ? 準備讀數據信號axi_rready:當從機告知可以讀數據時,拉高axi_rready。當完成握手后,拉低axi_rready。
????????????????讀響應錯誤信號read_resp_error:根據響應值對響應結果進行正確性判斷????????????
????????????????理論上應該被讀出的信號expected_rdata:根據之前寫入的信號判斷當前理應被讀出的信號是多少
NO.12:
//NO.12--------------------------------讀、寫事務狀態判斷------------------------------------------- //最后一次寫事務 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) last_write <= 1'b0; else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY) //達到最大寫次數,且第四次寫地址已完成 last_write <= 1'b1; else last_write <= last_write; end //全部寫事務完成 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) writes_done <= 1'b0; else if (last_write && M_AXI_BVALID && axi_bready) writes_done <= 1'b1; else writes_done <= writes_done; end //最后一次讀事務 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) last_read <= 1'b0; else if ((read_index == C_M_TRANSACTIONS_NUM) && (M_AXI_ARREADY) ) last_read <= 1'b1; else last_read <= last_read; end //全部讀事務完成 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) reads_done <= 1'b0; else if (last_read && M_AXI_RVALID && axi_rready) reads_done <= 1'b1; else reads_done <= reads_done; end? ? ? ? 這個部分主要對4個信號賦值:
? ? ? ? ? ? ? ? 最后一次寫事務信號last_write:當開始了最后一次寫事務后,拉高此信號。
? ? ? ? ? ? ? ? 最后一次讀事務信號last_read:當開始了最后一次讀事務后,拉高此信號。
????????????????全部寫事務完成信號writes_done:當完成了所有寫事務后,拉高此信號。????????
????????????????全部讀事務完成信號reads_done:當完成了所有讀事務后,拉高此信號。?
NO.13:
//NO.13--------------------------------錯誤判斷------------------------------------------- //判斷讀出的數據是否與寫入的數據匹配 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) read_mismatch <= 1'b0; else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata)) //讀、寫數據不匹配 read_mismatch <= 1'b1; else read_mismatch <= read_mismatch; end //錯誤判斷的三種類型:1、讀寫不匹配;2、寫響應不正確;3、讀響應不正確 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) error_reg <= 1'b0; else if (read_mismatch || write_resp_error || read_resp_error) error_reg <= 1'b1; else error_reg <= error_reg; end endmodule? ? ? ? 這個部分主要對2個信號賦值:
? ? ? ? ? ? ? ? 判斷讀、寫是否匹配的信號read_mismatch:若讀出的數據與寫入的數據不一致則拉高此信號。
? ? ? ? ? ? ? ? 錯誤error_reg:若存在以下三種錯誤則拉高此信號:
????????????????????????1、讀寫不匹配;
????????????????????????2、寫響應不正確;
????????????????????????3、讀響應不正確?? ?
3、仿真波形
? ? ? ? 代碼分析完了,接下來使用Vivado自帶的仿真器來進行仿真,觀看仿真結果,加深對設計方法的理解:
3.1、AXI4-Lite總線的仿真波形
? ? ? ? 我們先把自動生成的仿真信號刪除,添加如下的波形信號:
? ? ? ? 仿真結果如下:
? ? ? ? 可以看到仿真結果是用這個彩條+字符的形式表示的,非常清晰。這就是添加了AXI VIP IP的效果。
? ? ? ? 在AXI4-Lite總線上共發生了8個事務:先是連續的4個寫事務,接著4個讀事務。下面的五個通道分別示意了此時通道內執行的握手操作,將鼠標放在其中任意一處上,會出現如下信息(順序1、地址40000000等):
?
?????????左鍵點擊,會顯示具體的事務流程如下:?
? ? ? ? ?從上圖的箭頭我們可以直到一次寫事務的流程:寫地址----寫數據----寫響應。再看看讀事務的流程:
????????可以看到讀事務的流程:讀地址----讀數據。
3.2、主機IP的master接口仿真波形
? ? ? ? 看完了AXI4-Lite總線的仿真波形,我們再看下上面具體解析代碼(可以理解為底層驅動)的仿真波形。按如下方法添加:
? ? ? ? 將信號按通道或用途做好分類(我還刪除了一些不要緊的信號),狀態機部分的仿真結果如下:
????????不講解了,直接看圖吧。
????????AXI4-Lite總線部分的仿真結果如下:
? ? ? ? 在上圖中,主機先發起了4次寫事務,分別往地址h0000_0000、h0000_0004、h0000_0000和h0000_000c中寫入了數據aa00_0000、?aa00_0001、aa00_0002和aa00_0003。
? ? ? ? 接著又發起了4次讀事務,分別從之前寫入的地址中讀取到了數據aa00_0000、?aa00_0001、aa00_0002和aa00_0003(這里圖沒截好),與寫入的一致,證明驗證成功。
4、其他
- 可以看到其實AXI4-Lite總線的使用還是相對比較簡單的,只要設計好各個通道的握手時序,以及讀寫的時序關系就好了。
- 創作不易,希望各位大佬多多三連支持!一家之言,如有錯誤還請指正!
版本信息
????????文件:V1.0
? ? ? ? 編號:64
? ? ? ? Vivado:Vivado 2019.2
? ? ? ? Modelsim:無
? ? ? ? Quartus II:無
?
??????????????????
總結
以上是生活随笔為你收集整理的带你快速入门AXI4总线--AXI4-Lite篇(3)----XILINX AXI4-Lite接口IP源码仿真分析(Master接口)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: adb 详细使用文档(ADB命令使用大全
- 下一篇: click option/argumen