一、异步FIFO
数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous), 异步是指相位不同,频率可能不同,不同时钟上升沿之间关系不确定,相位关系不确定,常用于跨时钟域数据的存储读写。
1、亚稳态
以单bit信号为例:

说明:
- 亚稳态:上图Din是DFF1的输入由上一个DFF0在时钟域clka下输出的单bit信号,当DFF0输出到clkb时钟域时,由于两个时钟域是异步的,所以无法保证采样到的DFF1的输出Ds是稳定的,因为信号跨时钟域,它要在clkb时钟域稳定下来需要一定的时间(这段时间包括触发器内部电路的时间、导线,导线中间组合逻辑电路的时间),这段稳定时间中的输出Ds是不稳定的,叫这个不稳定的状态为亚稳态,但经过一段时间最终输出Ds会达到稳定状态0或1。
2、单bit信号Din慢时钟a同步到快时钟b
若单bit信号Din慢时钟a到快时钟b采用打两拍的方法,打的拍数越多亚稳态消除的越好(信号抖动时间不是确定的,随时钟变化)
- 第一拍:b时钟下同步Din,减小采到亚稳态的概率,虽采到随机值,但最终会达到一个正确的稳定值(随机值最终不会到一个错误的值), 所以输出信号的Ds基本稳定了,但不能用这个信号,因为在时钟b下,仍然有可能会采样到亚稳态,采样到的亚稳态可能为1或0,是不确定的。
- 第二拍:第二次b时钟下同步Din,此时采到Ds信号基本稳定已消除了亚稳态,再输出Dout信号
3、单bit信号Din快时钟b同步到慢时钟a
若单bit信号Din快时钟b同步到慢时钟a,一定可以采到稳定的信号,但可能会出现重复采样问题即出现了多个快时钟周期内同一个数据的信号。
4、多bit信号为什么不能采用打两拍的方法进行跨时钟域同步
- 假设从时钟域A垮到时钟域B,是域A从00变到11,在时域B还能采样到从00变到11吗?在时域B中,时钟的上升沿来的时间是肯定一致的,但是各个bit从时钟域A到达时钟域B的时间是不一样的,亚稳态恢复到稳态的时间也是不一样的。即00中的两个0并不是同时变成1的,因此时钟域B中可能采到中间态01或10。
- 单比特发送亚稳态的概率变成多bit后概率会大很多,不直接打两拍是可以降低亚稳态发生的概率的。
二、异步FIFO设计:用于跨时钟域数据的存储读写

说明:
- 写指针(wptr)指向的位置表示还未写,将要写,系统复位后(FIFO为空),写指针指向0,每写入一笔数据,写指针加1;
- 读指针(rptr)指向的位置表示还未读,将要读,系统复位后(FIFO为空),读指针指向0,每读入一笔数据,读指针加1;
- 空标志(empty):当读指针和写指针相等时,空标志=1,复位时,两指针都为0,或者当读指针追赶上了写指针
- 满标志(full):当写指针和读指针相等时,满标志=1,写指针写了一圈后,又追上了读指针
1、为什么多bit要使用格雷码?
以深度为8的FIFO为例:

- 在异步FIFO中,读写指针多出一个高bit信号来表示读写指针是否已经跑完一圈,自然二进制跑完一圈,例如rptr是1,wptr是9,从高位可以看出FIFO为满,若wptr也是1,说明FIFO为空,但使用自然二进制不好,因为按自然二进制读写指针的变化从3跳到4,变化了3个bit,多bit很容易采到亚稳态,所以使指针转换到格雷码来判断FIFO空满更方便,相邻格雷码只有一个bit不同,可以采取打两拍的方式同步。
- 例如rptr是1,wptr是9,对应格雷码是0001,1101,即wptr格雷码的高两位取反后与rptr格雷码相同,表示FIFO满,若对应格雷码直接相同,则表示FIFO为空。
- full信号产生:与写时钟域有关,需要把转换为格雷码的读指针同步到写时钟域。
- empty信号产生:与读时钟域有关,需要把转换为格雷码的写指针同步到读时钟域。
2、自然二进制转格雷码

