一、AXI 相關知識介紹
https://download.csdn.net/download/mvpkuku/90841873?AXI_LITE
?選出部分重點,詳細文檔見上面鏈接。
1.AXI4 協議類型
2.握手機制?
?二、AXI_LITE 協議的實現?
?1. AXI_LITE 通道及各通道端口功能介紹
2.實現思路及框架?
2.1 總體框架?
2.2 寫通道?
1.復位信號進行跨時鐘域同步(用戶時鐘/AXI4時鐘)
2.僅當user_wr_ready為高時,用戶的寫通道是有效的
3.例化兩個fifo分別存儲用戶端傳來的地址以及數據用戶時鐘寫入,并通過AXI時鐘讀出
4.控制AXI_LITE(狀態機)接口信號是在AXI時鐘下進行 (控制cmd_rden/data_rden并輸出對應的相關接口數據)
`timescale 1ns / 1ps
//
// Description: AXI_lite寫通道
//
module axlite_wr_channel#(parameter USER_WR_DATA_WIDTH = 32 ,//用戶寫數據位寬和AXI4—Lite數據位寬保持一致parameter AXI_DATA_WIDTH = 32, //AXI4_LITE總線規定,數據位寬只支持32Bit或者64bitparameter AXI_ADDR_WIDTH = 32
)(input wire clk, //用戶寫時鐘 input wire axi_clk, //從機讀時鐘 input wire reset,//與 用戶端 交互信號input wire user_wr_en,input wire [USER_WR_DATA_WIDTH-1:0] user_wr_data,input wire [AXI_ADDR_WIDTH-1 :0] user_wr_addr,output wire user_wr_ready, //與 axi_lite從機 交互信號output reg [AXI_ADDR_WIDTH -1:0] m_axi_awaddr, //write addr channeloutput wire [2:0] m_axi_awprot, output reg m_axi_awvalid, input wire m_axi_awready,output reg [AXI_DATA_WIDTH-1:0] m_axi_wdata, //write data channeloutput wire [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb,output reg m_axi_wvalid,input wire m_axi_wready,input wire [1:0] m_axi_bresp, //wirte response channelinput wire m_axi_bvalid,output wire m_axi_bready
);(* dont_touch = "true"*) reg reset_sync_d0; //user clk
(* dont_touch = "true"*) reg reset_sync_d1;
(* dont_touch = "true"*) reg reset_sync;
(* dont_touch = "true"*) reg a_reset_sync_d0; //axi clk
(* dont_touch = "true"*) reg a_reset_sync_d1;
(* dont_touch = "true"*) reg a_reset_sync;reg [31:0] cmd_din;
reg cmd_wren;
wire [31:0] cmd_dout;
reg cmd_rden;
wire cmd_wrfull ;
wire cmd_rdempty;
wire [4:0] cmd_wrcount;
wire [4:0] cmd_rdcount;reg [31:0] data_din;
reg data_wren;
wire [31:0] data_dout;
reg data_rden;
wire data_wrfull;
wire data_rdempty;
wire [4:0] data_wrcount;
wire [4:0] data_rdcount;reg [2 : 0] cur_status;
reg [2 : 0] nxt_status;localparam WR_IDLE = 3'b000;
localparam WR_PRE = 3'b001;
localparam WR_DATA_EN = 3'b010;
localparam WR_END = 3'b100;
/*--------------------------------------------------*\assign
\*--------------------------------------------------*/
assign m_axi_bready = 1'b1;
assign m_axi_awprot = 0;
assign m_axi_wstrb = {AXI_DATA_WIDTH/8{1'b1}};assign user_wr_ready = reset_sync ? 1'b0 : cmd_wrcount <= 'd12 ; //留一點余量 //當user_wr_ready為低的時候,用戶發送寫是無效的
/*--------------------------------------------------*\CDC process
\*--------------------------------------------------*/
always @(posedge clk) beginreset_sync_d0 <= reset;reset_sync_d1 <= reset_sync_d0;reset_sync <= reset_sync_d1;
endalways @(posedge axi_clk) begina_reset_sync_d0 <= reset;a_reset_sync_d1 <= a_reset_sync_d0;a_reset_sync <= a_reset_sync_d1;
end/*--------------------------------------------------*\wirte addr to cmd fifo、write data to data fifo
\*--------------------------------------------------*/
always @(posedge clk) beginif (user_wr_ready) begincmd_wren <= user_wr_en;cmd_din <= user_wr_addr;data_wren <= user_wr_en;data_din <= user_wr_data; endelse begincmd_wren <= 0;cmd_din <= 0;data_wren <= 0;data_din <= 0; end
end/*--------------------------------------------------*\WR state machine (三段式)
\*--------------------------------------------------*/
always@(posedge axi_clk)beginif(a_reset_sync)cur_status <= WR_IDLE;elsecur_status <= nxt_status;
endalways@(*)beginif(a_reset_sync)beginnxt_status <= WR_IDLE;endelse begincase(cur_status)WR_IDLE : beginif(!cmd_rdempty)nxt_status <= WR_PRE;elsenxt_status <= cur_status;endWR_PRE : beginnxt_status <= WR_DATA_EN;endWR_DATA_EN : beginif (m_axi_bvalid && m_axi_bready)nxt_status <= WR_END;else nxt_status <= cur_status;end WR_END : beginnxt_status <= WR_IDLE;enddefault : nxt_status <= WR_IDLE;endcaseend
end
/*-----------------------------------------------------------*\read addr from cmd_fifo 、 read data from data_fifo
\*-----------------------------------------------------------*/
always @(*) beginif (a_reset_sync) begincmd_rden <= 0;data_rden <= 0;endelse begincmd_rden <= cur_status == WR_PRE;data_rden <= cur_status == WR_PRE;end
endalways @(posedge axi_clk) beginif (cmd_rden) m_axi_awaddr <= cmd_dout;else m_axi_awaddr <= m_axi_awaddr;
endalways @(posedge axi_clk) beginif (a_reset_sync) m_axi_awvalid <= 0;else if (cur_status == WR_PRE)m_axi_awvalid <= 1'b1;else if (m_axi_awvalid && m_axi_awready)m_axi_awvalid <= 0;
endalways @(posedge axi_clk) beginif (data_rden) m_axi_wdata <= data_dout;else m_axi_wdata <= m_axi_wdata;
endalways @(posedge axi_clk) beginif (a_reset_sync) m_axi_wvalid <= 0;else if (cur_status == WR_PRE)m_axi_wvalid <= 1'b1;else if (m_axi_wvalid && m_axi_wready)m_axi_wvalid <= 0;
end//寫地址fifo
fifo_w32xd16 wr_cmd_fifo (.rst ( reset_sync ), // input wire rst.wr_clk ( clk ), // input wire wr_clk 用戶寫時鐘.din ( cmd_din ), // input wire [31 : 0] din.wr_en ( cmd_wren ), // input wire wr_en.rd_clk ( axi_clk ), // input wire rd_clk 從機讀時鐘 .rd_en ( cmd_rden ), // input wire rd_en.dout ( cmd_dout ), // output wire [31 : 0] dout.full ( cmd_wrfull ), // output wire full.empty ( cmd_rdempty ), // output wire empty.rd_data_count( cmd_wrcount ), // output wire [4 : 0] rd_data_count.wr_data_count( cmd_rdcount ) // output wire [4 : 0] wr_data_count
);//寫數據fifo
fifo_w32xd16 wr_data_fifo (.rst ( reset_sync ), // input wire rst.wr_clk ( clk ), // input wire wr_clk 用戶寫時鐘.din ( data_din ), // input wire [31 : 0] din.wr_en ( data_wren ), // input wire wr_en.rd_clk ( axi_clk ), // input wire rd_clk 從機讀時鐘 .rd_en ( data_rden ), // input wire rd_en.dout ( data_dout ), // output wire [31 : 0] dout.full ( data_wrfull ), // output wire full.empty ( data_rdempty ), // output wire empty.rd_data_count( data_rdcount ), // output wire [4 : 0] rd_data_count.wr_data_count( data_wrcount ) // output wire [4 : 0] wr_data_count
);
endmodule
2.3 讀通道?
讀通道的實現分兩步,用戶端發出讀請求并給讀的地址,然后從機根據地址發出數據,用戶讀出。
因此讀地址fifo的邏輯部分與寫通道一致,只有狀態機跳轉的RD_DATA_EN的條件根據模塊端口有所改變,但是讀數據fifo,是在AXI_CLK的時鐘域下,端口輸入有效及端口輸入數據,根據非空開始用戶讀,然后賦給模塊用戶端口
`timescale 1ns / 1ps
//
// Description: AXI_LITE讀通道
//
module axilite_rd_channel#(parameter USER_RD_DATA_WIDTH = 32 , //用戶讀數據位寬和AXI4—Lite數據位寬保持一致parameter AXI_DATA_WIDTH = 32, parameter AXI_ADDR_WIDTH = 32
)(input wire clk, input wire axi_clk, input wire reset,//用戶端讀請求,讀地址信號input wire user_rd_en,input wire [AXI_ADDR_WIDTH-1 :0] user_rd_addr,output wire user_rd_ready,output reg [USER_RD_DATA_WIDTH-1:0] user_rd_data,output reg user_rd_valid,//與AXI_LITE從機 交互信號output reg m_axi_arvalid, // axi read address channelinput wire m_axi_arready, output reg [AXI_ADDR_WIDTH-1:0] m_axi_araddr,output wire [2:0] m_axi_arprot, input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, // axi read data channelinput wire [1:0] m_axi_resp,input wire m_axi_rvalid,output wire m_axi_rready
);(* dont_touch = "true"*) reg reset_sync_d0; //user clk
(* dont_touch = "true"*) reg reset_sync_d1;
(* dont_touch = "true"*) reg reset_sync;
(* dont_touch = "true"*) reg a_reset_sync_d0; //axi clk
(* dont_touch = "true"*) reg a_reset_sync_d1;
(* dont_touch = "true"*) reg a_reset_sync;reg [31:0] cmd_din;
reg cmd_wren;
wire [31:0] cmd_dout;
reg cmd_rden;
wire cmd_wrfull;
wire cmd_rdempty;
wire [4:0] cmd_wrcount;
wire [4:0] cmd_rdcount;reg [31:0] data_din;
reg data_wren;
wire [31:0] data_dout;
wire data_rden;
wire data_wrfull;
wire data_rdempty;
wire [4:0] data_wrcount;
wire [4:0] data_rdcount;reg [2 : 0] cur_status;
reg [2 : 0] nxt_status;localparam RD_IDLE = 3'b000;
localparam RD_PRE = 3'b001;
localparam RD_DATA_EN = 3'b010;
localparam RD_END = 3'b100;
/*--------------------------------------------------*\assign
\*--------------------------------------------------*/
assign user_rd_ready = reset_sync ? 1'b0 : cmd_wrcount <= 'd12 ;
assign m_axi_rready = 1'b1;
assign m_axi_arprot = 0;
/*--------------------------------------------------*\CDC process
\*--------------------------------------------------*/
always @(posedge clk) beginreset_sync_d0 <= reset;reset_sync_d1 <= reset_sync_d0;reset_sync <= reset_sync_d1;
endalways @(posedge axi_clk) begina_reset_sync_d0 <= reset;a_reset_sync_d1 <= a_reset_sync_d0;a_reset_sync <= a_reset_sync_d1;
end/*--------------------------------------------------*\wirte addr to cmd fifo
\*--------------------------------------------------*/
always @(posedge clk) beginif (user_rd_ready) begincmd_wren <= user_rd_en;cmd_din <= user_rd_addr; endelse begincmd_wren <= 0;cmd_din <= 0; end
end/*--------------------------------------------------*\RD state machine
\*--------------------------------------------------*/
always @(posedge axi_clk) beginif (a_reset_sync) begincur_status <= RD_IDLE;endelse begincur_status <= nxt_status;end
endalways @(*) beginif (a_reset_sync) beginnxt_status <= RD_IDLE; endelse begincase(cur_status)RD_IDLE : beginif (~cmd_rdempty)nxt_status <= RD_PRE;else nxt_status <= cur_status;endRD_PRE : beginnxt_status <= RD_DATA_EN;endRD_DATA_EN : beginif (m_axi_rvalid && m_axi_rready)nxt_status <= RD_END;else nxt_status <= cur_status;endRD_END : beginnxt_status <= RD_IDLE;enddefault : nxt_status <= RD_IDLE;endcase end
end/*-----------------------------------------------------------*\read addr from cmd_fifo
\*-----------------------------------------------------------*/
always @(*) beginif (a_reset_sync) cmd_rden <= 0;else cmd_rden <= cur_status == RD_PRE;
endalways @(posedge axi_clk) beginif (cmd_rden) m_axi_araddr <= cmd_dout;else m_axi_araddr <= m_axi_araddr;
endalways @(posedge axi_clk) beginif (a_reset_sync) m_axi_arvalid <= 0;else if (cur_status == RD_PRE)m_axi_arvalid <= 1'b1;else if (m_axi_arvalid && m_axi_arready)m_axi_arvalid <= 0;
end/*-----------------------------------------------------------*\read user data from data fifo
\*-----------------------------------------------------------*/
always @(posedge axi_clk) begindata_din <= m_axi_rdata;data_wren <= m_axi_rvalid;
end assign data_rden = reset_sync ? 1'b0 : ~data_rdempty;always @(posedge clk) beginuser_rd_valid <= data_rden;user_rd_data <= data_dout;
end//讀地址fifo
fifo_w32xd16 rd_cmd_fifo (.rst ( reset_sync ), // input wire rst.wr_clk ( clk ), // input wire wr_clk 用戶寫時鐘.din ( cmd_din ), // input wire [31 : 0] din.wr_en ( cmd_wren ), // input wire wr_en.rd_clk ( axi_clk ), // input wire rd_clk 從機讀時鐘 .rd_en ( cmd_rden ), // input wire rd_en.dout ( cmd_dout ), // output wire [31 : 0] dout.full ( cmd_wrfull ), // output wire full.empty ( cmd_rdempty ), // output wire empty.rd_data_count( cmd_wrcount ), // output wire [4 : 0] rd_data_count.wr_data_count( cmd_rdcount ) // output wire [4 : 0] wr_data_count
);//讀數據fifo
fifo_w32xd16 rd_data_fifo (.rst ( a_reset_sync ), // input wire rst.wr_clk ( axi_clk ), // input wire wr_clk 從機寫時鐘.din ( data_din ), // input wire [31 : 0] din.wr_en ( data_wren ), // input wire wr_en.rd_clk ( clk ), // input wire rd_clk 用戶讀時鐘 .rd_en ( data_rden ), // input wire rd_en.dout ( data_dout ), // output wire [31 : 0] dout.full ( data_wrfull ), // output wire full.empty ( data_rdempty ), // output wire empty.rd_data_count( data_rdcount ), // output wire [4 : 0] rd_data_count.wr_data_count( data_wrcount ) // output wire [4 : 0] wr_data_count
);
endmodule
3.仿真?
通過調用AXI_LITE接口的BRAM IP核,實/復位結束等一會兒進入寫數據狀態,讀寫數據(寫10個數據,讀10個數據),數據一致累加,地址也不斷累加,地址最大為1024。
?仿真結果如下,可以看出讀寫地址和數據完全一樣,說明AXI_LITE接口代碼實現無誤。
?
需要工程請私信