fifo¶
模块描述¶
异步FIFO的实现主体,分别实例化了一组双端口存储器和一组读/写地址控制逻辑
参数说明¶
parameter FIFO_DEPTH = 8 ;
parameter FIFO_DAT_WD = 4 ;
parameter FIFO_ADDR_WD = 3 ;
FIFO_DEPTH : FIFO的深度,即一共可以存储多少个数据
FIFO_DAT_WD : FIFO的数据位宽
FIFO_ADDR_WD : FIFO地址位所需的最大位宽(与深度相对应)
I/O端口及变量说明¶
input wr_clk ;
input wr_rstn ;
input [FIFO_DAT_WD -1:0] wr_dat_i ;
input wr_en_i ;
input rd_clk ;
input rd_rstn ;
output [FIFO_DAT_WD -1:0] rd_dat_o ;
input rd_en_i ;
wire wr_full_w ;
wire rd_empty_w ;
wire [FIFO_ADDR_WD -1:0] rd_addr_w ;
wire [FIFO_ADDR_WD -1:0] wr_addr_w ;
端口名称 |
端口类型 |
描述 |
具体说明 |
|---|---|---|---|
wr_clk |
输入 |
写时钟 |
- |
wr_rstn |
输入 |
写时钟域的异步复位信号 |
- |
wr_dat_i |
输入 |
待写入数据(与写时钟同步) |
- |
wr_en_i |
输入 |
写使能信号(与写时钟同步) |
其有效且未写满时,向FIFO中写入待写入数据并令写地址+1 |
rd_clk |
输入 |
读时钟 |
- |
rd_rstn |
输入 |
读时钟域的异步复位信号 |
- |
rd_dat_o |
输出 |
数据读出端(与读时钟同步) |
- |
rd_en_i |
输入 |
读使能信号(与读时钟同步) |
其有效且未读空时,从FIFO中读出数据到rd_dat_o并令读地址+1 |
变量名称 |
变量类型 |
描述 |
具体说明 |
|---|---|---|---|
wr_full_w |
wire |
写满标志信号 |
由写指针和同步到写时钟域的读指针决定,若写指针追上读指针,则表示写满,在有数据被读出前不允许继续写入新数据 |
rd_empty_w |
wire |
读空标志信号 |
由读指针和同步到读时钟域的写指针决定,若读指针追上写指针,则表示读空,在有新数据被写入前不允许继续读取数据 |
rd_addr_w |
wire |
读地址(读指针) |
指向FIFO中下一个待读取的数据,编码方式为格雷码 |
wr_addr_w |
wire |
写地址(写指针) |
指向FIFO中下一个待写入数据的位置,编码方式为格雷码 |
工作时序¶
上电,读时钟域和写时钟域分别完成复位操作
写时钟域
当写使能信号有效且写满标志位无效(未写满)时,将wr_dat_i写入FIFO中写指针wr_addr_w指向的位置,并将写指针+1(指向下一个待写入位置)
在完成一次写入(写指针发生变化)或者同步过来的读指针发生变化后,比较当前写指针+1后的地址与同步过来的读指针的地址
若二者相等,意味着写指针已经再次追上读指针(滞后的),此时不可继续写入,写满标志位置1(写满有效)
若二者不相等,意味着写指针未再次追上读指针(滞后的),允许写入,写满标志位置0(写满无效)
读时钟域
当读使能信号有效且读空标志位无效(未读空)时,将FIFO中读指针rd_addr_w指向处的数据读出到rd_dat_o,并将读指针+1(指向下一个待读出位置)
在完成一次读出(读指针发生变化)或者同步过来的写指针发生变化后,比较当前读指针的地址与同步过来的写指针的地址
若二者相等,意味着读指针已经追上写指针(滞后的),此时不可继续读FIFO数据,读空标志位置1(读空有效)
若二者不相等,意味着读指针未追上写指针(滞后的),允许读出FIFO数据,读空标志位置0(读空无效)
备注¶
当写满/读空有效时,即使写/读使能有效,也不能执行写/读以及指针+1的操作
从时序上来看是先写入/读取后再执行指针+1,但二者是在同一个时钟边沿完成的,尽管如此,考虑到实际路径的延时,写入/读取操作肯定会早于指针+1,因此不必考虑写入/读取位置错误的可能性
所谓的同步过来的指针是“滞后的”,是指当前反馈到写时钟域的读指针(反馈到读时钟域的写指针类似)是滞后于当前的读指针的
原因:考虑到两个异步信号直接比较时会产生亚稳态问题,进行了两拍同步
带来的影响:当“写满”信号有效时,写指针并未完全追上实际的读指针,即实际并未写满(“虚满”),这不会导致数据同步出现错误,仅仅增大了同步的延时
慢时钟域同步快时钟域的地址时,会出现遗漏问题,但这同样不会导致同步出现错误,因为这一情况并不会跳过“读空”或者“写满状态”(即任一指针实际上并不会越过另一指针),原理与上类似
RTL代码¶
注:此顶层模块中仅进行了模块例化,所涉及具体逻辑请参考各子模块
1module fifo(
2 wr_clk ,
3 wr_rstn ,
4 wr_dat_i ,
5 wr_en_i ,
6 rd_clk ,
7 rd_rstn ,
8 rd_dat_o ,
9 rd_en_i
10);
11
12 parameter FIFO_DEPTH = 8 ;
13 parameter FIFO_DAT_WD = 4 ;
14 parameter FIFO_ADDR_WD = 3 ;
15
16
17 input wr_clk ;
18 input wr_rstn ;
19 input [FIFO_DAT_WD -1:0] wr_dat_i ;
20 input wr_en_i ;
21 input rd_clk ;
22 input rd_rstn ;
23 output [FIFO_DAT_WD -1:0] rd_dat_o ;
24 input rd_en_i ;
25
26
27 wire wr_full_w ;
28 wire rd_empty_w ;
29 wire [FIFO_ADDR_WD -1:0] rd_addr_w ;
30 wire [FIFO_ADDR_WD -1:0] wr_addr_w ;
31
32 fifo_ctrl #(.FIFO_ADDR_WD(FIFO_ADDR_WD)) fifo_ctrl(
33 .rd_clk ( rd_clk ),
34 .rd_rstn ( rd_rstn ),
35 .rd_en_i ( rd_en_i ),
36 .rd_empty_o ( rd_empty_w ),
37 .rd_ptr_o ( rd_addr_w ),
38 .wr_clk ( wr_clk ),
39 .wr_rstn ( wr_rstn ),
40 .wr_en_i ( wr_en_i ),
41 .wr_full_o ( wr_full_w ),
42 .wr_ptr_o ( wr_addr_w )
43 );
44
45 fifo_mem #(.FIFO_DEPTH(FIFO_DEPTH), .FIFO_DAT_WD(FIFO_DAT_WD), .FIFO_ADDR_WD(FIFO_ADDR_WD)) fifo_mem(
46 .wr_clk ( wr_clk ),
47 .wr_rstn ( wr_rstn ),
48 .wr_dat_i ( wr_dat_i ),
49 .wr_en_i ( wr_en_i ),
50 .wr_addr_i ( wr_addr_w ),
51 .wr_full_i ( wr_full_w ),
52 .rd_clk ( rd_clk ),
53 .rd_rstn ( rd_rstn ),
54 .rd_dat_o ( rd_dat_o ),
55 .rd_en_i ( rd_en_i ),
56 .rd_addr_i ( rd_addr_w ),
57 .rd_empty_i ( rd_empty_w )
58 );
59
60endmodule