AXI4总线协议 ------ AXI_FULL协议

https://download.youkuaiyun.com/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 + 36bit

FIFO使用了一个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状态机启动信号

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Description: 写通道控制模块
//////////////////////////////////////////////////////////////////////////////////
module wr_ctrl#(
	parameter		USER_WR_DATA_WIDTH = 16  ,
	parameter       AXI_DATA_WIDTH     = 128 ,
	parameter       AXI_ADDR_WIDTH     = 32  ,
	parameter       WR_BURST_LENGTH    = 4096   //可以为4096,2048,1024....字节
)(
    input                              clk               , //用户端写时钟
	input                              reset             ,

    /*-----DDR初始化完成信号,与MIG核交互------------------*/
	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 , //初始写ddr数据地址  一定要被4096整除
	input   [AXI_ADDR_WIDTH-1:0]       user_wr_end_addr  , //一定要被4096整除
    
    /*-------------与wr_buffer模块交互信号----------------*/
    output  reg                        wr_data_valid     ,
    output  reg  [AXI_DATA_WIDTH-1:0]  wr_data_out       ,
    output  reg                        wr_data_last      ,
    output       [7:0]                 wr_burst_length   ,
    output  reg                        wr_req_en         ,
    output  reg  [AXI_ADDR_WIDTH-1:0]  wr_data_addr      
);


(* dont_touch ="true" *)        reg reset_sync_d0;  //(* dont_touch ="true" *)的作用是防止信号被优化
(* dont_touch ="true" *)        reg reset_sync_d1;
(* dont_touch ="true" *)        reg reset_sync;

reg                             ddr_init_done_d0;
reg                             ddr_init_done_d1;
reg                             ddr_wr_enable   ;

reg                             user_wr_en_d    ;
reg [USER_WR_DATA_WIDTH-1:0]    user_wr_data_d  ;

localparam  WR_CNT_MAX       = AXI_DATA_WIDTH / USER_WR_DATA_WIDTH - 1;    //用户端数据与AXI数据位宽转换计数
localparam  MAX_BURST_LENGTH = WR_BURST_LENGTH / (AXI_DATA_WIDTH / 8) - 1; //AXI突发写长度

reg [$clog2(WR_CNT_MAX) - 1 :0] wr_cnt          ;
reg [7:0]                       wr_burst_cnt    ;
/*--------------------------------------------------*\
				     CDC process
\*--------------------------------------------------*/
always @(posedge clk) begin
	reset_sync_d0 <= reset;
	reset_sync_d1 <= reset_sync_d0;
	reset_sync    <= reset_sync_d1;    
end

always @(posedge clk) begin
	ddr_init_done_d0 <= ddr_init_done;
	ddr_init_done_d1 <= ddr_init_done_d0;
	ddr_wr_enable    <= ddr_init_done_d1;
end

always @(posedge clk) begin //在DDR初始化完成之后,才开始将数据写入DDR,如果DDR没有初始化完成,用户端有数据来了,就丢掉
    if (ddr_wr_enable) begin
    	user_wr_en_d   <= user_wr_en;
    	user_wr_data_d <= user_wr_data;
    end  
    else begin
    	user_wr_en_d   <= 0;
    	user_wr_data_d <= 0;
    end 
end

/*--------------------------------------------------*\
				     data
\*--------------------------------------------------*/
always @(posedge clk) begin //也可以拆开写
    if (reset_sync) begin
    	wr_cnt          <= 0;
    	wr_burst_cnt    <= 0;
    end
    else if (user_wr_en_d) begin
    	if (wr_cnt == WR_CNT_MAX)begin
    		wr_cnt          <= 0;
    		wr_burst_cnt    <= (wr_burst_cnt == MAX_BURST_LENGTH) ? 0 : wr_burst_cnt + 1;
    	end
    	else begin
    		wr_cnt          <= wr_cnt + 1;
    		wr_burst_cnt    <= wr_burst_cnt;
    	end
    end   
