使用DC FIFO来实现不同时钟区域的数据传递

66 篇文章 22 订阅
58 篇文章 27 订阅

FIFO我们一般的理解是缓存,也就是收发不能完全周期同步,先放在里面保存一下,保存的逻辑是先进来的先出去,这种缓冲队列的思路尤其适合于数据流特征的数字系统。

在双时钟使用的FIFO不但具有上述缓冲功能,并且还可以实现数据的时钟区域穿越,这就比较厉害了~~~。实现数据在不同时钟区域的穿越是异步设计中比较关键的问题,而FIFO就这样轻易解决了(当然具体实现时候还要配合必要的约束),所以就有了以异步时钟区域数据传递为目的的应用。这其实是弱化了DC_FIFO的FIFO缓冲功能,发挥了DC_FIFO的双时钟数据穿越DC功能。

看代码说话:

module cdc_fifo # (parameter aw =4,parameter dw =8 )(
input wr_clk, rst,wr_en ,
input [7:0] wr_dat,
input rd_clk,
output [7:0] rd_dat,
output reg rd_vd
);

wire rst_n  = ~ rst ;
 reg[7:0] st ;
 wire rd_fifo =  ~empty  ;
 always @ (posedge rd_clk ) rd_vd <= rd_fifo ;
 wire [1:0]rd_level,wr_level;
 generic_fifo_dc_gray #(.aw(aw),.dw(dw)) generic_fifo_dc_gray (	
 .rd_clk(rd_clk) , 
 .wr_clk(wr_clk) , 
 .rst(rst_n) , 
 .clr(~rst_n) , 
 .din(wr_dat ) , 
 .we(wr_en) ,
 .dout(rd_dat ) , 
 .re(rd_fifo ) , 
 .full(full) , 
 .empty(empty )  , 
 .wr_level(wr_level) , 
 .rd_level(rd_level)  
 );
 
 

endmodule  

这里是用在和PHY芯片连接的一个的一部分逻辑,这里很显然有两个特点:

1,只要FIFO非空就是被读出(并保持输出)。

2,因为1,FIFO永远为空,跟不会满,所以empty=1,full=0,也就没有必要引出这个full和empty信号了。

如果我们要在数据更新的那个周期告知rd_clk时钟区,就可以用一个将rd_vd用rd_clk打一个周期后输出(FIFO数据的输出是在rd_vd=1的下一个周期,详见上两篇中的代码分析和仿真)。

----------------------------------------------

我这几天在做UDP通讯的VERILOG实现,用到了一个时钟穿越,就是千兆以太网的PHY芯片传递给FPGA的数据段使用的自己的125M时钟,RX_CLK伴随这RX_DATA和RX_DV(DV=Data Valid)一起加在FPGA的引脚。我们必须使用RX_CLK在DV=1时采样和保存DATA数据,传递给UDP工作的主时钟区域。

DC_FIFO在这需求里刚好可以实现。在这个实现中,我们在使用DC FIFO时需要考虑一下几点具体问题:

1,要穿越时钟区域两端都是125M,但是不同源。

2,PHY一旦开始传输,就不会产生字节间断,直到此帧完全结束。

3,我们设计PHY上层的MAC层是2这样想得,所以MAC接受层也没有靠字节间断,所以要求经过DC FIFO帧内字节还是连续的。

结合这三点显然,上述三点的满足我们可以这样实现:FIFO本身就是队列,可以保存若干个条目,我们保存比如8个条目,我们先等待写入3个或者以上,之后再触发读使能,再非空前全部读完。因为时钟频率一样,因此不会产生写满也不会读空。

我们实现代码如下:



module cdc_fifo # (parameter aw =4,parameter dw =8 )(
input wr_clk, rst,wr_en ,
input [7:0] wr_dat,
input rd_clk,
output [7:0] rd_dat,
output reg rd_vd
);

