IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 二、20【FPGA】FPGA开发中常用的IP核——PLL/ROM/RAM/FIFO -> 正文阅读

[网络协议]二、20【FPGA】FPGA开发中常用的IP核——PLL/ROM/RAM/FIFO

前言

学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。

学习视频:是根据野火FPGA视频教程——第二十三讲? 到 第二十七讲
https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3

这里进行常用的IP核简单的介绍,如果深度学习推荐数据:

《Xilinx系列FPGA芯片 IP核详解》

理论应用

????????IP(Intellectual Property)即知识产权。美国 Dataquest 咨询公司将半导体产业的 IP 定义为“用于 ASIC FPGA 中的预先设计好的电路功能模块”。简而言之,这里的 IP 即电路功能模块。IP 核在数字电路中常用于比较复杂的功能模块(如 FIFORAMFIR 滤波器、SDRAM 控制器、PCIE 接口等)设计成参数可修改的模块,让其他用户可以直接调用这些模块。随着设计规模增大,复杂度提高,使用 IP 核可以提高开发效率,减少设计和调试时间,加速开发进程,降低开发成本,是业界的发展趋势。利用 IP 核设计电子系统,引用方便,修改基本元件的功能容易。具有复杂功能和商业价值的 IP 核一般具有知识产权,尽管 IP 核的市场活动还不规范,但是仍有许多集成电路设计公司从事 IP 核的设计、开发和营销工作。

分类依据:产品交付方式

  • HDL语言形式?---?软核

??????硬件描述语言;可进行参数调整、复用性强;布局、布线灵活;设计周期短、设计投入少

  • 网表形式?---?固核

?????????完成了综合的功能块;可预布线特定信号或分配特定的布线资源

  • 版图形式?---?硬核
    ??????????硬核是完成提供设计的最终阶段产品—掩膜(Mask);
    缺乏灵活性、可移植性差;更易于实现IP核的保护。 ? ?? ?

缺点:

?IP核往往不能跨平台使用
IP核不透明,看不到内部核心代码
定制IP需额外收费????

IP 核生成工具提供的 IP 核主要有以下几类:

1数学运算模块,包括累加器、乘加器、乘累加器、计数器、加/减法器、实/复数乘法器、除法器、CORDIC 算法器、DSP48 宏和浮点数操作器。

2存储器构造模块,包括块存储器和分布式存储器、先入先出存储器(FIFO)和移位寄存器。

3DSP 功能,包括直接数字频率合成(DDS)编译器、数字上变频/下变频(DUC/DDC)编译器、有限冲激响应(FIR)滤波器、级联积分梳状(CIC)滤波器、离散傅里叶变换(DFT)和快速傅里叶变换(FFT)。

4信道纠错码,包括 RS 码编码器和译码器、卷积码编码器、Viterbi 译码器、Turbo码编/译码器和低密度奇偶校验码(LDPC)编码器等。

5网络应用,包括媒体访问控制器(MAC)、以太网物理编码子层/物理介质连接(PCS/PMA)、网络负载统计、以太网拓展连接单元接口(XAUI)、减少引脚使用的XAUI(RXAUI)、MAC 封装包和音/视频桥接(AVB)端点。

6FPGA 结构属性,包括时钟向导、高速串行收发器(GTX/GTP)和系统监视向导。

7连接器,包括标准总线接口(如 PCI/PCI-XPCI ExpressCAN)和数据接口(如以太网、RapidIO 等)。

