一、同步FIFO介绍
FIFO (First-In-First-Out) 是一种先进先出的数据交互方式,相当于一个RAM存储模块,在数字ASIC/SOC设计中常常被使用。FIFO按工作时钟域的不同又可以分为:同步FIFO和异步FIFO*。同步FIFO的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。异步FIFO的写时钟和读时钟为异步时钟,FIFO内部的写逻辑和读逻辑的交互需要异步处理,异步FIFO常用于跨时钟域交互。
同步FIFO通常由三大部分组成:
(1)、 FIFO写逻辑控制——产生FIFO写地址、写有效信号,同时产生FIFO写满、写错等状态信号;
(2)、 FIFO读逻辑控制——产生FIFO读地址、读有效信号,同时产生FIFO读空、读错等状态信号;
(3)、 FIFO存储体(如Memory,reg等)。
其逻辑结构如下所示:
FIFO读写逻辑控制如下:
1)、当FIFO初始化(复位)时fifo_write_addr与fifo_read_addr同指到0x0,此时FIFO处于空状态;
2)、当FIFO进行写操作时,fifo_write_addr递增(增加到FIFO DEPTH时回绕),与fifo_read_addr错开,此时FIFO处于非空状态;
3)、当FIFO进行读操作时,fifo_read_addr递增;
FIFO空满状态产生:
为产生FIFO空满标志,引入FIFO Counter计数器,FIFO Counter寄数器用于指示FIFO内部存储数据个数;
1)、当只有写操作时,FIFO Counter加1;只有读操作是,FIFO Counter减1;其他情况下,FIFO Counter保持;
2)、当FIFO Counter为0时,说明FIFO为空,fifo_empty置位;
3)、当FIFO Counter等于FIFO_DEPTH时,说明FIFO已满,fifo_full置位;
二、同步FIFO Code
2.1.计数器Counter判断空满
//1)、RAM design ,16×8 daul ram---存储模块设计
module dp_ram( //verilog---1995
wr_clk,
wr_en,
wr_addr,
wr_data,
rd_clk,
rd_en,
rd_addr,
rd_data);
parameter WIDTH = 8;
parameter DEPTH = 16;
parameter ADDR = 4;
input wr_clk;
input wr_en;
input [ADDR-1:0] wr_addr;
input [WIDTH-1:0] wr_data;
input rd_clk;
input rd_en;
input [ADDR-1:0] rd_addr;
output[WIDTH-1:0] rd_data;
reg [WIDTH-1:0] rd_data;
reg [WIDTH-1:0] memory[DEPTH-1:0]; //memory为一个由16个8位寄存器组成的存储体
//read and write data
always @(posedge wr_clk) begin //写数据部分
if(wr_en) begin
memory[wr_addr] <= wr_data; //时序逻辑,采用非阻塞赋值"<="
end
end
always @(posedge rd_clk) begin //读数据部分
if(rd_en) begin
rd_data <= memory[rd_addr]; //时序逻辑,采用非阻塞赋值"<="
end
end
endmodule
//2)、top module design---顶层模块设计,FIFO
module sync_fifo
#(parameter F_WIDTH = 8,
parameter F_DEPTH = 16,
parameter F_ADDR = 4 )
(input clk, //verilog---2001
input rst,
input wr_en,
input rd_en,
input [F_WIDTH-1:0] wr_data,
output[F_WIDTH-1:0] rd_data,
output full,
output empty //full 和empty都为wire类型,以便于使用assign状态设计
);
reg [F_ADDR-1:0] rd_addr;
reg [F_ADDR-1:0] wr_addr; //读写地址为中间信号
reg [F_ADDR:0] counter; //计数器最大数值需达到16,4位2进制数最大只能表示15,故不需减1
//define full and empty status
assign full = (counter == F_DEPTH)?1:0;
assign empty = (counter == 'h0)?1:0;
//initialization,将顶层设计与存储体连接
dp_ram u_dp_ram(
.wr_clk (clk),
.wr_en (wr_en),
.wr_addr (wr_addr),
.wr_data (wr_data),
.rd_clk (clk),
.rd_en (rd_en),
.rd_addr (rd_addr),
.rd_data (rd_data)
);
//generate rd_addr, 产生读地址
always@(posedge clk,negedge rst)begin
if(!rst) begin
rd_addr <= 'h0;
end
else if(!empty && rd_en) begin
rd_addr <= rd_addr + 1'b1;
end
end
//generate wr_addr, 产生写地址
always@(posedge clk,negedge rst)begin
if(!rst) begin
wr_addr <= 'h0;
end
else if(!full && wr_en) begin
wr_addr <= wr_addr + 1'b1;
end
end
//counter in fifo,计录存储体中的数据长度
always@(posedge clk,negedge rst)begin
if(!rst)
counter <= 'b0;
else if(!full && wr_en && !rd_en)
counter <= counter + 1'b1;
else if(!empty && !wr_en && rd_en)
counter <= counter - 1'b1;
else
counter <= counter;
endmodule
关于模块引用连接,采用名字映射时,相对应数据类型的规定如下图二所示:
代码参考:https://blog.csdn.net/u014070258/article/details/90052370
2.2.扩展地址最高位判断空满
module sync_fifo
#(parameter WIDTH = 8,
parameter DEPTH = 16,
parameter ADDR = 4)
(input clk, rst, wr_en, rd_en,
input [WIDTH-1:0] wr_data,
output reg [WIDTH-1:0] rd_data, //尽量将reg与output声明到一起避免提示变量重复声明
output full,empty);
wire [ADDR-1:0] rd_addr,wr_addr; //中间信号不必在端口中声明
reg [WIDTH-1:0] memory [DEPTH-1:0];
reg [ADDR:0] rd_addr_1; //扩展一位地址用于判断空满
reg [ADDR:0] wr_addr_1; //扩展一位地址用于判断空满
//reg [WIDTH-1:0] rd_data; // 有时将output与reg分开声明Questasim编译可能会报错
assign wr_addr = wr_addr_1[ADDR-1:0];
assign rd_addr = rd_addr_1[ADDR-1:0];
always @(posedge clk or negedge rst) //写数据
begin
if(!rst)
wr_addr_1 <= 'h0;
else if(!full && wr_en)begin
wr_addr_1 <= wr_addr_1 + 1'b1;
memory[wr_addr] <= wr_data;
end
end
always @(posedge clk or negedge rst) //读数据
begin
if(!rst)
rd_addr_1 <= 'h0;
else if(!empty && rd_en)begin
rd_addr_1 <= rd_addr_1 + 1'b1;
rd_data <= memory[rd_addr];
end
end
assign empty = (w_addr_1==rd_addr_1)?1:0; //地址判断
assign full = ((w_addr_1[ADDR]!=rd_addr_1[ADDR]) && (w_addr_1[ADDR-1:0]==rd_addr_1[ADDR-1:0]))?1:0;
endmodule