主模块:
`timescale 1ns / 1ps
module Async_FIFO#(
parameter ADDR_WIDTH = 4,
DATA_WIDTH = 8,
ALMOST_GAP = 3
)(
input wire wr_clk , //写时钟
input wire wr_rst_n, //写复位
input wire wr_en , //写使能
input wire [DATA_WIDTH-1:0] wr_data , //用于写入的数据
input wire rd_clk , //读时钟
input wire rd_rst_n, //读复位
input wire rd_en , //读使能
output reg full , //FIFO写满信号,使用输出寄存器以保证与控制时钟同步
output reg empty , //FIFO读空信号,使用输出寄存器以保证与控制时钟同步
output reg almost_full,//FIFO将满信号,使用输出寄存器以保证与控制时钟同步
output reg almost_empty,//FIFO将空信号,使用输出寄存器以保证与控制时钟同步
output wire [DATA_WIDTH-1:0] rd_data //读出的数据
);
wire full_next, empty_next, almost_empty_next, almost_full_next;//空满信号次态逻辑
reg [ADDR_WIDTH:0] wr_addr_ptr_next, rd_addr_ptr_next;//读写地址指针二进制码次态逻辑
reg [ADDR_WIDTH:0] wr_addr_ptr, rd_addr_ptr; //读写地址指针
wire [ADDR_WIDTH-1:0] wr_addr, rd_addr; //读写地址
//读写地址赋值
assign wr_addr = wr_addr_ptr[ADDR_WIDTH-1:0];
assign rd_addr = rd_addr_ptr[ADDR_WIDTH-1:0];
wire [ADDR_WIDTH:0] wr_addr_ptr_gray_next, rd_addr_ptr_gray_next; //读写地址指针格雷码次态逻辑
reg [ADDR_WIDTH:0] wr_addr_ptr_gray, wr_addr_ptr_gray1, wr_addr_ptr_gray2; //写地址指针格雷码转换及跨时钟打拍信号
reg [ADDR_WIDTH:0] rd_addr_ptr_gray, rd_addr_ptr_gray1, rd_addr_ptr_gray2; //读地址指针格雷码转换及跨时钟打拍信号
// *************************** 写时钟域时序逻辑 ***************************** //
//数据更新
always@(posedge wr_clk or negedge wr_rst_n)
if(!wr_rst_n) begin
wr_addr_ptr <= 0;
wr_addr_ptr_gray <= 0;
full <= 0;
almost_full <= 0;
end
else begin
wr_addr_ptr <= wr_addr_ptr_next;
wr_addr_ptr_gray <= wr_addr_ptr_gray_next;
full <= full_next;
almost_full <= almost_full_next;
end
//格雷码读指针同步至写时钟域
always@(posedge wr_clk or negedge wr_rst_n)
if(!wr_rst_n) begin
rd_addr_ptr_gray1 <= 0;
rd_addr_ptr_gray2 <= 0;
end
else begin
rd_addr_ptr_gray1 <= rd_addr_ptr_gray ;
rd_addr_ptr_gray2 <= rd_addr_ptr_gray1;
end
// ************************************************************************** //
// *************************** 读时钟域时序逻辑 ***************************** //
//数据更新
always@(posedge rd_clk or negedge rd_rst_n)
if(!rd_rst_n) begin
rd_addr_ptr <= 0;
rd_addr_ptr_gray <= 0;
empty <= 0;
almost_empty <= 0;
end
else begin
rd_addr_ptr <= rd_addr_ptr_next;
rd_addr_ptr_gray <= rd_addr_ptr_gray_next;
empty <= empty_next;
almost_empty <= almost_empty_next;
end
//格雷码写指针同步至读时钟域
always@(posedge rd_clk or negedge rd_rst_n)
if(!rd_rst_n) begin
wr_addr_ptr_gray1 <= 0;
wr_addr_ptr_gray2 <= 0;
end
else begin
wr_addr_ptr_gray1 <= wr_addr_ptr_gray ;
wr_addr_ptr_gray2 <= wr_addr_ptr_gray1;
end
// ************************************************************************** //
// ********************** 格雷码计数器的处理(次态逻辑) ********************** //
//写地址指针:二进制转格雷码
assign wr_addr_ptr_gray_next = (wr_addr_ptr_next >> 1) ^ wr_addr_ptr_next;
//读地址指针:二进制转格雷码
assign rd_addr_ptr_gray_next = (rd_addr_ptr_next >> 1) ^ rd_addr_ptr_next;
reg [ADDR_WIDTH:0] wr_addr_ptr_gray_to_bin;
reg [ADDR_WIDTH:0] rd_addr_ptr_gray_to_bin;
//同步后的写地址格雷码指针解码:格雷码转二进制
integer i;
always@(*) begin
wr_addr_ptr_gray_to_bin[ADDR_WIDTH] = wr_addr_ptr_gray2[ADDR_WIDTH];
for(i=ADDR_WIDTH-1; i>=0; i=i-1)
wr_addr_ptr_gray_to_bin[i] = wr_addr_ptr_gray2[i] ^ wr_addr_ptr_gray_to_bin[i+1];
end
//同步后的读地址格雷码指针解码:格雷码转二进制
integer j;
always@(*) begin
rd_addr_ptr_gray_to_bin[ADDR_WIDTH] = rd_addr_ptr_gray2[ADDR_WIDTH];
for(j=ADDR_WIDTH-1; j>=0; j=j-1)
rd_addr_ptr_gray_to_bin[j] = rd_addr_ptr_gray2[j] ^ rd_addr_ptr_gray_to_bin[j+1];
end
// ************************************************************************** //
// ******************************* 次态逻辑 ********************************* //
//二进制写地址指针次态逻辑
always@(*)
if(!wr_rst_n)
wr_addr_ptr_next = 0;
else if(wr_en && !full_next)
wr_addr_ptr_next = wr_addr_ptr + 1;
else
wr_addr_ptr_next = wr_addr_ptr;
//二进制读地址指针次态逻辑
always@(*)
if(!rd_rst_n)
rd_addr_ptr_next = 0;
else if(rd_en && !empty_next)
rd_addr_ptr_next = rd_addr_ptr + 1;
else
rd_addr_ptr_next = rd_addr_ptr;
//满信号次态逻辑,用格雷码高两位取反,用二进制码最高位取反
assign full_next = ({!wr_addr_ptr[ADDR_WIDTH], wr_addr} == rd_addr_ptr_gray_to_bin)?1:0;
//空信号次态逻辑
assign empty_next = (rd_addr_ptr == wr_addr_ptr_gray_to_bin)?1:0;
reg [ADDR_WIDTH:0] room_avail;
reg [ADDR_WIDTH:0] data_avail;
//room_avail信号生成,用于进行将满判断,对应写时钟域信号
always@(*)
if(wr_addr_ptr[ADDR_WIDTH] != rd_addr_ptr_gray_to_bin[ADDR_WIDTH])
room_avail = rd_addr_ptr_gray_to_bin[ADDR_WIDTH-1:0] - wr_addr_ptr[ADDR_WIDTH-1:0];
else
room_avail = 2**ADDR_WIDTH - (wr_addr_ptr[ADDR_WIDTH-1:0] - rd_addr_ptr_gray_to_bin[ADDR_WIDTH-1:0]);
assign almost_full_next = (room_avail <= ALMOST_GAP)?1:0;
//data_avail信号生成,用于进行将空判断,对应读时钟域信号
always@(*)
if(rd_addr_ptr[ADDR_WIDTH] != wr_addr_ptr_gray_to_bin[ADDR_WIDTH])
data_avail = 2**ADDR_WIDTH - (rd_addr_ptr[ADDR_WIDTH-1:0] - wr_addr_ptr_gray_to_bin[ADDR_WIDTH-1:0]);
else
data_avail = wr_addr_ptr_gray_to_bin[ADDR_WIDTH-1:0] - rd_addr_ptr[ADDR_WIDTH-1:0];
assign almost_empty_next = (data_avail <= ALMOST_GAP)?1:0;
wire RAM_wr_en, RAM_rd_en;
assign RAM_wr_en = wr_en && !full_next;
assign RAM_rd_en = rd_en && !empty_next;
// ************************************************************************** //
// ****************************** 存储模块例化 ****************************** //
RAM_MEM RAM_MEM_inst
(
.wr_clk ( wr_clk ) ,
.wr_rst_n ( wr_rst_n ) ,
.wr_en ( RAM_wr_en ) ,
.wr_addr ( wr_addr ) ,
.rd_clk ( rd_clk ) ,
.rd_rst_n ( rd_rst_n ) ,
.rd_en ( RAM_rd_en ) ,
.rd_addr ( rd_addr ) ,
.data_in ( wr_data ) ,
.data_out ( rd_data )
);
// ************************************************************************** //
endmodule
// ************************************************************************** //
// ****************************** 存储模块定义 ****************************** //
// ************************************************************************** //
module RAM_MEM #(
parameter DATA_WIDTH = 8,
ADDR_WIDTH = 4
)(
input wire wr_clk ,
input wire wr_rst_n,
input wire wr_en ,
input wire [ADDR_WIDTH-1:0] wr_addr ,
input wire rd_clk ,
input wire rd_rst_n,
input wire rd_en ,
input wire [ADDR_WIDTH-1:0] rd_addr ,
input wire [DATA_WIDTH-1:0] data_in ,
output reg [DATA_WIDTH-1:0] data_out
);
//存储阵列定义
reg [DATA_WIDTH-1:0] SRAM_MEM [2**ADDR_WIDTH - 1:0];
//数据写入
integer i;
always @ (posedge wr_clk or negedge wr_rst_n)
if(!wr_rst_n)
for(i=0; i<2**ADDR_WIDTH;i=i+1)
SRAM_MEM[i] <= 0;
else if (wr_en)
SRAM_MEM [wr_addr] <= data_in;
else
SRAM_MEM [wr_addr] <= SRAM_MEM [wr_addr];
//数据读出
always @ (posedge rd_clk or negedge rd_rst_n)
if(!rd_rst_n)
data_out <= 'b0;
else if (rd_en)
data_out <= SRAM_MEM [rd_addr];
else
data_out <= 'b0;
endmodule
// ************************************************************************** //
testbench:
`timescale 1ns / 1ps
module tb_Async_FIFO();
parameter ADDR_WIDTH = 4,
DATA_WIDTH = 8;
reg wr_clk ;
reg wr_rst_n ;
reg wr_en ;
reg [DATA_WIDTH-1:0] wr_data ;
reg rd_clk ;
reg rd_rst_n ;
reg rd_en ;
wire full ;
wire empty ;
wire almost_full ;
wire almost_empty;
wire [DATA_WIDTH-1:0] rd_data;
initial begin
wr_clk = 0;
wr_rst_n = 0;
wr_en = 0;
wr_data = 0;
rd_clk = 0;
rd_rst_n = 0;
rd_en = 0;
#20 wr_rst_n = 1; rd_rst_n = 1;
wr_data = 8'd5;
#20 wr_en = 1;
#20 wr_data = 8'd6;
#20 wr_data = 8'd7;
#20 wr_data = 8'd8;
#20 wr_data = 8'd9;
#20 wr_data = 8'd10;
#10 wr_en = 0;
#20 rd_en = 1;
#300 rd_en = 0; wr_en = 1;
#10 wr_data = 8'd1;
#20 wr_data = 8'd2;
#20 wr_data = 8'd3;
#20 wr_data = 8'd4;
#20 wr_data = 8'd5;
#20 wr_data = 8'd6;
#20 wr_data = 8'd7;
#20 wr_data = 8'd8;
#20 wr_data = 8'd9;
#20 wr_data = 8'd10;
#20 wr_data = 8'd11;
#20 wr_data = 8'd12;
#20 wr_data = 8'd13;
#20 wr_data = 8'd14;
#20 wr_data = 8'd15;
#20 wr_data = 8'd16;
#20 wr_data = 8'd17;
#100 wr_en = 0; rd_en = 1;
end
always #10 wr_clk = ~wr_clk;
always #20 rd_clk = ~rd_clk;
Async_FIFO Async_FIFO_inst
(
.wr_clk ( wr_clk ) , //写时钟
.wr_rst_n ( wr_rst_n ) , //写复位
.wr_en ( wr_en ) , //写使能
.wr_data ( wr_data ) , //用于写入的数据
.rd_clk ( rd_clk ) , //读时钟
.rd_rst_n ( rd_rst_n ) , //读复位
.rd_en ( rd_en ) , //读使能
.full ( full ) ,
.empty ( empty ) ,
.almost_full ( almost_full ) ,
.almost_empty ( almost_empty ) ,
.rd_data ( rd_data ) //读出的数据
);
endmodule
逻辑仿真波形图: