FIFO小总结

  • 什么是FIFO?

FIFO:first in,first out;数据先进先出,后进后出,是一种传统的顺序执行方法,本质上是一种双口数据缓存器,没有外部地址(由内部指针递增来充当数据地址),只能顺序写入,顺序读取,因此不能写入或读取指定地址里的数据。

  • 为什么需要FIFO?

在系统设计时,包含很多工作在不同时钟频率下的元件,如处理器、外设等,可能有自己的时钟振荡器,不同模块处理速度有快有慢,但是每个模块各自时钟不间歇,如果模块之间有数据交互,会造成计算紊乱,这也就是跨时钟域要解决的问题,当数据量很大,数据传输速率要求高,需要匹配不同传输速率的系统时,FIFO是处理此类问题的优解。

  • FIFO与串口的区别

Uart全称为通用异步收发器(Universal Asynchronous Receiver/Transmitter),是一种通用串行数据总线,先将接收到的并行数据转成串行进行传输,然后再将串行转成并行数据,在两个uart设备之间通信,指定相同传输速率(波特率)、起始位结束位等,遵循同一通信协议。

FIFO本质上只是一个缓冲器,如果CPU处理uart数据绰绰有余,可以不用FIFO的帮助,如果在复杂系统中,CPU工作量太大,那么CPU在处理别的任务时,就会时常被收发数据中断,导致CPU工作效率很低,这时通过FIFO来缓冲,存下若干数据再集中处理就很有必要了。

FIFO和uart可以联合工作,提高收发效率。

  • FIFO的分类
  1. 同步FIFO

读取与写入的时钟相同,可以用计数器或读写指针来判断空满

  1. 异步FIFO

读取与写入的时钟不同,需要用到同步器、gray码来减少亚稳态的产生,用指针的gray来判断空满,避免使用二进制指针比较空满,这会导致指针值取样错误。

  • 异步(双时钟)FIFO主要参数

宽度:FIFO存储的每个数据的位宽

深度:FIFO存储数据的个数

写满标志:满标志拉高,则无法继续写入

                  若[写域写指针(gray)== 写域读指针(gray)],则full==1

读空标志:空标志拉高,则无法继续读取

                  若[读域读指针(gray)== 读域写指针(gray)],则null==1

写指针(写域):每存入一个数据,写指针递增1,

读指针(读域):每读取一个数据,读指针递增1

写指针(读域):将写域写指针转化成gray码同步到读域

读指针(写域):将读域读指针转化成gray码同步到写域

指针位数要比FIFO深度多一位用来判断空满!

产生可靠的空满标志是设计FIFO的关键!

  • gray码与二进制

格雷码是一种绝对编码方式,它的循环、单步特性消除了随机取数时出现重大误差的可能,是一种可靠的、错误最小化的编码方式,在相邻位间转换时,只有一位产生变化,出错可能大大降低。

        框与绿框里的格雷码体现了极美的对称性,最高位MSB红框为0,绿框为1,剩下的3位LSB相同。

转化公式:

bnary to Gray

g(3) = b(3) ^ 0

g(2) = b(3) ^ b(2)

g(1) = b(2) ^ b(1)

g(0) = b(1) ^ b(0)

g(n) = b(n+1) ^ b(n)

Gray to Binary

b(3) = g(3)

b(2) = g(3) ^ g(2)

b(1) = g(3) ^ g(2) ^ g(1)

b(0) = g(3) ^ g(2) ^ g(1) ^ g(0)

  • 空满判断(gray)

FIFO读空条件:write pointer == read pointer

FIFO写满条件

        1、FIFO的深度==2的幂次

        假设深度为8的FIFO一开始就只写不读,写满8个后,写指针gray码为4’b1100,此时读指针是4’b0000; 再假设读指针停在4’b0011,写指针写到4‘b1111,

        则此时FIFO写满。可以读写指针看到高两位相反,剩下位数相同。因此写满标志条件: (写域写指针高两位==~写域读指针高两位)&&(写域写指针剩余LSB==写域读指针剩余LSB)

          2、FIFO的深度!=2的幂次

        若FIFO深度为7,则首尾向内收缩一位,即4’b0001与4‘b1001,这样可以保证只有最高位取反,其余相等,深度为6同理,如红色箭头所示,收缩两位。

  • 写在最后

        仿真时,如果写时钟快于读时钟,仿真图里将写指针同步到读域时,发现漏读了指针,不要惊慌!这个问题肯定会发生,基本无法解决,但是不会对读取数据产生影响,因为同步写指针到读域时,写指针还在按自己的时钟递增,读域采到的写指针数值肯定不会大于此刻写域的写指针。

        这个主要影响判断读空的条件,可能会产生假空的状态,而实际上FIFO并没有空,此时读数据就会停止,直到空标志拉低,再继续读即可。

        同理,如果读时钟快于写时钟,可能会出现假满状态,也不会影响读数据。

代码

module fifo #(
    parameter WSIZE = 8,     //the width of fifo
    parameter DSIZE = 5      //the depth of fifo
)
(
    input sys_rst_n,
    
    //write
    input wclk,                    //clk of writing
    input w_en,                    //en of writing
    input wire [WSIZE-1:0]wdata,    //data of writing
    output w_full,                 //the signal of writing fully

    //read
    input rclk,                    //clk of reading
    input r_en,                    //en of reading
    output reg [WSIZE-1:0]rdata,   //data of reading
    output r_null,                 //the signal of reading nully
    output reg r_valid             //Read the data validly
);


//buffer
reg [WSIZE-1:0]buffer[(1<<DSIZE)-1:0];          //Pay attention to this writing

