1、实验目的
了解Compact系列的ROM IP的使用以及配置的方法。
2、实验原理
2.1、ROM介绍
ROM即只读存储器,在程序的运行过程中他只能被读取,无法被写入,因此我们应该在初始化的时候就给他配置初值,一般是在生成IP的时候通过导入.dat文件对其进行初值配置。
注意:PDS的IP配置工具中提供两种不同的ROM,一种是Distributed ROM(分布式ROM)另一种是DRM Based ROM,分布式ROM用的是LUT(查找表)资源去构成的ROM,这种ROM会消耗大量LUT资源,因此通常在一些比较小的存储才会用到这种RAM,以节省DRM资源。而DRM Based ROM是利用片内的DRM资源去构成的ROM,不占用逻辑资源,而且速度快,通常设计中均使用DRM Based ROM。
2.2、IP配置
以下给出比较常用的ROM的配置作为介绍,由于只能读,因此其均为单端口ROM如图所示:
首先点击快捷工具栏的 IP 图标,进入IP例化设置
然后在IP目录处选择DRM,在Instance name处为本次实例化的IP取一个名字,接着点击Customise进入IP配置页面。操作示意图如下:
接着进行如下配置:其他选项卡可以保持默认。
注意:
如果勾选Enable Output Register(输出寄存),输出数据会延迟一个时钟周期。
同时,可以看到Enable Init选项是默认勾选的,并且不可取消。
导入的数据的格式只能为二进制或者是十六进制。
可以看到下图给出的是完整的接口列表,一般我们只需要addr、rd_data、clk、rst这四个信号即可。具体每个端口的含义这里参考官方手册,大家也可以自行查看IP手册,如图所示:
我们可以简单的使用matlab生成.dat文件,然后将生成的dat文件在ip配置页面指定其位置,注意是16进制,其代码如下:
% 生成从0到127的数字
numbers = 0:127;
% 打开一个名为 'data.dat' 的文件以写入
fileID = fopen('data.dat', 'w');
% 遍历数字数组,并将每个数字以十六进制格式写入文件
for i = 1:length(numbers)
fprintf(fileID, '%02X\n', numbers(i));
end
% 关闭文件
fclose(fileID);
disp('数据已写入到 data.dat 文件中');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2.3、ROM的读时序
以下时序图均来自官方IP手册,并且均未使能输出寄存。
可以看到该时序是非常简单的,比如在TI时刻,当clk上升沿到来时,且clk_en为高电平时,给出要读出的地址,rd_data就会输出数据,在不勾选输出使能寄存的情况下,rd_data的输出会有延迟,具体时间可以从仿真里看到,所以我们在下个时钟周期的上升沿即T2时。刻的上升沿才能获取到ROM读出的值。
所以整体时序非常简单,如果勾选了clk_en信号,就要给clke_en高电平才能读数据,如果不勾选clk_en信号,就一直根据地址读取ROM数据。
3、代码设计
模块端口列表如下:
rom_test_top顶层模块如下:
module rom_test_top
(
input wire rd_clk ,//读时钟
input wire rst_n ,//复位
input wire [9:0] rd_addr ,//读地址
output wire [7:0] rd_data //读数据
);
rom_test rom_test_inst (
.addr(rd_addr), // input [9:0]
.clk(rd_clk), // input
.rst(~rst_n), // input
.rd_data(rd_data) // output [7:0]
);
endmodule
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
该模块的功能是例化rom ip,这里不多做介绍。
rom_test_tb测试代码如下:
`timescale 1ns/1ns
module rom_test_tb();
reg sys_clk;
reg rst_n;
reg [9:0] rd_addr;
wire [7:0] rd_data;
initial
begin
rst_n <= 1'd0;
sys_clk <= 1'd0;
#20
rst_n <= 1'd1;
end
//50MHZ
always#10 sys_clk = ~sys_clk;
//
GTP_GRS GRS_INST(
.GRS_N(1'b1)
) ;
always@(posedge sys_clk or negedge rst_n) begin
if(!rst_n)
rd_addr <= 10'd0;
else if(rd_addr == 10'd127)
rd_addr <= 10'd0;
else
rd_addr <= #2 rd_addr + 1'b1;
end
rom_test_top u_rom_test_top(
.rd_clk ( sys_clk ),
.rst_n ( rst_n ),
.rd_addr ( rd_addr ),
.rd_data ( rd_data )
);
endmodule
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
第24-31行通过一个always块不断生成地址,任何给到ROM IP,将数据读出,由于没勾选clk_en信号,所以数据在ROM复位完成后就会不断读出。所以并没有复杂的逻辑,就是让地址从0不断累加,到达127后重新计数,将rom中初始化的数据循环读出。
第33-38行例化了ROM的顶层模块,该模块里面其实就是调用了ROM IP,然后把信号引出端口,没有任何逻辑操作。
4、实验现象
右键仿真的文件,选择Run Behavior Simulation开始行为仿真。启动Moselsim进行仿真:
dat文件中储存了0~127共128个数据,当复位信号为高时,ROM IP根据当前输入的地址输出数据,数据在地址送入的后一个时钟周期输出。