alt

分析

题意整理

本题要求实现同步FIFO,FIFO的位宽和深度可配置。 题解主体

设计FIFO的时候一般需要考虑的有两点:

  1. FIFO的大小 FIFO的大小指就是双端口ram的大小,这个可以根据设计需要来设置。

  2. FIFO空满状态的判断 FIFO空满状态的判断通常有两种方法。

    a、FIFO中的ram一般是双端口ram,所以有独立的读写地址。因此可以一种是设置读,写指针,写指针指向下一个要写入数据的地址,读指针指向下一个要读的地址,最后通过比较读指针和写指针的大小来确定空满状态。

    b、设置一个计数器,当写使能有效的时候计数器加一;当读使能有效的时候,计数器减一,将计数器与ram的size进行比较来判断fifo的空满状态。这种方法设计比较简单,但是需要的额外的计数器,就会产生额外的资源,而且当fifo比较大时,会降低fifo最终可以达到的速度。

本题解采用第二种方式。实现方式如下:用一个fifo_cnt来指示实际写入的数据量 alt

地址指针waddr和raddr比实际地址多一位,最高位用来指示套圈情况。当waddr和raddr的最高位相同时,fifo_cnt = waddr-raddr;当waddr和raddr的最高位相反时,fifo_cnt = DEPTH - raddr[ADDR_WIDTH-1:0] + waddr[ADDR_WIDTH-1:0] 。

如何根据fifo_cnt 的值来判断空满呢?对于空,只要fifo_cnt == 0,即为空,对于满,只要fifo_cnt == DEPTH,即为满。注意,为什么不是fifo_cnt == DEPTH-1呢?假设FIFO深度设计为16,DEPTH=16,那么第16个写数据是写在了waddr=15的地址中,但是当写完第16个数据后,即使winc拉低,waddr也会自动加1,停在waddr = 10000,所以相当于写完数据后的写地址比最后一位数据的存储地址,多加了1,所以就不需要再DEPTH-1了。fifo_cnt = 10000 - 0000 + 0000 = DEPTH。

`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  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    parameter ADDR_WIDTH = $clog2(DEPTH);
/******************************addr bin gen ******************/
    reg    [ADDR_WIDTH:0] waddr;
    reg    [ADDR_WIDTH:0] raddr;
    always@(posedge clk or negedge rst_n) begin: gen_waddr
        if(~rst_n)
            waddr <= 0;
        else if(~wfull && winc)
            waddr <= waddr + 1;
    end
    always@(posedge clk or negedge rst_n) begin: gen_raddr
        if(~rst_n)
            raddr <= 0;
        else if(~rempty && rinc)
            raddr <= raddr + 1;
    end
    
/******************* full empty gen*********************/
    wire    [ADDR_WIDTH:0] fifo_cnt;
    assign fifo_cnt = (waddr[ADDR_WIDTH] == raddr[ADDR_WIDTH]?(waddr-raddr):
                       (DEPTH+waddr[ADDR_WIDTH-1:0]-raddr[ADDR_WIDTH-1:0]));
    
    always@(posedge clk or negedge rst_n) begin: gen_full_empty
        if(~rst_n) begin
            wfull <= 0;
            rempty <= 0;
        end
        else if(fifo_cnt == 0)
            rempty <= 1;
        else if(fifo_cnt == DEPTH)
            wfull <= 1;
        else begin
            rempty <= 0;
            wfull <= 0;
        end
    end
    
    wire    ren;
    wire    wen;
    assign    wen = winc && (~wfull);
    assign    ren = rinc && (~rempty);
    
    dual_port_RAM #(.DEPTH(DEPTH),
                    .WIDTH(WIDTH))
    dual_port_RAM_U0(
        .wclk    (clk),
        .wenc    (wen),
        .waddr   (waddr[ADDR_WIDTH-1:0]),
        .wdata   (wdata),
        .rclk    (clk),
        .renc    (ren),
        .raddr   (raddr[ADDR_WIDTH-1:0]),
        .rdata   (rdata)
    );
            
endmodule