end

always @(posedge clk) begin
    if (wr_cnt == WR_CNT_MAX && user_wr_en_d) 
        wr_data_valid <= 1'b1;
    else 
        wr_data_valid <= 0; 
end
//高进低出
always @(posedge clk) begin
    if (user_wr_en_d) 
       wr_data_out <= {user_wr_data_d,wr_data_out[AXI_DATA_WIDTH-1 : USER_WR_DATA_WIDTH]}; 
    else 
       wr_data_out <= wr_data_out;
end

always @(posedge clk) begin
    if (wr_cnt == WR_CNT_MAX && wr_burst_cnt == MAX_BURST_LENGTH && user_wr_en_d) 
        wr_data_last <= 1'b1;
    else 
        wr_data_last <= 0; 
end
/*--------------------------------------------------*\
				        req / addr
\*--------------------------------------------------*/
always @(posedge clk) begin
    if (wr_cnt == WR_CNT_MAX && wr_burst_cnt == MAX_BURST_LENGTH && user_wr_en_d) 
        wr_req_en <= 1'b1;
    else 
       	wr_req_en <= 0; 
end

//每突发一次写完成,写地址+突发长度
always @(posedge clk) begin
    if (reset) 
        wr_data_addr <= user_wr_base_addr;
    else if (wr_req_en && wr_data_addr >= user_wr_end_addr - WR_BURST_LENGTH) 
        wr_data_addr <= user_wr_base_addr;
    else if (wr_req_en)
        wr_data_addr  <= wr_data_addr + WR_BURST_LENGTH;
    else 
        wr_data_addr  <= wr_data_addr;
end

assign wr_burst_length = MAX_BURST_LENGTH;
endmodule

2.2 wr_buffer模块

1.复位信号进行跨时钟域同步(用户时钟/AXI4时钟)

2.将写数据addr和length写入CMD fifo (clk),cmd_wren在wr_req_en为1时拉高,并且此时更新地址(与2.1wr_ctrl交互的信号)

3..将要写数据data和last 写入data fifo;data_wren随着上层数据有效写入data_fifo

步骤2.3均是对上层模块的输入进行寄存;下面步骤做输出转换设计:

4.状态机设计(输出传给axi4_master模块)

axi_aw_req_en和axi_aw_ready)握手机制控制是否开始写状态

//表示AXI4写请求  本模块时序控制,状态机一个状态产生请求信号,握手成功之后拉低
//axi_aw_req_en 和axi_aw_ready同时为高,开启一次AXI4写传输

并在该信号跳转开始写数据状态的同时,拉高cmd_rden,读出数据突发长度以及写地址

5.AXI部分信号的时序

        突发长度和地址由当cmd_rden为高时,cmd_dout分配

        其次是是数据有效同上,已进入写数据状态就拉高,写完最后一个数据拉低;

                                         

        数据和last由当data_rden为高时由data_dout分配,其中data_rden信号设计如下:

        ps:值得注意一下的是三个信号同步

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Description: 写缓存(主要是cmd_fifo/data_fifo)
//////////////////////////////////////////////////////////////////////////////////
module wr_buffer #(
	parameter       AXI_DATA_WIDTH     = 128 ,
	parameter       AXI_ADDR_WIDTH     = 32  

)(
	input                             clk               , //用户端写时钟
	input                             axi_clk           , //AXI4端时钟
	input                             reset             ,

    /*-------------wr_ctrl模块交互信号----------------*/
    input  	                          wr_req_en         ,
    input  	    [AXI_ADDR_WIDTH-1:0]  wr_data_addr      , 
    input  	    [7:0]                 wr_burst_length   ,
    input  	                          wr_data_valid     ,
    input  	    [AXI_DATA_WIDTH-1:0]  wr_data_in        ,
    input  	                          wr_data_last      ,

    /*-------------axi_wr_master_模块交互信号-------------*/
    output reg                        axi_aw_req_en     , //表示AXI4写请求,需要本模块设计给出
 	input                             axi_aw_ready      , //axi_aw_req_en 和axi_aw_ready同时为高,开启一次AXI4写传输
 	output reg  [7:0]                 axi_aw_burst_len  ,
 	output reg  [AXI_ADDR_WIDTH-1:0]  axi_aw_addr       ,

 	output reg                        axi_w_valid       ,
 	input                             axi_w_ready       ,
 	output reg  [AXI_DATA_WIDTH-1:0]  axi_w_data        ,
 	output reg                        axi_w_last        

);


