异步fifo的一些实现方法过程

1.1异步fifo的产生

1,异步FIFO主要是为了解决跨时钟域传输的操作,一个电路系统中可能出现不同的时钟频率和不同的接口电路,同时异步fifo主要采用格雷码的编码方式进行传输,因为格雷码相邻位之间只有一位发生变化,相反如果采用二进制编码的手段,同一码流之间,可能会出现信号的多位同时发生变化,这对信号的传输会产生非稳态过程,对系统而言是不理想的。同时由于格雷码在0~15的编码方式是关于镜像对称的。同步fifo中用于判断写满的最高位相等的判断,用于异步fifo的判断是不恰当的。应选择最高位和次高位同时进行判断。

1.2异步fifo verilog代码段设计

//异步fifo

module asnyc_fifo #(
    parameter  DATA_WIDTH = 'd8 ,  //FIFO位宽
	 parameter  DATA_DEPTH = 'd16   //FIFO深度
)
(
   //写数据 
   input                            wr_clk ,  //写时钟
   input                            wr_rst_n ,  //复位信号
   input                            wr_en    ,  //写使能信号
   input   [DATA_WIDTH - 1: 0 ]     data_in  ,  // 写入的数据
   
   //读数据
   input                            rd_clk ,  //读时钟
   input                            rd_rst_n , //读复位信号
   input                            rd_en    ,  //读使能信号
   
   output  reg [DATA_WIDTH - 1 :0 ] data_out , // 输出信号
   output                           empty   , // 满标志
   output                           full      //空标志
   
);
//用二维数组实现ram
reg  [DATA_WIDTH - 1 : 0 ] fifo_buffer [DATA_DEPTH - 1 : 0 ] ;

reg [$clog2(DATA_DEPTH ) : 0]       wr_ptr ; //写地址指针,binary
reg [$clog2(DATA_DEPTH ) : 0]       rd_ptr ; //读地址指针,binary

reg [$clog2(DATA_DEPTH ) : 0]       rd_ptr_g_d1 ;  //读指针格雷码在写时钟域下同步一拍
reg [$clog2(DATA_DEPTH ) : 0]       rd_ptr_g_d2 ;  //读指针格雷码在写时钟域下同步两拍

reg [$clog2(DATA_DEPTH ) : 0]       wr_ptr_g_d1 ;  //写指针格雷码在读时钟域下同步一拍
reg [$clog2(DATA_DEPTH ) : 0]       wr_ptr_g_d2 ;  //写指针格雷码在读时钟域下同步两拍


//wire define 

wire [$clog2(DATA_DEPTH) : 0]       wr_ptr_g ; //写地址指针,格雷码
wire [$clog2(DATA_DEPTH) : 0]       rd_ptr_g ; //读地址指针,格雷码
wire [$clog2(DATA_DEPTH) - 1 : 0 ]  wr_ptr_true ; //真实的写地址指针,作为写ram的地址
wire [$clog2(DATA_DEPTH) - 1 : 0 ]  rd_ptr_true ; //真实的读地址指针,作为读ram的地址


//地址指针从二进制转换到格雷码
assign  wr_ptr_g = wr_ptr ^ (wr_ptr >> 1) ;
assign  rd_ptr_g = rd_ptr ^ (rd_ptr >> 1) ; 

//读写ram地址赋值
assign wr_ptr_ture = wr_ptr[$clog2(DATA_DEPTH ) - 1 : 0] ; 
assign rd_ptr_true = rd_ptr[$clog2(DATA_DEPTH) - 1 : 0] ;

//写操作,更新写地址
always @ (posedge wr_clk or negedge wr_rst_n) begin 
    if (!wr_rst_n) begin 
	    wr_ptr <= 0 ;
	end
	else  if (!full && wr_en) begin 
	    wr_ptr <= wr_ptr + 1'b1 ;
	    fifo_buffer[wr_ptr_ture] <= data_in ;
	end
end

//将读指针的格雷码同步到写时钟域,来判断是否写满
always @ (posedge wr_clk or negedge wr_rst_n ) begin 
    if (!wr_rst_n) begin 
	    rd_ptr_g_d1 <=  0;   // 寄存一拍
		rd_ptr_g_d2 <=  0;   // 寄存两拍
	end
	else begin
	    rd_ptr_g_d1 <= rd_ptr_g ;   //寄存一拍
		rd_ptr_g_d2 <= rd_ptr_g_d1 ; //寄存两拍
	end
end
//读操作,更新读地址
always @ (posedge wr_clk or negedge wr_rst_n) begin 
    if (!wr_rst_n) begin 
	    rd_ptr <= 0 ;
	end
	else  if (!full && wr_en) begin 
	    data_out <= fifo_buffer[rd_ptr_true] ;
		rd_ptr <= rd_ptr + 1'b1 ;
	end
end

//将写时钟的格雷码同步到读时钟域上,来判断是否读空
always @ (posedge rd_clk or negedge rd_rst_n ) begin 
    if (!rd_rst_n) begin 
	    wr_ptr_g_d1 <=  0;   // 寄存一拍
		wr_ptr_g_d2 <=  0;   // 寄存两拍
	end
	else begin
	    wr_ptr_g_d1 <= wr_ptr_g ;   //寄存一拍
		wr_ptr_g_d2 <= wr_ptr_g_d1 ; //寄存两拍
	end
end

//更新指示信号
//当所有位都相等时,读指针追到写指针,fifo被读空了
assign    empty = (wr_ptr_g_d2 == rd_ptr_g) ? 1'b1 : 1'b0 ;

