异步FIFO

异步FIFO介绍 : https://www.cnblogs.com/IClearner/p/6579754.html

 

//==============================================================================
// Author      : chauncey_wu
// Email       : chauncey_wu@163.com
//==============================================================================
// File        : Asychronous_fifo.v
// Create      : 
// Last edit   : 
// Editor      : 
// Revision    : V1.0
// Description : 
//==============================================================================
`timescale    1ns/1ns 
module asyc_fifo	#(
	//默认生成位宽为4,深度为4的fifo
	parameter	WDATA_WIDTH = 4, //读数据位宽
				RDATA_WIDTH = 4, //写数据位宽
				DEEP_WIDTH  = 4, //FIFO深度,存疑:fifo深度和地址位宽是什么关系
				ALMOST_DEEP = 1, //读写地址相差为1则认为,写满/读空
				TCQ         = 2
	)
	(
	//写时钟域
	input   wire                    wclk,
	input   wire                    wrst_n,
	input   wire                    winc,   //外部winc,   //外部输入的写使能信号
	input	wire [WDATA_WIDTH-1:0]	wdata,
	output  reg  					wfull,
	output  reg                     wfull_almost,
	//读时钟域
	input   wire                    rclk,
	input   wire                    rrst_n,
	input   wire                    rinc,
	output  wire [RDATA_WIDTH-1:0]  rdata,
	output  reg                     rempty,
	output  reg                     rempty_almost
	);
	//中间变量
	reg	 	[DEEP_WIDTH:0]		waddr;//写时钟域地址,最高位位回卷标志位,0表示没有回卷,1表示有回卷
	reg  	[DEEP_WIDTH:0]     raddr;
	wire 	[DEEP_WIDTH:0]   	wptr; //要同步到读时钟域的写指针,最高位为回卷标志位
	wire 	[DEEP_WIDTH:0]     rptr; //要同步到写时钟域的读指针,格雷码形式
	reg  	[DEEP_WIDTH:0]		wq1_rptr; //
	reg  	[DEEP_WIDTH:0]		wq2_rptr; //格雷码读地址同步到写时钟域
	reg  	[DEEP_WIDTH:0]		rq1_wptr; //
	reg  	[DEEP_WIDTH:0]		rq2_wptr; 
	reg  	[DEEP_WIDTH:0]		wq2_rptr_b; //格雷码读地址同步到写时钟域,再转化为二进制码 
	reg  	[DEEP_WIDTH:0]		rq2_wptr_b;  
	reg 	[DEEP_WIDTH:0]	    i; //格雷码转转为二进制码时的位数计数
	reg 	[DEEP_WIDTH:0]	    j;
	wire                    wclken; //写RAM使能信号
	wire                    rclkwn;
	
	
	//------------------读时钟域---------------------------//
	
	//二进制读地址转化为格雷码,并同步到写时钟域,在转换为二进制码与实际写地址比较,产生满信号
	assign rptr = (raddr>>1)^raddr;    //格雷码的值只需要在原来的二进制的基础上右移一位再与原来的二进制值异或即可得到
	
	always @(posedge wclk)
	begin
		if (wrst_n)
		begin 
			wq1_rptr <= #TCQ {DEEP_WIDTH{1'b0}};
			wq2_rptr <= #TCQ {DEEP_WIDTH{1'b0}};
		end 
		else
		begin 
			wq1_rptr <= #TCQ rptr;
			wq2_rptr <= #TCQ wq1_rptr; 
		end 
	end 
	
	always @(wq2_rptr)  //格雷码转二进制码
	begin 
		wq2_rptr_b[4] = #TCQ wq2_rptr[4];
		wq2_rptr_b[3] = #TCQ wq2_rptr[3]^wq2_rptr[4];
		wq2_rptr_b[2] = #TCQ wq2_rptr[2]^wq2_rptr[3]^wq2_rptr[4];
		wq2_rptr_b[1] = #TCQ wq2_rptr[1]^wq2_rptr[2]^wq2_rptr[3]^wq2_rptr[4];
		wq2_rptr_b[0] = #TCQ wq2_rptr[0]^wq2_rptr[1]^wq2_rptr[2]^wq2_rptr[3]^wq2_rptr[4];
	end 
	//--------------------写满信、将满号的产生------------------//
	always @(posedge wclk)
	begin 
		if (wrst_n)
		begin 
			wfull 		 <= #TCQ 1'b0; 
			wfull_almost <= #TCQ 1'b0;
			waddr 		 <= #TCQ {DEEP_WIDTH{1'b0}};
		end 
		else if (winc)
		begin											//标志位不同且地址为相同则为写满,
			if ((waddr[DEEP_WIDTH-1:0] == wq2_rptr_b[DEEP_WIDTH-1:0]) && (waddr[DEEP_WIDTH] != wq2_rptr_b[DEEP_WIDTH]))
			begin 
				wfull 		 <= #TCQ 1'b1;
			    wfull_almost <= #TCQ 1'b1;
			end 										//标志位相同且地址为相差为1则为将满
			else if ((waddr[DEEP_WIDTH-1:0]-wq2_rptr_b[DEEP_WIDTH-1:0] == ALMOST_DEEP) && (waddr[DEEP_WIDTH] != wq2_rptr_b[DEEP_WIDTH]))
			begin 
				wfull 		  <= #TCQ 1'b0;
			    wfull_almost  <= #TCQ 1'b1;
				waddr 		  <= #TCQ waddr + 1'b1;
			end 
			else 
			begin	
				waddr         <= #TCQ  waddr + 1'b1;
			end 	
		end 
	end 
	//写满信号与写使能信号生成对DPRAM的写使能信号
	assign wclken =  winc & !wfull;	
	
	//***************************************************************************************************//
	
	//------------------写时钟域---------------------------//
	
	assign wptr =  (waddr>>1)^waddr;   
	
	always @(posedge rclk)
	begin
		if (wrst_n)
		begin 
			rq1_wptr <= #TCQ {DEEP_WIDTH{1'b0}};
			rq2_wptr <= #TCQ {DEEP_WIDTH{1'b0}};
		end 
		else
		begin 
			rq1_wptr <= #TCQ wptr;
			rq2_wptr <= #TCQ rq1_wptr; 
		end 
	end 
	
	always @(rq2_wptr) // 格雷码转二进制码
	begin 
		rq2_wptr_b[4] = #TCQ rq2_wptr[4];
		rq2_wptr_b[3] = #TCQ rq2_wptr[3]^rq2_wptr[4];
		rq2_wptr_b[2] = #TCQ rq2_wptr[2]^rq2_wptr[3]^rq2_wptr[4];
		rq2_wptr_b[1] = #TCQ rq2_wptr[1]^rq2_wptr[2]^rq2_wptr[3]^rq2_wptr[4];
		rq2_wptr_b[0] = #TCQ rq2_wptr[0]^rq2_wptr[1]^rq2_wptr[2]^rq2_wptr[3]^rq2_wptr[4];
	end 
	//--------------------读空、将空号的产生------------------//
	always @(posedge rclk)
	begin 
		if (rrst_n)
		begin 
			rempty        <= #TCQ 1'b0; 
			rempty_almost <= #TCQ 1'b0;
			raddr         <= #TCQ {DEEP_WIDTH{1'b0}};
		end 
		//else if (rinc)
		//begin											//标志位xiang同且地址为相同则为读空,
		else if (raddr[DEEP_WIDTH:0] == rq2_wptr_b[DEEP_WIDTH:0])
			begin 
				rempty        <= #TCQ 1'b1;
			    rempty_almost <= #TCQ 1'b1;
			end 										//标志位相同且地址为相差为1则为将读空
			else if ((raddr[DEEP_WIDTH:0] - rq2_wptr_b[DEEP_WIDTH:0]) == ALMOST_DEEP)
			begin 
				rempty        <= #TCQ 1'b0;
			    rempty_almost <= #TCQ 1'b1;
				raddr         <= #TCQ raddr + 1'b1;
			end 
			else 
			begin	
				rempty        <= #TCQ 1'b0;
				raddr         <= #TCQ raddr + 1'b1;
			end 	
		//end 
	end 
	//写满信号与写使能信号生成对DPRAM的写使能信号
	assign rclken =  rinc & !rempty;	

	//------------------------------------------------------//
	//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
	blk_mem_gen_0 simple_DPRAM (
		.clka(wclk),    // input wire clka
		.ena(wclken),      // input wire ena
		.wea(wclken),      // input wire [0 : 0] wea
		.addra(waddr),  // input wire [2 : 0] addra
		.dina(wdata),    // input wire [7 : 0] dina
		
		.clkb(rclk),    // input wire clkb
		.enb(rclken),      // input wire enb
		.addrb(raddr),  // input wire [2 : 0] addrb
		.doutb(rdata)  // output wire [7 : 0] doutb
	);
	
endmodule 	




简单的仿真 TESTBENCH : 

//timescale
`timescale    1ns/1ns 
module  tb_module    #(
                    parameter DATA_WIDTH = 4    )();
