路科验证(Rocker IC)专注于验证系统思想和前沿工程资讯,拥有一支活跃的技术原创团队,著有《芯片验证漫游指南》一书,致力为高校微电子相关专业学生与IC从业人员提供技术食粮。 您可以在手机移动端同步关注微信订阅号“路科验证”或是登录网页www.rockeric.com了解更多资讯。如果您需要联系我们,请发送邮件至 bin.rocker.liu@intel.com 。

SV及UVM高级话题篇之四:OVM到UVM的移植

上一篇 / 下一篇  2018-06-12 21:05:15 / 个人分类:验证系统思想

在过去的几年中,UVM在几大EDA公司不遗余力的推广和工具的支持下,迅速成为了verifier的必备技术之一。而对于那些大公司,那些有深厚技术储备的公司,他们在验证技术的演变中一直在扮演着迁徙者的角色,即从水草贫乏之地(以前不是这样的)迁完丰饶的地方。这种迁移譬如当年的VMM到OVM、或者OVM到UVM等等。而我们今天要介绍的OVM到UVM的转化,更贴近目前的演化阶段。在OVM迁移到UVM时,不少验证组都会评估代码迁移的好处和代价,如果有更多的好处,更少的代价,那是最好的。


首先来看看,如果迁移后的好处是什么?

  • 主流的仿真器都将支持重心迁移到UVM,而OVM的特性则不再主要考虑,这意味着后者最终会停止更新和支持。

  • UVM的寄存器模型包现在成为了验证领域的主流,而之前OVM一侧的各个第三方的寄存器模型包不具有共通性,另外OVM在后期也本身引入了UVM的寄存器宝。

  • 主流的VIP都是以UVM为主,从VIP的数量来看,UVM也逐渐占据多数,与OVM的VIP数量拉开了距离。

  • 从接下来五年的验证潮流来看,目前找不到直接的竞争对手会取代UVM,这就使得在迁移后整个团队保持了与最新技术的联系。


造成OVM与UVM在2013年前后势均力敌的原因之一在于,他们在2013前后的受欢迎度是不分伯仲的。这也使得早年应用OVM的人,已经习惯了OVM,而UVM之初仅仅是迁移了OVM的所有特性并没自己什么个性的时候,这些OVM用户并没有做出要迁移到UVM的决定。然而各领风骚四五年,UVM而今已经不容置疑地统一了动态仿真界,在这一形势下,即便UVM并不会从技术上有太多突破,但在外部资源、技术更新上的考虑,越来越多的人选择了迁往UVM,或者将新的UVM VIP置入到原有的OVM环境中,做两种方法学的混合仿真(这一点我们会在下一节《OVM与UVM的混合仿真》谈到)。


那做了迁移的决定之后,我们需要考虑,什么样的方法会使得迁移的成本最小呢?

  • 在迁移之前,我们需要审视现有的OVM环境代码,得出哪些方式在UVM中已经被废弃了(不建议使用)。我们应该替换掉这些废弃代码,转而使用推荐的代码方式。

  • 一个合适的脚本可以帮助我们完成批量处理。

  • 在脚本帮助完成转化之后,我们还需要再次完成编译和回归测试,确保转换前后的回归测试结果是一致的。

  • 在转换过程中,我们应当遵守单元测试的原则,先完成从OVC到UVC的转化和测试,再完成OVM顶层转换到UVM顶层转换和测试的目标,逐级完成转换和测试。


在2013年初,Verilab公司的Litterick Mark(首席验证顾问)就推出了关于如何实现从OVM到UVM的自动化转化的论文和相关脚本。在他的论文中提出利用脚本实现从OVM到UVM转换的四个基本步骤:

  • 检视原有的OVM代码,移除已经废止的用法和不建议的用法。

  • 通过脚本实现从OVM到UVM的一些通用关键词的转换(批量处理)。

  • 通过脚本或者手动修改的方式将OVM的编码方式修改为UVM的吧编码方式。

  • 使用新的UVM的特性。


而在具体操作过程中,上面四个步骤,Litterick Mark又分别给出了四个脚本来对应,通过这些脚本,或者可以帮助检查出需要修改的地方,或者可以帮助用户直接完成替换。下面的表格中,可以看到通过脚本首先可以生成一些报告,指出代码中需要修改的部分,继而再通过后续脚本进行替换或者手动替换。关于这些脚本,读者可以在本书对应的目录中找到。