(* dont_touch ="true" *)reg reset_sync_d0;    //用户端复位信号
(* dont_touch ="true" *)reg reset_sync_d1;
(* dont_touch ="true" *)reg reset_sync;

(* dont_touch ="true" *)reg a_reset_sync_d0; // axi4端的复位信号
(* dont_touch ="true" *)reg a_reset_sync_d1;
(* dont_touch ="true" *)reg a_reset_sync;

/*------------------------------------------*\
                FIFO端口信号定义
\*------------------------------------------*/
reg				cmd_wren;
wire [39:0]	    cmd_dout;
wire			cmd_rden;
wire			cmd_wrfull;
wire			cmd_rdempty;
wire [4:0]	    cmd_wrcount;
wire [4:0]	    cmd_rdcount;
reg	 [39:0]		cmd_din;       //根据项目实际需要修改这边的输入,本模块  AXI_ADDR_WIDTH = 32  + wr_burst_length  =8


/*------------------------------------------*\
                状态机
\*------------------------------------------*/
reg [2:0] cur_status;
reg [2:0] nxt_status;

localparam WR_IDLE     = 3'b000;
localparam WR_REQ      = 3'b001;
localparam WR_DATA_EN  = 3'b010;
localparam WR_DATA_END = 3'b100;
/*--------------------------------------------------*\
				     CDC process
\*--------------------------------------------------*/
always @(posedge clk) begin
	reset_sync_d0 <= reset;
	reset_sync_d1 <= reset_sync_d0;
	reset_sync    <= reset_sync_d1;    
end

always @(posedge axi_clk) begin
	a_reset_sync_d0 <= reset;
	a_reset_sync_d1 <= a_reset_sync_d0;
	a_reset_sync    <= a_reset_sync_d1;    
end

/*--------------------------------------------------*\
		     将地址和length写入CMD fifo
\*--------------------------------------------------*/
always @(posedge clk) begin
    if (wr_req_en) begin
    	cmd_wren <= 1'b1;
    	cmd_din  <= {wr_burst_length,wr_data_addr};
    end
    else begin
    	cmd_wren <= 0;
    	cmd_din  <= 0;    	
    end  
end

/*--------------------------------------------------*\
		   状态机设计,注意时钟域,是在axi_clk时钟下
\*--------------------------------------------------*/
always @(posedge axi_clk) begin
    if (a_reset_sync) 
        cur_status <= WR_IDLE;
    else 
        cur_status <= nxt_status;
end

always @(*) begin
    if (a_reset_sync) 
        nxt_status <= WR_IDLE;
    else begin
    	case(cur_status)
    		WR_IDLE : begin
    			if (~cmd_rdempty)
    				nxt_status <= WR_REQ;
    			else 
    				nxt_status <= cur_status;
    		end
    		WR_REQ : begin
    			if (axi_aw_req_en && axi_aw_ready)	//握手机制控制写状态开始
    				nxt_status <= WR_DATA_EN;
    			else 
    				nxt_status <= cur_status;
    		end
    		WR_DATA_EN : begin
    			if (axi_w_valid && axi_w_ready && axi_w_last)
    				nxt_status <= WR_DATA_END;
    			else 
    				nxt_status <= cur_status;
    		end
    		WR_DATA_END : begin
    			nxt_status <= WR_IDLE;
    		end
    		default : nxt_status <= WR_IDLE;
    	endcase
    end
end

