深度为32、数据位宽为32bit的同步FIFO(可通过修改parameter参数改成任意深度位宽)

FIFO介绍

FIFO即First In First out,顾名思义就是先进先出,他是用来存储数据的,先存入的数据会被先读出,数据读出的顺序和数据写入的顺序相同,所以一般用作数据缓冲。例如FIFO前级模块的数据处理速率为100Mbps,后级模块的处理速率为50Mbps,这时候显然不能直接将前级数据直连到后级模块,此时就可以利用FIFO将数据暂存起来,后级模块处理时便从中读出并且不影响数据写入,也不用担心后级模块处理数据过程中数据终端的情况,因为写入速率是快于读出的速率。

而FIFO又分为同步FIFO和异步FIFO。同步FIFO指的是读写使用同一个人时钟,异步FIFO指的就是读写时钟不同。显然同步FIFO先对于异步FIFO实现起来更加简单一些,不需要考虑跨时钟域的亚稳态问题。

同步FIFO设计思路

FIFO其实就是一个存储器,那么如何实现先进先出呢?首先我们需要明确读写是独立的, 而数据的存储一般需要进行寻址,因此我们可以将地址分为读地址和写地址,每次写入数据时写地址自增,读地址不变;每次读数据时读地址自增,写地址不变,以此来完成对读写数据当前顺序的确定。

既然是存储器那必然有一个存储容量,达到存储容量后就不能写入数据了,都则会造成数据丢失。那如何确定何时达到了存储容量呢?假设FIFO的深度为100,那当写地址为99时(地址从0开始增加)就已经达到存储容量吗?显然不是,因为在这个过程中可能数据也会被读出,就好比泳池注水和排水的过程。如果写数据的过程中有数据读出,那么当写地址增加到99后,下一个写地址又会变为0(因为读数据顺序和写地址顺序一样,先从哪写的就先从哪出,既然数据被读出那一定是地址0的数据先被读出,然后是地址1的数据以此类推),之后又开始自增。那何时才是真的写满FIFO了呢,,可以将读写比作两个人围着操场跑步,两人从同一起点出发,他们之间差的距离就是目前已经存储的数据个数,每次跑完一圈回到起点的过程就是上面读写地址从99变为0,只有当前面的那个人快看到慢的那个人的背影,即将超过跑的慢的人时,这时候就已经比他快了一圈,而操场的一圈就代表FIFO的深度(存储容量),这时候就已经存满了。
在这里插入图片描述

如何标志FIFO满的状态

根据上面的描述,当写地址要追上读地址的时候就是写满FIFO了,那可以用已经写的地址数量(注意不是写地址,而是已经写的数量,写地址只有0-99)减去已经读的地址数量就好了。这样比较简单,但是有一个问题,因为读写数据可能一直在进行,我们得给多大的位宽才行呢?这个问题是没有办法的,如果读写速率相同并且一直在进行,那么已经写的地址数量/读的地址数量的位宽是没有边界的。但是我们需要明确,写地址只会比读地址最多快一圈,因为FIFO写满后我们就不能让数据继续写入了。因此只需要对地址位宽扩展一位,就能够得到FIFO中现存数据的数量了。下面举个例子说明:

比如读写地址数量从0-31,即FIFO的深度为32,那么读写地址的范围为5’b0-5’b11111(十进制为0-31),那么对其进行扩展一位,地址就变为6’b0-6’b011111,当写地址写满一圈后继续增加时就变为6’b100000,而读地址还处在第一圈,假设其地址为6’b0xxxxx,其实扩展的这一位就代表目前读写地址处在哪一圈,当写地址继续增加到6’b111111时,再增加就变为6’b000000,假设读地址也读完了第一圈,那读地址为6’b1xxxxx,这时候最高位又不一样,就说明最高位代表了读写地址有没有在同一轮次(也就是跑步的两人是不是都在跑第x圈),因此这时候就可以通过扩展的这一位和写地址、读地址来得到FIFO中现存数据的个数。如果最高位不同,那写地址比读地址快一圈,原本的5位写地址+FIFO深度减去原本的5位读地址,就是现存数据的数量,当现存数据等于FIFO深度时,就拉高写满信号,停止数据写入。(也可以直接利用扩展后的写地址减去读地址得到FIFO现存数据量,即使写地址小于读地址,差值是负数,但是用补码表示依然可以得到正确的数量如写地址为6’b0 00011,读地址为6’b1 00011,写地址减去读地址得到6’b100000,此时读写指针处于同一位置,也就是说写入的数量为FIFO的深度,此时FIFO写满了)

