异步fifo

异步FIFO 是指读写时钟不一致,读写时钟是互相独立的,多用于跨时钟域的数据传输,也可用于位宽变换。

同步FIFO:读写时钟一样,输入输出的位宽可以不同。

同步/异步FIFO的配置

一般的异步FIFO的配置步骤参考:vivado ip核 FIFO的配置与调用_vivado的fifoip核实现不同位宽-
CSDN博客

注意:下图对应位置不勾选,IP出现wr_rst和rd_rst信号;勾选,整个IP只有一个rst信号

手撕异步FIFO的代码

module async_fifo
#(
    parameter   FIFO_WIDTH = 8,
    parameter   FIFO_DEPTH = 8,
    parameter   ADDR_WIDTH = $clog2(FIFO_DEPTH)
)
(
    //write clock domain
    input                       wr_clk      ,
    input                       wr_rst_n    ,
    input                       wr_en       ,
    input   [FIFO_WIDTH-1:0]    din         ,
    output                      full        ,
    //read clock domain
    input                       rd_clk      ,
    input                       rd_rst_n    ,
    input                       rd_en       ,
    output  [FIFO_WIDTH-1:0]    dout        ,
    output                      empty        
);

//------------------------------------------------//
integer i;
//memory
reg     [FIFO_WIDTH-1:0]    mem     [FIFO_DEPTH-1:0];
//write address and read address
wire    [ADDR_WIDTH-1:0]    wr_addr;
wire    [ADDR_ WIDTH-1:0]    rd_addr;
//write pointer    写指针
reg     [ADDR_WIDTH:0]      wr_ptr;

//写指针转换成格雷玛
wire    [ADDR_WIDTH:0]      wr_ptr_gray;
reg     [ADDR_WIDTH:0]      wr_ptr_gray_w2r_1;
reg     [ADDR_WIDTH:0]      wr_ptr_gray_w2r_2;
//read pointer    读指针
reg     [ADDR_WIDTH:0]      rd_ptr;

//读指针转换成格雷玛
wire    [ADDR_WIDTH:0]      rd_ptr_gray;
reg     [ADDR_WIDTH:0]      rd_ptr_gray_r2w_1;
reg     [ADDR_WIDTH:0]      rd_ptr_gray_r2w_2;
//读出数据寄存器
reg     [FIFO_WIDTH-1:0]    dout_r;

//------------------------------------------------//
//----------------------//
//write pointer
always @(posedge wr_clk or negedge wr_rst_n) begin
    if(!wr_rst_n)
        wr_ptr <= 'd0;
    else if(wr_en && !full)
        wr_ptr <= wr_ptr + 1'b1;
end

//write pointer(binary to gray)
assign wr_ptr_gray = wr_ptr ^ (wr_ptr>>1);

//gray write pointer syncrous   打2拍
always @(posedge rd_clk or negedge rd_rst_n) begin
    if(!rd_rst_n) begin
        wr_ptr_gray_w2r_1 <= 'd0;
        wr_ptr_gray_w2r_2 <= 'd0;
    end
    else begin
        wr_ptr_gray_w2r_1 <= wr_ptr_gray;
        wr_ptr_gray_w2r_2 <= wr_ptr_gray_w2r_1;
    end
end 

//----------------------//
//read pointer
always @(posedge rd_clk or negedge rd_rst_n) begin
    if(!rd_rst_n)
        rd_ptr <= 'd0;
    else if(rd_en && !empty)
        rd_ptr <= rd_ptr + 1'b1;
end

//read pointer(binary to gray)      打2拍
assign rd_ptr_gray = rd_ptr ^ (rd_ptr>>1);

//gray read pointer syncrous
always @(posedge wr_clk or negedge wr_rst_n) begin
    if(!wr_rst_n) begin
        rd_ptr_gray_r2w_1 <= 'd0;
        rd_ptr_gray_r2w_2 <= 'd0;
    end
    else begin
        rd_ptr_gray_r2w_1 <= rd_ptr_gray;
        rd_ptr_gray_r2w_2 <= rd_ptr_gray_r2w_1;
    end
end 

//----------------------//
//write address and read address
assign wr_addr = wr_ptr[ADDR_WIDTH-1:0];
assign rd_addr = rd_ptr[ADDR_WIDTH-1:0];

//full
assign full = (wr_ptr_gray == {~rd_ptr_gray_r2w_2[ADDR_WIDTH:ADDR_WIDTH-1],rd_ptr_gray_r2w_2[ADDR_WIDTH-2:0]}) ? 1'b1 : 1'b0;
//empty
assign empty = (rd_ptr_gray == wr_ptr_gray_w2r_2) ? 1'b1 : 1'b0;

