input wire s00_axi_aclk, input wire s00_axi_aresetn, input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr, input wire [2 : 0] s00_axi_awprot, input wire s00_axi_awvalid, output wire s00_axi_awready, input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata, input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb, input wire s00_axi_wvalid, output wire s00_axi_wready, output wire [1 : 0] s00_axi_bresp, output wire s00_axi_bvalid, input wire s00_axi_bready, input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr, input wire [2 : 0] s00_axi_arprot, input wire s00_axi_arvalid, output wire s00_axi_arready, output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata, output wire [1 : 0] s00_axi_rresp, output wire s00_axi_rvalid, input wire s00_axi_rready
沒錯筆者曾在《AXI總線概述》這節中提到了他們,此次經過源碼分析再次隆重介紹它們。 html
always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin slv_reg0 <= 0; slv_reg1 <= 0; slv_reg2 <= 0; slv_reg3 <= 0; end else begin if (slv_reg_wren) begin case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 0 slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 2'h1: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 1 slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 2'h2: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 2 slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 2'h3: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 3 slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end default : begin slv_reg0 <= slv_reg0; slv_reg1 <= slv_reg1; slv_reg2 <= slv_reg2; slv_reg3 <= slv_reg3; end endcase end end end
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end
其中,C_S_AXI_DATA_WIDTH的宏定義的值爲32,也就是數據位寬,S_AXI_WSTRB就是寫選通訊號,S_AXI_WDATA就是寫數據信號。緩存
存在於for循環中的最關鍵的一句:架構
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];app
當byte_index = 0的時候這句話就等價於:函數
slv_reg0[7:0] <= S_AXI_WDATA[7:0];源碼分析
當byte_index = 1的時候這句話就等價於:性能
slv_reg0[15:8] <= S_AXI_WDATA[15:8];學習
當byte_index = 2的時候這句話就等價於:測試
slv_reg0[23:16] <= S_AXI_WDATA[23:16];ui
當byte_index = 3的時候這句話就等價於:
slv_reg0[31:24] <= S_AXI_WDATA[31:24];
也就是說,只有當寫選通訊號爲1時,它所對應S_AXI_WDATA的字節纔會被讀取。
讀懂了這段話以後,咱們就知道了,若是咱們想獲得PS寫到總線上的數據,咱們只須要讀取slv_reg0的值便可。
那若是,咱們想寫數據到總線讓PS讀取該數據,咱們該怎麼作呢?咱們繼續來看有關RADTA讀數據代碼:
// Output register or memory read data always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin axi_rdata <= 0; end else begin // When there is a valid read address (S_AXI_ARVALID) with // acceptance of read address by the slave (axi_arready), // output the read dada if (slv_reg_rden) begin axi_rdata <= reg_data_out; // register read data end end end
觀察可知,當PS讀取數據時,程序會把reg_data_out複製給axi_rdata(RADTA讀數據)。咱們繼續追蹤reg_data_out:
always @(*) begin // Address decoding for reading registers case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0 : reg_data_out <= slv_reg0; 2'h1 : reg_data_out <= slv_reg1; 2'h2 : reg_data_out <= slv_reg2; 2'h3 : reg_data_out <= slv_reg3; default : reg_data_out <= 0; endcase end
和前面分析的同樣此時經過判斷axi_awaddr[3:2]的值來判斷將那個值給reg_data_out上,一樣當PS調用讀取函數時,這裏axi_awaddr[3:2]默認是0,因此咱們只須要把slv_reg0替換成咱們本身數據,就可讓PS經過總線讀到咱們提供的數據。
這裏可能有的讀者會問了,slv_reg0不是總線寫過來的數據嗎?由於筆者說過這個程序是Vivado爲咱們提供的例子,它這麼作無非是想驗證我寫出去的值和我讀進入的值相等。可是他怎麼寫確實會對初看代碼的人形成困擾。
最後筆者提出一個問題,爲何寫通道要比讀通道多了一列應答通道,這是爲何呢?
首先,你要知道這個應答信號是幹什麼用的?
寫應答,主要是回覆主機你這個寫過程是沒有問題的,那讀爲何不須要這個過程呢?
這時由於主機在讀取數據時,從機能夠直接經過讀數據通道給主機反饋信息,所以就沒有必要再來開闢一個單獨的應答通道了。
小結:
若是咱們想讀AXI4_Lite總線上的數據時,只需關注slv_reg的數據,咱們可自行添加一段代碼,如:
reg [11:0]rlcd_rgb; always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin rlcd_rgb <= 12'd0; end else begin rlcd_rgb <= slv_reg0[11:0]; end end assign lcd_rgb = rlcd_rgb;
若是咱們想對AXI4_Lite信號寫數據時,咱們只需修改對reg_data_out的賦值,如:
//寫總線測試修改!!!!!!!!! wire[31:0]wlcd_xy;// = {10'd0,lcd_xy}; assign wlcd_xy = {10'd0,lcd_xy}; assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid; always @(*) begin // Address decoding for reading registers case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0 : reg_data_out <= wlcd_xy;//slv_reg0; 2'h1 : reg_data_out <= slv_reg1; 2'h2 : reg_data_out <= slv_reg2; 2'h3 : reg_data_out <= slv_reg3; default : reg_data_out <= 0; endcase end
最後強調下若是咱們自定義的IP的地址被映射爲0x43C00000,那麼咱們Xil_Out32(0x43C00000,Value)寫的就是slv_reg0的值。若是地址偏移4位,如Xil_Out32(0x43C00000 + 4,Value) 寫的就是slv_reg1的值,依次類推。
目前這裏只有4個寄存器,那是由於以前選擇的是4個,其實咱們能夠定義的更多:
#define XPAR_ MYIPFREQUENCY_ 0_ S00_ AXI_ BASEADDR 0x43C00000 #define XPAR_ MYIPFREQUENCY_ 0_ S00_ AXI_ HIGHADDR 0x43C0FFFF
理論上只要基地址 + 偏移量不要超過HIGHADDR便可。
Step9:接下來依然是,右鍵單擊Block文件,文件選擇Generate the Output Products。
Step10:繼續右鍵單擊Block文件,選擇Create a HDL wrapper,根據Block文件內容產生一個HDL 的頂層文件,並選擇讓vivado自動完成。
Setp11:單擊Run Synthesis,若是有 Save 對話框彈出選擇保存。
Setp12:綜合結束後選擇Synthesized Design option單擊 OK。
Step13:在以下對話框中找到Unassigned debug nets(若是對話框沒有出現選擇 菜單->Window > Debug)
Step14:右擊 Unassigned Debug Nets 選擇Set up Debug… 以後單擊 Next
Step15:刪除紅色錯誤的信號而後單擊Next 到結束