O_同步FIFO的FPGA实现


一、项目概况

名称:同步FIFO设计与验证
预估设计时间:5天
预估占用逻辑单元数量:1100
FPGA芯片型号:Cyclone IV E EP4CE6E22C8

二、设计思想

  项目目标为8位同步FIFO,深度为16。因为标志计数器要计数16,所以标志计数器要定义5位以存储17个数字。

  将项目划分为4个主要模块:RAM寄存器模块、读指针控制模块、写指针控制模块、空满标志控制模块。

  同时划分5项主要功能:读操作、写操作、更新读地址、更新写地址、更新标志位。

  读操作首先判断当前是否是系统时钟的上升沿或复位信号的下降沿,如果不是则不做任何操作,如果是再判断系统复位信号是否有效,若有效则输出0,若无效则再判断读使能信号是否有效且FIFO是否为空,如果满足条件则将当前地址的数据输出,否则不做任何操作。

  写操作首先判断系统时钟是否为上升沿,如果不是则不做操作,如果是再判断写使能信号是否有效且FIFO非满,若满足条件则将数据写入对应写地址,否则不做操作。

  更新读地址操作首先判断是否为时钟信号上升沿或复位信号下降沿,如果不是则不做操作,如果是再判断系统复位信号是否有效,若是则将读地址置为0,若不是再判断读使能信号是否有效且FIFO非空,如果不满足条件则不做操作,否则将读地址加1。

  更新写地址操作首先判断是否为时钟信号上升沿或复位信号下降沿,如果不是则不做操作,如果是再判断系统复位信号是否有效,若是则将写地址置为0,若不是再判断写使能信号是否有效且FIFO非满,若果不满足条件则不做操作,否则将写地址加1。

  更新标志位操作首先判断是否为时钟信号上升沿或复位信号下降沿,如果不是则不做操作,如果是再判断系统复位信号是否有效,若是则将标志计数器置为0,否则对标志计数器进行操作:不可读不可写或可读可写时标志计数器不变,不可读可写时标志计数器加1,可读不可写时标志计数器减1;然后根据标志计数器数值对空满标志进行操作:标志计数器为0时将空标志置为1,标志计数器为16时将满标志置为1,否则空满标志都为0;然后将空满标志输出。

三、项目流程图

读操作:
fifo1
写操作:
fifo2
更新读地址:
fifo3
更新写地址:
fifo4
更新标志位:
fifo5

四、项目实现代码

注:设计块已修改为可上板综合的代码,若进行仿真请酌量修改。

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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/***************************Copyright (c)***************************
** QSTSKING
**
**----------------------------File Info-----------------------------
** File Name: fifo.v
** Last Modified Date: 2018-4-25
** Last Modified Version: 1.0
** Description: xxx
**------------------------------------------------------------------
** Created By: ArronTY
** Created Date: 2018-4-25
** Version: 1.0
** Description: The origin version
**------------------------------------------------------------------
** Modified By:
** Modified Date:
** Modified Version:
** Description:
**------------------------------------------------------------------
*******************************************************************/
module ram(input clk,
input rst,
input wr_en,
input rd_en,
input [3:0]wr_addr,
input [3:0]rd_addr,
input full,
input empty,
input [7:0]data_in,
output reg[7:0]data_out);

reg [7:0]ram[0:16];

always @(posedge clk or negedge rst)
begin
if(!rst)
begin
data_out<=0;
end
else
begin
if(rd_en==1 && empty==0)
data_out<=ram[rd_addr];
end
end

always @(posedge clk)
begin
if(wr_en==1 && full==0)
begin
ram[wr_addr]<=data_in;
end
end
endmodule


module wr_addr_gen(input clk,
input rst,
input wr_en,
input full,
output reg [3:0]wr_addr);

always @(posedge clk or negedge rst)
begin
if(!rst)
begin
wr_addr<=0;
end
else if(full==0 && wr_en==1)
begin
wr_addr<=wr_addr+1;
end
else
begin
wr_addr<=0;
end
end
endmodule


module rd_addr_gen(input clk,
input rst,
input rd_en,
input empty,
output reg[3:0]rd_addr);

always @(posedge clk or negedge rst)
begin
if(!rst)
begin
rd_addr<=0;
end
else
begin
if(empty==0&&rd_en==1)
begin
rd_addr<=rd_addr+1;
end
end
end
endmodule


module flag_gen(input clk,
input rst,
input wr_en,
input rd_en,
output reg full,
output reg empty);

reg [4:0]count;



always @(posedge clk or negedge rst)
begin
if(!rst)
begin
count=0;
end
else
begin
case({wr_en,rd_en})
2'b00:count<=count;
2'b01:if(count!=5'b00000)
count <= count-1;
2'b10:if(count!=5'b01111)
count <= count+1;
2'b11:count<=count;
endcase
end
end

