文章目录
在定制一个RAM IP核之前,强烈建议您先阅读:
从底层结构开始学习FPGA----RAM IP核及关键参数介绍
从底层结构开始学习FPGA----Xilinx RAM IP的定制与测试
在上面两篇篇文章中,已经对RAM IP核的各个关键因素做了详细的讲解,并对RAM IP的例化做了一次定制与测试。
本文介绍一种在FPGA开发中非常常用的存储类 IP 核——ROM 的使用方法。
ROM 是只读存储器(Read-Only Memory)的简称,是一种只能读出事先所存数据的固态半导体存储器。其特性是一旦储存资料就无法再将之改变或删除,且资料不会因为电源关闭而消失。而事实上在 FPGA 中通过 IP 核生成的 ROM 或 RAM调用的都是 FPGA 内部的 BRAM 资源,掉电内容都会丢失(这也很容易解释,FPGA 芯片内部本来就没有掉电非易失存储器单元)。
用 IP 核生成的 ROM 模块只是提前添加了数据文件(.coe 格式),在 FPGA 运行时通过数据文件给 ROM 模块初始化,才使得 ROM 模块像个“真正”的掉电非易失存储器;也正是这个原因,ROM 模块的内容必须提前在数据文件中写死,无法在电路中修改。
简单地讲,RAM是一个可写、可读的IP核;而ROM只是一个可读却不可写的IP核。所以可以把ROM看成一个是“青春版”的RAM,其本身只具备RAM的读取功能。事实上,在xilinx的FPGA开发中,RAM与ROM均是用BRAM资源实现(定制IP也是同一个),只不过对外接口封装不同。
在RAM IP章节,我们知道了RAM的组成形式有三种:
那么ROM的组成形式有几种呢?
不难推断,单口ROM肯定是可以实现的,即一个端口来读。那么双口ROM会不会有两种?不会,因为ROM只读不写的特性就注定了只有一种双口ROM,即两个端口都可读的ROM。如下:
(1)单端口 ROM:不可实现写操作,只可使用一个端口实现读操作

(2)双端口ROM:不可实现写操作,可以使用两个端口实现读操作,两个端口读取数据的位宽可以不同,但必须是整数倍关系

ROM IP核的定制和RAM IP核的定制过程非常相似,但是需要关注的参数更少,所以实现更简单。接下来将展示定制一个位宽4bit,深度16的单口ROM的过程。




关于载入的初始文件(.coe文件),可以按如下格式编写,然后在上面点击 Browse 选取:

也可以点击,Edit,然后新创建一个.coe文件后,编写自己的内容(需要注意的是,不要使用;来结束):

当然了,上面载入的都是一些比较简单的,有规律的数据。如果您要载入的数据比较复杂,建议使用MATLAB或者其他脚本软件来生成,然后保存为coe文件再载入。

按上述步骤生成IP后,复制 IP核 自带的例化模板。


然后在我们的RTL代码中,例化该ROM。RTL不做其他功能,只例化ROM即可。
- module rom_test(
- input clk, //输入时钟
- input en, //使能信号
- input [3:0] addr, //地址
- output [3:0] data_out //输出数据
- );
-
- //例化ROM IP核
- rom_w4_d16 rom_w4_d16_inst (
- .clka (clk),
- .ena (en),
- .addra (addr),
- .douta (data_out)
- );
-
- endmodule
写个testbench,对ROM IP测试一下。
测试行为:之前编写初始化文件(.coe)的时候已经说了,载入的内容分别为0、1、2······F。所以稳定后从地址0-15读取数据,观察读出的数据是否与初始化内容一致。
- `timescale 1ns / 1ns
- module tb_rom_test();
-
- reg clk;
- reg en;
- reg [3:0] addr;
- wire [3:0] data_out;
-
- initial begin
- clk = 0;
- en = 0;
- addr = 0;
-
- #110
- en = 1; //开始读取数据
- repeat(18) begin //从地址0-15读取数据(加上延迟3)
- #10 addr = addr + 1;
- end
- en = 0; //停止读取数据
-
- #20 $finish; //结束仿真
- end
-
- always #5 clk = ~clk; //生成时钟,周期10ns
-
- //例化被测模块rom_test
- rom_test rom_test_inst(
- .clk (clk ),
- .en (en ),
- .addr (addr ),
- .data_out (data_out )
- );
-
- endmodule
使用vivado自带的仿真工具simulator运行仿真,仿真结果如下:

在3个时钟周期的延迟后,读出的数据分别为0、1、2······、F,符合预期结果。
参考资料1:Block Memory Generator v8.4