赞
踩
阅读本文前,建议先阅读下面几篇文章:
同步FIFO
二进制转格雷码的实现
在上篇文章同步FIFO中简要介绍了FIFO的基本概念以及同步FIFO的实现。本篇文章将重点介绍异步FIFO的工作原理以及硬件实现。
异步FIFO的读写时钟不同,FIFO的读写需要进行异步处理,异步FIFO常用于多bit数据跨时钟域处理。异步FIFO一般有复位rst_n、读端口和写端口。读端口一般包括读时钟(rd_clk)、读使能(rd_en)、读数据(data_out)、读空(empty)。写端口一般包括写时钟(wr_clk)、写使能(wr_en)、写数据(data_in)、写满(wr_full)。
实现异步FIFO难点仍在于如何得到读空和写满信号。由于异步FIFO的读写具有不同的时钟,读写独立,但是空满信号的判决仍需要通过读写地址(多bit数据)来实现,这里涉及到了多bit数据的跨时钟域处理,当然这里无法使用异步FIFO来实现这个跨时钟处理,毕竟我们就是为了做异步FIFO。多bit跨时钟处理还可以通过改变编码方式来降低亚稳态的发生概率。当读地址由4’b0111向4’b1000变化时,所有位都需要变化,如果写时钟恰好在地址变化时采样,写时钟得到的读地址是不确定的(为0000~1111中任意一个),因此为了降低该亚稳态的发生概率,地址采用格雷码编码。格雷码每次只变化一位,可以有效降低亚稳态的发生概率,同时单bit又可以采用打两拍的方法再次降低亚稳态发生的概率。
综上所述,为有效解决地址的跨时钟问题,采取格雷码编码+打两拍的方式降低地址变化发生亚稳态的概率。二进制转格雷码的实现。
由于异步FIFO的读写地址采用格雷码,空满信号的判决条件不同于同步FIFO。同同步FIFO,可以将读写地址扩展一位,用于判断是否读写完一轮,即深度为8,地址为4bit。以下为深度为8的异步FIFO进行读写情况,以下只列举了四种操作,其余读写少于深度个数据的情况也是类似的。
综上所述,读空empty信号拉高的判决条件是读写地址的格雷码相同。写满信号拉高的判决条件是读写地址的格雷码的高2位不同,其余位相同。
但是上述判决条件可能存在一些问题。由于读写地址需要打两拍,如果打拍期间还有读写操作,那读写地址又改变了,得到的并不是真正的空满信号,那会出现数据丢失的情况吗?举例说明如下:
module async_fifo#( parameter DEPTH = 16, parameter WIDTH = 8, parameter ADDR_BIT = 4, //log2(DEPTH) parameter RAM_STYLE_VAL = "distributed" )( input wr_clk, input rst_n, input wr_en, input [WIDTH-1:0] data_in, input rd_clk, input rd_en, output [WIDTH-1:0] data_out, output full, output empty ); (*ram_style=RAM_STYLE_VAL*) reg [WIDTH-1:0] mem [DEPTH-1:0]; reg [WIDTH-1:0] data_out_r; reg [ADDR_BIT:0] wr_addr; wire [ADDR_BIT:0] wr_addr_gray; reg [ADDR_BIT:0] wr_addr_w2r1; reg [ADDR_BIT:0] wr_addr_w2r2; reg [ADDR_BIT:0] rd_addr; wire [ADDR_BIT:0] rd_addr_gray; reg [ADDR_BIT:0] rd_addr_r2w1; reg [ADDR_BIT:0] rd_addr_r2w2; //*********************** Address ***********************// //write address always @ (posedge wr_clk or negedge rst_n)begin if(!rst_n) wr_addr <= 'd0; else if(wr_en) wr_addr <= wr_addr + 1; else wr_addr <= wr_addr; end //write address - > gray assign wr_addr_gray = (wr_addr>>1)^wr_addr; //Write address synchronization to read clock domain always @ (posedge wr_clk or negedge rst_n)begin if(!rst_n) {wr_addr_w2r2,wr_addr_w2r1} <= 'd0; else {wr_addr_w2r2,wr_addr_w2r1} <= {wr_addr_w2r1,wr_addr_gray}; end //read address always @ (posedge rd_clk or negedge rst_n)begin if(!rst_n) rd_addr <= 'd0; else if(rd_en) rd_addr <= rd_addr + 1; else rd_addr <= rd_addr; end //write address - > gray assign rd_addr_gray = (rd_addr>>1)^rd_addr; //Read address synchronization to write clock domain always @ (posedge rd_clk or negedge rst_n)begin if(!rst_n) {rd_addr_r2w2,rd_addr_r2w1} <= 'd0; else {rd_addr_r2w2,rd_addr_r2w1} <= {rd_addr_r2w1,rd_addr_gray}; end //************************* Data *************************// //write data always @ (posedge wr_clk)begin if(wr_en) mem[wr_addr] <= data_in; else mem[wr_addr] <= mem[wr_addr]; end //read data delay 1clk assign data_out = data_out_r; always @ (posedge rd_clk or negedge rst_n)begin if(!rst_n) data_out_r <= {WIDTH{1'b0}}; else if(rd_en==1) data_out_r <= mem[rd_addr]; else data_out_r <= data_out_r; end //********************** Full/Empty *********************// //Empty signal judgment assign empty = (wr_addr_w2r2 == rd_addr_gray); //Full signal judgment assign full = ({~(rd_addr_r2w2[4:3]),rd_addr_r2w2[2:0]}==wr_addr_gray[4:0]); endmodule
module tb_async; reg wr_clk,rd_clk; reg rst_n; reg wr_en; reg rd_en; reg [7:0] data_in; wire [7:0] data_out; wire full; wire empty; parameter WR_PERIOD = 10; parameter RD_PERIOD = 20 ; async_fifo async_fifo( .wr_clk (wr_clk ), .rst_n (rst_n ), .wr_en (wr_en ), .data_in (data_in ), .rd_clk (rd_clk ), .rd_en (rd_en ), .data_out (data_out ), .full (full ), .empty (empty ) ); initial begin wr_clk = 0;rd_clk=0;rst_n = 0; wr_en <= 0;rd_en <= 0;data_in = 'd0; #15 rst_n = 1; write_data(16); #100 read_data(16); #100 $finish; end always # (WR_PERIOD/2) wr_clk = ~wr_clk; always # (RD_PERIOD/2) rd_clk = ~rd_clk; task write_data(input [7:0] t); begin repeat(t)begin @(posedge wr_clk) data_in <= {$random}%256; wr_en <= 1; end @(posedge wr_clk) wr_en <= 0;data_in <= 'd0; end endtask task read_data(input [7:0] t); integer i; begin repeat(t)begin @(posedge rd_clk) rd_en <= 1; end @(posedge rd_clk) rd_en <= 0; end endtask endmodule
仿真结果如下,可以发现该异步fifo逻辑正确。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。