8调试和验证,包括逻辑调试内核(集成控制器核(ICON)、集成逻辑分析核(ILA)、虚拟输入/输出核(VIO)、Agilent 跟踪核(ATC2)、误比特率测试核(IBERT)和集成总线分析核(IBA)。

9针对不同设计方法的特殊IP核,包括用工程导航工具进行逻辑设计的IP核、用Xilinx系统生成工具进行DSP算法设计的IP核,以及用Xilinx平台开发环境(XPS)或 PlanAhead进行嵌入式设计的IP核。

本章将重点介绍几个常用的 IP,如锁相环(PLL)FIFORAMROM 等,详细说明各 IP 核的功能以及其使用方法,通过使用这些简单的 IP 核来掌握所有 IP 核的基本使用方法,起到抛砖引玉的效果。

实战演练

?一、PLL(锁相环)IP核? ?

1.1 PLL简介

????????PLL(Phase Locked LoopP,即锁相环)是最常用的 IP 核之一,其性能强大,可以对输入到 FPGA 的时钟信号进行任意分频、倍频、相位调整、占空比调整,从而输出一个期望时钟,实际上,即使不想改变输入到 FPGA 时钟的任何参数,也常常会使用 PLL,因为经过 PLL 后的时钟在抖动(Jitter)方面的性能更好一些。Xilinx 中的 PLL 是模拟锁相环,和数字锁相环不同的是模拟锁相环的优点是输出的稳定度高、相位连续可调、延时连续可调;缺点是当温度过高或者电磁辐射过强时会失锁(普通环境下不考虑该问题)。

1.2 基本工作原理

反馈系统,ref_clk:参考时钟,FD/PD:鉴频/相器。

1、参考时钟(ref_clk)通过鉴频(FD)鉴相器(PD)和需要比较的时钟频率进行比较,我们以频率调整为例,如果参考时钟频率等于需要比较的时钟频率则鉴频鉴相器输出为 0,如果参考时钟频率大于需要比较的时钟频率则鉴频鉴相器输出一个变大的成正比的值,如果参考时钟频率小于需要比较的时钟频率则鉴频鉴相器输出一个变小的正比的值。

2、鉴频鉴相器的输出连接到环路滤波器(LF)上,用于控制噪声的带宽,滤掉高频噪声,使之稳定在一个值,起到将带有噪声的波形变平滑的作用。如果鉴频鉴相器之前的波形抖动比较大,经过环路滤波器后抖动就会变小,趋近于信号的平均值。

3、经过环路滤波器的输出连接到压控振荡器(VCO)上,环路滤波器输出的电压可以控制 VCO 输出频率的大小,环路滤波器输出的电压越大 VCO 输出的频率越高,然后将这个频率信号连接到鉴频鉴相器作为需要比较的频率。

  • 倍频实现

DIV(分频器),如果ref_clk为50MHz,假设DIV为1/2分频器,则最终分频输出为50MHz,那么之前的pll_out则为100MHz,实现了倍频的功能。

  • 分频器实现?

如果ref_clk为50MHz,假设DIV为1/5分频器,则最终DIV输出为10MHz,经过基本PLL结构,pll_out输出也为10MHz。

1.3 PLL IP核配置

????????建立一个PLL工程,在工程中找到IP核目录,并搜索时钟clk,找到时钟IP核选项。

双击打开时钟IP核,配置界面

1、框中我们输入 IP 核的命名,后面实例化 IP 核的时候都是使用的该名字,这里所取的名字最好是和该 IP 核相关,因为本节我们主要讲解 PLL,所以给该 IP 核取名为 pll_ip。

2、?框中我们选中 PLL。

3 框中是输入 IP 核的输入时钟,由于我们开发板上的时钟晶振为 50MHz,所以这里我们输入 50。

设置完之后切换到“Output Clocks”页面。

1、框中是对输出时钟频率、相位以及占空比的设置。为了让大家能够看到 PLL 核每种参数的设置的效果,我们生成四个输出时钟,输出的设置分别为输入时钟的 2 倍频、即100MHz;输入时钟的 2 分频,即 25MHz;输入时钟像移 90°;输入时钟占空比为 20%。

2、框中是对复位管脚以及锁定管脚的设置。其中 reset 为复位管脚,用来对 PLL IP 进行复位,locked 为锁定管脚,用来检测 PLL IP 核是否已经锁定,当输出时钟稳定时该信号会输出高电平表示输出稳定。对于一般的应用而言,可以不用添加这两个管脚,这里我们只添加上“locked”生成锁定管脚,以便在仿真时能够体现 PLL 的工作特点。

设置完之后切换到“Port Renaming”页面。

?

  • ?如图 25-10 所示,在“Summary”页面,从“IP Symbol”窗口可以看到我们最终创建
    PLL 核的端口信号。点击“ OK ”完成 PLL 的创建设置。

?生成IP核

  • 添加已经有的 IP核

首先使用的芯片、工具及版本相同,这里为了演示,先把上面建立好的IP核从Vivado中移除,然后进行添加演示。

打开IP核文件夹,添加IP核?

1.4 PLL-IP核调用

实例化模板:从1、2文件中均可以找到实例化模板。

  • 调用IP核代码
module pll(
    input wire sys_clk,
    output wire clk_mul_2,
    output wire clk_div_2,
    output wire clk_phase_90,
    output wire clk_ducle_20,
    output wire locked
    );
//PLL IP核的调用     
pll_ip  pll_ip_inst
   (
    // Clock out ports
    .clk_100m(clk_mul_2),     // output clk_100m
    .clk_25m(clk_div_2),     // output clk_25m
    .clk_p_90(clk_phase_90),     // output clk_p_90
    .clk_d_20(clk_ducle_20),     // output clk_d_20
    // Status and control signals
    .locked(locked),       // output locked
   // Clock in ports
    .pll_clk(sys_clk));      // input pll_clk      
endmodule
  • ?仿真代码
`timescale 1ns / 1ns
//
// Company: 追逐者-桥的小作坊
// Create Date: 2022/05/26 17:50:56
// Design Name: PLL IP核
// Module Name: tb_pll
module tb_pll();
    reg  sys_clk;
    wire clk_mul_2;
    wire clk_div_2;
    wire clk_phase_90;
    wire clk_ducle_20;
    wire locked;
    initial sys_clk = 1'b1;
    always #10 sys_clk = ~sys_clk;

pll pll_inst(
    . sys_clk     (sys_clk     ),
    . clk_mul_2   (clk_mul_2   ),
    . clk_div_2   (clk_div_2   ),
    . clk_phase_90(clk_phase_90),
    . clk_ducle_20(clk_ducle_20),
    . locked      (locked      )
    );
endmodule
  • ?仿真波形图

二、ROM? IP核? ??

2.1 ROM简介

?ROM 是只读存储器(Read-Only Memory)的简称,是一种只能读出事先所存数据的固态半导体存储器。其特性是一旦储存资料就无法再将之改变或删除,且资料不会因为电源关闭而消失。而事 实上在 FPGA 中通过 IP 核生成的 ROM RAMRAM 将在下一节为大家讲解)调用的都是FPGA 内部的 RAM 资源,掉电内容都会丢失(这也很容易解释,FPGA 芯片内部本来就没有掉电非易失存储器单元)。用 IP 核生成的 ROM 模块只是提前添加了数据文件(.coe 格式)(.mf/.nex格式),在 FPGA 运行时通过数据文件给 ROM 模块初始化,才使得 ROM 模块像个“真正”的掉电非易失存储器;也正是这个原因,ROM 模块的内容必须提前在数据文件中写死,无法在电路中修改

最简单的使用有效时钟CLKA、有效地址ADDRA和有效使能EA,就可以输出DOUTA

2.2 创建ROM初始化文件

ROM 作为只读存储器,在进行 IP 核设置时需要指定初始化文件,即写入存储器中的数据,数据要以规定的格式才能正确写入 ROM,这种格式就是 coe 文件。coe Vivado 规定的一种文件格式,文件格式示意图,具体见图 25-24

?比较复杂时可以使用python和matlab自动生成文件。

2.3 ROM IP核配置

打开IP核目录并搜索找到ROM IP核:RAM & ROM 核“Block Memory Generator”双击打开配置。

  • ?Basic界面

1、框中我们输入 IP 核的命名。

2、类型,普通和接口形式。

3、框中选择存储器类型,可供选择的类型有:Single Port RAM(单端口 RAM)、Simple Dual Port RAM(简单双口 RAM)、True Dual Port RAM(真双口 RAM)、Singl Port ROM(单端口 ROM)、Doul Port ROM(双端口 ROM)。这里我们选择“Single Port Rom”单端口 ROM。

4、框在 Algorithm 一栏中可选择用于实现内存的算法,其中 Minimum Area 为最小面积算法;Low Power 为低功耗算法;Fixed Primitives 为固定单元算法。这里我们按默认选择Minimum Area 即可。

5、校验选项,只有RAM中才会使用到。

6、设置使能位数,也是只有在RAM中才会用到。

  • 设置完之后切换到“Potr A Options”页面。 ?

  • 设置完之后切换到“Other Options”页面。

框中选项是加载数据文件,即我们前面讲到的 ROM 初始化文件,由于 ROM 是只读存储器,所以我们必须添加 ROM 初始化文件才行。勾选上“Load Init File”点击 Browse 进行添加.coe 初始化文件,文件格式我们之前已经讲解,大家可根据自己想存入的数据进行生成该文件。该页面其余按默认设置即可。

  • 设置完之后切换到“Summary”页面。

在“Summary”页面,从“IP Symbol”窗口可以看到我们最终创建的 ROM 核的端口信号。点击“OK”完成 ROM 的创建设置。

  • ?将编好的coe文件复制到IP核文件中

  • 在IP核中添加该初始化.coe文件?

  • 双端口ROM配置

该窗口是对端口 B 的设置,这也是双端口 ROM 与单端口 ROM 最大的区别,多了一组端口。这里我们只需对 B 端口的数据位宽设置即可,数据深度会根据端口 A 的设置自动设置。例如我们端口 A 设置的数据位宽为 8bit,深度为 256;而我们 B 端口设置的数据位宽为 16bit,则其深度即为 128,其数据总量是一样的。其余设置与端口 A 设置一样即可。

2.4 ROM IP核调用

与之前的PLL类似调用类似,打开IP Sources找到.veo文件,内有实例化模型之间复制

  • 程序代码
module rom(
    input wire sys_clk,
    input wire [7:0] addra,
    output wire [7:0] douta   
    );              
rom_8x256 rom_ip (
  .clka(sys_clk),    // input wire clka
  .addra(addra),  // input wire [7 : 0] addra
  .douta(douta)  // output wire [7 : 0] douta
    );
endmodule
  • 仿真代码
`timescale 1ns / 1ns
//
// Company: 追逐者-桥的小作坊
// Create Date: 2022/05/27 15:05:26
// Design Name: ROM IP核
// Module Name: tb_rom_ip
//
module tb_rom_ip();
    reg sys_clk;
    reg sys_rst_n;
    reg [7:0] addra;
    wire [7:0] douta;    //将数据引出因此设置为网线型   
    initial begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
    end 
    always #10 sys_clk = ~ sys_clk;
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            addra <= 8'd0;
        else if(addra == 8'd255)
            addra <= 8'd0;
        else 
            addra <= addra + 1'b1;
rom rom_inst(
    .sys_clk(sys_clk),
    .addra  (addra  ),
    .douta  (douta  ) 
    );
endmodule
  • 仿真后的波形图

有波形图可以看出可以通过输入相应的地址返回相应的数据,但是两者的时钟差2个时钟

下图框中是否在输出增加一个寄存器,如果加入寄存器会在增加一个时钟周期,不增加仅滞后一个时钟周期。

?将其取消选取时,可以看出只延迟了1个时钟周期:

三、RAM? IP核? ?

3.1 RAM IP核简介

???????????????RAM 是随机存取存储器(Random Access Memory)的简称,是一个易失性存储器。 RAM 工作时可以随时从任何一个指定的地址写入或读出数据,同时我们还能修改其存储的数据,即写入新的数据,这是 ROM 所并不具备的功能。在 FPGA 中这也是其与 ROM 的最大区别。ROM 是只读存储器,而 RAM 是可写可读存储器,在我们 FPGA 中使用这两个存储器主要也是要区分这一点,因为这两个存储器使用的都是我们 FPGA 内部的 RAM 资源,不同的是 ROM 是只用到了 RAM 资源的读数据端口。

  • ?对于单端口 RAM,读写操作共用一组地址线,读写操作不能同时进行;

  • 对于简单双端口 RAM,读操作和写操作有专用地址端口(一个读端口和一个写端口),即写端口只能写不能读,而读端口只能读不能写;?

  • 对于真正双端口 RAM,有两个地址端口用于读写操作(两个读/写端口),即两个端口都可以进行读写。?

3.2 RAM IP核的配置

  • 单端口配置?

?操作方式:

Write First:读写在同一始终下,先执行写操作,读出的是写入的值。

Read First:读写在同一始终下,先执行读操作,读出的是之前写入的值。

NO:读和写不能在同一始终下进行

?后面两栏基本不用配置,直接生成IP核即可。

  • 简单双端楼RAM IP核

  • ?真正双端口IP核配置

3.3?RAM IP核的调用

module ram(
    input wire sys_clk,
    input wire [7:0] addra,
    input wire [7:0] dina,
    input wire  wea,      //写使能
    output wire [7:0] douta
    );   
s_ram_gen_8x256 s_ram_gen_8x256_inst (
  .clka(sys_clk),    // input wire clka
  .wea(wea),      // input wire [0 : 0] wea
  .addra(addra),  // input wire [7 : 0] addra
  .dina(dina),    // input wire [7 : 0] dina
  .douta(douta)  // output wire [7 : 0] douta
);
endmodule

四、FIFO? IP核?

4.1 FIFO IP核简介

????????FIFO(First In First Out,即先入先出),是一种数据缓冲器,用来实现数据先入先出的读写方式。与 ROM RAM 的按地址读写方式不同,FIFO 的读写遵循“先进先出”的原则,即数据按顺序写入 FIFO,先被写入的数据同样在读取的时候先被读出,所以 FIFO存储器没有地址线。FIFO 有一个写端口和一个读端口外部无需使用者控制地址,使用方便。

????????FIFO 存储器主要是作为缓存,应用在同步时钟系统和异步时钟系统中,在很多的设计中都会使用,后面实例中如:多比特数据做跨时钟域的转换前后带宽不同步等都用到了FIFOFIFO 根据读写时钟是否相同,分为 SCFIFO(同步 FIFO)和 DCFIFO(异步FIFO),SCFIFO 的读写为同一时钟,应用在同步时钟系统中;DCFIFO 的读写时钟不同,应用在异步时钟系统中。

数据的产生模块与数据使用模块不对应时就会使用到FIFO,如两者的时钟频率不同无法在同一时钟下进行传输(多比特数据做跨时钟域的转换) ;两者的数据带宽不同下传输(前后带宽不同步)。

4.2 FIFO IP核配置

  • scfifo IP核

在IP Catalog中搜索FIFO,并选择fifo核“FIFO Generator”,双击打开

?

?

  • ?dcfifo IP核

?

4.3?FIFO IP核调用

  • scfifo IP核

???????????????RTL 代码顶层的输入信号有:50MHz 的写时钟 wr_clk、输入 256 8bit 的数据 pi_data(值为十进制 0~255)和伴随该输入数据有效的标志信号 pi_flag25MHz 的读时钟rd_clkFIFO 的写请求信号 rd_en。这些输入信号需要在 Testbench 中产生激励。

????????RTL 代码顶层的输出信号有:FIFO 空标志信号 emptyFIFO 满标志信号 full、同步于wr_clk 指示 FIFO 中存在数据个数的信号 wr_data_count、从 FIFO 中读取的数据 po_data、同步于 rd_clk 指示 FIFO 中存在数据个数的信号 rd_data_count。这些信号也是我们需要通过仿真 DCFIFO IP 核主要观察的信号,这些信号通过 Testbench 中给输入信号激励后产生输出。

  • 程序代码
module scfifo(
    input wire sys_clk,
    input wire sys_rst_n,
    input wire [7:0] pi_data,     //写数据
    input wire pi_flag,           //写请求信号
    input wire rd_en,            //FIFO读请求信号
    
    output wire [7:0] po_data,    //读数据
    output wire full,      //FIFO满标志,高有效
    output wire empty,     //FIFO空标志,高有效
    output wire [7:0] data_count   //FIFO中存在的数据个数   
    );
    
scfifo_8x256 scfifo_8x256_inst (
  .clk(sys_clk),                // input wire clk
  .srst(~sys_rst_n),              // input wire srst
  .din(pi_data),                // input wire [7 : 0] din
  .wr_en(pi_flag),            // input wire wr_en
  .rd_en(rd_en),            // input wire rd_en
  .dout(po_data),              // output wire [7 : 0] dout
  .full(full),              // output wire full
  .empty(empty),            // output wire empty
  .data_count(data_count)  // output wire [7 : 0] data_count
);        
endmodule
  • 仿真代码
`timescale 1ns / 1ns
module tb_scfifo();
    reg sys_clk;
    reg sys_rst_n;
    reg [7:0] pi_data;
    reg pi_flag;
    reg rd_en;
    reg [1:0] cnt_baud;
    
    wire [7:0] po_data;
    wire full;
    wire empty;
    wire [7:0] data_count;
    initial begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20 
        sys_rst_n <= 1'b1;
    end 
    always #10 sys_clk = ~sys_clk;
//计数器,产生数据间的间隔
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_baud <= 2'b0;
        else if(&cnt_baud == 1'b1)
            cnt_baud <= 2'b0;
        else 
            cnt_baud <= cnt_baud + 1'b1;
//pi_flag:输入标志信号
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            pi_flag <= 1'b0;
        else if((cnt_baud == 2'd0) && (rd_en == 1'b0))
            pi_flag <= 1'b1;
        else
            pi_flag <= 1'b0;
//pi_data:数据写入
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            pi_data <= 8'b0;
        else if((pi_data == 8'd255) && (pi_flag == 1'b1))
            pi_data <= 8'b0;
        else if(pi_flag == 1'b1)
            pi_data <= pi_data + 1'b1;
//rd_en:读请求信号
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            rd_en <= 1'b0;
        else if(full == 1'b1)
            rd_en <= 1'b1;
        else if(empty == 1'b1)
            rd_en <= 1'b0;
            
scfifo scfifo_inst(
    .sys_clk   (sys_clk   ),
    .sys_rst_n (sys_rst_n ),
    .pi_data   (pi_data   ),     //写数据
    .pi_flag   (pi_flag   ),           //写请求信号
    .rd_en     (rd_en     ),            //FIFO读请求信号
              
    .po_data   (po_data   ),    //读数据
    .full      (full      ),      //FIFO满标志,高有效
    .empty     (empty     ),     //FIFO空标志,高有效
    .data_count(data_count)   //FIFO中存在的数据个数   
    );


endmodule
  • ?仿真波形

虽然使用的是同步时钟,但是写的时钟间隔做了改变,而读的时钟依然使用的是系统时钟。

  • dcfifo IP核

????????RTL 代码顶层的输入信号有:50MHz 的写时钟 wr_clk、输入 256 8bit 的数据 pi_data(值为十进制 0~255)和伴随该输入数据有效的标志信号 pi_flag25MHz 的读时钟 rd_clkFIFO 的写请求信号 rd_en。这些输入信号需要在 Testbench 中产生激励。

????????RTL 代码顶层的输出信号有:FIFO 空标志信号 emptyFIFO 满标志信号 full、同步于wr_clk 指示 FIFO 中存在数据个数的信号 wr_data_count、从 FIFO 中读取的数据 po_data、同步于 rd_clk 指示 FIFO 中存在数据个数的信号 rd_data_count。这些信号也是我们需要通过仿真 DCFIFO IP 核主要观察的信号,这些信号通过 Testbench 中给输入信号激励后产生输出。

  • 程序代码
module dcfifo(
    input wire wr_clk, rd_clk,
    input wire [7:0] din,
    input wire wr_en, rd_en,
    
    output wire full, empty,
    output wire [15:0] dout,
    output wire [6:0] rd_data_count,
    output wire [7:0] wr_data_count          
    );
    
    
dcfifo_gen dcfifo_gen_inst (
  .wr_clk(wr_clk),                // input wire wr_clk
  .rd_clk(rd_clk),                // input wire rd_clk
  .din(din),                      // input wire [7 : 0] din
  .wr_en(wr_en),                  // input wire wr_en
  .rd_en(rd_en),                  // input wire rd_en
  .dout(dout),                    // output wire [15 : 0] dout
  .full(full),                    // output wire full
  .empty(empty),                  // output wire empty
  .rd_data_count(rd_data_count),  // output wire [6 : 0] rd_data_count
  .wr_data_count(wr_data_count)  // output wire [7 : 0] wr_data_count
);      
endmodule
  • 仿真代码
`timescale 1ns / 1ns

module tb_dcfifo();
    reg     wr_clk;
    reg     rd_clk;
    reg [7:0] din;    
    reg     wr_en;
    reg     rd_en;
    reg     sys_rst_n;     
    reg [1:0] cnt_baud;    
    reg     full_reg0;
    reg     full_reg1;
    
    wire  empty;
    wire  full;
    wire [15:0] dout;
    wire [6:0] rd_data_count;
    wire [7:0] wr_data_count;
    
    initial begin
        wr_clk = 1'b1;
        rd_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #100;
        sys_rst_n <= 1'b1;
        #100000
        sys_rst_n <= 1'b0;
    end 
    
    always #10 wr_clk = ~wr_clk;
    always #20 rd_clk = ~rd_clk;
    
//计数器,产生数据间的间隔
    always@(posedge wr_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_baud <= 2'b0;
        else if(&cnt_baud == 1'b1)
            cnt_baud <= 2'b0;
        else 
            cnt_baud <= cnt_baud + 1'b1;
//wr_en:输入标志信号
    always@(posedge wr_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            wr_en <= 1'b0;
        else if((cnt_baud == 2'd0) && (rd_en == 1'b0))
            wr_en <= 1'b1;
        else
            wr_en <= 1'b0;            
//din:数据写入
    always@(posedge wr_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            din <= 8'b0;
        else if((din == 8'd255) && (wr_en == 1'b1))
            din <= 8'b0;
        else if(wr_en == 1'b1)
            din <= din + 1'b1;            
//将同步于rd_clk时钟的写满标志信号full在rd_clk时钟下打两拍
    always@(posedge rd_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            begin
                full_reg0 <= 1'b0;
                full_reg1 <= 1'b0;
            end 
        else 
            begin
                full_reg0 <= full;
                full_reg1 <= full_reg0;
            end 
//rd_en:读请求信号
    always@(posedge rd_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            rd_en <= 1'b0;
        else if(full_reg1 == 1'b1)
            rd_en <= 1'b1;
        else if(empty == 1'b1)
            rd_en <= 1'b0;            
    
dcfifo dcfifo_inst(
    .wr_clk       (wr_clk ), 
    .rd_clk       (rd_clk ),
    .din          (din    ),
    .wr_en        (wr_en  ), 
    .rd_en        (rd_en  ),
                 
    .full         (full         ), 
    .empty        (empty        ),
    .dout         (dout         ),
    .rd_data_count(rd_data_count),
    .wr_data_count(wr_data_count)          
    );    
    
endmodule
  • ?仿真波形图

五、总结

SCFIFO DCFIFO 我们都讲解过了,还需要大家注意几个问题:
1 、在单位时间内,写数据的总带宽一定要等于读数据的总带宽,否则一定会存在写满
或读空的现象;
2 、控制好、利用好 FIFO 的关键信号,如读写时钟、读写使能、空满标志信号;
3 、根据实际的项目需求还要考虑需要多大的 FIFO ,大了会浪费资源,小了则达不到需求。
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-06-01 15:28:43  更:2022-06-01 15:28:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:48:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码