前言
FPGA本身是SRAM架构的,断电之后程序就会消失,那么如何利用FPGA实现一个ROM呢,我们可以利用FPGA内部的RAM资源实现ROM,但这不是真正意义上的ROM,而是每次上电都会把初始化的值先写入RAM。Vivado软件中提供了ROM的IP核 , 我们只需通过IP核例化一个ROM,根据ROM的读时序来读取ROM中存储的数据。本实验将介绍如何使用FPGA内部的ROM以及程序对该ROM的数据读操作。该实验与ZYNQ之FPGA 片内RAM读写测试实验操作类似,可以参考。
一、创建ROM初始化文件
对于ROM,我们需要提前准备好数据,这样在FPGA 实际运行时,就可以直接读取这些ROM中预存好的数据了。Xilinx FPGA 的片内ROM支持初始化数据配置,我们可以创建一个后缀名为“.coe”的ROM初始化文件。
首先创建一个名为rom_test的文件夹。
然后在该文件夹下新建一个文本文档,将其后缀名改为“.coe”,我这里给文件命名为rom_init.coe。
rom_init.coe文件中的内容如下。
//该代码来自正点原子
MEMORY_INITIALIZATION_RADIX=16; //表示ROM内容的数据格式是16进制
MEMORY_INITIALIZATION_VECTOR=
11,
22,
33,
44,
55,
66,
77,
88,
99,
aa,
bb,
cc,
dd,
ee,
ff,
00,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8,
b1,
b2,
b3,
b4,
b5,
b6,
b7,
b8; //每个数据后面用逗号或者空格或者换行符隔开,最后一个数据后面加分号
ROM初始化文件的内容格式比较简单,其中,第1行为定义的数据格式,第3行到第34行是这个 32*8bit 大小ROM的初始化数据,每行数字后面用逗号,最后一行数字结束时用分号。
二、添加ROM IP核
先新建一个名为rom_test的Vivado工程,具体的步骤可以参见:Vivado软件的使用——以led的交替闪烁为例。
新工程到下图所示的界面后点击Finish即可完成工程的创建。
接着在工程中添加ROM IP,步骤为依次按照下图中的序号找到Block Memory Generator双击打开。
在弹出的对话框中的Basic下修改Component Name为rom_ip,修改Memory Type为Single Port ROM。
在Port A Options下按照下图中的序号依次修改。
把Primitives Output Register取消勾选,其功能是在输出数据上加寄存器,这可以有效改善时序 ,但读出的数据会落后地址两个周期,因此在很多情况下,不用这项功能,保持读出的数据落后地址一个周期即可。
在Other Options下勾选Load Init File,然后点击Browse查找第一步中创建好的文件rom_init.coe。
按照下图中序号依次选择到ROM的初始化文件。
可以看到该文件已经添加成功了,点击OK即可。
在弹出的对话框中点击Generate就可以生成ROM IP。
三、编写测试程序
ROM的程序设计非常简单,在程序中我们只要每个时钟改变ROM的地址,ROM就会输出当前地址的内部存储数据 ,例化ILA,用于观察地址和数据的变化。
新建名为rom_test的Verilog文件,依次按照下图中标注的序号进行即可。
在新建好的rom_test.v中写入如下代码。
//该代码来自正点原子
`timescale 1ns / 1ps
module rom_test(
input sys_clk, //50MHz时钟
input rst_n //复位,低电平有效
);
wire [7:0] rom_data; //ROM读出数据
reg [4:0] rom_addr; //ROM输入地址
//产生ROM地址读取数据
always @ (posedge sys_clk or negedge rst_n)
begin
if(!rst_n)
rom_addr <= 10'd0;
else
rom_addr <= rom_addr+1'b1;
end
//实例化ROM
rom_ip rom_ip_inst
(
.clka (sys_clk), //inoput clka
.addra (rom_addr), //input [4:0] addra
.douta (rom_data) //output [7:0] douta
);
//实例化逻辑分析仪
ila_0 ila_m0
(
.clk (sys_clk),
.probe0 (rom_addr),
.probe1 (rom_data)
);
endmodule
其中实例化ROM部分的代码来自rom_ip中的rom_ip.veo文件,不过需要将括号内的参数做一修改。
四、添加ILA
我们还需要添加ILA,其添加过程可参见:使用Vivado软件进行硬件调试。
探针的数量这里设置为2,即rom_addr和rom_data两个探针。
这里每个探针的位数按照代码中的分配,rom_addr是5位,rom_data是8位。
对应的代码如下。
wire [7:0] rom_data; //ROM读出数据
reg [4:0] rom_addr; //ROM输入地址
在弹出的对话框中点击Generate即可。
实例化ILA逻辑分析仪部分的代码来自ila_0中的ila_0.veo文件,不过需要将括号内的参数做一修改。
五、分配管脚
本实验中需要分配管脚的只有时钟信号clk(管脚为U18)和复位信号rst_n(管脚为N15),按照下图中的数字顺序即可完成管脚的分配。
管脚分配完成后Ctrl+S保存,名称与工程名保持一致。
管脚分配的信息在rom_test.xdc文件中。
六、Simulator仿真
Simulator仿真创建文件的过程可参见:Vivado中Simulator仿真软件的使用。
右击Simulation Sources选择Add Source按照下图中序号依次新建TB文件。
在tb_rom_test.v文件中写入如下代码。
//该代码来自正点原子
`timescale 1ns / 1ps
module vtf_rom_tb;
// Inputs
reg sys_clk;
reg rst_n;
// Instantiate the Unit Under Test (UUT)
rom_test uut (
.sys_clk (sys_clk),
.rst_n (rst_n)
);
initial
begin
// Initialize Inputs
sys_clk = 0;
rst_n = 0;
// Wait 100 ns for global reset to finish
#100;
rst_n = 1;
end
always #10 sys_clk = ~ sys_clk; //20ns一个周期,产生50MHz时钟源
endmodule
保存代码后选择SIMULATION下的Run Simulation,选择第一个行为仿真。
将所有代码中定义的信号拖入到波形仿真窗口,设置仿真时间为1000ns,运行后结果如下图所示。
由上图可知,该仿真结果与RAM读取数据一样,数据滞后于地址一个周期。
七、硬件调试
连接开发板,点击Generate Bitstream生成比特流文件,并将其下载到开发板上。
下载后弹出如下窗口,设置rom_addr的初始数值为00,然后点击运行按钮,得到如下波形,将游标移动到红线所在位置并放大得到下图。
由上图可以知道,硬件调试的结果与仿真的结果一致。
总结
以上就是ZYNQ之FPGA 片内ROM读写测试实验的所有内容了,该实验过程与ZYNQ之FPGA 片内RAM读写测试实验非常相似,大的不同就是该实验需要创建ROM初始化文件,然后在添加ROM IP核时选上该文件。
本文参考资料:正点原子–course_s1_ZYNQ那些事儿-FPGA实验篇V1.06.pdf