异步FIFO电路结构
双端口SRAM:
用来存储上游节点写入的数据wdata,下游节点用rdata将其读出。
SRAM的读写地址采用了每次只递增1的机制,保证了写入和读出按顺序进行,写和读到最高地址后,重新返回零地址。
满信号生成电路
在上游节点和SRAM之间有一个满信号生成电路。
这个电路通过判断写时钟域下,写指针和读指针的关系,然后实时生成满信号wfull,以通知上游节点停止写操作。
空信号生成电路
在下游节点和SRAM之间有一个空信号生成电路,
这个电路通过判断读时钟域下,写指针和读指针的关系,然后实时生成空信号rempty,以通知下游节点停止读操作。
注意:
将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号,因此,这里就涉及到如何处理信号传输的亚稳态问题。
使用同步器把信号传递到对面的时钟域进行同步处理,可以采用同步器(由2~3级FF组成)对单bit的信号进行同步操作。
FIFO空满状态的判断:
判断读指针rptr和写指针wptr的关系(格雷码):
读指针和写指针在地址位中添加一个额外的位(extra bit)。
“空”状态的判断:
读指针和写指针二者完全相等(包括MSB)
1
2
3读指针:rptr[n-1] 写指针:wptr[n-1] empty=(rptr == wptr)
“满”的判断:
- wptr和同步过来的rptr的MSB不相等, 因为wptr必须比rptr多折回一次
- wptr与rptr的次高位不相等(如上图位置 7和位置15,转化为二进制对应的是 0111和1111,MSB不同说明多折回一 次, 111相同代表同一位置。)
- 剩下的其余位完全相等
1
2
3
4读指针:rptr[n-1] 写指针:wptr[n-1] full= (rptr[n-1] != wptr[n-1] )&&(rptr[n-2:0] == wptr[n-2:0])
二进制码和格雷码的转换:
二进制码转化为格雷码:
从最右边第一位开始,依次将每一位与左邻 一位异或(XOR),作为对应格雷码该位的值,最左边一位不变。
转换公式:
1
2
3
4
5
6
7二进制码:B[n-1:0] 格雷码: G[n-1:0] //二进制转换成格雷码 G[n-1] = B[n-1] G[i] = B[i+1]^B[i] //i=0、1、2、、、n-2 //格雷码的第i位的值等于二进制第i位和i+1位的异或
例子:
格雷码转化为二进制码:
从左边第二位起,将每位与左边一位解码后的 值异或(XOR),作为该位解码后的值(最左边一位依然不变)。
转换公式:
1
2
3
4
5
6二进制码:B[n-1:0] 格雷码: G[n-1:0] //格雷码转换成二进制 G[n-1] = B[n-1] B[i] = G[i]^B[i+1] //i=0、1、2、、n-2
例子:
注意“虚空”、“虚满”
假设,读指针rptr地址为3,在写时钟控制下,同步两个时钟进入写时钟域,即信号rptr_sync_2。
这时,在写时钟域下,刚好满足“满”的条件,生成满信号。
但是,此时rptr的值已经更新为5。
即在FIFO认为自己已经满的时候,读地址,又从里面读走了两个数据。实际FIFO并没有真正的满,只是接近满“虚满”。
结论:
- 对于full信号的生成机制,同步后的读地址一定是小于或者等于当前的 读地址,所以此时判断FIFO为满不一定是真满,这样更保守;
- Empty信号的机制同样成立, “空”时,不一定是真“空”
总结:
异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,此机制保证 了FIFO在空满极限情况下,依然留有余量,存在一定的冗余空间。
这种方法使得FIFO不会发生写满溢出、读空多读的情况。
异步FIFO的Verilog代码描述
空满标志生成
以写地址同步到读时钟域为例,示意图:
二进制码地址生成格雷码地址
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//write interface pointer //生成写地址指针 always@(posedge wr_clk or negedge wr_rst_n_i ) if(!wr_rst_n_i) wr_ptr<= {ADDR_WIDTH+1}{1'b0}; else if(wr_en_i && !full_o) wr_ptr <=wr_ptr +1'b1; else wr_ptr <=wr_ptr; //##############二进制码->格雷码############### //#######格雷码写地址 assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ; always@(*)begin gray_wr_ptr_next= gray_wr_ptr; end //-------------------------------------------------------------------------------- //read interface pointer //生成读地址指针 always@(posedge rd_clk or negedge rd_rst_n_i ) if(!rd_rst_n_i) rd_ptr <= {ADDR_WIDTH+1}{1'b0}; else if(rd_en_i && !empty_o) rd_ptr <=rd_ptr +1'b1; else rd_ptr <=rd_ptr; //##############二进制码->格雷码############### //#######格雷码读地址 assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ; always@(*)begin gray_rd_ptr_next= gray_rd_ptr; end
格雷码地址同步到对方地址
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//格雷码写地址同步两级 传递到读时钟域 always@(posedge rd_clk or negedge rd_rst_n) begin if(! rd_rst_n) begin gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0}; gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0}; end else begin gray_wr2rd_ptr_1 <= gray_wr_ptr_next; gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1; end end //格雷码读地址同步两级 传递到写时钟域 always@(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) begin gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0}; gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0}; end else begin gray_rd2wr_ptr_1 <= gray_rd_ptr_next; gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1; end end
生成空满标志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//#####空满标志产生 //满标志 assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr ); always@(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) full_o <=1'b0; else full_o <= full_comb; end //空标志 assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr ); always@(posedge rd_clk or negedge rd_rst_n) begin if(!rd_rst_n) empty_o <=1'b0; else empty_o <= empty_comb; end
读写数据
读数据
1
2
3
4
5
6
7
8//read data always@(posedge rd_clk or negedge rd_rst_n_i) if(!rd_rst_n_i) begin rd_data_o<= 0; end esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空 rd_data_o <= RAM[rd_ptr];
写数据
1
2
3
4
5
6
7
8
9
10
11//write data interger i; always@(posedge wr_clk or negedge wr_rst_n_i ) if(!wr_rst_n_i) begin for(i=0;i<FIFO_DEPTH;i=i+1)begin RAM[i] <= 0; end end esle if(wr_en_i && !full_comb) RAM[wr_ptr] <= wr_data_i;
完整代码
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
149
150
151
152
153
154
155
156
157
158module async_fifo #(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=3) ( //write interface input wire wr_clk, input wire wr_rst_n_i, input wire wr_en_i, input wire [DATA_WIDTH-1:0] wr_data_i, //read interface input wire rd_clk, input wire rd_rst_n_i, input wire rd_en_i, output reg [DATA_WIDTH-1:0] wr_data_o, //flags output full_o, output empty_o ); wire FIFO_DEPTH = 1<< ADDR_WIDTH;//2^3=8 // RAM definition reg [DATA_WIDTH-1:0] RAM [0:FIFO_DEPTH-1]; //fifo width :DATA_WIDTH 32bit; fifo depth:FIFO_DEPTH 8 //读写指针 reg [ADDR_WIDTH:0] wr_ptr; //写指针 reg [ADDR_WIDTH:0] rd_ptr; //读指针 //写指针 二进制->格雷码 //格雷码写地址 reg [ADDR_WIDTH:0] gray_wr_ptr; //格雷码写地址 reg [ADDR_WIDTH:0] gray_wr_ptr_next;//格雷码写地址同步1拍 // 格雷码写地址同步到读时钟 reg [ADDR_WIDTH:0] gray_wr2rd_ptr_1; //格雷码写地址同步到读时钟同步1拍 reg [ADDR_WIDTH:0] gray_wr2rd_ptr_2; //格雷码写地址同步到读时钟同步2拍 //读地址 二进制->格雷码 //格雷码读地址 reg [ADDR_WIDTH:0] gray_rd_ptr; //格雷码读地址 reg [ADDR_WIDTH:0] gray_rd_ptr_next; //格雷码读地址同步1拍 //格雷码读地址同步到写时钟 reg [ADDR_WIDTH:0] gray_rd2wr_ptr_1;//格雷码读地址同步到写时钟同步1拍 reg [ADDR_WIDTH:0] gray_rd2wr_ptr_2;//格雷码读地址同步到写时钟同步2拍 //组合逻辑空满标志 wire full_comb; wire empty_comb; //########读写地址指针产生#################### //write interface pointer always@(posedge wr_clk or negedge wr_rst_n_i ) if(!wr_rst_n_i) wr_ptr<= {ADDR_WIDTH+1}{1'b0}; else if(wr_en_i && !full_o) wr_ptr <=wr_ptr +1'b1; else wr_ptr <=wr_ptr; //read interface pointer always@(posedge rd_clk or negedge rd_rst_n_i ) if(!rd_rst_n_i) rd_ptr <= {ADDR_WIDTH+1}{1'b0}; else if(rd_en_i && !empty_o) rd_ptr <=rd_ptr +1'b1; else rd_ptr <=rd_ptr; //##############二进制码->格雷码############### //#######格雷码写地址 assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ; always@(*)begin gray_wr_ptr_next= gray_wr_ptr; end //格雷码写地址同步两级 传递到读时钟域 always@(posedge rd_clk or negedge rd_rst_n) begin if(! rd_rst_n) begin gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0}; gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0}; end else begin gray_wr2rd_ptr_1 <= gray_wr_ptr_next; gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1; end end //#######格雷码读地址 //#######格雷码读地址 assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ; always@(*)begin gray_rd_ptr_next= gray_rd_ptr; end //格雷码读地址同步两级 传递到写时钟域 always@(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) begin gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0}; gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0}; end else begin gray_rd2wr_ptr_1 <= gray_rd_ptr_next; gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1; end end //#####空满标志产生 //满标志 assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr ); always@(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) full_o <=1'b0; else full_o <= full_comb; end //空标志 assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr ); always@(posedge rd_clk or negedge rd_rst_n) begin if(!rd_rst_n) empty_o <=1'b0; else empty_o <= empty_comb; end //########################################## //write data interger i; always@(posedge wr_clk or negedge wr_rst_n_i ) if(!wr_rst_n_i) begin for(i=0;i<FIFO_DEPTH;i=i+1)begin RAM[i] <= 0; end end esle if(wr_en_i && !full_comb) RAM[wr_ptr] <= wr_data_i; //read data always@(posedge rd_clk or negedge rd_rst_n_i) if(!rd_rst_n_i) begin rd_data_o<= 0; end esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空 rd_data_o <= RAM[rd_ptr]; endmodule
参考
芯动力——硬件加速设计方法(3.2、3.3、3.4)
异步FIFO编程练习
练习网站:
牛客网-在线编程-Verilog练习
里面还有其他人总结的各种题解,可以自己总结下思路。写完代码可以在线运行,检查自己的代码哪里有问题。
最后
以上就是怡然书包最近收集整理的关于异步FIFO异步FIFO电路结构FIFO空满状态的判断: 异步FIFO的Verilog代码描述异步FIFO编程练习的全部内容,更多相关异步FIFO异步FIFO电路结构FIFO空满状态内容请搜索靠谱客的其他文章。
发表评论 取消回复