目录
一、FIFO
- 同步FIFO 即时钟同步
- FIFO是什么,有什么用?
First Input First Output的缩写,先入先出队列,这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。 - 端口
二、skills
1、读写地址变化方式
1、关于写地址的变化,只需考虑we有效的情况:
只写不读时:若FIFO满,则写失败,写地址不变;若FIFO不满,则写一定成功,写地址加1。
又写又读时:若FIFO满,读写为同一单元,因读操作只需读取旧数据,所以读写都成功,写地址加1;若FIFO不满,则进行读写操作的是两个不同单元,所以写一定成功,所以写地址也加1。
总结,wr_en && (!full || (full && rd_en))时,写地址加1;其他情况,写地址不变。因为只有写成功(写地址加1)的情况,才能让RAM的写使能有效,所以RAM的写使能也为wr_en && (!full || (full && rd_en))。
2、关于读地址的变化,只需考虑re有效的情况:
只读不写时:若FIFO空,则读失败,读地址不变;若FIFO不空则读一定成功,读地址加1.。
又读又写时:若FIFO空,则读写为同一单元,由于单元内无旧数据,读失败,只有写成功,此时读地址不变;若FIFO不空,则进行读写操作的是两个不同单元,所以读一定成功,读地址加1。
总结,(re有效&&empty标志无效)时,读地址加1;其他情况,读地址不变。因为只有读成功(读地址加1)的情况,才能让RAM的读使能有效,所以RAM的读使能也为(re有效&&empty标志无效)。
2、notes
- FIFO满时,和空时,读写指针指向同一地址
- 满时,读写使能为1,可同时读写
- 空时,读写使能为1,只能写
三、RTL design
module sync_fifo(
clk,
rst, //async
wr_data,
wr_en,
rd_data,
rd_en,
full,
empty
);
parameter DATA_WIDTH = 32,
DATA_DEPTH = 8,
ADD_WIDTH = 3;
input clk;
input rst;
input wr_data;
input wr_en;
input rd_en;
output rd_data;
output full;
output empty;
reg [DATA_WIDTH-1:0] wr_data;
reg [DATA_WIDTH-1:0] rd_data;
reg [DATA_WIDTH-1:0] ram_32_8[DATA_DEPTH-1:0];
reg [ADD_WIDTH-1:0] wr_add;
reg [ADD_WIDTH-1:0] rd_add;
reg [ADD_WIDTH-1+1:0] data_count;
wire full;
wire empty;
reg wr_enbuf;
reg rd_enbuf;
assign wr_enbuf = wr_en && (!full || (full && rd_en));
assign rd_enbuf = rd_en && !empty;
//写指针移动
always@(posedge clk or negedge rst)begin
if(!rst)
wr_add <= 3'b0;
else if(wr_enbuf)
wr_add <= wr_add + 1'b1;
end
//读指针移动
always@(posedge clk or negedge rst)begin
if(!rst)
rd_add <= 3'b0;
else if(rd_enbuf)
rd_add <= rd_add + 1'b1;
end
//数据计数器
always@(posedge clk or negedge rst)begin//case is good
if(!rst)
data_count <= 4'b0;
else begin
if(rd_enbuf && wr_enbuf) //writing while reading
data_count <= data_count;
else if(wr_enbuf) //writing
data_count <= data_count + 1'b1;
else if(rd_enbuf) //reading
data_count <= data_count - 1'b1;
end
end
//满空判断
// always@(posedge clk or negedge rst)begin
// if(!rst)
// full <= 1'b0;
// else
// //full <= (!rd_enbuf && data_count == 4'b1000)||(!rd_enbuf && data_count == 4'b0111 && wr_enbuf)||(rd_enbuf && wr_enbuf && data_count == 4'b1000);
// end
assign full = data_count == 4'b1000;
// always@(posedge clk or negedge rst)begin
// if(!rst)
// empty <= 1'b1;
// else
// //empty <= (!wr_enbuf && data_count == 3'b000)||(!wr_enbuf && data_count ==3'b001 && rd_enbuf);
// end
assign empty = data_count == 0;
//ram写读数据
always@(posedge clk or negedge rst)begin
if(!rst)begin
integer i ;
for(i=0;i < DATA_DEPTH;i=i+1)
begin
ram_32_8[i] <= 32'b0;
end
end
else if(wr_enbuf)begin
ram_32_8[wr_add] <= wr_data;
end
end
always@(posedge clk or negedge rst)begin
if(!rst)begin
rd_data <= 32'b0;
end
else if(rd_enbuf)begin
rd_data <= ram_32_8[rd_add];
end
end
endmodule
- tb
`timescale 1ps/1ps
module test;
//data type spicifiction
reg clk;
reg rst;
reg [31:0] wr_data;
reg wr_en;
reg rd_en;
wire [31:0] rd_data;
wire full;
wire empty;
//reset all
initial begin
clk = 0;
rst = 1;
wr_en = 0;
rd_en = 0;
wr_data = 32'b0;
#2 rst = 0;
#5 rst = 1;
end
//clock genarating
always #0.5 clk = ~clk;
//stimulate
initial begin
#10 wr_en = 1;
rd_en = 0;
#10 wr_en = 0;
rd_en = 1;
#10 wr_en = 1;
rd_en = 0;
#3 rd_en = 1;
#10
repeat(100) begin
#5 wr_en = {$random}%2;
rd_en = {$random}%2;
end
end
always #1 wr_data = {$random}%10;
//module instantiation
sync_fifo u_sync_fifo(
.clk (clk ),
.rst (rst ),
.wr_data (wr_data),
.wr_en (wr_en ),
.rd_data (rd_data),
.rd_en (rd_en ),
.full (full ),
.empty (empty )
);
//stop
initial begin
forever begin
#100
//$display("---gyc---:%d",$time);
if($time >= 2000)begin
$finish;
end
end
end
endmodule