IP核的使用之ROM(Vivado)

存储类IP核——ROM

一.引言

在理论给大家介绍什么是ROM之前,先设想这么一个实际场景:现在有某芯片,它拥有500个寄存器,且需要在上电的时候由FPGA向这些寄存器中写入初始值,假设初始值已经通过相应的文档给出了具体值,这些值都是已知的。
这类实际需求有哪些特点呢?
1.数据量比较多
2.数据值都是已知的
3.数据内容不需要换
我们可以用什么办法来解决这个问题呢?
我们需要用一个存储器将这些数据先起来,使用的时候读取存储器就可以了。这个存储器只需要支持读功能就可以了。这就是ROM(read only memory)
实际应用有哪些呢?
1.DDS信号发生器(固定波形数据的形式)
2.对应CMOS摄像头初始化的应用

二.ROM IP核及相关内容扫盲

1.ROM简介

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

2.ROM的初始化文件介绍

ROM 作为只读存储器,在进行 IP 核设置时需要指定初始化文件,即写入存储器中的数据,数据要以规定的格式才能正确写入 ROM,这种格式就是 coe 文件.coe 是 Vivado 规定的一种文件格式,如下图所示
在这里插入图片描述
如上图所示,该文件的格式较为简单,**第一行是定义数据的格式,其中 16 表示数据格式为 16 进制,**也可将数据格式定义为二进制和八进制,只需将 16 改为 2 或 8 即可。其中第 3 到第 18 行是 16*8bit 大小 ROM 的初始化数据。
上面用到了两个关键字,memory_initialization_radix 和 memory_initialization_vector。其中,memory_initialization_radix 是 ROM 初始化数值进制设置,对应后面 Value 值可设置 2,10,16,分别表示 2 进制,10 进制,16 进制。memory_initialization_vector 是 ROM 初始化数据向量,对应后面 Value 值就是填写 ROM 初始化的数据,数据与数据之间通过空格或逗号隔开,数据个数不允许超过设置 ROM 的深度(可以少于设置的深度),数据不允许为负数。
下图为在第三部分自己编写coe文件时的界面:
在这里插入图片描述
注:Key 和 Value 里面填写内容不区分大小写。

3.分布式ROM和块ROM简介

我们先新建 Vivado 工程,单击 IP Catalog,在右边窗口 Search 位置输入 rom,在 Memories &Storage Elements 下可以看到有两个与 ROM 相关的 IP,一个是 Distributed Memory Generator(分布式ROM),另一个是 Block Memory Generator(块ROM)。
在这里插入图片描述
先简单说说两个的差别,两者最主要的差别是生成的 Core所占用的 FPGA 资源不一样,从Distributed Memory Generator 生成的 ROM/RAM Core 占用的资源是 LUT(查找表,查找表本质就是一个小的 RAM);从 Block Memory Generator 生成的 ROM/RAM Core 占用的资源是 Block Memory(嵌入式的硬件 RAM),有关两者详细的区别和使用下面会进行讲解。

4.单端口ROM和双端口ROM简介

对于单端口 ROM 提供一个读地址端口和一个读数据端口,只能进行读操作;双端口 ROM 与单端口 ROM 类似,区别是其提供两个读地址端口和两个读数据端口,基本上可以看做两个单口 RAM 拼接而成。下面是 ROM 不同配置模式存储器的接口信号图
单端口ROM接口信号双端口ROM接口信号
上图中的接口信号我们并不是全部需要用到,因为我们配置时,有些信号是可以不创建的。所以很多接口信号我们可以不用管,只需管我们需要用的信号即可,而**什么信号是我们需要用到的呢?**这在我们调用完 IP 核后,是可以生成其例化模块的,到时候就可以看到我们需要控制的信号了。

三.分布式ROM IP核的创建

