目录
UVM组件
uvm_driver
定义一个driver
uvm_monitor
执行的工作
uvm_sequencer
uvm_agent
scoreboard
uvm_env
uvm_test
UVM结构总结
※ uvm_component
※ uvm_env
※ uvm_test
UVM里中的十大类 作者:
本篇文章聊到的组件,都是继承于uvm_component。在SV环境中的验证组件按照功能需要,被称之为激励器(stimulator)、检测器(monitor)和检查器(checker)。这三个核心组件与验证环境的三个关键特性相对应:激励(sequencer)、检测(monitor)和检查(scoreboard)。
uvm_agent、uvm_env、uvm_test在UVM中所承担的功能并不多,主要起到构建环境层次的作用。
从uvm_component类继承的类,均可以构成验证环境,这是因为它们都从uvm_component类继承了phase机制,也都会经历各个phase阶段。
主要常见的组件类为:uvm_driver、uvm_monitor、uvm_sequencer、uvm_agent、uvm_scoreboard、uvm_env、uvm_test。
该类会从uvm_sequencer中获取事务(transaction),经过转化进而在接口中对DUT进行时序激励。任何继承于uvm_driver的类需要注意的是,该类是参数化类,因此在定义时需要声明参数的类型。
用户定义新的driver类时,应当声明该类所需获取的事务参数REQ类型,默认情况下,RSP参数类型同REQ类型保持一致。 如上代码段,默认的REQ类型是uvm_sequence_item类型。(REQ request;RSP response)
uvm_driver在uvm_component基础上没有扩展新的函数,而只是扩展了一些用来通信的端口和变量:
driver和sequencer类之间的通信就是为了获取新的事务对象,这一操作是通过pull的方式实现的:
这里extern 声明了一个task,会发现这个task没有endtask配对,这是因为这里不是对task的功能进行描述,而是利用extern做一个声明,告诉大家我已经定义一个task,名叫run_phase,占一个坑,然后在外部定义具体功能。
如果没有extern,那么就要在声明的同时,下面接着定义task的功能,然后再endtask;这两种定义task的方法均可以,没有任何区别,就是coding风格不一样而已;那么什么时候使用extern,什么时候不使用呢?
在商业的代码里,就可能会采用extern的形式,然后把定义包装成.svh的head文件,这样你在查阅代码的时候,就不会出现三百行代码里还有一大部分是task的实现,方便你快速阅读和理解,然后在配套写一个.sv文件,其中.svh用来声明和定义,.sv则具体到如何实施;或者在商业的VIP里,为了保密,而不把具体内容写在task里,而是用extern定义外部实现的方式来做,你只要知道如何使用就行了,而不被允许知道如何实现。
从名字来看,这个类是为了监测接口数据,而任何需要用户自定义数据监测行为的monitor都应当继承于该类。
虽然uvm_monitor与它的父类相比,并没有增添新的成员变量和方法,理论上继承于uvm_monitor和继承于uvm_component是一样的,但还是建议coding过程中,遵循UVM模板,将新定义的monitor类继承uvm_monitor,有助于实现uvm_monitor的方法和特性。
观测DUT的interface,并且收集总线信息;
永远保持PASSIVE模式,只能做监测,不做任何主动修改信号的操作,即永远不会驱动DUT;
在总线协议或者内部信号协议观察时,可以做一些功能和时序的检查;
对于更加复杂的检查要求,它们可以将数据发送至其他验证组件,例如scoreboard、reference model或者coverage collector。
外部定义了task run_phase,在serial_monitor中声明一下
uvm_sequencer就如同一个管道,从这个管道中会连续传递激励事务(sequence产生事务,sequencer传递事务),并最终通过TLM端口送至driver一侧。
sequencer既管理着sequence,同时也将sequence中产生的transaction item传送到driver一侧,可以说是整个激励环节中的“路由器”。sequencer是一个uvm_component类型,但是sequence是uvm_object类型,这也符合我们的认知,产生transaction为动态创建的,在仿真的任何时刻都可以创建sequence,并且创建使用完后就可以扔掉,所以sequence为object类型。
定义my_sequencer也是三部曲,继承、注册和定义new函数,这是最基本的,然后再在此之上定义更多的内容譬如:接口。
uvm_agent是一个标准的验证环境“单位”。这也的一个标准单位通常包含一个driver、一个monitor以及一个sequence。这三者通常是聚在一起组成一个agent。
有的时候agent不需要发送激励,那么就可以只包含一个monitor,而不需要driver和sequencer,这就需要一个变量来进行有条件的例化。
is_active是agent的一个成员,缺省值是UVM_ACTIVE,这表示处在active模式的agent需要例化driver、monitor和sequencer;而如果is_active的值是UVM_PASSIVE,这表示agent是passive模式,只可以例化monitor。active模式的agent既有激励功能也有监测功能;passive模式的agent只有监测功能。
如何配置agent为active模式还是passive模式呢?可以通过顶层利用config_db来配置。
agent的存在是为了验证环境的复用。按照总线的传输方向划分可以分为master和slave,master agent是用来向DUT发起transaction的,slave agent是用来响应DUT的events的。
在my_agent中声明了build_phase和connect_phase,在外部定义。is_active的配置可以通过外部使用config_db的方式配置。
uvm_scoreboard承担的功能就是数据对比和报告。uvm_scoreboard本身也没有添加额外的成员变量和方法,但UVM建议用户将自定义的scoreboard类继承于uvm_scoreboard类,这便于子类在日后可以自动继承于可能被扩充到uvm_scoreboard类中的成员。
在实际环境中,uvm_scoreboard会接收来自多个monitor的监测数据,继而进行比对和报告。
在scoreboard中通常会声明TLM端口以供monitor传输数据。简易比较的方法,可以采用UVM预定义的comparator。对于复杂的设计,可以在scoreboard中分别创建reference model和comparator。
在scoreboard中定义的端口,跟monitor相连监测数据,还有一端和out数据相连。在build_phase中,端口也要做例化。 作者:不吃葱的酸菜鱼 https://www.bilibili.com/read/cv18700607 出处:bilibili
从验证环境的结构而言,uvm_env可能包含多个uvm_agent和其它component。这些不同的组件共同构成一个完整的验证环境,而这个环境在将来复用中可以作为子环境被进一步集成到更高的环境中。
如上图,验证一个系统的时候,可以先验证子系统,为子系统或者说子模块搭建一个验证环境sub_env,然后验证完毕之后,把验证更多的功能时,可以让top_env继承于suv_env并增添一些接口和模块,来实现垂直复用。
uvm_env的角色就是一个结构化的容器,它可以容纳其它组件,同时它也可以作为子环境在更高层的集成中被嵌入。
在实际使用中,用户很容易混淆uvm_agent和uvm_env之间的嵌套关系,而且容易在创建对象阶段出现错误,这时要注意:uvm_agent作为一个标准单元,在更上层的集成中应该被例化到uvm_env,所以不要用uvm_agent去嵌套uvm_agent。uvm_env在更高层的复用中,可以被其它uvm_env所嵌套。
uvm_env的嵌套,要和设计的层次结构一致,这样才能更好的做环境的维护。
uvm_test类本身没有什么新成员,但是作为测试用例的“代表”,它不但决定着环境的结构和连接关系,也决定着使用哪一个测试序列。
uvm_test是验证环境建立的唯一入口,要例化uvm_env,如果没有它,整个环境都无从建立,只有通过它才能正常的运行UVM的phase机制。在一个顶层test中,可以例化多个组件譬如uvm_env或者uvm_agent,而在仿真时通过uvm_test可以实现验证环境的运转。
推荐在uvm_test中只例化一个顶层uvm_env,这便于提供一个唯一环境节点以形成树状的拓扑结构。且用户写的uvm_test的顶层必须继承于uvm_test而不能继承于uvm_component,虽然uvm_test没有添加新的成员,但是我们在调用一些uvm_test函数时,只有继承于uvm_test的类,系统才能识别这些uvm_test函数,否则UVM系统不予承认。
config_db也可以构建一个嵌套的环境,在不同的层次中给不同的组件来做传参。要注意的是使用uvm_config_db来做配置,一定要先配置,再创建组件,执行build_phase,get config。包括在set interface的时候也是一样,先set再做run_test()。
uvm_top是uvm_root类的唯一实例,它由UVM创建和管理,它所在的域是uvm_pkg。uvm_top是所有test组件的顶层,所有验证环境中的组件在创建时都需要指明它的父一级,如果某些组件在创建时父一级的参数为“null”,那么它将直接隶属于uvm_top。
uvm_top提供一系列的方法来控制仿真,例如phase机制、objection防止仿真退出机制等。
test类是用户自定义类的顶层结构,必须继承于uvm_test,否则uvm_top不识别。test的目标包括:提供不同的配置(config_db)、环境结构配置、测试模式配置等,然后再创建环境、例化测试序列,挂载目标sequencer,使其命令driver发送激励。
所有需要配置的,所有需要修改参数的全都在test层次中修改(譬如测试序列、override部分),而不在其他例如env、agent修改,方便我们维护。
构建环境的主要组件主要由三类UVM构架模块(基类)组成
继承于uvm_report_object(进一步继承于uvm_object),提供消息方法;所有的验证环境组件均继承于uvm_component;管理验证环境的层次。
uvm_component提供以下接口或者API:
对于uvm_component组件的构建函数,固定形式为:
※ string name :用来声明当前例化组件的名称,用来自动和它所在的父一级层次组合为组件的整个层次名称,可以用get_full_name()方法获取;
※ uvm_component parent: 用来指示所例化的父一级句柄,通常用“this”来指代,即例化在当前的父一级组件中。凡是继承与uvm_component的组件,也应该保持同样的形式参数列表。
注意这里的new函数要和uvm_object的构建函数new(string name)进行区分,由于uvm_object并不参与组件的层次构建,因此它只有一个形参,而没有uvm_component parent。
继承于uvm_component;没有额外的功能,用来为环境结构提供一个容器,方便维护和复用。
继承于uvm_component;没有额外的功能,用来提供对uvm_env的额外配置以及挂载激励。