https://download.csdn.net/download/mvpkuku/90855619
?一、AXI_FULL協議的前提知識
1. 各端口的功能
2. 4K邊界問題?
3. outstanding?
4.時序仿真體驗?
可通過VIVADO自帶ADMA工程觀察仿真波形圖
二、FPGA實現 (主要用于讀寫DDR)
1.功能模塊及框架
將用戶端的寫/讀相關信號轉換為AXI4_MASTER接口信號。因此頂層設計主要分為用戶端接口以及AXI4協議主機接口
1、換存數據量較小的時候,FIFO建議采用DRAM
DRAM是由邏輯資源搭建而成的,消耗的是LUT的資源
緩存命令、地址、幀長度等等。2、緩存數據量比較大的時候,采用BRAM,
BRAM是一種資源。18k = 18 * 1024bit = 36bit(最大寬度) * 512(深度)= 18bit * 1024 = 9bit * 2048
36k = 2 * 18k = 72bit(最大寬度) * 512(深度)= 36bit * 1024 = 18bit * 2048
37bit = 36bit + 36bitFIFO使用了一個18k的bram,即使FIFo的深度和寬度開的很小,很多空間沒有使用,
這個FIFO的消耗的BRAM,不能被其他的模塊(FIFO)使用
FIFO采用BRAM的時候,消耗最少相同的BARM數量,建議把FIFO的深度和寬度開到最大AXI_DATA_WIDTH == 64
64bit + 1bit = 65bit = 36bit + 36bit ?至少2個18k bram
2.寫通道實現?
2.1 wr_ctrl模塊?
寫入用戶端信號(寫使能,寫數據,寫地址)
1.對復位信號以及ddr初始化完成信號進行同步,進而對用戶端信號進行有效寄存
2.對用戶端寫數據進行計數(位寬轉換,突發寫字節數)
3.生成數據及數據有效,寫數據最后一位,寫突發長度
4.生成寫請求和寫地址
ps:3、4輸出信號均給wr_buffer模塊進行跨時鐘與緩存
下一個模塊的cmd_wren在wr_req_en為1時拉高,并且此模塊在此時更新地址
wr_req_en作為wr_buffer狀態機啟動信號
2.2 wr_buffer模塊
1.復位信號進行跨時鐘域同步(用戶時鐘/AXI4時鐘)
2.將地址和length寫入CMD fifo (clk),cmd_wren在wr_req_en為1時拉高,并且此時更新地址
3..將數據和last 寫入data?fifo
寫數據隨著上層數據有效寫入data_fifo
4.狀態機設計(輸出傳給axi4_master模塊)
(axi_aw_req_en和axi_aw_ready)握手機制控制是否開始寫狀態
//表示AXI4寫請求? 本模塊時序控制,狀態機一個狀態產生請求信號,握手成功之后拉低
//axi_aw_req_en 和axi_aw_ready同時為高,開啟一次AXI4寫傳輸
并在該信號跳轉開始寫數據狀態的同時,拉高cmd_rden,讀出數據突發長度以及寫地址
5.輸出信號的時序
突發長度和地址cmd_dout分配
其次是是數據有效同上,已進入寫數據狀態就拉高
數據和last由當data_en為高時由data_dout分配
三個信號同步
2.3 axi_master模塊
1.對除了與下游從機交互的握手信號以及寫地址,寫突發長度的信號進行固定賦值;
2.?axi_aw_req_en啟動狀態機
3.組合邏輯輸出作為叢機給wr_buffer的握手準備信號,一個由狀態機產生,一個由下游從機輸入
4.對和下游從機交互的握手信號以及寫地址,寫突發長度以及數據,數據最后一位的信號 時序控制
2.4 實現頂層代碼?
`timescale 1ns / 1ps
//
// Description: 寫通道
//
module axi_wr_channel#(parameter USER_WR_DATA_WIDTH = 16 ,parameter AXI_DATA_WIDTH = 128 ,parameter AXI_ADDR_WIDTH = 32
)(input user_wr_clk , //用戶端寫時鐘input axi_clk , // AXI4端時鐘input reset ,input ddr_init_done ,/*-------------用戶寫端口信號------------------------*/input user_wr_en ,input [USER_WR_DATA_WIDTH-1:0] user_wr_data ,input [AXI_ADDR_WIDTH-1:0] user_wr_base_addr , //一定要被4096整除input [AXI_ADDR_WIDTH-1:0] user_wr_end_addr , //一定要被4096整除 /*-------------AXI寫通道端口信號---------------------*/output m_axi_awvalid ,input m_axi_awready ,output [AXI_ADDR_WIDTH-1:0] m_axi_awaddr ,output [3:0] m_axi_awid ,output [7:0] m_axi_awlen ,output [1:0] m_axi_awburst ,output [2:0] m_axi_awsize ,output [2:0] m_axi_awport ,output [3:0] m_axi_awqos ,output m_axi_awlock ,output [3:0] m_axi_awcache ,output m_axi_wvalid ,input m_axi_wready ,output [AXI_DATA_WIDTH-1:0] m_axi_wdata ,output [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb , output m_axi_wlast ,input [3:0] m_axi_bid ,input [1:0] m_axi_bresp ,input m_axi_bvalid ,output m_axi_bready
);wire wr_req_en;wire [7:0] wr_burst_length;wire [AXI_ADDR_WIDTH-1:0] wr_data_addr;wire wr_data_valid;wire [AXI_DATA_WIDTH-1:0] wr_data_out;wire wr_data_last;wire axi_aw_req_en;wire axi_aw_ready;wire [7:0] axi_aw_burst_len;wire [AXI_ADDR_WIDTH-1:0] axi_aw_addr;wire axi_w_valid;wire axi_w_ready;wire [AXI_DATA_WIDTH-1:0] axi_w_data;wire axi_w_last;wr_ctrl #(.USER_WR_DATA_WIDTH(USER_WR_DATA_WIDTH),.AXI_DATA_WIDTH(AXI_DATA_WIDTH),.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),.WR_BURST_LENGTH(1024)) wr_ctrl (.clk (user_wr_clk),.reset (reset),.ddr_init_done (ddr_init_done),.user_wr_en (user_wr_en),.user_wr_data (user_wr_data),.user_wr_base_addr (user_wr_base_addr),.user_wr_end_addr (user_wr_end_addr),.wr_req_en (wr_req_en),.wr_burst_length (wr_burst_length),.wr_data_addr (wr_data_addr),.wr_data_valid (wr_data_valid),.wr_data_out (wr_data_out),.wr_data_last (wr_data_last));wr_buffer #(.AXI_DATA_WIDTH(AXI_DATA_WIDTH),.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH)) wr_buffer (.clk (user_wr_clk),.axi_clk (axi_clk),.reset (reset),.wr_req_en (wr_req_en),.wr_burst_length (wr_burst_length),.wr_data_addr (wr_data_addr),.wr_data_valid (wr_data_valid),.wr_data_in (wr_data_out),.wr_data_last (wr_data_last),.axi_aw_req_en (axi_aw_req_en),.axi_aw_ready (axi_aw_ready),.axi_aw_burst_len (axi_aw_burst_len),.axi_aw_addr (axi_aw_addr), .axi_w_valid (axi_w_valid),.axi_w_ready (axi_w_ready),.axi_w_data (axi_w_data),.axi_w_last (axi_w_last));axi_wr_master #(.AXI_DATA_WIDTH(AXI_DATA_WIDTH),.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH)) axi_wr_master (.axi_clk (axi_clk),.reset (reset),.axi_aw_req_en (axi_aw_req_en),.axi_aw_ready (axi_aw_ready),.axi_aw_burst_len (axi_aw_burst_len),.axi_aw_addr (axi_aw_addr),.axi_w_valid (axi_w_valid),.axi_w_ready (axi_w_ready),.axi_w_data (axi_w_data),.axi_w_last (axi_w_last),.m_axi_awvalid (m_axi_awvalid),.m_axi_awready (m_axi_awready),.m_axi_awaddr (m_axi_awaddr),.m_axi_awid (m_axi_awid),.m_axi_awlen (m_axi_awlen),.m_axi_awburst (m_axi_awburst),.m_axi_awsize (m_axi_awsize),.m_axi_awport (m_axi_awport),.m_axi_awqos (m_axi_awqos),.m_axi_awlock (m_axi_awlock),.m_axi_awcache (m_axi_awcache),.m_axi_wvalid (m_axi_wvalid),.m_axi_wready (m_axi_wready),.m_axi_wdata (m_axi_wdata),.m_axi_wstrb (m_axi_wstrb),.m_axi_wlast (m_axi_wlast),.m_axi_bid (m_axi_bid),.m_axi_bresp (m_axi_bresp),.m_axi_bvalid (m_axi_bvalid),.m_axi_bready (m_axi_bready));endmodule
2.5 部分仿真時序圖?
3.讀通道實現?
3.1 rd_ctrl模塊
基本上與wr_ctrl模塊類似,主要注意幾個區別:
1.狀態機由讀請求的上升沿在ddr初始化完成之后啟動;
2.給用戶端讀忙的信號,只要不處于初始態??user_rd_req_busy
3.wr-buffer輸入fifo是否寫滿的信號 rd_req_ready
3.2 rd_buffer模塊?
同理:仍然與wr_buffer對比
1. rd_cmd_fifo?
通過 cmd_wrcount 給出?fifo是否寫滿的信號 rd_req_ready,在這個前提下cmd開始寫入fifo
cmd_rden由下游axi_master的握手信號控制
2. rd_data_fifo
? ??????由下游axi_master 數據寫入 (axi_clk)data_wren/data_din;
????????data_rden 需要data_fifo非空,并且不在data_fifo非讀,一周期拉低
? ? ? ? rd_data_fifo_out :在data_rden一次性讀取位寬128的數據,然后在rd_data_flag進行數據的移位
????????rd_data_fifo_last:通過data_dout[64]判斷是否為1,持續最后一個128位寬數據時間
3. 狀態機起始跳轉需要cmd非空而且data_fifo沒寫滿;
結束跳轉的條件
4.最后將rd_data_fifo_out截取16位給到用戶端口,同步數據有效,rd_data_fifo_last計數最后一個移位數據給到用戶端口
?3.3 axi_rd_master模塊
?m_axi控制拉低,axi_控制拉高
`timescale 1ns / 1ps
//
// Description:讀通道
/
module axi_rd_channel#(parameter AXI_DATA_WIDTH = 128,parameter AXI_ADDR_WIDTH = 32 ,parameter USER_RD_DATA_WIDTH = 16)(input user_rd_clk , //用戶端讀時鐘input axi_clk , //axi的時鐘input reset ,input ddr_init_done ,/*--------------用戶端讀請求信號----------------------*/input user_rd_req , //上升沿觸發用戶讀請求input [AXI_ADDR_WIDTH-1:0] user_rd_base_addr , //一定要被4096整除input [AXI_ADDR_WIDTH-1:0] user_rd_end_addr , //一定要被4096整除output user_rd_req_busy , //用戶讀請求忙標志 output user_rd_valid ,output user_rd_last ,output [USER_RD_DATA_WIDTH-1:0] user_rd_data ,/*-------------AXI讀通道端口信號---------------------*/output m_axi_arvalid ,input m_axi_arready ,output [AXI_ADDR_WIDTH-1:0] m_axi_araddr ,output [3:0] m_axi_arid ,output [7:0] m_axi_arlen ,output [1:0] m_axi_arburst ,output [2:0] m_axi_arsize ,output [2:0] m_axi_arport ,output [3:0] m_axi_arqos ,output m_axi_arlock ,output [3:0] m_axi_arcache ,input [3:0] m_axi_rid ,input m_axi_rvalid ,output m_axi_rready ,input [AXI_DATA_WIDTH-1:0] m_axi_rdata ,input m_axi_rlast ,input [1:0] m_axi_rresp );wire rd_req_en ;wire [7:0] rd_burst_length ;wire [AXI_ADDR_WIDTH-1:0] rd_data_addr ;wire rd_req_ready ;wire axi_ar_req_en ;wire axi_ar_ready ;wire [7:0] axi_ar_burst_len;wire [AXI_ADDR_WIDTH-1:0] axi_ar_addr ;wire axi_r_valid ;wire [AXI_DATA_WIDTH-1:0] axi_r_data ;wire axi_r_last ;rd_ctrl #(.AXI_DATA_WIDTH(AXI_DATA_WIDTH),.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),.RD_BURST_LENGTH(1024)) rd_ctrl (.clk (user_rd_clk),.reset (reset),.ddr_init_done (ddr_init_done),.user_rd_req (user_rd_req),.user_rd_base_addr (user_rd_base_addr),.user_rd_end_addr (user_rd_end_addr),.user_rd_req_busy (user_rd_req_busy),.rd_req_en (rd_req_en),.rd_burst_length (rd_burst_length),.rd_data_addr (rd_data_addr),.rd_req_ready (rd_req_ready));rd_buffer #(.AXI_DATA_WIDTH(AXI_DATA_WIDTH),.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),.USER_RD_DATA_WIDTH(USER_RD_DATA_WIDTH)) rd_buffer (.clk (user_rd_clk),.axi_clk (axi_clk),.reset (reset),.rd_req_en (rd_req_en),.rd_burst_length (rd_burst_length),.rd_data_addr (rd_data_addr),.rd_req_ready (rd_req_ready),.axi_ar_req_en (axi_ar_req_en),.axi_ar_ready (axi_ar_ready),.axi_ar_burst_len (axi_ar_burst_len),.axi_ar_addr (axi_ar_addr),.axi_r_valid (axi_r_valid),.axi_r_data (axi_r_data),.axi_r_last (axi_r_last),.user_rd_valid (user_rd_valid),.user_rd_last (user_rd_last),.user_rd_data (user_rd_data));axi_rd_master #(.AXI_DATA_WIDTH(AXI_DATA_WIDTH),.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH)) axi_rd_master (.axi_clk (axi_clk),.reset (reset),.axi_ar_req_en (axi_ar_req_en),.axi_ar_ready (axi_ar_ready),.axi_ar_burst_len (axi_ar_burst_len),.axi_ar_addr (axi_ar_addr),.axi_r_valid (axi_r_valid),.axi_r_data (axi_r_data),.axi_r_last (axi_r_last),.m_axi_arvalid (m_axi_arvalid),.m_axi_arready (m_axi_arready),.m_axi_araddr (m_axi_araddr),.m_axi_arid (m_axi_arid),.m_axi_arlen (m_axi_arlen),.m_axi_arburst (m_axi_arburst),.m_axi_arsize (m_axi_arsize),.m_axi_arport (m_axi_arport),.m_axi_arqos (m_axi_arqos),.m_axi_arlock (m_axi_arlock),.m_axi_arcache (m_axi_arcache),.m_axi_rid (m_axi_rid),.m_axi_rvalid (m_axi_rvalid),.m_axi_rready (m_axi_rready),.m_axi_rdata (m_axi_rdata),.m_axi_rlast (m_axi_rlast),.m_axi_rresp (m_axi_rresp));endmodule