嵌入式FIFO核的调用

本次设计源码下载地址:http://download.csdn.net/detail/noticeable/9915523

课程目标:学习调用quartus II 软件的FIFO(先进先出)IP核,并通过仿真,了解其时序。

实验现象:通过quartus II 调用FIFO IP核,并进行不同形式的配置,通过仿真验证其接口时序。

知识点:FIFO IP核的使用。

    FIFO是什么?有什么用?

    FIFO 即先进先出存储器,是一个在FPGA中使用到的具有先进先出特性的一个存储器,其常被用来作为数据的缓存或者高速异步数据的交互。

    FIFO可分为两种结构:单时钟FIFO(SCFIFO)和双时钟FIFO(DCFIFO),其中双时钟FIFO 又可分为普通时钟(DCFIFO)和混合宽度时钟FIFO(DCFIFO_MIXED_WIDTHS).。

两种结构的时钟的符号图如下所示:

关于各接口的引脚说明可以在上传到压缩包中查看官方文档,这里不再缀述 。

从结构图可以看出

  单时钟FIFO具有一个独立的时钟端口clock,因此所有输入信号的读取都是在clock的上升沿进行的,所有输出信号的变化也是在clock信号的上升沿的控制下进行的1,即单时钟FIFO的所有输入输出信号都是同步于clock信号的。

  在双时钟FIFO结构中,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟wrclk的,所有与读相关的信号都是同步于读时钟rdclk的。在双时钟FIFO的符号图中,位于上部分的为与写相关的所有信号,位于中间部分的为与读相关的所有信号,位于下部的为异步清零信号。

    其中根据两种FIFO 各种的用处不同,使用的地点也不同。‘

单时钟FIFO:

  单时钟FIFO常用于片内数据交互,例如,在FPGA的控制下从外部传感器读取到的一连串传感器数据,首先被写入FIFO中,然后再以UART串口的数据发送速率将数据依次发送出去。由于传感器的单次读取数据可能很快,但并不是时刻都需要采集数据,例如某传感器使用SPI接口的协议,FPGA以2M的SPI数据速率从该传感器中读取20个数据,然后以9600的波特率通过串口发送出去。此过程每秒钟执行一次。因为2M的数据速率远高于串口9600的波特率,因此需要将从传感器中采集到的数据首先用FIFO缓存起来,然后再以串口的数据速率缓慢发送出去。这里,由于传感器数据的读取和串口数据的发送都是可以同步于同一个时钟的,因此可以使用单时钟结构的FIFO来实现此功能。

双时钟FIFO:

    双时钟FIFO的一个典型应用就是异步数据的收发。

    所谓异步数据是指数据的发送端和接收端分别同步与不同的时钟域,使用双时钟FIFO的独立的读写时钟结构,能够将不同时钟域中的数据同步到所需的时钟域系统中。例如,在一个高速数据采集系统中,实现将高速ADC采集的数据通过千兆以太网发送到PC机。ADC的采样时钟(CLK1)由外部专用锁相环芯片产生,则高速ADC采样得到的数据就是同步于该时钟信号的,在FPGA内部,如果FPGA工作时钟(CLK2)是由独立的时钟芯片加片上锁相环产生的,则CLK1和CLK2就是两个不同域的时钟,他们的频率和相位没有必然的联系,假如CLK1为65M,CLK2为125M,那么就不能使用125M的数据来直接采集65M速率的数据,因为两者数据速率不匹配,在采集过程中会出现包括亚稳态问题在内的一系列问题,所以这里就可以使用一个具备双时钟结构的FIFO来进行异步数据的收发。

下图为使用FIFO进行异步数据收发的简易系统框图:

基于千兆以太网传输的高速数据采集(8bit)系统

   在此系统中,由于ADC的数据位宽为8位,基于UDP协议的以太网发送模块所需的数据也是8位,因此使用的是非混合宽度的双时钟FIFO结构。假如CLK1的频率为20M,ADC的数据位宽为16位,则可以使用混合宽度的双时钟FIFO,在实现异步时钟域数据收发的同时,实现数据位宽的转换。通过设置双时钟FIFO的写入位宽为16位,读取位宽为8位,则可以实现将16位的ADC数据转换为以太网支持的8位发送数据,然后通过以太网发送到PC机。

总而言之:FIFO在系统中是作为缓冲器存在的一种状态,其根据缓冲的需求不同选择不同机构的fifo,从而使两个或多个不同采样率的数据可以进行数据的交互。

FIFO的设计方法:

