设计思想

FIFO也就是先进先出的队列,是一种特殊的RAM,特殊在读写地址默认是自增1,所以FIFO内部管理读写地址,不需要暴露读写地址端口。
同步FIFO指读写使用同一个时钟,异步FIFO则读写使用不同的时钟,同步FIFO相对简单一些,异步FIFO还涉及到亚稳态、格雷码和二进制的转化等问题。

FIFO的难点在于空满的判断,这里同步FIFO的空满判断有两种方式,一是使用计数器,这个很简单,fifo_cnt等于0就为空,等于深度就满,二是使用读写指针进行判断,这里我使用第一种方式。

下面给出了经典同步fifo设计的源码,用来面试手撕,采用了参数化、$clog2函数,代码很规范也很好记。
总结一下博客,重点强调下记忆的方法,方便面试手撕代码,如有错误的地方恳请指正!

端口

分三方面记忆,时钟复位+读+写。注意读写不光有数据,还有使能和空满信号。

代码块

一共可以分为六个代码块:

  • 读数据部分有读指针always块、读操作always块;
  • 写数据部分有写指针always块、写操作always块;
  • 空满判断部分有fifo元素数量always块、空满判断assign块;

寄存器

包括:

  • fifo_buffer存储数据
  • fifo_cnt记录当前fifo元素数量
  • rd_pointer和wr_pointer指针

同步FIFO代码

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
module syn_fifo
#
(
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 8,
)
(
input clk ,
input rst_n ,

// read
output [DATA_WIDTH - 10] read_data ,
input rd_en ,
output rd_empty ,

// write
input [DATA_WIDTH - 10] write_data ,
input wr_en ,
output wr_full ,
);

// Parameters
parameter DEPTH_WIDTH = $clog2(DATA_DEPTH) ;

// Regs
reg [DATA_WIDTH - 10] fifo_buffer [0 : DATA_DEPTH - 1] ;
reg [DATA_DEPTH : 0] fifo_cnt ;
reg [DATA_DEPTH - 1 : 0] wr_pointer,rd_pointer ;

// read pointer
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
read_pointer <= 'b0;
end
else begin
if( rd_en && !rd_empty) begin
if( rd_pointer == DATA_DEPTH - 1) begin
rd_pointer <= 'b0;
end else begin
rd_pointer <= rd_pointer + 1'b1;
end
end

end
end

// read operation
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
read_data <= 'b0;
end
else begin
read_data <= fifo_buffer[rd_pointer];
end
end

// write pointer
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
wr_pointer <= 'b0;
end
else begin
if( wr_en && !wr_full) begin
if( wr_pointer == DATA_DEPTH - 1) begin
wr_pointer <= 'b0;
end else begin
wr_pointer <= wr_pointer + 1'b1;
end
end

end
end

// write operation
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
fifo_buffer[wr_pointer] <= 'b0;
end
else begin
fifo_buffer[wr_pointer] <= read_data;
end
end

// current fifo_cnt in fifo_buffer
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
fifo_cnt <= 'b0;
end
else begin
fifo_cnt <= fifo_cnt + 1'b1;
end
end


// empty and full
assign full = (fifo_cnt == DATA_DEPTH);
assign empty = (fifo_cnt == 0 );


endmodule