赞
踩
FIFO(First In First Out)是异步数据传输时经常使用的存储器。该存储器的特点是数据先进先出(后进后出)。其实,多位宽数据的异步传输问题,无论是从快时钟到慢时钟域,还是从慢时钟到快时钟域,都可以使用 FIFO 处理。异步FIFO 是指读写时钟不一致,读写时钟是互相独立的。 在现代逻辑设计中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步 FIFO 是这个问题的一种简便、快捷的解决方案,使用异步 FIFO 可以在两个不同时钟系统之间快速而方便地传输实时数据。
控制电路将信号分为写入数据信号、读出数据信号、满信号、空信号、读使能信号、写使能信号、复位信号、时钟信号等。
信号名称 | I/O | 描述 | |
w_clk | I | 写时钟,频率50hz | |
r_clk | I | 读时钟,频率25hz | |
w_rst_n | I | 写复位清零 | 低有效 |
r_rst_n | I | 读复位清零 | 低有效 |
data_in | I | 输入数据信号 | |
wr_en | I | 写使能信号 | 高有效 |
rd_en | I | 读使能信号 | 高有效 |
data_out | O | 输出数据信号 | |
full | O | 满信号 | 高有效 |
empty | O | 空信号 | 高有效 |
典型异步 FIFO 结构图如下所示
双端口SRAM:
FIFO 的存储用双端口 SRAM代替,用来存储写入的数据 data_in 以及读出的输出数据 data_out 。
SRAM 的读写地址每次递增一个地址,保证了写入和读出按先进先出的顺序进行,写和读到最高地址后,重新返回零地址。
满信号、写地址生成电路:
在原理图 SRAM 左侧为满信号以及写地址生成电路。这个电路通过判断写时钟域下,写指针和读指针的关系,然后生成满信号 full,以便停止对 FIFO 进行写操作,防止出现 FIFO 已满还在写的错误行为。
空信号、读地址生成电路:
在原理图 SRAM 右侧为空信号以及读地址生成电路。这个电路通过判断读时钟域下,写指针和读指针的关系,然后生成满信号 empty,以便停止对 FIFO 进行读操作,防止出现 FIFO 已空还在读的错误行为。
将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号,在两个不同时钟域下的指针进行对比,这里就涉及跨时钟域(CDC)的问题,需要打两拍来解决。
跨时钟域问题(CDC):
将读指针同步到写时钟域,读指针可能会不变或增加(由于打拍会向后延迟两个时钟周期),而写指针本来就在写时钟域中,所以不必再次同步,也就是说,写指针和读指针做对比,写指针是真正的写指针,而同步后读指针是小于等于同步前的读指针,这样的作法,会导致满信号提前拉高,影响异步 FIFO 的性能,例如(深度为16,到14就满了),不会发生满了还在写的错误,但是会发生读空,却仍然有数据读出,所以这种情况就造成了FIFO的功能错误。
将写指针同步到读时钟域,写指针可能会不变或增加(由于打拍会向后延迟两个时钟周期),而读指针本来就在读时钟域中,所以不必再次同步,也就是说,写指针和读指针做对比,读指针是真正的读指针,而同步后写指针是小于等于同步前的写指针,这样的作法,会导致 FIFO 已经写满但满信号还没有拉高,造成了FIFO的功能错误。但是不会发生读空,还接着读的现象。
如图所示,将读指针同步到写时钟域,若在地址 2 处,FIFO 已经读空,则实际上读地址已经读到地址 4,会产生读空还读的错误;若在地址 2 处,FIFO 已经写满,则实际上读地址已经读到地址 4,会读空两个地址,只会产生性能的损失。所以进行写满判断时,应把读指针同步到写时钟域。
如图所示,将写指针同步到读时钟域,若在地址 2 处,FIFO 已经写满,则实际上写地址已经写到地址 4,会产生写满还写的错误;若在地址 2 处,FIFO 已经读空,则实际上写地址已经写到地址 4,会写满两个地址,只会产生性能的损失。所以进行读空判断时,应把写指针同步到读时钟域。
在进行跨时钟域处理时,每一个 bit 从 0 到 1 或从1 到 0 的变换都会有产生亚稳态的情况。亚稳态是指触发器无法在某个规定的时间段内到达一个可以确认的状态,如下图所示。
为了减轻产生亚稳态对电路的影响,我们将二进制码转换为格雷码。格雷码是一种循环二进制码或者叫作反射二进制码。格雷码的特点是从一个数变为相邻的一个数时,只有一个数据位发生跳变,由于这种特点,就可以避免二进制编码计数组合电路中出现的亚稳态,并且当格雷码的最高位和次高位相同,其余位相同时认为是读空;当最高位和次高位不同,其余位相同认为是写满。
十进制 | 二进制 | 格雷码 |
---|---|---|
0 | 0000 | 0000 |
1 | 0001 | 0001 |
2 | 0010 | 0011 |
3 | 0011 | 0010 |
4 | 0100 | 0110 |
5 | 0101 | 0111 |
6 | 0110 | 0101 |
7 | 0111 | 0100 |
二进制的最高位作为格雷码的最高位,次高位的格雷码为二进制的高位和次高位相异或得到,其他位与次高位类似。
- module afifo_12_1024
- #(parameter
- DATA_WIDTH = 12,
- DATA_DEPTH = 1024,
- ADDR_WIDTH = 10)
- (
- w_clk,
- w_rst_n,
-
- r_clk,
- r_rst_n,
-
- wr_en,
- rd_en,
- data_in,
-
- full,
- empty,
- data_out
- );
-
- //**********************Port definition***********************//
-
- input wr_en;
- input rd_en;
- input [DATA_WIDTH-1:0] data_in;
-
- input w_clk;
- input w_rst_n;
-
- input r_clk;
- input r_rst_n;
-
- output full;
- output empty;
- output reg [DATA_WIDTH-1:0] data_out;
-
- //******************write address generation******************//
-
- reg [ADDR_WIDTH:0] waddr_ptr;
- always@(posedge w_clk or negedge w_rst_n)
- if(!w_rst_n)
- waddr_ptr <= 'd0;
- else if((full == 'd0) && (wr_en))begin
- if(waddr_ptr >= 'd1023)
- waddr_ptr <= 'd0;
- else
- waddr_ptr <= waddr_ptr + 1'd1;
- end
- else
- waddr_ptr <= waddr_ptr;
- wire [ADDR_WIDTH-1:0] waddr;
- assign waddr= waddr_ptr[ADDR_WIDTH-1:0];
- //******************read address generation*******************//
- reg [ADDR_WIDTH:0] raddr_ptr;
- always@(posedge r_clk or negedge r_rst_n)
- if(!r_rst_n)
- raddr_ptr <= 'd0;
- else if((empty == 'd0) && (rd_en))begin
- if(raddr_ptr >= 'd1023)
- raddr_ptr <= 'd0;
- else
- raddr_ptr <= raddr_ptr + 1'd1;
- end
- else
- raddr_ptr <= raddr_ptr;
-
- wire [ADDR_WIDTH-1:0] raddr;
- assign raddr = raddr_ptr[ADDR_WIDTH-1:0];
-
- //****************Binary conversion Gray code*****************//
-
- wire [ADDR_WIDTH:0] waddr_gray;
- assign waddr_gray = (waddr_ptr >> 1) ^ waddr_ptr;
- wire [ADDR_WIDTH:0] raddr_gray;
- assign raddr_gray = (raddr_ptr >> 1) ^ raddr_ptr;
- //************Cross-clock domain processing(CDC)**************//
-
- reg [ADDR_WIDTH:0] waddr_gray_dly1;
- reg [ADDR_WIDTH:0] waddr_gray_dly2;
- always@(posedge r_clk or negedge r_rst_n)
- if(!r_rst_n)
- {waddr_gray_dly2,waddr_gray_dly1} <= 'd0;
- else
- {waddr_gray_dly2,waddr_gray_dly1} <= {waddr_gray_dly1,waddr_gray};
- reg [ADDR_WIDTH:0] raddr_gray_dly1;
- reg [ADDR_WIDTH:0] raddr_gray_dly2;
- always@(posedge w_clk or negedge w_rst_n)
- if(!w_rst_n)
- {raddr_gray_dly2,raddr_gray_dly1} <= 'd0;
- else
- {raddr_gray_dly2,raddr_gray_dly1} <= {raddr_gray_dly1,raddr_gray};
-
- //****************empty full signal generation****************//
-
- assign full = ((waddr_gray[ADDR_WIDTH:ADDR_WIDTH-1] !== raddr_gray_dly2[ADDR_WIDTH:ADDR_WIDTH-1]) &&
- (waddr_gray[ADDR_WIDTH-2:0] == raddr_gray_dly2[ADDR_WIDTH-2:0]) && (w_rst_n))? 'd1:'d0;
- assign empty = ((raddr_gray == waddr_gray_dly2) && (r_rst_n))? 'd1:'d0;
-
- //***********************write to fifo************************//
-
- integer i;
- reg [ADDR_WIDTH-1:0] sram [0:DATA_DEPTH-1];
- always@(posedge w_clk or negedge w_rst_n)
- if(!w_rst_n)
- for(i = 0; i < DATA_DEPTH;i = i + 1) begin
- sram[i] <= 'd0;
- end
- else if(wr_en && (!full))
- sram[waddr] <= data_in;
- //******************read out to the fifo**********************//
- always@(posedge r_clk or negedge r_rst_n)
- if(!r_rst_n)
- data_out <= 'd0;
- else if(rd_en && (!empty))
- data_out <= sram[raddr];
- else
- data_out <= data_out;
-
- endmodule
- `timescale 1ns/1ps
- `define clk_period 20
- module afifo_12_1024_tb();
-
- reg wr_en;
- reg rd_en;
- reg [11:0] data_in;
-
- reg w_clk;
- reg w_rst_n;
-
- reg r_clk;
- reg r_rst_n;
-
- wire full;
- wire empty;
- wire [11:0] data_out;
-
- afifo_12_1024 u1(
- .w_clk(w_clk),
- .w_rst_n(w_rst_n),
-
- .r_clk(r_clk),
- .r_rst_n(r_rst_n),
-
- .wr_en(wr_en),
- .rd_en(rd_en),
- .data_in(data_in),
-
- .full(full),
- .empty(empty),
- .data_out(data_out)
- );
-
- initial w_clk = 1;
- always # 10 w_clk = ~ w_clk;
-
- initial r_clk = 1;
- always # 20 r_clk = ~ r_clk;
-
- initial begin
- r_rst_n = 'd0;
- w_rst_n = 'd0;
- wr_en = 'd0;
- rd_en = 'd0;
- data_in = 'd0;
-
- #(`clk_period*10+1);
- w_rst_n = 'd1;
- r_rst_n = 'd1;
- #(`clk_period*10);
-
- data_in = 'd0;
-
- repeat(1023)begin
- wr_en = 'd1;
- data_in = data_in + 'd1;
- #(`clk_period);
- end
- wr_en = 'd0;
-
- #(`clk_period*100);
- rd_en = 'd1;
-
- #(`clk_period*3000);
-
- rd_en = 'd0;
- #(`clk_period*10);
- $stop;
- end
- endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。