目录
Q101.new()和new[]的区别
Q102.solve...before如何使用
Q103.mailbox和队列的异同
Q104.什么是静态变量
Q105.什么是生命周期
Q106.交叉覆盖率的优点
Q107.pass_by_value和pass_by_ref区别
Q108.$display和$write区别
Q109.同一个作用范围内使用枚举类型需要注意什么
Q110.敏感信号列表信号缺失会如何
Q111.covergroup在类中使用和类外分别如何使用
Q112.简述回调机制
Q113.三段式状态机是哪三段(状态转移、组合逻辑描述状态转移规律、电路输出)
Q114.什么是虚接口,为什么要使用虚接口
Q115.Verilog中for能不能综合
Q118.SystemVerilog中##n表示什么
Q119.UVM指的是什么?具有哪些特点,为什么要使用UVM?
Q120.简介工厂机制(factory)
Q121.简介事务级建模
Q122.uvm_component和uvm_object的区别
Q123.UVM中run_phase和main_phase的区别
Q124.为什么要使用phase机制
Q125.m_sequencer和p_sequencer区别
new( ):
1, 在使用new( )函数创建对象过程中,除了会为类中各个对象分配内存之外,还会将变量设置为默认数值;
2, new函数由于是构造函数,其本身不能有返回值,而是返回一个指向类对象的句柄,其类型就是类本身。
new[ ]:
一般new [ ]也是用来开辟内存并初始化,但是它主要用于设置动态数组的大小
与new( )不同点:
1, 调用new函数是用于将对象实例化,而new[ ]则是创建了一个含有多个元素的数组;
2, new( )函数可以利用参数来改变对象中变量的值,而new[ ]的参数仅仅被用作设置动态数组的大小。
1.例子
rand bit a;
rand bit b;constraint c1 {
a != b ;
solve a before b;
}2.结果
假如 a随机成了0,那么b就只能是1了。
假如 a随机成了1,那么b就只能是0了。3.使用solve和before的条件
不能使用randc。
随机值只能是整数。
- mailbox需要例化,而队列不用。
- mailbox中可以同时储存不同数据类型的数据(不建议),而队列不行。
- 队列中push_back和pop_front()为非阻塞,再取数时要保证队列大小大于0 (queue.size() > 0),而mailbox既有阻塞又又非阻塞。
- mailbox传递形式参数时,传递拷贝的是它的指针,而对于queue若不声明为ref(默认为input),那么传递数组时会对数组进行拷贝而不是影响原来的数组。
静态变量就是被static修饰的变量 (如 static int a)
在SV中,我们将数据的生命周期分为动态(automatic)和静态(static)
- 局部变量是动态生命周期,其生命周期同所在域共存亡,例如function/task中的临时变量,调用结束后,临时变量的生命也将终结。
- 全局变量是静态生命周期,从程序执行开始到结束一直存在,例如module中的变量默认都是全局变量。
- 如果数据变量被声明为automatic,那么在进入该进程/方法后,automatic变量会被创建,离开后会被销毁。
- 而static变量从仿真开始时就被创建,在进程/方法执行过程中,自身不会别销毁,还会被多个进程/方法共享
交叉覆盖率:可以同时测量两个或两个以上的覆盖点的值,如果一种由N种取值,另一个有M种取值,需要N X M交叉仓来存储所有的组合。
使用交叉覆盖率在搭配使用ignore bins/illegal bins等声明可以将覆盖率定义到更需要的点上。
pass_by_value 是将参数传递给function或task的默认方法,每个子例程保留该参数的本地副本,如果在子例程中更改了参数,不会影响原来的值.
pass_by_ref function和task直接访问作为参数传递的指针变量,传递的是指针,指向同一对象,如果不想更改数组值,可以使用const ref。
二者的区别在于,前者会自动换行,而后者不会;
- 枚举类型支持first,last,next,prev操作
- 枚举类型默认的值为int,若未在声明中指定数值,则第一个值为0,第二个值为1,依次递增;若在声明中需要指定数值,则必须满足所有的数值必须唯一这一条件,对于没有指定的元素,其数值是按照前一个元素的数值加1
- 枚举类型取值范围不能超多基础类型的有效范围!!!给四值逻辑变量赋值X/Z是合法的,但是必须给其之后的变量显式赋值,一般在设计中很少赋值为x/z
不完整的信号列表会造成不同的仿真和综合结果,因此需要保证敏感信号的完备性;
当敏感信号不完备时,会使得仿真结果不一样,这是因为仿真器在工作时不会自动补充敏感信号表。
如果缺少信号,则无法触发和该信号相关的仿真进程,也就得不到正确的仿真结果。
Callback机制其作用是提高 TB的可重用性, 其还可进行特殊激励的产生等, 与 factory类似,两者可以有机结合使用。 与 factory不同之处在于 callback的类 还是原先的 类,只内部callback函数变了,而 factory这是产生一个新的扩展类进行替换。
1) UVM组件中内嵌 callback函数或者任务
2) 定义一个常见的 uvm_callbacks class
3) 从 UVM callback空壳类扩展 uvm_callback类
4) 在验证环境中创建并登记 uvm_callback
- 第一个always语句实现同步状态跳转;
- 第二个always语句采用组合逻辑判断状态转移条件;
- 第三个always语句描述状态输出(可以用组合电路输出,也可以时序电路输出)。
实例可参考:三段式状态机_我去给你买橘子吃的博客-CSDN博客_三段式状态机三段式状态机https://blog.csdn.net/weixin_40936351/article/details/91347983?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167029467616782429777894%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167029467616782429777894&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-91347983-null-null.142^v67^control,201^v4^add_ask,213^v2^t3_control1&utm_term=%E4%B8%89%E6%AE%B5%E5%BC%8F%E7%8A%B6%E6%80%81%E6%9C%BA%E6%98%AF%E5%93%AA%E4%B8%89%E6%AE%B5&spm=1018.2226.3001.4187
virtual interface的本质是指向interface的指针,因此其并不是一个真实存在的实体,而interface是一个真实存在的实体;基于OOP的测试平台会通过调用virtual interface来间接操作实际的信号。
通过引入interface可以简化模块儿之间的连接,即interface是连接硬件的,其是硬件语言;但对于验证来说,其描述语言往往是软件语言,interface无法在基于OOP的测试平台中实例化,因此我们无法通过interface把激励传送到DUT中;为了解决这个问题,引入了virtual interface,使得基于OOP的验证环境可以通过虚接口把激励传送给DUT。
在硬件电路Verilog中for语句一般不在可综合代码中使用,因为for循环会被综合器展开为所有变量情况的执行语句,每个变量独立占用寄存器资源,每条执行语句并不能有效地复用硬件逻辑资源,造成巨大的资源浪费。简单的说就是:for语句循环几次,就是将相同的电路复制几次,因此循环次数越多,占用面积越大,综合就越慢。
##
表示周期延迟符号,如##n表示在n个时钟周期后,##0表示在当前周期,即交叠周期。##[min:max]
表示在一个范围内的时钟周期延迟。min和max必须是非负数,序列会在从min到max时间窗口中最早的时间来匹配。
UVM 其实就是 SV 的一个封装,将我们在搭建测试平台过程中的一些重复性和重要的工作进行封装,从而使我们能够快速的搭建一个需要的测试平台,并且可重用性还高。但是UVM又不仅仅是封装,其还有一些内建的基类,方法和函数供验证人员去使用,极大的方便了验证人员搭建验证平台.
常见的UVM机制:
- field_automation机制
对field_automation最直观的感受是,他可以自动实现copy、compare、print等三个函数。当使用uvm_field系列相关宏注册之后,可以直接调用以上三个函数,而无需自己定义。这极大的简化了验证平台的搭建,尤其是简化了driver和monitor,提高了效率。
- config_db机制
config_db机制在UVM验证平台中主要用于参数传递。它们通常都是成对出现的,set函数是寄信,而get函数是收信。函数格式如下:
uvm_config_db# (Type)::set(this,"inst_path","field",value); //将value寄送给 “field(字段)”
uvm_config_db# (Type)::get(this,"","field",field) //field将接收参数的传递
结果得到:field = value;
所谓“字段”,是变量名包含的内容。在set和get函数中为第三个参数,字段在set和get中必须保持一致,才能保证参数的正确传递。例如要将参数值10,传递给int类型的变量data,则:
uvm_config_db# (int)::set(this,"env.i_agt.drv","dat",10); //将10寄送给env组件中的成员变量“data”
uvm_config_db# (int)::get(this,"","dat",data) //字段“dat”保持一致,成员变量data将接收参数的传递
结果得到:data = 10;
- objection机制
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中第一个消耗仿真时间的语句之前。
- factory机制
factory机制的优势在于其具有重载功能。重载并不是factory机制的发明,只是factory机制的重载与这些重载都不一样。要想使用factory机制的重载功能,必须满足以下要求:
1) 无论是重载的类(parrot)还是被重载的类(bird),都要在定义时注册到factory机制中。
2) 被重载的类(bird)在实例化时,要使用factory机制的方式进行实例化,而不能使用传统的new的方式。
3) 最重要的是,重载的类(parrot)要与被重载的类(bird)有派生关系。重载的类必须派生自被重载的类,被重载的类必须是重载类的父类。
4) component与object之间互相不能重载。虽然uvm_component是派生自uvm_object,但是这两者根本不能重载。因为,从两者的new参数的函数就可以看出来,二者互相重载时,多出来的一个parent参数会使factory机制无所适从。
当然,factory机制的实现被集成在了一个宏中:uvm_component_utils。这个宏最主要的任务是,将字符串登记在UVM内部的一张表中,这张表是factory功能实现的基础。只要在定义一个新的类时使用这个宏,就相当于把这个类注册到了这张表中。这样,factory机制可以实现:根据一个字符串自动创建一个类的实例,并且调用其中的函数(function)和任务(task),这个类的main_phase就会被自动调用。
- callback机制
在UVM验证平台中,callback机制最大的用处就是提高验证平台的可重用性。很多情况下,验证人员期望在一个项目中开发的验证平台能够用于另外一个项目。但是,通常来说,完全的重用是比较难实现的,两个不同的项目之间或多或少会有一些差异。如果把两个项目不同的地方使用callback函数来做,而把相同的地方写成一个完整的env,这样重用时,只要改变相关的callback函数,env可完全的重用。
除了提高可重用性外,callback机制还用于构建异常的测试用例,VMM用户会非常熟悉这一点。只是在UVM中,构建异常的测试用例有很多种方式,如factory机制重载,而callback机制只是其中的一种。
- phase机制
在不同的时间做不同的事情,这就是UVM中phase的设计哲学。但是仅仅划分成phase是不够的,phase的自动执行功能才极大方便了用户。当new语句执行完成后,后边的connect语句肯定就会自动执行。现引入phase概念,将前面的new的部分包裹进build_phase里面,把后边的connect语句包裹进connect_phase里边,很自然的,当build_phase执行结束后就应该自动执行connect_phase。
phase的引入在很大程度上解决了因代码顺序杂乱可能会引起的问题。遵循UVM的代码顺序划分原则(如build_phase做例化工作,connect_phase做连接工作等),可以在很大程度上减少验证平台开发者的工作量,使其从一段杂乱的工作中解脱出来。
UVM中的phase按照其是否消耗仿真时间($time打印出的时间)的特性,可以分成两大类:一类是function_phase,如产生build_phase、connect_phase等,这些phase都不消耗仿真时间,通过函数来实现;另一类是task_phase,如run_phase等,他们消耗仿真时间,通过任务来实现。给DUT施加激励、监测DUT的输出都是在这些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
- 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全部发送完毕后,它的生命周期也就结束了。
- 通信机制TLM
UVM中,通常使用TLM(Transaction Level Modeling)实现component之间transaction级别的通信。TLM现在已经发展到TLM2.0,是对TLM1.0的扩展。但是TLM1.0足以满足大多数完整的验证平台的通信要求,所以主要学习TLM1.0。
Factory机制也叫工厂机制,其存在的意义就是为了能够方便的替换 TB中的 实例或者已注册的类型 。一般而言,在搭建完 TB后,我们如果需要对 TB进行 更改配置或者相关的类信息,我们可以通过使用 factory机制进行覆盖,达到替换的效果,从而大大提高 TB的可重用性和灵活性。要使用 factory机制先要进行
1. 将类注册到 factory表中2. 创建对象,使用对应的语句
3. 编写相应的类对基 类进行覆盖。
TLM通信步骤:
- 分辨出initiator和target,producer和consumer。
- 在target中实现TLM通信方法 ,例如,在comp2中由于有一个uvm_nonblocking_put_imp #(request, comp2)nbp_imp,因此需要实现两个方法try_put()和can_put();而comp4中有一个uvm blocking get_imp #(request, comp bg_imp,则需要实现对应的方法get()。
- 在两个对象中创建TLM端口。
- 在更高层次中通过connect()函数将两个对象的端口进行连接。
从数据流向来看,传输方向可以分为单向(unidirection)和双向( bidirection)
- 单向传输:由initiator发起request transaction。
- 双向传输:由initiator发起request transaction,传送至target;而target在消化了request transaction后,会发起response transaction,继而返回给initiator。
端口的按照类型可以划分为三种:
- port:经常作为initiator的发起端,initiator凭借port才可以访问target的TLM通信方法。
- export:作为initiator和target中间层次的端口。
- imp:只能作为target接收request的末端,它无法作为中间层次的端口,所以imp的连接无法再次延伸。
端口的使用:
- 就单向端口而言,声明port和export作为request发起方,需要指定transaction类型参数,而声明imp作为request接收方,不但需要指定transaction类型,也需要指定它所在的component类型。
- 就声明双向端口而言,指定参数需要考虑双向传输的因素,将传输类型transaction拆分为request transaction类型和response transaction类型。
- 从对应连接关系得出TLM端口连接的一般做法:
- 在initiator端例化port,在中间层次例化export,在target端例化imp。
- 多个port可以连接到同一个export或者imp;但是单个port或者export无法连接多个imp。这可以抽象为多个initiator可以对同一个target发起request,但是同一个initiator无法连接多个target。
- port应为request起点,imp应为request终点,而中间可以穿越多个层次。基于单元组件的自闭性考虑,路桑建议在穿越的中间层次声明export,继而通过层层连接实现数据通路。
- port可以连接port、export或者imp; export可以连接export或者imp; imp只能作为数据传送的终点,无法扩展连接。
Peek和get的流向一样
- 阻塞传输方式将blocking前缀作为函数名的一部分,而非阻塞方式则名为nonblocking。阻塞端口的方法类型为task,这保证了可以实现事件等待和延时;非阻塞端口的方式类型为function,这确保了方法调用可以立即返回。
- 我们从方法名也可以发现,例如uvm_blocking_put_PORT提供的方法task put()会在数据传送完后返回,uvm_nonblocking_put_PORT对应的两个函数try_put()和can_put()是立刻返回的。
uvm_put_PORT则分别提供了blocking和nonblocking的方法,这为通讯方式提供了更多选择。blocking阻塞传输的方法包含:
Put():initiator先生成数据Tt,同时将该数据传送至target。
Get():initiator从target获取数据Tt,而target中的该数据Tt则应消耗。
Peek(): initiator从target获取数据Tt,而target中的该数据Tt还应保留。
这六个非阻塞函数与对应阻塞任务的区别在于,它们必须立即返回,如果try_xxx函数可以发送或者获取数据,那么函数应该返回1,如果执行失败则应返回0。或者通过can_xxx函数先试探target是否可以接收数据,如果可以,再通过try_xxx函数发送,提高数据发送的成功率。
- UVM验证平台所有的组件都要派生自UVM的类。( uvm_component和uvm_object以及他俩的“孩子”)。uvm_component继承与uvm_object。
- SV中“固定资产”(test、environment、agent等)派生自uvm_component。SV中的“非固定资产”(比如数据包transaction等的产生)派生自uvm_object。
- 只有uvm_component派生的类,才有节点(每个结点是一个class实例)。
- uvm_component和uvm_object的主要区别:uvm_object是有生命周期的;uvm_component没有生命周期。也就是说仿真过程中,uvm_object在启动后可以finish;uvm_component是伴随仿真过程一直要存在的。
- uvm_component及派生的类是uvm树的骨架,需要标明parent表明继承关系,uvm_object不用说明parent。每一个派生自uvm_component或其派生类的类在其new函数中要指明两个参数:name和parent,这是uvm_component类的一大特征(如果parent设定为null,那它将作为uvm_top的“孩子”;parent设定为this,那它的parent为当前的类)
- main_phase( )是12个分支任务 phase 中的一个,run_phase 的执行贯穿这12个分支 phase。
- run_phase( )和 main_phase( )是并行执行的,只有等12个分支phase全部执行完,才开始执行 extract_phase 等后面的phase。
run_phase( ) 和 main_phase( )的区别:
- 首先,Objection机制是UVM中唯一可以控制仿真开始和结束的方式。在任务 phase 中,至少有一个任务 phase 要在消耗第一条消耗仿真时间的语句执行之前要挂起 Objection。
- 如果12个分支中有一个任务phase(比如main_phase)挂起了 Objection ,那么 run_phase 中不需要挂起 Objection 就可以执行其中的代码;但是这时,run_phase 的运行时间被动地受这个挂起 Objection 的分支任务phase的控制。
- 而如果在 run_phase 中挂起了 Objection ,没有在main_phase中挂起,main_phase中的操作则不会执行。
phase机制主要目的就是确保各个组件之间可以有序运行
那么UVM 的各个phase又是如何运行的呢?
1.各个phase按照一定的顺序,从上往下顺序运行,
2.每个组件内部的class 按照phase名称顺序运行。比如driver内有两个class 都有build phase 和main phase 那么,是两个class内的build phase都运行完了以后再运行main phase
3.UVM内部的各个组件中的phase也是按照顺序运行。同理driver 和sequencer都有build phase main phase 那么是当driver 和sequencer内的build phase 都运行完以后再会运行driver 和sequencer的main phase以上即为顺序运行
4.UVM内部的phase会自动运行,但是用户自定义的phase只有调用的时候才会运行。如main phase , main phase11, main phase22。假如没有调用main phase11那么这个phase是不会运行的
更多参考:UVM-phase机制_卢卡猫的博客-CSDN博客_uvm的phase机制文章目录1.UVM Phase2.phase的执行3.仿真开始4.仿真结束4.1 objection机制1.UVM Phaseuvm利用phase机制实现了各个组件之间的同步。因为每个组件都包括一些预定义的同名phase,在没有执行完所有组件的当前phase之前绝对不会去执行所有组件的下一个phase。phase机制存在的意义:传统的硬件设计模型在仿真开始之前就已经完成了例化和连接,而SV的软件部分对象例化则在仿真开始之后执行只通过new()函数对对象例化,无法解决一个重要问题,就是验证环境在实https://blog.csdn.net/sinat_41774721/article/details/121786702?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167029809916800180658551%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=167029809916800180658551&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-121786702-null-null.142^v67^control,201^v4^add_ask,213^v2^t3_control1&utm_term=%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8phase%E6%9C%BA%E5%88%B6&spm=1018.2226.3001.4187
equence/item一旦“挂载”到某一个sequencer上,该sequencer的句柄即被赋值于m_sequencer(uvm_sequencer_base类);而p_sequencer通常需要通过在定义sequence类使,通过宏`uvm_declare_p_sequencer(SEQUENCER)声明,间接定义p_sequencer成员变量。m_sequencer与p_sequencer均指向同一个挂载的sequencer句柄,只是前者使父类句柄,后者往往是所挂载sequencer句柄(子类句柄)
更多请见:
UVM m_sequencer 和 p_sequencer_踩坑记录的博客-CSDN博客p_sequencer https://blog.csdn.net/qq_40456702/article/details/126373808?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167029840016782425117686%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=167029840016782425117686&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-126373808-null-null.142^v67^control,201^v4^add_ask,213^v2^t3_control1&utm_term=m_sequencer%E5%92%8Cp_sequencer%E5%8C%BA%E5%88%AB&spm=1018.2226.3001.4187