异步fifo设计(2)---手撕代码

异步fifo的工作原理,内部结构,在第一部分已经做了详细的说明,本小节主要介绍如何手写一个任意位宽,任意深度的异步fifo模块。

首先,需要一个fifo控制器,用来设置产生各种信号,例如空满水限,写读地址,该控制器的写数据由设计者定义,读数据由例化的双端口存储器模块送出。

module MyFIFO_Ctrl #(
    parameter Full_Limit  = 1022,
    parameter Empty_Limit = 1   , 
    parameter Data_Width  = 8   ,
    parameter Addr_Width  = 10  
)
(
    input      WClk                          ,
    input      RClk                          ,
    input      Rst                           ,
    input      Wen                           ,
    input      Ren                           ,
    input      [Data_Width - 1 : 0]     Din  ,
    input      [Data_Width - 1 : 0]  RAM_Rd  ,
    
    output     [Data_Width - 1 : 0]      Dout,
    output     RAM_We                        ,
    output reg [Addr_Width - 1 : 0] RAM_Waddr,
    output reg [Addr_Width - 1 : 0] RAM_Raddr,
    output     [Data_Width - 1 : 0] RAM_Wd   ,
    output reg Empty                         ,
    output reg Full                          ,
);

 端口定义好后,需要控制写读使能控制下的存储器地址变化,并将地址信号的值转换为格雷码表示。

//write addr on wclk
always @(posedge WClk or posedge Rst)
begin
    if (Rst)
    RAM_Waddr <= 0;
    else if (Wen)
    RAM_Waddr <= RAM_Waddr + 1'b1;
end

//write addr's binary to grey logic circuit

defparam U1_Norm2Gray.Data_Width = Addr_Width;

Norm2Gray U1_Norm2Gray (
    .Din            (RAM_Waddr),
    .Dout            (RAM_Waddr_Gray_Wire)
);

//RAM_Waddr_Gray_Wire's flip_flop output on wclk
always @(posedge WClk or posedge Rst)
begin
    if (Rst)
    RAM_Waddr_Gray <= 0;
    else
    RAM_Waddr_Gray <= RAM_Waddr_Gray_Wire;
end

//read addr on rclk
always @(posedge RClk or posedge Rst)
begin
    if (Rst)
    RAM_Raddr <= 0;
    else if (Ren)
    RAM_Raddr <= RAM_Raddr + 1'b1;
end

//read addr's binary to gray circuit

defparam U2_Norm2Gray.Data_Width = Addr_Width;

Norm2Gray U2_Norm2Gray (
    .Din            (RAM_Raddr),
    .Dout            (RAM_Raddr_Gray_Wire)
);

//RAM_Raddr_Gray_Wire's flip_flop output on rclk
always @(posedge RClk or posedge Rst)
begin
    if (Rst)
    RAM_Raddr_Gray <= 0;
    else
    RAM_Raddr_Gray <= RAM_Raddr_Gray_Wire;
end

 使能与地址对应产生完成后,考虑空满信号何时产生,首先,需要实现地址的跨时钟域传输;

//ram read addr (gray) to sync by wclk
always @(posedge WClk or posedge Rst)
begin
    if (Rst)
    begin
        RAM_Raddr_Gray_OnWClk_P <= 0;
        RAM_Raddr_Gray_OnWClk   <= 0;
    end
    else
    begin
        RAM_Raddr_Gray_OnWClk_P <= RAM_Raddr_Gray;
        RAM_Raddr_Gray_OnWClk   <= RAM_Raddr_Gray_OnWClk_P;
    end
end

//read (gray) addr on wclk to gray to binary circuitGray2Norm U1_Gray2Norm (
    .Din            (RAM_Raddr_Gray_OnWClk),
    .Dout            (RAM_Raddr_OnWClk_Wire)
);
defparam U1_Gray2Norm.Data_Width = Addr_Width;

//RAM_Raddr_OnWClk_wire to flip_flop output on WClk
always @(posedge WClk or posedge Rst)
begin
    if (Rst)
    RAM_Raddr_OnWClk <= 0;
    else
    RAM_Raddr_OnWClk <= RAM_Raddr_OnWClk_Wire;
end
 

 将读地址同步到写时钟域后,判断full信号的产生。

//write full circuit
assign diff_WRaddr_OnWClk = RAM_Waddr - RAM_Raddr_OnWClk;

always @(posedge WClk or posedge Rst)
begin
    if (Rst)
    Full <= 1'b0;
    else
    begin
        if (diff_WRaddr_OnWClk >= Full_Limit)
        Full <= 1'b1;
        else
        Full <= 1'b0;
    end
end

 将写地址同步到读时钟域产生空信号也是一样的处理方式,代码如下:

