异步FIFO

异步fifo

异步FIFO就是读写地址不同,要么不同频率,要么同频不同相。读地址和空标志由读时钟产生,写地址和满标志由写时钟产生

问题

  1. 异步fifo读写时钟不一致,当要产生fifo的空满标志位时,需要比较读写地址。比如产生空标志位需要在读时钟下采样写地址,并跟读地址进行比较。由于地址一般是多位,在读时钟下各位跳变不一样,产生毛刺,要过一段时间才能稳定,如果在不稳定阶段采样到了数据会发生错误

    解决方法是格雷码,格雷码每次跳变一位。 格雷码相邻数据只跳变一位

  2. 跨时钟采样读写地址可能会有1T的延迟,但这个延迟并不会导致full/empty错误置位。

如果地址信号传递到读时钟域时延时了 1T,此时接收端并不知道数据已经写入了 FIFO,仍然认为 FIFO 是空的,这种情况只会对 FIFO 的吞吐率 throughput 有影响,但是不会导致 underflow; 如下图,先写满 FIFO,然后开始读:在 t6 时 FIFO 读空,empty = 1,在 t7 时,写入了一个新数据,此时 FIFO 内已经有有效数据了,但是 wr_ptr 同步到读时钟域要花费 2T,所以在 t9 时 empty = 0。有两个时钟周期(t7, t8) rd 被阻塞了,但是并不影响 FIFO 正常工作。


wtr_ptr_asyn这个异步的写地址是读时钟域延迟两拍采样到的写地址,用来进行进行读空判断。

如果地址信号传递到写时钟域时延是了 1T,此时发送端并不知道 FIFO 已经有空余地址了,仍然认为 FIFO 是满的,这种情况也是只会对 FIFO 的吞吐率 throughput 有影响,但是不会导致 overflow; 如下图,先写满 FIFO,然后开始读:在 t5 时,full = 1,在 t6 时,读出了一个数据,此时 FIFO 已经有空余地址了,但是 rd_ptr 同步到写时钟域要花费 2T,所以在 t8 时 full = 0。有两个时钟周期(t6, t7) wr 被阻塞了,但是并不影响 FIFO 正常工作。


rd_ptr_asyn这个异步的读地址是写时钟域延迟两拍采样到的读地址,用来进行进行写满判断。

空满标志

  1. 异步fifo也可以增加一位地址位作为空满的判断,根据格雷码的格式


如上图,如果还是按照同步fifo的判断方式,读写地址一样时判断为空,如圆圈1,2,这样是正确的;但如果根据MSB不同,其他位相同,判断fifo满,如圆圈3,这样出错,此时fifo并没有满。

异步FIFO增加一位地址用来判断空满。如果读写指针最高位不同,此时如果读写对同一块内存区域,那么会产生满标志。从上面格雷码中可以看到,对于内存地址只有三位,表示的FIFO空间只有8个字节,第四位是添加的一位。如果内存地址被套圈,比如读0000,写1100,最高位不同,次高位也不同,其他位相同。所以空满的关系如下:

空:读写地址相同

满:MSB和次MSB位不同,其他位相同

Cummings异步fifo设计

以下是参考Cummings的异步fifo论文

2002-SD_Simulation and Synthesis Techniques for Asynchronous FIFO Design

FIFO的结构如下图

其中格雷码转换器的结构如下图,将n位的二进制地址和其转换成的格雷码用寄存器寄存

这篇论文中fifo的空满判断: 空:读写地址相同;满:MSB和次MSB位不同,其他位相同。

根据结构图分成五个子模块和一个top模块,代码如下:

注意:代码基本上跟论文中的一样,编译通过了,但没有进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
module asyn_fifo_top
#(
parameter WIDTH=8,
parameter DEPTH=4
)
(
input wclk,winc,wrst_n,
input rclk,rinc,rrst_n,
input[WIDTH-1:0] data_i,
output [WIDTH-1:0] data_o,
output full,
output empty
);
wire [DEPTH:0] wptr,rptr;
wire [DEPTH :0] r2_wptr;
wire [DEPTH :0] w2_rptr;
wire [DEPTH-1:0] raddr,waddr;

