汇总了数字芯片实验室公众号的文章,微信搜索即可。
附IC面试常考题链接:
**callback(回调)**是一种在不更改实际代码的条件下更改为验证组件(例如driver,generator或monitor)行为的机制
class abc_transactor;
virtual taskpre_send();
endtask
virtual taskpost_send();
endtask
task xyz();
this.pre_send();
this.post_send();
endtask : xyz
endclass :abc_transactor
class my_abc_transactor extend abc_transactor;
virtual taskpre_send();
... //This function is implemented here
endtask
virtual taskpost_send();
... //This function is implemented here
endtask
endclass :my_abc_transactor
在上面的例子中,基类abc_transactor具有3个tasks,其中2个被声明为virtual task并且没有具体实现。另一个已完全实现xyz() task会调用这两个virtual task。(Virtual task必须实例化后才可使用,但是静态)
未实现的virtual task称为callback taskes(回调任务)。从基类abc_transactor扩展的子类my_abc_transactor将实现先前未实现的pre_send() task和post_send() task,无需更改xyz() task的代码。
通过这种方式,我们可以将可执行代码注入到函数xyz()task中,而无需对其进行修改。
例如你正在编写将被多个测试环境使用的基类,并且对于每个测试环境,基类中的某些task代码将发生变化,这时就可以将该task/function实现为callback method(回调方法)。
顾名思义,Factory pattern(工厂模式)旨在解决对象创建的问题。
class TOY; // Common data memeber string type;
string type;
virtual function stringget_type();
endclass
class TOY_Tank extends TOY;
function new();
this.string ="Toy Tank";
endfunction
string function stringget_type();
return this.string;
endfunction
endclass
class TOY_Bus extends TOY;
function new();
this.string ="Toy Bus";
endfunction
string function stringget_type();
return this.string;
endfunction
endclass
上面是我们将要创建的对象。我们需要解决的下一个问题是编写TOY_factory类本身。让我们考虑以下情况:我们传递“1”来获得TOY_Tank类的实例,传递“2”来从TOY_Bus类的实例。
TOY_factory如下所示。
class TOY_factory;
Toy my_toy
// Common methods
function toy get_toy(inttype);
if(type == 1)
this.my_toy =new TOY_Tank();
if(type == 2)
this.my_toy =new TOY_Bus();
return this.my_toy;
endfunction
endclass
**Wire:**用于连接不同的元件,不存储值,被连续赋值(assign)或者端口(port)驱动
**Reg:**并不意味实际的寄存器,代表Verilog / SystemVerilog中的数据存储元素。保存值,直到将下一个赋值(不是通过assign语句赋值)。可以被综合成触发器,锁存器或组合电路
**Logic:**不能被多驱动,改进了Reg数据类型,可以被连续赋值。
interface指定Testbench和DUT通信的信号。但是interface没有明确指定任何时序规则和同步要求。
SystemVerilog添加了clocking block指定任何时序规则和同步要求,以确保Testbench在正确的时间与DUT交互,还可以避免竞争冒险问题。
module中的initial语句块调度在Active region,而program中的initial语句块调度在Reactive region。
在program中使用非阻塞赋值来驱动设计信号,在re-NBA区域中对其进行更新。
通过带有**#0** input skews的clocking blocks。
无法在SystemVerilog中的non-module实体内部实例化Interface,但是需要验证环境中的class中驱动Interface。
Virtual interface是一种数据类型(可以在一个class中实例化),它保存着对实际接口的句柄(这意味着class可以使用virtual interface来驱动该interface)。
Virtua linterface提供了一种可以将验证平台抽象模型与构成设计的实际信号分开的机制。
virtual interface的另一个大优点是,类可以在运行时(run time)动态连接到不同的物理接口(physicalinterfaces)。
可以从一个通用基类(base class)派生所需的类。基类列出了子类(subclasses)的原型。由于不打算实例化基类,只用来派生子类,因此可以通过将基类指定为virtual class将其抽象化(Abstract)
virtual method为子程序提供了原型。如果abstract class具有virtual methods,则必须重写所有virtual methods才能实例化子类。
这里涉及到OOP的多态(Polymorphism,dynamic method lookup)概念,可以直接从super class中引用那些subclasses的方法。
有时候仅需要描述一组对象的属性而无需知道其实际的行为。Abstract classes的方法和构造函数在扩展类中使用,扩展类可以通过Abstract classes共享方法。
Queue(队列)
队列有固定的顺序,很难将数据插入队列中。但是链表可以轻松地将数据插入任何位置。
$random系统函数每次调用时都会返回一个32位带符号的随机数。
$urandom系统函数每次被调用时都会返回一个32位无符号随机数。
1.randomize
2.pre_randomize
3.post_randomize
在Verilog中,模块内变量/任务/函数的声明特定于模块(module)。SystemVerilog的package结构允许声明全局变量/任务/函数,从而在modules/classes之间使用。
packages可以包含module/class/function/task/constraints/covergroup等声明,在使用时需要使用范围解析运算符(:或import来访问packages中的内容。
package ABC;
typedef enum {RED, GREEN,YELLOW} Color;
void function do_nothing()
endfunction
endpackage : ABC
import ABC::Color;
import ABC::*; // Import everything inside the package
通过 $cast 可以将值赋给数据类型不同的变量。特别是在将基类的句柄赋值给扩展类的句柄。
$cast分为static casting和dynamic casting。
static casting示例:
i = int'(10.0-0.1); // static cast convert real to integer
dynamic casting示例:
function int $cast( singular dest_var, singular source_exp );
task $cast( singular dest_var, singular source_exp );
super.task()
**rand-随机变量(Random Variable)*在出现所有可能的值之前,可能会出现相同的值。类似于有放回抽签。
randc-随机循环变量(RandomCyclic Variable):,在出现所有可能的值之前,不会出现相同的值。类似于无放回抽签。
top-level scope,可用于引用任意层次中例化的模块
$root.A; // top level instance A
$root.A.B.C; // item C within instance B within top level instance A
默认情况下,SystemVerilog中的约束是双向的。这意味着约束求解器(constraint solver)不遵循约束指定的顺序,同时约束所有变量。例如,(a == 0)->(b == 1)应该作为(!(a == 0)||(b == 1))的所有可能解来求解。
如果用户要指定约束求解器求解约束的顺序,则用户可以在约束之前通过solve指定顺序。
int UniqVal[10];
foreach(UniqVal[i]) UniqVal[i]= i;
UniqVal.shuffle(); //打乱排序
Pass by value是将参数传递给functions和tasks的默认方法。每个子例程保留该参数的本地副本。如果在子例程声明中更改了参数,不会影响原来的值。
pass by ref中functions和tasks直接访问作为参数传递的指定变量,就像传递变量的指针一样。传递数组给子例程时,使用pass by ref可以避免大量数据的复制。如果不想更改数组值,可以使用const ref。
class Transaction;
static int count = 0; // Number of objectscreated
int id; // Unique instance ID
function new();
id = count++; // Set ID, bump count
endfunction
endclass
Transaction t1, t2;
initial begin
t1 = new(); // 1st instance, id=0, count=1
t2 = new(); // 2nd instance, id=1, count=2
$display("Second id=%d, count=%d",t2.id, t2.count);
end
无论创建许多Transaction对象,静态变量count只有同一个。
byte sig_2是有符号的,而bit [7:0]是无符号的。
**Program是在SystemVerilog中新添加的,**它用于以下目的:
module和program之间的主要区别是:
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
modports是interface的一部分。modports用于指定与不同模块接口所连接的信号的方向。
interface my_intf;
wire x, y, z;
modport master (input x, y, output z);
modport slave (output x, y, input z);
endinterface
initial begin
clk= 0;
forever#(cycle/2) clk <= ~ clk;
end
有时需要在类声明之前声明另一个类变量。例如,如果两个类各自需要对方的句柄。使用typedef可以解决此问题,为第二个类提供前向声明/引用.
typedef class C2;
class C1;
C2 C;
endclass
class C2;
C1 C;
endclass
过度指定约束求解顺序可能会导致循环依赖,对此没有解决方案,并且约束求解器可能会给出错误/警告,约束求解失败。
int x,y,z;
constraint XYZ{
solve x before y;
solve y before z;
solve z before x;
}
在多个cover point上同时统计接收到的信息。Cross coverage通过下面的方式指定
covergroup cg;
cover_point_y : coverpoint y ;
cover_point_z : coverpoint z ;
cross_yz : cross cover_point_y,cover_point_z ;
endgroup
disable fork
**立即断言:**当使用“if”或“assert()”执行语句时,立即断言检查表达式是否为真。
**并发断言:**并发断言通过使用“property”持续检查仿真过程中的信号值
interfacearb_if(input bit clk);
logic [1:0] grant, request;
logic rst;
property request_2state;
@(posedge clk) disable iff (rst)
$isunknown(request) == 0; //Make sure no Z or X found
endproperty
assert_request_2state: assert property(request_2state);
endinterface
class ABC;
rand bit [7:0] data [];
constraint cc {
data.size inside{[1:10]}; // Constraining size
data[0] > 5; //Constraining individual entry
foreach(data[i]) // All elements
if(i > 0)
data[i]> data[i-1];
}
endclass
randsequence由一个或多个式子组成。每个式子均包含名称和列表。列表进一步分为terminals和non-terminals。
non-terminals是根据terminals和其他non-terminals定义的。
terminals是不可分割的项,除了其自身的代码块外,不需要其他定义。最终,每个non-terminals都分解为terminals。列表以“|”分隔,表明一组随机的选择。
randsequence( main )
main : first second done ;
first : add | dec ;
second : pop | push ;
done : { $display(“done”); };
add : { $display(“add”); } ;
dec : { $display(“dec”); } ;
pop : { $display(“pop”); } ;
push : { $display(“push”); };
endsequence
bin使用**名称(name)和计数(count)记录一组值(value)**或者序列(value transitions)的次数。
如果bin指定一组值,则每次覆盖点与该组中的一个值匹配时,相应的计数就会增加。
如果bin指定序列,则每次覆盖点与序列匹配时,相应的计数就会增加。
program main;
bit [0:2] y;
bit [0:2] values[$]= '{3,5,6};
covergroup cg;
cover_point_y : coverpoint y {
option.auto_bin_max= 4 ; }
endgroup
cg cg_inst = new();
initial
foreach(values[i])
begin
y = values[i];
cg_inst.sample(); //gather coverage
end
endprogram
transaction为UVM中的sequence item概念,通常需要约束随机数据,因此class更适合于此。Struct仅对数据类型进行重组,而不对方法进行重组。
SV允许我们使用受约束的随机测试(constrained-random tests,CRT),这是Verilog不能实现的。通过使用constrained-random data,我们输入有意义的激励。
constraint_mode()
Verilog的assign赋值是单向赋值(one-way assign),通过RHS变化影响LHS,但是LHS的变化不会影响RHS。SystemVerilog中“alias”可以进行双向赋值。
module byte_rip (inoutwire [31:0] W, inout wire [7:0]LSB, MSB);
alias W[7:0] = LSB;
alias W[31:24] = MSB;
endmodule
如果将一个对象赋值给另一个对象,则两个句柄将指向同一对象,这不会复制transaction。
Struct:
initial begin
typedef struct {
int a;
byte b;
shortint c;
int d;
} my_struct_s;
my_struct_s st = '{
32'haaaa_aaaad,
8'hbb,
16'hcccc,
32'hdddd_dddd
};
$display("str = %x %x %x %x ",st.a, st.b, st.c, st.d);
end
Union:与Struct不同,Union的成员均引用同一内存位置。这样,可以在不同时间使用union来容纳不同类型的对象,而无需为每种新类型创建单独的对象。
typedef union {int i; real f; } num_u;
num_u un;
un.f = 0.0; // setvalue in floating point format
当你经常需要以几种不同的格式读写寄存器时,Union很有用。但是更经常使用的还是class。
当我们使用变量名时,SystemVerilog将在**当前作用域(current scope)中查找它,然后在父作用域(parent scopes)**中查找,直到找到该变量。“This”起到指定作用域的作用
class Scoping;
string oname;
function new(string oname);
this.oname = oname; // classoname = local oname
endfunction
endclass
tagged union包含一个带标记的隐式成员,该隐式成员表示最后一个存储/写入的union成员的名称。如果从非最后写入标记的union成员中读取值,则将显示错误消息。
union tagged {
inti;
realr;
} data;
data data_un;
data_un = tagged i 5; //store the5 in data.i, and set it as the implicit tag.
d_out = data_un.i; //read value
d_out = data_un.r;//ERROR:memeber doesnt match the union's implicit tag
Extern关键字允许在类外定义方法。**范围解析运算符(::)**将方法构造链接到类声明。
class XYZ;
extern void task SayHello();
endclass
void task XYZ :: SayHello();
$Message("Hello !!!\n");
endtask
bits: 2值逻辑,默认值为0
logic:4值逻辑,默认值为X
always @(posedge clk)
reg1<= a & $rose(b);
在此示例中,(posedge clk)应用于 r o s e 。 当 b 的 采 样 值 变 为 1 时 , rose。当b的采样值变为1时, rose。当b的采样值变为1时, rose为true。
posege返回一个事件(event),而$rose返回一个布尔值,因此它们是不可互换的。
覆盖率驱动验证是一种**以结果为导向(resultoriented approach)**的功能验证方法。
根据测试的反馈来开发随机测试激励。初始测试可以使用许多不同的种子,从而创建许多唯一的输入序列。后来,即使使用了新的种子,测试激励也不太可能产生到达所有的设计空间。
当功能覆盖率逐渐接近其极限时,需要更改测试以找到新方法来覆盖设计的未覆盖空间。这就是所谓的“覆盖率驱动验证”。
Build Phase
生成配置:随机化DUT和周围环境的配置
构建环境:根据配置规划和连接(Connect Phase)测试平台组件。
Run Phase
Wrap-up Phase
int q[$] = {1,2,3,4};
int index[4], out;
foreach(index[i])
index[i] = i;
index.shuffle(); //orindex[i] = $urandom_range (1,4);
foreach(q[i])
out = q[index[i]];
Mailbox或queue。对于每个周期,我们分别从输入和响应中收集并删除这些数据。因此,queue或Mailbox比array更方便。
流操作符<<和>>**将数据块打包为比特流。
没有返回值的函数
const ref
initial语句块在仿真开始时执行,final语句块在仿真结束时执行
final语句块不能有delay、wait和non-blocking具有时序的语句
检查该对象是否已初始化。在SV中,所有未初始化的对象句柄都具有特殊的null值。
assert(obj == NULL)
semaphore用于控制对资源的访问。
Functional coverage决定了设计已实现了多少功能。
Code coverage描述设计中代码执行的客观信息
使用always语句块对组合逻辑进行建模时,可能会导致latch的生成。SystemVerilog添加了专门的always_comb建模组合逻辑
always_comb
begin
sum = b + a;
parity = ^sum;
end
SystemVerilog添加了专门的always_latch建模锁存器
always_latch
begin : ADDER
if (enable) begin
sum <= b + a;
parity <= ^(b + a);
end
end
SystemVerilog添加了专门的always_ff建模触发器
always_ff @(posedge clk iff rst == 0 or posedge rst)
begin : ADDER
if (rst) begin
sum <= 0;
parity <= 0;
end
else begin
sum <= b + a;
parity <= ^(b + a);
end
end
static和automatic的区别在于值是否共享同一块内存空间,默认为静态。
int [1:2][1:3]='{'{0,1,2),'{3{4}}};
Packed数组和unpacked数组。在声明数组时,数组维度在名称之前为packed array,数组维度在名称之后为unpacked array。packed array和unpacked array内存占用是不同的。
int [7:0] c1; //packed array
reg c2 [7:0] //unpacked array
断言主要用于检查设计的行为是否正常。可以用来提供功能覆盖信息。断言可以分为立即断言(immediate assertions)和并发断言(concurrent assertions)。
code(High)、functional(High)- 活差不多干完了
code(High)、functional(Low) -需要运行更多的仿真或改进测试平台
code(Low)、functional(High)-缺少功能覆盖点
code(Low)、functional(Low) - 需要运行更多的仿真或改进测试平台
#5ns
从derived class中使用super关键字来引用base class的成员。
在systemverilog中,我们可以在class内部创建一个静态变量。声明为static的变量在其他class之间共享。静态变量通常在声明时完成实例化。
systemverilog中的对象可以声明为public和private。public声明可从该对象外部访问,默认情况下,systemverilog中的声明是public
一旦将数据声明为local,就可以在特定类中对其进行访问。
通过使用modports
interfacearb_if(input bit clk);
logic [1:0] grant, request;
logic rst;
clocking cb @(posedge clk); // Declare cb
output request;
input grant;
endclocking
modport TEST (clocking cb, output rst);//rst is asynchronous signal
modport DUT (input request, rst, outputgrant);
endinterface
module test(arb_if.TEST arbif);
initial begin
arbif.cb.request <= 0;
@arbif.cb;
$display("@%0t: Grant =%b", $time, arbif.cb.grant);
end
endmodule
共有三种类型的fork…join
Chandle是一种数据类型,用于存储从DPI传递的指针。chandle的大小取决于机器,初始化值为null。它可用于将参数传递给函数或任务。
不需要begin和end。function可以具有void返回类型,task可以具有return
DPI(Direct Programming Interface,直接编程接口)用于调用C,C ++,System C函数。
将数据和方法绑定在一起。
Mailbox用于两个进程之间的通信。
mailbox mbx;
mbx = new(); // Allocate mailbox
mbx.put (data); //Put data object in mailbox
mbx.get (data); // Data updated with new data from FIFO
count = mbx.num(); // To count number of elements is mailbox
捕获来自随机激励刺激,封装覆盖率要求。
enum { red, green, blue } color;
bit [3:0] pixel;
covergroupg1 @ (posedgeclk);
coverpoint color;
coverpoint pixel;
AxC: cross color, pixel;
endgroup
为(DUT)创建真实的环境以检查行为。验证计划与硬件规范紧密相关,并包含需要验证哪些功能的描述。
验证计划包含:Directed testing、Random testing、Assertion、Hardware andsoftware co-verification
Use of verification IP
**关联数组:**具有字符串索引功能,在编译时分配内存。
**动态数组:**在运行时分配内存,构造函数用于初始化动态数组的大小。
if (!obj.randomize())
begin
$display("Error in randomization");
$finish;
end
或者
assert (!obj.randomize())
property name_of_property;
< test expression > or
< complex sequenceexpressions >
endproperty;
assert_name_of_property: assert property (name_of_property);
立即断言是某个判断必须为真的语句,类似于if语句。如果assert求值为X,Z或0,则断言失败。
my_assert:assert(condition1& condition2)
$display("Passed..");
else
$error("Failed..");
设置property时,如果不指定property的失败情况,则默认情况下,仿真器应将错误指定为$error严重级别。其他严重级别还包括
$fatal、$error、$warning 和 $info
并发断言中使用的变量在pre-poned区域中采样,并且在Observe区域中评估断言。这两个区域都紧接在时钟沿之前。
Sequence
Property
Assert property
[* ]
指定一个信号或序列在指定的时钟数内连续匹配。
该运算符指定表达式将匹配指定的次数,而该次数不一定是连续的时钟周期。
x [->4:7]
接口是设计重用的理想选择。
1、当信号数量很大时,它减少了信号错误连接的可能性。
2、易于在设计中添加新信号
有时我们使用循环生成进程,并且在下一次迭代之前不保存变量值。我们应该在fork join语句中使用automatic变量来保存变量。
program automatic test;
task wait for mem(input [31:01 addr, expect data,
output success);
while (bus. addr!== addr)
@(bus . addr);
success = (bus. data== expect data);
endtask
...
endprogram
同时调用此task多次,如果没有automatic修饰符,再次调用wait formem,则第二次调用将覆盖两个参数。
Unpacked array:
bit [7:0]b_unpack[3]; // Unpacked
bit [3:0] [7:0]bytes; // 4 bytes packed into 32-bits
bytes =32'hCafe Dada;
混合packed array和unpacked array:
bit [3:0] [7:0] barray[31; // Packed: 3x32-bit
bit [31:0] Iw =32'h0123 4567; // Word
barray[01 = lw;
barray[0][31 =8'h01;
barray[0] [1][61 =1'h1;
int dyn[];
initial begin
dyn = new[5];
foreach (dyn[j]) dyn[j] = j;
dyn. size();
dyn. delete();
end
当集合的大小未知或数据空间稀疏时,关联数组是更好的选择。
关联数组在使用前不会分配任何存储,并且索引不限于整数表达式。
像链表一样,可以在队列中的任何位置添加或删除元素,而无需单步执行前面的元素。
像数组一样,可以直接访问带有索引的任何元素,而无需分配新的数组并复制整个内容。
int j, q2[$]-{3,4), q[$] ={0,2,5};
bit [4:0] ack-que[$];
initial begin
q. insert(1, 1); //{0,1,2,5} Insert 1before 2
q. insert(3, q2); //[0,1,2,3,4,5} Insertqueue in q1
q. delete(1); //f0,2,3,4,5} Delete elem.#1
//These operations are fast
q. push back(8); //(0,2,3,4,8} Insert atback
j= q. pop front; //{2,3,4,8} j=0
foreach (q[ il)
$display(q[i]); //Printentire queue
q. delete(); //{} Delete entirequeue end
class parent;
int a, b;
task display_c();
$display("Parent");
endtask
endclass
class child extends parent;
int a;
task display_c();
$display("Child");
endtask
endclass
initial begin
parent p;
child c;
c= new();
p=c;
c. display_c();
p. display_c();
//result: Child
//result: Parent
end
没有在父类中声明virtual function,尽管"p"指向"“的句柄,但不允许调用该函数。如果多个子类使用相同的名称定义"display_c()”,则称为多态。
virtual class parent;
pure virtual task display_c();
endclass
class child1 extends parent;
task display_c();
$display("Child1");
endtask
endclass
class child2 extends parent;
task display_c();
$display("Child2");
endtask
endclass
initial begin
parent np[2];
p[0]=new(); //illegal
child1 c1 = new();
child2 c2 = new();
p[0]=c1;
p[1]=c2;
foreach(p[i]) begin
P[i]. display_c();
end
使用枚举类型表示各种激励的类别,从而定义控制字段,控制受约束的变量,提高可读性。
typedef enum {ZERO, SHORT, MEDIUM, LARGE, MAX} simple item delay_e; class simple item extends uvm sequence item;
rand int unsigned addr;
rand int unsigned data;
rand int unsigned delay;
rand simple_item delay edelay kind; // Control field
// UVM automation macros forgeneral objects uvm object `uvm_object_utils_begin(simple_item)
`uvm field int(addr,UVM ALL ON)
`uvm field enum(simple item delay e, delay kind, UVM ALL ON)
`uvm_object_utils_end
constraint delay_order_ c {solve delay kind before delay; }
constraint delay_c {
(delay kind == ZERO)-> delay == 0;
(delay kind == SHORT)-> delay inside { [1:10] }:
(delay kind =-MEDIUM) -> delay inside { [11:99] };
(delay kind == LARGE)-> delay inside { [100:999]};
(delay kind =MAX)- delay == 1000;
delay >=0; delay<= 1000;
}
endclass: simple item
在派生类中使用super关键字来引用父类的成员。
**Logic:**过程赋值和连续赋值,可以保存值
**Reg:**过程赋值,可以保存值
**Wire:**连续赋值,不能保存值