1 libmap的作用
主要两个作用:
解决module名重复问题:
比如有两个IP, IP0和IP1, 它们都例化了一个叫ADD的module, 而且它们的filelist中都包含add.v.
这时会引起编译错误, 这时可以:
(1) 指定IP0中的add.v编译到库lib0中, IP1中的add.v编译到库lib1中,
(2) 指定IP0中的ADD使用库lib0, IP1中的ADD使用库lib1.
将指定模块做成空module, 用来加速仿真.
比如module M0例化了4次, U0~U3, 如果只仿真U0, 则可以:
(1) 做一个空module M0_DUMMY, 只保留端口信号.
(2) M0_DUMMY编译到DUMMY库中.
(3) 指定U1~U3使用DUMMY库.
其实通过修改设计的方式, 也可以完成上述两个功能, 但修改设计不易维护, 或者有时无法修改原设计, 所以必须要用到libmap方法.
2 如何使用libmap
使用libmap有两种方法: 两步法, 三步法.
两步法: 1) 编译(在编译阶段指定libmap), 2) 仿真.
三步法: 1) RTL编译/tb编译, 2) elaboration, 3) 仿真.
三步法有点麻烦.
2.1 两步法
2.1.1 编写dummy.v
将a.v和b.sv复制一份, 并删去其中的内容, 只保留port声明, 文件命名为a_dummy.v和b_dummy.sv
2.1.2 编写libmap文件
libmap文件有两部分内容: 1) 指定哪些文件编译到DUMMY库中. 2) 指定哪些inst使用DUMMY库(这部分可以放到单独的topcfg.v文件中).
libmap.v
//指定哪些文件编译到DUMMY库中. DUMMY是个字符, 可以随便改成其它名字.
library DUMMY xx/xx/a_dummy.v
library DUMMY xx/xx/b_dummy.sv
//指定哪些inst使用DUMMY库
config lib_cfg;
design top; //指定仿真顶层名称, 详细见2.1.5 注意1.
instance top.U_A1 liblist DUMMY; //U_A1使用DUMMY库
instance top.U_B1 liblist DUMMY; //U_B1使用DUMMY库
//instance top.U_A0 liblist work; //U_A0使用work库, 默认的, 可以不设置
//instance top.U_B0 liblist work; //U_B0使用work库, 默认的, 可以不设置
endconfig
2.1.3 编译
在编译时有三点:
(1) 指定dummy.v.
(2) 指定libmap文件.
(3) 指定top为config指定的名称lib_cfg(可以随便改成其它名字)
vcs \
-f file.lst \ # 原本的设计
-f file_dummy.lst \ # dummy.v
-libmap libmap.v \ # libmap文件
-top lib_cfg \ # 如果不指定lib_cfg为top, 会导致libmap有问题, 详细见2.1.5 注意2.
...
2.1.4 仿真
正常调用simv仿真即可. 这时top.U_A1和top.U_B1就会使用空module了.
2.1.5 注意事项
注意1: libmap config中design语句
在libmap config的design语句中, design必须要指定仿真的顶层名称, 这样libmap才会正确生效.
如果指定中间层次, 会导致libmap不成功.
注意2: 关于仿真选项-top的问题:
假设仿真的顶层名称是top_tb, libmap的名称是lib_cfg, 同时config中design的名称是top_tb, 这时:
(1) 如果同时指定两个top: -top top_tb -top lib_cfg, 会导致comiple报错: “duplicate top level module is found Module top_tb”.
(2) 如果只定义一个-top top_tb, 不定义-top lib_cfg, 会导致libmap异常: 所有DUMMY library都生效了, 无法通过instance xx/U_A liblist DUMMY精细指定.
(3) 如果只定义一个-top lib_cfg, 不定义top_tb, 这时是正常的, DUMMY library符合预期.
(4) 如果两个top都不指定: vcs会将每个module都视为top, 而lib_cfg也不生效(其实也相当于没指定-top lib_cfg), 结果与第(2)点相同: 无法精细指定.
另一篇讲解
解决办法有:
1.创建共用的add模块,g0和g1均采用该模块,通过参数配置来达到不同的效果。
2.简单粗暴的修改add模块名,如g0_add和g1_add。
如果类似情况比较多,而且重名的模块是IP厂家提供的,比如CPU IP和GPUIP,那么方法1就行不通了。
方法2仍然可用,大不了写个脚本解决,一键搞定。但是维护性就不太好了,IP版本更新了,新项目要继承,工艺改变,多IP使用,那么都需要来一遍。
看起来以上方法都不是最优解,那么有没有比较好的方式呢?
编译g0的时候我就只看g0下面add,g1的时候只看g1的add。也就是各自采用各自的库,这就需要libmap来实现。
Libmap实际是Verilog的特性,各家EDA工具均支持这个特性,由于猴哥目前主要使用VCS为主,今天讨论是使用VCS的时候怎么来使用libmap特性。
VCS常用两步法和三步法,两步法即vcs命令编译+elaboration生成可执行simv文件,然后simv执行仿真。
三步法是将编译vlogan和elaboration分开实现,成为2步,elaboration是从库文件中获取例化的模块进行例化链接,然后生成simv文件,第3步就是仿真了。
首先是说明两步法libmap的使用方法,这个比较简单。
Vcs的编译默认情况下是将编译结果放到work这个库里,这是默认的库名。编译器会使用的synopsys_sim.setup来指定库名。
WORK > DEFAULT
DEFAULT : ./work
所以不使用libmap的时候编译生成的库就是work,所有的都放在这里,也就是导致重复定义错误的原因。
那么libmap目的就是将库分开,指定不同模块编译放到不同库中,elaboration的时候也指定库名,从中获取模块的内容。
1.定义一个libmap文件:
该文件告诉编译器,将g0目录下的.v文件编译后放入lib_g0中,将g1目录下的.v文件编译后放入lib_g1中。
Top下面的模块g0使用的是lib_g0这个库,模块g1使用的是lib_g1这个库。
Liblist后面可以是多个lib名字,elaboration的时候会从指定的库里面获取需要的内容而不是从默认work库中获取。
对于没有指定library的其它文件,其编译结果放入到work库中。
2.编译命令中加入libmap文件:
这样vcs编译器会使用libmap文件中的指向来存放编译输出结果,生成额外的lib_g0库和lib_g1库。编译器也通过它获得top中模块例化的信息。
通过这两个步骤就实现了两步法的libmap使用,解决了重复文件名的问题。
2.2 三步法
接着,对于三步法,使用libmap就比较复杂一些了:
三步法的使用是为了节省编译时间,将RTL单独编译,testbench单独编译,最后再一起elaboration。
不过实际上elaboration占用的时间比较多,并不能节省太多时间。
如果环境是UVM的,还需要单独先对UVM的package做一次独立编译。
三步法使用libmap特性需要如下步骤:
1.编写synopsys_sim.setup文件
2.用libmap生成库
vlogan –libmap libmap -f filelist.f
此处的libmap文件只包含库信息
3.编写config信息文件lib_cfg.v
4.顶层编译
vlogan +v2k lib_cfg.v top.v
5.elaboration
vcs libcfg –l elab.log
最后输出执行文件simv.exe
可以看到,使用三步法的时候,编译需要用synopsys_sim.setup来声明库文件,否则在elaboration的时候看不到LIB_G0和LIB_G1。
三步法使用libmap步骤比较多,工具存在的问题还是比较多的,特别对复杂的芯片设计来说。并不是太推荐这种方法使用。
参考文章
http://t.csdn.cn/nSvf3
https://www.cnblogs.com/gaiqingfeng/p/15577602.html