//write addr (gray) to sync by rclk
always @(posedge RClk or posedge Rst)
begin
    if (Rst)
    begin
        RAM_Waddr_Gray_OnRClk_P <= 0;
        RAM_Waddr_Gray_OnRClk   <= 0;
    end
    else
    begin
        RAM_Waddr_Gray_OnRClk_P <= RAM_Waddr_Gray;
        RAM_Waddr_Gray_OnRClk   <= RAM_Waddr_Gray_OnRClk_P;
    end
end

//write addr (gray) on wclk to gray to binary logic circuit

Gray2Norm U2_Gray2Norm (
    .Din            (RAM_Waddr_Gray_OnRClk),
    .Dout            (RAM_Waddr_OnRClk_Wire)
);

defparam U2_Gray2Norm.Data_Width = Addr_Width;

//RAM_Waddr_OnRClk_wire to flip_flop on rclk
always @(posedge RClk or posedge Rst)
begin
    if (Rst)
    RAM_Waddr_OnRClk <= 0;
    else
    RAM_Waddr_OnRClk <= RAM_Waddr_OnRClk_Wire;
end

//read empty circuit
assign diff_WRaddr_OnRClk = RAM_Waddr_OnRClk - RAM_Raddr;

always @(posedge RClk or posedge Rst)
begin
    if (Rst)
    Empty <= 1'b1;
    else
    begin
        if (diff_WRaddr_OnRClk <= Empty_Limit)
        Empty <= 1'b1;
        else
        Empty <= 1'b0;
    end
end

 格雷码与二进制互相转换的电路统一封装在一个模块内。

`timescale 1ns/1ps

module Norm2Gray #(parameter Data_Width = 8) (
    input     [Data_Width - 1 : 0] Din,
    output    [Data_Width - 1 : 0] Dout
);

assign    Dout = Din ^ {1'b0, Din[Data_Width - 1 : 1]};

endmodule        

module Gray2Norm #(parameter Data_Width = 8) (
    input     [Data_Width - 1 : 0] Din,
    output    reg    [Data_Width - 1 : 0] Dout
);

integer    i, j;
reg     Tmp;
always @(*)
    begin
        for (i = Data_Width - 1; i >= 0; i = i - 1)
        begin
            Tmp = 1'b0;
            begin
                for (j = Data_Width - 1; j >= i; j = j - 1)
                    Tmp = Tmp ^ Din[j];
            end
            Dout[i] = Tmp;
        end
    end

endmodule    
 

 以上就是整个fifo控制器的内容,但这还不能直接使用,需要添加一个顶层模块,将该模块与存储器模块相连,从而实现fifo的功能。存储器模块的RTL代码不在本节的设计内容以内,可自行选择调用IP核或RTL描述,顶层代码如下。

module MyFIFO1024X8 #(
    parameter Data_Width = 8 ,
    parameter Addr_Width = 10
)
(
    input  WClk                     ,
    input  RClk                     ,
    input  Rst                      ,
    input  Wen                      ,
    input  Ren                      ,
    input  [Data_Width - 1 : 0] Din ,
    output [Data_Width - 1 : 0] Dout,
    output Empty                    ,
    output Full
);

    wire [Data_Width - 1 : 0] RAM_Rd   ;
    wire [Data_Width - 1 : 0] RAM_Wd   ;
    wire                      RAM_We   ;
    wire [Addr_Width - 1 : 0] RAM_Waddr;
    wire [Addr_Width - 1 : 0] RAM_Raddr;

 MyFIFO_Ctrl MyFIFO_Ctrl
(
    .WClk(WClk)                          ,
    .RClk(RClk)                          ,
    .Rst(Rst)                            ,
    .Wen(Wen)                            ,
    .Ren(Ren)                            ,
    .Din(Din)                            ,
    .RAM_Rd(RAM_Rd)                      ,
    
    .Dout(Dout)                          ,
    .RAM_We(RAM_We)                      ,
    .RAM_Waddr(RAM_Waddr)                ,
    .RAM_Raddr(RAM_Raddr)                ,
    .RAM_Wd(RAM_Wd)                      ,
    .Empty(Empty)                        ,
    .Full(Full)                          
);

blk_mem_gen_0    U_bram_1024x8 (
    .dina      ( RAM_Wd ),
    .addrb  ( RAM_Raddr ),
    .clkb        ( RClk ),
    .addra  ( RAM_Waddr ),
    .clka        ( WClk ),
    .wea       ( RAM_We ),
    .doutb     ( RAM_Rd )
);

endmodule

 验证代码波形

写使能拉高,第一个数据ff一拍后被送到输出端口,拉高读使能后等待两拍出数据,满足第一节中双端口RAM逻辑时序,且空满信号正常产生,设计正确。

 以上,是标准模式下的fifo,即读的数据会延迟两拍送出,经实测可直接使用,只需修改数据位宽,地址位宽。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值