Menu Close

vivado 仿真工程中$readmemh 使用

在verilog 仿真中,有时会使用$readmemh系统任务,帮助仿真者快速装载仿真所使用的数据。在一些简单的仿真中, 我们可以通过initial 来加载仿真的数据,但是当仿真数据比较多的情况下, 就需要使用系统函数加载数据了。$readmemh 系统任务就是用来帮助开发者加载仿真数据的。$readmemh系统任务本身具有文件操作功能,因此不需要$fopen等文件操作。通常来说 , $readmemh 也被归为文件操作类型的系统任务。(注 $readmemh 没有返回值,它是系统任务)

1.$readmemh 语法格式

其中:
  • <数据文件名> 是指向一个文本文件,用来保存仿真的数据。每一行代表一个十六进制的数据。
  • <存储器名> 为仿真文件中例化的存储器的名称。
  • <起始地址>,<终止地址> 指示将文本文件中的数据存储到存储器的位置段。
  • 注:如果存储器的位宽是8-bit的, 那么使用$readmemh 将读取文本文件中每行的最后一个byte(8-bit)

2.$readmemh在Vivado下仿真的使用

  • 存储器类型变量初始化

以下举例说明:

例化一个 8-bit 宽的ram存储器,深度为128。 将文本文件test.txt 文件中每一行最后一个byte 数据,存储到ram中, 从ram[0] 开始, 直到test.txt 文件的最后一行结束,或者是读出的数据达到 ram 的最大深度。文本文件的格式需要注意只能使用0-9,a-f,每一行的末尾需要以回车换行结束。使用$readmemh的仿真文件如例1所示:
例1:
其中仿真引入的文件led_sim.sim 为文本文件, 缺省的目录为仿真执行的目录:
图1
只要将文本文件led_sim.sim 放到xsim目录下,就可以打开了。在仿真文件中可以按如下方式引用,
localparam FILE_NAME = “led_sim.sim”;
使用xsim目录的缺点是每次仿真复位后(右键点击SIMULTAION,选择Reset,如图2所示),会删除led_sim.sim 文件,用户需要重新制作文件,再次放入的xsim 这个目录中,比较麻烦; 如果将led_sim.sim 放到其他的目录下,可以避免这个问题的出现。
图2
因此上例中,并没有将led_sim.sim 放到xsim 这个目录下,而是使用的../../../led_sim.sim, 这时使用重定向来指定文本文件的位置:
图3
led_sim.sim 文件的内容及格式如下:

 
仿真输出结果如下:
图4
图4中显示了读取16 个 ram 中数据的结果。

修改这个仿真文件:

将 $readmemh (FILE_NAME, ram); 替换为 $readmemh (FILE_NAME, ram, 2, 8);  表示将led_sim.sim 文本文件中的数据 写入ram中 (从ram[2] 开始写, 写到ram[8] 结束)
仿真结果如下:
图5
注意图5中显示ram[00],ram[01],ram[09]-ram[15]的值都是0xxx(未初始化的值),真正初始化的只有ram[02]- ram[08]。使用 $readmemh (FILE_NAME, ram, 2, 8);  这种格式可以在一定范围内对存储器进行局部修改。
  • 利用$readmemh初始化block memory IP核

reg [7:0] ram [0:127];  这样的定义既可以在仿真中使用,也可以在综合真实项目中使用。但在真实的Verilog 项目中, 更多的情况下是使用block memory 例化的IP 实现数据存储。 那么block memory IP 中的数据装载,我们可以使用*.coe 文件, 但是*.coe 文件不够灵活,每次修改*.coe 文件,都要重新生成IP ,然后才能仿真。 本文将使用$readmemh 系统函数,快速的加载数据到block memory IP 中, 方便仿真调试。使用$readmemh初始化block memory IP核的工程文件如例2所示。

 

例2:

使用环境:

Vivado 版本: Vivado 2018.2

开发板: FII-PRX100-D

代码如下:


 

例2中工程文件的主要功能是大约每330 ms,读取block memory (RAM_LED_VAL ) 中的数据,并将读出的数据显示到LED 上。其中在例化RAM_LED_VAL时,使用了*.coe文件,作为初始化的数据。

*.coe 文件内容及格式如下:


