概述
这两个系统任务是用来从指定文件中读取数据到寄存器数组或者RAM、ROM中。除了可以在仿真的任何时刻被执行使用外,根据综合工具的不同,也可以用来对RAM或者ROM进行初始化(Vivado支持)。
使用格式共6种:
- $readmemb("<数据文件名>",<数组名>)
- $readmemb ("<数据文件名>",<数组名>,<起始地址>)
- $readmemb ("<数据文件名>",<存贮器名>,<起始地址>,<结束地址>)
- $readmemh("<数据文件名>",<数组名>)
- $readmemh ("<数据文件名>",<数组名>,<起始地址>)
- $readmemh ("<数据文件名>",<数组名>,<起始地址>,<结束地址>)
$readmemh(h,hexadecimal,十六进制)用来读取16进制的数据,而$readmemb(b,binary,二进制)则用来读取2进制的数据。
由于$readmemh与$readmemb的用法几乎一样,仅仅是读取数字的进制不同。所以下文均已$readmemh来阐述这两个系统任务的用法。
用法示例
下面具体说说这两个系统任务的用法,并结合仿真工具进行仿真测试来辅助说明。
数据文件的格式
在这两个系统任务中,被读取的数据文件的内容只能包含:空白位置(空格、换行、制表格、注释行、二进制或十六进制的数字。数字中不能包含位宽说明和格式说明,对于$readmemb和$readmemh系统任务,每个数字可以是二进制或者十六进制数字。另外,数字必须用空白位置或注释行来分隔开。
比如文件data.txt用来表示10个8bit的16进制数据,可以这么表示(每个数据占一行):
也可以这么表示(每个数据用空格隔开):
还可以用@+地址的方式来跳跃式地指定某一个地址的数据内容(不太建议使用):
数据文件的路径表示方法
可以用两种路径表示方法来指导综合工具找到你的数据文件:1、绝对路径;2、相对路径。
(1)绝对路径
绝对路径就是数据文件在系统中的位置,比如:
数据文件data.txt的绝对路径就是 D:\read_test\read_test.srcs\sim_1\new
但是一定需要注意的是,尽管在windows系统中使用斜杠 \ 来表示不同的层级,但是在系统函数$readmemh中取需要使用反斜杠 / 来表示层级,如下:
$readmemh("D:/read_test/read_test.srcs/sim_1/new/data.txt",mem_test); //绝对路径
这样综合工具就会去这个目录下寻找数据文件。
(2)相对路径
相对路径是根据仿真文件或者RTL文件的位置来相关的。
比如在vivado中使用系统函数$readmemh时采用如下表示方法:
$readmemh("data.txt",mem_test); //相对路径
同时,数据文件必须放在这个路径下:
综合工具就会直接去该目录下寻找数据文件,如果该目录下没有指定的数据文件,则会发出警告,同时仿真读取的值都是x。modelsim的相对路径貌似宽松一点,但没具体测试过,不多说。
从这,也可以看到,使用绝对路径其实要灵活一点,从不同的平台迁移会比较方便,所以建议使用绝对路径。
使用方法:
接下来分别对$readmemh的三种使用方法做个仿真测试和介绍,使用的数据文件data.txt如下:
使用方法1:不指定起始地址和结束对应地址
即$readmemh("<数据文件名>",<数组名>),此时会将从数据文件中读到的第1个数据填入数组的第0个位置,此后类推,直到数组被填满。如果数据的个数大于数据文件中数据的个数,则数组无法被填满,未被填满的部分则依然未被赋值。
(1)数组被填满:
-
`timescale
1ns /
1
ns
-
-
module tb_read_test();
-
-
integer i;
-
reg [
7:
0] mem_test [
9:
0];
//mem_test是位宽8bit,个数为10的数组
-
-
initial $readmemh(
"D:/read_test/read_test.srcs/sim_1/new/data.txt",mem_test);
//绝对路径
-
-
//显示数组的10个值
-
initial begin
-
for(i=0; i<10; i=i+1)
-
$display("%d: %h", i, mem_test[i]);
-
end
-
-
endmodule
仿真结果如下:
(2)去除部分数据文件中的数据,导致数组无法被填满(注释掉后面4个数据):
仿真结果如下:
数据文件仅有6个数据,而数组个数为10,所以数组的后4个数据仍没有被赋值。
使用方法2:指定起始地址,但不指定结束地址
即$readmemh ("<数据文件名>",<数组名>,<起始地址>),此时会将从数据文件中读到的第1个数据填入数组的起始地址,此后类推,直到数组被填满。而之前被起始地址跳过的数组的数据则不会被赋值。如果数据的个数大于数据文件中数据的个数,则数组无法被填满,未被填满的部分则依然是未知状态。
指定数据从数组的地址2开始赋值,则地址2应该被赋值0,地址3被赋值1,···等等。而地址0和地址1则仍保持未赋值状态:
-
`timescale
1ns /
1
ns
-
-
module tb_read_test();
-
-
integer i;
-
reg [
7:
0] mem_test [
9:
0];
//mem_test是位宽8bit,个数为10的数组
-
-
initial $readmemh(
"D:/read_test/read_test.srcs/sim_1/new/data.txt",mem_test,
2);
//绝对路径,从地址2开始
-
-
//显示数组的10个值
-
initial begin
-
for(i=0; i<10; i=i+1)
-
$display("%d: %h", i, mem_test[i]);
-
end
-
-
endmodule
仿真结果如下:
从地址2开始依次被赋值00~07,而地址0和地址1则仍保持未赋值状态。
使用方法3:同时指定起始地址和结束地址
即$readmemh ("<数据文件名>",<数组名>,<起始地址>,<结束地址>),此时会将从数据文件中读到的第1个数据填入数组的起始地址,此后类推,直到指定的终结地址对应的数组也被赋值。处于起始地址和终结地址构成的区间范围外的地址对应的数组则不会被赋值。如果数据的个数大于数据文件中数据的个数,则数组无法被填满,未被填满的部分则依然是未知状态。
指定数据从数组的地址2开始赋值,直到地址8终结,则地址2应该被赋值0,地址3被赋值1,···等等,直到地址8也被赋值。而地址0、1、9则仍保持未赋值状态:
-
`timescale
1ns /
1
ns
-
-
module tb_read_test();
-
-
integer i;
-
reg [
7:
0] mem_test [
9:
0];
//mem_test是位宽8bit,个数为10的数组
-
-
initial $readmemh(
"D:/read_test/read_test.srcs/sim_1/new/data.txt",mem_test,
2,
8);
//绝对路径,从地址2开始
-
-
//显示数组的10个值
-
initial begin
-
for(i=0; i<10; i=i+1)
-
$display("%d: %h", i, mem_test[i]);
-
end
-
-
endmodule
仿真结果如下:
从地址2开始直到地址8依次被赋值00~06,而地址0、1、9则仍保持未赋值状态。
- 📣博客主页:wuzhikai.blog.csdn.net
- 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵
- 📣您有任何问题,都可以在评论区和我交流📞!
- 📣创作不易,您的支持是我持续更新的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!
转载:https://blog.csdn.net/wuzhikaidetb/article/details/125728219