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;然后将空满标志输出。
三、项目流程图
读操作:
写操作:
更新读地址:
更新写地址:
更新标志位:
四、项目实现代码
注:设计块已修改为可上板综合的代码,若进行仿真请酌量修改。
/***************************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代码
`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
项目实现截图及分析:

分析:时间单位为1ns,时间精度为1ps,每10ns时钟信号反转一次。首先将各信号置0初始化,在40ns后将复位信号置为1,经过35ns后将写使能信号置为1,使FIFO开始写入数据,经过20ns后将读信号置为1,此时读写并存,写入数据由一for循环进行20次递增赋值,在写使能信号有效时将数据存入RAM,然后在读使能信号有效时将数据读出,然后按顺序显示。仿真结果表示顺序输入顺序输出,输出顺序与输入顺序相同,则同步FIFO功能正常。
七、遇到的问题及解决办法
Q:问题;S:解决方法。- Q: 开发板连接PC的USB接口时驱动安装异常。
S: 下载时要保持开发板电源开启,并安装合适的库。 - Q: 下载程序时端口设置异常。
S: 端口设置可以利用其他空闲端口进行协调。 - Q: Modelsim仿真时没有波形。
S: Modelsim仿真注意仿真对象模块要是激励块,不能为设计块。 - Q: 在QuartusII中仿真时always中的判断条件种类太多时会出错。
S: QuartusII中always判断条件只能为电平触发或边沿触发中的一种类型,如想要多种条件可以在always块中用if来判断。 - Q: 在QuartusII中调用Modelsim进行仿真时出错,无法打开Modelsim软件。
S: 在QuartusII中的Modelsim路径设置中,如果Modelsim软件为OEM版本时要在Modelsim-Altera中设置软件路径,其他版本则在Modelsim中设置。 - Q: 使用QuartusII打开MOdelsim仿真无波形。
S: 再QuartusII中设置的testbench中路径或模块名要对应。
八、附录
1.RTL视图




2.SignalTapII仿真

评论已关闭