同步FIFO设计

1、方法

(1)空满标志

使用计数器计数,当写数据时候计数加一,当读数据时减一,这样当计数值为 0 时表示空,当计数值达到FIFO深度时则表示满;

(2)读写指针

当写使能且非满时候写地址加1;

当读使能且非空时候读地址加1;

(3)读写数据

根据读写地址、空满标志、读写使能信号写入和读取数据到RAM;

2、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
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

module fifo_sync
#(
parameter DEPTH = 3,
parameter WIDTH = 8
)
(
input clk,
input rstn,
input ren,
input wen,
input [WIDTH-1:0] din,
output reg [WIDTH-1:0] dout,
output empty,
output full
);

parameter DEPTH_CNT = 1<<DEPTH;

reg [DEPTH-1:0] wr_addr;
reg [DEPTH-1:0] rd_addr;
reg [DEPTH:0] count;

reg [WIDTH-1:0] buf_mem[0:DEPTH_CNT-1];

always @(posedge clk or negedge rstn) begin
if(!rstn) begin
count <= 0;
end else begin
if(wen && !full && ren && !empty) begin
count <= count;
end
else if(wen && !full) begin
count <= count + 1;
end
else if(ren && !empty) begin
count <= count - 1;
end
end
end

always @(posedge clk or negedge rstn)
begin
if(!rstn) begin
wr_addr <= 0;
end else begin
if(wr_addr == (DEPTH_CNT-1)) begin
wr_addr <= 0;
end
else if(wen && !full) begin
wr_addr <= wr_addr + 1;
end
end
end

always @(posedge clk or negedge rstn)
begin
if(!rstn) begin
rd_addr <= 0;
end else begin
if(rd_addr == (DEPTH_CNT -1)) begin
rd_addr <= 0;
end
else if(ren && !empty) begin
rd_addr <= rd_addr + 1;
end
end
end

always @(posedge clk)
begin
if(wen && !full) begin
buf_mem[wr_addr] <= din;
end
end

always @(posedge clk or negedge rstn)
begin
if(!rstn) begin
dout <= 0;
end else begin
if(ren && !empty) begin
dout <= buf_mem[rd_addr];
end
end
end

assign full = (count == DEPTH_CNT);
assign empty = (count == 0);

endmodule

3、仿真
  • testbench :
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
`timescale 1ns/100ps

module tb_fifo_sync();

parameter CLK_PERIOD = 5;

reg clk;
reg rstn;

// clk gen
initial clk = 1'b0;
always #CLK_PERIOD clk = ~clk;

reg ren;
reg wen;
reg [7:0] din;
wire [7:0] dout;

wire full;
wire empty;

// rst
initial begin
rstn = 1'b1;
#200;
rstn = 1'b0;
repeat(4) @(posedge clk);
rstn <= 1'b1;

//init
ren = 0;
wen = 0;
din = 0;
#200;

//write 2 -> ok
//read 2 -> ok, now empty
//read 3 -> empty
//write 10 -> full
//read 12 -> empty

fifo_write(2);
repeat(2) @(posedge clk);

fifo_read(2);
repeat(2) @(posedge clk);

fifo_read(3);
repeat(2) @(posedge clk);

fifo_write(10);
repeat(2) @(posedge clk);

fifo_read(12);
repeat(2) @(posedge clk);

//read and write same time
@(posedge clk) wen <= 1'b1;
repeat(4) @(posedge clk);

@(posedge clk) ren <= 1'b1;
repeat(6) @(posedge clk);

@(posedge clk) wen <= 1'b0;
repeat(4) @(posedge clk);
@(posedge clk) ren <= 1'b0;


#3000;
$finish();
end

integer i = 0;
task fifo_write;
input [7:0] cnt;
begin
for(i=0; i<cnt; i=i+1) begin
@(posedge clk) begin
wen <= 1'b1;
din <= i + 1;
end
end

@(posedge clk) wen <= 1'b0;
end
endtask

task fifo_read;
input [7:0] cnt;
begin
for(i=0; i<cnt; i=i+1) begin
@(posedge clk) begin
ren <= 1'b1;
end
end

@(posedge clk) ren <= 1'b0;
end
endtask

fifo_sync
#(
.DEPTH(3),
.WIDTH(8)
)
m_fifo_sync
(
.clk(clk),
.rstn(rstn),
.ren(ren),
.wen(wen),
.din(din),
.dout(dout),
.empty(empty),
.full(full)
);

endmodule

  • 仿真波形