注意:

  1. *.coe文件里的memory_initialization_radix有效的选择只有 2,10或是16。
  2. 注意数字之间可以用一个空格,一个逗号,或者回车隔开。
  3. 在memory_initialization_radix行或者memory_initialization_vector行结尾时需要使用分号。

 

RAM_LED_VAL  例化步骤如下:

 

图6

图7

图8

当前工程的仿真文件如例3所示:

例3:


 

其中$readmemh (FILE_NAME, top_inst.RAM_LED_VAL_inst.inst.native_mem_module.blk_mem_gen_v8_4_1_inst.memory); 是如何找到这个路径的呢?步骤如下:

  • 先注释这一行文件,然后执行仿真工程。
  • 在仿真Scope界面上(见图9)观察用户当前例化IP的位置。使用 . 连接上下级模块,例如top_inst.RAM_LED_VAL_inst.inst.native_mem_module.blk_mem_gen_v8_4_1_inst
  • 在路径的末尾添加memory,最终完整的路径为top_inst.RAM_LED_VAL_inst.inst.native_mem_module.blk_mem_gen_v8_4_1_inst.memory

图9

 

在这里,就可以知道我们的IP 在什么地方了, 再将 $readmemh (FILE_NAME, top_inst.RAM_LED_VAL_inst.inst.native_mem_module.blk_mem_gen_v8_4_1_inst.memory); 这一行重新打开(不再注释掉),就可以将led_sim.sim 中的内容存储到RAM_LED_VAL_inst中,从而在仿真中替换了RAM_LED_VAL_inst 中原有的*.coe文件。 由于led_sim.sim 文件修改, 仿真文件执行速度都是比较快的, 所以这种方法要比修改*.coe文件, 重新生成IP 核,工作效率要高很多。

 

仿真结果如下:

图10

 

至此, 在不修改verilog 工程的情况下, 只是使用仿真系统任务,就可以轻松替换 IP 中的*.coe文件,快速的进行仿真了。

3.$readmemh在Vivado综合中的使用

$readmemh系统函数也可以在综合(Synthesis)时使用,下面的例3就是对存储器类型变量初始化。(注:Vivado生成的block memory暂时不支持在综合中使用$readmemh系统函数初始化数据,但是综合时不会报错。)

使用$readmemh对存储器类型变量初始化时,应注意存储器单元的引用必须使用变量索引,不能使用常数索引,如下例所示:

错误使用:


正确使用:


例3:


 

综合后生成*.bit文件在FII-PRX100-D开发板上的实验现象与仿真结果不符合。在写完21个数据后,从地址(led_addr )22-31,输出的led_reg结果确实为0。但是,从led_addr 等于32开始,又重复出现了前面的21个数据,如图11所示(ILA 输出结果)。

 

图11

解决方案:

将led_sim.sim文本文件写满128行数据,以对应128位不同的地址。总结来说,在Vivado下使用$readmemh系统函数进行综合建议保持文本文件的行数和存储器的深度一致。

4.$readmemh在Quartus及ModelSim下使用

考虑到差异性,将上面的例子做简单修改,在Quartus及Modelsim下进行测试,工程文件及仿真文件如例4所示。

例4:

Quartus 下的工程文件


 

Quartus 下的仿真文件

  • ModelSim下仿真

对存储器型变量中使用$readmemh在modelsim下进行仿真,有如图12所示警告,ram的值没有按照文件中给定的值初始化。ModelSim 要求$readmemh读取的文本文件每行的数据宽度必须小于等于存储器定义宽度。

 

图12

修改方案: 将led_sim.sim 文本文件中的第一行由00abcdef 修改为 ef 即可。修改后的仿真结果如图13所示。

图13

从图13中可以看出,ModelSim下对存储器型变量使用$readmemh的仿真结果是正确的。

  • Quartus 下利用$readmemh进行综合测试

例4中工程综合后并在FII-PRA006(Altera FPGA)开发板上进行测试,结果与仿真一致,可以正确运行。同时从例4也可以看出,在Quartus常量索引存储器或者使用变量索引存储器都是可行的。

由此可见,$readmemh系统函数在仿真中使用较多,在实际综合中使用还是需要注意一些限制条件。

vivado  工程文件下载(注册用户可见)

coe_prj.zip

 

Posted in FPGA, FPGA 教材教案, Verilog, 教材与教案, 文章