//法一
module bin2gry(Gry,Bin);
parameter length = 8;
output [length-1:0] Gry;
input [length-1:0] Bin;
reg [length-1:0] Gry;
integer i;
always @(Bin)
begin
for(i=0;i<length-1;i=i+1)
Gry[i]=Bin[i]^Bin[i+1];
Gry[i]=Bin[i];
end
//法二
assign Gray = (Bin >>1)^Bin;
endmodule
3、格雷码转自然二进制

module gry2bin(Gry,Bin);
parameter length = 8;
input [length-1:0] Gry;
output [length-1:0] Bin;
reg [length-1:0] Bin;
integer i;
always @(Gry)
begin
Bin[length-1]=Gry[length-1];
for(i=length-2;i>=O;i=i-1)
Bin[i]=Bin[i+1]^Gry[i];
end
endmodule
说明:
- 格雷码转二进制可以用来判断几乎空几乎满。
三、RTL代码设计:
1、dual_port_sram:
模拟双端口RAM的行为
`timescale 1ns/1ps
module dual_port_sram #(
parameter SRAM_DEPTH = 16,
parameter DATA_WIDTH = 8)(
input wclk ,
input rclk ,
input rst_n,
input wr_en,
input rd_en,
input [$clog2(SRAM_DEPTH)-1:0] waddr,
input [$clog2(SRAM_DEPTH)-1:0] raddr,
input [DATA_WIDTH-1:0] wdata,
output reg [DATA_WIDTH-1:0] rdata
);
reg [DATA_WIDTH-1:0] mem[0:SRAM_DEPTH-1];
integer i;
//write mem
always @(posedge wclk or rst_n)
begin
if(!rst_n)
for(i=0;i<=SRAM_DEPTH-1;i=i+1)
mem[i] <= {(DATA_WIDTH){1'b0}};
else if(wr_en)
mem[waddr] <= wdata;
else
mem[waddr] <= mem[waddr];
end
//read mem
always @(posedge rclk or rst_n)
begin
if(!rst_n)
rdata <= {(DATA_WIDTH){1'b0}};
else if(rd_en)
rdata <= mem[raddr];
else
rdata <= {(DATA_WIDTH){1'b0}};
end
endmodule
2、async_fifo:
包括空满信号与几乎空几乎满信号的产生
`timescale 1ns/1ps
/***************************************AFIFO*****************************************/
module async_fifo#(
parameter DATA_WIDTH = 8 ,
parameter FIFO_DEPTH = 16,
parameter ALMOST_WFULL = 12,
parameter ALMOST_REMPTY = 4
)(
input wclk ,
input rclk ,
input rst_n ,
input wr_en ,
input rd_en ,
input [DATA_WIDTH-1:0] wdata ,
output [DATA_WIDTH-1:0] rdata ,
output wfull ,
output al_wfull ,
output rempty ,
output al_rempty
);
localparam ADDR_WIDTH = $clog2(FIFO_DEPTH);
reg [ADDR_WIDTH:0] wptr;
reg [ADDR_WIDTH:0] rptr;
wire [ADDR_WIDTH-1:0] wptr_addr;
wire [ADDR_WIDTH-1:0] rptr_addr;
wire [ADDR_WIDTH:0] wptr_gray;
wire [ADDR_WIDTH:0] rptr_gray;
reg [ADDR_WIDTH:0] wptr_gray1;
reg [ADDR_WIDTH:0] rptr_gray1;
reg [ADDR_WIDTH:0] wptr_gray2;
reg [ADDR_WIDTH:0] rptr_gray2;
reg [ADDR_WIDTH:0] wptr_gray3;
reg [ADDR_WIDTH:0] rptr_gray3;
wire [ADDR_WIDTH:0] wptr_gray_bin;
wire [ADDR_WIDTH:0] rptr_gray_bin;
reg [ADDR_WIDTH-1:0] alfull_cnt;
reg [ADDR_WIDTH-1:0] alempty_cnt;
wire wenc;
wire renc;
dual_port_sram #(
.SRAM_DEPTH(FIFO_DEPTH),
.DATA_WIDTH(DATA_WIDTH))
u_dual_port_sram(
.wclk (wclk) ,
.rclk (rclk) ,
.rst_n (rst_n),
.wr_en (wenc) ,
.rd_en (renc) ,
.waddr (wptr_addr) ,
.raddr (rptr_addr) ,
.wdata (wdata),
.rdata (rdata)
);
assign wptr_addr = wptr[ADDR_WIDTH-1:0];
assign rptr_addr = rptr[ADDR_WIDTH-1:0];
assign wenc = wr_en && (~wfull) ;
assign renc = rd_en && (~rempty);
//wptr & rptr
always @(posedge wclk or negedge rst_n)
begin
if(!rst_n)
wptr <= {(ADDR_WIDTH+1){1'b0}};
else if(wenc)
wptr <= wptr + 1'b1;
end
always @(posedge rclk or negedge rst_n)
begin
if(!rst_n)
rptr <= {(ADDR_WIDTH+1){1'b0}};
else if(renc)
rptr <= rptr + 1'b1;
end
//bin to gray sync
assign wptr_gray = wptr ^ (wptr >> 1);
assign rptr_gray = rptr ^ (rptr >> 1);
//timing logic outputs gray prevent burrs
always @(posedge wclk or negedge rst_n)
begin
if(!rst_n)
wptr_gray1 <= {(ADDR_WIDTH+1){1'b0}};
else
wptr_gray1 <= wptr_gray;
end
always @(posedge rclk or negedge rst_n)
begin
if(!rst_n)
rptr_gray1 <= {(ADDR_WIDTH+1){1'b0}};
else
rptr_gray1 <= rptr_gray;
end
//use write clock to sync read addr
always @(posedge rclk or negedge rst_n)
begin
if(!rst_n)begin
wptr_gray2 <= {(ADDR_WIDTH+1){1'b0}};
wptr_gray3 <= {(ADDR_WIDTH+1){1'b0}};
end
else begin
wptr_gray2 <= wptr_gray1;
wptr_gray3 <= wptr_gray2;
end
end
//use read clock to sync write addr
always @(posedge wclk or negedge rst_n)
begin
if(!rst_n)begin
rptr_gray2 <= {(ADDR_WIDTH+1){1'b0}};
rptr_gray3 <= {(ADDR_WIDTH+1){1'b0}};
end
else begin
rptr_gray2 <= rptr_gray1;
rptr_gray3 <= rptr_gray2;
end
end
//wfull & rempty
assign wfull = wptr_gray == {~rptr_gray3[ADDR_WIDTH:ADDR_WIDTH-1],rptr_gray3[ADDR_WIDTH-2:0]};
assign rempty = rptr_gray == wptr_gray3;
//gray to bin
generate
genvar i;
for(i=ADDR_WIDTH;i>=0;i=i-1)
begin:gray_bin
if(i==ADDR_WIDTH)begin
assign wptr_gray_bin[i] = wptr_gray3[i];
assign rptr_gray_bin[i] = rptr_gray3[i];
end
else begin
assign wptr_gray_bin[i] = wptr_gray_bin[i+1] ^ wptr_gray3[i];
assign rptr_gray_bin[i] = rptr_gray_bin[i+1] ^ rptr_gray3[i];
end
end
endgenerate
//almost wfull & almost rempty
always@(*)
begin
if(wptr[ADDR_WIDTH] == rptr_gray_bin[ADDR_WIDTH])
alfull_cnt = wptr[ADDR_WIDTH-1:0] - rptr_gray_bin[ADDR_WIDTH-1:0];
else
alfull_cnt = FIFO_DEPTH - (rptr_gray_bin[ADDR_WIDTH-1:0] - wptr[ADDR_WIDTH-1:0]);
end
assign al_wfull = alfull_cnt >= ALMOST_WFULL ;
always@(*)
begin
if(rptr[ADDR_WIDTH] == wptr_gray_bin[ADDR_WIDTH])
alempty_cnt = wptr_gray_bin[ADDR_WIDTH-1:0] - rptr[ADDR_WIDTH-1:0];
else
alempty_cnt = FIFO_DEPTH - (rptr[ADDR_WIDTH-1:0] - wptr[ADDR_WIDTH-1:0]);
end
assign al_rempty = alempty_cnt <= ALMOST_REMPTY;
endmodule
3、tb_async_fifo
测试代码:
`timescale 1ns/1ps
module tb_async_fifo;
parameter DATA_WIDTH = 8;
parameter FIFO_DEPTH = 8;
parameter ALMOST_WFULL = 6;
parameter ALMOST_REMPTY = 2;
reg wclk ;
reg rclk ;
reg rst_n ;
reg wr_en ;
reg rd_en ;
reg [DATA_WIDTH-1:0] wdata ;
wire [DATA_WIDTH-1:0] rdata ;
wire wfull ;
wire al_wfull ;
wire rempty ;
wire al_rempty;
async_fifo #(
.DATA_WIDTH (DATA_WIDTH ),
.FIFO_DEPTH (FIFO_DEPTH ),
.ALMOST_WFULL (ALMOST_WFULL ),
.ALMOST_REMPTY(ALMOST_REMPTY)
)u_async_fifo(
.wclk (wclk ),
.rclk (rclk ),
.rst_n (rst_n ),
.wr_en (wr_en ),
.rd_en (rd_en ),
.wdata (wdata ),
.rdata (rdata ),
.wfull (wfull ),
.al_wfull (al_wfull ),
.rempty (rempty ),
.al_rempty (al_rempty)
);
always #5 rclk = ~rclk;
always #8 wclk = ~wclk;
reg [7:0] tempdata=0;
initial begin
wclk = 0;
rclk = 0;
rst_n = 0;
wr_en = 0;
rd_en = 0;
wdata = 0;
#15
rst_n = 1;
push(1);
fork
push(2);
pop(tempdata);//出现了假空现象
join
push(10);
push(20);
push(30);
push(40);
push(50);
push(60);
push(70);
push(80);
push(90);
push(100);
push(110);
push(120);
push(130);
pop(tempdata);
push(tempdata);//出现了假满现象
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(140); //假几乎满
pop(tempdata);
push(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(5);
pop(tempdata);
#300
$finish;
end
initial begin
$vcdpluson();
$vcdplusmemon();
$fsdbDumpfile("test.fsdb");
$fsdbDumpvars(0,tb_async_fifo,"+parameter");
$fsdbDumpMDA;
end
task push(input [7:0] data);
if(wfull)
$display("---cannot push %d:Buffer Full---",data);
else if(al_wfull)
begin
$display(" pushed %d:Almost Full---",data);
wdata = data;
wr_en = 1;
@(posedge wclk);
#5 wr_en = 0;
end
else begin
$display("pushed:",data);
wdata = data;
wr_en = 1;
@(posedge wclk);
#5 wr_en = 0;
end
endtask
task pop(output [7:0] data);
if(rempty)
$display("---cannot pop :Buffer Empty---");
else if(al_rempty)
begin
rd_en = 1;
@(posedge rclk);
#3 rd_en = 0;
data = rdata;
$display("poped %d:Almost Empty---",data);
end
else begin
rd_en = 1;
@(posedge rclk);
#3 rd_en = 0;
data = rdata;
$display("poped:",data);
end
endtask
endmodule
本文详细探讨了异步FIFO在跨时钟域数据存储读写中的应用,重点讨论了亚稳态现象、单bit和多bit信号的同步策略。还介绍了使用格雷码来降低亚稳态风险和实现FIFO的空满标志检测。最后,概述了异步FIFO的RTL代码设计,包括dual_port_sram和async_fifo模块。

3319

被折叠的 条评论
为什么被折叠?



