同步FIFO的设计与实现.docx
《同步FIFO的设计与实现.docx》由会员分享,可在线阅读,更多相关《同步FIFO的设计与实现.docx(25页珍藏版)》请在冰豆网上搜索。
同步FIFO的设计与实现
同步缓冲器(FIFO)的设计与实现
姓名:
学号:
2
班级:
2010级测控1班
院系:
控制系
专业:
测控技术与仪器
同组人姓名:
(说明:
我们三个人前面的报告部分是一样的,因为课设基本是三个人商议完成,所以就感觉报告部分没什么不同的就只写了一份报告)
1原理与系统设计
FIFO(FirstInFirstOut)——是一种可以实现数据先入先出的存储器件。
FIFO就像一个单向管道,数据只能按固定的方向从管道一头进来,再按相同的顺序从管道另一头出去,最先进来的数据必定是最先出去。
FIFO被普遍用作数据缓冲器。
FIFO的基本单元是寄存器,作为存储器件,FIFO的存储能力是由其内部定义的存储寄存器的数量决定的。
本题中所设计的是同步FIFO(即输出输入端时钟频率一致),异步复位,其存储能力为(16x8),输出两个状态信号:
full与empty,以供后继电路使用。
根据系统要求,画出的系统框图,如图1所示
图1同步FIFO框图
端口说明:
输入:
in_data:
输入数据端口,位宽为8位;
read_n:
读使能端,当read_n=0时,可以读出数据;
write_n:
写使能端,当write_n=0时,可以写入数据;
clock:
时钟信号,在时钟的正边沿进行采样;
reset_n:
复位信号,当reset_n=0时,计数器及读写都被清零(即:
读写地址指针都指向0)
输出:
out_data:
输出数据端口,位宽为8位;;
full:
FIFO状态信号,当full=1时,表明该FIFO存储器已经写满;
empty:
FIFO状态信号,当empty=1时,表明该FIFO存储器已经读空;
FIFO满的情况下,不能再写,写指针不能加1;
FIFO空的情况下,不能再读,读指针不能加1;
2设计思想
由以上的系统框图和端口分析,我们将设计的重点定在了解决以下三个核心问题上:
1.FIFO的存储体如何表示?
2.如何实现“先进先出”的逻辑功能?
3.如何知道FIFO内部使用了多少,是满是空?
针对以上三个问题,我们所采取的方法是:
1.定义一个16×8的二维数组来表示FIFO的存储体。
2.为了实现“先进先出”的逻辑功能,我们定义了“读指针”及“写指针”,分别用来指示读操作与写操作的位置。
3.为了表示FIFO是满还是空,我们定义了一个计数器,用以标志FIFO已使用了多少空间。
在解决了以上三个重点问题以后,针对同步FIFO的逻辑功能,我们拟定了以下一个结构图,如图2所示:
图2FIFO设计结构图
3.源码与注释
3.1源代码
我们在完成了之前两步的准备工作之后,进行了源码的设计,具体的代码如下:
`defineDEL1//为了使仿真接近真实情形,我们定义了从时钟到输出的延时
modulesfifo(clock,reset_n,in_data,read_n,write_n,out_data,full,empty);
//输入信号
inputclock;//输入时钟
inputreset_n;//复位信号,低有效
input[7:
0]in_data;//输入的数据
inputread_n;//读控制信号,低有效
inputwrite_n;//写控制信号,低有效
//输出信号
output[7:
0]out_data;//FIFO的输出数据
outputfull;//FIFO满标志信号
outputempty;//FIFO空标志信号
//信号声明
reg[7:
0]out_data;
reg[7:
0]fifo_mem[15:
0];//FIFO存储体即8*16存储器,用数组表示
reg[4:
0]counter;//计数器表示FIFO中已用了多少
reg[3:
0]rd_pointer;//FIFO读指针,指向下次读操作的地址
reg[3:
0]wr_pointer;//FIFO读指针,指向下次读操作的地址
//赋值声明,给出满标志与空标志的实现
assign#`DELfull=(counter==16)?
1'b1:
1'b0;
assign#`DELempty=(counter==0)?
1'b1:
1'b0;
//本模块实现读指针、写指针和计数器的功能
always@(posedgeclockornegedgereset_n)
begin
if(~reset_n)
begin//计数器及读、写指针清零
rd_pointer<=#`DEL4'b0;
wr_pointer<=#`DEL4'b0;
counter<=#`DEL5'b0;
end
else
begin
if(~read_n)
begin
//如果FIFO为空,不能再读,并报错
if(counter==0)//检查fifo是否溢出(empty)
begin
$display("\nERRORattime%0t:
",$time);
$display("FIFOUnderflow\n");
$stop;//终止系统任务,用于调试
end
//读有效,写无效时,计数器减1
if(write_n)
begin
counter<=#`DELcounter-1;
end
//如果读指针已指到最后一个位置,则返回起始位置
if(rd_pointer==15)
rd_pointer<=#`DEL4'b0;
else
rd_pointer<=#`DELrd_pointer+1;
end
if(~write_n)//检查fifo是否溢出(full)
begin
if(counter>=16)
begin
$display("\nERRORattime%0t:
",$time);
$display("FIFOoverflow\n");
$stop;
end
if(read_n)//写有效,读无效时,计数器加1
begin
counter<=#`DELcounter+1;
end
if(wr_pointer==15)//如果写指针已指到最后一位,则返回起始位置
wr_pointer<=#`DEL4'b0;
else
wr_pointer<=#`DELwr_pointer+1;
end
end
end
always@(posedgeclock)//本模块实现数据的读写功能
begin
if(~write_n)
begin
fifo_mem[wr_pointer]<=#`DELin_data;
end
if(~read_n)
begin
out_data<=#`DELfifo_mem[rd_pointer];//读取数据
end
end
endmodule
3.2测试文件
本设计中为了让输入激励能够完整地测试出设计的功能,以证明FIFO确实能起到数据缓冲的作用,因而要测试当读写速度不一致的情况,即要仿真写速度大于读速度的情形以及读速度大于写速度的情形。
测试文件中:
异步复位如下进行:
reset_n=1;#20reset_n=0;#20reset_n=1;
时钟信号如下产生:
always#100clock<=~clock;
写入数据递增加1产生:
in_data<=in_data+1;
而编写测试文件的核心问题在于:
在同一文件中如何既能仿真写快与读的情形又能仿真读快于写的情形?
对此,我们的想法:
先让写快于读以达到满的状态(full=1),而后让读快于写以达到排空的状态(empty=1)
由此我们定义了两个状态信号:
fast_read:
fast_read=1时以高速度进行读操作
fast_write:
fast_write=1时以高速度进行写操作
又为了让读和写的速度产生差异,我们定义了一个周期计数信号cycle_count(其周期计数的值为......01010101......),它用来控制生成读写使能信号,控制方式为:
当fast_write=1时,只要FIFO非满就使写入,得到写使能信号;
在非空的情况下,当cycle_count==1时才产生读使能信号;
当fast_read=1时,只要FIFO非空就使读取,得到读使能信号;
在非满的情况下,当cycle_count==1时才产生写使能信号;
从而达到了让快的一方速度是慢的一方速度2倍的效果。
在解决了以上核心问题之后,具体的测试代码如下:
//DEFINES
`defineDEL1//时钟到输出的延时
moduletest_sfifo(clock,reset_n,in_data,read_n,write_n,out_data,full,empty);
//INPUTS
input[7:
0]out_data;
inputempty,full;
//OUTPUTS
outputclock,reset_n,read_n,write_n;
output[7:
0]in_data;
//信号声明,这些信号应与测试模块中的端口信号一一对应
regclock;
regreset_n;
reg[7:
0]in_data;//输入到端口in_data的激励信号
regread_n;
regwrite_n;
wire[7:
0]out_data;//从端口out_data输出的信号
wirefull;
wireempty;
//定义需要的一些信号
integerfifo_count;//记录FIFO中的字节数,定义为实型整数
reg[7:
0]exp_data;//期望从FIFO输出的数据
regfast_read;//标志以高速度进行读操作
regfast_write;//标志以高速度进行写操作
regfilled_flag;//标志FIFO已填满
regcycle_count;//周期计数,用来生成读写控制信号
//对FIFO进行实例化
sfifoSfifo(
.clock(clock),
.reset_n(reset_n),
.in_data(in_data),
.read_n(read_n),
.write_n(write_n),
.out_data(out_data),
.full(full),
.empty(empty));
initialbegin
in_data=0;
exp_data=0;
fifo_count=0;
read_n=1;
write_n=1;
filled_flag=0;
cycle_count=0;
clock=1;
//写速度大于读速度
fast_write=1;
fast_read=0;
//复位
reset_n=1;
#20reset_n=0;
#20reset_n=1;
//初始情况下,FIFO应该为空,即empty==1且full==0
if(empty!
==1)
begin
$display("\nERRORattime%0t:
",$time);
$display("Afterreset,emptystatusnotasserted\n");
//报错
$stop;
end
if(full!
==0)
begin
$display("\nERRORattime%0t:
",$time);
$display("Afterreset,emptystatusnotasserted\n");
$stop;
end
end
//生成时钟信号
always#100clock<=~clock;
//对FIFO中的字节数进行计数。
每次写操作时,计数加1;读操作时计数减1
always@(posedgeclock)
begin
if(~write_n&&read_n)//写但不读
fifo_count<=fifo_count+1;
elseif(~read_n&&write_n)//读但不写
fifo_count<=fifo_count-1;
end
//检查输出数据是否是期望的数据。
如果不是,则报错
always@(negedgeclock)//读写控制信号和输入信号在下降沿产生,而采样在上升沿完成,如此可防止锁错数据
begin
if(~read_n&&(out_data!
==exp_data))begin
$display("\nERRORattime%0t:
",$time);//$time为当前仿真时间
$display("Expecteddataout=%h",exp_data);
$display("Actualdataout=%h\n",out_data);
$stop;
end
if((fast_write||(cycle_count&1'b1))&&~full)
begin//只要非满就使写入,得到写控制信号
write_n<=0;
//生成输入数据
in_data<=in_data+1;
end
else
write_n<=1;
//得到读控制信号,并给出期望的数据
if((fast_read||(cycle_count&1'b1))&&~empty)
begin//在非空的情况下,当cycle==1时才产生读控制信号
read_n<=0;
exp_data<=exp_data+1;//由于写入数据为递增加1的,所以exp_data同意也是递增加1的
end
else
read_n<=1;
if(full)
begin//如果fifo已满,则使读速度大于写速度
fast_read<=1;
fast_write<=0;
//设置写满标志
filled_flag<=1;
end
if(filled_flag&&empty)//如果已清空fifo,则结束
begin
$display("\nSimulationcomplete-noerrors");
$finish;//仿真结束
end
//对计算周期进行计数
cycle_count<=cycle_count+1;//由于默认cycle_count为1位变量,所以cycle的值实质为'……010101……’
end
//checkallofthestatussignalswitheachchangeoffifo_count
always@(fifo_count)
begin//waitamomenttoevaluateeverything
#`DEL;
#`DEL;
#`DEL;
case(fifo_count)
0:
begin
if((empty!
==1)||(full!
==0))
begin
$display("\nERRORattime%0t:
",$time);
$display("fifo_count=%h",fifo_count);
$display("empty=%b",empty);
$display("full=%b\n",full);
//Use$stopfordebugging
$stop;
end
if(filled_flag==1)
begin//thefifohasfilledandemptied
$display("\nSimulationcomplete-noerrors\n");
$finish;
end
end
16:
begin
if((empty!
==0)||(full!
==1))
begin
$display("\nERRORattime%0t:
",$time);
$display("fifo_count=%h",fifo_count);
$display("empty=%b",empty);
$display("full=%b\n",full);
//Use$stopfordebugging
$stop;
end
//如果fifo写满,则给出写满标志,此时,读速度大于写速度
filled_flag<=1;
fast_write<=0;
fast_read<=1;
end
default:
begin
if((empty!
==0)||(full!
==0))
begin
$display("\nERRORattime%0t:
",$time);
$display("fifo_count=%h",fifo_count);
$display("empty=%b",empty);
$display("full=%b\n",full);
//Use$stopfordebugging
$stop;
end
end
endcase
end
endmodule
.4仿真
4.1波形仿真(仿真过程使用ModlSimSE6.0进行)
波形图中端口信号说明:
empty:
FIFO状态信号,当empty=1时,表明该FIFO存储器已经读空;
full:
FIFO状态信号,当full=1时,表明该FIFO存储器已经写满;
clock:
时钟信号,在时钟的正边沿进行采样;
reset_n:
复位信号,当reset_n=0时,计数器及读写都被清零(即:
读写地址指针都指向0)
read_n:
读使能端,当read_n=0时,可以读出数据;
write_n:
写使能端,当write_n=0时,可以写入数据;
in_data:
输入数据端口,位宽为8位;
out_data:
输出数据端口,位宽为8位;
fifo_count:
记录FIFO中的字节数,定义为实型整数
exp_data:
期望从FIFO输出的数据
fast_read:
标志以高速度进行读操作
fast_write:
标志以高速度进行写操作
filled_flag:
标志FIFO已填满
cycle_count:
周期计数,用来生成读写控制信号
图1写快于读直至写满的波形图
图2读快于写由满直至读空的波形图
图3仿真全过程波形图
4.2时序,逻辑分析
图1写快于读直至写满的波形图的分析:
仿真开始时,由于fast_write=1,fast_read=0,所以:
只要FIFO非满就使写入,得到写使能信号/在非空的情况下,当cycle_count==1时才产生读使能信号.所以写速度是读速度的2倍(即写入2个数据,只读取一个数据),当FIFO写满后,这时full=1,填满信号filled_flag由0变为了1.
图2读快于写由满直至读空的波形图的分析:
由于FIFO在图1中所示过程已填满,所以接着必须使读速度大于写速度,即fast_read=1,fast_write=0,此时:
只要FIFO非空就使读取,得到读使能信号/在非满的情况下,当cycle_count==1时才产生写使能信号.所以读速度是写速度的2倍(即读取2个数据,只写入一个数据),在此过程中full=0,filled_flag保持为1,最终FIFO排空时,empty=1.
图3仿真全过程波形图分析:
由仿真全过程的波形图来看:
在reset_n=0后,计数器及读写地址指针都被清0,说明达到了异步复位的要求;由read_n及write_n值的变化与读写数据的波形来看,read_n及write_n起到了读写使能控制的作用;由波形可知读写操作都是在时钟clock的上升沿完成,说明FIFO是同步的;对比full与empty的值与FIFO的满空状况来看,full与empty能正确反映FIFO的实际状态。
由上述分析可以验证:
本次设计的器件能满足同步FIFO的逻辑功能。
.5综合(综合过程使用LeonardoSpectrum2000.1b进行)
(考虑到使用不同的器件库,综合出来的电路图不同,说明一下:
以下综合使用的器件库为MAX3000A)
5.1顶层综合电路示意图
图1
图2
注:
顶层综合电路图由图1与图2两部分构成,其由三个端口相连,它们依次是:
Not_write_n,Not_reset_n,Not_read_n
5.2门级综合电路示意图
图1
注:
此为顶层图中读写地址指针产生模块
注:
此为顶层图中的ram存储器部分的门级图
5.3输入输出描述
输入:
in_data:
输入数据端口,位宽为8位;
read_n:
读使能端,当read_n=0时,可以读出数据;
write_n:
写使能端,当write_n=0时,可以写入数据;
clock:
时钟信号,在时钟的正边沿进行采样;
reset_n:
复位信号,当reset_n=0时,计数器及读写都被清零(即:
读写地址指针都指向0)
输出:
out_data:
输出数据端口,位宽为8位;;
full:
FIFO状态信号,当full=1时,表明该FIFO存储器已经写满;
empty:
FIFO状态信号,当empty=1时,表明该FIFO存储器已经读空;
6.心得体会与建议(小组成员