基于VMM1.2的SystemC参考模型功能覆盖率收集方案
万辰 wanchenhata@hisilicon.com
海思半导体
摘要
在当前的SOC设计中,往往需要首先对芯片各个主要功能模块进行ESL 建模,建立ESL系统,用以早期对系统架构以及性能进行验 证,并且进行早期软件调试。这些功能模块的ESL模型经常被用作后期EDA验证的参考模型,由于ESL模型采用的是SystemC语言 编写,无法在RM(Reference Model)内部进行功能覆盖率收集,这样造成EDA验证过程中功能覆盖率收集的诸多不便,本文描述 了如何采用Synopsys工具,实现在SystemC写的RM中收集功能覆盖率并且将其与环境其他地方收集的覆盖率合并进行分析。
Abstract
For the current SoC design, we always make ESL modeling according to different major modules firstly. And then we’ll establish the whole ESL system for the verification of system architecture and performance, and even software debug. These ESL models which are written in SystemC language will be used as reference model in the following verification flow. But we must meet an issue about collecting functional coverage in these SystemC models. This article introduces a new method of collecting functional coverage in RM based on SystemC.
1. 简介
在SOC芯片设计当中,ESL设计扮演着越来越重要的角色,它利 用高级语言实现芯片的基本功能,为芯片早期建立模型,用于帮 助系统工程师早期验证架构以及性能。并且,在后期芯片的RTL 设计和验证阶段, ESL模型非常适合作为芯片验证的黄金参考 模型。但是,由于ESL Model一般采用SystemC语言进行编写, 不能很好的与当前的VMM验证方法学融合,使得重用ESL模型 作为验证参考模型没有达到原有效果。在重用中,其中一个重要 的关键技术瓶颈就是无法实现在ESL Model中进行功能覆盖率收 集。本文将介绍一种可行的解决方案,真正实现验证重用。
2. 功能覆盖率收集
对于在SystemC语言写的RM (Reference Model) 中实现功能覆盖率收集的思路,无外乎两种:
(一)在RM内部进行功能覆盖率收集。由于SystemC语言不像 SystemVerilog语言一样,有专门收集功能覆盖率的标准语法, 因此需要额外单独通过C/C++语言编写一个库文件实现功能覆 盖率收集语法,另外即使实现了该语法,能够在SystemC中进行 覆盖率收集,但是RM的覆盖率信息无法导入Synopsys 的覆盖 率分析工具中,比如Synopsys Verification Planner.
(二) 在RM外部进行功能覆盖率收集,将RM的关键信息从RM中传递出来,然后在环境中进行功能能覆盖率收集。 在VMM1.2方法学中,引入OSCI TLM 2.0接口,可以非常方便的支持SystemC与SystemVerilog之间的进行信息交互,使得在RM外部进行功能覆盖率收集方案成为最优方案。
2.1 验证环境
对于典型的VMM验证环境结构如所示:

图 1 -验证环境结构图
其中Reference Model为参考模型,如果需要进行功能覆盖率收集,需要将收集的信息通过TLM中的tlm_analysis_port传递到 VMM验证环境中,再通过单独的功能覆盖率收集模块进行收集。
这里选择tlm_analysis_port进行信息传递的通道,主要是因为tlm_analysis_port有以下优点: (一) 一个tlm_analysisi_port可以和任意多个vmm_tlm_analysis_export 进行对接。 (二) 当不需要进行功能覆盖率收集时,也可以支持不连接vmm_tlm_analysis_export。 以上两个优点给予tlm_analysis_port极大的灵活性,非常适合作为功能覆盖率收集的传输通道。
2.2 vmm_analysis_port介绍
VMM验证方法学在1.2版本[2]中引入了标准的OSCI TLM2.0接口[1],该接口支持SystemVerilog与SystemC进行事务级别的信息通208VMM验证方法学在1.2版本[2]中引入了标准的OSCI TLM2.0接口[1],该接口支持SystemVerilog与SystemC进行事务级别的信息通信。 Analysis ports是TLM众多接口中一个接口,该接口支持把transactions分发给多个分析组件,如Scoreboard与功能覆盖率模型
等,从而完成功能正确性的核对或者功能覆盖率的收集等。
Analysis ports区别于其他port的最关键的属性是:
(一) 一个port可以连接到多个channel或者subscribers。(可以把analysis ports连接到多个observers,而把analysis exports连接 到多个 producers)
(二) Analysis port可以不接任何channel或者subscribers而不会造成异常。
(三) 通过扩展write函数来实现Port两端的信息交互与同步。
2.3 TLM_GENERIC_PAYLOAD 介绍
tlm_generic_payload是OSCI TLM2.0标准中的重要组成部分,其目的主要是通过两个方面来改善多个带存储地址特性的总线模型之间的信息交互。
(一) 在实现抽象级别的总线模型时,提供了现成的可扩展的Transaction,来达到快速的信息交互。
(二) 当需要在两种不同协议之间增加转换桥或者适配层时,可以采用tlm_geneic_payload作为基础,它可以大大的降低实现成本 以及加快仿真速度。
既然tlm_genric_payload目的是为了给总线进行建模,所以它就包含了一些总线协议常用的信息,比如命令类型,地址,数据, 数据mask等等。接下来将主要介绍各个域的主要作用。

