近期参与的项目涉及到异步时钟域下多bit位数据传输的问题,解决的最好的办法便是异步FIFO。在网上查了相关的异步FIFO的资料,对于读写侧数据位宽相同情况下的异步FIFO有所了解。若要是读写侧数据位宽不同就再合适不过了,经过自己的一些改变,完成了该实现。理论知识网上介绍的很是详细明了,咱们索性单刀直入,直捣黄龙。
一、总体实现架构图
总体实现架构图并没有变化,依旧包括存储模块、读指针及满生成模块、写指针及空生成模块及其两级数据同步模块。
二、读、写指针生成模块
本次设计的异步FIFO输入、输出数据分别为96bit、192bit。为了方便读写地址的设置,FIFO Memeory的宽度设置为24bit,深度设置为16。这样在数据加载时,每个wclk写指针wptr增加4(96/24),在有足够数据读出时,每个rclk读指针rptr增加8(192/24)。关于宽度及深度的设置也许不是最理想的,但是自己认为能够比较好的说明问题。感兴趣的话,也可以去网上查一下理想的深度设置。
读指针的生成实现架构图。
//-----------wptr---------------------------
always@ (posedge wclk or negedge wrst_n)
begin
if(wrst_n == 1'b0) begin
wptr <= 5'd0;
end
else if(w_en && ~wfull)
wptr <= wptr + 3'b100;
end
写指针生成实现架构图。
//-----------rptr---------------------------------
always@ (posedge rclk or negedge rrst_n)
begin
if(rrst_n == 1'b0) begin
rptr <= 5'd0;
end
else if(r_en && ~rempty && enough_data)
rptr <= rptr + 4'b1000;
end
其中写指针wptr、读指针rptr位宽均为5bit。对于深度为16的Memeory来说,地址宽度4bit即够,增加一位用于判断空满信号的准确生成。
三、满、空信号及其enough_data信号生成
跨时钟域数据必存在亚稳态问题,对于单bit数据传输一般采用双寄存器方法减小发生的概率。这里涉及到读指针rptr同步到写时钟域wclk下,wptr同步到读时钟域rclk下的多bit位宽数据跨时钟域传输的问题。若是二进制编码形式的指针在变化时并不是每次只变换1bit,而双寄存器方法适用于单bit数据变换的情况,这时采用格雷码编码形式可以解决这一问题。二进制码转换格雷码时,最高位不变。格雷码次高位为二进制码高位与次高位异或,依次类推。
//-----------wq2_rptr-----------------------------
always@ (posedge wclk or negedge wrst_n)
begin
if(wrst_n == 1'b0)
{wq2_rptr,wq1_rptr} <= {5'd0,5'd0};
else
{wq2_rptr,wq1_rptr} <= {wq1_rptr,gray_rptr};
end
//-----------rq2_wptr-----------------------------
always@ (posedge rclk or negedge rrst_n)
begin
if(rrst_n == 1'b0)
{rq2_wptr,rq1_wptr} <= {5'd0,5'd0};
else
{rq2_wptr,rq1_wptr} <= {rq1_wptr,gray_wptr};
end
//------------------------------------------------
在写时钟域wclk下生成满信号,在读时钟域rclk下生成空信号,只有在这种情况下生成的满信号才能有效的“告知外部”停止数据加载,空信号同理。应用的最多的空、满信号生成算法便是通过比较wptr、rptr来判断。
首先我们认清一点,写指针wptr一定“大于”读指针rptr。也不是真正的大于,恒领先于rptr。可以想象的到当同步后的rq2_wptr等于rptr时,近似的认为空的状态。事实上此时Memeory内已经写了2个rclk数据,这是由于双寄存器方法存在两个rclk延迟。在判断满状态时,只在写指针wptr领先同步过来的读指针wq2_rptr一圈时,我们近似的认为处于满状态。对于二进制码我们可以想象的到最高位不同,而其余位相同。而此时通过双寄存器同步过到写时钟域下的读指针wq2_rptr是格雷码形式,同样我们可以举一个特例来说明情况:读指针wq2_rptr为0时,格雷码表示为00000;写指针wptr为16时,格雷码表示为11000。可以看出高两位相反且其余位相同时为满。
//------------------------------------------------
assign wfulls = (gray_wptr == {~wq2_rptr[ADDR:ADDR-1],wq2_rptr[ADDR-2:0]});
assign remptys = (gray_rptr == rq2_wptr);
//-----------wfull--------------------------------
always@ (posedge wclk or negedge wrst_n)
begin
if(wrst_n == 1'b0)
wfull <= 1'b0;
else begin
wfull <= wfulls;
end
end
//-----------rempty-------------------------------
always@ (posedge rclk or negedge rrst_n)
begin
if(rrst_n == 1'b0)
rempty <= 1'b1;
else begin
rempty <= remptys;
end
end
//------------------------------------------------
对于读写侧不同情况的异步FIFO,在读取数据时不能仅仅根据空信号来判读是否读取。在空信号基础上增加了enough_data信号。同样enough_data信号在写时钟域rclk下通过比较同步过来的写指针和读指针来生成。是否生成enough_data信号就分成两种情况。 1.wptr与rptr高位相同,只需waddr >= raddr + 8;2.wptr与rptr高位不同时,上图中给出一个特例,rptr >= waddr + 8。
下述verilog中rp2_wptr即是同步到读时钟域rclk的写指针情况下。
//------------------------------------------------
assign bin_rq2_wptr = {rq2_wptr[4],bin_rq2_wptr[4]^rq2_wptr[3],bin_rq2_wptr[3]^rq2_wptr[2],bin_rq2_wptr[2]^rq2_wptr[1],bin_rq2_wptr[1]^rq2_wptr[0]};
//------------------------------------------------
assign bin_rq2_waddr = bin_rq2_wptr[WADDR-1:0];
//-----------enough_data--------------------------
assign enough_data = (bin_rq2_waddr >= rptr[RADDR-1:0] + 8 || rptr[RADDR-1:0] >= bin_rq2_waddr + 8);
//------------------------------------------------
本次设计的读写侧数据位宽不同的异步FIFO总体代码如下,欢迎大家提出自己的想法,也恳请各位提出宝贵的意见!!!本人在测试的时候对读写时钟设置为wclk = 2rclk,目测无误。
******注释******
① 在快时钟域到慢时钟域传输数据时,此设计不再适用。原因一:将wptr同步到读时钟域下,采样到的rq2_wptr信号并不是按照格雷码的顺序去采样(由于慢时钟不能够及时采样快时钟),这时格雷码就会失去意义,不能够降低亚稳态出现的概率。原因二:通过采样到的rq2_wptr不能够足以判断enough_data信号的生成。
//------------------------------------------------
module asycn_d_fifo
(wclk ,
rclk ,
wrst_n,
rrst_n,
w_en ,
r_en ,
wdata ,
rdata ,
wfull ,
rempty
);
//------------------------------------------------
input wclk;
input rclk;
input wrst_n;
input rrst_n;
input w_en;
input r_en;
input[95:0] wdata;
output[191:0] rdata;
output wfull;
output rempty;
//------------------------------------------------
parameter ADDR = 4;
parameter WADDR = 4;
parameter RADDR = 4;
parameter WIDTH = 24;
parameter DEPTH = 16;
//------------------------------------------------
reg[WIDTH-1:0] data[DEPTH-1:0];
//------------------------------------------------
reg[191:0] rdata;
reg wfull;
reg rempty;
wire wfulls;
wire remptys;
wire enough_data;
reg[WADDR:0] wptr;
reg[RADDR:0] rptr;
reg[RADDR:0] wq2_rptr;
reg[RADDR:0] wq1_rptr;
reg[WADDR:0] rq2_wptr;
reg[WADDR:0] rq1_wptr;
wire[WADDR:0] gray_wptr;
wire[RADDR:0] gray_rptr;
wire[WADDR:0] bin_rq2_wptr;
wire[WADDR-1:0] bin_rq2_waddr;
//------------------------------------------------
wire[WADDR-1:0] waddr ;
wire[WADDR-1:0] waddr1;
wire[WADDR-1:0] waddr2;
wire[WADDR-1:0] waddr3;
wire[RADDR-1:0] raddr ;
wire[RADDR-1:0] raddr1;
wire[RADDR-1:0] raddr2;
wire[RADDR-1:0] raddr3;
wire[RADDR-1:0] raddr4;
wire[RADDR-1:0] raddr5;
wire[RADDR-1:0] raddr6;
wire[RADDR-1:0] raddr7;
//------------------------------------------------
assign waddr1 = waddr + 1'd1;
assign waddr2 = waddr + 2'd2;
assign waddr3 = waddr + 2'd3;
assign raddr1 = raddr + 1'd1;
assign raddr2 = raddr + 2'd2;
assign raddr3 = raddr + 2'd3;
assign raddr4 = raddr + 3'd4;
assign raddr5 = raddr + 3'd5;
assign raddr6 = raddr + 3'd6;
assign raddr7 = raddr + 3'd7;
//------------------------------------------------
function[ADDR:0] gray_conv;
input[ADDR:0] bin;
begin
gray_conv[ADDR] = bin[ADDR];
gray_conv[ADDR-1:0] = bin[ADDR-1:0] ^ bin[ADDR:1];
end
endfunction
//----------write data----------------------------
always@ (posedge wclk or negedge wrst_n)
begin
if(wrst_n == 1'b0) begin
data[waddr] <= 24'd0;
data[waddr1] <= 24'd0;
data[waddr2] <= 24'd0;
data[waddr3] <= 24'd0;
end
else if(w_en && ~wfull) begin
data[waddr] <= wdata[23:0] ;
data[waddr1] <= wdata[47:24];
data[waddr2] <= wdata[71:48];
data[waddr3] <= wdata[95:72];
end
end
//----------read data-----------------------------
always@ (posedge rclk or negedge rrst_n)
begin
if(rrst_n == 1'b0) begin
rdata <= 95'd0;
end
else begin
if(r_en && ~rempty && enough_data) begin
rdata[23:0] <= data[raddr] ;
rdata[47:24] <= data[raddr1];
rdata[71:48] <= data[raddr2];
rdata[95:72] <= data[raddr3];
rdata[119:96] <= data[raddr4];
rdata[143:120] <= data[raddr5];
rdata[167:144] <= data[raddr6];
rdata[191:168] <= data[raddr7];
end
end
end
//-----------wptr---------------------------------
always@ (posedge wclk or negedge wrst_n)
begin
if(wrst_n == 1'b0) begin
wptr <= 5'd0;
end
else if(w_en && ~wfull)
wptr <= wptr + 3'b100;
end
//-----------rptr---------------------------------
always@ (posedge rclk or negedge rrst_n)
begin
if(rrst_n == 1'b0) begin
rptr <= 5'd0;
end
else if(r_en && ~rempty && enough_data)
rptr <= rptr + 4'b1000;
end
//------------------------------------------------
//------------------------------------------------
assign gray_wptr = gray_conv(wptr);
assign gray_rptr = gray_conv(rptr);
//-----------wq2_rptr-----------------------------
always@ (posedge wclk or negedge wrst_n)
begin
if(wrst_n == 1'b0)
{wq2_rptr,wq1_rptr} <= {5'd0,5'd0};
else
{wq2_rptr,wq1_rptr} <= {wq1_rptr,gray_rptr};
end
//-----------rq2_wptr-----------------------------
always@ (posedge rclk or negedge rrst_n)
begin
if(rrst_n == 1'b0)
{rq2_wptr,rq1_wptr} <= {5'd0,5'd0};
else
{rq2_wptr,rq1_wptr} <= {rq1_wptr,gray_wptr};
end
//------------------------------------------------
//------------------------------------------------
assign waddr = wptr[ADDR-1:0];
assign raddr = rptr[ADDR-1:0];
//------------------------------------------------
assign wfulls = (gray_wptr == {~wq2_rptr[ADDR:ADDR-1],wq2_rptr[ADDR-2:0]});
assign remptys = (gray_rptr == rq2_wptr);
//-----------wfull--------------------------------
always@ (posedge wclk or negedge wrst_n)
begin
if(wrst_n == 1'b0)
wfull <= 1'b0;
else begin
wfull <= wfulls;
end
end
//-----------rempty-------------------------------
always@ (posedge rclk or negedge rrst_n)
begin
if(rrst_n == 1'b0)
rempty <= 1'b1;
else begin
rempty <= remptys;
end
end
//------------------------------------------------
assign bin_rq2_wptr = {rq2_wptr[4],bin_rq2_wptr[4]^rq2_wptr[3],bin_rq2_wptr[3]^rq2_wptr[2],bin_rq2_wptr[2]^rq2_wptr[1],bin_rq2_wptr[1]^rq2_wptr[0]};
//------------------------------------------------
assign bin_rq2_waddr = bin_rq2_wptr[WADDR-1:0];
//-----------enough_data--------------------------
assign enough_data = (bin_rq2_waddr >= rptr[RADDR-1:0] + 8 || rptr[RADDR-1:0] >= bin_rq2_waddr + 8);
//------------------------------------------------
endmodule