VL22 同步FIFO

描述

根据题目提供的双口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

描述

clk

input

时钟

rst_n

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

题意整理

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

题解主体

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

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

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

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

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

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

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

示意图如下

地址指针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。

Verilog代码描述如下:

 

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值