set_report_max_quit_count/set_timeout

3.UVM基础3.1uvm_component与uvm_objecta.uvm_component本质上也是派生自uvm_objectb.uvm_component有两大特性是uvm_object所没有的:一是在build_phase中通过在定义new的时候以及调用构造函数new时指定parent参数来形成一种树形组织结构,二是有phase的自动执行特点。c.transaction派生自uvm_sequence_item而不是uvm_transaction,sequence派生自uvm_sequence。d.与uvm_component相比,uvm_monitor,uvm_scoreboard,uvm_env,uvm_test都没有做任何扩展;  uvm_driver增加了5个成员变量;uvm_sequencer做了相当多的扩展;agent的使用主要是从可重用性的角度来考虑的,如果在做验证平台时不考虑可重用性,那么agent是可有可无的,与uvm_component相比,uvm_agent最大的改动在于引入了一个变量:is_active;UVM并没有针对reference model定义一个类,所以ref model都是直接派生自与uvm_component。e.由于uvm_component作为UVM树的结点存在的,这一特性是它失去了uvm_object的某些特性,一个是无法使用clone函数,如定义了一个类的两个指针,进行第二个类clone时无法指定parent参数,因为只能在new中指定parent参数;另一限制是同一个父结点下的不同component不能使用相同的名字。f.为什么UVM中会分为uvm_component与uvm_object两个大类呢?  因为UVM吸收了先分类再管理的这种哲学,如自然界的生物分为动物和植物。g.uvm_component在仿真中是一直存在的,但是像transaction发送完就没有必要存在了。3.2 UVM的树形结构a.为什么UVM使用树形的组织结构呢?  因为作为一个验证平台,它必须能够掌握自己治下的所有“人口”,只有这样才能管理大家统一步伐做事情,而不会漏洞谁,而树形结构是这种管理的一种比较简单的方式。b.UVM一般会在component内部维护一个数组m_children,当其中实例化子类时,会把子类的指针加入到该数组中,只有这样,该component才知道自己有哪些孩子。c.UVM树的叶子很好判断,但是UVM中真正的树根不是uvm_test_top,二是uvm_top,它是一个全局变量,是uvm_root唯一的一个实例。d.如果一个component在实例化时,其parent被设置为null,那么这个component的parent将会被系统设置为系统中唯一的uvm_root的实例uvm_top。所以我们在代码中testcase的new函数将parent设置为null,而其他的在new时都传入this。e.uvm_root的存在可以保证整个验证平台中只有一棵树,所有节点都是uvm_top的子结点。f.get_parent(),get_child(),get_children(),get_num_children()。​3.3 field automation机制a.提供如下函数:copy, compare, pack_bytes, unpack_bytes, pack,unpack, pack_ints, unpack_ints,print,cloneb.对于某个变量,可以用标志位控制是否执行上面列出的功能。3.4UVM中打印信息的控制a.UVM支持用一个函数设置某component输出信息的冗余度阈值,以及递归设置。b.UVM支持当UVM_ERROR达到一定数量时结束仿真,如在base_test的build_phase中设置UVM_ERROR为5时结束仿真:   set_report_max_quit_count(5);c.不同的仿真器支持不同方式的断点设置,UVM支持内建的断点功能,当执行到断点时,自动停止仿真,进入交互模式:   env.i_agt.drv.set_report_severity_action(UVM_WARNING,UVM_DISPLAY|UVM_STOP);此句作用是:当drv中出现UVM_WARNING时,立即停止仿真,进入交互模式。d.UVM支持将不同严重性信息输出到不同的log文件。e.四种严重性信息有很多种行为,如UVM_DISPLAY, UVM_STOP, UVM_COUNT, UVM_EXIT等。3.5config_db机制a.config_db机制用于在UVM验证平台间传递参数b.set函数的第一个和第二个参数联合起来组成目标路径,第一个参数一般写this,第二个参数写目标component相对于该component的路径c.get函数的前两个参数也是联合起来组成目标路径,不过一般情况下,get的第一个参数写this,第二个写"",即置空。如果get的第一个参数写null, UVM会自动把第一个参数替换为uvm_root::get(),也就是树根uvm_top。d.component中可以省略某变量的get语句,有三个条件:第一是该component必须使用factory机制注册,第二是该变量必须使用field automation机制注册,第三是调用set时其第三个参数必须与该变量名字一致。e.假设有两个component向一个component set参数,其set的第一个参数都是this,那么谁的层次高,就get谁的值,这叫跨层次的多重设置f.验证中写代码的一个原则是同样的语句只在一个地方出现,尽量避免在多个地方出现。g.在一个component向另一个component用config_db设置了多次,那么目标以最近一次收到的参数为主,这叫做同一层次的多重设置。h.直线设置,如test到env到agt到drv,非直线的设置,如scb向driver设置一个变量。i.直线获取,drv get任何component给它的设置都是直线获取,非直线获取,如在ref获取其他component设置给drv的参数。j.一般要尽量避免非直线的设置,因为agt和scb的build_phase谁先执行并不一定。k.非直线的获取在某些情况下避免了config_db::set的冗余,因为如果也用直线设置给ref相同的参数,就写了多个config_db::set​语句,那么会增大出错的可能性。l.config_db set时支持通配符,但是并不推荐这么做,因为通配符的存在使原本非常清晰的设置路径变的扑朔迷离,导致代码难于维护。m.set的第二个参数是字符串,如果写错,就不能正确地设置参数,UVM和仿真器都不会提供任何错误提示,因为它是字符串!这个问题经常使验证人员感到困扰,很多有经验的验证人员也深受其害。针对这种情况,UVM提供了一个函数check_config_usage(),它可以显示出截止到次函数调用时有哪些参数是被设置过但是却没有被获取过,由于config_db的set和get一般都用于build_phase阶段,所以此函数一般在connect_phase被调用。使用方式:uvm_top.check_config_usage();n.UVM还提供了print_config(1)函数用于找出哪些被设置过的信息对于它们是可见的,1表示递归查询,若为0,只查询当前component的信息。

