1. $display和$monitor的区别?
(1) $display:主要起到一个打印数据的作用,将要打印的信息输出在仿真窗口中,可以在任何地方调用,
(2) $monitor:起到一个监视器的功能,监视要输出的内容,当他有变化时才输出,输出在仿真控制台上创建一个监视器窗口,只能在模块中调用;
一般使用$display来打印仿真过程中的一些信息,用$monitor来监视调试过程中的变量变化情况;
2. clocking block的作用?
对于设计中的竞争问题,通常可以使用非阻塞赋值代替阻塞赋值来消除竞争问题。在验证仿真过程中,仍然也会存在时序竞争的问题,以下主要讨论在验证仿真中的时序问题。
(1) 通过使用clocking block来实现采样和驱动与时钟的同步,消除竞争(这里主要是由于在时钟上升沿采样或者输出一个信号时,没有clocking的规定,系统不知道在上升沿到来之际,
到底是采样原来的值还是上升沿变化后的值,同理也不知道到底驱动上升沿变化之前的值还是变化之后的值),
(2) 在时钟边沿前采样(采样原来的值),在时钟边沿后驱动(驱动变化后的值)
(3) 为什么会有多个时钟块:因为对于不同的组件,他的总线的输入和输出方向有可能是不一致的,因此针对这些不同的组件,应该添加不同的时钟块加以区分,这里的时钟块起到了
类似modport的功能。
在定义了clocking block时,需要设置该cb是在时钟上升沿还是下降沿采样,因此在程序中,只需要使用@intf.cb即可实现等待时钟边沿的效果
(4) 接口中的logic和wire
一般建议在接口中使用logic数据类型,因为wire类型只能使用连续赋值语句,但是连续赋值是同步的,如果要使用异步的赋值,那么就不能使用wire类型了;
而logic不能有多个驱动源去驱动,因此,若涉及到多个驱动源驱动同一个信号,则需要使用wire类型
(5) modport的用法:
在定义cb时可以将信号进行分组,然后在例化模块传入接口时需要指明传递的是该接口哪一个modport,例如 module1 test(intf.modport1)
(6) SV中仿真的timeslot,在SV中,数据的采样、驱动以及信号变化等都是在时钟沿上完成的,但是在软件的仿真环境中无法做到真正的并行执行,因此在一个时间片中划分不同的阶段
来执行不同的操作。
active区域 :执行设计的事件
observa区域 :执行断言语句
reactivate区域 :执行测试平台
postponed区域 :所有设计都稳定后的信号采样时间
因此,想要采样到稳定的设计的值,应该在时钟沿的上一个时钟片中的postpone区域采样(使用1step延时),然后在时钟沿后
3. SV中include和import的区别和联系:
(1) import使用包路径。当某个package编译到lib后,处在同一个lib的文件就可以使用import语句将package里面的内容引用到文件里面(不同lib之间不能import),并且在仿真的过程中还能动态的去使用;
(2) include使用相对路径或绝对路径,而相当于把外部的文件内容直接复制到当前文件中,不能动态使用
4. SV中的program块
program是SV为了测试(非UVM环境)而准备的环境外壳。也因此,program中不可以出现硬件行为语句,例如always、module、interface和其它program例化;program对数据的采样会发生在
reactive阶段即硬件变化后的稳定数据,从而避免采样竞争问题(数据不稳定);program也具备自动结束仿真的隐性方法,这也是module所不具备的。
5. $sformatf() / $sformat()函数
$sformatf()直接返回一个格式化的字符串
$sformat()需要传入第一个参数去接收字符串
6. case,casex 以及casez的区别
(1) 在case语句中,敏感表达式中与各项值之间的比较是一种全等比较,每一位都相同才认为匹配。
(2) 在casez语句中,如果分支表达式某些位的值为高阻z,那么对这些位的比较就会忽略,不予考虑,而只关注其他位的比较结果。
(3) 在casex语句中,则把这种处理方式进一步扩展到对x的处理,即如果比较双方有一方的某些位的值是z或x,那么这些位的比较就不予考虑。
7. UVM的factory机制的重载使用的步骤
factory的实现包含三步
(1)注册:当定义一个类的时候,必须要对其类型进行注册。`uvm_component_utils()
(2)实例化对象:在对 component或 object 型对象进行实例化的时候要使用静态方法,不能采用 new()去实例化。也就是要用如下实例化格式:
object_name = class_type::type_id::create("object_name",this);
(3)重载override:在需要override的时候,按照类型或者名称去override原来的对象。
// 注意,这里的class是定义类时定义的类名,比如class my_driver extends uvm_driver,那么class就是my_driver,而不是定义类的构造函数时传进去的name属性;
set_type_override_by_type(oringin_class::get_type(), new_class::get_type());
// 以上代码等价于下面代码
set_type_override("oringin_class_name", "new_class_name")