双端口SRAM:
用来存储上游节点写入的数据wdata,下游节点用rdata将其读出。
SRAM的读写地址采用了每次只递增1的机制,保证了写入和读出按顺序进行,写和读到最高地址后,重新返回零地址。
满信号生成电路
在上游节点和SRAM之间有一个满信号生成电路。
这个电路通过判断写时钟域下,写指针和读指针的关系,然后实时生成满信号wfull,以通知上游节点停止写操作。
空信号生成电路
在下游节点和SRAM之间有一个空信号生成电路,
这个电路通过判断读时钟域下,写指针和读指针的关系,然后实时生成空信号rempty,以通知下游节点停止读操作。
注意:
将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号,因此,这里就涉及到如何处理信号传输的亚稳态问题。
使用同步器把信号传递到对面的时钟域进行同步处理,可以采用同步器(由2~3级FF组成)对单bit的信号进行同步操作。
判断读指针rptr和写指针wptr的关系(格雷码):
读指针和写指针在地址位中添加一个额外的位(extra bit)。
“空”状态的判断:
读指针和写指针二者完全相等(包括MSB)
- 读指针:rptr[n-1]
- 写指针:wptr[n-1]
- empty=(rptr == wptr)
“满”的判断:
- 读指针:rptr[n-1]
- 写指针:wptr[n-1]
-
- full= (rptr[n-1] != wptr[n-1] )&&(rptr[n-2:0] == wptr[n-2:0])
二进制码转化为格雷码:
从最右边第一位开始,依次将每一位与左邻 一位异或(XOR),作为对应格雷码该位的值,最左边一位不变。
转换公式:
- 二进制码:B[n-1:0]
- 格雷码: G[n-1:0]
-
- //二进制转换成格雷码
- G[n-1] = B[n-1]
- G[i] = B[i+1]^B[i] //i=0、1、2、、、n-2
- //格雷码的第i位的值等于二进制第i位和i+1位的异或
例子:
格雷码转化为二进制码:
从左边第二位起,将每位与左边一位解码后的 值异或(XOR),作为该位解码后的值(最左边一位依然不变)。
转换公式:
- 二进制码:B[n-1:0]
- 格雷码: G[n-1:0]
-
- //格雷码转换成二进制
- G[n-1] = B[n-1]
- B[i] = G[i]^B[i+1] //i=0、1、2、、n-2
例子:
假设,读指针rptr地址为3,在写时钟控制下,同步两个时钟进入写时钟域,即信号rptr_sync_2。
这时,在写时钟域下,刚好满足“满”的条件,生成满信号。
但是,此时rptr的值已经更新为5。
即在FIFO认为自己已经满的时候,读地址,又从里面读走了两个数据。实际FIFO并没有真正的满,只是接近满“虚满”。
结论:
总结:
异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,此机制保证 了FIFO在空满极限情况下,依然留有余量,存在一定的冗余空间。
这种方法使得FIFO不会发生写满溢出、读空多读的情况。
以写地址同步到读时钟域为例,示意图:
- //write interface pointer
- //生成写地址指针
- always@(posedge wr_clk or negedge wr_rst_n_i )
- if(!wr_rst_n_i)
- wr_ptr<= {ADDR_WIDTH+1}{1'b0};
- else if(wr_en_i && !full_o)
- wr_ptr <=wr_ptr +1'b1;
- else
- wr_ptr <=wr_ptr;
-
- //##############二进制码->格雷码###############
- //#######格雷码写地址
- assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
- always@(*)begin
- gray_wr_ptr_next= gray_wr_ptr;
- end
-
-
- //--------------------------------------------------------------------------------
- //read interface pointer
- //生成读地址指针
- always@(posedge rd_clk or negedge rd_rst_n_i )
- if(!rd_rst_n_i)
- rd_ptr <= {ADDR_WIDTH+1}{1'b0};
- else if(rd_en_i && !empty_o)
- rd_ptr <=rd_ptr +1'b1;
- else
- rd_ptr <=rd_ptr;
- //##############二进制码->格雷码###############
- //#######格雷码读地址
- assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
- always@(*)begin
- gray_rd_ptr_next= gray_rd_ptr;
- end
- //格雷码写地址同步两级 传递到读时钟域
- always@(posedge rd_clk or negedge rd_rst_n)
- begin
- if(! rd_rst_n)
- begin
- gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
- gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
- end
- else
- begin
- gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
- gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
- end
- end
-
- //格雷码读地址同步两级 传递到写时钟域
- always@(posedge wr_clk or negedge wr_rst_n)
- begin
- if(!wr_rst_n)
- begin
- gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
- gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
- end
- else
- begin
- gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
- gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1;
- end
- end
- //#####空满标志产生
- //满标志
- assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
- always@(posedge wr_clk or negedge wr_rst_n)
- begin
- if(!wr_rst_n)
- full_o <=1'b0;
- else
- full_o <= full_comb;
- end
-
- //空标志
- assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
- always@(posedge rd_clk or negedge rd_rst_n)
- begin
- if(!rd_rst_n)
- empty_o <=1'b0;
- else
- empty_o <= empty_comb;
- end
- //read data
- always@(posedge rd_clk or negedge rd_rst_n_i)
- if(!rd_rst_n_i)
- begin
- rd_data_o<= 0;
- end
- esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空
- rd_data_o <= RAM[rd_ptr];
- //write data
- interger i;
- always@(posedge wr_clk or negedge wr_rst_n_i )
- if(!wr_rst_n_i)
- begin
- for(i=0;i<FIFO_DEPTH;i=i+1)begin
- RAM[i] <= 0;
- end
- end
- esle if(wr_en_i && !full_comb)
- RAM[wr_ptr] <= wr_data_i;
- module async_fifo
- #(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=3)
- (
- //write interface
- input wire wr_clk,
- input wire wr_rst_n_i,
- input wire wr_en_i,
- input wire [DATA_WIDTH-1:0] wr_data_i,
- //read interface
- input wire rd_clk,
- input wire rd_rst_n_i,
- input wire rd_en_i,
- output reg [DATA_WIDTH-1:0] wr_data_o,
- //flags
- output full_o,
- output empty_o
- );
-
- wire FIFO_DEPTH = 1<< ADDR_WIDTH;//2^3=8
-
- // RAM definition
- reg [DATA_WIDTH-1:0] RAM [0:FIFO_DEPTH-1];
- //fifo width :DATA_WIDTH 32bit; fifo depth:FIFO_DEPTH 8
-
- //读写指针
- reg [ADDR_WIDTH:0] wr_ptr; //写指针
- reg [ADDR_WIDTH:0] rd_ptr; //读指针
-
-
- //写指针 二进制->格雷码
- //格雷码写地址
- reg [ADDR_WIDTH:0] gray_wr_ptr; //格雷码写地址
- reg [ADDR_WIDTH:0] gray_wr_ptr_next;//格雷码写地址同步1拍
-
- // 格雷码写地址同步到读时钟
- reg [ADDR_WIDTH:0] gray_wr2rd_ptr_1; //格雷码写地址同步到读时钟同步1拍
- reg [ADDR_WIDTH:0] gray_wr2rd_ptr_2; //格雷码写地址同步到读时钟同步2拍
-
-
- //读地址 二进制->格雷码
- //格雷码读地址
- reg [ADDR_WIDTH:0] gray_rd_ptr; //格雷码读地址
- reg [ADDR_WIDTH:0] gray_rd_ptr_next; //格雷码读地址同步1拍
-
- //格雷码读地址同步到写时钟
- reg [ADDR_WIDTH:0] gray_rd2wr_ptr_1;//格雷码读地址同步到写时钟同步1拍
- reg [ADDR_WIDTH:0] gray_rd2wr_ptr_2;//格雷码读地址同步到写时钟同步2拍
-
- //组合逻辑空满标志
- wire full_comb;
- wire empty_comb;
-
- //########读写地址指针产生####################
- //write interface pointer
- always@(posedge wr_clk or negedge wr_rst_n_i )
- if(!wr_rst_n_i)
- wr_ptr<= {ADDR_WIDTH+1}{1'b0};
- else if(wr_en_i && !full_o)
- wr_ptr <=wr_ptr +1'b1;
- else
- wr_ptr <=wr_ptr;
- //read interface pointer
- always@(posedge rd_clk or negedge rd_rst_n_i )
- if(!rd_rst_n_i)
- rd_ptr <= {ADDR_WIDTH+1}{1'b0};
- else if(rd_en_i && !empty_o)
- rd_ptr <=rd_ptr +1'b1;
- else
- rd_ptr <=rd_ptr;
-
- //##############二进制码->格雷码###############
- //#######格雷码写地址
- assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
- always@(*)begin
- gray_wr_ptr_next= gray_wr_ptr;
- end
-
- //格雷码写地址同步两级 传递到读时钟域
- always@(posedge rd_clk or negedge rd_rst_n)
- begin
- if(! rd_rst_n)
- begin
- gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
- gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
- end
- else
- begin
- gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
- gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
- end
- end
-
- //#######格雷码读地址
- //#######格雷码读地址
- assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
- always@(*)begin
- gray_rd_ptr_next= gray_rd_ptr;
- end
- //格雷码读地址同步两级 传递到写时钟域
- always@(posedge wr_clk or negedge wr_rst_n)
- begin
- if(!wr_rst_n)
- begin
- gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
- gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
- end
- else
- begin
- gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
- gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1;
- end
- end
-
- //#####空满标志产生
- //满标志
- assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
- always@(posedge wr_clk or negedge wr_rst_n)
- begin
- if(!wr_rst_n)
- full_o <=1'b0;
- else
- full_o <= full_comb;
- end
-
- //空标志
- assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
- always@(posedge rd_clk or negedge rd_rst_n)
- begin
- if(!rd_rst_n)
- empty_o <=1'b0;
- else
- empty_o <= empty_comb;
- end
-
-
-
- //##########################################
- //write data
- interger i;
- always@(posedge wr_clk or negedge wr_rst_n_i )
- if(!wr_rst_n_i)
- begin
- for(i=0;i<FIFO_DEPTH;i=i+1)begin
- RAM[i] <= 0;
- end
- end
- esle if(wr_en_i && !full_comb)
- RAM[wr_ptr] <= wr_data_i;
- //read data
- always@(posedge rd_clk or negedge rd_rst_n_i)
- if(!rd_rst_n_i)
- begin
- rd_data_o<= 0;
- end
- esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空
- rd_data_o <= RAM[rd_ptr];
-
- endmodule
参考