Distributed Memory Generator(分布式ROM) 是使用 LUT 资源创建的内存结构。主要可以用于创建以下内存类型:
(1)Distributed ROM
(2)Distributed Single-Port RAM
(3)Distributed Dual-Port RAM
(4)Distributed Simple Dual-Port RAM
使用Distributed Memory Generator 去生成的 ROM IP 是基于 LUT 的分布式 ROM 资源来创建深度为 16,位宽为 1bit 的 ROM,并通过生成一个基于结构的总线多路复用器来创建一个更深、更宽ROM。在 ROM 生成时,通过 vivado 加载一个内存初始化文件(COE 文件)来初始化该 ROM,IP 生成完后,ROM 存储的数据就固定了。分布式 ROM 原理图示意图和其实现框图如下。
在这里插入图片描述
在 IP Catalog 窗口选择 Distributed Memory Generator 并双击,进入IP配置界面,如下:
在这里插入图片描述
(1)Documentation:IP 相关文档入口
(2)IP Location:生成 IP 的存放路径,可以通过点击 … 设置更换存放路径,默认是存放在工程路径下的 bin_counter.srcs\ sources_1\ ip,我这里就保持默认。
(3)Switch to Default:点击后所有的设置恢复到默认值
(4)Component Name:设置生成 IP Core 的名称,我这里将名字设置为 dist_mem_rom_ip;
(5)Depth:设置 ROM 深度,我这里设置 256;
(6)Data Width:设置数据位宽,我这里设置 8;
(7)Memory Type:选择存储类型,这里选择 ROM
端口配置
(8)Input Options:设置是否对输入的地址增加一级寄存器,这里设置选择 Registered,对输入的地址增加一级寄存器(也可以选择 Non Registered,不增加寄存器)。
(9)Output Option:设置是否对输出的地址增加一级寄存器,这里设置选择 Both,对输出数据信号 spo 和增加一级寄存器后的输出数据信号 qspo 都从端口引出。下面还有个 SinglePort Output CE 可选配,当勾选后,ROM Core 就会多出一个 qspo_ce 的端口,这个信号是用来控制使能输出寄存器的时钟。这里没有用到这个功能,不勾选该项。
在这里插入图片描述
(10)Pipeline Stages:流水线级数,在生成 ROM 模式下,不可配。
在这里插入图片描述
(11)添加 COE 文件位置,可以通过选择路径添加已有的 COE 文件,软件会通过 COE 文件生成 ROM 初始化文件 MIF 文件。关于如何创建 COE 文件看下一步骤。
(12) 编辑按钮,当(11)中未加载 COE 文件时,点击这里的按钮,会弹出提示框,点击YES 可进入到创建 COE 文件的流程,这里点击 YES。我这里是用的小梅哥生产的软件Mif精灵,可在小梅哥论坛下载。
在这里插入图片描述
之后保存到工程的根目录下,按照(11)操作就可以了。
(13)COE Options:设置 ROM 中初始化数据没有覆盖的区域的数值,比如 ROM 深度 256,添加的 COE 文件中仅初始化了 200 个数据,这里就是统一设置剩下的 56 个数据的数值。
(14)Reset Options:添加输出寄存器复位信号,可添加同步复位或异步复位,这里就不勾选添加复位信号。
(15)ce overrides 设置
CE Overrides Sync Controls:在选择了输出寄存器同步复位和时钟使能才能启用,启用后,同步控制由时钟使能限定。
Sync Controls Overrides CE:在选择了输出寄存器同步复位和时钟使能才能启用,启用后,同步控制信号会工作,不受时钟使能限定。
前面未勾选时钟使能信号 CE,这里就不可设置,保持默认即可。
ROM IP 的各种参数设置完后点击 OK。
在这里插入图片描述
出现如上图弹窗,点击 Generate,之后等 IP 生成完成后,出现如下图弹窗,点击 OK。
可 以 在 PROJECT MANAGER 窗 口 下 IP Source 窗 口 IP Synthesis 下 看 到 dist_mem_rom_ip.mif 文件,如下图
在这里插入图片描述
这个文件就是 IP 生成过程中通过我们添加的 coe 文件自动生成的,这点和 quartus 有所不(quartus 是用户直接添加指定 mif 文件)。有一点比 quartus 好的就是在 ROM Core 建立时 vivado 软件会根据用户添加 coe 文件以及设置 ROM 参数(深度,位宽)对 coe 文件进行校验,如果不符合要求会提示相关错误,可以有效避免初始化文件不匹配的问题。

四.分布式ROM IP核的测试仿真

这里除了实现例化需要仿真的文件以及时钟创建,还实现了地址数从 0 自加到 2559d,但是由于本 ROM 的最大数据个数是 255d,因此在 address第一次加满 255 后会重新从 0 开始自加,这样就有十个地址数循环。ROM_tb.v 文件代码如下:

`timescale 1ns/1ns
`define CLK_PERIOD 20

module ROM_tb;
reg clk;
reg [7:0]addr;
wire [7:0]dout;
wire [7:0]dout_reg;
 
initial clk = 1;
always #(`CLK_PERIOD/2) clk = ~clk;

dist_mem_rom_ip rom (
    .a(addr), // input wire [7 : 0] a
    .clk(clk), // input wire clk
    .spo(dout), // output wire [7 : 0] spo
    .qspo(dout_reg) // output wire [7 : 0] qspo
); 
 
integer i = 0;
initial begin
addr = 0;
#21;
for(i=0;i<2560;i=i+1)begin
#`CLK_PERIOD;
addr = addr + 1'b1;
end
#(`CLK_PERIOD * 50);
$stop;
end

endmodule

可通过在信号 dout 右键选择 Waveform Style 设置为 Analog,还可以选择 Radix 设置显示数值的进制,这里选择无符号的十进制数。
在这里插入图片描述
在这里插入图片描述

五.块ROM IP核的创建

