多比特跨时钟域之异步FIFO

一、异步FIFO结构图

 具体代码直接按照结构图来写,分为读控制模块,写控制模块,同步模块,及顶层模块。

二、各模块代码

1、写控制模块

module wr_ctrl#(
    parameter DEPTH = 16,
    parameter ADDR_WIDTH = $clog2(DEPTH)
)(
    input wire wr_clk,
    input wire wr_rst_n,
    input wire [ADDR_WIDTH : 0] rd_addr_gray,
    input wire wr_en,

    output wire [ADDR_WIDTH : 0] wr_addr_p_gray,
    output wire [ADDR_WIDTH - 1 : 0] wr_addr,
    output wire wr_full
    );
    
    
    reg [ADDR_WIDTH : 0] wr_addr_p;
    reg [ADDR_WIDTH : 0] rd_addr_p; 

    always@(posedge wr_clk or negedge wr_rst_n)begin
        if(!wr_rst_n)
            wr_addr_p <= {ADDR_WIDTH+1{1'b0}};
        else if(wr_en && (~wr_full))
            wr_addr_p <= wr_addr_p + 1'b1;
    end

    assign wr_addr = wr_addr_p[ADDR_WIDTH - 1 : 0];

    assign wr_addr_p_gray = {wr_addr_p[ADDR_WIDTH],wr_addr_p[ADDR_WIDTH:1] ^ wr_addr_p[ADDR_WIDTH-1:0]};

    integer i ;
    always@(*)begin
        rd_addr_p[ADDR_WIDTH] = rd_addr_gray[ADDR_WIDTH];
        for(i = ADDR_WIDTH - 1 ; i >= 0 ; i = i - 1)
            rd_addr_p[i] = rd_addr_p[i+1] ^ rd_addr_gray[i];
    end

    assign wr_full = (wr_addr_p[ADDR_WIDTH] != rd_addr_p[ADDR_WIDTH]) && (wr_addr_p[ADDR_WIDTH-1:0] == rd_addr_p[ADDR_WIDTH-1:0]);

    
endmodule

写控制模块作用包括:产生写地址指针,写地址,将读时钟域的格雷码转换,产生写满标志位。

这里我格雷码转二进制用了for循环,因为考虑到模块复用的情况,地址宽度是变化的,我没有想到更好的方法。下面读控制同理。(有好的方法欢迎提出)

2、读控制模块

module rd_ctrl#(
    parameter DEPTH = 16,
    parameter ADDR_WIDTH = $clog2(DEPTH)
)(
    input wire rd_clk,
    input wire rd_rst_n,
    input wire rd_en,
    input wire [ADDR_WIDTH:0] wr_addr_gray,

    output wire rd_empty,
    output wire [ADDR_WIDTH-1:0] rd_addr,
    output wire [ADDR_WIDTH:0] rd_addr_p_gray
);
    

    reg [ADDR_WIDTH:0] rd_addr_p;
    reg [ADDR_WIDTH:0] wr_addr_p;

    always@(posedge rd_clk or negedge rd_rst_n)begin
        if(!rd_rst_n)
            rd_addr_p <= {ADDR_WIDTH+1{1'b0}};
        else if(rd_en && (~rd_empty))
            rd_addr_p <= rd_addr_p + 1'b1;
    end

    assign rd_addr = rd_addr_p[ADDR_WIDTH-1:0];

    assign rd_addr_p_gray = {rd_addr_p[ADDR_WIDTH],rd_addr_p[ADDR_WIDTH:1] ^ rd_addr_p[ADDR_WIDTH-1:0]};

    integer j ;
    always@(*)begin
        wr_addr_p[ADDR_WIDTH] = wr_addr_gray[ADDR_WIDTH];
        for(j = ADDR_WIDTH - 1 ; j >= 0 ; j = j - 1)
            wr_addr_p[j] = wr_addr_p[j+1] ^ wr_addr_gray[j]; 
    end

    assign rd_empty = wr_addr_p == rd_addr_p;

endmodule

读控制模块作用包括:产生读地址指针,读地址,将写时钟域的格雷码转换,产生读空标志位。

3、同步模块

module sync2#(
    parameter DEPTH = 16,
    parameter ADDR_WIDTH = $clog2(DEPTH)
)(
    input wire clk,
    input wire rst_n,
    input wire [ADDR_WIDTH : 0] addr_in,

    output wire [ADDR_WIDTH : 0] addr_out
);
    reg [ADDR_WIDTH : 0] addr_d1;
    reg [ADDR_WIDTH : 0] addr_d2;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            addr_d1 <= 0;
            addr_d2 <= 0;
        end
        else begin
            addr_d1 <= addr_in;
            addr_d2 <= addr_d1;
        end
    end

    assign addr_out = addr_d2;

endmodule

同步模块作用:格雷码传输,相邻格雷码间只有1位变化,使用两级同步器。

4、顶层模块

module async_fifo
#(
    parameter DEPTH = 16,
    parameter WIDTH = 8
    )(
    input wire wr_clk,
    input wire wr_req,
    input wire wr_rst_n,
    input wire [WIDTH - 1:0] data_in,
    output wire wr_full,

    input wire rd_clk,
    input wire rd_req,
    input wire rd_rst_n,
    output wire rd_empty,
    output reg [WIDTH - 1:0] data_out
);
    parameter ADDR_WIDTH = $clog2(DEPTH);

    reg [WIDTH - 1 : 0] regs_array [DEPTH - 1 : 0];
    
    wire [ADDR_WIDTH : 0] wptr,rptr;
    wire [ADDR_WIDTH : 0] wptr_d2,rptr_d2;
    wire [ADDR_WIDTH - 1 : 0] wr_addr,rd_addr;

    always@(posedge wr_clk)begin
        if(wr_req && (!wr_full))
            regs_array[wr_addr] <= data_in;
    end

    always@(posedge rd_clk)begin
        if(rd_req && (!rd_empty))
            data_out <= regs_array[rd_addr];
    end

    wr_ctrl  #(
    .DEPTH      (16)
    )wr_ctrl_inst(
    .wr_clk         (wr_clk),
    .wr_rst_n       (wr_rst_n),
    .wr_en          (wr_req),
    .rd_addr_gray   (rptr_d2),

    .wr_addr        (wr_addr),
    .wr_addr_p_gray (wptr),
    .wr_full        (wr_full)
    );

    rd_ctrl #(
    .DEPTH      (16)
    )rd_ctrl_inst(
    .rd_clk         (rd_clk),
    .rd_rst_n       (rd_rst_n),
    .rd_en          (rd_req),
    .wr_addr_gray   (wptr_d2),

    .rd_addr        (rd_addr),
    .rd_addr_p_gray (rptr),
    .rd_empty       (rd_empty)
    );

    sync2 #(
    .DEPTH      (16)
    )sync2_inst1(
    .clk        (wr_clk),
    .rst_n      (wr_rst_n),
    .addr_in    (rptr),
    .addr_out   (rptr_d2)
    );

    sync2#(
    .DEPTH      (16)
    )sync2_inst2(
    .clk        (rd_clk),
    .rst_n      (rd_rst_n),
    .addr_in    (wptr),
    .addr_out   (wptr_d2)
    );