//----------------------//
//write
always @(posedge wr_clk or negedge wr_rst_n) begin
    if(!wr_rst_n)
        for(i=0;i<FIFO_DEPTH;i=i+1) begin
            mem[i] <= 'd0;
        end
    else if(wr_en && !full)
        mem[wr_addr] <= din;
end

//read
always @(posedge rd_clk or negedge rd_rst_n) begin
    if(!rd_rst_n)
        dout_r <= 'd0;
    else if(rd_en && !empty)
        dout_r <= mem[rd_addr];
end

//输出赋值
assign dout = dout_r;

endmodule

异步fifo的测试文件

`timescale 1ns/1ps
module test_tb();
reg                 rst;
reg                 wr_clk;
reg                 rd_clk;
reg      [4 : 0]    din;
wire                 wr_en;
wire                 rd_en;
wire     [4 : 0]    dout;
wire                full;
wire                wr_ack;
wire                empty;
wire                valid;
wire     [3 : 0]    rd_data_count;
wire     [3 : 0]    wr_data_count;
wire                almost_full;
wire                overflow;
wire                almost_empty;
wire                underflow;
 
 
//读时钟
initial rd_clk = 1;
always#5 rd_clk = ~rd_clk;
 
//写时钟
initial wr_clk = 1;
always#25 wr_clk = ~wr_clk;
 
//读使能、写使能
assign wr_en = ((~rd_en) && (~full));
assign rd_en = ((~wr_en) && (~empty));
 
//写数据 
always @ ( posedge wr_clk ) begin
    if ( rst )
        din <= 5'd1;
    else if ( wr_en )
        din <= din + 1'b1;
end
 
initial begin
    rst = 1;
    #11;
    rst = 0;
 
end
 
//例化
t1 t1_inst(
    .rst            ( rst           ),
    .wr_clk         ( wr_clk        ),
    .rd_clk         ( rd_clk        ),
    .din            ( din           ),
    .wr_en          ( wr_en         ),
    .rd_en          ( rd_en         ),
    .dout           ( dout          ),
    .full           ( full          ),
    .wr_ack         ( wr_ack        ),
    .empty          ( empty         ),
    .valid          ( valid         ),
    .rd_data_count  ( rd_data_count ),
    .wr_data_count  ( wr_data_count ),
    .almost_full    (almost_full    ),   
    .overflow       (overflow       ),   
    .almost_empty   (almost_empty   ),       
    .underflow      (underflow      )    
    );
endmodule

手撕同步FIFO

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/05/16 09:23:07
// Design Name: 
// Module Name: sync_fifo
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 同步FIFO
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module sync_fifo
#(
    parameter   DATA_WIDTH = 8,
    parameter   DATA_DEPTH = 8,
    parameter   CNT_WIDTH  = $clog2(DATA_DEPTH)
)
(
    input                      clk         ,
    input                      rst_n       ,
    input                      wr_en       ,
    input   [DATA_WIDTH-1:0]   din         ,
    input                      rd_en       ,
    output  [DATA_WIDTH-1:0]   dout        ,
    output                     almost_full ,
    output                     full        ,
    output                     almost_empty,    
    output                     empty        
);

integer i;
reg     [DATA_WIDTH-1:0]    mem     [DATA_DEPTH-1:0];
reg     [CNT_WIDTH:0]       cnt;
reg     [CNT_WIDTH-1:0]     wr_ptr,rd_ptr;
reg     [DATA_WIDTH-1:0]    dout_r;

//===========write pointer address===========//
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_ptr <= 'd0;
    else if(wr_en && !full)
        wr_ptr <= wr_ptr + 1'b1;
    else
        wr_ptr <= wr_ptr;
end

//===========read pointer address===========//
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_ptr <= 'd0;
    else if(rd_en && !empty)
        rd_ptr <= rd_ptr + 1'b1;
    else
        rd_ptr <= rd_ptr;
end

//===========counter===========//
//原版
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt <= 'd0;
    else if(wr_en && !full && !rd_en)
        cnt <= cnt + 1'b1;
    else if(rd_en && !empty && !wr_en)
        cnt <= cnt - 1'b1;
    else
        cnt <= cnt;
end

/*
//修改版
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt <= 'd0;
    else if(wr_en && !full && !rd_en) //写使能wr_en有效,读使能rd_en无效
        cnt <= cnt + 1'b1;
    else if(wr_en && !full && rd_en && empty) //rd_en为高电平,但是empty为高电平,所以实际上读使能rd_en无效
        cnt <= cnt + 1'b1;
    else if(rd_en && !empty && !wr_en) //读使能rd_en有效,写使能wr_en无效
        cnt <= cnt - 1'b1;
    else if(rd_en && !empty && wr_en && full) //wr_en为高电平,但是full为高电平,所以实际上写使能wr_en无效
        cnt <= cnt - 1'b1;
    else
        cnt <= cnt;
end
*/

//===========full===========//
assign full = (cnt == DATA_DEPTH) ? 1'b1 : 1'b0;

//===========empty===========//
assign empty = (cnt == 'd0) ? 1'b1 : 1'b0;

//===========almost_full===========//
assign almost_full = (cnt >= DATA_DEPTH-1) ? 1'b1 : 1'b0;

//===========almost_empty===========//
assign almost_empty = (cnt <= 'd1) ? 1'b1 : 1'b0;

//===========read data===========//
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        dout_r <= 'd0;
    else if(rd_en && !empty)
        dout_r <= mem[rd_ptr];
end
assign dout = dout_r;

//===========write data===========//
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        for(i=0;i<DATA_DEPTH;i=i+1) begin
            mem[i] <= 'd0;
        end
    else if(wr_en && !full)
        mem[wr_ptr] <= din;
end

endmodule
 

同步FIFO的仿真文件

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/05/16 11:22:57
// Design Name: 
// Module Name: tb_sync_fifo
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module tb_sync_fifo;

parameter   DATA_WIDTH = 8;
parameter   DATA_DEPTH = 16;
parameter   CNT_WIDTH  = $clog2(DATA_DEPTH);

reg                      clk,rst_n;
reg                      wr_en    ;
reg   [DATA_WIDTH-1:0]   din      ;
reg                      rd_en    ;
wire  [DATA_WIDTH-1:0]   dout     ;
wire                     full     ;
wire                     empty    ;
wire                     almost_full ;
wire                     almost_empty;

parameter  clk_period = 10; 

initial 
begin
    clk = 1'b1;
    rst_n <= 1'b1;
    rd_en <= 1'b0;
    wr_en <= 1'b0;
    din <= 'd0;
  
    #(clk_period*10)
    rst_n <= 1'b0;
    #(clk_period*1)
    rst_n <= 1'b1;    

    repeat(50) begin
        @(posedge clk) begin
          wr_en <= {$random}%2;
          rd_en <= {$random}%2;
          
          
          din <= din + 1'b1;
        end
    end
    #(clk_period)
    #(clk_period*10)
    $stop;
end

always #(clk_period/2)  clk = ~clk;

sync_fifo
#(
    .DATA_WIDTH(DATA_WIDTH),
    .DATA_DEPTH(DATA_DEPTH)
)sync_fifo
(
    .clk         (clk         ),
    .rst_n       (rst_n       ),
    .wr_en       (wr_en       ),
    .din         (din         ),
    .rd_en       (rd_en       ),
    .dout        (dout        ),
    .almost_full (almost_full ),
    .full        (full        ),
    .almost_empty(almost_empty),    
    .empty       (empty       ) 
);

endmodule
 

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vivado是一款由赛灵思(Xilinx)公司开发的集成电路设计工具。它提供了丰富的功能和工具,用于设计、验证和实现FPGA(现场可编程门阵列)和SoC(片上系统)。异步FIFO(First In First Out)是FIFO的一种类型,用于跨时钟域信号传递。异步FIFO与同步FIFO相比,不要求读写时钟频率相同,因此更加灵活。 在Vivado中,可以使用IP核(Intellectual Property)库中提供的现成的异步FIFO IP来设计异步FIFO。这个IP核包含了异步FIFO的功能模块,可以直接在设计中使用。可以通过Vivado的可视化界面添加异步FIFO IP核,并根据设计需求进行配置。配置完成后,可以生成对应的硬件描述语言(HDL)代码,通过这些代码可以实例化异步FIFO模块。 除了使用Vivado提供的异步FIFO IP核外,您也可以自己编写异步FIFO模块。根据您提供的引用,您可以自编异步FIFO,并根据具体需求选择normal模式或show-ahead模式。在设计完成后,您可以使用Vivado提供的testbench功能对异步FIFO进行仿真,以验证其功能和性能。根据您的描述,测试结果与Xilinx IP一致,这表明您的自编异步FIFO在功能上与Xilinx IP相似。 总结起来,您可以在Vivado中使用现成的异步FIFO IP核,也可以自己编写异步FIFO模块。通过Vivado的可视化界面或者HDL代码进行配置和实例化。最后,使用Vivado的testbench功能对异步FIFO进行仿真,以验证其功能和性能。这样,您就可以在Vivado中设计和实现异步FIFO了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值