文章开始有提到 ROM/RAM 创建根据使用 FPGA 资源不同有两个创建的入口,前面创建的 ROM 使用的是 LUT 资源,下面创建一个使用硬件块 RAM 资源的 ROM IP,步骤和前面创建使用分布式 LUT 资源的 ROM 类似,选择点击 Block Memory Generator进入到 RAM IP 配置界面。
在这里插入图片描述
端口类型的选择,Xilinx 的很多 IP 一般都有提供两种接口,一种是常规接口,一种是AXI 接口,这里选择选择常规接口 Native。
在这里插入图片描述
存储器类型的选择,有 ROM 和 RAM 两种类型,ROM 又分为单端口 ROM 和双端口ROM。对于 RAM 而言,有三种可选项,单端口 RAM、简单双端口 RAM 和真双端口 RAM。具体的差异我们可以通过依次选择,然后观察窗口左边 RAM 端口来进行对比。在扫盲部分我们已经比较了单端口 ROM 和双端口 ROM,关于 RAM,我们在其他地方再详细讲解。这里我们选择单端口 ROM。
第一个设置界面,可以操作选择的是算法类型,有三种选项可选,最小面积、低功耗、
固定原语。这里不过多讲解,需要了解更多的可以查阅 IP 手册,IP 手册上面 42 页开始有对
这个详细的讲解。这里我们保持默认的最小面积选项即可(手册通过IP配置页左上角的documentation即可获得)
在这里插入图片描述
ROM 数据位宽和深度设置,这个根据实际应用需求进行设置,这里设置数据位宽 8bit,深度 256。
在这里插入图片描述
端口使能信号类型设置,一个是一直使能,一个是通过一个 ENA 信号管脚控制,这里选择 Always Enable。
在这里插入图片描述
端口 A 输出寄存器配置,这里可以看下 ROM 内部结构图,可以很清楚的看到 Primitives Output Register 是结构中的 1 处的寄存器,Core Output Register 是结构图中 2 出的寄存器。REGCEB Pin 是寄存器使能管脚,如果勾选,会有一个寄存器使能控制管脚用于控制寄存器的使能,如果不勾选寄存器就一直使能状态。要得到更好的性能,我将这里的两个寄存器都勾选。
在这里插入图片描述
端口 A 输出置位/复位设置,这里不创建置位/复位端口,需注意这里置位/复位并不复位RAM 中的数据而是只复位寄存器上的值。
不创建
勾选 Load Init File,加载初始化文件,这里可以加载上面生成的 coe 文件,下面的 FillRemaining Memory Location 是用来填充初始化文件没有覆盖的地址区域的数值,比如,ROM设置深度为 256,初始化文件中初始化的数据只有 200 个,勾选这个选项就可以对剩余的 56个数据进行统一初始化一个固定的数值,这个数值可以设置。我们前面初始化文件已经对整个 ROM 空间覆盖到了,就不用勾选 Fill Remaining Memory Location
在这里插入图片描述
最后看一看总的情况,信息包括使用的资源,A 端口的地址位宽,以及端口 A Read Latency 为 3 个时钟周期。
在这里插入图片描述
对上图中 Latency 的概念做一下补充,Latentcy 指的是相对于某个时钟起始位的 1 个或多个时钟后数据才有效,一般以时钟为单位,这里表示的是时钟采集到读数据地址到数据有效的时间间隔,举例子说明。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里之所以 Latency 等于 3,是因为我们前面配置同时勾选了 Primitives Output Register和 Core Output Register,相当于数据打了两拍。
到这里 IP 设置就完成了,点击 OK,点击 Generate 生成 IP。

六.块ROM IP核的测试仿真

前面仿真分布式 ROM 我们是自己编写 testbench 文件进行仿真,下面介绍一种 Vivado比较方便的功能,可以快速生成一个 Example Design 及相关的仿真文件。使用该功能对 IP的仿真和使用有很多可以值得借鉴的经验。在 PROJECT 窗口的 IP Sources 中找到我们生成的 block ROM IP ,右键找到 Open IP Example Design。
在这里插入图片描述
会弹出打开一个新的 Vivado 的窗口,并打开了 Example project。
自动生成了顶层文件和仿真文件
可以看到,自动生成了一个顶层文件和相应的仿真 tb 文件。这个时候可以直接点击SIMULATION 下的 Run Simulation,点击 Run Behavioral Simulation 进行仿真,然后RUN ALL。
可以在 Vivado 下面 Tcl Console 窗口看到,打印信息,仿真测试成功。这个打印信息是tb 文件里的,可以在 tb 文件中找到这句话出处。
在这里插入图片描述
在这里插入图片描述
这里 tb 文件代码是 VHDL 语言编写,从代码上来看,仿真正确成功会打印“Test Completed Successfully”,仿真错误会打印"Simulation Failed",从 Tcl Console 窗口打印信息就可以比较清晰的看到仿真是 OK 的。
将 rom IP 的信号加入波形显示窗口,然后重新仿真
在这里插入图片描述
按照前面方式更改波形显示为 Analog,得到结果:
在这里插入图片描述
参考文献与视频:
小梅哥FPGA
野火FPGA

  • 27
    点赞
  • 153
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

通信学会了吗?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值