//当高位相反,且其他位相等的时候,写指针超过读指针,FIFO被写满
//同步后的读指针格雷码高两位取反,再拼接上余下位
assign     full = ( wr_ptr_g == {~(rd_ptr_g_d2[$clog2(DATA_DEPTH) : $clog2(DATA_DEPTH) - 1]) ,rd_ptr_g_d2[$clog2(DATA_DEPTH) - 2 : 0 ]}) ? 1'b1 : 1'b0 ;

endmodule  

 总结,参考孤独的单刀博主文章 : 

FIFO:写满:将读时钟同步到写时钟域上,来判断是否写满;

FIFO:  读空:将写时钟同步到读时钟域上,来判断是否读空;

同步两拍+格雷码编码的目的是为了消除电路中可能出现的亚稳态;

1.3牛客网vl_45刷题总结

描述

请根据题目中给出的双口RAM代码和接口描述,实现异步FIFO,要求FIFO位宽和深度参数化可配置。

电路的接口如下图所示。

双口RAM端口说明:

端口名

I/O

描述

wclk

input

写数据时钟

wenc

input

写使能

waddr

input

写地址

wdata

input

输入数据

rclk

input

读数据时钟

renc

input

读使能

raddr

input

读地址

rdata

output

输出数据

同步FIFO端口说明:

端口名

I/O

描述

wclk

input

写时钟

rclk

input

读时钟

wrstn

input

写时钟域异步复位

rrstn

input

读时钟域异步复位

winc

input

写使能

rinc

input

读使能

wdata

input

写数据

wfull

output

写满信号

rempty

output

读空信号

rdata

output

读数据

双口RAM代码如下,可在本题答案中添加并例化此代码。

module dual_port_RAM #(parameter DEPTH = 16,
   parameter WIDTH = 8)(
 input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata      //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end 

endmodule  

输入描述:

    input                     wclk    , 
    input                     rclk    ,   
    input                     wrstn    ,
    input                    rrstn    ,
    input                     winc    ,
    input                      rinc    ,
    input         [WIDTH-1:0]    wdata    

输出描述:

    output wire                wfull    ,
    output wire                rempty    ,
    output wire [WIDTH-1:0]    rdata

 

`timescale 1ns/1ns

/***************************************RAM*****************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/***************************************AFIFO*****************************************/
module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	rdata
);
	parameter ADDR_WIDTH = $clog2(DEPTH);
/***************************************Bin Logic*****************************************/
	reg [ADDR_WIDTH:0] wptr_bin;
	reg [ADDR_WIDTH:0] rptr_bin;
	wire [ADDR_WIDTH:0] wptr_bin_next;
	wire [ADDR_WIDTH:0] rptr_bin_next;
	
	assign wptr_bin_next = wptr_bin + (winc & (!wfull));
	assign rptr_bin_next = rptr_bin + (rinc & (!rempty));
	
/***************************************Gray Logic*****************************************/
	reg [ADDR_WIDTH:0] wptr_gray;
	reg [ADDR_WIDTH:0] rptr_gray;
	wire [ADDR_WIDTH:0] wptr_gray_next;
	wire [ADDR_WIDTH:0] rptr_gray_next;
	
	assign wptr_gray_next = wptr_bin ^ (wptr_bin >> 1);
	assign rptr_gray_next = rptr_bin ^ (rptr_bin >> 1);
	
	always @ (posedge wclk or negedge wrstn) begin
		if (!wrstn) begin
			{wptr_bin,wptr_gray} <= 'd0;
		end
		else
			{wptr_bin,wptr_gray} <= {wptr_bin_next, wptr_gray_next};
	end
	
	always @ (posedge rclk or negedge rrstn) begin
		if (!rrstn) begin
			{rptr_bin,rptr_gray} <= 'd0;
		end
		else
			{rptr_bin,rptr_gray} <= {rptr_bin_next, rptr_gray_next};
	end
/***************************************Full Logic*****************************************/
	reg [ADDR_WIDTH:0] rptr_gray_wr,rptr_gray_wr2;
	
	always @ (posedge wclk or negedge wrstn) begin
		if (!wrstn) begin
			{rptr_gray_wr,rptr_gray_wr2} <= 'd0;
		end
		else
			{rptr_gray_wr2,rptr_gray_wr} <= {rptr_gray_wr,rptr_gray};
	end
	
	assign wfull = wptr_gray == {~rptr_gray_wr2[ADDR_WIDTH:ADDR_WIDTH-1], rptr_gray_wr2[ADDR_WIDTH-2:0]};
/***************************************Empty Logic*****************************************/
	reg [ADDR_WIDTH:0] wptr_gray_rr,wptr_gray_rr2;
	
	always @ (posedge rclk or negedge rrstn) begin
		if (!rrstn) begin
			{wptr_gray_rr2,wptr_gray_rr} <= 'd0;
		end
		else
			{wptr_gray_rr2,wptr_gray_rr} <= {wptr_gray_rr,wptr_gray};
	end
	
	assign rempty =  rptr_gray == wptr_gray_rr2;
/***************************************Dual RAM*****************************************/
	wire wenc, renc;
	wire [ADDR_WIDTH-1:0] raddr, waddr;
	
	assign wenc = winc & !wfull;
	assign renc = rinc & !rempty;
	
	assign raddr = rptr_bin[ADDR_WIDTH-1:0];
	assign waddr = wptr_bin[ADDR_WIDTH-1:0];
	
	dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH)) 
				u_dual_port_RAM (
				.wclk(wclk), 
				.rclk(rclk), 
				.wenc(wenc), 
				.renc(renc),
				.raddr(raddr),
				.waddr(waddr),
				.wdata(wdata),
				.rdata(rdata)
				);
/***************************************AFIFO*****************************************/    
endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值