always @(posedge axi_clk) begin
    if (a_reset_sync) 
        axi_aw_req_en <= 0;
    else if (axi_aw_req_en && axi_aw_ready) 
        axi_aw_req_en <= 0;
    else if (cur_status == WR_REQ)
        axi_aw_req_en <= 1; 
    else 
    	axi_aw_req_en <= axi_aw_req_en;
end


assign cmd_rden = axi_aw_req_en && axi_aw_ready;

always @(*) begin
    if (a_reset_sync) begin
        axi_aw_burst_len <= 0;
        axi_aw_addr      <= 0;
    end
    else begin
        axi_aw_burst_len <= cmd_rden ? cmd_dout[39:32]: axi_aw_burst_len;
        axi_aw_addr      <= cmd_rden ? cmd_dout[31:0] : axi_aw_addr;
    end
end

/*--------------------------------------------------*\
		               cmd fifo
\*--------------------------------------------------*/
fifo_w40xd16 wr_cmd_fifo (
  .rst           ( reset_sync    ),  // input wire rst
  .wr_clk        ( clk           ),  // input wire wr_clk
  .rd_clk        ( axi_clk       ),  // input wire rd_clk

  .din           ( cmd_din       ),  // input wire [39 : 0] din
  .wr_en         ( cmd_wren      ),  // input wire wr_en

  .rd_en         ( cmd_rden      ),  // input wire rd_en
  .dout          ( cmd_dout      ),  // output wire [39 : 0] dout

  .full          ( cmd_wrfull    ),  // output wire full
  .empty         ( cmd_rdempty   ),  // output wire empty
  .rd_data_count ( rd_data_count ),  // output wire [4 : 0] rd_data_count
  .wr_data_count ( wr_data_count )   // output wire [4 : 0] wr_data_count
);

/*--------------------------------------------------*\
		       根据AXI_DATA_WIDTH 生成不同位宽的fifo
\*--------------------------------------------------*/
reg				data_wren;
reg				data_rden;
wire			data_wrfull;
wire			data_rdempty;

