1、设计框图
图1
这张图是我摘抄来的,图片来源于eetop。我觉得这张图有两处问题,比如说wclk module 中的g端口接入的是syncronize to read clk,根据理解应该是ginc而非为g。原因在于g端口输出的数据仍旧为2进制数据,但是ginc端口数据的数据应该为gray数据。同理,另一个问题类似。
如上图所示的同步模块synchronize to write clk,其作用是把读时钟域的读指针rd_ptr采集到写时钟(wr_clk)域,然后和写指针wr_ptr进行比较从而产生或撤消写满标志位wr_full;类似地,同步模块synchronize to read clk的作用是把写时钟域的写指针wr_ptr采集到读时钟域,然后和读指针rd_ptr进行比较从而产生或撤消读空标志位rd_empty。
另外还有写指针wr_ptr和写满标志位wr_full产生模块,读指针rd_ptr和读空标志位rd_empty产生模块,以及双端口存储RAM模块。
2、使用gray码的原因
异步FIFO读写指针需要在数学上的操作和比较才能产生准确的空满标志位,但由于读写指针属于不同的时钟域及读写时钟相位关系的不确定性,同步模块采集另一时钟域的指针时,此指针有可能正处在跳变的过程中,如图2所示,那么采集到的值很有可能是不期望的值,当然,不期望的错误结果也会随之发生。
采用gray每次只改变一位,有效避免跳变过程中出现的问题。
3、Verilog实现gray码的方式
gray[n - 1:0 ] = (bin[n - 1 :0] >> 1) ^ bin[n - 1 : 0]
4、gray码计数器的实现结构图
如下图3所示,指向存储器的地址指针由二进制计数器产生,而用于跨时钟域传播的格雷码指针是对二进制指针的实时转换并用寄存器采集获得的。这里要注意的是,计数器的位宽比实际所需的位宽要多出一位,这样做的目的是方便判断FIFO的空或满,这一点下文中将会介绍。
5、读空与写满标志位的产生
异步FIFO最核心的部分就是精确产生空满标志位,这直接关系到设计的成败。本文采用比较读写指针来判断FIFO的空满,如果FIFO的深度是n-1位线所能访问到的地址空间,那么此设计所要用的指针位宽就比实际多出一位,也就是n位,这样做有助于判断FIFO是空还是满。
读空:只有在两种情况下,FIFO才会为空:第一种是系统复位,读写指针全部清零;另一种情况是在FIFO不为空时,数据读出的速率快于数据写入的速率,读地址赶上写地址时FIFO为空。空标志位的产生需要在读时钟域内完成。
判断依据:当读地址rd_ptr赶上写地址wr_ptr,也就是rd_ptr完全等于wr_ptr时,可以断定,FIFO里的数据已被读空。
存在某些问题:1、由于写指针被同步之后与读指针进行比较,因此写指针不能及时更新,造成虚空(FIFO非空但是产生了空标志位)标志位的产生,虚空标志并不会是引起传输错误。
写满:举个简单而又生动的例子,读写指针的关系就好比A,B两个田径运动员在一环形跑道上赛跑一样,当B运动员领先A并整整超前一圈时,A,B两人的地点相同,此种情况对应于读写指针指向了同一地址,但写指针超前整整一圈,FIFO被写满。
**注意:**如果地址的宽度和FIFO实际深度所需的宽度相等,某一时刻读写地址相同了,那FIFO是空还是满就难以判断了。所以读写指针需要增加一位来标记写地址是否超前读地址(在系统正确工作的前提下,读地址不可能超前于写地址),比如FIFO的深度为8,我们需要用宽度为4的指针。
**判断依据:**首先,最高位相异(因为读指针不可能超前于写指针,所以只可能是写指针超前于读指针);其次,如果把最高位为1的所有格雷码指针的次高位均取反后,除去最高位不看,则指向同一存储空间的两指针相同,从而得出第二个条件是:次高也相异。例如:如地址为0 的gray为0000,或者1100,此时读写指针指向同一位置,并且最高位和次高位都不一样即00 和11 不同,那么我们就可以判断写满操作。
6、总结
异步FIFO重点理解了空满标志信号的产生方法以及可能会发生的“虚空”和“虚满”现象。理解了这些关键信号的产生原理,设计一个异步FIFO也就不难了。
下面贴出代码:主要包含五部分,每一部分都是根据图一编写。其中,例化RAM的代码没有给出,我例化的是8 * 8bit的RAM。
1、顶层文件:
//========================================================
// Module Name : async_fifo_top
// Author : Winson_c
// Tool version : Quartus II 13.1
// Revision : v1.0
// Description : Asynchronize FIFO
//=========================================================
`include "define.v"
module async_fifo_top(
input wclk,
input rclk,
input wrst_n,
input rrst_n,
input write,
//input read,
input [`DATA_WIDTH - 1: 0]wdata,
output wfull,
output rempty,
output [`DATA_WIDTH - 1: 0]rdata
);
wire [`FIFO_WIDTH : 0]wr_ptr_gray;
wire [`FIFO_WIDTH -1 : 0]wr_ptr_bin;
wire [`FIFO_WIDTH : 0]rd_ptr_gray;
wire [`FIFO_WIDTH -1 : 0]rd_ptr_bin;
//=====================================================\\
//Write Module
//=====================================================//
wire [`FIFO_WIDTH : 0]rd_ptr_out;
wclk_module wclk_module_inst(
.wclk (wclk) ,
.wrst_n (wrst_n) ,
.write (write) ,
.rd_ptr_out (rd_ptr_out) ,
.wfull (wfull) ,
.wr_ptr_bin (wr_ptr_bin) ,
.wr_ptr_gray (wr_ptr_gray)
);
sync_module sync_module_inst(
.clk (wclk) ,
.rst_n (wrst_n) ,
.sync_din (rd_ptr_gray) ,
.sync_dout (rd_ptr_out)
);
//=====================================================\\
//Read Module
//=====================================================//
wire [`FIFO_WIDTH : 0]wr_ptr_out;
rclk_module rclk_module_inst(
.rclk (rclk) ,
.rrst_n (rrst_n) ,
.read (write) ,//read = ~write
.wr_ptr_out (wr_ptr_out) ,
.rempty (rempty) ,
.rd_ptr_bin (rd_ptr_bin) ,
.rd_ptr_gray (rd_ptr_gray)
);
sync_module sync_module_inst2(
.clk (rclk) ,
.rst_n (rrst_n) ,
.sync_din (wr_ptr_gray) ,
.sync_dout (wr_ptr_out)
);
//=====================================================\\
//Memory Module
//=====================================================//
memory memory_inst (
.data (wdata) ,
.rdaddress (rd_ptr_bin) ,
.rdclock (rclk) ,
.wraddress (wr_ptr_bin) ,
.wrclock (wclk) ,
.wren (write) ,
.q (rdata)
);
endmodule
2、定义参数
//define paraments
`define FIFO_DEPTH 8
`define DATA_WIDTH 8
`define FIFO_WIDTH 3
3、读时钟域模块
//======================================================================================
// Module Name : rclk_module
// Author : rinson_c
// Tool version : Quartus II 13.1
// Revision : v1.0
// Description : wclk Module includes rd_ptr and rempty
//======================================================================================
`include "define.v"
module rclk_module(
input rclk,
input rrst_n,
input read,
input [`FIFO_WIDTH : 0]wr_ptr_out,
output [`FIFO_WIDTH - 1 : 0]rd_ptr_bin,
output [`FIFO_WIDTH : 0]rd_ptr_gray,
output rempty
);
reg [`FIFO_WIDTH : 0]rd_ptr_bin_reg;
reg rempty_reg;
always@(posedge rclk or negedge rrst_n) begin
if (rrst_n == 0)
rd_ptr_bin_reg <= 0;
else if (read == 1'b0 && !rempty)
rd_ptr_bin_reg <= rd_ptr_bin_reg + 1'b1;
else rd_ptr_bin_reg <= rd_ptr_bin_reg;
end
assign rd_ptr_gray = (rd_ptr_bin_reg >> 1) ^ rd_ptr_bin_reg;
assign rd_ptr_bin = rd_ptr_bin_reg[`FIFO_WIDTH - 1 : 0];
//Judge read empty flag
assign rempty = (rd_ptr_gray == wr_ptr_out) ? 1'b1 : 1'b0;
/*assign flag_read = (rd_ptr_gray == wr_ptr_out);
always@(posedge rclk or negedge rrst_n) begin
if (rrst_n == 0)
rempty_reg <= 1'b1;
else rempty_reg <= flag_read;
end
assign rempty = rempty_reg;*/
endmodule
4、写时钟域模块
//======================================================================================
// Module Name : wclk_module
// Author : Winson_c
// Tool version : Quartus II 13.1
// Revision : v1.0
// Description : Generator wr_ptr_gray for Synchronize Module
//======================================================================================
`include "define.v"
module wclk_module(
input wclk,
input wrst_n,
input write,
input [`FIFO_WIDTH : 0]rd_ptr_out,
output [`FIFO_WIDTH - 1 : 0]wr_ptr_bin,
output wfull,
output [`FIFO_WIDTH : 0]wr_ptr_gray
);
reg [`FIFO_WIDTH : 0]wr_ptr_bin_reg;
reg wfull_reg;
always@(posedge wclk or negedge wrst_n) begin
if (wrst_n == 0)
wr_ptr_bin_reg <= 0;
else if (write == 1'b1 && !wfull)
wr_ptr_bin_reg <= wr_ptr_bin_reg + 1'b1;
else wr_ptr_bin_reg <= wr_ptr_bin_reg;
end
assign wr_ptr_gray = (wr_ptr_bin_reg >> 1) ^ wr_ptr_bin_reg;
assign wr_ptr_bin = wr_ptr_bin_reg[`FIFO_WIDTH - 1 : 0];
//Judge write full flag
assign wfull = (wr_ptr_gray == {~rd_ptr_out[`FIFO_WIDTH:`FIFO_WIDTH-1], rd_ptr_out[`FIFO_WIDTH - 2 : 0]}) ? 1'b1 : 1'b0;
/*assign flag_wfull = (wr_ptr_gray == {~rd_ptr_out[`FIFO_WIDTH:`FIFO_WIDTH-1], rd_ptr_out[`FIFO_WIDTH - 2 : 0]}) ;
always@(posedge wclk or negedge wrst_n) begin
if (wrst_n == 0)
wfull_reg <= 1'b0;
else wfull_reg <=flag_wfull;
end
assign wfull = wfull_reg;*/
endmodule
5、同步模块
//======================================================================================
// Module Name : sync_module
// Author : winson_c
// Tool version : Quartus II 13.1
// Revision : v1.0
// Description : asynchronize clock(delay 2 clock)
//======================================================================================
//`include "C:\Users\winson\Desktop\FIFO\asynchronous\rtl\define.v"
`define FIFO_DEPTH 8
`define DATA_WIDTH 8
`define FIFO_WIDTH 3
module sync_module(
input clk,
input rst_n,
input [`FIFO_WIDTH : 0]sync_din,
output [`FIFO_WIDTH : 0]sync_dout
);
reg [`FIFO_WIDTH : 0]sync_dout_1d;
reg [`FIFO_WIDTH : 0]sync_dout_2d;
always@(posedge clk or negedge rst_n) begin
if (rst_n == 0) begin
sync_dout_1d <= 0;
sync_dout_2d <= 0;
end
else begin
sync_dout_1d <= sync_din;
sync_dout_2d <= sync_dout_1d;
end
end
assign sync_dout = sync_dout_2d;
endmodule
部分仿真结果图:
该图存在虚空现象。