4.UVM中的TLM1.0通信a.UVM的TLM起源于SC的一种通信标准b.UVM的TLM共有两个版本,TLM1.0和TLM2.0c.TLM中的三种操作:put,get,transport(相当于一次Put和一次get),动作发起者的端口是port,动作接收者的端口是exportd.动作发起者的三种操作按照阻塞和非阻塞分类总共有9种port,名称中不含blocking和nonblocking的则表示既可以用做阻塞也可以用做非阻塞。另外,peek用于主动获取数据(复制),get_peek集合了get和peek两个操作,所以动作发起者总共有15种port。e.动作接收者对应有15种export。f.port和export体现的是一种控制流,port具有高优先级,export具有低优先级,只有高优先级的端口才能向低优先级的端口发起三种操作g.port与export的连接:        1. A.A_port.connect(B.B_export),只有动作发起者才能调用connect函数,而动作接收者作为connect的参数        2.port在new时第一个参数是名字,第二个参数是其parent,另外还有两个参数,min_size表示必须连接到这个port的下级端口数量的最小值,max_size则表示最大值,这两个参数默认都是1。        3.IMP才是UVM中的精髓,在UVM中只有IMP才能作为连接关系的终点,如果是port或者export作为终点则会报错;对应15种port和export有15中imp;IMP的优先级最低。        4.因此g.1中的连接还没有完,在B中需要定义一个imp,并且在其connect_phase中将expoort与imp连接起来:B_export.connect(B_imp);同时还需要在B中实现一个put函数(与A中A_port的put同名);当A中调用A.A_port.put()时,B中的put就会自动执行。        5.imp在定义时需要指定两个参数类型,一个是传输类型,一个是imp所在的component类名(因为最终的操作函数在该类中实现)        6.port恰如一道门,export也是如此,因为只是一道门,没有存储作用,除了转发操作之外不能做其他操作,因此这次传输一定要由B_export后续的某个组件进行处理,在UVM中完成这种后续处理的也是一种端口:IMP。m.port可以直接与imp连接n.export也可以与imp连接,上面的export与imp的连接是export作为中间环节,这里指export作为起点。o.比如A是B的parent,A中的port可以与B中的port连接起来,export也是类似。p.blocking_put和blocking_transport端口的使用,nonblocking端口的使用。q. 上面的内容总结为:动作发起者和接收者,三种操作,45种端口及其连接。r.UVM中的analysis端口        1.UVM中还有三种特殊的端口:analysis_port, analysis_export, analysis_imp。       2.默认情况下analysis_port可以连接多个IMP,而put和get端口默认是1对1,除非改参数。        3.analysis端口没有阻塞和非阻塞的概念,因为它本身就是广播。        4.对于put端口有put,try_put和can_put三种操作,而analysis只有一个操作:write。s.fifo        1.本质是:一块缓存加两个IMP,fifo左边控制流和数据流都是从mon到fifo,fifo右边控制流是从scb到fifo–数据流是从fifo到scb。        2.默认的缓存大小是1        3.有两种fifo        4.fifo中有很多export,其本质是imp        5.fifo的flush函数用于清空其中缓存的数据

  1. UVM验证平台的运行5.1 phase机制a.function phase不消耗仿真时间,task phase消耗仿真时间,run_phase和12个小的run-time phase是task phase。build_phase/connect_phase/end_of_elaboration_phase/start_of_simulation_phase;extract_phase/check_phase/report_phase/final_phase。b.对于同一个component,从build_phase依次执行到final_phase结束仿真,其中run_phase和12个小的run-time phase是并行执行的。c.一般的验证方法学都会把仿真分成不同的阶段,但是这些阶段的划分通常没有UVM划分的这么多、这么细,所以一般来说当其他验证方法学向UVM迁移的时候,总能找到一个phase来对应原来方法学中的仿真阶段。d.12个小的run-time phase是UVM中才引入的,其他phase在OVM中已经存在;为什么引入12个小的run-time phase呢?分成小的phase是为了实现更加精细的控制;reset/configure/main/shutdown是核心。e.UVM的设计哲学就是在build_phase中做实例化的工作,如果在其他phase中实例化uvm_component,那么系统会报错。f.对于不同的component,除了build_phase之外,所有不耗费仿真时间的phase都是自下向上执行的。g.对于同一层次的component其执行顺序是如何的呢? UVM源代码的设计方式是按照字典序,即根据他们在new时指定的名字,假如drv实例化时指定名字为aaa, mon实例化时指定bbb,则某一阶段drv的各phase先执行。h.对于不同的component,其task_phase是自下而上启动,然后同时在运行。i.同一时刻,所有component都处于同一个阶段的phase,只有当所有component的某阶段phase都完成之后,才能进入下一阶段的phase。j.对于叔侄关系的component其build_phase是如何执行的呢?  UVM采用深度优先的原则,即i_agt的build_phase执行完毕后,接下来执行driver/monitor/sequencer的build_phase,当全部执行完毕后再执行scb的build_phase。k.super.xxx_phase意思是执行该component父类中定义的一些操作,从UVM库中派生出来的类,只有build_phase中有一个自动获取参数的操作,对于其他派生自库中component,完全可以去掉super.xxx_phase。但是测试用例派生自base_test除过,因为这个base_test是用户自己定义的,其中可能定义了一些具体的操作。l.之前在drv中如果获取接口失败则用uvm_fatal直接退出,其实用uvm_error也会退出并提示出uvm_fatal,因为UVM定义在end_of_elaboration_phase及其之前的phase中出现UVM_ERROR的话,那么UVM就认为出现了致命的错误,会调用uvm_fatal结束仿真。m.上面的这个特性在小型设计中体现不出优势,但是在大型设计中非常有用,因为大型设计真正仿真之前的编译和优化可能会花费几个小时的时间,因此在获取参数后使用uvm_error可以将所有类似问题一次性暴露出来,然后统一修复,再去编译。n.在driver的main_phase中可以通过phase.jump()进行跳转,向前最多跳转到pre_reset_phase,向后则可以跳到final_phase。o.在不同时间做不同的事情,这就是UVM中phase的设计哲学,phase的引入在很大程度上解决了因代码顺序杂乱可能会引发的问题。p.UVM提供命令行参数UVM_PHASE_TRACE来对phase机制进行调试,这样就不用每次都是用uvm_info来打印调试。q.可以通过uvm_root的set_timeout函数设置超时退出时间: 在base_test的build_phase中  uvm_top.set_timeout(500ns,0);   如果达到500ns测试用例还没运行完的话则会给出一条uvm_fatal的提示信息并退出仿真;参数0表示是否可以被其后其他的set_timeout语句覆盖。5.2objection机制a.在进入某一phase时,UVM会收集此phase阶段提出的所有objection,并且实时监测所有Objection是否已经被撤销了,当发现所有都已经撤销,那么就会关闭此phase,开始进入下一个phase。b.如果UVM发现此phase阶段没有提起任何objection,那么将会直接跳转到下一个phase中。c.UVM用户一定要注意,如果想执行一些耗费时间的代码,那么要在此phase下任意一个component中至少提起依一次bjection。(经过实验证明,不提起objection是可以进入该phase,但是不会执行消耗仿真时间的代码)d.对于run_phase,如果在12个小的run-time phase中有提起过objection,那么它的代码就会被执行;如果只在run_phase中提起objection而12个小的没有,那么只会执行run_phase的代码不会执行这12个run-time phase的代码。e.为什么所有phase在定义时括号里都有一个phase参数,其实就是为了方便phase.raise_objection和drop的。f.在build_phase等function phase中也可以raise_objection,仿真器也不会报错,但是一般不这么做,因为objection的引入是为了解决何时结束仿真的问题,更多的是面向task phase。g.UVM的设计哲学就是全部由sequence来控制激励的生成,因此一般情况下在sequence中控制objection。h.在base_test的main_phase中用phase.phase_done.set_drain_time(this,200)设置drop_objection之后等待多久再进入post_main_phase;每一种phase对应一个set_drain_time。i.UVM提供命令行参数进行objection调试,它会在raise和drop objection时打印出log信息。5.3 domain的应用a.domain是UVM中一个用于组织不同组件的概念,默认情况下,验证平台中所有的component都位于一个名字为common_domain的domain中。b.假设DUT分为两个相对独立的部分,这两个部分可以分别复位、配置和启动,那么可以用domain将其对应的组件划分开。c.domain只能隔离run-time的phase,对于其他phase,其实还是同步的。d.多domain中phase的跳转只在其domain中进行。​​
    6.​UVM中的sequence6.1 sequence基础a.使用sequence之后,在不同的测试用例中,将不同的sequence设置成sequencer的main_phase的default_sequence。当sequencer执行到main_phase时,发现有default_sequence,那么它就启动sequence​​b.sequence的启动可以用default_sequence,也可以调用start任务。sequence除过body(),还有pre_body()和post_body()。​6.2 sequence的仲裁机制c.有两种方式可以指定发送sequence的优先级一种是用uvm_do_pri和uvm_do_pri_with产生transaction时指定优先级,同时需要 在testcase中设置sequencer的仲裁算法,如:    env.iagt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);    fork        seq0.start(env.i_agt.sqr);        seq1.start(env.i_agt.sqr);       join   //seq0和seq1中用了uvm_do_pri,seq1的优先级高于seq0,seq1发送完才发送seq0。另一种是启动sequence时指定优先级,如:    fork        seq0.start(env.i_agt.sqr,null,100);        seq1.start(env.i_agt.sqr,null,200);       join  ​d.sequencer的lock操作与grab操作的区别:启动含有lock()的sequence后,该lock()之前的uvm_do执行完才独占sequencer,grab则是该sequence一旦被启动,它就独占sequencer。​e.可以通过重载uvm_sequence的is_relevant函数来使sequence失效。该函数与lock,grab任务的作用是完全相反的,通过设置is_relevant使sequence主动放弃sequencer的使用权,而后者则是强占sequencer的所有权。is_relevant与wait_for_relevat一般应成对重载,不能只重载其中的一个。​6.3​ sequence相关宏及其实现f.8个uvm_do系列宏的另外四个:uvm_do_on,uvm_do_on_with,uvm_do_on_pri,uvm_do_on_pri_with。其实另外7个宏都是用uvm_do_on_pri_with实现的。g.uvm_create,uvm_send,uvm_send_prih.uvm_rand_send,uvm_rand_send_with,uvm_rand_send_pri,uvm_rand_send_pri_with。i.uvm_do系列宏其实就是将下述repeat内的动作封装在了一个宏中,:    virtual task body        repeat(10) begin            tr = new(""tr);            start_item(tr);            assert(tr.randomize() with {tr.pload.size() == 200;});            finish_item(tr);    endtask如果要指定packet优先级,那么在这两个任务中都要加入优先级参数:start_item(tr,100);   finish_item(tr,100)   ;j.uvm_do宏封装了从packet实例化到发送的一系列操作,封装的越多,则其灵活性越差,为了增加uvm_do系列宏的功能,UVM提供了三个接口:pre_do(),mid_do()与post_do()。​​6.4 sequence进阶应用a.嵌套的sequence:在一个sequence中,除了可以使用uvm_do产生transaction外,还可以启动其他的sequence,有两种启动方式,详见p183b.在sequence中也可以使用rand修饰符  sequence和transaction都可以调用randomize进行随机化,都可以有rand修饰符的成员变量,从某种程度上来说二者的界限比较模糊。这也就是为什么uvm_do系列宏可以接受sequence作为其参数的原因。  sequence中的变量名一定不要与transaction中的相同c.一般来说,sequence,sequencer,driver的数据包类型都是一样的。有一种方法可以让sequence产生两种包都被sequencer和driver接受,即sequence的类定义不指定传入数据包类型,然后sequencer和driver的类传入 #(uvm_sequence_item)这种类型,最后在driver中seq_item_port.get_next_item(req)之后用KaTeX parse error: Double subscript at position 275: …用'uvm_declare_p_̲sequencer(my_se…(int)::set(this,“env.i_agt.sqr.*”,“count”,9);  //思考为什么第二个参数用了通配符在sequence中用这种方式获得参数:   uvm_config_db$(int)::get(null,get_full_name(),“count”,count);UVM中可以使用get_full_name()得到一个component的完整路径   //思考为什么第一个参数为null,第二个参数为get_full_name()​b.在sequence中设置参数与之前top_tb中的一样,也可以向其他sequence和自己传递参数c.之前的component都是在build_phase中get参数,但是sequence都是运行在sqr的task phase中,当其设置一个参数的时候,其时间往往是不固定的。为此,UVM提供wait_modified任务,它的参数有三个,与config_db 的get的前三个参数完全一样。用法是:把该component  main_phase的所有进程都fork起来,在fork中添加一个while(1)循环,在该循环中使用wait_modified任务,该任务检测到第三个参数的值被更新过后,它就返回,否则就一直等待在那里,其后面再用config_db 进行get。6.7response的使用a.在复杂的验证平台中,sequence需要根据driver对transaction的反应来决定接下来要发送的transaction。sequence机制提供对这种反馈的支持,它允许driver将一个response返回给sequence,即在sequence中uvm_do后用get_response(rsp),在driver中用put_response如下: drive_one_pkt(req); rsp = new(“rsp”); rsp.set_id_info(req); seq_item_port.put_response(rsp); seq_item_port.item_done();由于可能存在多个sequence在同一个sequencer上启动,因此用set_id_info设置rsp的id等信,sequencer才知道将response返回给哪个sequence。另外rsp也可以作为item_done的参数,这样就可以省去第四行,但是这只适用于只执行一次put_response()。b.一个transaction也可以对应多个response,即sequence中多次调用get_response(),而在driver中,需要多次调用put_response。 response机制的原理是driver将rsp推送给sqr,而sqr中维持一个队列,当有新的response时就推入此队列;队列大小默认情况下为8,因此sequence没有及时get_response,使sqr的response超过8的话就会发生溢出报UVM_ERROR。c.当在sequence中启动get_response时,进程就会阻塞在那里,一直到response_queue中被放入新的记录。假如driver需要延时较长一段时间才能将response返回,在此期间driver希望能够继续从sequence中得到新的transaction并驱动它,但是由于sequence被阻塞在了那里,根本不可能发出新的transaction。发生这种情况的主要原因是sequence中发送transaction与get_response是在同一个进程中执行,用response_handler来处理,书上讲的一直没看懂,TBD。d.put/get_response都是新建了一个transaction,并将其返回给sequence;另类的response:      seq_item_port.get_next_item(req);      drive_one_pkt(req);      req.frm_drv = “this is information from driver”;      seq_item_port.item_done();第四行driver中向req的成员赋值,而在sequence中可以检测这个值。这种另类的response在很多总线的driver中用到。e.UVM也支持rsp与req类型不同的情况,需要在sequence,sequencer和driver类定义时,传入两种transaction。6.8 sequence librarya.所谓sequence library,就是一系列sequence的集合,sequence library派生自uvm_sequence,本质上也是一个sequence;sequence library类定义时需要在new函数中调用Init_sequence_library(),否则内部的sequence候选队列就是空的;在其他sequence定义时使用uvm_add_to_seq_lib指定加入某sequence ;sequence library定义好后,在testcase中将其设为sqr的default sequence。b.sequence library默认随机从sequence队列中选择几个执行,这是由其变量selection_mode决定的,另外还有三种模式供选择,可以在testcase的build_phase中使用config_db来配置该变量。c.默认情况下会执行10个sequence,同样通过config_db改变最小值和最大值来控制产生sequence数目的范围。d.另外两种方式配置selection_mode,min_random_count,max_random_count:一种是在testcase中实例化cfg,uvm_sequence_library_cfg cfg, cfg = new(“cfg”,UVM_SEQ_LIB_RANDC,5,20),最后用config_db将cfg传到default_sequence。另一种是sequence library先实例化,然后可以直接改变其三个变量的值,再用config_db将其设置为default sequence:function void my_case0::build_phase(uvm_phase phase);                simple_seq_library seq_lib;                super.build_phase(phase);
                  seq_lib = new(“seq_lib”);   ​              seq_lib.selection_mode = UVM_SEQ_LIB_RANDC;                seq_lib.min_random_count = 10;                seq_lib.max_random_count = 15;                 uvm_config_db#(uvm_sequence_base)::set(this,                                                                                                                               “env.i_agt.sqr.main_phase”,                                                                                    “default_sequence”,                                                                                    seq_lib);endfunction​​