generate
	if (AXI_DATA_WIDTH == 256) begin
		reg  [287:0] data_din;
		wire [287:0] data_dout;
		wire [9:0]   data_wrcount;
		wire [9:0]   data_rdcount; 		

		always @(posedge clk) begin
			data_din  <= {31'h0,wr_data_last,wr_data_in};//256+1+31
			data_wren <= wr_data_valid;
		end
		
		always @(posedge axi_clk) begin
		    if (axi_aw_req_en && axi_aw_ready) 
		        axi_w_valid <= 1'b1;
		    else if (axi_w_valid && axi_w_ready && axi_w_last) 
		        axi_w_valid <= 0;
		    else 
		        axi_w_valid <= axi_w_valid; 
		end
		
		always @(*) begin
		    if (data_rden) begin
		        axi_w_data <= data_dout[255:0];
		        axi_w_last <= data_dout[256];
		    end
		    else begin
		    	axi_w_data <= 0;
		    	axi_w_last <= 0;
		    end 
		end
		
		always @(*) begin
			data_rden <= axi_w_valid && axi_w_ready && cur_status == WR_DATA_EN;
		end
		

		fifo_w288xd512 wr_data_fifo (
		  .rst          (reset_sync     ),// input wire rst
		  .wr_clk       (clk            ),// input wire wr_clk
		  .rd_clk       (axi_clk        ),// input wire rd_clk
		  .din          (data_din       ),// input wire [287 : 0] din
		  .wr_en        (data_wren      ),// input wire wr_en
		  .rd_en        (data_rden      ),// input wire rd_en
		  .dout         (data_dout      ),// output wire [287 : 0] dout
		  .full         (data_wrfull    ),// output wire full
		  .empty        (data_rdempty   ),// output wire empty
		  .rd_data_count(data_rdcount   ),// output wire [9 : 0] rd_data_count
		  .wr_data_count(data_wrcount   ) // output wire [9 : 0] wr_data_count 
		);

	end else if (AXI_DATA_WIDTH == 128) begin
		reg  [143:0] data_din;
		wire [143:0] data_dout;
		wire [9:0]   data_wrcount;
		wire [9:0]   data_rdcount; 		

		always @(posedge clk) begin
			data_din  <= {15'h0,wr_data_last,wr_data_in};
			data_wren <= wr_data_valid;
		end
		
		always @(posedge axi_clk) begin
		    if (axi_aw_req_en && axi_aw_ready) 
		        axi_w_valid <= 1'b1;
		    else if (axi_w_valid && axi_w_ready && axi_w_last) 
		        axi_w_valid <= 0;
		    else 
		        axi_w_valid <= axi_w_valid; 
		end
		
		always @(*) begin
		    if (data_rden) begin
		        axi_w_data <= data_dout[127:0];
		        axi_w_last <= data_dout[128];
		    end
		    else begin
		    	axi_w_data <= 0;
		    	axi_w_last <= 0;
		    end 
		end
		
		always @(*) begin
			data_rden <= axi_w_valid && axi_w_ready && cur_status == WR_DATA_EN;
		end
		

		fifo_w144xd512 wr_data_fifo (
		  .rst          (reset_sync     ),// input wire rst
		  .wr_clk       (clk            ),// input wire wr_clk
		  .rd_clk       (axi_clk        ),// input wire rd_clk
		  .din          (data_din       ),// input wire [287 : 0] din
		  .wr_en        (data_wren      ),// input wire wr_en
		  .rd_en        (data_rden      ),// input wire rd_en
		  .dout         (data_dout      ),// output wire [287 : 0] dout
		  .full         (data_wrfull    ),// output wire full
		  .empty        (data_rdempty   ),// output wire empty
		  .rd_data_count(data_rdcount   ),// output wire [9 : 0] rd_data_count
		  .wr_data_count(data_wrcount   ) // output wire [9 : 0] wr_data_count 
		);


	end else if (AXI_DATA_WIDTH == 64) begin
		reg  [71:0] data_din;
		wire [71:0] data_dout;
		wire [9:0]  data_wrcount;
		wire [9:0]  data_rdcount; 		

		always @(posedge clk) begin
			data_din  <= {7'h0,wr_data_last,wr_data_in};
			data_wren <= wr_data_valid;
		end
		
		always @(posedge axi_clk) begin
		    if (axi_aw_req_en && axi_aw_ready) 
		        axi_w_valid <= 1'b1;
		    else if (axi_w_valid && axi_w_ready && axi_w_last) 
		        axi_w_valid <= 0;
		    else 
		        axi_w_valid <= axi_w_valid; 
		end
		
		always @(*) begin
		    if (data_rden) begin
		        axi_w_data <= data_dout[63:0];
		        axi_w_last <= data_dout[64];
		    end
		    else begin
		    	axi_w_data <= 0;
		    	axi_w_last <= 0;
		    end 
		end
		
		always @(*) begin
			data_rden <= axi_w_valid && axi_w_ready && cur_status == WR_DATA_EN;
		end
		
		fifo_w72xd512 wr_data_fifo (
		  .rst          (reset_sync     ),// input wire rst
		  .wr_clk       (clk            ),// input wire wr_clk
		  .rd_clk       (axi_clk        ),// input wire rd_clk
		  .din          (data_din       ),// input wire [287 : 0] din
		  .wr_en        (data_wren      ),// input wire wr_en
		  .rd_en        (data_rden      ),// input wire rd_en
		  .dout         (data_dout      ),// output wire [287 : 0] dout
		  .full         (data_wrfull    ),// output wire full
		  .empty        (data_rdempty   ),// output wire empty
		  .rd_data_count(data_rdcount   ),// output wire [9 : 0] rd_data_count
		  .wr_data_count(data_wrcount   ) // output wire [9 : 0] wr_data_count 
		);

	end
endgenerate

endmodule

2.3 axi_master模块

1.对除了与下游从机交互的握手信号以及写地址,写突发长度的信号进行固定赋值;

2. axi_aw_req_en启动状态机

3.组合逻辑输出作为丛机给wr_buffer的握手准备信号,一个由状态机产生,一个由下游从机输入

4.对和下游从机交互的握手信号以及写地址,写突发长度以及数据,数据最后一位的信号 时序控制

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Description: axi_wr_master
//////////////////////////////////////////////////////////////////////////////////
module axi_wr_master #(
	parameter       AXI_DATA_WIDTH     = 128 ,
	parameter       AXI_ADDR_WIDTH     = 32  	
)(
	input                             axi_clk           ,
	input                             reset             ,

    /*-------------wr_buffer模块交互信号-------------*/
 	input                             axi_aw_req_en     , //表示AXI4写请求,由上层wr_buffer传入
 	output reg                        axi_aw_ready      , //axi_aw_req_en 和axi_aw_ready同时为高,开启一次AXI4写传输
 	input      [7:0]                  axi_aw_burst_len  ,
 	input      [AXI_ADDR_WIDTH-1:0]   axi_aw_addr       ,

 	input                             axi_w_valid       ,
 	output reg                        axi_w_ready       ,
 	input      [AXI_DATA_WIDTH-1:0]   axi_w_data        ,
 	input                             axi_w_last        ,

    /*-------------AXI写通道端口信号---------------------*/
 	output reg                        m_axi_awvalid     ,
 	input                             m_axi_awready     ,
 	output reg [AXI_ADDR_WIDTH-1:0]   m_axi_awaddr      ,
 	output reg [7:0]                  m_axi_awlen       ,

 	output reg [1:0]                  m_axi_awburst     ,
 	output reg [2:0]                  m_axi_awsize      ,

	output reg [3:0]                  m_axi_awid        ,
 	output reg [2:0]                  m_axi_awport      ,
 	output reg [3:0]                  m_axi_awqos       ,
 	output reg                        m_axi_awlock      ,
 	output reg [3:0]                  m_axi_awcache     ,

 	output reg                        m_axi_wvalid      ,
 	input                             m_axi_wready      ,
 	output reg [AXI_DATA_WIDTH-1:0]   m_axi_wdata       ,
 	output reg [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb       ,   	
 	output reg                        m_axi_wlast       , 

    input      [3:0]                  m_axi_bid         ,
    input      [1:0]                  m_axi_bresp       ,
    input                             m_axi_bvalid      ,
    output                            m_axi_bready     
);

(* dont_touch ="true" *)reg a_reset_sync_d0; // axi4端的复位信号
(* dont_touch ="true" *)reg a_reset_sync_d1;
(* dont_touch ="true" *)reg a_reset_sync;

assign m_axi_bready = 1'b1;

/*------------------------------------------*\
                状态机定义
\*------------------------------------------*/
reg [2:0] cur_status;
reg [2:0] nxt_status;

localparam AXI_WR_IDLE = 3'b000;
localparam AXI_WR_PRE  = 3'b001;
localparam AXI_WR_DATA = 3'b010;
localparam AXI_WR_END  = 3'b100; 

/*--------------------------------------------------*\
				     CDC process
\*--------------------------------------------------*/
always @(posedge axi_clk) begin
	a_reset_sync_d0 <= reset;
	a_reset_sync_d1 <= a_reset_sync_d0;
	a_reset_sync    <= a_reset_sync_d1;    
end

always @(posedge axi_clk) begin
    m_axi_awport  <= 0;
	m_axi_awid    <= 0;
	m_axi_awburst <= 2'b01;
	m_axi_awlock  <= 0;
	m_axi_awcache <= 0;
	m_axi_awqos   <= 0;
	m_axi_wstrb   <= {AXI_DATA_WIDTH/8{1'b1}};
	m_axi_awsize  <= AXI_DATA_WIDTH == 512 ? 3'h6 :
					 AXI_DATA_WIDTH == 256 ? 3'h5 :
					 AXI_DATA_WIDTH == 128 ? 3'h4 :
					 AXI_DATA_WIDTH == 64  ? 3'h3 :
					 AXI_DATA_WIDTH == 32  ? 3'h2 : 3'h0;
end

/*--------------------------------------------------*\
				     状态机代码
\*--------------------------------------------------*/

always @(posedge axi_clk) begin
    if (a_reset_sync) 
        cur_status <= AXI_WR_IDLE;
    else 
        cur_status <= nxt_status;
end

always @(*) begin
    if (a_reset_sync) 
        nxt_status <= AXI_WR_IDLE;
    else begin
    	case(cur_status)
    		AXI_WR_IDLE : begin
    			if (axi_aw_req_en) 
    				nxt_status <= AXI_WR_PRE;
    			else 
    				nxt_status <= cur_status;
    		end
    		AXI_WR_PRE : begin
    			nxt_status <= AXI_WR_DATA;
    		end
    		AXI_WR_DATA : begin
    			if (m_axi_wvalid && m_axi_wready && m_axi_wlast)
    				nxt_status <= AXI_WR_END;
    			else 
    				nxt_status <= cur_status;
    		end
    		AXI_WR_END : begin
    			nxt_status <= AXI_WR_IDLE;
    		end
    		default : nxt_status <= AXI_WR_IDLE;
    	endcase
    end
end

/*------------------------------------------*\
           输出给wr_buffer模块ready信号
\*------------------------------------------*/
always @(*) begin
	axi_aw_ready <= cur_status == AXI_WR_PRE;
	axi_w_ready  <= m_axi_wready;
end

/*------------------------------------------*\
               AXI4写通道的信号
\*------------------------------------------*/

always @(posedge axi_clk) begin
    if (a_reset_sync) 
        m_axi_awvalid <= 0;
    else if (m_axi_awvalid && m_axi_awready) 
        m_axi_awvalid <= 0;
    else if (axi_aw_req_en && axi_aw_ready)
        m_axi_awvalid <= 1;
end

always @(posedge axi_clk) begin
	if (axi_aw_req_en && axi_aw_ready) begin
		m_axi_awaddr <= axi_aw_addr;
		m_axi_awlen  <= axi_aw_burst_len;
	end
	else begin
		m_axi_awaddr <= m_axi_awaddr;
		m_axi_awlen  <= m_axi_awlen;		
	end
end


always @(posedge axi_clk) begin
    if (a_reset_sync) 
        m_axi_wvalid <= 0;
    else if (m_axi_wvalid && m_axi_wready && m_axi_wlast) 
        m_axi_wvalid <= 0;
    else if (axi_w_valid && axi_w_ready)
        m_axi_wvalid <= 1;
    else 
    	m_axi_wvalid <= m_axi_wvalid;
end

always @(posedge axi_clk) begin
    if (a_reset_sync) 
        m_axi_wlast <= 0;
    else if (m_axi_wvalid && m_axi_wready && m_axi_wlast)
        m_axi_wlast <= 0;        
    else if (axi_w_valid && axi_w_ready && axi_w_last) 
        m_axi_wlast <= 1;
end

always @(posedge axi_clk) begin
    if (axi_w_valid && axi_w_ready) 
        m_axi_wdata <= axi_w_data;
    else 
        m_axi_wdata <= m_axi_wdata;
end

endmodule

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

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Description: 读控制
//////////////////////////////////////////////////////////////////////////////////
module rd_ctrl#(
	parameter	AXI_DATA_WIDTH  = 128,
	parameter   AXI_ADDR_WIDTH  = 32,
	parameter   RD_BURST_LENGTH = 4096 
)(
	input                              clk               , //用户端读时钟
	input                              reset             ,
    
    /*-----DDR初始化完成信号,与MIG核交互------------------*/
	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  , //用户读请求忙标志

    /*--------------与rd_buffer模块交互信号---------------*/
    output  reg                        rd_req_en         ,
    output       [7:0]                 rd_burst_length   ,
    output  reg  [AXI_ADDR_WIDTH-1:0]  rd_data_addr      ,
    input                              rd_req_ready         //用来指示rd_buffer模块里面的cmd_fifo是否爆满
);


localparam	MAX_BURST_LENGTH = RD_BURST_LENGTH / (AXI_DATA_WIDTH/8) - 1;

/*--------------------------------------------------*\
				     定义状态机
\*--------------------------------------------------*/
reg	[1:0]	cur_status;
reg	[1:0]	nxt_status;
localparam 	RD_IDLE = 2'b00;
localparam 	RD_REQ  = 2'b01;
localparam 	RD_END  = 2'b10;

/*--------------------------------------------------*\
				     CDC
\*--------------------------------------------------*/
(* dont_touch ="true" *) reg reset_sync_d0;
(* dont_touch ="true" *) reg reset_sync_d1;
(* dont_touch ="true" *) reg reset_sync;

reg			ddr_init_done_d0;
reg			ddr_init_done_d1;
reg			ddr_rd_enable   ;

reg			user_rd_req_d0  ;
reg			user_rd_req_d1  ;
reg         rd_req_trig     ;
wire        user_rd_req_rise;

always @(posedge clk) begin
	reset_sync_d0 <= reset;
	reset_sync_d1 <= reset_sync_d0;
	reset_sync    <= reset_sync_d1;    
end

always @(posedge clk) begin
	ddr_init_done_d0 <= ddr_init_done;
	ddr_init_done_d1 <= ddr_init_done_d0;
	ddr_rd_enable    <= ddr_init_done_d1;
end

always @(posedge clk) begin
	user_rd_req_d0 <= user_rd_req;
	user_rd_req_d1 <= user_rd_req_d0;
end

assign user_rd_req_rise = user_rd_req_d0 & ~user_rd_req_d1;

always @(posedge clk) begin
    if (ddr_rd_enable) 
        rd_req_trig <= user_rd_req_rise; 
    else 
        rd_req_trig <= 0;
end

/*--------------------------------------------------*\
				      状态机
\*--------------------------------------------------*/

always @(posedge clk) begin
    if (reset_sync) 
        cur_status <= RD_IDLE;
    else 
        cur_status <= nxt_status;
end

always @(*) begin
    if (reset_sync) 
        nxt_status <= RD_IDLE;
    else begin
    	case(cur_status)
    		RD_IDLE : begin
    			if (rd_req_trig) 
    				nxt_status <= RD_REQ;
    			else 
    				nxt_status <= cur_status;
    		end
    		RD_REQ : begin
    			if (rd_req_en && rd_req_ready) 
    				nxt_status <= RD_END;
    			else 
    				nxt_status <= cur_status;
    		end
    		RD_END : begin
    			nxt_status <= RD_IDLE;
    		end
    		default : nxt_status <= RD_IDLE;
    	endcase
    end
end

/*--------------------------------------------------*\
				       rd req 
\*--------------------------------------------------*/
always @(posedge clk) begin
    if (rd_req_en && rd_req_ready) 
       rd_req_en <= 0; 
    else if (cur_status == RD_REQ) 
       rd_req_en <= 1; 
    else 
       rd_req_en <= rd_req_en;
end

always @(posedge clk) begin
    if (reset_sync) 
        rd_data_addr <= user_rd_base_addr;
    else if (rd_req_en && rd_req_ready && rd_data_addr >= user_rd_end_addr - RD_BURST_LENGTH) 
        rd_data_addr <= user_rd_base_addr;
    else if (rd_req_en && rd_req_ready)
        rd_data_addr <= rd_data_addr + RD_BURST_LENGTH;
end


assign user_rd_req_busy = cur_status != RD_IDLE;
assign rd_burst_length  = MAX_BURST_LENGTH;

endmodule

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

4.仿真实现

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值