SV知识点总结
Tags: Learn
-
program和module有什么区别?
Program是在SystemVerilog中新添加的,它用于以下目的:
- 分离testbench与DUT
- 有助于确保testbench和DUT没有竞争冒险(race condition)
- 提供了执行testbench的入口点
- 通过(program … endprogram)指定了Reactive Region的调度。
program内部不能包含always语句块,而module可以
program中不能包含UDP,modules或其他program实例,而module可以
program在调度队列的re-active region中执行,module在active region中执行
program可以调用modules或其他program中task或function,但是module不能调用program中的task或function。 -
packages的用途是什么?
在Verilog中,模块内变量/任务/函数的声明特定于模块(module)。SystemVerilog的package结构允许声明全局变量/任务/函数,从而在modules/classes之间使用。
packages可以包含module/class/function/task/constraints/covergroup等声明,在使用时需要使用范围解析运算符或import来访问packages中的内容。
-
$ rose和posege有什么区别?
posege返回一个事件(event),而$rose返回一个布尔值,因此它们是不可互换的。
-
动态数组在new时可以不分配大小吗 ?
不可以
-
端口设置Ref 代表的意思 Ref的使用规则?
Ref表示的是引用,若方法中的变量类型是ref类型,则函数里面对数据的操作对外部实时可见,若要求方法中的数据操作不影响数据变化,可用const限定,意思为只读不写
-
Class或package中声明的function/task默认时静态还是动态方法
动态(软件中)只有task可以调用task,task可以消耗时间而function不能从最大灵活性角度考虑,所有用于调用的子程序都应该被定义成函数而非任务
-
类里面的interface为什么要是virtual?
需求因素:在systemverilog中的软件环境中无法实例化硬件module(interface),但是需求验证环境中的class驱动interface。
为什么是virtual:定义一个interface,且实例化多个后,如果没有定义virtual,则在任何一个实例中修改了某个信号值,在其他实例中都会受到影响。如果定义了virtual,则每个实例独立,如果该interface只有一个实例,可用可不用virtual,有多个实例,需要virtual。更好的办法是,都加上virtual。virtual interface只是申明一个handle, 保存实际接口的句柄。就好像一个指针一样, 可以在程序进行中进行construct构造, 所以class里必须是virtual interface
接口interface作为一个类,方便了不同的模块直接的连接(使模块直接的连接看起来更为简洁,修改也更为方便。)但是不同模块直接调用(多出实例化),可能某一个值改变而改变了所有使用此接口处的值(相当于于物理连接)。而不同模块使用virtual interface以后,就类似一个指针用来指向此接口。Virtual interface作为一个句柄,此时在该class中调用时,专属这一个接口对实际的静态interface并无影响。
类似virtual interface还有virtual task 、virtual function 、virtual class等。其应该主要体现出OOP的思想,可以根据自己的需要快速的搭建验证环境
-
解释数据类型logic,reg,和wire之间的区别和联系
Wire:用于连接不同的元件,不存储值,被连续赋值(assign)或端口(port)驱动。
Reg:并不意味实际的存储器,代表verilog/sv 中的数据存储元素,保存值,直到被下一次赋值(阻塞或非阻塞),可被综合为触发器,锁存器或组合电路。过程赋值
Logic:不能被多驱动,改进reg数据类型,可以被连续赋值。连续赋值。过程赋值 -
SV clocking block的用处和好处?
clocking只能在module/interface/checker/program中声明,不能在function/task/package中。clocking块里声明的inout类型相当于对具有相同名字的input和output的两个信号同时进行声明的简略写法,和port声明里的inout意义不同
主要是为了更好解决testbench和DUT之间的timing和同步的问题,
Interface 指定了testbench和DUT之间的通信信号,但是Interface,没有指定任何的时序规则和同步要求Clocking block指定timing规则和同步要求,以确保在正确的时间与DUT交互,避免竞争冒险问题。
-
使用SV 避免testbench和DUT之间的竞争冒险的方法有哪些?
- Module中的initial语句块调度在active region, Program中的initial语句快调度在reactive region。
- 在program中使用非阻塞赋值来驱动设计信号,在re-NBA区域对其进行更新
- 通过带有 #0 input skews的clocking blocks。
-
Timescale的理解描述
Timeslot:中发生的行为,可以认为是实际电路中同一时刻并行完成的,而在仿真中是有先后顺序调度的。
Preponed 时间片入口,断言采样时间 断言采样到的值一定是旧值
active/in-active/no-block module中代码执行时间
active区域 阻塞赋值直接在本区域完成
in-active区域: #0延时的线程会在该区域执行
NBA域 完成非阻塞赋值的操作使赋值行为生效 环境”采样module信号是在NBA之后,那么采样到的就是“新值”;
observed 断言检查时间
re-active/re-inactive/re-NBA program中代码执行时间
program中线程开始执行,program中的re-active域就相当于module中active域一样,这里的阻塞赋值立即执行,非阻塞赋值被推如re-NBA域
postponed 时间片出口
除了顺序执行的非阻塞赋值会立即生效外,其他线程大体都会需要一个采样-生效的过程,而这个采样点一般是preponed域、observed域。
-
fork-join_none与循环语句共同使用的行为探究
- fork-join_none提起线程本身不会阻塞时间片前进;
- fork-join_none不会立即执行,而是暂时被挂起等待延时语句阻塞时间片时,再被执行;
eg1 begin fork $display("dislpay fork1"); join_none fork $display("dislpay fork2"); join_none fork $display("dislpay fork3"); join_none #10ns; End #display fork3 #display fork3 #display fork3
begin for(int i=0; i<10; i++)begin fork $display("A: i = %0d", i); join_none end end #10ns; // A: i = 10; // A: i = 10; // A: i = 10; ….. /*循环中的10个线程都被提起来都等待执行,for循环内没有阻碍时间进程的语句因此for循环直接执行 完成,跳出循环时i==10,遇到#10ns语句执行所有悬挂的打印线程。对于所有打印的线程,i值均为 10,因此打印了10次i = 10。*/
begin for(int i=0; i<10; i++)begin fork automatic int k = i; $display("B: k = %0d", k); join_none end end #10ns; /* # B: k = 9; # B: k = 8; # B: k = 7; ….. 其中int k创建语句会立即执行不会被挂起,因此i的值通过k保留了下来。等到#10ns时候指令 打印时,每个线程都打印了自己作用域内的k值,因此时没有问题的。*/
-
build_phase的执行顺序
要分清build_phase执行和真正component的create的创立是有区别的。真正节点的建立是new函数的执行。
上图的执行build_phase和creat的创建顺序。
2的build_phase的执行(按照书写顺序创建子节点3.4.5.6),然后执行到4的build_phase(按照字母顺序排序)创建了7.8.9(按照书写顺序)然后执行789的build_phase(按照字母排序)但是一个节点的build_phase执行时如果还有子节点,就先进行子节点,在执行兄弟节点的子节点build_phase,所以7.8.9.10.的build_phase的执行顺序为7.8.10.9
build_phase是按自上而下的原则执行的,但只有直接上下级关系的节点能保证先后顺序。
对于在UVM树中具有叔侄关系的节点的创建顺序,并不能保证谁先谁后。
build_phase是按照深度优先的原则遍历的。
create操作是在父节点的build_phase里执行,同级节点的create操作是按照代码书写顺序执行的.
当该父节点的build_phase执行完,开始执行其子节点的build_phase时,则按照字母表顺序执行各子节点的build_phase.
要分清build_phase执行和真正component的create的区别。
**对一个节点来说:**
其create操作是在父节点的build_phase中完成的。
该节点的build_phase, 是在其父节点的build_phase完成后,将该父节点的所有儿子节点按字母表排序,顺序执行的。
在其build_phase中,按书写顺序create其下级子节点。
一个节点,只有其下的所有子孙节点的build_phase完成后,才开始执行其兄弟节点的build_phase.
-
Systemverilog中有哪些覆盖率的类
代码覆盖率:又包括(行覆盖率,路径覆盖率,状态覆盖,翻转覆盖)
功能覆盖率:是最重要的,主观的,反映设计意图实现的完备性。
如代码覆盖率高而功能覆盖率低就代表可能代码没有实现一些功能
如代码覆盖率低而功能覆盖率高就代表功能测试表不够完备。
断言覆盖率:衡量在测试过程中断言的触发。
寄存器覆盖率:寄存器时候都测试到。可读写域的检查。 -
解释Abstract class(抽象类)和virtual meath
- Abstract class(抽象类)是可以扩展但不能直接实例化的类。使用virtual关键字定义的。
- 虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖
- 抽象方法只能在抽象类中声明,虚方法不是。其实如果类包含抽象方法,那么该类也是抽象的,也必须声明为抽象的。
- 抽象方法必须在派生类中重写,这一点跟接口类似,虚方法不必。
抽象方法和虚方法的区别 :
~抽象方法和虚方法的区别在于:虚拟方法有一个实现部分,并为派生类提供了覆盖该方法的选项,相反,抽象方法没有提供实现部分,强制派生类覆盖方法(否则 派生类不能成为具体类); ~abstract方法只能在抽象类中声明,虚方法则不是; ~abstract方法必须在派生类中重写,而virtual则不必; ~abstract方法不能声明方法实体,虚方法则可以。
Abstract class是不打算实例化的基类,只用来派生子类。可将其抽象化。
用途:有时候需要描述一组对象的属性而无需知道其实际的行为,派生的扩展子类可以通过abstract class 共享方法。
Virtual methods是类的多态必须的,父类的句柄指向子类同名的方法,需要将父类的方法声明为虚方法。
-
用来建立scoreboard的数据结构是什么
Queue(队列)或mailbox
队列像链表一样,可以在队列中的任何位置添加或删除元素,而无需单步执行前面的元素。 像数组一样,可以直接访问带有索引的任何元素,而无需分配新的数组并复制整个内容
-
什么是$root?
top-level scope,可用于引用任意层次中例化的模块
-
在constraint之前“solve”是什么意思?
如果用户要指定约束求解器求解约束的顺序,则用户可以在约束之前通过solve指定顺序
SystemVerilog添加了专门的always_ff建模触发器
SystemVerilog添加了专门的always_latch建模锁存器
SystemVerilog添加了专门的always_comb建模组合逻辑 -
在systemverilog中为function和task添加了哪些功能?
不需要begin和end。function可以具有void返回类型,task可以具有return
-
什么是封装(Encapsulation)?
将数据和方法绑定在一起。
-
Associative arrays(关联数组)和dynamic arrays(动态数组)之间的区别?
关联数组:具有字符串索引功能,在编译时分配内存。
动态数组:在运行时分配内存,构造函数用于初始化动态数组的大小。
-
与队列(queue)相比,链表(linked-list)的优点是什么?
队列有固定的顺序,很难将数据插入队列中。但是链表可以轻松地将数据插入任何位置。
-
不使用randmize或rand,生成随机循环数组
int UniqVal[10]; foreach(UniqVal[i]) UniqVal[i]= i; UniqVal.shuffle(); //打乱排序
-
解释pass by ref和pass by value的区别?
pass by value 是将参数传递给function或task的默认方法,每个子例程保留该参数的本地副本,如果在子例程中更改了参数,不会影响原来的值
pass by ref function和task直接访问作为参数传递的指针变量,传递的是指针,指向同一对象,如果不想更改数组值,可以使用const ref。 -
如何在program语句中实现always语句块?
//用forever和fork join、any,none加begin end实现 always @(posedge clk or negedge reset) begin if(!reset) begin data <= '0; end else begin data <= data_next; end end // Using forever : slightly complex but doable forever begin fork begin @ (negedge reset); data <= '0; end begin @ (posedge clk); if(!reset) data <= '0; else data <=data_next; end join_any disable fork end
-
什么是前向引用?如何避免此问题?
有时需要在类声明之前声明另一个类变量。例如,如果两个类各自需要对方的句柄。使用typedef可以解决此问题,为第二个类提供前向声明/引用
-
Covergroup中的option和type_option的区别,
type_option与option的区别,type_option会影响最后总和的结果,而option影响的是每个intance内的结果。这一点对于weight, comment等其他参数也一样