// 写地址被采样到读时钟域
w2r #(DEPTH) w2r_u(.rclk(rclk),.rrst_n(rrst_n),.wptr(wptr),.r2_wptr(r2_wptr));
// 读地址被采样到写时钟域
r2w #(DEPTH) r2w_u(.wclk(wclk),.wrst_n(wrst_n),.rptr(rptr),.w2_rptr(w2_rptr));
// 空标志产生模块
empty_r #(WIDTH,DEPTH) empty_u(.rclk(rclk),.rrst_n(rrst_n),.rinc(rinc),.r2_wptr(r2_wptr),.rptr(rptr),.raddr(raddr),.empty(empty));
// 满标志产生模块
full_w #(WIDTH,DEPTH) full_u(.wclk(wclk),.wrst_n(wrst_n),.winc(winc),.w2_rptr(w2_rptr),.wptr(wptr),.waddr(waddr),.full(full));
// fifo读写模块
fifomem #(WIDTH,DEPTH) mem_u(.wclk(wclk),.winc(winc),.full(full),.raddr(raddr),.waddr(waddr),.data_i(data_i),.data_o(data_o));
endmodule

// w2r.v
module w2r
#(
parameter DEPTH=4
)
(
input rclk,rrst_n,
input [DEPTH:0] wptr,
output reg[DEPTH:0] r2_wptr
);

reg [DEPTH:0] r1_wptr;

always@(posedge rclk or negedge rrst_n) begin
if(rrst_n == 1'b0) {r2_wptr,r1_wptr} <= 0;
else {r2_wptr,r1_wptr} <= {r1_wptr,wptr};
end
endmodule
// r2w.v
module r2w
#(
parameter DEPTH=4
)
(
input wclk,wrst_n,
input [DEPTH:0] rptr,
output reg [DEPTH:0] w2_rptr
);
reg [DEPTH:0] w1_rptr;
always@(posedge wclk or negedge wrst_n) begin
if(wrst_n == 1'b0) {w2_rptr,w1_rptr} <= 0;
else {w2_rptr,w1_rptr} <= {w1_rptr,rptr};
end
endmodule

// empty_r.v
module empty_r
#(
parameter WIDTH=8,
parameter DEPTH=4
)
(
input rclk,rrst_n,rinc,
input [DEPTH:0] r2_wptr,
output reg[DEPTH:0] rptr,
output [DEPTH-1:0] raddr,
output reg empty
);
reg [DEPTH:0] rbin;
wire [DEPTH:0] binnext,graynext;
wire empty_val;
assign binnext = (rinc & !empty) ? rbin : rbin+1'b1;
assign graynext = binnext ^ (binnext>>1);
assign raddr = rbin[DEPTH-1:0];
always@(posedge rclk or negedge rrst_n) begin
if(rrst_n == 1'b0) {rbin,rptr} <= 0;
else {rbin,rptr} <= {binnext,graynext};
end
// empty
assign empty_val = (r2_wptr==rptr);
always@(posedge rclk or negedge rrst_n) begin
if(rrst_n == 1'b0) empty <= 1;
else empty <= empty_val;
end
endmodule

// full_w.v
module full_w
#(
parameter WIDTH=8,
parameter DEPTH=4
)
(
input wclk,wrst_n,winc,
input [DEPTH:0] w2_rptr,
output reg[DEPTH:0] wptr,
output [DEPTH-1:0] waddr,
output reg full
);
wire full_val;
reg [WIDTH:0] wbin;
wire [WIDTH:0] binnext,graynext;

assign waddr = wbin[WIDTH-1:0];
assign binnext = wbin + (winc & !full);
assign graynext = binnext ^ (binnext>>1);

always@(posedge wclk or negedge wrst_n) begin
if(wrst_n==1'b0) {wbin,wptr} <= 0;
else {wbin,wptr} <= {binnext,graynext};
end

assign full_val = w2_rptr == {~wptr[DEPTH:DEPTH-1],wptr[DEPTH-2:0]};
always@(posedge wclk or negedge wrst_n) begin
if(wrst_n==1'b0) full <= 1'b0;
else full <= full_val;
end
endmodule

// fifomem.v
module fifomem
#(
parameter WIDTH=8,
parameter DEPTH=4
)
(
input wclk,winc,full,
input [DEPTH-1:0] raddr,
input [DEPTH-1:0] waddr,
input [WIDTH-1:0] data_i,
output [WIDTH-1:0] data_o
);
reg [WIDTH-1:0] memory[0:(1<<DEPTH)-1];
always@(posedge wclk ) begin
if(winc & !full) memory[waddr] <= data_i;
end
assign data_o = memory[raddr];
endmodule

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!