wire rst_n  = ~ rst ;
 reg[7:0] st ;
 wire rd_fifo = ( st == 20 ) && ( ~empty ) ;
 always @ (posedge rd_clk ) rd_vd <= rd_fifo ;
 wire [1:0]rd_level,wr_level;
 generic_fifo_dc_gray #(.aw(aw),.dw(dw)) generic_fifo_dc_gray (	
 .rd_clk(rd_clk) , 
 .wr_clk(wr_clk) , 
 .rst(rst_n) , 
 .clr(~rst_n) , 
 .din(wr_dat ) , 
 .we(wr_en) ,
 .dout(rd_dat ) , 
 .re(rd_fifo ) , 
 .full(full) , 
 .empty(empty )  , 
 .wr_level(wr_level) , 
 .rd_level(rd_level)  
 );
 
 always @ (posedge rd_clk or posedge  rst) if ( rst )  st <= 0;else case (st)
 0: st<=10;
10: if (rd_level == 2'b10)st<=20;
20: if (empty) st<=10;
default  st<=0;endcase

endmodule  

这里使用状态机是最直接的思路实现,我们看到检测rd_level是2'B10

wr_level	indicates the FIFO level:
		2'b00	0-25%	 full
		2'b01	25-50%	 full
		2'b10	50-75%	 full
		2'b11	%75-100% full

rd_level	indicates the FIFO level:
		2'b00	0-25%	 empty
		2'b01	25-50%	 empty
		2'b10	50-75%	 empty
		2'b11	%75-100% empty

也就是有了25%-50%的数据存在FIFO里面,比如FIFO深度为8,那么就有2或者3或者4个内容已经被写入到FIFO里,这时候我们就进入状态20,允许读了,直到读空了才进入状态10从新判断先一次满足内存条目为2,3,4的情况。

当然这里可以省略状态机,直接用一个一位寄存器来实现;设置一个允许读寄存器allow_rd,在rd_level==2'b10时候就是有2,3,4个条目时候设置为1以表示允许读出,在empty为空的时候就设置为0,防止下次条目少于2时候被读出。

module cdc_fifo2 # (parameter aw =4,parameter dw =8 )(
input wr_clk, rst,wr_en ,
input [7:0] wr_dat,
input rd_clk,
output [7:0] rd_dat,
output reg rd_vd
);

wire rst_n  = ~ rst ;
 reg allow_rd ;
 wire rd_fifo = allow_rd & ( ~empty ) ;
 always @ (posedge rd_clk ) rd_vd <= rd_fifo ;
 wire [1:0]rd_level,wr_level;
 generic_fifo_dc_gray #(.aw(aw),.dw(dw)) generic_fifo_dc_gray (	
 .rd_clk(rd_clk) , 
 .wr_clk(wr_clk) , 
 .rst(rst_n) , 
 .clr(~rst_n) , 
 .din(wr_dat ) , 
 .we(wr_en) ,
 .dout(rd_dat ) , 
 .re(rd_fifo ) , 
 .full(full) , 
 .empty(empty )  , 
 .wr_level(wr_level) , 
 .rd_level(rd_level)  
 );
 
 always @ (posedge rd_clk or posedge  rst) if ( rst )  allow_rd <= 0; 
 else if (rd_level == 2'b10)allow_rd<=1;else if (empty) allow_rd<=0;
 
endmodule  

其实我们说rd_level是2'B10表示内部有2,3,4个条目,那会不会达到5,6,7,8个条目,这样我们的代码就没有对应的处理,代码就有BUG了。不会的,因为一旦满足有2个条目,触发了读,2个周期之后FIFO被一直读,两者读写速度相等,所以会保持一个FIFO内有固定4条,直到不再对FIFO写,读出端继续读,直到empty。

这里实际是一个读写频率都一样(也就是说短时间内差别可忽略)的特例,如果有一定的相差频率,则要从两个方面进行分析和解决

1,上层处理模块允许帧内字节数据有间断。

2,设置足够大的FIFO保证在慢速读出的时候FIFO不产生溢出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值