在推出这一方式之后,UVM委员会考虑并吸取了这一思想和部分脚本,经过简化之后,在目前的UVM-1.2安装包中,用户通过$UVM_HOME/bin/ovm-to-uvm10.pl完成上述步骤的第二部,即通过批量处理完成OVM关键词到UVM关键词的转换,而更多的特性用法则需要用户自己检视代码和手动修改。因此,目前UVM官方给出的转换步骤进化为了下面的方式:

  • 用户需要自己检视代码(而不再是通过脚本来检视和报告)

  • 通过脚本完成基本的关键词转换

  • 完成从OVM phase到UVM phase的编码转换

  • 抛弃原有的OVM objection方式转而使用UVM objection编码方式

  • 用UVM configuration替换原有的OVM configuration方式

  • 添加新的UVM特性


可以看到UVM委员会给出的转换方式舍弃了之前Litterick建议的脚本检视和后续的自动替换,而只保留了最“安全”的替换基本关键词的脚本,例如ovm_component到uvm_component的转换。而路桑通过实际的转换经验,建立读者有需要的情况下,使用“中和”的方式:

  • 通过脚本完成检视,替换掉已经废止和不建议的OVM用法。

  • 通过脚本完成从OVM到UVM的基本关键词转换。

  • 剩余的部分考虑到需要更多的语义和结构分析,建议进行手动替换。


接下来,我们就这一建议的方式来给出大致的操作步骤,而相关的脚本读者可以在本书附赠的源代码包中找到。


OVM代码检视

这一环节中的脚本ovm_audit.pl的作用是用来检视OVM代码和生成报告:

  • 指出已经被废止的OVM代码

  • 指出不建议使用的代码

  • 指出在OVM中有效而在UVM已经废除的代码


检视脚本的作用是用来生成报告,并不会修改代码本身。而下进行下一个步骤之前,用户应该根据检视报告来修改那些不合适的代码,在修改完之后用户可以在OVM环境中编译运行,确保修改不影响原有的功能。下面是脚本的使用范例:

audit one file:

% ovm_audit.pl file.svh

audit a set of files:

% ovm_audit.pl *.sv*

audit a whole tree of files:

% find . -name "*.sv*" | xargs -r ovm_audit.pl


这些关键词在OVM-2.1.2中已经被废止或者不建议使用:

  • ovm_test_top

  • ovm_top_levels

  • ovm_phase_func

  • post_new_ph

  • export_connections_ph

  • import_connections_ph

  • configure_ph

  • pre_run_ph

  • ovm_find_component

  • ovm_print_topology

  • avm_

  • global_reporter

  • ovm_threaded_component

  • do_sprint

  • print_unit

  • do_test

  • m_do_test_mode

  • do_task_phase

  • set_parent_seq

  • get_parent_seq

  • seq_item pre_do

  • seq_item body

  • seq_item mid_do

  • seq_item post_do

  • seq_item wait_for_grant

  • seq_item send_request

  • seq_item wait_for_item_done

  • start_sequence

  • OVM_REPORT_

  • ovm_seq_prod_if

  • ovm_seq_cons_if

  • ovm_seq_item_prod_if

  • ovm_seq_item_cons_if

  • ovm_virtual_sequencer

  • ovm_scenario

  • check_connection_size

  • do_display

  • absolute_lookup

  • relative_lookup

  • find_component

  • get_num_components

  • get_component

  • do_set_env

  • m_env

  • add_to_debug_list

  • build_debug_lists

  • m_components

  • m_ports

  • m_exports

  • m_implementations

  • ovm_report_message

  • report_message_hook


OVM到UVM的代码自动转换

在完成上面的检视和修改之后,我们可以运行UVM安装包自带的脚本ovm-to-uvm10.pl,这一脚本会将.sv/.svh后缀扩展名文件代码中的OVM关键词转换为UVM关键词。通过这个脚本我们可以完成下面的自动化替换:

  • 在.sv/.svh后缀名文件中的ovm字符会被替换为uvm字符。

  • 文件名本生含有ovm的文件会被替换为uvm。

  • 一个替换记录报告文件会生成,其中记录所有的替换历史即转换前后的不同。

  • 用户可以通过--backup选项来决定是否需要备份替换前的文件,或者通过--write来决定是否需要直接在原文件上修改。

  • --all_text_files选项会将目录中其它的文件例如脚本文件中的ovm关键词也替换为uvm。


替换OVM phase方法

在完成了上面的基本替换之后,我们还需要考虑哪些OVM中的特性无法只通过替换关键词来实现转换步骤的。接下来在第三个步骤中需要替换OVM phase方法。下面是OVM和UVM中的一段例码:


OVM中phase的代码

