题意整理

本题要求实现异步FIFO,FIFO的位宽和深度可配置。

题解主体


异步FIFO结构如上图所示

1. 第1部分是双口RAM,用于数据的存储。

2. 第2部分是数据写入控制器

3. 第3部分是数据读取控制器

4. 读指针同步器

   使用写时钟的两级触发器采集读指针,输出到数据写入控制器。

5. 写指针同步器

   使用读时钟的两级触发器采集写指针,输出到数据读取控制器。

本题解采用的空满判断的方式是用格雷码的比较来产生空满信号。


如上图所示,使用4位格雷码作为深度为8的FIFO的读写指针。

将格雷码转换成四位二进制数,使用二进制数低三位作为访问RAM的地址。

与同步FIFO类似,当读写指针相等时,得出FIFO为空


当写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满。

Verilog代码描述如下:


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);


/**********************addr bin gen*************************/

reg [ADDR_WIDTH:0]  waddr_bin;

reg [ADDR_WIDTH:0]  raddr_bin;


always @(posedge wclk or negedge wrstn) begin

       if(~wrstn) begin

              waddr_bin <= 'd0;

       end

       else if(!wfull && winc)begin

              waddr_bin <= waddr_bin + 1'd1;

       end

end

always @(posedge rclk or negedge rrstn) begin

       if(~rrstn) begin

              raddr_bin <= 'd0;

       end

       else if(!rempty && rinc)begin

              raddr_bin <= raddr_bin + 1'd1;

       end

end


/**********************addr gray gen*************************/

wire       [ADDR_WIDTH:0]  waddr_gray;

wire       [ADDR_WIDTH:0]  raddr_gray;

reg [ADDR_WIDTH:0]  wptr;

reg [ADDR_WIDTH:0]  rptr;

assign waddr_gray = waddr_bin ^ (waddr_bin>>1);

assign raddr_gray = raddr_bin ^ (raddr_bin>>1);

always @(posedge wclk or negedge wrstn) begin

       if(~wrstn) begin

              wptr <= 'd0;

       end

       else begin

              wptr <= waddr_gray;

       end

end

always @(posedge rclk or negedge rrstn) begin

       if(~rrstn) begin

              rptr <= 'd0;

       end

       else begin

              rptr <= raddr_gray;

       end

end

/**********************syn addr gray*************************/

reg         [ADDR_WIDTH:0]  wptr_buff;

reg         [ADDR_WIDTH:0]  wptr_syn;

reg         [ADDR_WIDTH:0]  rptr_buff;

reg         [ADDR_WIDTH:0]  rptr_syn;

always @(posedge wclk or negedge wrstn) begin

       if(~wrstn) begin

              rptr_buff <= 'd0;

              rptr_syn <= 'd0;

       end

       else begin

              rptr_buff <= rptr;

              rptr_syn <= rptr_buff;

       end

end

always @(posedge rclk or negedge rrstn) begin

       if(~rrstn) begin

              wptr_buff <= 'd0;

              wptr_syn <= 'd0;

       end

       else begin

              wptr_buff <= wptr;

              wptr_syn <= wptr_buff;

       end

end

/**********************full empty gen*************************/

assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]});

assign rempty = (rptr == wptr_syn);


/**********************RAM*************************/

wire       wen ;

wire ren  ;

wire       wren;//high write

wire [ADDR_WIDTH-1:0]     waddr;

wire [ADDR_WIDTH-1:0]     raddr;

assign wen = winc & !wfull;

assign ren = rinc & !rempty;

assign waddr = waddr_bin[ADDR_WIDTH-1:0];

assign raddr = raddr_bin[ADDR_WIDTH-1:0];


dual_port_RAM #(.DEPTH(DEPTH),

                            .WIDTH(WIDTH)

)dual_port_RAM(

       .wclk (wclk), 

       .wenc (wen), 

       .waddr(waddr[ADDR_WIDTH-1:0]),  //深度对2取对数,得到地址的位宽。

       .wdata(wdata),       //数据写入

       .rclk (rclk),

       .renc (ren),

       .raddr(raddr[ADDR_WIDTH-1:0]),   //深度对2取对数,得到地址的位宽。

       .rdata(rdata)               //数据输出

);


endmodule

参考答案

`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);

/**********************addr bin gen*************************/
reg 	[ADDR_WIDTH:0]	waddr_bin;
reg 	[ADDR_WIDTH:0]	raddr_bin;

always @(posedge wclk or negedge wrstn) begin
	if(~wrstn) begin
		waddr_bin <= 'd0;
	end 
	else if(!wfull && winc)begin
		waddr_bin <= waddr_bin + 1'd1;
	end
end
always @(posedge rclk or negedge rrstn) begin
	if(~rrstn) begin
		raddr_bin <= 'd0;
	end 
	else if(!rempty && rinc)begin
		raddr_bin <= raddr_bin + 1'd1;
	end
end

/**********************addr gray gen*************************/
wire 	[ADDR_WIDTH:0]	waddr_gray;
wire 	[ADDR_WIDTH:0]	raddr_gray;
reg 	[ADDR_WIDTH:0]	wptr;
reg 	[ADDR_WIDTH:0]	rptr;
assign waddr_gray = waddr_bin ^ (waddr_bin>>1);
assign raddr_gray = raddr_bin ^ (raddr_bin>>1);
always @(posedge wclk or negedge wrstn) begin 
	if(~wrstn) begin
		wptr <= 'd0;
	end 
	else begin
		wptr <= waddr_gray;
	end
end
always @(posedge rclk or negedge rrstn) begin 
	if(~rrstn) begin
		rptr <= 'd0;
	end 
	else begin
		rptr <= raddr_gray;
	end
end
/**********************syn addr gray*************************/
reg		[ADDR_WIDTH:0]	wptr_buff;
reg		[ADDR_WIDTH:0]	wptr_syn;
reg		[ADDR_WIDTH:0]	rptr_buff;
reg		[ADDR_WIDTH:0]	rptr_syn;
always @(posedge wclk or negedge wrstn) begin 
	if(~wrstn) begin
		rptr_buff <= 'd0;
		rptr_syn <= 'd0;
	end 
	else begin
		rptr_buff <= rptr;
		rptr_syn <= rptr_buff;
	end
end
always @(posedge rclk or negedge rrstn) begin 
	if(~rrstn) begin
		wptr_buff <= 'd0;
		wptr_syn <= 'd0;
	end 
	else begin
		wptr_buff <= wptr;
		wptr_syn <= wptr_buff;
	end
end
/**********************full empty gen*************************/
assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]});
assign rempty = (rptr == wptr_syn);

/**********************RAM*************************/
wire 	wen	;
wire	ren	;
wire 	wren;//high write
wire [ADDR_WIDTH-1:0]	waddr;
wire [ADDR_WIDTH-1:0]	raddr;
assign wen = winc & !wfull;
assign ren = rinc & !rempty;
assign waddr = waddr_bin[ADDR_WIDTH-1:0];
assign raddr = raddr_bin[ADDR_WIDTH-1:0];

dual_port_RAM #(.DEPTH(DEPTH),
				.WIDTH(WIDTH)
)dual_port_RAM(
	.wclk (wclk),  
	.wenc (wen),  
	.waddr(waddr[ADDR_WIDTH-1:0]),  //深度对2取对数,得到地址的位宽。
	.wdata(wdata),       	//数据写入
	.rclk (rclk), 
	.renc (ren), 
	.raddr(raddr[ADDR_WIDTH-1:0]),   //深度对2取对数,得到地址的位宽。
	.rdata(rdata)  		//数据输出
);

endmodule