7.UVM中的寄存器模型7.1寄存器模型简介a.通常来说,DUT中会有一组控制端口,通过控制端口,可以配置DUT中的寄存器,DUT可以通过寄存器的值来改变其行为,这组控制端口就是寄存器配置总线。b.对于书中附录B-2的代码,其中有一个1比特的寄存器invert,带读写配置总线,因此在数据传输之前先驱动该总线配置寄存器对以前平台的修改如下:增加bus_agt,其中实现bus_if,bus_item(bus的数据包),sqr和drv;在env中实例化该agent;定义vsqr,并在base_test中实例化之,设置好connect_phase;在case0中增加vseq,修改将其设置为vsqr main_phase的默认sequence;vseq中将先用uvm_do_on_with发送一个bus_item,接着再启动原来的sequence。c.一般在driver中复位结束则跳进解包的while循环中,driver_one_packet一次需要3个时钟周期,则3个周期后会执行下一次drive。d.有了寄存器模型之后,ref只与寄存器模型打交道,无论是发送读的指令还是获取读操作的返回值,都可以由寄存器模型完成。e.有了寄存器模型之后,可以在任何耗费时间的phase中使用寄存器模型以前门访问或后门访问的方式来读取寄存器的值,也能在不耗费仿真时间的phase中使用后门访问方式来读取寄存器的值。f.所谓前门访问,指的是通过模拟CPU在总线上发出读的指令,进行读写操作;后门访问不通过总线进行读写操作,而是直接通过层次化的引用来改变寄存器的值。g.另外寄存器模型还提供一些任务,如mirror、update,它们可以批量完成寄存器模型与DUT中相关寄存器的交互。h.可见,UVM寄存器模型的本质就是重新定义了验证平台与DUT的寄存器接口,使验证人员更好地组织及配置寄存器,简化流程、减少工作量。i.  uvm_reg_field/uvm_reg/uvm_reg_block/uvm_reg_map,每个寄存器在假如寄存器模型时都有其地址,uvm_reg_map就是存储这些地址,并将其转换成可以访问的物理地址。7.2简单的寄存器模型a.给示例中的DUT建立寄存器,需要做以下几件事情     1.从uvm_reg派生一个invert类取名reg_invert;其中定义各个uvm_reg_field的指针,并建立一个build函数,其中实例化各个uvm_reg_field,并调用configure函数配置各uvm_reg_field。     2.定义好寄存器后,从uvm_reg_block派生一个类取名reg_model;其中定义reg_invert的指针invert,并建立一个build函数,其中实例化default_map和reg_invert,调用reg_invert.configure配置该寄存器,调用invert.build函数,最后用default_map.add_reg()函数将该寄存器加入到default_map中。 3.无论读写操作,寄存器模型都会通过sequence产生一个uvm_reg_bus_op的变量,其中存储着操作类型和地址入,如果是写还会有要写入的数据。此变量中的信息要经过一个转换器的转换后交给bus_sequencer。一个转换器要定义好两个函数,一是reg2bus,其作用是将寄存器模型通过sequence发出的那个变量转化成bus_sequencer能够接受的形式;另一个是bus2reg,其作用是当监测到总线上有操作时,它将收集来的transaction转换成寄存器模型能够接受的形式,以便寄存器模型能够更新相应的寄存器值。 4.在base_test的build_phase中实例化reg_model和adapter,依次调用reg_model的四个函数:rm.configure, rm.build, rm.lock_model, rm.reset;在connect_phase中将此reg_model的指针传递给env.ref.p_rm,并使用rm.default_map.set_sequencer(env.bus_agt.sqr,reg_sqr_adapter)将bus_sequencer和adapter告知reg_model的default_map,并使用rm.default_map.set_auto_predict(1)将default_map设置为自动预测状态。 5.在tb中使用寄存器模型:p_rm.invert.read(status,value,UVM_FRONT_DOOR),status用于表示读操作是否成功。7.3

