【CDC跨时钟域】多bit_异步fifo

异步fifo设计包含以下5步:

  1. 产生二进制表示的读写地址

  2. 将二进制地址转换为格雷码形式表达

  3. 打两拍同步读写指针,将读指针同步到写时钟域,写指针同步到读时钟域

  4. 空满信号的产生与判断

  5. 例化RAM
    电路图如下:
    在这里插入图片描述

    代码如下:

//设计一个深度为16,宽度为8的异步fifo
module dual_port_RAM #(parameter DEPTH = 16,
             parameter WIDTH = 8)(
   input wclk
  ,input wenc
  ,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
  ,input [WIDTH-1:0] wdata        //数据写入
  ,input rclk
  ,input renc
  ,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
  ,output reg [WIDTH-1:0] rdata     //数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
always @(posedge wclk) begin
  if(wenc)
    RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
  if(renc)
    rdata <= RAM_MEM[raddr];
end 

endmodule 

module asyn_fifo#(
  parameter  WIDTH = 8,
  parameter   DEPTH = 16
)(
  input           wclk  , 
  input           rclk  ,   
  input           wrstn  ,
  input          rrstn  ,
  input           winc  ,
  input            rinc  ,
  input     [WIDTH-1:0]  wdata  ,

  output wire        wfull  ,// 写满
  output wire        rempty  ,// 读空
    output wire [WIDTH-1:0]  rdata    // 读数据
);

//二进制地址的产生//读地址//写地址
parameter ADDR_WIDTH = $clog2(DEPTH);

reg [ADDR_WIDTH:0] waddr_bin;
reg [ADDR_WIDTH:0] raddr_bin;

always@(posedge wclk or negedge wrstn)begin
    if(!wrstn)
        waddr_bin <= 'd0;
    else if(!wfull && winc)
        waddr_bin <= waddr_bin + 1'd1;
end

always@(posedge rclk or negedge rrstn)begin
    if(!rrstn)
        raddr_bin <= 'd0;
    else if(!rempty && rinc)
        raddr_bin <= raddr_bin + 1'd1;
end

//gray 格雷码地址的产生//读指针//写指针
wire   [ADDR_WIDTH:0]  waddr_gray;
wire   [ADDR_WIDTH:0]  raddr_gray;
reg    [ADDR_WIDTH:0]  wptr;//写指针
reg    [ADDR_WIDTH:0]  rptr;//读指针

assign waddr_gray = waddr_bin^(waddr_bin>>1);
assign raddr_gray = raddr_bin^(raddr_bin>>1);

always @(posedge wclk or negedge wrstn) begin 
        if(~wrstn) begin
            wptr <= 'd0;
        end 
        else begin
            wptr <= waddr_gray;
        end
    end
   always @(posedge rclk or negedge rrstn) begin 
        if(~rrstn) begin
            rptr <= 'd0;
        end 
        else begin
            rptr <= raddr_gray;
        end
    end
    
//打两拍同步读写指针,将读指针同步到写时钟域,将写指针同步到读时钟域
reg     [ADDR_WIDTH:0]  wptr_buff;
reg     [ADDR_WIDTH:0]  wptr_syn;
reg     [ADDR_WIDTH:0]  rptr_buff;
reg     [ADDR_WIDTH:0]  rptr_syn;

always @(posedge wclk or negedge wrstn) begin 
        if(~wrstn) begin
            rptr_buff <= 'd0;
            rptr_syn <= 'd0;
        end 
        else begin
            rptr_buff <= rptr;
            rptr_syn <= rptr_buff;
        end
    end
    always @(posedge rclk or negedge rrstn) begin 
        if(~rrstn) begin
            wptr_buff <= 'd0;
            wptr_syn <= 'd0;
        end 
        else begin
            wptr_buff <= wptr;
            wptr_syn <= wptr_buff;
        end
    end   
    
//空满信号的产生,写满:高两位不同,其余位相同;读空:所有位相同。
assign wfull=(wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]});
assign rempty=(rptr == wptr_syn);//读指针等于打两拍之后同步之后的写指针

//例化RAM
    wire wen;
    wire ren;
    wire wren;  
    wire [ADDR_WIDTH-1:0]  waddr;//这里为4位
    wire [ADDR_WIDTH-1:0]  raddr;
    assign wen = winc & !wfull;
    assign ren = rinc & !rempty;
    assign waddr = waddr_bin[ADDR_WIDTH-1:0];
    assign raddr = raddr_bin[ADDR_WIDTH-1:0];
    
    dual_port_RAM dual_port_RAM0(
        .wclk(wclk)
        ,.wenc(wen)
        ,.waddr(waddr)  //深度对2取对数,得到地址的位宽。
        ,.wdata(wdata)        //数据写入
        ,.rclk(rclk)
        ,.renc(ren)
        ,.raddr(raddr) //深度对2取对数,得到地址的位宽。
        ,.rdata(rdata)
    );

endmodule


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值