图2 –tlm_generic_payload关键属性描述表
2.4 VCS TLI适配层介绍
Synopsys仿真工具VCS在20011版本之后开始提供TLI适配层, 支持SystemVerilog与SystemC之间进行事务级别信息传递。如 下图所示[3]:

图 3 –VCS TLI接口结构图
其中SystemC端采用标准的TLM2.0接口与adaptor层进行 连接,同时SystemVerilog端采用VMM1.2中的TLM接口与 adaptor 层进行连接。传递的事务采用标准的OSCI TLM2.0标 准中的tlm_generic_payload[1]。
因此在进行功能覆盖率收集时,需要采用VCS TLI 适配层来作 为媒介将RM中的关键事务传递到验证环境中,进行覆盖率收集。
下面详细描述TLI Adaptor原理。由于功能覆盖率收集采用tlm_ analysis_port进行信息交互,因此描述TLI Adaptor原理时,采 用tlm_analysis_port作为例子进行阐述。

图 4 – TLI接口内部调用关系图
上图为SystemC层次到SystemVerilog层次的信息交互过程中,TLI adaptor层内部具体流程。 1用户调用Write函数。
用户在使用analysis_port前,需要对analysis_port实例化,并且给予唯一的实例化名字id,并且调用tli_tlm_bind函数将该 analysis_port函数与SystemC Adaptor进行对接,在SystemC Adaptor内部会例化一个tlm_analysis_subscriber接口,并且将该 接口与用户定义的tli_analysis_port进行对接,详细请参见2.5.3节。使用tlm_analysis_port时,当需要通过该port传递transaction 时,需要用户主动调用tlm_analysis_port::write()函数。其中transaction默认采用OSCI TLM2.0标准中的tlm_generic_payload类, 该class类定义参见2.3节。
2SC Adaptor 获得用户数据。 当用户调用write函数进行数据传递时,自动会调用adaptor层中tli_tlm_analysis_subscriber接口的write函数,Adaptor便通过该函数实时的获得用户需要传递的transaction,并且进行进一步处理,以满足DPI接口的处理。 3数据类转换。
由于tlm_generic_payload类比较复杂,不适合直接通过DPI接口进行传递,因此在Adaptor层定义了一个新的数据类,该数据结构 非常简单,非常适合在DPI接口中进行传递。该数据类内部主要包含一个大的FIFO,这里以SystemVerilog的代码为例说明。
class tlmpkt;
logic [7:0] PBA[];
int phase;
int delay; endclass
整个class类里只有一个非常的动态数组,这样在进行数据传递时,只需要传递该数组,没有额外的其他的信息,这样可以达到最 大的传输效率。
在DPI中进行数据传递的类并非用户看见的tlm_generic_payload,因此Adaptor层需要负责将用户需要传递的tlm_generic_payload 转换成tlmpkt格式。原理非常将单,只需要将tlm_generic_payload中各个变量按照顺序塞入PBA中即可。

