简介:
同步 FIFO 是采用 EDA 技术设计的一种先进先出的模块接口电路,它与普通存储器的区别是没有外部读写地址线,使用起来非常简单,缺点是只能顺序读写,而不能随机读写。同步FIFO电路设计主要由存储器(用 SRAM 代替)、计数器、比较器、锁存器结合实现。
电路接口:
控制电路将信号分为写入数据信号、读出数据信号、满信号、空信号、读使能信号、写使能信号、复位信号、时钟信号等。
信号名称 | I/O | 描述 | 备注 |
clk | I | 输入时钟,频率50hz | |
rst_n | I | 复位清零 | 低有效 |
data_in | I | 输入数据信号 | |
wr_en | I | 写使能信号 | 高有效 |
rd_en | I | 读使能信号 | 高有效 |
data_out | O | 输出数据信号 | |
data_cnt | O | FIFO内数据数量 | |
full | O | 满信号,时钟上升沿为满 | 高有效 |
empty | O | 空信号,时钟上升沿为空 | 高有效 |
设计思路:
读指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)。
写指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)。
当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:
当读写指针再次相等时,表明 FIFO 为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:
为了区分到底是满状态还是空状态,可以采用以下方法:
在 FIFO 中添加一个 data_cnt(数据计数器),当 data_cnt 为 0 时,FIFO 此时为空;当 data_cnt 为 DATA_DEPTH 时,FIFO 此时为满。
电路图:
编译代码:
module fifo_8_256
#(parameter
DATA_WIDTH = 8,
DATA_DEPTH = 256,
ADDR_WIDTH = 8)
(
clk,
rst_n,
wr_en,
rd_en,
data_in,
full,
empty,
data_cnt,
data_out
);
//**********************Port definition***********************//
input wr_en;
input rd_en;
input [DATA_WIDTH-1:0] data_in;
input clk;
input rst_n;
output full;
output empty;
output reg [7:0] data_cnt;
output reg [DATA_WIDTH-1:0] data_out;
//******************write address generation******************//
reg [ADDR_WIDTH-1:0] waddr;
always@(posedge clk or negedge rst_n)
if(!rst_n)
waddr <= 'd0;
else if((full == 'd0) && (wr_en))begin
if(waddr >= 'd255)
waddr <= 'd0;
else
waddr <= waddr + 1'd1;
end
else
waddr <= waddr;
//******************read address generation*******************//
reg [ADDR_WIDTH-1:0] raddr;
always@(posedge clk or negedge rst_n)
if(!rst_n)
raddr <= 'd0;
else if((empty == 'd0) && (rd_en))begin
if(raddr >= 'd255)
raddr <= 'd0;
else
raddr <= raddr + 1'd1;
end
else
raddr <= raddr;
//***********************Data counters***********************//
always@(posedge clk or negedge rst_n)
if(!rst_n)
data_cnt <= 'd0;
else if (wr_en && rd_en)
data_cnt <= data_cnt;
else if (!full && wr_en)
data_cnt <= data_cnt + 1'b1;
else if (!empty && rd_en)
data_cnt <= data_cnt - 1'b1;
else
data_cnt <= data_cnt;
//****************empty full signal generation****************//
assign full = (data_cnt == DATA_DEPTH-1 && (rst_n))?'d1:'d0;
assign empty = (data_cnt == 'd0 && (rst_n))?'d1:'d0;
//***********************write to fifo************************//
integer i;
reg [ADDR_WIDTH-1:0] sram [0:DATA_DEPTH-1];
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
for(i = 0; i < DATA_DEPTH;i = i + 1) begin
sram[i] <= 'd0;
end
end
else if(wr_en && (!full))
sram[waddr] <= data_in;
//*******************read out of the fifo*********************//
always@(posedge clk or negedge rst_n)
if(!rst_n)
data_out <= 0;
else if(rd_en && (!empty))
data_out <= sram[raddr];
else
data_out <= data_out;
endmodule
仿真代码:
`timescale 1ns/1ps
`define clk_period 20
module fifo_8_256_tb();
reg wr_en;
reg rd_en;
reg [7:0] data_in;
reg clk;
reg rst_n;
wire full;
wire empty;
wire [7:0] data_cnt;
wire [7:0] data_out;
fifo_8_256 u1(
.clk(clk),
.rst_n(rst_n),
.wr_en(wr_en),
.rd_en(rd_en),
.data_in(data_in),
.full(full),
.empty(empty),
.data_cnt(data_cnt),
.data_out(data_out)
);
initial clk = 1;
always # 10 clk = ~ clk;
initial begin
rst_n = 'd0;
wr_en = 'd0;
rd_en = 'd0;
data_in = 'd0;
#(`clk_period*10+1);
rst_n = 'd1;
#(`clk_period*10);
data_in = 'd0;
repeat(255)begin
wr_en = 'd1;
data_in = data_in + 'd1;
#(`clk_period);
end
wr_en = 'd0;
#(`clk_period*100);
repeat(255)begin
rd_en = 'd1;
#(`clk_period);
end
rd_en = 'd0;
#(`clk_period*10);
wr_en = 'd1;
#(`clk_period*10);
wr_en = 'd0;
#(`clk_period*100);
$finish;
end
initial begin
$fsdbDumpfile("./fifo_8_256_tb.fsdb");
$fsdbDumpvars(0,"fifo_8_256_tb");
$fsdbDumpSVA();
end
endmodule