首页 > 其他分享 >异步FIFO设计

异步FIFO设计

时间:2024-01-14 13:44:10浏览次数:30  
标签:异步 wire FIFO wptr rst 设计 reg ADDRSIZE 指针

同步FIFO设计思路

  • 方法1:使用计数器记录FIFO有效数据,从而产生空满信号
  • 方法2:指针空间扩大一倍,读写指针最高位相同为空,最高位不同,剩下数据位相同为满

异步FIFO设计

  • 读写指针分别在各自的时钟域进行维护
  • 读空的时候需要在读时钟域进行判断,写满的时候需要在写时钟域进行判断,所以需要将读写指针同步给对方
  • 读指针同步到写时钟域,写指针同步到读时钟域?读写指针都是多bit信号,所以不能直接通过直接打两拍的方式进行同步,使用格雷码进行实现
  • 不使用格雷码,有些bit同步快,有些bit同步慢,所以会产生中间状态,影响电路功能

使用格雷码进行转换

  • 相邻两个数之间格雷码只有1bit发生变化
  • 格雷码产生:gray_code = din ^ (din >> 1),原数据异或右移1位的数据
  • 用指针的格雷码判断空满情况:如果写指针和读指针最高两位不等,其余位全相等表示满,如果读指针和写指针的全相等表示空

RTL

fifo.v
  wr_full.v
  rd_empty.v
  sync_2_stage.v
  fifo_mem.v

fifo.v


定义参数

  • ADDRSIZE - 地址位宽(和FIFO的深度有关)
  • DATASIZE - 数据位宽
  • SD_SLAVE_FIFO_DEPTH - FIFO深度
  • ahb_soft_rst_n - 是同步复位
  • ahb_fifo_rst_n和sd_fifo_rst_n是异步复位信号
  • blk_len输入的作用:当前fifo是1k(256深度*数据位宽32bit)的容量,sd host传输数据一个block length的容量是512byte,也就是半满状态就可以认为是fifo满了,所以需要block length产生判断半满的信号
  • bist测试 - 因为使用的是双端口的RAM,所以BIST逻辑分为a,b两端
    中间信号根据coding过程进行书写

sync_two_stage.v

  • 两级同步器,将读写指针转变为格雷码表示之后,经过两级同步器给到读时钟域和写时钟域
module sync_two_stage
#(
    parameter UDLY = 1,
    parameter ADDRSIZE = 8,
    parameter DATASIZE = 32,
    parameter SD_SLAVE_FIFO_DEPTH = 256
)
(
    input   wire                    clk     ,
    input   wire                    rst_n   ,
    input   wire                    clr     ,
    input   wire    [ADDRSIZE:0]    ptr_reg ,

    output  reg     [ADDRSIZE:0]    ptr_s2_reg         
);

    reg [ADDRSIZE:0] ptr_s1_reg;

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            ptr_s1_reg <= #UDLY 0;
            ptr_s2_reg <= #UDLY 0;
        end
        else if(clr) begin
            ptr_s1_reg <= #UDLY 0;
            ptr_s2_reg <= #UDLY 0;
        end
        else begin
            ptr_s1_reg <= #UDLY ptr_reg;
            ptr_s2_reg <= #UDLY ptr_s1_reg;
        end
            
    end
endmodule
  • 在sd host项目中,因为fifo模块两端都是可以进行读写操作的,所以需要四个同步器,例化四个两级同步器,同步读写指针信号
  • clr信号就是复位信号,由fifo.v模块产生之后输入
  • 需要同步的指针由rd_empty.v模块产生

rd_empty.v

  • 在读时钟域下产生读空信号,并且维护读指针给到RAM进行读取数据

  • 当读使能有效的时候并且没有读空的时候,读指针加1
  • 将读指针的二进制数转成格雷码的形式,将格雷码形式的读地址(读指针)输出
  • 空信号判断:转变为格雷码之后的读指针和输入的写指针地址是否一致,一致表示空,否则表示满
