FDMA 3.1 米联客的Axi-DDR3控制器及其配套的Dbuf

超过Brust Length的处理方法:

        自定义的FDMA Brust Length,能够接收比实际Axi  Brust Lengtht更多的数据,这是通过这个模块实现的:

        即通过设置的Axi总线最大brust len后,通过对比其位宽过一位的数据是否为高,来判断需求的brust长度是否是否超过了max brust len,如果超过了则进行截断。。

        FDMA的一次读写操作的分界线是一次FDMA Brust Length的完成,其中可以涵盖多个Axi  Brust Length。

//fdma write data burst len counter----------------------------------
reg wburst_len_req = 1'b0;
reg [15:0] fdma_wleft_cnt =16'd0;

// wburst_len_req信号是自动管理每次axi需要burst的长度
always @(posedge M_AXI_ACLK)
        wburst_len_req <= fdma_wstart|axi_wlast;

// fdma_wleft_cnt用于记录一次FDMA剩余需要传输的数据数量  
always @(posedge M_AXI_ACLK)
    if( fdma_wstart )begin
        wfdma_cnt <= 1'd0;
        fdma_wleft_cnt <= I_fdma_wsize;
    end
    else if(w_next)begin
        wfdma_cnt <= wfdma_cnt + 1'b1;  
        fdma_wleft_cnt <= (I_fdma_wsize - 1'b1) - wfdma_cnt;
    end
//当最后一个数据的时候,产生fdma_wend信号代表本次fdma传输结束
assign  fdma_wend = w_next && (fdma_wleft_cnt == 1 );
//一次axi最大传输的长度是256因此当大于256,自动拆分多次传输
always @(posedge M_AXI_ACLK)begin
    if(M_AXI_ARESETN == 1'b0)begin
        wburst_len <= 1;
    end
    else if(wburst_len_req)begin
        if(fdma_wleft_cnt[15:MAX_BURST_LEN_SIZE] >0)  //当你希望FDMA提供的Brust长度超过了所配置的Axi brust长度时,brust长度设置为所配置Axi的最大brust长度
            wburst_len <= M_AXI_MAX_BURST_LEN;
        else 
            wburst_len <= fdma_wleft_cnt[MAX_BURST_LEN_SIZE-1:0]; //否则按照你配置的来
    end
    else wburst_len <= wburst_len;
end

完整代码:


`timescale 1ns / 1ns

/*******************************MILIANKE*******************************
*Company : MiLianKe Electronic Technology Co., Ltd.
*WebSite:https://www.milianke.com
*TechWeb:https://www.uisrc.com
*tmall-shop:https://milianke.tmall.com
*jd-shop:https://milianke.jd.com
*taobao-shop1: https://milianke.taobao.com
*Create Date: 2023/03/23
*Module Name:
*File Name:
*Description: 
*The reference demo provided by Milianke is only used for learning. 
*We cannot ensure that the demo itself is free of bugs, so users 
*should be responsible for the technical problems and consequences
*caused by the use of their own products.
*Copyright: Copyright (c) MiLianKe
*All rights reserved.
*Revision: 3.1
*Signal description
*1) I_ input
*2) O_ output
*3) IO_ input output
*4) S_ system internal signal
*5) _n activ low
*6) _dg debug signal 
*7) _r delay or register
*8) _s state mechine
*********************************************************************/

/*********uiFDMA(AXI-FAST DMA Controller)基于AXI总线的自定义内存控制器***********
--版本号3.1
--1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨,读写对称
--2.fdma控制信号,简化了AXI总线的控制,根据I_fdma_wsize和I_fdma_rsize可以自动完成AXI总线的控制,完成数据的搬运
*********************************************************************/

module uiFDMA#
(
parameter  integer         M_AXI_ID_WIDTH			= 3		    , //ID,demo中没用到
parameter  integer         M_AXI_ID			        = 0		    , //ID,demo中没用到
parameter  integer         M_AXI_ADDR_WIDTH			= 32		,//内存地址位宽
parameter  integer         M_AXI_DATA_WIDTH			= 128		,//AXI总线的数据位宽
parameter  integer		   M_AXI_MAX_BURST_LEN      = 64         //AXI总线的burst 大小,对于AXI4,支持任意长度,对于AXI3以下最大16
)
(
input   wire [M_AXI_ADDR_WIDTH-1 : 0]      I_fdma_waddr          ,//FDMA写通道地址
input                                      I_fdma_wareq          ,//FDMA写通道请求
input   wire [15 : 0]                      I_fdma_wsize          ,//FDMA写通道一次FDMA的传输大小                                            
output                                     O_fdma_wbusy          ,//FDMA处于BUSY状态,AXI总线正在写操作  
				
input   wire [M_AXI_DATA_WIDTH-1 :0]       I_fdma_wdata		   ,//FDMA写数据
output  wire                               O_fdma_wvalid         ,//FDMA 写有效
input	wire                               I_fdma_wready		   ,//FDMA写准备好,用户可以写数据

input   wire [M_AXI_ADDR_WIDTH-1 : 0]      I_fdma_raddr          ,// FDMA读通道地址
input                                      I_fdma_rareq          ,// FDMA读通道请求
input   wire [15 : 0]                      I_fdma_rsize          ,// FDMA读通道一次FDMA的传输大小                                      
output                                     O_fdma_rbusy          ,// FDMA处于BUSY状态,AXI总线正在读操作 
				
output  wire [M_AXI_DATA_WIDTH-1 :0]       O_fdma_rdata		   ,// FDMA读数据
output  wire                               O_fdma_rvalid         ,// FDMA 读有效
input	wire                               I_fdma_rready		   ,// FDMA读准备好,用户可以读数据

//以下为AXI总线信号		
input 	wire  								M_AXI_ACLK			,
input 	wire  								M_AXI_ARESETN		,
output 	wire [M_AXI_ID_WIDTH-1 : 0]		    M_AXI_AWID			,
output 	wire [M_AXI_ADDR_WIDTH-1 : 0] 	    M_AXI_AWADDR		,
output 	wire [7 : 0]						M_AXI_AWLEN			,
output 	wire [2 : 0] 						M_AXI_AWSIZE		,
output 	wire [1 : 0] 						M_AXI_AWBURST		,
output 	wire  								M_AXI_AWLOCK		,
output 	wire [3 : 0] 						M_AXI_AWCACHE		,
output 	wire [2 : 0] 						M_AXI_AWPROT		,  
output 	wire [3 : 0] 						M_AXI_AWQOS			,
output 	wire  								M_AXI_AWVALID		,
input	wire  								M_AXI_AWREADY		,
output  wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_WID			,
output  wire [M_AXI_DATA_WIDTH-1 : 0] 	    M_AXI_WDATA			,
output  wire [M_AXI_DATA_WIDTH/8-1 : 0] 	M_AXI_WSTRB			,
output  wire  								M_AXI_WLAST			, 			
output  wire  								M_AXI_WVALID		,
input   wire  								M_AXI_WREADY		,
input   wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_BID			,
input   wire [1 : 0] 						M_AXI_BRESP			,
input   wire  								M_AXI_BVALID		,
output  wire  								M_AXI_BREADY		, 
output  wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_ARID			,	 

output  wire [M_AXI_ADDR_WIDTH-1 : 0] 	    M_AXI_ARADDR		,	 	
output  wire [7 : 0] 						M_AXI_ARLEN			,	 
output  wire [2 : 0] 						M_AXI_ARSIZE		,	 
output  wire [1 : 0] 						M_AXI_ARBURST		,	 
output  wire  								M_AXI_ARLOCK		,	 
output  wire [3 : 0] 						M_AXI_ARCACHE		,	 
output  wire [2 : 0] 						M_AXI_ARPROT		,	 
output  wire [3 : 0] 						M_AXI_ARQOS			,	 	   
output  wire  								M_AXI_ARVALID		,	 
input   wire  								M_AXI_ARREADY		,	 
input   wire [M_AXI_ID_WIDTH-1 : 0] 		M_AXI_RID			,	 
input   wire [M_AXI_DATA_WIDTH-1 : 0] 	    M_AXI_RDATA			,	 
input   wire [1 : 0] 						M_AXI_RRESP			,	 
input   wire  								M_AXI_RLAST			,	 
input   wire  								M_AXI_RVALID		,    
output  wire  								M_AXI_RREADY				
	);

//计算数据位宽
function integer clogb2 (input integer bit_depth);              
begin                                                           
	 for(clogb2=0; bit_depth>0; clogb2=clogb2+1)                   
	 bit_depth = bit_depth >> 1;                                 
end                                                           
endfunction 

localparam AXI_BYTES =  M_AXI_DATA_WIDTH/8;
localparam [3:0] MAX_BURST_LEN_SIZE = clogb2(M_AXI_MAX_BURST_LEN -1);         
                                                    
//fdma axi write----------------------------------------------
reg     [M_AXI_ADDR_WIDTH-1 : 0]   axi_awaddr  =0;                       //AXI4 写地址
reg                                axi_awvalid = 1'b0;                   //AXI4 写地有效
wire    [M_AXI_DATA_WIDTH-1 : 0]   axi_wdata   ;                         //AXI4 写数据
wire                               axi_wlast   ;                         //AXI4 写LAST信号
reg                                axi_wvalid  = 1'b0;                   //AXI4 写数据有效
wire                               w_next= (M_AXI_WVALID & M_AXI_WREADY);//当valid ready信号都有效,代表AXI4数据传输有效
reg   [8 :0]                       wburst_len  = 1  ;                    //写传输的axi burst长度,代码会自动计算每次axi传输的burst 长度
reg   [8 :0]                       wburst_cnt  = 0  ;                    //每次axi bust的计数器
reg   [15:0]                       wfdma_cnt   = 0  ;                    //fdma的写数据计数器
reg                                axi_wstart_locked  =0;                //axi 传输进行中,lock住,用于时序控制
wire  [15:0] axi_wburst_size   =   wburst_len * AXI_BYTES;               //axi 传输的地址长度计算

assign M_AXI_AWID       = M_AXI_ID; //写地址ID,用来标志一组写信号, M_AXI_ID是通过参数接口定义
assign M_AXI_AWADDR     = axi_awaddr;
assign M_AXI_AWLEN      = wburst_len - 1;//AXI4 burst的长度
assign M_AXI_AWSIZE     = clogb2(AXI_BYTES-1);
assign M_AXI_AWBURST    = 2'b01;//AXI4的busr类型INCR模式,地址递增
assign M_AXI_AWLOCK     = 1'b0;
assign M_AXI_AWCACHE    = 4'b0010;//不使用cache,不使用buffer
assign M_AXI_AWPROT     = 3'h0;
assign M_AXI_AWQOS      = 4'h0;
assign M_AXI_AWVALID         = axi_awvalid;
assign M_AXI_WDATA      = axi_wdata;
assign M_AXI_WSTRB      = {(AXI_BYTES){1'b1}};//设置所有的WSTRB为1代表传输的所有数据有效
assign M_AXI_WLAST      = axi_wlast;
assign M_AXI_WVALID     = axi_wvalid & I_fdma_wready;//写数据有效,这里必须设置I_fdma_wready有效
assign M_AXI_BREADY     = 1'b1;
//----------------------------------------------------------------------------  
//AXI4 FULL Write
assign  axi_wdata        = I_fdma_wdata;
assign  O_fdma_wvalid      = w_next;
reg     fdma_wstart_locked = 1'b0;
wire    fdma_wend;
wire    fdma_wstart;
assign   O_fdma_wbusy = fdma_wstart_locked ;
//在整个写过程中fdma_wstart_locked将保持有效,直到本次FDMA写结束
always @(posedge M_AXI_ACLK)
    if(M_AXI_ARESETN == 1'b0 || fdma_wend == 1'b1 )
        fdma_wstart_locked <= 1'b0;
    else if(fdma_wstart)
        fdma_wstart_locked <= 1'b1;                                
//产生fdma_wstart信号,整个信号保持1个  M_AXI_ACLK时钟周期
assign fdma_wstart = (fdma_wstart_locked == 1'b0 && I_fdma_wareq == 1'b1);    
        
//AXI4 write burst lenth busrt addr ------------------------------
//当fdma_wstart信号有效,代表一次新的FDMA传输,首先把地址本次fdma的burst地址寄存到axi_awaddr作为第一次axi burst的地址。如果fdma的数据长度大于256,那么当axi_wlast有效的时候,自动计算下次axi的burst地址
always @(posedge M_AXI_ACLK)
    if(fdma_wstart)    
        axi_awaddr <= I_fdma_waddr;
    else if(axi_wlast == 1'b1)
        axi_awaddr <= axi_awaddr + axi_wburst_size ;                    
//AXI4 write cycle -----------------------------------------------
//axi_wstart_locked_r1, axi_wstart_locked_r2信号是用于时序同步
reg axi_wstart_locked_r1 = 1'b0, axi_wstart_locked_r2 = 1'b0;
always @(posedge M_AXI_ACLK)begin
    axi_wstart_locked_r1 <= axi_wstart_locked;
    axi_wstart_locked_r2 <= axi_wstart_locked_r1;
end
// axi_wstart_locked的作用代表一次axi写burst操作正在进行中。
always @(posedge M_AXI_ACLK)
    if((fdma_wstart_locked == 1'b1) &&  axi_wstart_locked == 1'b0)
        axi_wstart_locked <= 1'b1; 
    else if(axi_wlast == 1'b1 || fdma_wstart == 1'b1)
        axi_wstart_locked <= 1'b0;
        
//AXI4 addr valid and write addr----------------------------------- 
always @(posedge M_AXI_ACLK)
     if((axi_wstart_locked_r1 == 1'b1) &&  axi_wstart_locked_r2 == 1'b0)
         axi_awvalid <= 1'b1;
     else if((axi_wstart_locked == 1'b1 && M_AXI_AWREADY == 1'b1)|| axi_wstart_locked == 1'b0)
         axi_awvalid <= 1'b0;       
//AXI4 write data---------------------------------------------------        
always @(posedge M_AXI_ACLK)
    if((axi_wstart_locked_r1 == 1'b1) &&  axi_wstart_locked_r2 == 1'b0)
        axi_wvalid <= 1'b1;
    else if(axi_wlast == 1'b1 || axi_wstart_locked == 1'b0)
        axi_wvalid <= 1'b0;//   
//AXI4 write data burst len counter----------------------------------
always @(posedge M_AXI_ACLK)
    if(axi_wstart_locked == 1'b0)
        wburst_cnt <= 'd0;
    else if(w_next)
        wburst_cnt <= wburst_cnt + 1'b1;    
            
assign axi_wlast = (w_next == 1'b1) && (wburst_cnt == M_AXI_AWLEN);
//fdma write data burst len counter----------------------------------
reg wburst_len_req = 1'b0;
reg [15:0] fdma_wleft_cnt =16'd0;

// wburst_len_req信号是自动管理每次axi需要burst的长度
always @(posedge M_AXI_ACLK)
        wburst_len_req <= fdma_wstart|axi_wlast;

// fdma_wleft_cnt用于记录一次FDMA剩余需要传输的数据数量  
always @(posedge M_AXI_ACLK)
    if( fdma_wstart )begin
        wfdma_cnt <= 1'd0;
        fdma_wleft_cnt <= I_fdma_wsize;
    end
    else if(w_next)begin
        wfdma_cnt <= wfdma_cnt + 1'b1;  
        fdma_wleft_cnt <= (I_fdma_wsize - 1'b1) - wfdma_cnt;
    end
//当最后一个数据的时候,产生fdma_wend信号代表本次fdma传输结束
assign  fdma_wend = w_next && (fdma_wleft_cnt == 1 );
//一次axi最大传输的长度是256因此当大于256,自动拆分多次传输
always @(posedge M_AXI_ACLK)begin
    if(M_AXI_ARESETN == 1'b0)begin
        wburst_len <= 1;
    end
    else if(wburst_len_req)begin
        if(fdma_wleft_cnt[15:MAX_BURST_LEN_SIZE] >0)  //当你希望FDMA提供的Brust长度超过了所配置的Axi brust长度时,brust长度设置为所配置Axi的最大brust长度
            wburst_len <= M_AXI_MAX_BURST_LEN;
        else 
            wburst_len <= fdma_wleft_cnt[MAX_BURST_LEN_SIZE-1:0]; //否则按照你配置的来
    end
    else wburst_len <= wburst_len;
end



//fdma axi read----------------------------------------------
reg     [M_AXI_ADDR_WIDTH-1 : 0]    axi_araddr =0   ; //AXI4 读地址
reg                         axi_arvalid  =1'b0; //AXI4读地有效
wire                        axi_rlast   ; //AXI4 读LAST信号
reg                         axi_rready  = 1'b0;//AXI4读准备好
wire                              r_next      = (M_AXI_RVALID && M_AXI_RREADY);// 当valid ready信号都有效,代表AXI4数据传输有效
reg   [8 :0]                        rburst_len  = 1  ; //读传输的axi burst长度,代码会自动计算每次axi传输的burst 长度
reg   [8 :0]                        rburst_cnt  = 0  ; //每次axi bust的计数器
reg   [15:0]                       rfdma_cnt   = 0  ; //fdma的读数据计数器
reg                               axi_rstart_locked =0; //axi 传输进行中,lock住,用于时序控制
wire  [15:0] axi_rburst_size   =   rburst_len * AXI_BYTES; //axi 传输的地址长度计算  

assign M_AXI_ARID       = M_AXI_ID; //读地址ID,用来标志一组写信号, M_AXI_ID是通过参数接口定义
assign M_AXI_ARADDR     = axi_araddr;
assign M_AXI_ARLEN      = rburst_len - 1; //AXI4 burst的长度
assign M_AXI_ARSIZE     = clogb2((AXI_BYTES)-1);
assign M_AXI_ARBURST    = 2'b01; //AXI4的busr类型INCR模式,地址递增
assign M_AXI_ARLOCK     = 1'b0; //不使用cache,不使用buffer
assign M_AXI_ARCACHE    = 4'b0010;
assign M_AXI_ARPROT     = 3'h0;
assign M_AXI_ARQOS      = 4'h0;
assign M_AXI_ARVALID    = axi_arvalid;
assign M_AXI_RREADY     = axi_rready&&I_fdma_rready; //读数据准备好,这里必须设置I_fdma_rready有效
assign O_fdma_rdata       = M_AXI_RDATA;    
assign O_fdma_rvalid      = r_next;    

//AXI4 FULL Read-----------------------------------------   

reg     fdma_rstart_locked = 1'b0;
wire    fdma_rend;
wire    fdma_rstart;
assign   O_fdma_rbusy = fdma_rstart_locked ;
//在整个读过程中fdma_rstart_locked将保持有效,直到本次FDMA写结束
always @(posedge M_AXI_ACLK)
    if(M_AXI_ARESETN == 1'b0 || fdma_rend == 1'b1)
        fdma_rstart_locked <= 1'b0;
    else if(fdma_rstart)
        fdma_rstart_locked <= 1'b1;                                
//产生fdma_rstart信号,整个信号保持1个  M_AXI_ACLK时钟周期
assign fdma_rstart = (fdma_rstart_locked == 1'b0 && I_fdma_rareq == 1'b1);    
//AXI4 read burst lenth busrt addr ------------------------------
//当fdma_rstart信号有效,代表一次新的FDMA传输,首先把地址本次fdma的burst地址寄存到axi_araddr作为第一次axi burst的地址。如果fdma的数据长度大于256,那么当axi_rlast有效的时候,自动计算下次axi的burst地址
always @(posedge M_AXI_ACLK)
    if(fdma_rstart == 1'b1)    
        axi_araddr <= I_fdma_raddr;
    else if(axi_rlast == 1'b1)
        axi_araddr <= axi_araddr + axi_rburst_size ;                                                
//AXI4 r_cycle_flag-------------------------------------    
//axi_rstart_locked_r1, axi_rstart_locked_r2信号是用于时序同步
reg axi_rstart_locked_r1 = 1'b0, axi_rstart_locked_r2 = 1'b0;
always @(posedge M_AXI_ACLK)begin
    axi_rstart_locked_r1 <= axi_rstart_locked;
    axi_rstart_locked_r2 <= axi_rstart_locked_r1;
end
// axi_rstart_locked的作用代表一次axi读burst操作正在进行中。
always @(posedge M_AXI_ACLK)
    if((fdma_rstart_locked == 1'b1) &&  axi_rstart_locked == 1'b0)
        axi_rstart_locked <= 1'b1; 
    else if(axi_rlast == 1'b1 || fdma_rstart == 1'b1)
        axi_rstart_locked <= 1'b0;
//AXI4 addr valid and read addr-----------------------------------  
always @(posedge M_AXI_ACLK)
     if((axi_rstart_locked_r1 == 1'b1) &&  axi_rstart_locked_r2 == 1'b0)
         axi_arvalid <= 1'b1;
     else if((axi_rstart_locked == 1'b1 && M_AXI_ARREADY == 1'b1)|| axi_rstart_locked == 1'b0)
         axi_arvalid <= 1'b0;       
//AXI4 read data---------------------------------------------------     
always @(posedge M_AXI_ACLK)
    if((axi_rstart_locked_r1 == 1'b1) &&  axi_rstart_locked_r2 == 1'b0)
        axi_rready <= 1'b1;
    else if(axi_rlast == 1'b1 || axi_rstart_locked == 1'b0)
        axi_rready <= 1'b0;//   
//AXI4 read data burst len counter----------------------------------
always @(posedge M_AXI_ACLK)
    if(axi_rstart_locked == 1'b0)
        rburst_cnt <= 'd0;
    else if(r_next)
        rburst_cnt <= rburst_cnt + 1'b1;            
assign axi_rlast = (r_next == 1'b1) && (rburst_cnt == M_AXI_ARLEN);
//fdma read data burst len counter----------------------------------
reg rburst_len_req = 1'b0;
reg [15:0] fdma_rleft_cnt =16'd0;
// rburst_len_req信号是自动管理每次axi需要burst的长度  
always @(posedge M_AXI_ACLK)
        rburst_len_req <= fdma_rstart | axi_rlast;  
// fdma_rleft_cnt用于记录一次FDMA剩余需要传输的数据数量          
always @(posedge M_AXI_ACLK)
    if(fdma_rstart )begin
        rfdma_cnt <= 1'd0;
        fdma_rleft_cnt <= I_fdma_rsize;
    end
    else if(r_next)begin
        rfdma_cnt <= rfdma_cnt + 1'b1;  
        fdma_rleft_cnt <= (I_fdma_rsize - 1'b1) - rfdma_cnt;
    end
//当最后一个数据的时候,产生fdma_rend信号代表本次fdma传输结束
assign  fdma_rend = r_next && (fdma_rleft_cnt == 1 );
//axi auto burst len caculate-----------------------------------------
//一次axi最大传输的长度是256因此当大于256,自动拆分多次传输
always @(posedge M_AXI_ACLK)begin
     if(M_AXI_ARESETN == 1'b0)begin
        rburst_len <= 1;
     end
     else if(rburst_len_req)begin
        if(fdma_rleft_cnt[15:MAX_BURST_LEN_SIZE] >0)  
            rburst_len <= M_AXI_MAX_BURST_LEN;
        else 
            rburst_len <= fdma_rleft_cnt[MAX_BURST_LEN_SIZE-1:0];
     end
     else rburst_len <= rburst_len;
end


		

//dbg_wave dbg_wave_inst
//(
//    .trig_out_0(),
//    .data_in_0({rburst_len[7:0],rburst_cnt[8:0],fdma_rleft_cnt[15:0],I_fdma_rareq,fdma_rstart,fdma_rstart_locked,axi_rstart_locked,axi_rlast,rburst_len_req,M_AXI_ARVALID,M_AXI_ARREADY,M_AXI_RVALID,M_AXI_RREADY,M_AXI_RLAST,wburst_len[7:0],wburst_cnt[8:0],fdma_wleft_cnt[15:0],I_fdma_wareq,fdma_wstart,fdma_wstart_locked,axi_wstart_locked,axi_wlast,wburst_len_req,M_AXI_AWVALID,M_AXI_AWREADY,M_AXI_WVALID,M_AXI_WREADY,M_AXI_WLAST,M_AXI_ARESETN}),
//    .ref_clk_0(M_AXI_ACLK)
//);		
		
		              		   
endmodule


你可能感兴趣的:(读书笔记,FPGA学习,DDR3,fpga开发)