SystemVerilog基础笔记

一、Training

1.数据类型

a. 两态变量:bit,  byte: 8bit,  shortint: 16bit,  int: 32bit​,  longint: 64bit,  shortreal相当于c的float: 占32bit的内存空间,  real相当于c的double: 占64 bit内存空间。其中只有bit是无符号。

b. 四态变量:reg, logic, integer: 32bit有符号, time: 64bit​。其中只有integer是有符号。

c. string

d. enum

e.

定长数组:

动态数组:定义时不指定大小,得先new()再使用,如reg [7:0] ID[]; ID = new(100);

队列:声明时要加$,如int q[$] = {0,1,3};q.delete()——删除掉整个队列

关联数组:​

    假如一个处理器的寻址范围是几个G,但是处理器只访问几百或几千个字节,这种情况    对几个G的存储空间进行分配和初始化显然是很浪费的

    用于保存稀疏矩阵的元素。​

    仿真器采用树或者哈希表的形式存放关联数组。

    便于建立存储模型,不需要在内存中建立非常大的存储。
​​​​    关联数组的定义,初始化和遍历见绿皮书31页

3.interface​

a.关于interface的clocking port, addr为什么没有加入该Port​?

4.线程​以及线程间的通信

a. 标准的verilog对语句的有两种分组方式,begin...end中的语句以顺序的方式执行,fork...join内的语句并发执行,fork...join内的线程必须执行完才能开始该块后面的语句。

b. fork.....join_none,块内块外同时执行,不会产生块后的线程必须等块内线程的情形。​

c. fork.....join_any 只要块内的线程有一个执行结束,就执行块之后的线程,块内的也继续执行。​​

5.面向对象

a.一般将基类中的函数定义为虚函数比较好,方便子类修改该函数,调用时根据传入的参数不同,会产生不同的结果。​​

b.句柄即指针,句柄指向对象,inst_pkt  pkt  只是定义了一个句柄,new之后产生对象,pkt指向该对象。

c.向下类型转换:从父类转换为子类    向上类型转换:从子类转换为父类​

d.

6. 随机​​化

a. 随机在DV人的认识里是非常重要的事情​

b. rand和randc的区别:randc是所有数值都出现过一次才开始重复​

c. randomize函数是每个class都可以调用的,两个附属函数为pre_randomize和post_randomize分别做前处理和后处理​

d.写constraint时可以使用dist进行权重分布,:=表示值范围内的每一个值权重是相同的, :/表示权重要分到值范围内的每一个值。​总权重的和不必是100。

如:

Constraint Limit{

            data dist{[5:7] := 10, 9:=20}; // 5 = weight 10

                                                            // 6 = weight 10

                                                            // 7 = weight 10

                                                            // 9 = weight 20

       }

Constraint Limit{

            data dist{[5:7] :/60,9:=40}; // 5 = weight 20

                                                            // 6 = weight 20

                                                            // 7 = weight 20

                                                            // 9 = weight 40

       }​

e.可以使用solve...before...来规定随机的顺序,如:​

class example​

    rand bit x;​

    rand bit [1:0] y;​

    constraint c_xy{​

        (x==0) ->y==0;​//  ->条件操作符在绿皮书的“条件约束”小节

        solve x before y;​

    }​

endclass​​

过度使用solve...before会降低计算速度,也会使约束难以理解。​​

f.内嵌约束inline constraint:即assert(t.randomize() with{xxx; xxx;xxx;});  //一般使用断言检查随机化的结果,如果成功则返回1。​

g.一般使用constraint_mode打开或者关闭约束,如:​​

    instr.constraint_mode(0);//instr是一个实例化之后的对象,该句关闭所有约束​

    instr.c_no_operands.constraint_mode(1);//打开instr对象名为c_no_operands的约    束。​

h.随机化个别变量:在调用randomize函数时只传递个别变量,如:​

    a.randomize(x,y);​

    r.randomize(low);  //low被随机即使它在类里面没有被定义为随机变量​

  也可以在随机时关闭某几个变量的随机,如:​

    task/function int object_name.property.rand_mode(0 | 1 );​

8.覆盖率​