8.UVM中的factory机制a.当在父类种定义一个函数/任务时,如果将其设置为virtual类型,那么就可以在子类中重载它。b.重载最大的优势是:一个子类的指针以父类的类型传递时,其表现出的行为依然是子类的行为。c.UVM中各个phase被调用时,如调用driver的phase时:c_ptr.build_phase,c_ptr本身是uvm_component类型,但是它最终指向的是my_driver。d.在一个验证平台中,UVM树上的结点是各个类型的,UVM不必理会它们具体是什么类型,统一将它们当作uvm_component来对待,这极大方便了管理。e.SV中一个非常有用的特性是支持约束的重载,假如第一次定义的transaction中的约束不太方便用于后面的仿真,则可以定义一个新的transaction继承自该transaction,然后把以前的约束重载,这样在新的sequence中就可以实例化新的transaction。f.使用factory机制的重载可以使父类的实例执行子类的函数,如:bird是父类,parrot是子类,bird_inst是父类的一个实例,在build_phase中用set_type_override_by_type(bird::get_type(),parror::get_type())将父类用子类重载,且bird_inst是用factory机制的实例化方式,那么再调用print_hungry(bird_inst)它实际上会执行子类的函数。g.factory机制最伟大的地方在于其具有重载功能。h.使用factory机制的重载有几个前提:1.重载的类和被重载的类在定义时都要注册到factory 2.被重载的类在实例化时必须使用factory机制的方式 3.最终重载的类和最终被重载的类要有派生关系。i.有时不希望把验证平台中的A类型全部替换为B类型,而只是替换一部分,则可以使用:set_inst_override_by_type(“env.o_agt.mon”,my_monitor::get_type(),new_monitor::get_type()),其第一个参数是component的路径,第二个参数是被重载的类型,第三个参数是要重载的类型。j.上面的两个替换函数写起来比较晦涩,UVM提供另外一种简单的方式:字符串!,与set_type_override_by_type()对应的是set_type_overide(“bird”,“parrot”),与set_inst_override_by_type()对应的是set_inst_override(“env.o_agt.mon”,“my_monitor”,“new_monitor”)k.上面的四个函数都是在uvm_component中使用,如果是在top_tb的initial中,UVM提供了另外四个函数与上面四个对应,另外还可以在命令行进行重载。l.UVM支持连续的重载,如用big_parrot重载parrot,那么bird_inst执行的是big_parrot的代码。m.UVM支持替换式重载,如用sparrow重载bird,则parrot的重载会被覆盖掉。n.factory机制的重载功能很强大,UVM提供print_override_info函数来输出所有的打印信息。o.uvm_factory的print函数有一个参数,可以是0、1或2;当为0时,仅仅打印被重载的实例和类型,当为1时,打印参数为0时的信​息,以及所有用户创建的、注册到factory的类的名称,当为2时,打印参数为1时的信息,以及系统创建的、所有注册到factory的类的名称(如uvm_reg_item)p.  uvm_top.print_topology()可以显示出整棵UVM树的拓扑结构,一般在base_test的connect_phase中使用。q.重载transaction: 笔记e中需要新建一个sequence并且修改default_sequence,有了factory机制的重载则可以保留原来的sequence,只要派生一个新的transaction,然后在testcase的build_phase中使用factory.set_type_override_by_type(my_transaction::get_type(),crc_err_tr::get_type())即可。r.重载sequence:   除过q中的重载还可以不用原来的sequence:从其派生一个新的sequence,实例化一个transaction,并将crc_err0的约束关闭,然后`uvm_rand_send_with发送crc_err1的包,这样原来的sequence依然是default_sequence,用新sequence将旧sequence重载掉即可。s.重载driver: 也可以使用重载driver实现上面的功能,在driver类中加入一个inject_cer_err的函数,在get_next_item(req)之后调用该函数注入crc_err。这个例子看不出重载driver的优势,因为CRC错误是一个非常普通的异常测试用例,对于那些异常到使用sequence实现起来非常麻烦的情况,重载driver就会显示出优势。t.scb和ref也可以进行重载,尤其是ref,处理异常的激励源是相当耗时的一件事情,对于有的DUT来说,可能其80%的代码都是用于处理异常情况,如果将所有的异常情况都只在一个参考模型中实现,那么这个参考模型的代码量将会非常的大;如果将其分散为数十个参考模型,每一个处理一种异常情况,当建立相应异常的测试用例时,将正常的参考模型由新模型替换掉,这样可使代码清晰并增加可读性。u.基于四个原因,不要将所有的测试用例都使用driver重载实现。只有将driver的重载与sequence相结合,才与UVM的设计初衷相符合,也才能构建起来可重用性高的验证平台。v.2.2.2节中根据run_test的参数"my_driver"创建了一个my_driver的实例,这是factory机制的一大功能!w.   string type_string;  type_string = “A”,如何根据一个字符串type_string创建一个类的实例?没有任何语言会内建一个如上的机制,要实现这种功能需要自己做,而在UVM中,factory机制实现了这样的功能。x.factory还提供了另外四个接口来创建一个类的实例:create_object/component_by_name/type()y.在没有factory机制之前,要创建一个类的实例,只能用new()函数,有了factory。还可以根据类名创建这个类的实例。z.从本质上来看,factory机制其实是对sv中new函数的重载,因为这个原始的new函数实在是太简单了,功能太少 了。​
9.UVM中代码的可重用性9.1 callback机制(基本用不到)9.2功能的模块化:小而美 a.Linux如此受欢迎的原因之一是它提供了众多的小工具。 b.在验证平台的设计中,要尽量做到小而美,避免大而全。 c.对于程序员来说,要尽量避免复制的使用,同样的代码只在验证平台上出现一处,如果要重用,将他们封装成可重载的任务、函数或者类。 d.不要建造强大的sequence,用几个小sequence去实现,尽量做到一看名字就知道这个sequence的用处。9.3参数化的类 a.config_db和factory都支持参数化的类 b.对于带默认参数的类,其对象在实例化时必须加上默认的参数。9.4模块级到芯片级的代码重用 a.现代芯片的验证通常分为两个层次,一是模块级验证(IP级验证),二是芯片级验证。一个大的芯片在开发时,是分成多个小的模块来开发的,每个模块开发一套独立的验证环境,通常每个模块有一个专门的验证人员负责,当模块级验证完成后,需要做整个系统的验证。​
10.UVM高级应用​(TBD)可变时钟、layer_sequence与错峰技术、心跳功能、聚合参数、congfig_db set函数的第二个参数检查

猜你喜欢

转载自blog.csdn.net/micheal2731/article/details/89247406
set