//the Internal motivation variable(register) and output wire 
    wire                        wfull           ;
    wire                        wfull_almost    ;
    reg    [DATA_WIDTH-1:0]     wdata           ;
    reg                         winc            ;
    reg                         wclk            ;
    reg                         wrst_n          ;
    //读时钟域信号
    wire                        rempty          ;
    wire                        rempty_almost   ;
    wire    [DATA_WIDTH-1:0]    rdata           ;
    reg                         rinc            ;
    reg                         rclk            ;
    reg                         rrst_n          ;


//the  External motivation storage variable


//Sub module signal,example: wire [1:0] xxx == xxx_inst.xxx_inst.xxx;

// Global variable initialization ,such as 'clk'、'rst_n'
initial begin
    #0 rrst_n = 1;
       wrst_n = 1 ;
	   
       wclk = 0;
       rclk = 0;
     //  rinc = 0 ;
     //  winc = 0 ;
       //wdata = 0 ;
    #20 rrst_n = 0 ;
    #25 wrst_n = 0 ;
end 

//cloclk signal generation
always #15 rclk = ~rclk ;
always #10 wclk = ~wclk ;


//Internal motivation variable initialization
//initial begin   
//end 
// winc generate    
always @(posedge wclk or wrst_n)begin
    if( wrst_n == 1'b1 )begin 
        winc = 1'b0;
    end 
    else if( wfull_almost )
        winc = 1'b0;
    else 
        winc = 1'b1 ;
end 
    
// rinc generate    
always @(posedge rclk or rrst_n)begin
    if( rrst_n == 1'b1 )begin
        rinc = 1'b0 ;
    end 
    else if( rempty )
        rinc = 1'b0;
    else 
        rinc = 1'b1 ;
end 

// wdata 
always @(posedge wclk or negedge wrst_n)begin
    if( wrst_n == 1'b1 )begin
        wdata = 4'd0 ;
    end 
    else if( winc )begin 
        wdata = wdata + 1'b1;
    end 
end 



//Cases of sub module xxxx xxxx_inst(.(),.(), ... ,.());
asyc_fifo Async_FIFO_inst(
    //写时钟域信号
    .wfull         ( wfull           ),
    .wfull_almost  ( wfull_almost    ),
    .wdata         ( wdata           ),
    .winc          ( winc            ),
    .wclk          ( wclk            ),
    .wrst_n        ( wrst_n          ),
    //读时钟域信号
    .rempty        ( rempty          ),
    .rempty_almost ( rempty_almost   ),
    .rdata         ( rdata           ),
    .rinc          ( rinc            ),
    .rclk          ( rclk            ),
    .rrst_n        ( rrst_n          )
    );

// Internal motivation variable assignment  using task or random
/*  example                                                
task data_assign(xx);                               | task    rand_bit();        
    integer    xx,xx,...;                              |     integer i;
    begin                                           |     begin
        for( ; ; )begin                                |         for(i=0; i<255; i=i+1)begin
        @(posedge clock)                            |             @(posedge sclk);                  
        Internal motivation variable <= xxxxx;      |             Internal motivation variable <={$random} %2;
        end                                         |          end    
    end                                             |        end    
endtask                                             |     endtask                             
*/            

endmodule                                            

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值