【IC设计】异步FIFO

同步FIFO:读写操作共用一个时钟。

异步FIFO:读操作时钟和写操作时钟相互独立。

因此异步FIFO的端口相对于同步FIFO时钟和复位信号有所变化:异步FIFO的输入端口有:时钟与复位信号(wr_clk、wr_rstn、rd_clk、rd_rst_n)、读写使能信号(wr_en、rd_en)、写数据信号(wr_data)。

异步FIFO的输出端口有:读数据信号(rd_data)、判空信号(fifo_empty)、判满信号(fifo_full)

异步FIFO内部一共有五个模块:

 1.存储模块

使用双端口RAM进行建模,数据写入和读出都是对这个RAM进行操作。

2、3.读指针同步模块and写指针同步模块

异步FIFO由于读写不是同一个时钟,如果还像同步FIFO那样直接将读指针和写指针比较的话,会产生亚稳态问题,因为两个指针所遵循的时钟是异步的。

1.为什么会产生亚稳态?

已知:需要将读指针和写指针比较来产生空满信号,其中空信号用来控制读,满信号用来控制写。所以需要在写控制端得到读控制端的读指针,来产生判空信号。需要在读控制端得到写控制端的写指针,来产生判满信号。

由于读控制端和写控制端不是同一个时钟,倘若,读指针传给写控制端的时机不对,没有满足建立时间或保持时间,就会产生亚稳态,导致判满信号输出错误;同理,假若写指针传递到读控制端的时机不对,没有满足时序,就会产生亚稳态;

2.如何解决跨时钟传输的亚稳态问题?

采用两级同步的D触发器,也就是图中的读指针同步模块和写指针同步模块。

读指针同步模块:在写时钟域下的两级触发器采集读指针,输出数据到写控制端。

写指针同步模块:在读时钟与下的两级触发器采样写指针,输出数据到读控制端。

4、5.写控制端and读控制端

写控制端主要完成三个功能:1.如果非满且写有效,就将wr_data写入二进制写指针对应的RAM中。2.将二进制写指针转换成格雷码写指针。3.将读指针同步模块传过来的格雷码读指针格雷码写指针相比较,用于判满。

读控制端同理,也要完成三个功能:1。如果非空且读有效,就将RAM中二进制读指针指向的内容输出给rd_data。2.将二进制读指针转换为格雷码读指针。3.将格雷码读指针与写指针同步模块传过来的格雷码写指针相比较,用于判空。

第一个功能比较好理解,就是普通的RAM写入读出操作。

第二个功能中,为什么要用格雷码指针呢?

将一个二进制数据从一个时钟域同步到另一个时钟域很容易出问题,因为二进制数的所有位都可能发生变化,比如上一个读指针是4’b1111,下一个读指针及变成4‘b0000了。位数变化太多,在同步的时候产生亚稳态的概率会大大增加。而格雷码具有相邻两位只有一位数据变化的特性,因此可以将二进制指针转换为格雷码指针,进而降低亚稳态发生的概率。

二进制数据转换为格雷码数据:将二进制数据右移一位在与该二进制数相异或就得到了格雷码数据

assign wr_ptr_g=wr_ptr^(wr_ptr>>1);//wr_ptr_g是格雷码写指针,wr_ptr是二进制写指针。

第三个功能中,如何使用格雷码指针来进行判空判满操作呢?

已知在同步FIFO中,我们用的是二进制指针,通过扩展位来判断空满的。现在在异步FIFO中我们为了降低亚稳态,不仅采用了二级同步,还采用了格雷码指针。其实判空就是读指针追上了写指针,而判满就是写指针追上了读指针。同样是分为四种情况,只不过将二进制指针转换成了格雷码指针,四个图目前没空画,直接给出结论:

判断读空时:读时钟域的格雷码指针和被同步到读时钟域的写指针每一位完全相同;

判断写满时:写时钟域的格雷码指针和被同步到写时钟域的读指针高两位不相同,其余各位完全相同;

异步FIFO设计代码

