利用SRAM设计一个FIFO.docx
《利用SRAM设计一个FIFO.docx》由会员分享,可在线阅读,更多相关《利用SRAM设计一个FIFO.docx(14页珍藏版)》请在冰点文库上搜索。
![利用SRAM设计一个FIFO.docx](https://file1.bingdoc.com/fileroot1/2023-5/6/a43aaba3-0993-4678-b285-d18a49c23a72/a43aaba3-0993-4678-b285-d18a49c23a721.gif)
利用SRAM设计一个FIFO
292
练习十二利用SRAM设计一个FIFO
在本练习中,要求同学利用练习十一中提供的SRAM模型,设计SRAM读写控制逻辑,使SRAM的行为对用户表现为一个FIFO(先进先出存储器。
1设计要求:
本练习要求同学设计的FIFO为同步FIFO,即对FIFO的读/写使用同一个时钟。
该FIFO应当提供用户读使能(fiford和写使能(fifowr输入控制信号,并输出指示FIFO状态的非空(nempty和非满(nfull信号,FIFO的输入、输出数据使用各自的数据总线:
in_data和
2FIFO接口的设计思路
FIFO的数据读写操作与SRAM的数据读写操作基本上相同,只是FIFO没有地址。
所以用SRAM实现FIFO的关键点是如何产生正确的SRAM地址。
我们可以借用软件中的方法,将FIFO抽象为环形数组,并用两个指针:
读指针(fifo_rp和写指针(fifo_wp控制对该环形数组的读写。
其中,读指针fifo_rp指向下一次读操作所要读取的单元,并且每完成一次读操作,fifo_rp加一;写指针fifo_wp则指向下一次写操作时存放数据的单元,并且每完成一次写操作,fifo_wp加一。
由fifo_rp和fifo_wp的定义易知,当FIFO被读空或写满后,fifo_rp和fifo_wp将指向同一单元,但在读空和写满之前FIFO的状态是不同的,所以如果能区分这两种状态,再通过比较fifo_rp和fifo_wp就可以得到nempty和nfull信号了。
下图为FIFO工作状态的示意。
N
单元内有数据单元内无数据
一般情况
再写即满
再读即空
在得到nfull和nempty信号后,就需要考虑如何应用这两个信号来控制对FIFO的读写,使得FIFO在被写满后不能再写入,从而防止覆盖原有数据,并且在被读空后也不能再进行读操作,防止读取无效数据。
此外,在进SRAM读写操作时,应该注意建立地址、数据和控制信号的先后顺序。
一般情况下,希望对SRAM读写的波形时序如下图所示:
RD
即写SRAM时,先建立地址和数据,然后置写使能信号WR有效,在WR保持有效一定时间后,先复位WR,然后释放地址和数据总线。
而读取SRAM时,则先建立地址,然后置读使能RD有效,在RD维持有效一定时间后,复位RD,同时读取数据总线上的值,然后再释放地址总线。
在进行FIFO操作时,用户一般希望除了没有地址外,其它三个信号的时序关系能保持不变。
请同学们在设计FIFO控制信号与SRAM控制信号间逻辑关系时注意这一点。
3FIFO接口的测试
在完成一个设计后,需要进行测试以确认设计的正确性和完整性。
而要进行测试,就需要编写测试激励和结果检查程序,即测试平台(testbench。
在某些情况下,如果设计的接口能够预先确定,测试平台的编写也可以在设计完成之前就进行,这样做的好处是在设计测试平台的同时也在更进一步深入了解设计要求,有助于理清设计思路,及时发现设计方案的错误。
编写测试激励时,除了注意对实际可能存在的各种情况的覆盖外,还要有意针对非正常情况下的操作进行测试。
在本练习中,就应当进行在FIFO读空后继续读取、FIFO写满后继续写入、FIFO复位后马上读取等操作的测试。
测试激励中通常会有一些复杂操作需要反复进行,如本练习中对FIFO的读写操作。
这时可以将这些复杂操作纳入到几个task中,即减小了激励编写的工作量,也使得程序的可读性更好。
下面的测试程序给同学们做为参考,希望同学们能先用这段程序测试所设计的FIFO接口,然后编写自己更全面的测试程序。
`defineFIFO_SIZE8
`include“sram.v”//有的仿真工具不需要加这句,只要sram.v模块编译过就可以了`timescale1ns/1ns
modulet;
reg[7:
0]in_data;//FIFO数据总线
regfiford,fifowr;//FIFO控制信号
wire[7:
0]out_data;
293
wirenfull,nempty;//FIFO状态信号
regclk,rst;
wire[7:
0]sram_data;//SRAM数据总线
wire[10:
0]address;//SRAM的地址总线
wirerd,wr;//SRAM读写控制信号
reg[7:
0]data_buf[`FIFO_SIZE:
0];//数据缓存,用于结果检查
integerindex;//用于读写data_buf的指针
//系统时钟
initialclk=0;
always#25clk=~clk;
//测试激励序列
initial
begin
fiford=1;
fifowr=1;
rst=1;
#40rst=0;
#42rst=1;
if(nempty$display($time,"Error:
FIFObeempty,nemptyshouldbelow.\n";
//连续写FIFO
index=0;
repeat(`FIFO_SIZEbegin
data_buf[index]=$random;
write_fifo(data_buf[index];
index=index+1;
end
if(nfull$display($time,"Error:
FIFOfull,nfullshouldbelow.\n";
repeat(2write_fifo($random;
#200
//连续读FIFO
index=0;
read_fifo_compare(data_buf[index];
if(~nfull$display($time,"Error:
FIFOnotfull,nfullshouldbehigh.\n";
repeat(`FIFO_SIZE-1begin
294
index=index+1;
read_fifo_compare(data_buf[index];
end
if(nempty$display($time,"Error:
FIFObeempty,nemptyshouldbelow.\n";
repeat(2read_fifo_compare(8'bx;
reset_fifo;
//写后读FIFO
repeat(`FIFO_SIZE*2
begin
data_buf[0]=$random;
write_fifo(data_buf[0];
read_fifo_compare(data_buf[0];
end
//异常操作
reset_fifo;
read_fifo_compare(8'bx;
write_fifo(data_buf[0];
read_fifo_compare(data_buf[0];
$stop;
end
fifo_interfacefifo_interface(
.in_data(in_data,.out_data(out_data,
.fiford(fiford,.fifowr(fifowr,
.nfull(nfull,.nempty(nempty,
.address(address,.sram_data(sram_data,
.rd(rd,.wr(wr,
.clk(clk,.rst(rst
;
sramm1(.Address(address,
.Data(sram_data,
.SRG(rd,//SRAM读使能
.SRE(1'b0,//SRAM片选,低有效
.SRW(wr;//SRAM写使能
taskwrite_fifo;
input[7:
0]data;
295
begin
in_data=data;
#50fifowr=0;//往SRAM中写数
#200fifowr=1;
#50;
end
endtask
taskread_fifo_compare;
input[7:
0]data;
begin
#50fiford=0;//从SRAM中读数
#200fiford=1;
if(out_data!
=data
$display($time,"Error:
Dataretrieved(%hnotmatchtheonestored(%h.\n",out_data,data;
#50;
end
endtask
taskreset_fifo;
begin
#40rst=0;
#40rst=1;
end
endtask
endmodule
4FIFO接口的参考设计
FIFO接口的实现有多种方案,下面给出的参考设计只是其中一种。
希望同学们在完成自己的设计后,和参考设计做一下比较。
`defineSRAM_SIZE8//为减小对FIFO控制器的测试工作量,置SRAM空间为8Byte`timescale1ns/1ns
modulefifo_interface(
in_data,//对用户的输入数据总线
out_data,//对用户的输出数据总线,
fiford,//FIFO读控制信号,低电平有效
fifowr,//FIFO写控制信号,低电平有效
nfull,
296
nempty,
address,//到SRAM的地址总线
sram_data,//到SRAM的双向数据总线
rd,//SRAM读使能,低电平有效
wr,//SRAM写使能,低电平有效
clk,//系统时钟信号
rst;//全局复位信号,低电平有效
//来自用户的控制输入信号
inputfiford,fifowr,clk,rst;
//来自用户的数据信号
input[7:
0]in_data;
output[7:
0]out_data;
reg[7:
0]in_data_buf,//输入数据缓冲区
out_data_buf;//输出数据缓冲区
//输出到用户的状态指示信号
outputnfull,nempty;
regnfull,nempty;
//输出到SRAM的控制信号
outputrd,wr;
//到SRAM的双向数据总线
inout[7:
0]sram_data;
//输出到SRAM的地址总线
output[10:
0]address;
reg[10:
0]address;
//InternalRegister
reg[10:
0]fifo_wp,//FIFO写指针
fifo_rp;//FIFO读指针
reg[10:
0]fifo_wp_next,//fifo_wp的下一个值
fifo_rp_next;//fifo_rp的下一个值
regnear_full,near_empty;
297
reg[3:
0]state;//SRAM操作状态机寄存器
parameteridle='b0000,
read_ready='b0100,
read='b0101,
read_over='b0111,
write_ready='b1000,
write='b1001,
write_over='b1011;
//SRAM操作状态机
always@(posedgeclkornegedgerst
if(~rst
state<=idle;
else
case(state
idle:
//等待对FIFO的操作控制信号
if(fifowr==0&&nfull//用户发出写FIFO申请,且FIFO未满
state<=write_ready;
elseif(fiford==0&&nempty//用户发出读FIFO申请,且FIFO未空
state<=read_ready;
else//没用对FIFO操作的申请
state<=idle;
read_ready:
//建立SRAM操作所需地址和数据
state<=read;
read:
//等待用户结束当前读操作
if(fiford==1
state<=read_over;
else
state<=read;
read_over:
//继续给出SRAM地址以保证数据稳定state<=idle;
write_ready:
//建立SRAM操作所需地址和数据
state<=write;
write:
//等待用户结束当前写操作
if(fifowr==1
state<=write_over;
else
state<=write;
298
write_over:
//继续给出SRAM地址和写入数据以保证数据稳定
state<=idle;
default:
state<=idle;
endcase
//产生SRAM操作相关信号
assignrd=~state[2];//state为read_ready或read或read_over
assignwr=(state==write?
fifowr:
1'b1;
always@(posedgeclk
if(~fifowr
in_data_buf<=in_data;
assignsram_data=(state[3]?
//state为write_ready或write或write_over
in_data_buf:
8'hzz;
always@(stateorfifordorfifowrorfifo_wporfifo_rp
if(state[2]||~fiford
address=fifo_rp;
elseif(state[3]||~fifowr
address=fifo_wp;
else
address='bz;
//产生FIFO数据
assignout_data=(state[2]?
sram_data:
8'bz;
always@(posedgeclk
if(state==read
out_data_buf<=sram_data;
//计算FIFO读写指针
always@(posedgeclkornegedgerst
if(~rst
fifo_rp<=0;
elseif(state==read_over
fifo_rp<=fifo_rp_next;
always@(fifo_rp
299
if(fifo_rp==`SRAM_SIZE-1
fifo_rp_next=0;
else
fifo_rp_next=fifo_rp+1;
always@(posedgeclkornegedgerst
if(~rst
fifo_wp<=0;
elseif(state==write_over
fifo_wp<=fifo_wp_next;
always@(fifo_wp
if(fifo_wp==`SRAM_SIZE-1
fifo_wp_next=0;
else
fifo_wp_next=fifo_wp+1;
always@(posedgeclkornegedgerst
if(~rst
near_empty<=1'b0;
elseif(fifo_wp==fifo_rp_next
near_empty<=1'b1;
else
near_empty<=1'b0;
always@(posedgeclkornegedgerst
if(~rst
nempty<=1'b0;
elseif(near_empty&&state==read
nempty<=1'b0;
elseif(state==write
nempty<=1'b1;
always@(posedgeclkornegedgerst
if(~rst
near_full<=1'b0;
elseif(fifo_rp==fifo_wp_next
near_full<=1'b1;
else
near_full<=1'b0;
always@(posedgeclkornegedgerst
if(~rst
300
nfull<=1'b1;
elseif(near_full&&state==write
nfull<=1'b0;
elseif(state==read
nfull<=1'b1;
endmodule
301