hash,通过一个索引值和一个数据组成,索引值必须是唯一的。Fork join:内部 begin end块并行运行,直到所有线程运行完毕才会进入下一个阶段。Fork join_any:内部 begin end块并行运行,任意一个begin end块运行结束就可以进入下一个阶段。Fork join_none:内部 begin end块并行运行,无需等待可以直接进入下一个阶段。wait fork:会引起调用进程阻塞,直到它的所有子进程结束,一般用来确保所有子进程(调用进程产生的进程,也即一级子进程)执行都已经结束。disable fork:用来终止调用进程 的所有活跃进程, 以及进程的所有子进程。多线程之间同步主要由mailbox、event、 semaphore三种进行一个通信交互。
mailbox邮箱:主要用于两个线程之间的数据通信,通过put函数和 get 函数还有peek函数进行数据的发送和获取。Event:事件主要用于两个线程之间的一个同步运行,通过事件触发和事件等待进行两个线程间的运行同步。使用@(event)或者 wait(event.trigger)进行等待,->进行触发。Semaphore:旗语主要是用于对资源访问的一个交互,通过key的获取和返回实现一个线程对资源的一个访问。使用put和 get函数获取返回key。一次可以多个。Interface是一组接口,用于对信号进行一个封装,捆扎起来。如果像verilog中对各个信号进行连接,每一层我们都需要对接口信号进行定义,若信号过多,很容易出现人为错误,而且后期的可重用性不高。因此使用interface接口进行连接,不仅可以简化代码,而且提高可重用性,除此之外,interface内部提供了其他一些功能,用于测试平台与DUT之间的同步和避免竞争。Clocking block:在interface内部我们可以定义clocking块,可以使得信号保持同步,对于接口的采样vrbg和驱动有详细的设置操作,从而避免TB与 DUT的接口竞争,减少我们由于信号竞争导致的错误。采样提前,驱动落后,保证信号不会出现竞争。封装、继承和多态
virtual声明,这样当调用基类句柄指向扩展类时,方法 会根据对象去识别,调用扩展类的方法,而不是基类中的。而基类和扩展类中方法有着同样的名字,但能够准确调用,叫做多态。Factory机制也叫工厂机制,其存在的意义就是为了能够方便的替换TB中的实例或者已注册的类型。一般而言,在搭建完TB后,我们如果需要对TB进行更改配置或者相关的类信息,我们可以通过使用factory机制进行覆盖,达到替换的效果,从而大大提高TB的可重用性和灵活性。Interface是一组接口,用于对信号进行一个封装,捆扎起来。如果像 verilog中对各个信号进行连接,每一层我们都需要对接口信号进行定义,若信号过多,很容易出现人为错误,而且后期的可重用性不高。因此使用interface接口进行连接,不仅可以简化代码,而且提高可重用性,除此之外,interface内部提供了其他一些功能,用于测试平台与DUT之间的同步和避免竞争。Clocking block:在interface内部我们可以定义clocking块,可以使得信号保持同步,对于接口的采样和驱动有详细的设置操作,从而避免TB与 DUT的接口竞争,减少我们由于信号竞争导致的错误。采样提前,驱动落后,保证信号不会出现竞争。UVM的启动总结:
a) 虽然SV可以通过层次化的interface的索引完成传递,但是这种传递方式不利于软件环境的封装和复用。通过使用uvm_config_db配置机制来传递接口,可以将接口的传递与获取彻底分离开。
b) 接口传递从硬件世界到UVM环境可以通过uvm_config_db来实现,在实现过程中应当注意:
c) 接口传递应发生在run_test()之前。这保证了在进入build_phase之前,virtual interface已经被传递到uvm_config_db中。
d) 用户应当把interface与virtual interface区分开来,在传递过程中的类型应当为virtual interface,即实际接口的句柄。
UVM其实就是SV的一个封装,将我们在搭建测试平台过程中的一些重复性和重要的工作进行封装,从而使我们能够快速的搭建一个需要的测试平台,并且可重用性还高。但是UVM又不仅仅是封装。
Ref参数类型是引用
ref获取最佳性能,如果不希望子程序改变数组的值,可以使用const ref类型UVM中component也是由object派生出来的,不过相比于object, component有很多其没有的属性,例如phase机制和树形结构等。在UVM中,不仅仅需要component这种较为复杂的类,进行TB的层次化搭建,也需要object这种基础类进行TB的事务搭建和一些环境配置等。Item是object
Sequencer:负责将数据转给driverdriver负责数据的发送;driver有时钟/时序的概念。Agent:其实只是简单的把driver ,monitor和sequencer封装在一起。Agent:对应的是物理接口协议,不同的接口协议对应不同的agent ,一个平台通常会有多个 agent 。Env:则相当于是一个特大的容器,将所有成员包含进去。Virtual sequencer主要用于对不同的agent进行协调时,需要有一定顶层的sequencer对内部各个agent中的sequencer进行协调virtual sequencer是面向多个sequencer的多个sequence群,而sequencer是面向一个sequencer 的sequence群。Virtual sequencer桥接着所有底层的sequencer的句柄,其本身也不需要传递item,不需要和driver连接。只需要将其内部的底层sequencer句柄和sequencer 实体对象连接。
在多个sequence同时向sequencer发送item时,需要有ID信息表明该item从哪个sequence来,ID信息在sequence创建item时就赋值了。
spec比较来发现,design是否行为正确,需要按verification plan来比较进度。用来衡量哪些设计特征已经被测试程序测试过的一个指标这个问题很重要,建议好好准备,面试的时候经常会问~
芯片架构-RTL设计-功能仿真-综合&扫描链的插入(DFT)-等价性检查-形式验证-静态时序分析(STA)-布局规划-布局布线-布线图和原理图比较-设计规则检查-GDII
find的队列应该是返回队列的值,一般的话是和with配合使用,find index应该是返回索引值
review验证功能点,测试用例,以及特殊情况下的波形等。严格意义上有2种:
用来触发事件时,使用->;用来等待事件使用@或者wait。
除了driver、monitor、agent、model、scoreboard、env、test之外全部用uvm_object。
get_next_item()是一个阻塞调用,直到存在可供驱动的sequence item为止,并返回指向sequence item的指针。try_next_item()是非阻塞调用,如果没有可供驱动的sequence item,则返回空指针。And 指的是两个序列具有相同的起始点,终点可以不同。Intersect 指的是两个序列具有相同的起始点和终点。Or 指的是两个序列只要满足一个就可以Throughout 指的是满足前面要求才能执行后面的序列break 语句结束整个循环。continue 立即结束本次循环,继续执行下一次循环。return语句会终止函数的执行并返回函数的值(如果有返回值的话)。return之后,function里剩下的语句不能执行,其是终止函数的执行,并返回函数的值。
主要是编写sequence,然后在body里面根据测试功能要求写相应的激励,然后再通过ref_model和checker判断功能是否实现?
可以写脚本让它们自动执行,例如makefile...
例如让你写abcd四个信号在时钟沿处监测,当cd同时为1时,在时钟的前两个周期要ab同时为1的断言
constraint_mode(0)关闭默认范围的约束块constraint_mode(1)是打开约束soft关键字修饰特定的约束语句,这样既可以让变量在一般的情况下取默认值,也可以直接给变量赋默认值范围外的取值。队列的使用方法:insert,delete,push_back和pop_frontPush插入,pop取出Front前边,back后边
rand修饰符:rand修饰的变量,每次随机时,都在取值范围内随机取一个值,每个值被随机到的概率是一样的,就想掷骰 子一样。randc修饰符:randc表示周期性随机,即所有可能的值都取到过后,才会重复取值initiator到target之间的数据流向是单一方向的initiator和target,但是数据流向在端对端之间是双向的initiator与target之间的相同TLM端口数目超过一个时的处理解决办法。initiator先生成数据Tt,同时将该数据传送至target。initiator从target获取数据Tt,而target中的该数据Tt则应消耗。initiator从target获取数据Tt,而target中的该数据Tt还应保留。TLM FIFO:可以进行数据缓存,功能类似于mailbox,不同的地方在于uvm_tlm_fifo提供了各种端口(put、get、peek)供用户使用analysis port:一端对多端,用于多个组件同时对一个数据进行处理,如果这个数据是从同一个源的TLM端口发出到达不同组件,则要求该端口能够满足一端到多端,如果数据源端发生变化需要通知跟它关联的多个组件时,我们可以利用软件的设计模式之一观察者模式实现,即广播模式analysis TLM FIFOa. 由于analysis端口提出实现了一端到多端的TLM数据传输,而一个新的数据缓存组件类uvm_tlm_analysis_fifo为用户们提供了可以搭配uvm_analysis_port端口uvm_analysis_imp端口和write()函数。
b.uvm_tlm_analysis_fifo类继承于uvm_tlm_fifo,这表明它本身具有面向单一TLM端口的数据缓存特性,而同时该类又有一个uvm_analysis_imp端口analysis_export并且实现了write()函数:
transport,即通过在target端实现transport()方法可以在一次传输中既发送request又可以接收response。copy()函数(如list.copy()),或者使用copy模块的copy()函数。深拷贝只能使用copy模块的deepcopy(),所以使用前要导入:from copy import deepcopyprotected,那么只有该类或者子类可以访问成员,而外部无法访问。local,那么只有该类可以访问成员,子类和外部均无法访问。UVM其实就是SV的一个封装,将我们在搭建测试平台过程中的一些重复性和重要的工作进行封装,从而使我们能够快速的搭建一个需要的测试平台,并且可重用性还高。因此我当时觉得它就是一个库。UVM中各种机制和模型的构造和相互之间关系之后,我觉得其实UVM方法学对于使用何种语言其实并不重要,重要的是他的思想,比如:在UVM中有sequence机制,以往如果我们使用SV进行TB搭建时,我们一般会采用driver一个类进行数据的产生,转换,发送,或者使用generator和driver两个进行,这种方式可重用性很低,而且代码臃肿;但是在UVM中我们通过将sequence、sequencer、driver、sequence_item拆开,相互独立而又有联系,因此我们只需关注每一个类需要做的工作就可以,可重用性高。我在学习sequence时,我经常把sequence 比作蓄水池,sequence_item就是水,sequencer就是一个调度站,driver就是总工厂,通过这种方式进行处理,我们的总工厂不需要管其他,只需处理运送过来的水资源就可以,而sequencer只需要调度水资源,sequence只需要产生不同的水资源。而这种处理方式和现实世界中的生产模式又是基本吻合的。除此之外,还有好多好多,其实UVM方法学中很多思想就是来源于经验,来源于现实生活,而不在乎是何种语言。画出UVM的验证环境结构,如图所示
首先,UVM测试平台基本是由object和 component组成的,其中 component搭建了TB的一个树形结构,其基本包含了driver、monitor、sequencer、agent、scoreboard、model、env、test、top;然后object一般包含sequence_item、config和一些其他需要的类。各个组件相互独立,又通过TLM事务级传输进行通信,除此之外,DUT 与driver和 monitor 又通过interface进行连接,实现驱动和采集,最后在top层进行例化调用test进行测试。
UVM中有很多非常有趣的机制,例如factory机制,field_automation机制,phase机制,打印机制,sequence机制,config_db机制等,这些机制使得我们搭建的UVM能够有很好的可重用性和使得我们平台运行有秩序稳定。
例如phase机制,phase机制主要是使得UVM的运行仿真层次化,使得各种例化先后次序正确。UVM的phase机制主要有9个,外加12个小phase。主要的 phase有build phase、connect phase、run phase、report phase、final phase等,其中除了run phase是** task**,其余都是function,然后build phase和final phase都是自顶向下运行,其余都是自底向上运行。Run phase和12个小phase( reset phase、configure phase、main phase、shutdown phase)是并行运行的,有这12个小phase主要是进一步将run phase 中的事务划分到不同的phase进行,简化代码。注意,run phase和 12个小phase最好不要同时使用。从运行上来看,9个phase顺序执行,不同组件中的同一个phase执行有顺序,build phase为自顶向下,只有同一个phase全部执行完毕才会执行下一个phase。
所有的phase按照以下顺序自上而下自动执行:(九大phase,其中run phase又分为12个小phase)build_pase
connect_phase
end_of_elaboration_phase
start_of_simulation_phase
run_pase
extract_phase
check_phase
report_phase
final_phase
其中,run_phase按照以下顺序自上而下执行:
pre_reset_phase
reset_phase
post_reset_phase
pre_configure_phase
configure_phase
post_configure_phase
pre_main_phase
main_phase
post_main_phase
pre_shutdown_phase
shutdown_phase
post_shutdown_phase
Domain是用来组织不同组件,实现独立运行的概率。默认情况下,UVM的9个phase属于 common_domain,12个小phase属于uvm_domain。例如,如果我们有两个dirver类,默认情况下,两个driver类中的复位phase和 main phase必须同时执行,但是我们可以设置两个driver属于不同的domain,这样两个dirver就是独立运行的了,相当于处于不同的时钟域(只针对12个小phase有效)。
run_phase和main phase(动态运行)都是task phase,且是并行运行的,后者称为动态运行(run-time)的phase。phase下任意一个component中至少提起一次objection,这个结论只适用于12个run-time的phase。对于run_phase则不适用,由于run_phase与动态运行的phase是并行运行的,如果12个动态运行的phase有objection被提起,那么run_phase根本不需要raise_objection就可以自动执行。在main_phase执行过程中,突然遇到reset信号被置起,可以用jump()实现从mian_phase到reset_phase的跳转:
UVM中采用事务级传输机制进行组件间的通信,可以大大提高仿真的速度和使得我们简化组件间的数据传输,简化工作,TLM独立于组件之外,降低组件间的依赖关系。UVM 接口主要由port、export、imp;驱动这些接口方式有put、get、peek、transport、analysis 等。peek是查看端口内部的数据事务但是不删除,get是获取后立即删除。我们一般会先使用peek进行获取数据,但不删除(保证put端不会立马又发送一个数据),处理完毕后再用get删除。都可以。Analysis port类似于广播,其可以同时对多个imp进行事务通信,只需要在每一个对应的imp端口申明write()函数即可。对比 put,get,peek port,他们都只能进行一对一传输,且也必须申明对应的函数如 put()、get()、peek()、can_put()/do_put()等。Fifo是可以不用申明操作函数的,其内部封装了很多的通信端口,如analysis_export等,我们只需要将端口与其连接即可实现通信。
item是基于uvm_object类,这表明了它具备UVM核心基类所必要的数据操作方法,例如copy、 clone、compare、record等。item对象的生命应该开始于sequence的body()方法,而后经历了随机化并穿越sequencer最终到达driver,直到被driver消化之后,它的生命一般来讲才会结束。sequence可以包含一些有序组织起来的item实例,考虑到item在创建后需要被随机化,sequence在声明时也需要预留一些可供外部随机化的变量,这些随机变量一部分是用来通过层级传递约束来最终控制item对象的随机变量,一部分是用来对item对象之间加以组织和时序控制的。(flat sequence):这一类往往只用来组织更细小的粒度,即item实例构成的组织。( hierarchical sequence):这一类是由更高层的sequence用来组织底层的sequence,进而让这些sequence或者按照顺序方式,或者按照并行方式,挂载到同一个sequencer上。(virtual sequence):这一类则是最终控制整个测试场景的方式,鉴于整个环境中往往存在不同种类的sequencer和其对应的sequence,我们需要一个虚拟的sequence来协调顶层的测试场景。之所以称这个方式为virtual sequence,是因为该序列本身并不会固定挂载于某一种sequencer类型上,而是将其内部不同类型sequence最终挂载到不同的目标sequencer上面。这也是virtual sequence不同于hierarchical sequence的最大一点。sequence机制用于产生激励,它是UVM中最重要的机制之一。sequence机制有两大组成部分:sequence和sequencer。sequence处于一个比较特殊的位置。sequence不属于验证平台的任何一部分,但是它与sequencer之间有着密切的关系。sequencer的帮助下,sequence产生的transaction才能最终送给driver;同样,sequencer只有在sequence出现的情况下才能体现出其价值,如果没有sequence,sequencer几乎没有任何作用。sequence与sequencer还有显著的区别。从本质上说,sequencer是一个uvm_component,而sequence是一个uvm_object。与my_transaction一样,sequence也有其生命周期。它的生命周期比my_transaction要更长一点,其内部的transaction全部发送完毕后,它的生命周期也就结束了。仲裁特性
锁定机制
Virtual含义就是其sequencer 并不需要传递item,也不会与driver连接,其只是一个去协调各个sequencer的中央路由器。通过virtual sequencer我们可以实现多个agent的多个sequencer他们的 sequence的调度和可重用。Virtual sequence可以组织不同sequencer 的sequence群落。
UVM中有sequence机制,以往如果我们使用SV进行TB搭建时,我们一般会采用driver 一个类进行数据的参数,转换,发送,或者使用genetor和driver两个进行,这种方式可重用性很低,而且代码臃肿;sequence、sequencer、driver、sequence_item拆开,相互独立而又有联系,因此我们只需关注每一个类需要做的工作就可以,可重用性高。我在学习sequence时,我经常把sequence 比作蓄水池,sequence_item就是水,sequencer就是一个调度站,driver就是总工厂,通过这种方式进行处理,我们的总工厂不需要管其他,只需处理运送过来的水资源就可以,而sequencer只需要调度水资源,sequence只需要产生不同的水资源。Interface如果不进行virtual声明的话是不能直接使用在dirver中的,会报错,因为interface声明的是一个实际的物理接口。一般在dirver中使用virtual interface进行申明接口,然后通过config_db进行接口参数传递,这样我们可以从上层组件获得虚拟的interface接口进行处理。Config_db传递时只能传递virtual接口,即interface的句柄,否则传递的是一个实际的物理接口,这在 driver中是不能实现的,且这样的话不同组件中的接口一一对应一个物理接口,那么操作就没有意义了。Factory机制也叫工厂机制,其存在的意义就是为了能够方便的替换TB中的实例或者已注册的类型。一般而言,在搭建完TB后,我们如果需要对TB进行更改配置或者相关的类信息,我们可以通过使用factory 机制进行覆盖,达到替换的效果,从而大大提高TB的可重用性和灵活性。要使用factory机制先要进行:
factory表中(type_id::create)Callback机制其作用是提高TB的可重用性,其还可进行特殊激励的产生等,与factory类似,两者可以有机结合使用。与factory不同之处在于 callback的类还是原先的类,只是内部的callback函数变了,而factory是产生一个新的扩展类进行替换。
UVM组件中内嵌callback函数或者任务uvm_callbacks classUVM callback空壳类扩展uvm_callback类uvm_callbackfield_automation机制:可以自动实现copy、compare、print等三个函数。当使用uvm_field系列相关宏注册之后,可以直接调用以上三个函数,而无需自己定义。这极大的简化了验证平台的搭建,尤其是简化了driver和monitor,提高了效率。UVM中通过objection机制来控制验证平台的关闭,需要在drop_objection之前先raise_objection。验证在进入到某一phase时,UVM会收集此phase提出的所有objection,并且实时监测所有objection是否已经被撤销了,当发现所有都已经撤销后,那么就会关闭此phase,开始进入下一个phase。当所有的phase都执行完毕后,就会调用$finish来将整个验证平台关掉。如果UVM发现此phase没有提起任何objection,那么将会直接跳转到 下一个phase中。UVM的设计哲学就是全部由sequence来控制激励生成,因此一般情况下只在sequence中控制objection。另外还需注意的是,raise_objection语句必须在main_phase中第一个消耗仿真时间的语句之前。Config_db 机制主要作用就是传递参数使得TB的可配置性高,更加灵活。Config_db机制主要传递的有三种类型:interface虚拟接口,通过传递virtual interface使得dirver和 monitor能够与DUT连接,并驱动接口和采集接口信号。int,string,enum等,这些主要就是为了配置某些循环次数,id号是多少等等。object类,这种主要是当配置参数较多时,我们可以将其封装成一个object类,去包含这些属性和相关的处理方法,这样传递起来就比较简单明朗,不易出错。Config_db 的参数主要由四个参数组成,如下所示,第一个参数为父的根parent,第二个参数为接下来的路径,对应的组件,第三个是传递时的名字(必须保持一致),第四个是变量名。uvm_config_db #(virtual interface) :: set(uvm_root:.get(),"uvm_test_top.c1",'vif",vif);uvm_config_db #(virtual interface) :: get(this,"”,"vif",vif);Component 之间通过在new函数创建时指定parent参数指定子关系,通过这种方法来将TB形成一个树形结构。UVM中运行是通过Phase机制进行层次化仿真的。从组件来看各个组件并行运行,从phase上看是串行运行,有层次化的。Phase机制的9个phase是串行运行的,不同组件中的同一个phase都运行完毕后才能进入下一个phase运行,同一个phase在不同组件中的运行也是由一定顺序的,build 和 final是自顶向下。
sequence有很多的方法:常用的方法有使用default sequence进行调用,其会将对应的sequence 与 sequencer绑定,当dirver请求获得req时,sequencer就会调用对应的sequence去运行body函数,从而产生req。start函数进行,其参数主要就是对应的需要绑定的sequencer和该类的上层sequence。如此,就可以实现启动sequence的功能。sequence 中 raise objection和 drop objectionTB之外,我们可以搭建一个测试寄存器的agent,去通过前门或者后门访问去控制DUT的寄存器,使得 DUT按照我们的要求去运行。UVM中内建了很多RAL的sequence,用于帮助我们去检测寄存器,除此之外,还有一些其他的类和变量去帮助我们搭建,以提高RAL的可重用性和便捷性还有更全的覆盖率。UVM DPI (uvm_hdl_read()、uvm_hdl_deposit()),将寄存器的操作直接作用到DUT内的寄存器变量,而不通过物理总线访问。path设置为UVM_FRONTDOORDUT一侧的HDL路径:使用add_hdl_path
5. 从上面的差别可以看出,后门访问较前门访问更便捷更快一些,但如果单纯依赖后门访问也不能称之为“正道”。6. 实际上,利用寄存器模型的前门访问和后门访问混合方式,对寄存器验证的完备性更有帮助。
在通过前门配置寄存器A之后,再通过后门访问来判断HDL地址映射的寄存器A变量值是否改变,最后通过前门访问来读取寄存器A的值。
mirror、desired、actual value()
mirrored value) , 一个是期望值(desired value) 。auto prediction)和显式预测( explicit)。uvm_reg_map::set_auto predict()predictor,而是利用寄存器的操作来自动记录每一次寄存器的读写数值,并在后台自动调用predict()方法的话,这种方式被称之为自动预测。sequence直接在总线层面上对寄存器进行操作(跳过寄存器级别的write/read操作,或者通过其它总线来访问寄存器等这些额外的情况,都无法自动得到寄存器的镜像值和预期值。显式预测predictor,该predictor由UVM参数化类uvm_reg_predictor例化并集成在顶层环境中。adapter与map的句柄也一并传递给predictor,同时将monitor采集的事务通过analysis port接入到predictor一侧。monitor一旦捕捉到有效事务,会发送给predictor,再由其利用adapter的桥接方法,实现事务信息转换,并将转化后的寄存器模型有关信息更新到map中。UVC monitor需要具备捕捉事务的功能和对应的analysis port,以便于同predictor连接。
AHB(Advanced High-performance Bus)高级高性能总线。APB(Advanced Peripheral Bus)高级外围总线AXI (Advanced eXtensible Interface)高级可拓展接口
AHB主要是针对高效率、高频宽及快速系统模块所设计的总线,它可以连接如微处理器、芯片上或芯片外的内存模块和DMA等高效率模块。APB主要用在低速且低功率的外围,可针对外围设备作功率消耗及复杂接口的最佳化。APB在AHB和低带宽的外围设备之间提供了通信的桥梁,所以APB是AHB的二级拓展总线。AXI高速度、高带宽,管道化互联,单向通道,只需要首地址,读写并行,支持乱序,支持非对齐操作,有效支持初始延迟较高的外设,连线非常多。Master:能够发起读写操作,提供地址和控制信号,同一时间只有1个Master会被激活。Slave:在给定的地址范围内对读写操作作响应,并对Master返回成功、失败或者等待状态。Arbiter:负责保证总线上一次只有1个Master在工作。仲裁协议是规定的,但是仲裁算法可以根据应用决定。Decoder:负责对地址进行解码,并提供片选信号到各Slave。每个AHB都需要1个仲裁器和1个中央解码器。
2. AHB基本信号(经常会问Htrans和Hburst,以及AHB的边界地址怎么确定)
3. APB读操作
Assertion可以分为立即断言和并发断言。check并不能完全检测多个时序信号之间的关系,但是并发断言却可以使用简洁的语言去监测,除此之外,还可以进行覆盖率检测。sequence编写,将多个信号的关系用断言中 特定的操作符进行表示;property的编写,它可以将多个sequence和多个property进行嵌套,外加上触发事件;assert的编写,调用property就可以。编写完断言后我们可以将它用在很多地方,比如DUT内部,或者在top层嵌入DUT 中,还可以在interface处进行编写,基本能够检测到信号的地方都可以进行断言检测。fsm覆盖率、跳转覆盖率、分支覆盖率,他们是否都是运行到的,比如 fsm,是否各个状态都运行到了,然后不同状态之间的跳转是否也都运行到了。covergroup和coverpoint去覆盖我们想要覆盖的数据和地址或者其他控制信号。Condition又称为条件覆盖率,当条件覆盖率未被覆盖时,我们需要通过查看覆盖率报告去定位哪些条件没有被覆盖到,是因为没有满足该条件的前提条件还是因为根本就遗漏了这些情况,根据这个我们去编写相应的case,进而将其覆盖到。
spec文档中功能点的覆盖检测-code覆盖率主要是针对RTL设计代码的运行完备度的体现,其包括行覆盖率、条件覆盖率、FSM覆盖率、跳转覆盖率、分支覆盖率(只要仿真就可以,看看DUT的哪些代码没有动,如果有一部分代码一直没动,看一下是不是case没写到)。covergroup是不是写少了,case写少了;或者代码冗余。当功能覆盖率很低而代码覆盖率高时,表示代码设计是不是全面,功能点遗漏;covergroup写的是不是冗余了。只有当两者覆盖率都高的时候才表明我们验证的大部分是可靠的。对于流程的话
spec文档,将模块的功能和接口总线时序搞明白,尤其是工作的时序,这对于后续写TB非常重要;TB应该怎么搭建,我的case大致会有哪些,这些功能点我应该如何去覆盖,时序应该如何去检查,总结列出这样的一个清单;TB,包括各种组件,和一些基础的 sequence还有test,暂时先就写一两个基础的sequence,然后还有一些环境配置参数的确定等,最后能够将TB正常运行,保证无误;sequence和 case,然后去仿真,保证仿真正确性,收集覆盖率;case,和更换不同的seed去仿真;regression,通过不同的 seed去跑,收集覆盖率和检测是否有其它bug;driver和monitor还有sequence和 assertion的编写至关重要,只有正确理解时序才能编写出正确的TB。case好写,也比较快就可以达到较高的覆盖率,但是那些边边角角的case需要自己去琢磨,去分析还需要写什么case。这些难点就是重点,还要能够自动化监测判断是否正确。这个问题面试的时候经常问,建议面试之前考虑一下,再做决定
我是使用UVM验证方法学搭建的TB,然后在VCS平台进行仿真的。目录结构的话:主要由RTL文件、doc文件、tb文件、sim文件、script文件这几部分。
test sequence和验证平台是隔离独立的,可以更好的控制激励而不需要重新设计agent. 改变测试sequence可以简单高效提高代码覆盖率。UVM支持工业标准,这会促进验证平台标准化。此外,UVM通过OOP(面向对象编程)的特点(例如继承)以及使用覆盖组件提高了重复使用率。因此UVM环境方便移植,架构清晰,组件连接方便,有利于进行大规模的验证。parrot)还是被重载的类(bird),都要在定义时注册到factory机制中。bird)在实例化时,要使用factory机制式的实例化方式,而不能使用传统的new方式。parrot)要与被重载的类(bird)有派生关系。重载的类必须派生自被重载的类,被重载的类必须是重载类的父类。UVM更高的层次更接近用户,为了让用户少和底层组件打交道,所以层次越高优先级越高,高层次的set会覆盖底层次的set,如果是层次相同再看时间先后顺序,谁发生的晚谁有效,时间靠后的会覆盖之前的。
spec文档和协议,将DUT的功能和接口总线时序搞明白VIP或者是用别人给的VIP,搭建验证环境和TB,包括各种组件,各个模块的pkg,基础的 sequence还有test,暂时先就写一两个基础的 sequence,然后还有一些环境配置参数的确定等,最后能够将TB正常运行,保证无误;sequence和 case,然后去仿真,保证仿真正确性,收集覆盖率;case,和更换不同的seed去仿真;regression,通过不同的seed去跑,收集覆盖率和检测是否有其它bug;
driver给 DUT 发送激励,montior监测 DUT 输出的数据,参考模型( reference model )能实现与 DUT相同的功能,scoreboard把 monitor接受到的数据和 reference model的输出数据进行比对,如果比对成功就表示 DUT 能完成设计的功能,
factory机制的实现被集成在了一个宏中:uvm_component_utils。UVM内部的一张表中,这张表是factory功能实现的基础。只要在定义一个新的类时使用这个宏,就相当于把这个类注册到了这张表中。这样,factory机制可以实现:根据一个字符串自动创建一个类的实例,并且调用其中的函数(function)和任务(task),这个类的main_phase就会被自动调用。TLM通信的步骤:initiator和target,producer和consumer。target中实现tlm通信方法。tlm端口。port,一般是initiator的发起端。export,作为initiator和target的中间端口。imp,只能作为target接受request的末端。port可以连接同一个export或imp,但是单个port或export不能连接多个imp。connect函数进行连接,例如A(initiator)与B进行连接,可以使用A.port.connect(B.export)component。