`timescale 1ns/1ns
module ASY_FIFO #(parameter DEPTH=8,
                   WIDTH=8
                   )
        (input wr_clk,
         input wr_rstn,
         input rd_clk,
         input rd_rstn,

         input wr_en,
         input rd_en,
         input [WIDTH-1:0] wr_data,
    
         output reg [WIDTH-1:0] rd_data,
         output reg fifo_full,
         output reg fifo_empty
);

/******************************1.写控制端&读控制端**************************/
//1.完成读写操作
reg [$clog2(DEPTH):0]wr_ptr,rd_ptr;     //二进制指针,用于读写RAM。$clog2()函数表示求以2为底的对数
reg [WIDTH-1:0] fifo [DEPTH-1:0];     //RAM建模

//1.1写操作
always@(posedge wr_clk or negedge wr_rstn)begin
    if(!wr_rstn)
       wr_ptr<=0;
    else if(wr_en && !fifo_full) begin
       fifo[wr_ptr[$clog2(DEPTH)-1:0]]<=wr_data;
       wr_ptr<=wr_ptr+1;
    end
end

//1.2读操作
always@(posedge rd_clk or negedge rd_rstn)begin
    if(!rd_rstn)
       rd_ptr<=0;
    else if(rd_en && !fifo_empty) begin
       fifo[rd_ptr[$clog2(DEPTH)-1:0]]<=rd_data;
       rd_ptr<=rd_ptr+1;
    end
end

//2.完成格雷码转换
wire [$clog2(DEPTH):0] wr_ptr_g, rd_ptr_g;
assign wr_ptr_g=wr_ptr^(wr_ptr>>1);
assign rd_ptr_g=rd_ptr^(rd_ptr>>1);

//3.判断空满,此处由于需要用到时钟同步模块的输出信号,因此,下面写一下同步模块的代码,判空判满放在最后

/***************************2.读指针时钟同步and写指针同步*******************/
reg [$clog2(DEPTH):0] wr_ptr_gr,wr_ptr_grr;//分别为一级触发后的读指针,和二级触发后的读指针
reg [$clog2(DEPTH):0] rd_ptr_gr,rd_ptr_grr;

//2.1写指针同步
always@(posedge rd_clk or negedge rd_rstn)begin
    if(!rd_rstn)begin
       wr_ptr_gr<=0;
       wr_ptr_grr<=0;
     end
    else begin
       wr_ptr_gr<=wr_ptr_g;
       wr_ptr_grr<=wr_ptr_gr;
    end
end
//2.1读指针同步
always@(posedge wr_clk or negedge wr_rstn)begin
    if(!wr_rstn)begin
       rd_ptr_gr<=0;
       rd_ptr_grr<=0;
     end
    else begin
       rd_ptr_gr<=rd_ptr_g;
       rd_ptr_grr<=rd_ptr_gr;
    end
end

/**************************3.判空判满**********************/
//3.1判满
always@(posedge wr_clk or negedge wr_rstn)begin
    if(!wr_rstn)begin
        fifo_full<=0;
     end
    else 
   
 if((wr_ptr_g[$clog2(DEPTH):$clog2(DEPTH)-1]!=rd_ptr_grr[$clog2(DEPTH):$clog2(DEPTH)-1])&&(wr_ptr_g[$clog2(DEPTH)-2:0]==rd_ptr_grr[$clog2(DEPTH)-2:0]))
   fifo_full<=1;
end

//3.2判空
always@(posedge rd_clk or negedge rd_rstn)begin
    if(!rd_rstn)begin
        fifo_empty<=0;
     end
    else 
    if(wr_ptr_grr[$clog2(DEPTH):0]==rd_ptr_g[$clog2(DEPTH):0])
    fifo_empty<=1;
end
endmodule

虚空与虚满

参考:FIFO设计-异步FIFO篇 - 知乎 

IC基础(一):异步FIFO原理与代码实现 - 你好24h - 博客园

IC基础课:异步FIFO的"假"满空和"真"满空 - 知乎

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
异步FIFO是一种基于时序异步电路的存储器,它的原理是通过两个独立的时钟域来实现数据的读写操作。异步FIFO的优点是具有高效、低功耗、可靠性高等特点,可以在多种应用场景中使用。 以下是一个简单的异步FIFO的代码参考: ```verilog module async_fifo ( input clk1, clk2, rst, input wr_en, rd_en, input [DATA_WIDTH-1:0] wr_data, output [DATA_WIDTH-1:0] rd_data, output empty, full ); parameter DEPTH = 16; parameter DATA_WIDTH = 8; reg [DATA_WIDTH-1:0] mem [0:DEPTH-1]; reg [4:0] wr_ptr, rd_ptr; reg [4:0] count; assign empty = (count == 0); assign full = (count == DEPTH); always @(posedge clk1 or posedge rst) begin if (rst) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; end else if (wr_en && !full) begin mem[wr_ptr] <= wr_data; wr_ptr <= wr_ptr + 1; count <= count + 1; end end always @(posedge clk2 or posedge rst) begin if (rst) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; end else if (rd_en && !empty) begin rd_data <= mem[rd_ptr]; rd_ptr <= rd_ptr + 1; count <= count - 1; end end endmodule ``` 以上代码是一个简单的8位异步FIFO,其中包含了两个时钟域,分别用于写入和读取操作。在写入时钟域中,当写使能信号wr_en为高电平且FIFO未满时,将写入数据wr_data存储到FIFO中,并将写指针wr_ptr加1,同时计数器count加1。在读取时钟域中,当读使能信号rd_en为高电平且FIFO非空时,将读指针rd_ptr加1,同时计数器count减1,并将读取数据rd_data从FIFO中取出。 需要注意的是,由于异步FIFO存在时序异步电路的特点,因此在设计时需要特别注意时序约束和时序分析,以确保电路的正确性和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值