图 5 – TLI内部数据类转化示意图
当SystemC侧tlmgp在通过DPI接口传输到Systemverilog侧的tlmpkt后,再按照入队的顺序从PBA中取出各个域,恢复出用户数据即可。 4将转换后的数据交给DPI
数据转换完成后,就需要通过DPI接口真正实现两种语言间的 数据交互了,adaptor中主要通过两部分动作实现
(一) 将转换后的数据通过upload函数上载到DPI函数中。
(二) 通过DPI函数tli_sc2sv_dpi_put通知SystemVerilog 侧,有 数据从SystemC处传入,请求SystemVerilog侧去取数据,同 时将用户定义的analysis_port的唯一的标识符ID作为该数据的 标识符传递给Systemverilog侧。至此SystemC 完成了所有工 作,接下来便交由SystemVerilog的adaptor层进行工作。
5SystemVerilog侧数据类转换在○4步当中,SystemC Adaptor调用了DPI函数tli_sc2sv_dpi_ put时,实际上是通过DPI接口调用了SystemVerilog Adaptor中 的tli_sc2sv_dpi_put函数。该函数完成如下操作:
(一) 从DPI函数中下载传输数据信息即tlmpkt。
(二) 将下载得到的tlmpkt数据,以及由SystemC侧传入的该数 据的标识符ID信息存入tli_interconnect中。其中ID信息用以告 知tli_interconnect进行数据区分。
(三) 由于一个analysis_port支持可以连接0个或者多个analysis_ subscriber中,而且对应TLM接口不止analysis_port一种接 口,所以adaptor在DPI接口后增加了tli_interconnect层, 用以缓存SystemC处传递过来的数据,并且根据用户的定 义的analysis_port的连接情况也即ID进行分发。其中tli_ interconnect就属于图3中描述的Global_Package.
6用户自定义analysis_port连接用户在搭建验证环境时,需要将vmm_tlm_analysis_export与 SystemVerilog Adaptor进行连接,同时指定需要连接的tlm_ analysis_port的唯一标识符ID,代码参见2.5.5节。 在调用连接 函数tli_tlm_bind时,SystemVerilog Adaptor会生成一个后台函 数,该函数会一直从tli_interconnect中主动读取符合要求ID的 数据。如果interconnect没有数据,则会一直阻塞,直到有数据到来。
当从tli_interconnect中获得符合要求的tlmpkt数据后, 需要 对该数据类进行转换,将其转换成通用的vmm_tlm_generic_ payload。转换过程非常简单,参见图2所示。
7调用用户实现的write函数。
用户在SystemVerilog侧需要扩展analysis_export的write 函 数,在write函数内部增加用户定义代码,详细代码参见2.5.4。 SystemVerilog Adaptor内部会定义一个vmm_tlm_analysis_ port,并且将该port与用户定义vmm_analysis_exprot对接, 在完成vmm_tlm_generic_payload数据类转换后,通过调用 vmm_analysis_port.write函数调用用户扩展的Write函数。
2.5 实现方案
上一节中,介绍了通过tlm_analysis_port接口以及TLI接口实现 两种语言间的信息交互的原理,本节将以一个具体实例说明在 SystemC中收集覆盖率的方案。
本例为实现标准的CRC算法的设计,实现CRC算法的参考模型 结构如下图所示。