class my component extends ovm_component      `ovm_component_utils(my_component)    

  ...   

  extern function void build();   

  extern function void connect();   

  extern task run; 

endclass 

 

function void my_component::build();    

    super.build();   

    ... 

  endfunction 

  ...


UVM中phase代码

class my component extends uvm_component

  `uvm_component_utils(my_component)   

  ...  

  extern function void build_phase(uvm_phase phase);  

  extern function void connect_phase(uvm_phase phase);  

  extern task run_phase(uvm_phase phase);

endclass 

 

function void my_component::build_phase(uvm_phase phase);

  super.build_phase(phase);  

  ...

endfunction 

...


对于两种方法学都熟悉的用户可以从上面的不同中看出,在迁移到UVM之后,建议将原有的phase方法名添加后缀“<name>_phase”,同时给添加参数“uvm_phase phase”。


替换OVM objection方法

在UVM中,原本在OVM中适用的stop()和global_stop_request()方法已经废弃,取而代之的是通过在phase阶段中挂起objection来控制仿真的结束。例如,在原来顶层的OVM test中,会使用global_stop_request()在序列发送完来结束仿真,而在UVM中需要使用uvm_phase来控制。


OVM中使用global_stop_request

task run();   

  seq.start( m_virtual_sequencer );   

  global_stop_request(); 

endtask


UVM中使用uvm_phase

task run_phase( uvm_phase phase );

  phase.raise_objection( this );  

  seq.start( m_virtual_sequencer );  

  phase.lower_objection( this ); 

endtask

类似地,以前通过uvm_test_done对象来实现全局控制结束的方式,在UVM中也交给了uvm_phase的控制,这就使得objection机制保持了风格统一。


OVM中使用ovm_test_done

task run();   

  ovm_test_done.raise_objection( this );   

  seq.start( m_virtual_sequencer );   

  ovm_test_done.drop_objection( this ); 

endtask


UVM中使用run_phase

task run_phase( uvm_phase phase );

  phase.raise_objection( this , "started sequence" );  

  seq.start( m_virtual_sequencer );  

  phase.drop_objection( this , "finished sequence"); 

endtask


替换OVM configuration方法

习惯于OVM配置方法的用户,在配置过程中都使用的是[set,get]_config_[int,string,object]来在组件层次中实现配置,尽管在UVM中这些方法仍然有效,但我们并不建议再使用旧有的方法,原因有二。第一是,原来在OVM中并不支持interface通过配置的方式直接传递interface,也因为这一种限制使得interface不得不找到其它的ovm_object wrapper来将其二次包装继而通过object的配置方式实现接口传递,这种传递也是OVM的配置中为人诟病的一个地方,在UVM中interface也被纳入了uvm_config_db传递的可行参数中,与UVM的配置方式完全兼容了;第二是,在以前set_config与get_config的配置不是成对出现的,这增加了UVM新手的调试难度,如果一些域已经通过OVM宏注册,那么就可以通过隐性的方式获取上层的配置数值,尽管这一点在UVM中也保留下来了,但我们依然建议通过uvm_config_db::set/get的方式来实现成对的配置,这使得配置的方式易于学习和调试。下面是分别是OVM和UVM的配置方式:


OVM set/get config配置方式

class my_env extends ovm_env;  

  ...  

  function void build();    

    ahb_cfg = ahb_config::type_id::create("ahb_cfg");    

    ahb_cfg.width = 16;    // set additional fields    

    set_config_object("*","ahb_cfg",ahb_cfg);  

  endfunction  

  ... 

endclass 


class my_ahb_agent extends ovm_component;  

  ...  

  function void build();    

    ovm_object cfg;    

    ahb_config my_cfg;    

    assert(get_config_object("ahb_cfg",cfg,0);    

    if (!$cast(my_cfg, cfg))      ovm_report_error(...);    

    ...  

  endfunction  

  ... 

endclass


UVM config_db set/get配置方式

class my_env extends uvm_env;

  ...

  function void build();    

    ahb_cfg = ahb_config::type_id::create("ahb_cfg");    

    ahb_cfg.width = 16;    // set additional fields    

    uvm_config_db#(ahb_config)::set(            

      this,"ahb_agent","ahb_cfg",ahb_cfg);  

  endfunction

  ... 

endclass 

 