在Altera FPGA中使用FIFO实现用户功能设计主要有三种实现方式,第一种为用户根据需求自己编写FIFO逻辑,当用户对于FIFO的功能有特殊需求时,可以使用此种方式实现,但此种方式要求用户有较高的RTL设计能力。第二种方式为使用第三方提供的开源IP核,此种IP核以源码的形式提供,能够快速的应用到用户系统中,当用户对FIFO功能有特殊需求时,可以在此源码的基础上进行修改,以适应自己的系统需求。第三种方式为使用Quartus II软件提供的免费FIFO IP核,此种方式下,Quartus II软件为用户提供了友好的图形化界面方便用户对FIFO的各种参数和结构进行配置,生成的FIFO IP核针对Altera不同系列的器件,还可以实现结构上的优化。该FIFO IP核也是通过Verilog语言进行描述的,在Quartus II13.0软件中,该IP核源码存放于Quartus II软件安装目录quartus\eda\sim_lib下的altera_mf.v文件中的第48189行(scfifo)(dcfifo结构较多,因此代码内容很多,与之相关的代码有几千行,大家可以在文件中搜索dcfifo即可找到)。由于该FIFO IP核已经提供了几乎我们设计所需的所有功能,因此在系统设计中,推荐使用该FIFO IP核进行系统设计。

设计步骤:

新建porject ,打开megawizard plug-in manager,选择FIFO IP核

之后直接next到finish即可。

下面对于IP核的时序接口进行仿真验证:

编写testbench文件

`timescale 1ns/1ps
`define clock_peride 20
module fifo_tb;
                                                reg              clk;//时钟信号接口
                                                reg            [15:0]  data;//输入数据接口
                                                reg              rdreq;//读请求
                                                reg              sclr;//同步清零
                                                reg              wrreq;//写请求
                                                wire      almost_empty;//将空信号
                                                wire      almost_full;//将满信号
                                                wire      empty;//空信号 
                                                wire      full;//满信号
                                                wire    [15:0]  q;//输出接口
                                                wire    [7:0]  usedw;//可用数据
            fifo                     fifo1 (
                                                    .clock(clk),
                                                    .data(data),
                                                    .rdreq(rdreq),
                                                    .sclr(sclr),
                                                    .wrreq(wrreq),
                                                    .almost_empty(almost_empty),
                                                    .almost_full(almost_full),
                                                    .empty(empty),
                                                    .full(full),
                                                    .q(q),
                                                    .usedw(usedw)
                                                    );

                                    initial clk=1;
                                    always#(`clock_peride/2)  clk=~clk;
                                    integer i;

                                        initial begin
                                        wrreq=0;
                                        rdreq=0;
                                        data=0;
                                        #(`clock_peride*20+1);
                                            for(i=0;i<=255;i=i+1)            //写操作
                                            begin
                                            wrreq=1;
                                            data=i;
                                            #(`clock_peride);
                                            end
                                            wrreq=0;
                                            #(`clock_peride*20);
                                                for(i=0;i<=255;i=i+1)                    //读操作
                                            begin
                                            rdreq=1;
                                            #(`clock_peride);
                                            end
                                            $stop;
                                        end
endmodule

设置仿真文件路径,点击仿真进行前仿,仿真结果如下,可以通过仿真了解各接口的作用及FIFO的读写操作。

下面继续创建一个双时钟FIFO,并进行仿真

之后next 到finish

编写dc_fifo_tb文件对IP核文件进行仿真

`timescale 1ns/1ps
`define wrclock_peride 20
`define rdclock_peride 10
                            module dc_fifo_tb;
                            reg    [15:0]  data; //输入数据
                            reg      rdclk;                //读时钟
                            reg      rdreq;            //读请求
                            reg      wrclk;                //写时钟
                            reg      wrreq;            //写请求
                            wire    [7:0]  q;        //输出开口
                            wire      rdempty;        //读空
                            wire      wrfull;                //写满

                                dc_fifo                dc_fifo1 (
                                                                                .data(data),
                                                                                .rdclk(rdclk),
                                                                                .rdreq(rdreq),
                                                                                .wrclk(wrclk),
                                                                                .wrreq(wrreq),
                                                                                .q(q),
                                                                                .rdempty(rdempty),
                                                                                .wrfull(wrfull)
                                                                                    );

                                                                            initial wrclk = 1;
                                    always #(`wrclock_peride/2) wrclk = ~wrclk;

                                    initial rdclk = 1;
                                    always #(`rdclock_peride/2)  rdclk = ~rdclk;
                                                    integer i;

                                                                initial begin
                                                                data=0;
                                                                rdreq=0;
                                                                wrreq=0;
                                                                #(`wrclock_peride*20+1)
                                                                for (i=0;i <= 255 ;i = i + 1)begin
                                                                    wrreq = 1;
                                                                    data = i + 1024;
                                                                    #`wrclock_peride;
                                                                end
                                                                wrreq = 0;
                                                                #(`rdclock_peride*20);
                                                            for (i=0;i <= 511 ;i = i + 1)begin
                                                                rdreq = 1;
                                                                #(`rdclock_peride);
                                                            end
                                                            rdreq = 0;
                                                            #(`rdclock_peride*20);
                                                            $stop;        

                                                                end
    endmodule

设置仿真路径,并进行仿真,仿真结果如下图