module rd_empty
#(
    parameter UDLY = 1,
    parameter ADDRSIZE = 8,
    parameter DATASIZE = 32,
    parameter SD_SLAVE_FIFO_DEPTH = 256
)
(
    input   wire                      clk             ,
    input   wire                      rst_n           ,
    input   wire                      clr             ,
    input   wire                      rinc            ,
    input   wire    [ADDRSIZE:0]      wptr_s2_reg     ,
    output  wire    [ADDRSIZE-1:0]    raddr           ,
    output  reg     [ADDRSIZE:0]      rptr_reg        ,
    output  reg                       rd_empty
);

    reg [ADDRSIZE:0] rptr_bin;
    
    wire             empty_val;

    wire [ADDRSIZE:0] rptr_bin_nxt;  // binary pointer
    wire [ADDRSIZE:0] rptr_gray_nxt; // gray code pointer
    wire [ADDRSIZE:0] rptr_gray_temp_nxt;

    // 产生二进制读指针

    assign rptr_bin_nxt = rptr_bin_nxt + {8'd0,(rinc && !rd_empty)};

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            rptr_bin <= 0;
        else if(clr)
            rptr_bin <= 0;
        else 
            rptr_bin <= rptr_bin_nxt;
    end

    assign rptr_gray_nxt = (rptr_bin_nxt >> 1) ^ rptr_bin_nxt;

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            rptr_reg <= 0;
        else if(clr)
            rptr_reg <= 0;
        else
            rptr_reg <= rptr_gray_nxt;
    end

    // 读地址
    assign raddr = rptr_bin[ADDRSIZE-1:0];

    assign empty_val = (rptr_gray_nxt == wptr_s2_reg);

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            rd_empty <= 0;
        else if(clr)
            rd_empty <= 0;
        else
            rd_empty <= empty_val;
    end

endmodule

wr_full.v

module wr_full
#(
    parameter UDLY = 1,
    parameter ADDRSIZE = 8,
    parameter DATASIZE = 32,
    parameter SD_SLAVE_FIFO_DEPTH = 256
)
(
    input   wire                      clk             ,
    input   wire                      rst_n           ,
    input   wire                      clr             ,
    input   wire                      winc            ,
    input   wire    [ADDRSIZE:0]      rptr_s2_reg     ,
    input   wire    [ADDRSIZE:0]      blk_len         , // wfull半满即为满
    output  wire    [ADDRSIZE-1:0]    waddr           ,
    output  reg     [ADDRSIZE:0]      wptr_reg        ,
    output  reg                       wr_full
);

    reg [ADDRSIZE:0] wptr_bin;

    wire             full_val;
    wire [ADDRSIZE:0] wptr_bin_nxt;  // binary pointer
    wire [ADDRSIZE:0] wptr_gray_nxt; // gray code pointer
    wire [ADDRSIZE:0] wptr_gray_temp_nxt;
    wire [ADDRSIZE:0] wptr_temp;

    // 产生二进制读指针

    assign wptr_bin_nxt = wptr_bin_nxt + {8'd0,(winc && !wr_full)};

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            wptr_bin <= 0;
        else if(clr)
            wptr_bin <= 0;
        else 
            wptr_bin <= wptr_bin_nxt;
    end

    // fifo深度 - block length
    assign wptr_temp = wptr_bin_nxt + (SD_SLAVE_FIFO_DEPTH - blk_len);

    // wptr_gray_nxt用于产生写指针,发送给另一个时钟域
    assign wptr_gray_nxt = (wptr_bin_nxt >> 1) ^ wptr_bin_nxt; 

    // wptr_gray_temp_nxt - 用于判断是否写满
    assign wptr_gray_temp_nxt = (wptr_temp >>1) ^ wptr_temp;  //wptr_gray_nxt

    // 输出写指针同步到读时钟域
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            wptr_reg <= 0;
        else if(clr)
            wptr_reg <= 0;
        else
            wptr_reg <= wptr_gray_nxt;
    end

    // 输出写地址给到RAM
    assign waddr = wptr_bin[ADDRSIZE-1:0];


    // 判断满信号,并输出出去
    assign full_val = (wptr_gray_temp_nxt == {~rptr_s2_reg[ADDRSIZE:ADDRSIZE-1],rptr_s2_reg[ADDRSIZE-2:0]});

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            wr_full <= 1'b0;
        else if(clr)
            wr_full <= 1'b0;
        else    
            wr_full <= full_val;
    end


endmodule

fifo_mem.v

  • fifo模块中例化存储体,将时钟复位\读写使能\读写数据的端口连接起来

标签:异步,wire,FIFO,wptr,rst,设计,reg,ADDRSIZE,指针
From: https://www.cnblogs.com/Icer-newer/p/17963349

相关文章

  • JUC并发编程 用CompletableFuture 创建异步任务
    1CompletableFuture对Future的改进1.1CompletableFuture为什么会出现get()方法在Future计算完成之前会一直处在阻塞状态下,阻塞的方式和异步编程的设计理念相违背。isDene()方法容易耗费cpu资源(cpu空转),对于真正的异步处理我们希望是可以通过传入回调函数,在Future结束时自动......
  • 基于SSM的图书商城网站的设计和开发
    现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本图书商城网站就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的......
  • JUC 异步编程利器 CompletableFuture 介绍
    1从FutureTask到CompletableFuture1.1FutureFuture接口(FutureTask实现类)定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消异步任务的执行、判断任务是否被取消、判断任务执行是否完毕等。举例:比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开......
  • 基于SSM的培训学校网站的设计与开发
    互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对培训学校展示信息管理混乱,出错率高,信息安全性差,劳动强度大,费时费力等问题,采用金旗帜文化培训学校网站可以有效管理,使信......
  • 基于SSM的新锐台球厅管理系统的设计与实现
    随着信息技术在管理上越来越深入而广泛的应用,作为一般的台球厅都会跟上时代的变化,用上计算机来代表重复性的劳动,并且给用户一种新奇的感受,实现新锐台球厅管理系统在技术上已成熟。本文介绍了新锐台球厅管理系统的开发全过程。通过分析新锐台球厅的需求,创建了一个计算机管理新锐台......
  • 【软件开发学习笔记】设计模式
    【软件开发学习笔记】设计模式设计模式设计模式是一种针对面向对象语言的软件设计方法,是对类设计的约束和指导。设计模式由“原则”和“方法”两部分组成,一个设计良好的项目结构应能完美符合“原则”中的要求,而为了实现完美往往需要按照“方法”的指导去设计。原则:这是必须......
  • 「C语言程序设计」程序设计的基本概念
    算法的特性有穷性:算法必须在执行有限的步骤后终止,不会无限循环或进入死循环确定性:算法的每个步骤必须明确定义,没有歧义。相同输入应产生相同的输出可执行性:算法中的每个步骤都必须能够被执行,不会包含无法实现的操作有零个或多个输入:算法可以接受零个或多个输入参数,这些参数是......
  • 开关电源的设计反激变换器
    开关电源的设计是一份非常耗时费力的苦差事,需要不断地修正多个设计变量,直到性能达到设计目标为止。本文step-by-step介绍反激变换器的设计步骤,并以一个6.5W隔离双路输出的反激变换器设计为例,主控芯片采用NCP1015。基本的反激变换器原理图如图1所示,在需要对输入输出进行电气隔......
  • 做好设计:流程设计
    没有什么是一成不变的。在“做好设计:存储设计基础”一文中,探讨了软件设计中的关键环节:存储设计。存储设计完成之后,要进行流程设计。要做好流程设计,首先要熟悉各种基本流程模式。可阅:““驯服”业务流程:盘点业务开发中的常见流程模式”。熟悉常用流程模式之后,就需要灵活加以......
  • Java多线程编程实战指南(设计模式篇)PDF
    随着CPU多核时代的到来,多线程编程在充分利用计算资源、提高软件服务质量方面扮演了越来越重要的角色。而解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案。然而,多线程编程相关的设计模式书籍多采用C++作为描述语言,且书中所举的例子多与应用开发人员的......