class my_ahb_agent extends uvm_component;  

  ...

  function void build();    

    ahb_config my_cfg;    

    if (!uvm_config_db::ahb_config::get(            

        this,"","ahb_cfg",my_cfg);     

      `uvm_error(...)    

    ...  

  endfunction

  ... 

endclass


添加UVM的新特性

另外到了添加UVM新特性的部分,用户需要考虑修改下面的这些部分(但不是必须):

  • 将OVM中的信息报告部分中的函数ovm_report_info/warning/error/fatal替换为宏`uvm_info/warning/error/fatal。这一点建议我们在之前的UVM宏比较中提到过,考虑到后者宏的性能更好,我们建议用户使用信息报告宏的方式。

  • 在上一个步骤的配置替换中,我们也提到了,尽管OVM中interface的传递通过object进行二次封装可以实现,但由于其复杂度在UVM中不再需要,因此在允许的情况下用户可以考虑使用config_db来简化interface的传递。


此外关于sequence和sequencer的使用,从OVM到UVM的迁移中也有很多的变化。譬如下面这些关键词已经在UVM中被废止:

OVM sequence宏:

  • ovm_sequencer_utils

  • ovm_sequencer_param_utils

  • ovm_sequence_utils

  • ovm_declare_sequence_lib

  • ovm_update_sequence_lib

  • ovm_sequence_lib_and_item


OVM内建sequence:

  • ovm_random_sequence

  • ovm_exhaustive_sequence

  • ovm_simple_sequence


OVM sequence API方法:

  • seq_kind

  • num_sequences

  • get_seq_kind

  • get_sequence

  • do_sequence_kind

  • get_sequence_by_name


OVM sequencer的'count'和'default_sequence'的相关机制:

  • count (in context of set_config)

  • max_random_count

  • max_random_depth


上面这些已经废止的方法,到了UVM一侧有了新的特性和使用方式,接下来我们就UVM新特性中的重点,关于UVM sequence和sequencer的新特性给出详细解释。


sequence的变化

由于`ovm_sequence_utils不再存在,因此需要将其替换为`ovm_object_utils,调整前后的代码如下:

调整前:

class my_seq extends my_base_sequence #(my_seq_item);

  `ovm_sequence_utils(my_seq, my_sequencer)

调整后:

class my_base_seq extends ovm_sequence #(my_seq_item);

  `ovm_object_utils(my_seq)

  `ovm_declare_p_sequencer(my_sequencer)


sequencer的变化

类似地,`ovm_sequencer_utils宏也已经废止,需要将其替换为`ovm_component_utils,调整前后的代码如下:

调整前:

class my_sequencer extends ovm_sequencer #(my_seq_item);

  `ovm_sequencer_utils(my_sequencer)

  `ovm_update_sequence_lib_and_item(my_seq_item)

调整后:

class my_sequencer extends ovm_sequencer #(my_seq_item);

  `ovm_component_utils(my_sequencer)


环境的变化

在上面提到了,跟sequencer相关的'count'和'default_sequence'用法也已经废止,这一点用户需要额外注意。

调整前:

class my_env extends ovm_env;

  set_config_int("*.i_sequencer", "count", 0);

  set_config_string("*.i_sequencer","default_sequence","my_seq");

调整后:

class my_env extends ovm_env;


test执行root sequence的变化

既然已经移除了'count'和'defualt_sequence'用法,用户则不再需要关心默认的序列如何指定。而伴随着这一特性的改变,在顶层test中,我们不再依赖于default_sequence来指定root sequence,而可以通过sequence自带的方法start来实现sequence挂载到sequencer上。

调整前:

class tc_test_seq extends my_test_base_seq;

...


class my_test extends ovm_test;

  virtual function void build();

    super.build();

    set_config_string("top_env.i_top_sequencer",

      "default_sequence", "tc_test_seq");


调整后:

class tc_test_seq extends my_test_base_seq;

...

class my_test extends ovm_test;

  tc_test_seq test_seq;

  virtual task run();

  super.run();

    test_seq = tc_test_seq::type_id::create("test_seq");

    test_seq.start(top_env.i_top_sequencer);


在介绍完了OVM到UVM的迁移之后,如果读者现在有完整的OVM环境且有需要向UVM迁移的计划,那么可以考虑通过上面详细的步骤和建议完成迁移。如果受限制与项目的人力、节点和技术等多个因素,无法完成短时间的迁移,但又不能避免OVM与UVM之间的混合仿真,那么请关注我们下一节《OVM与UVM的混合仿真》。


谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。



TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

路科验证

路科验证

路科验证(Rocker IC)专注于验证系统思想和前沿工程资讯,拥有一支活跃的技术原创团队,为高校微电子相关专业学生与IC从业人员提供技术食粮。 您可以在手机移动端同步关注微信订阅号“路科验证”。如果您需要联系我们,请发送邮件至 rocker.ic@vip.163.com 或者 bin.rocker.liu@intel.com 。

日历

« 2018-06-21  
     12
3456789
10111213141516
17181920212223
24252627282930

数据统计

  • 访问量: 114732
  • 日志数: 221
  • 建立时间: 2016-06-25
  • 更新时间: 2018-06-14

RSS订阅

Open Toolbar
博聚网