直接上代码;参考Clifford E. Cummings大师的style1设计。
给出其英文原文链接:
Simulation and Synthesis Techniques for Asynchronous
FIFO Design
`timescale 1ns / 1ps
//
// Company:
// Engineer: lhf
//
// Create Date: 2022/05/20 19:00:24
// Design Name:
// Module Name: asyn_FIFO
// Project Name:
// Target Devices:
// Tool Versions:
// Description: FIFO 深度为8; 数据位宽为16
// 格雷码指针作用:(1)用于跨时钟域(保证在时钟边沿时只有单bit变化) (2)用来比较空满
// 二进制指针作用:(1)低三位0-7-7-0循环,可以用来产生读写地址 (2)整体0-16-16-0循环,用于产生格雷码
// 打拍的作用?(跨时钟域同步)
// 写“满”判断是在写时钟域,当wclk更快时,可能发生多采,但多采不是问题;当rclk更快时,可能发生漏采,漏采只会造成假“满”,不会造成功能错误。
// 读“空”信号是在读时钟域,当wclk更快时,可能发生漏采,漏采只会造成假“空”,不会有功能问题;当rclk更快时,可能发生多采,但多采不是问题。
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module asyn_FIFO (
//write signal
input wclk,
input wrst_n,
input [15:0] wr_data,
input wr_en,
//read signal
input rclk,
input rrst_n,
output reg [15:0] rd_data,
input rd_en,
//flag
output full,
output empty
);
reg [15:0] RAM[7:0]; //RAM
reg [3:0] wptr; //二进制写指针
reg [3:0] rptr; //二进制读指针
wire [2:0] waddr; //写地址
wire [2:0] raddr; //读地址
wire [3:0] g_wptr; //格雷码写指针
wire [3:0] g_rptr; //格雷码读指针
reg [3:0] g_rptr_r, g_rptr_r1; //打拍
reg [3:0] g_wptr_r, g_wptr_r1; //打拍
//binary to gray
assign g_wptr = (wptr>>1) ^ wptr;
assign g_rptr = (rptr>>1) ^ rptr;
//低三位为地址
assign waddr = wptr[2:0];
assign raddr = rptr[2:0];
//读写指针递增
always @(posedge wclk or negedge wrst_n) begin
if(!wrst_n) begin
wptr <= 0;
end
else if(wr_en && !full)
wptr <= wptr + 1'b1;
end
always @(posedge rclk or negedge rrst_n) begin
if(!rrst_n) begin
rptr <= 0;
end
else if(rd_en && !empty)
rptr <= rptr + 1'b1;
end
//数据读写
always @(posedge wclk) begin //可以不用初始化RAM
if(wr_en && !full) begin
RAM[waddr] <= wr_data;
end
end
always @(posedge rclk) begin
if(rd_en && !empty)
rd_data <= RAM[raddr];
end
//打两拍(作同步)(适用于单bit变化)
always @(posedge wclk or negedge wrst_n) begin //将读gray指针同步到写时钟域,以便于 写满判断
if(!wrst_n) begin
g_rptr_r <= 0;
g_rptr_r1 <= 0;
end
else begin
g_rptr_r <= g_rptr;
g_rptr_r1 <= g_rptr_r;
end
end
always @(posedge rclk or negedge rrst_n) begin //将写gray指针同步到读时钟域,以便于 读空判断
if(!rrst_n) begin
g_wptr_r <= 0;
g_wptr_r1 <= 0;
end
else begin
g_wptr_r <= g_wptr;
g_wptr_r1 <= g_wptr_r;
end
end
//空满判断
assign empty = (g_rptr == g_wptr_r1); //每一位都相同
//assign full = ( (g_wptr[3:2] != g_rptr_r1[3:2]) && (g_wptr[1:0] == g_rptr_r1[1:0]) );
// (g_wptr[3:2] != g_rptr_r1[3:2])逻辑错了,有一位不同这个表达式就成立,这不正确;要特别注意
assign full = (g_wptr == ({~g_rptr_r1[3:2],g_rptr_r1[1:0]})); //高两位不同,低两位相同
endmodule