还可以自己观察仿真波形中的接口的变化规律,这里就不再缀述了。

时间: 2024-08-03 00:14:19

嵌入式FIFO核的调用的相关文章

嵌入式ROM核的调用

本次设计的工具和源码在:http://download.csdn.net/detail/noticeable/9914766 课程目的:调用quartus II提供的rom(read only memory)进行系统项目设计 实验现象:将一组固定的波形数据以MIF的格式存储于fpga中使用IP核构建的片上ROM中,开发板上电后,系统从ROM 中读取数据,并将数据通过I/O口输出,使用signal TAP II取I/O口输出的数据,即可得到三角波形:并通过quartus II提供的in syste

三星嵌入式四核4412开发板开源平台,助您快速开发新产品

POP 封装 长宽:5CM * 6CM,高度 1.5MM,320 个引脚(80 * 4):板载 1GB 内存,电源管理: SCP 封装 长宽:6CM * 7CM,高度 1.5MM,320 个引脚(80 * 4): SCP 板载 1G 或者 2G 内存,电源管理: 底板 iTOP-4412 全能版底板如下图所示: 屏幕 屏幕尺寸:9.7寸电容屏 分辨率:1024*768 核心板参数 尺寸:6cm*7cm 高度:连同连接器在内0.26cm CPU:Exynos4412,四核Cortex-A9,主频为

C语言与汇编的嵌入式编程:汇编调用函数(两数交换)

编写一个两数交换函数swap,具体代码如下: #include<stdio.h> void swap(int *a,int *b) { int temp; temp = *a; *a = *b; *b= temp; //printf("a=%d,b=%d,temp=%d\n",a,b,temp); } void main(){ int a=0; int b=0; char *str1="a=%d,b=%d\n"; printf("++++++\

FPGA设计经验谈

从大学时代第一次接触FPGA至今已有10多年的时间.至今记得当初第一次在EDA实验平台上完成数字秒表,抢答器,密码锁等实验时,那个兴奋劲.当时由于没有接触到HDL硬件描述语言,设计都是在MAX+plus II原理图环境下用74系列逻辑器件搭建起来的.后来读研究生,工作陆陆续续也用过Quartus II,Foundation,ISE,Libero,并且学习了verilogHDL语言,学习的过程中也慢慢体会到verilog的妙用,原来一小段语言就能完成复杂的原理图设计,而且语言的移植性可操作性比原理

FIFO管道探索历程

刚开始代码的实现如下: void CreateFIFO(){    if((mkfifo(FIFOPATH,O_CREAT|O_EXCL|O_RDWR)<0)&&(errno!=EEXIST))    {        printf(strerror(errno));    }   } int OpenFIFO(){    fd=open(FIFOPATH,O_RDWR|O_NONBLOCK);    return fd;} 觉得非常的不优雅,毕竟需要调用两个函数,而且写函数又有一个

【UNIX网络编程】FIFO

管道作为进程间通信的最古老方式,它的缺点是没有名字,因此只能用在有亲缘关系的父子进程之间.对于无亲缘关系的进程间,无法用管道进行通信.FIFO可以完成无亲缘关系的进程间的通信,FIFO也被称为命名管道.它是一种特殊类型的文件,在文件系统中以文件名的形式存在,但它的行为却和上面提到的管道类似. 创建命名管道有两种方法: 1.在命令行上执行命令:mkfifo filename 来创建FIFO. 2.使用mkfifo函数创建FIFO. #include <sys/stat.h> #include &

进程间通信(一)——管道和FIFO

1. 概述 管道没有名字,适用于有亲缘关系的进程间. FIFO指first in first out,有一个路径名与之关联,从而允许无亲缘关系的进程间使用.亦称:命名管道named pipe. 两者都是单向数据流(半双工管道),具有随进程的持续性,数据都是先进先出,在进程间通信不需要某种形式的同步. 2.管道 #include <unistd.h> int pipe(int fd[2]): 返回:成功0,出错-1: 返回两个文件描述符:fd[0]打开来读,fd[1]打开来写. 典型使用: (1

进程间通信(5) - 命名管道(FIFO)

1. 前言 本篇文章的所有例子,基于RHEL6.5平台.前一篇文章介绍了匿名管道.点此链接. 2.介绍 管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(named pipe或FIFO)提出后,该限制得到了克服.FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中.这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,

细说linux IPC(六):pipe和FIFO

在unix系统上最早的IPC形式为管道,管道的创建使用pipe函数: #include <unistd.h> int pipe(int pipefd[2]); 该函数创建一个单向的管道,返回两个描述符 pipefd[0],和pipefd[1],pipefd[0]用于读操作,pipefd[1]用于写操作.该函数一般应用在父子进程(有亲缘关系的进 程)之间的通信,先是一个进程创建管道,再fork出一个子进程,然后父子进程可以通过管道来实现通信.管道具有以下特点:管道是半双工的,数据只能向一个方向流