题意整理

本题要求实现同步FIFOFIFO的位宽和深度可配置。

题解主体

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

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

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

 

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

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

本题解采用第二种方式。实现方式如下:

用一个fifo_cnt来指示实际写入的数据量

示意图如下:

地址指针waddrraddr比实际地址多一位,最高位用来指示套圈情况。当waddrraddr的最高位相同时,fifo_cnt = waddr-raddr;当waddrraddr的最高位相反时,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深度设计为16DEPTH=16,那么第16个写数据是写在了waddr=15的地址中,但是当写完第16个数据后,即使winc拉低,waddr也会自动加1,停在waddr = 10000,所以相当于写完数据后的写地址比最后一位数据的存储地址,多加了1,所以就不需要再DEPTH-1了。fifo_cnt = 10000 - 0000 + 0000 = DEPTH

Verilog代码描述如下:

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    


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
    if(~rst_n) begin
        waddr <= 'd0;
    end 
    else if(!wfull && winc)begin
        waddr <= waddr + 1'd1;
    end
end
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        raddr <= 'd0;
    end 
    else if(!rempty && rinc)begin
        raddr <= raddr + 1'd1;
    end
end

/**********************full empty gen*************************/
wire        [ADDR_WIDTH : 0]    fifo_cnt;

assign fifo_cnt = (waddr[ADDR_WIDTH] == raddr[ADDR_WIDTH]) ? (waddr[ADDR_WIDTH:0] - raddr[ADDR_WIDTH:0]) :
                  (DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0]);
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        wfull <= 'd0;
        rempty <= 'd0;
    end 
    else if(fifo_cnt == 'd0)begin
        rempty <= 1'd1;
    end
    else if(fifo_cnt == DEPTH)begin
        wfull <= 1'd1;
    end
    else begin
        wfull <= 'd0;
        rempty <= 'd0;
    end 
end

/**********************RAM*************************/
wire     wen    ;
wire    ren    ;
wire     wren;//high write
assign wen = winc & !wfull;
assign ren = rinc & !rempty;

dual_port_RAM #(.DEPTH(DEPTH),
                .WIDTH(WIDTH)
)dual_port_RAM(
    .wclk (clk),  
    .wenc (wen),  
    .waddr(waddr[ADDR_WIDTH-1:0]),  //深度对2取对数,得到地址的位宽。
    .wdata(wdata),           //数据写入
    .rclk (clk), 
    .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       ,input [WIDTH-1:0] wdata               ,input rclk     ,input renc     ,input [$clog2(DEPTH)-1:0] raddr       ,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     if(~rst_n) begin         waddr <= 'd0;     end      else if(!wfull && winc)begin         waddr <= waddr + 1'd1;     end
end
always @(posedge clk or negedge rst_n) begin     if(~rst_n) begin         raddr <= 'd0;     end      else if(!rempty && rinc)begin         raddr <= raddr + 1'd1;     end
end

/**********************full empty gen*************************/
wire        [ADDR_WIDTH : 0]    fifo_cnt;

assign fifo_cnt = (waddr[ADDR_WIDTH] == raddr[ADDR_WIDTH]) ? (waddr[ADDR_WIDTH:0] - raddr[ADDR_WIDTH:0]) :
                  (DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0]);
always @(posedge clk or negedge rst_n) begin     if(~rst_n) begin         wfull <= 'd0;         rempty <= 'd0;     end      else if(fifo_cnt == 'd0)begin         rempty <= 1'd1;     end     else if(fifo_cnt == DEPTH)begin         wfull <= 1'd1;     end     else begin         wfull <= 'd0;         rempty <= 'd0;     end 
end

/**********************RAM*************************/
wire     wen    ;
wire    ren    ;
wire     wren;//high write
assign wen = winc & !wfull;
assign ren = rinc & !rempty;

dual_port_RAM #(.DEPTH(DEPTH),                 .WIDTH(WIDTH)
)dual_port_RAM(     .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


如果关于此次题单有问题或者反馈意见,欢迎加入牛客用户反馈群沟通~