分类:验证平台编码
存储器?你说的不就是下面这句话吗?
的确,在大多数的场景下,memory的内存占用并没有成为我们的困扰。这应该归功于EDA厂商不断提升的仿真器性能,以及内存厂商的竞争使内存颗粒的价格保持在可以接受的范围之内。
但是,不可否认的是memory依然是内存占用大户。例如,UVM对于“Register”和“Memory”的理解就不一致。
“memory”不像“register”存在一个镜像空间,因为Memory较大,存在占用较大数据空间(large data space)的可能。
面对可能占用大量内存空间的memory,我们需要更多的更灵活的处理方式。下面,我们分情况来讨论memory的定义方式。
1、memory的总空间较小(memory例化数*单个memory空间)
“较小”是一个相对概念,这个信息会随着EDA软硬件资源的升级,而不断变大。我们的处理方式是:在没有明显感到内存占用成为瓶颈时,可以采用最简单的内存定义方式。
为描述方便,本文将以下定义方式命名为“直接定义法”。
2、当memory的存储空间较大,但是使用比较“稀疏”时。
如图所示,memory每个数据的宽度为32bit,memory的深度为2^25=33554432。其中,memory的使用量,与用例执行的数据包个数有关,而绝大部分的用例,数据包个数不超过1000个。对于这样的场景,如何处理呢?
首先,如果采用“直接定义法”,定义为bit [32-1:0]memory [2**25-1:0]并没有对仿真造成任何困扰的话,这样的设计方式是可以接受的。
然后,如果的确造成了一些困扰,我们可以考虑使用稀疏矩阵的定义方式。
当提到“稀疏矩阵”时,有时候会让我们想到SystemVerilog的语法——关联数组。关联数组的确可以解决一部分存储器单元定义的问题,且关联数组的内存占用与存入关联数组的数据个数成正比。
“关联数组法”(在大内存,且有效数据很少时才推荐)
但是,关联数组的内存占用,远远大于普通数组。当memory内部的数据个数稍微多一些时,关联数组的效率就会迅速下降,甚至比“直接定义法”的效率还低很多。
那么,面对“稀疏”结构,还有没有其它方式呢?
这个方式就是我们像存储器厂商学习的,我们命名为“有效索引法”。
“有效索引法”的源代码,可以在该链接下载:
https://www.micron.com/resource-details/3caf8e7e-ace6-4a7c-bf04-e374d9c08564
“有效索引法”的思路是:
定义一个不是很大的存储空间,能够存储“足够多”的数据信息即可。这个“足够多”的数量,一般会小于(甚至远小于)总的memory空间的大小。
“有效索引法”中,memory定义实现步骤是:
估计用例使用的memory空间大小;
定义memory空间的大小与memory估计大小相同,使用index索引,存储数据信息;
定义address空间的大小与memory估计大小相同,使用index索引,存储地址信息;
定义memory_used,记录目前已经使用了多少个index,主要用于判断定义的空间是否满足用例要求(为可测试性功能)。
“有效索引法”的内存占用估算:
memory空间,存储估计的memory数据;
address空间,存储估计的address数据;
memory_index、memory_used可忽略。
即存储空间为memory*有效估计值+address*有效估计值。若有效估计值远小于memory实际深度,则非常节省内存空间。若有效估计值等于memory实际深度,则“有效索引法”占用的内存空间大于“直接定义法”,若address的位宽与memory的位宽一致,那么“有效索引法”占用的内存空间为“直接定义法”的2倍。
“有效索引法”的结构如上图所示。我们可以通过memory_read、memory_write操作,来进一步理解“有效索引法”的方案。
读操作解析:
判断是否可获取有效的index:若该地址(addr)存在,那么在address存储单元内,可以找到与addr相等的单元,设置get_index为1(指示找到地址),使用memory_index记录相对应addr地址的索引;若该地址(addr)不存在,则在address存储单元内,找不到与addr相等的单元,返回get_index为循环前设置的0(未找到地址);
根据get_index返回情况,获取数据信息:若get_index返回1,则指示找到地址,按照memory_index索引取memory空间信息,即为data;若get_index返回0,则指示未找到地址,返回data为不定态。
写操作解析:
判断是否可获取有效的index:若该地址(addr)存在,那么在address存储单元内,可以找到与addr相等的单元,设置get_index为1(指示找到地址),使用memory_index记录相对应addr地址的索引;若该地址(addr)不存在,则在address存储单元内,找不到与addr相等的单元,返回get_index为循环前设置的0(未找到地址);
根据get_index返回情况,存储数据信息:若get_index返回1,则指示找到地址(该地址之前被写入过),按照memory_index索引取memory空间信息,写入新的数据data;若get_index返回0,则指示未找到地址,也即这个地址没有被写入过,应当将该数据data写入一片新的空间。但是,如果memory_used达到最大值,则说明没有新的空间可以被写入,我们之前估计的memory使用空间不足,需要报错;若没有达到memory_used的最大值,即还有可以使用的新空间,我们只要将memory写入新空间,然后更新memory_used,即使用的memory个数(也是最后一个数据的下一个索引)。
3、当memory的存储空间较大,使用也不“稀疏”,比较饱和时。
在这种情况下,使用“直接定义法”、“关联数组法”、“有效索引法”都难以解决内存使用较大的问题了。
这时候,解决问题的思路,类似于Windows操作系统的“虚拟内存”。使用硬盘解决软件内存不够的方案,“文件存储法”。
虽然存储信息较大,但是,我们可以将memory信息存储在文件中,需要读取时,根据地址信息,读取文件相应位置的数据。
初始化打开文件:
memory_read:
memory_write:
全文总结:
1、 一般情况下,使用“直接定义法”即可解决memory定义的问题;
2、 稀疏结构下,推荐使用“有效索引法”;
3、 稀疏差异较大,且数据量很小时(1k以下),可以使用“关联数组法”;
4、 存储器容量大,且数据使用饱和时,推荐使用“文件存储法”,类似于虚拟内存。
联系客服