endmodule

顶层模块作用包括:例外各个子模块,定义存储器变量(图中双口ram),读写数据控制。

三、testbench

`timescale 1ns/1ns;
module async_fifo_tb();
    parameter WIDTH = 8;
    
    reg wr_clk;
    reg wr_req;
    reg wr_rst_n;
    reg [WIDTH - 1:0] data_in;
    
    reg rd_clk;
    reg rd_req;
    reg rd_rst_n;

    wire wr_full;
    wire rd_empty;
    wire [WIDTH - 1:0] data_out;

    reg init_done;

always #2 wr_clk = ~wr_clk;
always #4 rd_clk = ~rd_clk;

initial begin
    wr_rst_n = 0;
    rd_rst_n = 0;
    wr_clk = 0;
    rd_clk = 0;
    wr_req = 0;
    rd_req = 0;
    data_in = 0;
    init_done = 0;
    #25;
    wr_rst_n = 1;
    rd_rst_n = 1;
    init_done = 1;
    #10000
    $finish;
end

always@(*)begin
    if(init_done)begin
        if(wr_full)
            wr_req = 0;
        else
            wr_req = 1;
    end
end

always@(*)begin
    if(init_done)begin
        if(rd_empty)
            rd_req = 0;
        else
            rd_req = 1;
    end
end

always@(posedge wr_clk)begin
    if(init_done)begin
        if(!wr_full)
            data_in <= data_in + 1'b1;
        else
            data_in <= data_in;
    end
    else
        data_in <= 0;
end

    async_fifo  #(
    .DEPTH      (16),
    .WIDTH      (8)
    )async_fifo_inst(
    .wr_clk     (wr_clk),
    .wr_req     (wr_req),
    .wr_rst_n   (wr_rst_n),
    .data_in    (data_in),
    .wr_full    (wr_full),

    .rd_clk     (rd_clk),
    .rd_req     (rd_req),
    .rd_rst_n   (rd_rst_n),
    .rd_empty   (rd_empty),
    .data_out   (data_out)
    );

    initial begin
        $fsdbDumpfile("tb.fsdb");
        $fsdbDumpvars;
    end

endmodule

testbench作用为:给激励,产生读写数据,控制读写请求,例化顶层模块。

四、仿真结果

仿真了边读边写的情况,可以看到写满拉高,读写时钟域分开,方便观察中间信号。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值