always @(count)
begin
if(count==5'b10000)
full<=1;
else
full<=0;
end

always @(count)
begin
if(count==5'b00000)
empty<=1;
else
empty<=0;
end
endmodule


module sync_fifo(input clk,
input rst,
output [7:0]data_out,
output full,
output empty,
output reg [4:0]time_cnt);

wire wr_en;
wire rd_en;
wire [3:0]rd_addr;
wire [3:0]wr_addr;
reg [7:0]data_in;


ram ram_top(.clk(clk),
.rst(rst),
.wr_en(wr_en),
.rd_en(rd_en),
.wr_addr(wr_addr),
.rd_addr(rd_addr),
.data_in(data_in),
.full(full),
.empty(empty),
.data_out(data_out));

rd_addr_gen rd_addr_gen_top(.clk(clk),
.rst(rst),
.rd_en(rd_en),
.empty(empty),
.rd_addr(rd_addr));

wr_addr_gen wr_addr_gen_top(.clk(clk),
.rst(rst),
.wr_en(wr_en),
.full(full),
.wr_addr(wr_addr));

flag_gen flag_gen_top(.clk(clk),
.rst(rst),
.wr_en(wr_en),
.rd_en(rd_en),
.full(full),
.empty(empty));


always @(posedge clk or negedge rst)
begin
if (rst == 1'b0)
data_in <= 8'd0;
else if (data_in == 8'd15)
data_in <= 8'd0;
else if (data_in >= 8'd0 && data_in < 8'd15)
data_in <= data_in + 8'd1;
end

always @(posedge clk or negedge rst)
begin
if (!rst)
time_cnt <=5'b0;
else if (data_in == 5'd31)
time_cnt <=5'b0;
else if (data_in >= 5'd0 && data_in < 5'd31)
time_cnt <= time_cnt + 5'd1;
end

assign wr_en = (time_cnt >= 1'b0 && time_cnt <= 5'd15) ? 1'b1:1'b0;
assign rd_en = (time_cnt >= 5'd16 && time_cnt <= 5'd31) ? 1'b1:1'b0;

endmodule

五、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
`timescale 1 ns/ 1 ps
module sync_fifo_vlg_tst();
reg eachvec;
reg clk;
reg rst;
reg rd_en;
reg wr_en;
reg [7:0] data_in;
wire [7:0] data_out;
wire empty;
wire full;
reg [7:0]dindely;

sync_fifo tb1 (.clk(clk),
.data_in(data_in),
.data_out(data_out),
.empty(empty),
.full(full),
.rd_en(rd_en),
.rst(rst),
.wr_en(wr_en));

always @(posedge clk)
begin
dindely<=data_in;
end

initial
begin
clk=0;
rst=0;
rd_en=0;
wr_en=0;
data_in=0;
#40
rst=1;
#35
wr_en=1;
#400
rd_en=1;
end

always #20 data_in<=data_in+1;
always #10 clk=~clk;

endmodule

六、最终项目实现状况

实际设计时间:5天
实际占用逻辑单元数量:1026

项目实现截图及分析:
fifo6
fifo7
 
  分析:时间单位为1ns,时间精度为1ps,每10ns时钟信号反转一次。首先将各信号置0初始化,在40ns后将复位信号置为1,经过35ns后将写使能信号置为1,使FIFO开始写入数据,经过20ns后将读信号置为1,此时读写并存,写入数据由一for循环进行20次递增赋值,在写使能信号有效时将数据存入RAM,然后在读使能信号有效时将数据读出,然后按顺序显示。仿真结果表示顺序输入顺序输出,输出顺序与输入顺序相同,则同步FIFO功能正常。

七、遇到的问题及解决办法

Q:问题;S:解决方法。
  1. Q: 开发板连接PC的USB接口时驱动安装异常。
    S: 下载时要保持开发板电源开启,并安装合适的库。

  2. Q: 下载程序时端口设置异常。
    S: 端口设置可以利用其他空闲端口进行协调。

  3. Q: Modelsim仿真时没有波形。
    S: Modelsim仿真注意仿真对象模块要是激励块,不能为设计块。

  4. Q: 在QuartusII中仿真时always中的判断条件种类太多时会出错。
    S: QuartusII中always判断条件只能为电平触发或边沿触发中的一种类型,如想要多种条件可以在always块中用if来判断。

  5. Q: 在QuartusII中调用Modelsim进行仿真时出错,无法打开Modelsim软件。
    S: 在QuartusII中的Modelsim路径设置中,如果Modelsim软件为OEM版本时要在Modelsim-Altera中设置软件路径,其他版本则在Modelsim中设置。

  6. Q: 使用QuartusII打开MOdelsim仿真无波形。
    S: 再QuartusII中设置的testbench中路径或模块名要对应。

八、附录

1.RTL视图

fifo8
fifo9
fifo10
fifo11

2.SignalTapII仿真

fifo12