精华内容
下载资源
问答
  • FIFO

    2013-11-03 08:04:29
    FIFO

           FIFO指代先进先出(first in, first out),Unix中的FIFO类似于管道。它是一个单向(半双工)数据流。不同于管道的是每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO也称谓有名管道(named pipe)。

           FIFO由mkfifo函数创建。

    <span style="font-size:14px">#include <sys/types.h>
    #include <sys/stat.h>
    
    int mkfifo(const char *pathname, mode_t mode);        返回:成功时为0,出错时为-1</span>

           FIFO创建完后,它必须或者打开来读或者打开来写。可以使用open或fopen函数打开。FIFO不能打开来既读又写,因为它是半双工的。

           对管道或FIFO的write总是往末尾添加数据,对它们的read则总是从开通返回数据,如果对管道或FIFO调用lseek,那就会返回ESPIPE错误。
           下面是一个简单的例子:

    /*
     * main.cpp
     *
     *  Created on: 2013-10-26
     *      Author: Richard
     */
    
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    
    #define FIFO1 "/tmp/fifo.1"
    #define FIFO2 "/tmp/fifo.2"
    
    void client(int readfd, int writefd)
    {
        size_t len;
        size_t n;
        char buff[1024];
        fgets(buff, 1024, stdin);
        len = strlen(buff);
        if (buff[len - 1] == '\n')
        {
            len--;
        }
        write(writefd, buff, len);
        while ((n = read(readfd, buff, 1024)) > 0)
        {
            write(STDOUT_FILENO, buff, n);
        }
    }
    
    void server(int readfd, int writefd)
    {
        int fd;
        ssize_t n;
        char buff[1025];
        if ((n = read(readfd, buff, 1024)) == 0)
        {
            printf("End of file while reading pathname");
            exit(0);
        }
        buff[n] = '\0';
        if ((fd = open(buff, O_RDONLY)) < 0)
        {
            snprintf(buff + n, sizeof(buff) - n, "Cannot open, %s\n",
                    strerror(errno));
            n = strlen(buff);
            write(writefd, buff, n);
        }
        else
        {
            while ((n = read(fd, buff, 1024)) > 0)
            {
                write(writefd, buff, n);
            }
            close(fd);
        }
    
    }
    
    int main(int argc, char **argv)
    {
        int readfd, writefd;
        pid_t childpid;
        if ((mkfifo(FIFO1, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0)
                && (errno != EEXIST))
        {
            printf("cannot create %s\n", FIFO1);
            return 0;
        }
    
        if ((mkfifo(FIFO2, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0)
                && (errno != EEXIST))
        {
            unlink(FIFO1);
            printf("cannot create %s\n", FIFO2);
            return 0;
        }
    
        if ((childpid = fork()) == 0)
        {
            readfd = open(FIFO1, O_RDONLY, 0);
            writefd = open(FIFO2, O_WRONLY, 0);
            server(readfd, writefd);
            exit(0);
        }
    
        writefd = open(FIFO1, O_WRONLY, 0);
        readfd = open(FIFO2, O_RDONLY, 0);
    
        client(readfd, writefd);
        waitpid(childpid, NULL, 0);
        close(readfd);
        close(writefd);
    
        unlink(FIFO1);
        unlink(FIFO2);
    
        return 0;
    }
    
    



    展开全文
  • fifo

    2018-07-23 14:56:58
    FIFO 有时被称为命名管道,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的祖先,但是,FIFO,不相关的进程也能交换数据。 FIFO是一种文件类型,通过stat结构的st_mode成员的编码...

    FIFO 有时被称为命名管道,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的祖先,但是,FIFO,不相关的进程也能交换数据。

    FIFO是一种文件类型,通过stat结构的st_mode成员的编码可以知道文件是否是FIFO类型。

    FIFO的两种用途:

    (1)shell命令使用FIFO将数据从一条管道传送到另一条时,无需创建中间临时文件。

    (2)客户进程-服务器进程应用程序中,FIFO用作汇集点,在客户进程和服务器进程之间传递数据。

     

    展开全文
  • FIFO及测试代码 fifo

    2019-04-30 16:18:08
    FIFO及测试代码 fifo,经过调试成功,可以放心使用,非常好的代码
  • 异步fifo和同步fifo

    2019-04-12 20:25:04
    fifo在ic设计的时候非常常用,尤其是在一些大型的设计中
  • FIFO的操作

    2021-01-19 15:27:43
    系统在上电复位时,SPI工作在标准SPI模式,禁止FIFO功能。FIFO的寄存器SPIFFTX、SPIFFRX和SPIFFCT不起作用。通过将SPIFFTX寄存器中的SPIFFEN的位置为1,使能FIFO模式。SPIRST能在操作的任一阶段复位FIFO模式。  ...
  • 采用C程序实现任意大小的FIFO
  • FIFO(二):FIFO工作原理

    万次阅读 多人点赞 2019-06-04 11:06:16
    FIFO工作原理 根据FIFO工作的时钟域分为同步/异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟在时钟沿来临时同时发生读写。异步FIFO读写时钟不一致,读写相互独立。 1.读写指针的工作原理 读指针:总是指向下...

     

                                                FIFO工作原理

         根据FIFO工作的时钟域分为同步/异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟在时钟沿来临时同时发生读写。异步FIFO读写时钟不一致,读写相互独立。

    1.读写指针的工作原理

    读指针:总是指向下一个将要读取的单元,复位时指向第一个单元(编号为0);

    写指针:总是指向当前要被读出的数据,复位时,指向第一个单元(编号为0);

    2. FIFO的“空/满”检测

      FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空/满”状态标识。

          很明显,当FIFO写满时,就不能再写新数据了,否则就导致数据的溢出;而在空状态即之前写入的数据已经读完,就不能再读取了,否则读出的数据就是无效数据。

    2.1读空信号(rd_empty)

          一般情况当读写指针相等时,表明FIFO为空,这种情况发生在 复位操作时 或者当读指针读出FIFO中最后一个字 后,追赶上写指针时,此时读空信号有效。

    发生如下图所示:

    2.2写满信号(wr_full)

         当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈折回来(wrapped around)又追上了读指针,如下图:

     

    以上FIFO的读写状态是基于读写指针的比较,而问题是两者的判断条件都是读写指针相等,区别在于写满状态是在写指针写满一圈后重新追上读指针,这该如何取表示呢???

    如何区分到底是满状态还是空状态呢??

    2.3 FIFO读写状态的判断方法

    可以采用以下两种方法:

    1extra bit

    一般情况下深度为N=2^n的FIFO其地址的位宽为n,其读写地址的位宽也为n。共有N个存储单元,若数据位宽为W则该FIFO的容量即为N*W bit。

    现在在指针中添加额外的位(extra bit,即地址的MSB)变为n+1bit,该extra bit用于指示写指针是否递增并越过最后一个FIFO地址,若越过则将该MSB加1,其它位清零。对读指针也进行同样的操作。如对于深度为8的FIFO,需要采用3+1bit是计数器:0000-1000、1001-1111,MSB作为折回标志,而低3位作为地址指针。

    那么判断机制为:

       *如果两个指针的MSB不同,就说明写指针比读指针多折回一次:如r_addr=0000,且w_addr=1000,为满;

       *如果两个指针的MSB相同,就说明两个指针折回次数相等。再者其余位相等(则说明FIFO为空。

    2)设置数据计数器

    设置一个data_counter, 当写使能有效时每写入一个data计数器同时加1;读操作时,每读出一个data时该计数器减1。如此,当data_counter为0时FIFO空,当data_counter计数至FIFO深度deep时,表示FIFO写满。

    由于计数器会占用额外的资源,当FIFO较大时,可能会降低FIFO的读写速度。

    3 读写指针的表示

    读写指针的编码有多种,上述提到的指针采用二进制码形式表示。

    3.1二进制FIFO指针

    将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个 二进制到gray码的转换电路  ,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

    3.2 gray码表示指针

    使用gray码解决了一个问题,同时也带来另一个问题,即格雷码如何判断空与满?

    空的判断

    依然依据二者完全相等(包括MSB);

    满的判断

    如下图,由于gray码除了MSB外具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB其余位都相同,不能说它为满。因此不能单纯地检测最高位了,在gray码上判断为满必须同时满足以下3条:

    wptr 和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次;

    wptr与rptr的次高位不相等;如上图位置7和和位置15,转化为2进制:0111和1111. 。MSB不同说明多折回一次,111相同代表同一个位置。 

     

    展开全文
  • fifo队列设计

    2017-10-10 17:05:25
    fifo队列设计fifo队列设计fifo队列设计fifo队列设计fifo队列设计fifo队列设计fifo队列设计fifo队列设计fifo队列设计
  • FIFO根据输入输出时钟是否一致,分为同步FIFO与...本文以异步FIFO与同步FIFO的异同入手,在比较过程中逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,最终实现异步FIFO,并进行了仿真、调试、以及验证

    FIFO根据输入输出时钟是否一致,分为同步FIFO与异步FIFO。同步FIFO中,读写控制信号以及数据均处于同一时钟域,满足STA分析时一般不会出现亚稳态等不稳定情形;而对于异步FIFO,读写相关信号处于不同时钟域,信号的不同步可能会导致亚稳态,导致FIFO工作异常,设计较为复杂;在之前的记录中,我们对同步FIFO的设计进行了分析:

    Verilog实现FIFO专题(3-同步FIFO设计)

    此处我们不再对同步FIFO进行介绍而直接以异步FIFO与同步FIFO的异同为线索,逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,并进一步实现异步FIFO。

    目录

    一、异步FIFO与同步FIFO工作流程比较

    1、同步FIFO

    2、异步FIFO

    二、异步FIFO的空满检测

    1、同步FIFO的空满检测

    2、异步FIFO的空满检测

    计数检测空满:

    指针比较检测空满:

    扩展指针比较检测空满:

    格雷码指针比较检测空满:

    三、异步FIFO的同步处理

    1、同步方式

    2、延迟对FIFO设计的影响

    结论:

    FIFO满检测:

    FIFO空检测:

    四、异步FIFO设计

    1、端口设计

    外部端口

    内部信号

    2、功能描述

    3、实现代码

    4、仿真验证

    五、参考文献


    一、异步FIFO与同步FIFO工作流程比较

    1、同步FIFO

    同步FIFO的读写控制信号以及数据均处于同一时钟域,即:FIFO在同一时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

    2、异步FIFO

    异步FIFO的工作内容与同步FIFO类似:FIFO在时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

    但是异步FIFO的控制并不像同步FIFO那么简单,因为异步FIFO工作在不同的时钟域,这就带来了一些问题:

    (1)如何进行空满检测?还能像同步FIFO中通过计数值来判断吗?

    (2)需要同步电路

    二、异步FIFO的空满检测

    1、同步FIFO的空满检测

    同步FIFO的空满检测可以通过计数很简单的实现:

    读写逻辑是同一个时钟,因此可以在每次时钟来临时进行判断,如果不执行读写操作/同时读写,则计数值不变;只执行读操作,计数值减1;只执行写操作,计数值加1;

    如果计数值为0,则说明FIFO空,只能写不能读(直到写入一次数据,计数值加1,FIFO不再为空才能执行读操作);

    如果计数值为FIFO深度,则说明FIFO满,只能读不能写(直到读出一次数据,计数值减1,FIFO不再为满才能执行写操作);

    2、异步FIFO的空满检测

    计数检测空满:

    异步FIFO不能采用同步FIFO这种计数方式来实现空满检测,因为用两个时钟去控制同一个计数器的加剪很明显是不可取的。

    如果不明白为什么不能用两个时钟控制同一个计数器,可以查阅:Verilog中always@()语句双边沿触发(语法与综合的差异)

    指针比较检测空满:

    读写指针指向读写操作面向的FIFO地址空间,因此空满检测的另一个思路是比较读写指针。每次写操作执行,写指针加1;而每次读操作执行,读指针加1,因此:

    FIFO空发生在:读指针追上写指针时;

    FIFO满发生在:写指针追上读指针时;

    但是这种处理仍存在一个问题,就是读写指针相等时,难以区分FIFO是空还是满。

    扩展指针比较检测空满:

    如上分析,直接比较读写指针时存在一个特例:读写指针相等时,难以区分FIFO是空还是满。

    因此,有人提出,将指针进行高位扩展。即指针加宽一位,当写指针超出FIFO深度时,这额外的一位就会改变。FIFO的深度决定了指针扩展前(即除了最高位的其余位)的宽度,而这扩展的一位与FIFO深度无关,是为了标志指针多转了一圈,因此:

    当读写指针完全相同时,FIFO空;

    当读写指针高位不同,其余位完全相同时,FIFO满;

    经过指针扩展,可以明确的进行空满检测。但是这种处理仍然不够,因为异步FIFO读写时钟相互独立,分属不同时钟域,相邻二进制地址位变化时,不止一个地址位都要变化,这样指针在进行同步过程中很容易出错,比如写指针在从0111到1000跳变时4位同时改变,这样读时钟在进行写指针同步后得到的写指针可能是0000-1111的某个值,一共有2^4个可能的情况,而这些都是不可控制的,你并不能确定会出现哪个值,那出错的概率非常大。

    格雷码指针比较检测空满:

    如上分析,直接比较扩展读写指针时可能因多位改变导致错误。因此,进一步采用gray码形式的指针,利用格雷码每次只变化一位的特性,降低同步发生时错误的概率。如图,为一个深度为8FIFO的格雷码指针(绿色框中):

    0-7为真实的FIFO地址,而8-15是指针多转一圈以后的地址(8-0,9-1...)。应注意,此时指针按照格雷码方式进行编码,不能再用二级制指针的比较方式来判断空满。比如:位置6(0101)和位置9(1101),除最高位外其余位均相等。但是很明显,位置9实际对应位置1处的存储空间。

    因此,格雷码指针下的空满检测条件为:

    当最高位和次高位均相同,其余位相同:FIFO空

    当最高位和次高位均相反,其余位相同:FIFO满

    因此,最终的空满检测方式为:将二进制指针转换为格雷码,用于另一时钟域接收,随后按照检测条件进行检测。

    二进制指针转换为格雷码的详情见:Verilog实现二进制码与格雷码转换  此处不再展开。

    三、异步FIFO的同步处理

    1、同步方式

    判断FIFO空满状态时,需要在读FIFO时获取写时钟域的写指针,与读指针比较来判断FIFO是否为空;需要在写FIFO时获取读时钟域的读指针,与写指针比较来判断FIFO是否为满;

    也就是说,判断空满状态时牵扯到跨时钟域问题,需要进行同步;

    采用两级寄存器打两拍的方式进行同步,具体实现见:亚稳态专题

    2、延迟对FIFO设计的影响

    异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域,所以在比较之前需要先将读写指针进行同步处理,将读写指针同步后再进行比较,判断FIFO空满状态。但是因为在同步指针时需要时间(如延迟两拍同步),而在这个同步的时间内有可能还会写入/读出新的数据,因此同步后的指针一定是小于或者等于当前实际的读/写指针,那么此时判断FIFO满空状态时是否会出错?是否会导致错误?

    结论:

    先说结论:异步逻辑进行同步时,不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。

    FIFO满检测:

    FIFO满检测发生在写时钟域,将读指针同步到写时钟域后再和写指针比较,进行FIFO满状态判断。因为同步时间的存在,同步后的读指针一定是小于或者等于当前真正的读指针(同步时间内可能出现了读操作,导致读指针增加),所以此时判断FIFO为满不一定是真满,这样更保守(即:写指针=同步读指针<=真实读指针),这样可以保证FIFO的特性:FIFO空之后不能继续读取。

    FIFO空检测:

    FIFO空检测发生在读时钟域,将写指针同步到读时钟域后再和读指针比较,进行FIFO空状态判断。因为同步时间的存在,同步后的写指针一定是小于或者等于当前真正的写指针(同步时间内可能出现了写操作,导致写指针增加),所以此时判断FIFO为空不一定是真空,这样更保守(即:读指针=同步写指针<=真实写指针),这样可以保证FIFO的特性:FIFO满之后不能继续写入。

    四、异步FIFO设计

    1、端口设计

    外部端口

    1、读时钟信号clk_r,作为异步FIFO的读驱动信号

    2、写时钟信号clk_w,作为异步FIFO的写驱动信号

    3、异步复位信号rst_n

    // 写FIFO相关

    4、数据输入信号din[DW-1:0],作为FIFO数据写入端,DW数据为位宽

    5、写使能we

    // 读FIFO相关

    6、数据输出信号dout[DW-1:0],作为FIFO数据输出端

    7、读使能re

    // 标志相关

    8、FIFO满标志full,FIFO满时不能再写入数据

    9、FIFO空标志empty,FIFO空时不能再读出数据

    内部信号

    1、读指针wp,作为读地址(FIFO读写只能顺序进行,不能外部设置,因此为内部信号)

    2、格雷码读指针wp_g,供写时钟域同步后判断FIFO满使用

    3、写指针rp,作为写地址

    4、格雷码写指针wp_g,供读时钟域同步后判断FIFO空使用

    5、[DW-1:0]ram[0:Depth-1],数据存储空间,Depth为FIFO深度

    2、功能描述

    读逻辑:

    clk_r来临,re有效,并且FIFO非空(empty=0)时,将读指针rp对应地址处的数据读出至dout;

    // 读操作
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            dout <= {DW{1'bz}};
        else if(!empty & re)
            dout <= ram[rp[AW-1:0]];
        else
            dout <= dout;
    end
    

    读指针逻辑

    clk_r来临,re有效,并且FIFO非空(empty=0)时,进行一次读操作,rp加1(即顺序读出),并进行格雷码转换,生成对应rp_g;

    // 读指针
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            rp <= {AW{1'b0}};
        else if(!empty & re)
            rp <= rp+1'b1;
        else
            rp <= rp;
    end
    

    写逻辑:

    clk_w来临,we有效,并且FIFO不满(full=0)时,将din写入写指针wp对应地址处;

    //写操作
    always@(posedge clk_w)
    begin
        if(!full & we)
            ram[wp[AW-1:0]] <= din;
        else
            ram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
    end
    

    写指针逻辑

    clk_w来临,we有效,并且FIFO不满(full=0)时,进行一次写操作,wp加1(即顺序存储),并进行格雷码转换,生成对应wp_g;

    //写指针
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            wp <= {AW{1'b0}};
        else if(!full & we)
            wp <= wp+1'b1;
        else
            wp <= wp;
    end
    

    格雷码指针生成逻辑:

    利用二进制与格雷码的转换关系,将二进制指针转换为格雷码指针,用于另一个时钟域的同步接收;

    // 二进制指针转换为格雷指针
    assign wp_g = (wp>>1) ^ wp;
    assign rp_g = (rp>>1) ^ rp;
    

    格雷码指针同步逻辑:

    读时钟域,同步写地址,用于空逻辑判断;写时钟域,同步读地址,用于满逻辑判断;

    // 读时钟域,写地址同步
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            begin
                wp_m <= {AW{1'b0}};
                wp_s <= {AW{1'b0}};       
            end
        else
            begin
                wp_m <= wp_g;
                wp_s <= wp_m;    
            end       
    end
    // 写时钟域,读地址同步
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            begin
                rp_m <= {AW{1'b0}};
                rp_s <= {AW{1'b0}};       
            end
        else
            begin
                rp_m <= rp_g;
                rp_s <= rp_m;    
            end       
    end
    

    空满检测逻辑:

    根据格雷码指针判断逻辑,进行空满检测,应注意:

    读时钟域,同步写地址。读格雷码指针同步后写格雷码指针比较,用于空逻辑判断;

    写时钟域,同步读地址,写格雷码指针同步后读格雷码指针比较,用于满逻辑判断;

    // 空满检测,使用同步后的格雷指针?
    assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
    assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针
    

    3、实现代码

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: guoliang CLL
    // 
    // Create Date: 2020/03/24 20:51:06
    // Design Name: 
    // Module Name: afifo
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module afifo
    #(parameter DW = 8,AW = 4)//默认数据宽度8,FIFO深度16
    (
        input clk_r,
        input clk_w,
        input rst_n,
        input we,
        input re,
        input [DW-1:0]din,
        output reg [DW-1:0]dout,
        output empty,
        output full
        );
    // internal signal
    parameter Depth = 1 << AW;//depth of FIFO 
    reg [DW-1:0]ram[0:Depth-1];
    reg [AW:0]wp;  //point
    reg [AW:0]rp;
    wire [AW:0]wp_g;//Gray point
    wire [AW:0]rp_g;
    reg [AW:0]wp_m;//mid_point for syn
    reg [AW:0]rp_m;
    reg [AW:0]wp_s;//point after syn
    reg [AW:0]rp_s;
    // FIFO declaration
    // 二进制指针转换为格雷指针
    assign wp_g = (wp>>1) ^ wp;
    assign rp_g = (rp>>1) ^ rp;
    // 空满检测,使用同步后的格雷指针?
    assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
    assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针
    // 读指针
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            rp <= {AW{1'b0}};
        else if(!empty & re)
            rp <= rp+1'b1;
        else
            rp <= rp;
    end
    //写指针
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            wp <= {AW{1'b0}};
        else if(!full & we)
            wp <= wp+1'b1;
        else
            wp <= wp;
    end
    // 读操作
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            dout <= {DW{1'bz}};
        else if(!empty & re)
            dout <= ram[rp[AW-1:0]];
        else
            dout <= dout;
    end
    //写操作
    always@(posedge clk_w)
    begin
        if(!full & we)
            ram[wp[AW-1:0]] <= din;
        else
            ram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
    end
    // 读时钟域,写地址同步
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            begin
                wp_m <= {AW{1'b0}};
                wp_s <= {AW{1'b0}};       
            end
        else
            begin
                wp_m <= wp_g;
                wp_s <= wp_m;    
            end       
    end
    // 写时钟域,读地址同步
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            begin
                rp_m <= {AW{1'b0}};
                rp_s <= {AW{1'b0}};       
            end
        else
            begin
                rp_m <= rp_g;
                rp_s <= rp_m;    
            end       
    end
    endmodule
    

    4、仿真验证

    测试文件如下:

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: CLL guoliang
    // 
    // Create Date: 2020/03/24 21:22:45
    // Design Name: 
    // Module Name: afifo_tsb
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module afifo_tsb(
    
        );
    // port declaration
    reg clk_r;
    reg clk_w;
    reg rst_n;
    reg we;
    reg re;
    reg [7:0]din;
    wire  [7:0]dout;
    wire empty;
    wire full;
    //clk
    initial
    begin
        clk_r = 1'b0;
        forever #25 clk_r = ~clk_r;
    end
    initial
    begin
        clk_w = 1'b0;
        forever #10 clk_w = ~clk_w;
    end
    // 
    initial
    begin
        rst_n = 1'b1;
        din = 1'b0;
        re = 1'b0;
        we = 1'b0;
        #50 rst_n = 1'b0;
        #50 rst_n = 1'b1;
        // only write
        we = 1'b1;
        repeat(20) #20 din = din+1'b1;
        // only read 
        we = 1'b0;
        re = 1'b1;
        repeat(20) #50; 
        // read and write
    //    we = 1'b1;
    //    re = 1'b1;
    //    din = 1'b0;
    //    repeat(20) #20 din = din+1'b1;     
    end
    // inst
    afifo inst2(
        .clk_r(clk_r),
        .clk_w(clk_w),
        .rst_n(rst_n),
        .we(we),
        .re(re),
        .din(din),
        .dout(dout),
        .empty(empty),
        .full(full)
    );
    endmodule
    
    

    仿真结果如下:

    篇幅有限,仿真结果不再展开分析。提醒自己,应注意仿真测试是很必要的,通过功能仿真能暴露出设计上的不足、缺陷、以及实现过程中因粗心等导致的其余问题;

    因此,如何设计测试文件也具有重要意义。测试文件容易编写,但是如何使得测试文件能全面的对设计进行检测,高效准确的对设计进行测试,无疑是一门学问;

    我只简单记录一下,我调试时关注的部分

    1、写逻辑

    数据能否在写时钟驱动下,顺序写入FIFO中对应地址;FIFO满时,是否停止写入;

    2、读逻辑

    能否在读时钟驱动下,顺序读出FIFO中对应数据;FIFO空时,是否停止读出;

    3、满判断

    设计能否在写时钟驱动下,同步读指针,并且在适当位置产生满标志;

    3、空判断

    设计能否在读时钟驱动下,同步写指针,并且在适当位置产生空标志;

    RTL电路如下:

    五、参考文献

    Verilog实现FIFO专题(3-同步FIFO设计)

    异步FIFO的设计

    Verilog中always@()语句双边沿触发(语法与综合的差异)

    Verilog实现二进制码与格雷码转换

    亚稳态专题



     

    展开全文
  • 深入理解FIFO(包含有FIFO深度的解释)

    万次阅读 多人点赞 2018-10-16 15:27:48
    深入理解FIFO(包含有FIFO深度的解释)  FIFO:  一、先入先出队列(First Input First Output,FIFO)这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。  1.什么是FIFO?  FIFO是...
  • FIFO库函数

    2017-02-25 09:14:29
    一个用C写的据有可移植性的FIFO库函数。已经验证过正确性
  • 同步FIFO与异步FIFO

    千次阅读 2019-11-28 18:07:36
    FIFO一般用于不同时钟域之间的数据传递,FIFO根据FIFO工作的时钟域,可以分为同步FIFO与异步FIFO:同步FIFO是读时钟与写时钟为同一时钟,在时钟上升沿同时发生读写操作。异步FIFO是读写时钟为不同时钟,读写时钟彼此...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 185,461
精华内容 74,184
关键字:

FIFO