//reg of write
wire [DSIZE:0]gwptr;         //gray code of Writing pointer
reg [DSIZE:0]bwptr,          //binary  code of Writing pointer
               bwptr_rclk,     //binary  code of Writing pointer of reading clk
               gwptr_wclk_1,   //gray code of Writing pointer for hitting 1 beat
               gwptr_rclk_1,   //gray code of reading pointer for hitting 1 beat
               gwptr_rclk_2;   //gray code of reading pointer for hitting 2 beat


//reg of read
wire [DSIZE:0]grptr;         //gray code of reading pointer
reg [DSIZE:0]brptr,          //binary  code of reading pointer
               brptr_wclk,     //binary  code of reading pointer of writing clk
               grptr_rclk_1,   //gray code of reading pointer for hitting 1 beat
               grptr_wclk_1,   //gray code of Writing pointer for hitting 1 beat
               grptr_wclk_2;   //gray code of Writing pointer for hitting 2 beat


//b to g
assign gwptr = (bwptr >> 1) ^ bwptr;             //write pointers to convert to Gray code
assign grptr = (brptr >> 1) ^ brptr;             //read pointers to convert to Gray code

//write the pointer Gray code in the read field to play two beats
always @(posedge rclk, negedge sys_rst_n) begin
    if(~sys_rst_n) begin
        gwptr_rclk_1 <= 0;
        gwptr_rclk_2 <= 0;
    end
    else begin
        gwptr_rclk_1 <= gwptr;
        gwptr_rclk_2 <= gwptr_rclk_1;
    end
end

//read the pointer Gray code in the write field to play two beats
always @(posedge wclk, negedge sys_rst_n) begin
    if(~sys_rst_n) begin
        grptr_wclk_1 <= 0;
        grptr_wclk_2 <= 0;
    end
    else begin
        grptr_wclk_1 <= grptr;
        grptr_wclk_2 <= grptr_wclk_1;
    end
end

//Judge full
assign w_full = ((gwptr[DSIZE:DSIZE-1] == ~grptr_wclk_2[DSIZE:DSIZE-1]) && (gwptr[DSIZE-2:0] == grptr_wclk_2[DSIZE-2:0]));
//Judge null
assign r_null = (grptr == gwptr_rclk_2)? 1'd1 : 1'd0;

//write pointer
always @(posedge wclk, negedge sys_rst_n) begin
    if(~sys_rst_n) begin
        bwptr <= 0;
    end
    else if(~w_full && w_en) begin
        bwptr <= bwptr + 1;
    end
    else begin 
        bwptr <= bwptr;
    end
end

//read pointer
always @(posedge rclk, negedge sys_rst_n) begin
    if(~sys_rst_n) begin
        brptr <= 0;
    end
    else if(~r_null && r_en) begin
        brptr <= brptr + 1;
    end
    else begin 
        brptr <= brptr;
    end
end

//module  memory ();
initial begin
$readmemb("C:/Users/86152/Desktop/FIFO/CODE/doc/memory.txt", buffer);
end


//write data
always @(posedge wclk, negedge sys_rst_n) begin
    if(~w_full && w_en) begin
        buffer[bwptr[DSIZE-1:0]] <= wdata;              //!This expression is ture
    end
end

//read data
always @(posedge rclk, negedge sys_rst_n) begin
    if(~r_null && r_en) begin
        rdata <= buffer[brptr[DSIZE-1:0]]; 
        r_valid <= 1'd1;            
    end
    else begin
        r_valid <= 1'd0;
        rdata <= rdata;
    end
end

endmodule

tb

`timescale 1ns/1ns
module fifo_tb;


//SIZE
parameter WSIZE = 8;     //the width of fifo
parameter DSIZE = 5;      //the depth of fifo

//system
reg sys_rst_n;

//write
reg  wclk;                     //clk of writing
reg w_en;                      //en of writing
reg [WSIZE-1:0]wdata;          //data of writing
wire w_full;                   //the signal of writing fully
//read
reg rclk;                     //clk of reading
reg r_en;                     //en of reading
wire [WSIZE-1:0]rdata;         //data of reading
wire r_null;                  //the signal of reading nully
wire r_valid;                  //Read the data validly


initial begin
    sys_rst_n <= 1'b0;
    //clk
    wclk <= 1'b0;
    rclk <= 1'b0;
    //en
    w_en <= 1'b0;
    r_en <= 1'b0;

    #10
    sys_rst_n <= 1'b1;
    w_en <= 1'b1;                         //just write
    r_en <= 1'b0;

    #64 
    w_en <= 1'd0;                        //write fully and then read completely
    r_en <= 1'b1;

    #192
    w_en <= 1'd1;                         //just write
    r_en <= 1'b0;

    #40
    w_en <= 1'd1;                         //write and read
    r_en <= 1'b1;

    #36
    w_en <= 1'd0;                         //just read
    r_en <= 1'b1;

    #40
    w_en <= 1'd1;                         //just 
    r_en <= 1'b1;
end

//wclk
always #1 wclk <= ~wclk;

//rclk
always #3 rclk <= ~rclk;

//wdata
always @(posedge wclk, negedge sys_rst_n) begin
    if(~sys_rst_n) begin
        wdata <= 8'b000_1111;
    end
    else begin
        wdata <= wdata + 8'd1;
    end
end

fifo fifo_tb(
    .sys_rst_n(sys_rst_n),
    .wclk(wclk),
    .w_en(w_en),
    .wdata(wdata),
    .w_full(w_full),
    .rclk(rclk),
    .r_en(r_en),
    .rdata(rdata),
    .r_null(r_null),
    .r_valid(r_valid)
);

endmodule

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值