a.一般情况下,定义一个bins,只要命中一次就算这个仓覆盖到了,下面是另外两种定义bins的方法:​​

    {bins  v[] = {[-51:51]};}      //表示此范围每个值都要出现​​,仓才能满

    {bins  v[4] = {[2:28'h7fff_ffe0]};  //表示此范围被分为四个区间,各区间击中一次仓就满了。​

二、SV的两本书

1.数据类型​

a.sv对经典的reg数据类型进行了改进,使得它除了作为一个变量以外还可以被连续赋值、门单元和模块驱动,为了与寄存器类型相区别,这种改进的数据类型被称为logic。除过双向总线这种有多个结构性驱动的地方,任何使用线网的地方都可以使用logic。

b.双状态数据类型有利于提高仿真器的性能并减少内存的使用量。

c.对于byte,其带符号,因此表示范围是-128到127,而不是0到255,在随机化时,带符号的变量可能会造成意向不到的结果。

d.在把双状态变量连接到被测设计,尤其被测设计的输出时无比要小心,如果被测设计试图产生X或Z,这些值会被转换成双状态值。

e.X或Z只是人们的一种表达方式,实际的芯片电路里,其要么是1要么是0。

f.单个字符是Byte类型,字符串使用动态存储方式,所以不用担心存储空间会全部用完。

g.  s.gets(0),即得到第一个字符;s.tolower,即将大写字母转换为小写;s.puts(s.len()-1,-),即将倒数第一个字符换为-;字符串支持拼接一个字符即s={s,"P1800"};s.substr(2,5),即街区位置2到5的字符。

h.枚举类型仅限于一些特定名称的集合,例如指令中的操作码或者状态机中的状态名。

i.定义枚举类型和结构体最好用上typedefine,这样以后方便随意创建枚举型变量个结构体变量。

j.使用如color.name()的name()函数可以得到枚举变量值对应的字符串。

k.枚举值缺省是从0开始递增的整数。

l.枚举类型缺省使用int类型存储,定义枚举变量时,其中必须有一个元素的枚举值是0,要不然创建枚举变量时不指定初始值的话,它会被初始化为0,但是0并不是枚举变量某个元素的枚举值。

m.first()返回第一个枚举常量;last返回最后一个枚举常量;next返回下一个枚举常量;next(N)返回以后第N个枚举常量;prev()返回前一个枚举常量;prev(N)返回前第N个枚举变量;当到达枚举常量列表的头或尾时,函数next和prev会自动以环形方式绕回。

n.const修饰符允许变量声明时对其进行初始化,但不能在过程代码中改变其值。

o.bit [7:0] b8; bit one; $displayb(one+one)   结果是0;b8= one+one结果是2;第三句由于溢出造成精度受损。

p.数组: 

1.int lohi [0:15]和 int lohi[16]作用一样,即sv允许只给出数组宽度的便捷声明方式,跟C语言类似。

 2.如果试图从越界的地址读取数据,logic类型会返回X,双状态类型会返回0,这一条适用于所有的数组类型,包括队列。

 3.很多sv仿真器在存放数组元素时使用32比特的字边界,所以byte,shortint和int都是存放在一个字中,而longint存放到两个字中,那么bit呢? 

4.仿真器通常使用两个或两个以上连续的字来存四状态类型,这会比双状态变量多占一倍的空间。 

5.常量数组:    int asend[4] = '{0,1,2,3};  int desend[5] = '{9,8,default:1};

6.操作数组最常见的方式是使用for或foreach循环。 

7.数组可以直接进行赋值和比较,但是队列好像不行。 

8.合并数组与非合并数组的不同是,它的存放方式是连续的比特集合,中间没有任何闲置的空间。是吗?如果是每个元素9比特该怎么存? 

9.合并数组:bit[3:0] [7:0]bytes;共四个元素,每个是8比特。合并数组:bit[3:0] [7:0]barry[3],共3个bytes那样的数组。 

10.动态数组,关联数组和队列都可以合并。 

11.sv提供的动态数组类型可以在仿真时分配空间或调整宽度,这样在仿真中就可以使用最小的存储量。 

12.动态数组的宽度在编译时不给出,而在程序运行时再指定。 

13.当定义常量定长数组时也可以使用动态数组即不指定数组宽度,sv会自动统计元素的个数。 

14.当把一个定宽数组复制给一个动态数组时,sv会调用构造函数new[]来分配空间并复制数值。注意动态数组不是new()。 

15.所有的数组类型都可以用$size,队列和动态数组可以用.size()。 

16.队列与数组相似,可以通过索引实现对任一元素的访问,而不需要像链表那样去遍历目标元素之前的所有元素。 

17.队列的增加或删除元素的操作性能比动态数组小得多,动态数组增加或删除必须分配新的数组并复制所有元素的值。 

18.队列的声明使用带有美元符号的下标:[$],队列元素从0到$。 

19.注意队列的常量只有花括号而没有数组常量开头的单引号。注意不要对队列使用构造函数new[]。 

20.当定义队列时,sv会分配额外的空间以便用户能够快速插入新的元素,当元素增加到超过原有空间的容量时,sv会自动分配更多的空间,sv会随时记录闲置的空间。

 21.队列操作:insert(), delete(), push_front(), push_back(),pop_front(), pop_back()。 

22.$符号放在范围表达式的左边代表最小值,如[$:2]代表[0:2],如果放到表达式的右边则代表最大值,如q[$] ={0,2,5}

,[1:$]代表[1:2]。 

23.在队列的首尾增删元素操作速度非常快,在队列中间增删元素则需要对已有的数据进行搬移以便腾出空间。 

24.虽然sv提供了链表数据结构但是应该尽量避免使用它,因为sv的队列更加高效和易用。 

25.sv提供关联数组用于保存稀疏矩阵的元素,这意味着当你对一个非常大的地址空间寻址时,sv只为实际写入的元素分配空间。 

26.仿真器可以采用树或哈希表的形式存放关联数组,但有一定的额外开销,因为索引值和数值都要保存。 

27.和Perl语言中的哈希数组类似,关联数组也可以使用字符串索引进行寻址。 

28.关联数组操作:assoc.first(idx),得到第一个索引;delete();next();

q.

数组的方法:可用于任何一种非合并的数组类型 

1.数组缩减方法,数组缩减方法是把数组缩减成一个值,最常用的是array.sum,它会对数组中所有的元素求和。其他的缩减方法还有product(积),and, or, xor。 

2.数组的定位方法,即在数组中查找数据,这些方法的返回值通常是一个队列。array.min返回最小值;array.max返回最大值;array.unique返回数组中的唯一值;tq是一个队列,tq=array.find with(item>3)找出数组中大于3的元素;int count, count=array.sum with(item>7)返回数组中元素大于7的个数;还有find_index。 

3.数组的排序方法:reverse(翻转)-sort(由大到小)-resort(由小到大)-shuffle (洗牌)

4.函数如果没有参数,则不必带上空括号。 

5.如何从数组中随机选取一个元素?

r.   typedef 

1.用户自定义最有用的类型是双状态的32比特无符号整数,typedef bit [31:0] uint; 

2.由于struct只是一个数据集合,所以它是可综合的。 

3.sv提供的合并结构可以更多地控制数据在存储器中的排布方式,如typedef struct packed {bit[7:0] r,g,b} pixel_s。尤其是当希望减少存储器的使用量时可以使用合并结构体。 

4.如果经常对整个结构体操作,则使用合并结构体效率更高,如果经常对结构体内的个体成员操作,那么就使用非合并结构体。

s.   数据类型转换 

1.静态转换不对转换值进行检查,并在需要转换的表达式前加单引号,如int i; i=int'(10.0-0.1); 

2.动态转换函数$cast会对越界的数值进行检查。

3.流操作符用于把其后的数据打包成一个比特流,如int h; bit[7:0] j[4]='{8'ha,8'hb,8'hc,8'hd};h={>>{j}}从前向后打包,h=0a0b0c0d。  h={<<{j}}从后向前打包,h=b030d050。

2.过程语句/task/function​​

a.sv支持在begin-end,fork-join等结束关键字后面加标记,使得程序块的首尾匹配更容易。

b.sv的循环支持continue跳过本轮循环继续下一轮循环,支持break用于终止循环。c.$feof用于检测流上的文件结束符,使用实例见绿皮书p52。

c-1.过程连续赋值语句包括assign deassign force release

c-2.循环语句:for-while-do while-repeat-forever-foreach

d.task可以消耗仿真时间,function不可以消耗仿真时间;task没有返回值,函数可以有返回值;任务和函数没有任何执行语句也是合法的。

e.任务可以调用函数,而函数只能在fork-join_none生成的线程中调用任务。

f.函数可以定义为void类型,另外如果函数有返回值但是想忽略之,可以使用这种调用方式:void'(check_ctrl())。

g.不带参数的子程序在定义和调用时不需要带空括号(子程序包括task和function)。

h.缺省的子程序参数类型是“logic输入”,一般后一个参数不指定方向和类型的话默认其与前一个参数的相同,不过为了避免滋生一些细小而难以发现的Bug,建议对所有子程序参数都带上方向和类型。

i.input/output传的参数sv的处理是:在子程序开头将input和inout的值复制给本地变量,在子程序退出时则复制output和inout的值。

j.sv可以使用引用传递参数,sv允许使用input传递数组,这时数组会被复制到堆栈区,这种操作的代价很高,因此向子程序传递数组时尽量使用ref以获取最佳性能;所谓引用传递就如C++的指针,也就是变量的入口地址。

k.ref参数只能用于带自动存储的子程序中。

l.传参数时使用的const修饰符作用是:子程序不能修改数组的值,如果子程序试图修改,编译器会报错。

m.ref类型的参数第二个好处是:子程序修改该变量,该变量对任何调用它的地方都是随时可见的,而ouput的参数只有子程序执行完,才会更新变量值。

m-1.当测试程序越来越复杂时,希望在不破坏已有的代码上增加额外的控制,sv支持参数的缺省值,在子程序定义时设置参数缺省值,如果子程序调用时没有指定该参数则默认使用缺省值。

n.sv手册中有时将子程序的参数称为端口,.....这是有原因的,我们可以使用模块实例化的方式传递参数,这样就不用管其参数顺序如何。

o. sv返回值赋给与函数同名的变量,函数名为sub,  在子程序中sub=~a和 return ~a作用一样;return可以在子程序任一点推出该子程序。

p.verilog最初被创建时目的是用于描述硬件,因此所有对象都是静态分配,变量和子程序参数都是被存放在固定位置,而不像其他语言那样存放在堆栈区。所以verilog-95如果在多个地方调用同一个任务,不同线程之间会窜用这些局部变量。

q.栈区-堆区-全局静态区-文字常量区-程序代码区。

r.verilog-2001可以指定任务、函数和模块使用自动存储,从而迫使仿真器使用堆栈区存储局部变量。

s.  sv中,模块和program块缺省情况仍然使用共享的静态存储区,那么task和function默认是automatic吗?

t.  避免在变量声明中赋予除常数以外的任何值,对于静态的program,如果声明一个变量时将另一变量初始化给它这样是有Bug的,因为该变量是静态,在仿真开始就有了初值(没太懂...)。

u.对于声明在class中的子程序和变量默认是动态存储。

3.interface
​​​a.在传统的verilog中,设计代码和验证代码在不同的模块中,但是使用模块来保存测试平台经常会引起驱动和采样的时序问题,所以sv引入了程序块program,从逻辑和时间上分开测试平台。

b. interface是sv中代表一捆连线的结构,主要作用有两个:一是简化模块之间的接口,二是实现类和模块的通信。

c.在interface中使用modport结构能够将信号分组并指定方向。

 d.在时钟周期级的测试平台中,需要在相对于时钟信号合适的时间点驱动和接收同步信号,驱动的太晚或采样的太早测试平台就会错过一个时钟周期。

e.接口中可以使用时钟块来指定同步信号相对于时钟的时序;时钟块中的任何信号都将同步地驱动或采样,这就保证了测试平台在正确的时间点与信号交互。

f.一旦定义了时钟块,就可以使用@arbif.cb表达式等待时钟,而不需要描述确切的时钟信号和边沿。

g. 使用时钟块的话,tb中就用非阻塞的方式赋值或采样信号。

h.时钟块默认时序是在时钟1 step之前采样输入信号,即在设计有任何动作之前采样;默认时序在#0驱动输出信号。 

clocking bus@(posedge clk); 

    default input #10nv  output #2ns;

    input data,ready;

endclocking

第二条语句修改了默认的输入偏差和输出偏差,输入偏差默认是负的,即在该时钟沿前面10ns采样输入,在时钟沿之后2ns驱动输出。

i.clocking块是实现基于周期验证方法学中的一个关键元素,使得用户能够在一个更高的抽象层次上编写验证平台。

j. logic信号可以直接被驱动,而wire需要使用assign。

k.VMM建议将接口中的信号都定义为wire,这个是从可重用性角度考虑,因为多结构性驱动必须使用wire,若定义为logic,在后面的项目如果遇到多结构性驱动就不得不改logic为wire,也就得维护两个版本的interface。

l.program块中不能有任何的层次级别,例如模块的实例、接口等。

m.如果存在多个program块,仿真会在最后一个程序块完成时结束,无需手动调用$finish,也可以在program中调用$exit提前中断任何一个程序块。

n.如果在program中想要实现always的功能可以使用initial forever,program中不能使用always。

o.当某个信号在时钟上升沿变化同时tb在这里采样,采到的是变化之前的值。

p.我们可以把虚接口看成对应接口类型的一个句柄,该句柄可以指向任意一个实体接口。

4.并发进程与进程同步

a.硬件设计可以看成是由很多并发或者并行的进程组成,例如MAC控制器接收模块和发送模块同时接收和发送以太包,仔细观察两个模块会发现更多并行进程:计数器、解码器、比较器等。

b.验证环境模拟硬件运行的能力越强,就越容易对硬件建模和验证。

c.通过mailbox实现进程间的通信;通过semaphore实现进程互斥和仲裁;通过event实现进程之间的同步。

d.fork...join内的每个语句块称为子进程,执行这段fork...join代码的称为父进程。

e.fork...join结构中将阻塞父进程的执行,直到其中所有子进程中止;join_any结构中父进程会被阻塞到其中任意一个子进程结束;join_none父进程会立即与产生的所有子进程并发执行。

f.子进程可以在其内部定义本地变量,也可以访问在fork结构以外的变量。因此变量可以作为进程通信的一种方式。父进程的变量默认是静态存储而不是自动存储,因此要注意其是否有竞争。

g.wait fork等待进程的结束,常用来确保所有子进程的执行都已经结束;disable fork结构停止进程的执行。

h.mialbox

1.mailbox可以看成是一个先进先出的存储数组。

2.mailbox是一种通信机制,它使得数据可以在进程间传递和通信,数据被一个进程发送到一个mailbox中,而另外一个进程可以从中获得。

3.有界的mailbox会变满,无界的mailbox永远也不会阻塞一个发送操作的进程。

4.定义一个mailbox:    mailbox  mbxRcv;

5.mailbox的方法:new();put()严格按照fifo的顺序将一个数据存储到mailbox中,如果mailbox已满,则进程会被阻塞直到队列中有足够的空间;try_put()不会阻塞进程,如果mialbox未满则返回1,如果已满则返回0;get();try_get();peek()从mailbox中复制一个数据但不会将其删除;try_peek();num()可以返回mailbox中存储数据的数目。

6.参数化mailbox可以指定存储数据的类型

i.semaphore

1.多个进程可能会出现竞争,比如多个进程需要通过一个接口发送数据包,因为每次只能允许一个包在该接口上发送,每个进程需要等待自己获取权限才可以发送。

2.每当出现资源竞争的时候,获取一个共享资源就必须通过仲裁。

3.sv通过semaphore提供了仲裁的功能,

4.从概念上讲,semaphore可以理解成一个存储桶,当为semaphore分配内存的时候,会创建一个带有一定数目key的存储桶。

5.semaphore基本操作:new();put();get();try_get()。

6.使用semaphore的进程在继续执行之前必须首先从存储桶中用get()方法获得一个key,执行完成后,用put()方法将key放回存储桶。

7.semaphore应用实例见白皮书p80,创建一个只有一个key的semaphore:semaphore s1=new(1);

j.event

1.触发事件使用->,等待事件使用@。

2.必须先等待事件,然后才能触发事件。

3.触发一个事件可以解除当前所有等待这个时间被阻塞的进程。

4.非阻塞事件使用->>操作符触发,其效果是语句会无阻塞地执行。​

5.面向对象

a.类可以将数据和对数据的操作封装在一起。

b.类的属性:类中定义的数据成员,方法:类中定义的函数或者任务,句柄:即指向对象的指针。

c.使用对象需要做三个步骤:定义类,声明对象,通过构造函数new创建对象的例化。构造函数为该对象分配内存空间并给句柄赋予有效值。

d.对象的声明并不意味着对象的创建,声明没有实际分配空间,它只是简单地创建了一个标识符。

e.如果对象没有被构造,句柄为空,引用该句柄会产生运行错误。

f. sv像Java一样当对象不再使用时会自动回收内存,不需要析构函数。

g. e1_pkt = new(); e2_pkt = e1_pkt,这种情况系统并没有分配新的空间给e2_pkt,二是将e2_pkt指向e1_pkt的空间。e2_pkt = new e1_pkt,创建新的对象e2_pkt并复制e1_pkt的内容,这种复制叫做浅复制,因为如果e1_pkt中有对象的话没有复制该对象,只是复制了其句柄,完整复制一般情况下需要使用者定制特殊的复制程序。

h.静态属性:如果要求所有的对象共享一个变量,我们可以使用关键字static来声明该变量。我们可以这样理解,类的静态属性是存放在每个类中的,而不是在每个类的对象中。

i.静态方法:静态方法对于类的所有对象都可以访问,也可以在类的外部被调用,即使没有该类的例化;静态方法不能访问非静态成员。

j.关键字this被用来明确地引用当前类的属性或方法。只能在非静态方法中使用。

k.类的块外方法声明必须与它的原型声明完全一致,语法上的唯一不同点是方法名字前面加入了类名字和范围操作符(:)。​

面向对象的继续:继承与多态

a.子类可以通过super操作符引用父类中的方法和成员。

b.被声明为local的数据成员和方法只对该类本身可见,对于外部和子类不可见。

c.被声明为protected的数据成员或方法,对外部不可见,对于子类可见。

d.子类新添加的成员和方法存储在独立的空间中,保持与继承成员和方法的独立性。

e.一个子类对象可以赋值给一个父类对象,如:DerivedPacket 1p=new(); Packet p=1p;

在此要注意两个问题:一是通过父类对象引用在子类中重写的属性或方法,结果只会调用父类的属性或方法;二是子类中新增的属性和方法对父类对象不可见。

f.父类对象赋值给子类对象是不合法的。

g.父类对象指向子类对象1,可以通过$cast将父类对象赋值给子类对象2, $cast会检查源句柄和目标句柄类型是否一致,如果是并且合法就赋值,否则会发出类型不匹配的告警。

h.子类构造函数new()执行的第一个 默认动作是调用其父类的构造函数new,并且会沿着继承关系一直向上回溯调用。比如有类3继承自类2,类2继承自类1,类3在new时先执行类1的new,然后是类2的new,最后才是他本身。

i.虚方法是一种基本的多态结构,比如,父类有task1和task2,task2中调用了task1,子类中对task1进行了重载,那么子类对象再调用task2时到底其中调用的是父类原有的task1还是自己改写的那个?

j.对于普通方法(非虚方法),其重写之后只能在本身和继承类中有效。

k.

l.当基于父类所设计的程序在应用的时候需要适应子类操作变化时,就需要使用虚方法通知编译器这种可能的变化,使编译器为虚方法调用生成特别的代码

m.多态的定义:虚方法与重写的实现就是多态,即父类对象与子类对象调用相同函数名,但是产生不同的行为。多态的目的:接口重用。

n.当父类对象方法1调用该虚方法时执行的是父类的代码,当子类对象方法1调用该虚方法时执行的是子类的代码。

o.为了实现代码重用,sv还引入了虚类和参数化类。虚类主要作为基类模板,不可以实例化。参数化类就如C++的类模板,可以根据需要修改其中参数的类型。

6.随机化

a.对于真正的随机,无法凭借当前值来预测下一个值,处理器是依顺序执行指令,因此计算机系统的确定性是很难实现一个随机的行为。

b.借助伪随机数产生器,计算机系统引入随机,如果知道序列的第一个数值,那么伪随机数是可以预测的。因此伪随机数的可预测性使得失败的测试用例可以在同样的条件下重新运行,从而重现特定的情景。

c. sv采用伪随机数产生器 来产生随机变量,从而产生随机激励。

d. 随机系统函数: 1.$urandom返回一个32位的无符号数。 2.$urandom_range可以在指定范围内返回一个无符号数,如val = $urandom_range(7,0)

e.randcase使得条件语句可以根据权重随机地选择执行某一行分支。randsequence

f.基于对象的随机包含三个步骤:定义随机变量、指定约束条件(可选)和调用内置randomize()方法产生随机。

g.randc是进行周期性遍历,第一轮遍历完开始第二轮,而不是第一轮完了之后就随便了。

h.   !(v inside {a,b,c})表示v的取值在这个范围之外。

i.    dist操作符与inside操作符使用的地方相同。randc 变量不能用dist操作符具体用法见白皮书p126。

j.可以使用foreach约束数组的每一个元素,如 rand byte A[]; constraint C1 {foreach(A[i])  A[i] inside {2,4,8,16};}

k.rand_mode可以被类调用以打开或关闭类中所有的随机变量,constraint_mode()只能打开或关闭一个约束。

l.标准随机函数(std::randomize())使得用户能够随机化当前范围内的数据,而无需定义一个类或实例化一个类的对象。标准随机函数与类的随机方法作用相同,只是它仅限于操作当前范围内的变量而不是类的成员变量。如:bit success, rd_wr;  success=randomize(rd_wr);​

7.DPI

​a.DPI= Direct Programming Interface  直接编程接口。

b.Verilog的PLI为其提供了一个可以调用C程序的机制,实际上每个使用verilog作为设计语言的大项目都会使用到verilog的PLI。

c.PLI可以全面地访问仿真数据的内部,如波形显示、图像化调试等,但DPI不可以。

d.任何C函数都可以被导入sv中,包括C的函数库。

e.方法的导出用export,与import一样写在sv中,然后C中就可以使用导出的方法。

   

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SystemVerilog是一种硬件描述语言(HDL),用于设计和验证数字电路。SystemVerilog具有丰富的特性,适用于各种设计和验证任务。以下是SystemVerilog入门笔记的一些重点内容。 首先,了解SystemVerilog的基本语法是很重要的。SystemVerilog继承了Verilog的语法,同时加入了一些额外的特性,如数据类型、类和接口等。熟悉这些语法规则对于编写有效的代码至关重要。 其次,了解模块和端口的概念。SystemVerilog中的模块是最基本的构建单元,而端口则是模块与外部环境之间的接口。了解如何定义和使用模块和端口是编写可重用代码的关键。 另外,掌握数据类型和运算符是必不可少的。SystemVerilog支持多种数据类型,如整数、浮点数、逻辑值等,同时也提供了丰富的运算符,如加法、减法、逻辑运算符等。这些知识对于设计和验证数字电路非常重要。 此外,了解时序控制和并发控制是必要的。SystemVerilog提供了多种时序控制和并发控制的语法和特性,如always块、initial块和fork/join语句等。掌握这些知识对于设计复杂的数字电路至关重要。 最后,了解Verilog验证中的一些基本概念也是很重要的。例如,了解如何使用assert语句进行断言检查、如何使用coverage语句进行覆盖率分析等。 总之,SystemVerilog入门需要掌握其基本语法、模块和端口、数据类型和运算符、时序控制和并发控制,以及一些基本的验证概念。这些知识是成为一名合格的SystemVerilog工程师所必备的基础

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值