图 6 –参考模型结构图
其中crc_sc包含crc_core,而crc_core又包含crc_calc,该三部 分逻辑采用C++语言进行编写,最后在最外层利用SystemC 语言包一层壳形成crc_sc_wrap。其中crc_calc模块为算法核心 逻辑,register模块为RM中的寄存器镜像,这两个模块为覆盖 率收集的主要模块,因此需要将该模块中的信息传递到VMM验 证环境中。下面介绍具体方法。
2.5.1 例化analysis_port
在crc_core层,实例化tlm_analysis_port,具体代码如下:
Typedef tlm::tlm_analysis_port crc_tlm_analysis_port;
Typedef tlm::tlm_generic_payload amba_payload;
#Define PORT_NUM 50
Class crc_core
{
Public:
crc_tlm_analyisi_port * analysis_port;
amba_payload *bus_payload;
….
….
Public:
crc_core( crc_sc *_bus_master)
{
analysis_port = new crc_tlm_analysis_port [PORT_NUM];
bus_payload = new amba_payload[PORT_NUM];
…..
}
}
这里生成了analysis_port的数组,因为对于功能覆盖率收集的需求有许多处,这里通过宏定义方式定义了50个接口。
2.5.2 调用write函数
接下来就是在需要调用功能覆盖率地方调用analysis_port的write函数,以在crc_calc中的calc_crc16以及calc_crc32的函数调用为 例。
#include “crc_calc.h”
#include “crc_register.h”
crc_calc::crc_calc(crc_core *_top)
{
top = _top;
}
unsigned char crc_calc::calc_crc16(unsigned char &data,unsigned int len,unsigned int crc_init_value)
{
…
…
unsigned char &data_temp; //定义新的一个数组
data_temp = new unsigned int [len];
memcpy(data_temp,data,len); //将原始数据拷入
data_temp[len] = calc_crc16 ; //将计算结果拷入
len ++;
top->bus_payload[0].set_data_ptr(data_temp); //通过port进行传递
top->bus_payload[0].set_data_length(len);
top->bus_payload[0].set_address(crc_init_value);
…
top->analysis_port[0].write(top->bus_payload[0]);
printf(“Crc Reference Model set a trans to fcov by calc_crc16 function “);
}
unsigned int crc_calc::calc_crc32(unsigned char &data,unsigned int len,unsigned int crc_init_value)
{
…
…
unsigned char &data_temp;
data_temp = new unsigned int [len];
memcpy(data_temp,data,len);
data_temp[len] = calc_crc32 ;
len ++;
top->bus_payload[1].set_data_ptr(data_temp);
top->bus_payload[1].set_data_length(len);
top->bus_payload[1].set_address(crc_init_value);
…
top->analysis_port[1].write(top->bus_payload[1]);
printf(“Crc Reference Model set a trans to fcov by calc_crc32 function “);
}
…
…
在传递信息时,需要将传递的关键信息转换成tlm_generic_payload的格式进行传递,因此对于不符合tlm_generic_payload格式的 信息,需要在调用write函数前进行一次格式转换。
转换可以仿造SystemC Adaptor层中数据类转换的思想,既然tlm_generic_payload中有一个数组data_ptr,那么可以将所有额外 的数据都塞入该数组当中,并且记录塞入的data_ptr数组的顺序。在上面例子里,需要将crc计算的原始数据以及最后的结果传输 到VMM验证环境中,因此重新定义了一个数组data_temp,然后将原始数据以及最后的计算结果一起存储到数组data_temp中, 然后再传入analysis_port中。

