Verilog实现任意位宽异步FIFO

主模块:

`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

 

逻辑仿真波形图:

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值