代码实现

FIFO设计代码如下:

module FIFO_sync
    #(
        parameter DEPTH = 32,
        parameter DEPTH_WIDTH = 5,
        parameter WIDTH = 32

        )
    (
    input clk,
    input rst_n,

    input wr_en,
    input [WIDTH-1:0]wr_data,

    input rd_en,
    output reg [WIDTH-1:0]rd_data,
    output reg rd_data_valid,

    output full,
    output almost_full,
    output empty,
    output almost_empty,
    output [DEPTH_WIDTH:0]data_count

    );

wire [DEPTH_WIDTH-1:0] wr_addr;
reg  [DEPTH_WIDTH:0]   wr_addr_bit;

wire [DEPTH_WIDTH-1:0] rd_addr;
reg  [DEPTH_WIDTH:0]   rd_addr_bit;

reg [DEPTH-1:0]memory[WIDTH-1:0];
integer i;

//写数据
//写地址
always @(posedge clk or negedge rst_n) 
begin
    if(~rst_n) 
        begin
            wr_addr_bit <= {(DEPTH_WIDTH+1){1'b0}};
        end 
    else if(wr_en)
        begin
            wr_addr_bit <= wr_addr_bit+1'b1;
        end
    else
        begin
            wr_addr_bit <= wr_addr_bit;
        end
end

assign wr_addr=wr_addr_bit[DEPTH_WIDTH-1:0];
//将数据写入存储单元
always @(posedge clk or negedge rst_n)
begin 
    if(!rst_n) 
    begin
        for(i=0;i<DEPTH;i=i+1)
            memory[i]<= 0;
    end 
    else if(wr_en) 
    begin
        for(i=0;i<DEPTH;i=i+1)
        begin
            if(i==wr_addr)
                memory[i]<= wr_data;
            else
                memory[i]<=memory[i];
         end
    end
    else
        for(i=0;i<DEPTH;i=i+1)
            memory[i]<= memory[i];
end


//读数据
//读地址
always @(posedge clk or negedge rst_n) 
begin
    if(~rst_n) 
        begin
            rd_addr_bit <= {(DEPTH_WIDTH+1){1'b0}};
        end 
    else if(rd_en)
        begin
            rd_addr_bit <= rd_addr_bit+1'b1;
        end
    else
        begin
            rd_addr_bit <= rd_addr_bit;
        end
end

assign rd_addr=rd_addr_bit[DEPTH_WIDTH-1:0];
always @(posedge clk or negedge rst_n)
begin 
    if(!rst_n) 
    begin
        rd_data<={WIDTH{1'b0}};
        rd_data_valid<=1'b0;
    end 
    else if(rd_en) 
    begin
        for(i=0;i<DEPTH;i=i+1)
        begin
            if(i==rd_addr)
                rd_data<= memory[i];
         end
         rd_data_valid<=1'b1;
    end
    else
    begin
         rd_data <={WIDTH{1'b0}};
         rd_data_valid<=1'b0;
     end
end

//FIFO中已存数据量
assign data_count=wr_addr_bit-rd_addr_bit;
//assign data_count=(wr_addr_bit[DEPTH_WIDTH]==rd_addr_bit[DEPTH_WIDTH])?(wr_addr_bit-rd_addr_bit):(wr_addr_bit+DEPTH-rd_addr_bit);
//满标志/将满标志判断
assign full= (data_count==DEPTH)|(data_count==DEPTH-1 && wr_en && (!rd_en));
assign almost_full= (data_count==DEPTH-1)|(data_count==DEPTH-2 && wr_en && (!rd_en));
//空标志/将空标志判断
assign empty=(data_count==0)|(data_count==1 && (!wr_en) && rd_en);
assign almost_empty=(data_count==1)|(data_count==2 && (!wr_en) && rd_en);


endmodule

仿真激励如下:

module tb_fifo_sync();

reg clk;
reg rst_n;
reg wr_en;
reg [31:0] wr_data;

reg rd_en;
wire [31:0] rd_data;
wire rd_data_valid;

wire full;
wire almost_full;
wire empty;
wire almost_empty;
wire [5:0]data_count;

integer seed = 100;


always #5 clk=!clk;
initial
begin
    clk=0;
    rst_n=0;
    wr_en=0;
    wr_data=32'b0;
    rd_en=0;
    #100
    rst_n=1;
    #55
    repeat(40)
    @(posedge clk)
    begin
        if(!full)
        begin
            wr_en<=1;
            wr_data<=$random(seed);
        end
        else  
        begin
            wr_en<=0;
            wr_data<=0; 
        end
    end
    #105
    repeat(40)
    @(posedge clk)
    begin
        if(!empty)
        begin
            rd_en<=1;
        end
        else  
        begin
            rd_en<=0;
        end
    end

    #1000
    $stop;

end


        FIFO_sync #(
            .DEPTH(32),
            .DEPTH_WIDTH(5),
            .WIDTH(32)
        ) inst_FIFO_sync (
            .clk           (clk),
            .rst_n         (rst_n),
            .wr_en         (wr_en),
            .wr_data       (wr_data),
            .rd_en         (rd_en),
            .rd_data       (rd_data),
            .rd_data_valid (rd_data_valid),
            .full          (full),
            .almost_full   (almost_full),
            .empty         (empty),
            .almost_empty  (almost_empty),
            .data_count    (data_count)
        );

endmodule
仿真结果

1.数据写入仿真
在这里插入图片描述
在这里插入图片描述

2.数据读出仿真
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
下面是一个Verilog HDL语言设计的同步FIFO电路,其中读、写数据位宽为8bit深度32。该电路使用双字节指针(读指针和写指针)来实现读和写操作,并使用一个计数器来跟踪FIFO中存储的数据数量。当FIFO为空时,读指针等于写指针,并且读空指示信号被置位。当FIFO已满时,写指针等于读指针并且写满指示信号被置位。 ```verilog module fifo( input clk, // 时钟信号 input rst_n, // 复位信号,低电平有效 input we, // 写使能信号 input [7:0] din, // 写数据信号 input re, // 读使能信号 output reg [7:0] dout,// 读数据信号 output reg full, // 写满指示信号 output reg empty // 读空指示信号 ); parameter DEPTH = 32; // FIFO深度 parameter WIDTH = 8; // 数据位宽 reg [7:0] mem [0:DEPTH-1]; // 存储FIFO数据的RAM reg [4:0] wr_ptr; // 写指针 reg [4:0] rd_ptr; // 读指针 reg [4:0] count; // FIFO中当前存储的数据数量 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; full <= 0; empty <= 1; end else begin if(we && !full) begin // 写操作 mem[wr_ptr] <= din; wr_ptr <= wr_ptr + 1; count <= count + 1; if(wr_ptr == rd_ptr) // FIFO已满 full <= 1; empty <= 0; end else if(re && !empty) begin // 读操作 dout <= mem[rd_ptr]; rd_ptr <= rd_ptr + 1; count <= count - 1; if(rd_ptr == wr_ptr) // FIFO为空 empty <= 1; full <= 0; end end end endmodule ``` 在该Verilog HDL语言设计的FIFO电路中,读写操作都是同步的,即它们都在时钟沿到来时执行。具体实现方式如下: - 写操作:当写使能信号we为高电平时,写入的数据被存储到RAM中,并且写指针wr_ptr递增。如果FIFO已满(即写指针wr_ptr等于读指针rd_ptr),则写满指示信号full被置位。 - 读操作:当读使能信号re为高电平时,RAM中的数据被读取出来,并且读指针rd_ptr递增。如果FIFO为空(即读指针rd_ptr等于写指针wr_ptr),则读空指示信号empty被置位。 在每个时钟周期的末尾,计数器用于更新FIFO中存储的数据数量count。如果该数量等于或超过FIFO深度,则写满指示信号full被置位。如果该数量为零,则读空指示信号empty被置位。 需要注意的是,该Verilog HDL语言设计的FIFO电路中的读写操作都是同步的,因此需要确保读写时钟信号相同并具有相同的相位。此外,还需要确保RAM存储器具有足够的带宽来支持读写操作,以确保FIFO电路的性能和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值