图 7 –类型转换示意图
2.5.3 实例化analysis_port
在RM的顶层模块中crc_sc_wrap中,将所有的analysis_port与Synopsys 仿真工具提供的DPI接口进行连接。注意这里需要编译Synopsys提供的的接口文件tli_sc_bindings.h文件。
#include “crc_sc.h”
#include “tli_sc_bindings.h” //必须include该文件,否则仿真 器不认识tli函数
#ifdef SC_DEBUG
#define DEBUG_MODE 3
#else
#define DEBUG_MODE 0
#endif
class crc_sc_wrap : public sc_module
{
public:
crc_sc *crc_inst0;
SC_HAS_PORCESS(crc_sc_wrap);
crc_sc_wrap(sc_module_name _name=”crc_sc_wrap”,int debug_mode = DEBUG_MODE)
: sc_module(_name)
{
crc_inst0 = new crc_sc(“crc_sc”);
tli_tlm_bind_analysis_parent(crc_inst0->m_crc_core->analysis_port[1],”crc32_port”);
tli_tlm_bind_analysis_parent(crc_inst0->m_crc_core->analysis_port[0],”crc16_port”); //将对应的 analysis_port连接到DPI接口,然后定义一个例化的名字,供将来VMM验证环境侧进行调用。
…
}
…
…
}
在本阶段调用了VCS工具提供的函数tli_tlm_bind_analysis_parent,该函数描述如下:
tli_tlm_bind_analysis_parent (tlm::tlm_analysis_port<> socket,std::string unique_id,bool debug_en)
其中socket为需要进行连接的analysis_port, unique_id为该特定analysis_port的唯一识别名字,用以将来在SystemVerilog侧进行 查找该analysis_port的识别号。
2.5.4 在VMM验证环境侧实现覆盖率收集
接下来就是在VMM验证环境中实现功能覆盖率收集,这里采用一个单独的class来实现覆盖率收集。
class crc_rm_fcov extends vmm_group;
typedef enum {CRC16,CRC32,OTHER}crc_type_enum;
crc_type_enum crc_type;
bit [31:0] crc_init_value;
bit [31:0] result;
`vmm_tlm_analysis_export(_1) //这里可能需要两个analysis port
`vmm_tlm_analysis_export(_2) //因此通过这个语句进行例化
vmm_tlm_analysis_export_1 #(crc_rm_fcov,vmm_tlm_generic_payload) socket_1;
vmm_tlm_analysis_export_2 #(crc_rm_fcov,vmm_tlm_generic_payload) socket_2;
covergroup cg_crc_type;
CrcType : coverpoint crc_type;
InitValue : coverpoint crc_int_value {
bins min_init = {32’h0};
bins max_init = {32’hffff_ffff};
bins mid1_init = {[32’h1:32’h0fff_ffff]};
bins mid2_init = {[32’h1000000:32’hffff_ffff]};
}
CrcTypeInit : cross CrcType ,InitValue;
endgoup
function new (string name ,vmm_unit parent = null);
super.new(get_typename(),name,parent);
socket_1 = new(this,”crc32”);
socket_2 = new(this,”crc16”);
cg_crc_type = new;
endfunction
virtual function write_1 (int id = -1,vmm_tlm_generic_payload trans);
`vmm_note(this.log,trans.psdisplay(“from Reference Model calc_crc32 function”));
crc_type = CRC32;
crc_init_value = trans.m_address;
result = trans.m_data[trans.m_length - 1];
cg_crc_type.sample();
endfunction
virtual function write_2 (int id = -1,vmm_tlm_generic_payload trans);
`vmm_note(this.log,trans.psdisplay(“from Reference Model calc_crc32 function”));
crc_type = CRC16;
crc_init_value = trans.m_address;
result = trans.m_data[trans.m_length - 1];
cg_crc_type.sample();
endfunction
endclass
由于该功能覆盖率需要连接两个analysis_port,因此需要调用VMM提供的宏定义,生成两个analysis_port。
2.5.5 连接analysis_port
完成覆盖率模型的收集后,就到了最后一步,即将analysis_export与SV adaptor进行对接,连接在vmm_env类中完成。
`include “tli_sv_binds.sv” //必须Include该文件,否则无法调用SV adaptor
vmm_tlm_wrapper #(vmm_tlm_generic_payload) wrapper = new(); //例化SV adaptor
class crc_env extends vmm_group;
…
crc_rm_fcov CrcRmFcov;
…
function void build_ph();
…
CrcRmFcov = new(“CrcRmFcov”,this);
endfunction
function void connect_ph();
….
wrapper.tli_tlm_bind(CrcRmFcov.socket_1,vmm_tlm::TLM_ANALYSIS_PORT”
,”crc32_port”);
wrapper.tli_tlm_bind(CrcRmFcov.socket_2,vmm_tlm::TLM_ANALYSIS_PORT”
,”crc16_port”); //将socket_1与Rm中的analysis_port进行连接。
endfunction
…
endclass
至此便可运行环境,进行覆盖率收集,最后通过VCS自带的URG工具生成覆盖率报告,并且进行分析覆盖情况。

3. 结论及建议
Synopsy公司提供的VCS在2011版本之后开始提供TLI adaptor,通过利用标准的TLM2.0接口连接到该adaptor层,使得两种语言 间可以非常方便的进行事务级信息传递。因此可以将ESL Model中的关键信息通过TLM接口传递到VMM验证环境处,然后所有的 功能覆盖率全部在验证环境处进行收集,这样便可以利用Synopsys提供各种工具(DVE,VMM Planner等)对覆盖率进行分析以及 统计,真正实现ESL Model与VMM验证方法学的融合。
4. 参考文献
[1] OSCI TLM2.0 User Manual
[2] VMM Standard Library User Guide (VMM1.2 release)
[3] VCS LCA Features (201103 Version)。



