【Block-Level Verification】 SystemVerilog 数据类型_数组操作_队列_结构体_枚举类型_字符串_过程块和方法_变量生命周期_例化和链接...

2023-10-26

System Verilog芯片验证 System Verilog语言

 

1、数据类型

    Verilog本身是来做硬件描述,是对硬件本身的行为进行建模。 SystemVerilog是Verilog的生命延续,.sv是对SystemVerilog进行编译,.v是对Verilog进行编译,SystemVerilog文件对Verilog是完全兼容的,所以把.v文件改成.sv文件进编译是允许的,SystemVerilog是侧重于Verification的语言。

    SystemVerilog是Verilog的3.0版本,相比较Verilog将寄存器类型(register)即reg(存储数据)和线网类型(net)即wire(模拟硬件连线)分得很清楚,在sysytem verilog中引入了新的数据类型logic,他们的区分和联系在于:

   1、Verilog作为硬件描述语言,倾向于设计人员自身懂得所描述的电路中哪些变量应该被实现为reg,而哪些变量应该被实现为wire。这不但有利于后端综合工具,也更便于阅读和理解。而System Verilog作为侧重于验证的语言,并不十分关切logic对应的逻辑应该综合为reg和wire,因为logic被使用的场景如果是验证环境,那么它只会作为单纯的变量进行赋值操作,而这些变量也只属于软件环境的构建。

   2、logic类型被推出的另外一个原因也是为了方便验证人员驱动和链接硬件模块、而省去考虑究竟该用reg和wire的精力,这即节省了时间,也避免了出错的可能,logic可能被综合为reg也可能会综合为wire。 与logic相对应的是bit类型,它们均可以构建矢量类型(vector),logic为四值逻辑,即可以表述为0、1、X、Z(没有被驱动),bit为二值逻辑,只可以表述为0和1。之所以要引入二值逻辑,因为在systemVerilog里期望将硬件的世界与软件的世界分开,在硬件的事件里更多的是用logic(四值事件),在软件的事件里更多的是用bit去做(二值事件)。

    四值逻辑类型:integer(32位)、logic、reg、net-type   与Verilog里面某些变量对应

    二值逻辑类型:byte、shortint、int(32位)、longint、bit   与C里面有些变量对应

 

如果按照有符号和无符号的类型进行划分,那么可以将常见的变量类型划分为:

      有符号类型:byte、shortint、int、longint、integer

      无符号类型:bit、logic、reg、net-type (例如wire、tri)

//example 已知程序求display的结果

logic [7:0]  logic_vec  =  8’b 1000_0000;
bit  [7:0]  bit_vec   =   8’b 1000_0000;
byte signed_vec  =  8’b1000_0000;

initial begin
  $display(“logic_vec = %d”, logic_vec );
  $display (“bit_vec = %d”, bit_vec );
  $display (“signed_vec = %d”, signed_vec );
end

结果分别是128,128 ,-128,$display调用byte,因为是有符号数,最高位1代表负数,那结果应该是0,可是8位有符号数的取值为-128 ~ 127,从-127~127都可以正常表示,他们只需要7位,而0对应的有两个,为了解决数值一一映射的问题,-128由1000_0000表示。

 

 

2、内建数据类型

尽量避免两种不一致的变量(不同数据类型、有无符号、数据位宽)进行操作,导致意外的错误,需要强制类型转换。

静态转化用:想要的数据类型 ’(),转成无符号数:unsigned’()

//example 求两次display的值

byte signed_vec = 8’b 1000_0000 ; //有符号8位
bit [8:0] result_vec;  //无符号9位

initial begin
result_vec = signed_vec; //直接把有符号数赋给无符号数是不对的
$display(“@1 result_vec = ‘h%x”, result_vec); //以十六进制输出
result_vec = unsigned’(signed_vec); //强制类型转换后再赋值(高位默认填0)
$display(“@2 result_vec = ‘h%x”,result_vec);
end

答案:’h180, ’h080

 

动态转化用$cast(tgt,scr)。 静态类型转换一般是不会检查转换是否合法的,因此具有一定的危险性。如i = int’ (10.0-0.1), 但是动态转换函数$cast却不是这样,它在运行时将进行类型检查,如果转换失败,会产生运行错误。动态转化会在方针的时候做操作,而静态转化是在编译的时候做操作。静态转换和动态类型转换都需要操作符号和系统函数介入,我们统称为显示转换。不需要进行转换的操作叫隐式转换。

如下博客里写得非常好

https://blog.csdn.net/seabeam/article/details/47841539

 

四值逻辑XZ变为二值逻辑只会转化为0,不同长度的类型赋值会把高位截掉。

 

//example 求两次display的结果

logic [3:0] x_vec = ‘b111x;  //4位宽4值逻辑
bit [2:0] b_vec;   //3位宽两值逻辑

initial begin
$display(“@1 x_vec =’b%b , x_vec”);
b_vec = x_vec;
$display(“@2 b_bec=’b%b” , b_vec);
end

答案 ‘b110,高位被截掉

 

3、数组声明

方法: 数据类型+名字+宽度

 

int  lo_hi  [0:15];  //16 位int [0]...[15]
int  c_style  [16];  //16 位int [0]...[15]

 

 

多维数组的声明和使用

int  array2  [0:7][0:3];  //完整声明
int  array3  [8] [4];  //紧凑声明 左高右低,高维度为8,低维度为4
array2  [7][3]=1;   //赋值最后一个元素

//初始化和赋值
int ascend [4] = ’{0,1,2,3};  //对4个元素初始化
int descend [5];   

descend = ‘ {4,3,2,1,0} ;   //为5个元素赋值
descend [0:2] =’{5,6,7} ;  //定向赋值
ascend = ‘{4{8}} ;  //四个值全为8
descend = ‘{9,8, default:1 };//  {9, 8 ,-1, -1, -1}

存储空间考量,以节省空间为导向,中括号放前面是连续存储的,放后面是非连续存储的。

//下面两个数组都可以表示24bit的数据容量,但实际硬件容量的角度出发,哪种方式的存储空间更小呢?(8bit是一个byte,32bits是一个word)

bit [3][7:0] b_pack ;   //合并型,会分配连续的24bit的内存空间,占据32bits空间,系统是总是以一个word整数倍分配空间
bit [7:0] b_unpack[3] ; //非合并型,会分配三个不连续(并行的)的内存空间,每个内存空间有8个bit, 占据3个word的存储空间;
//如果使用logic类型来存储24bit的数据,实际的存储空间应该是多少呢?

logic [3][7:0] 1_pack;  //由于是四值类型,故占用连续的48bit,即2个word就够了
logic [7:0] 1_unpack[3]; //3是分开存储的,而 8是连续存储的,需要3个word

如何判断最高维度和最低维度

int [8][4] array [16][2]             //1 2 3 4维度为16 2 8 4,最高维度为4

 

4、for循环和foreach循环

 

initial begin

bit [31:0]  scr[5]  dst[5];  //数组声明另一种声明方式,数据类型,宽度,名字,宽度

for (int i=0;  i<$size(scr) ; i++)    //调用系统函数,$size (src,1),如果不写即$size(src)默认为最高维度
  src[i] = i;

对数组做循环时,用foreach比较方便
foreach ( dst [ j ] )              //默认创建为J的变量为维度的索引,如果为
    dst [ j ] = src [ j ] *2;       //bit [31:0] scr[10:6] 用foreach写的比较方便

end

 

5、基本数组操作复制和比较

对于赋值,可以利用赋值符号=”直接进行数组的复制。

对于比较,可以用==”或者“!==”来比较数组的内容。

数组复制用单引号

bit [31:0]   src[5] = ‘{0,1,2,3,4};
                 dst[5] =’{5,4,3,2,1};

if (src==dst)
    $display(“src == dst”);  //比较数组
else
    $dispaly (“sec !=dst”);
dst = src;  //数组复制
src[0] = 5;  //修改数组中的一个元素

 

6、动态数组

定宽数组类型的宽度在编译时就确定了,但如果在程序运行时数组的宽度不需要立刻定下来,就需要使用动态数组了,动态数组最大的特点即是可以在仿真运行时灵活调节数组的大小。

动态数组在一开始声明时,需要利用‘[ ]’来声明,而数组此时是空的,即0容量,编译时候是空的,其后,需要使用new []来分配空间,在方括号中传递数组的宽度(个数)。

此外,也可以在调用new[ ] 时将数组名也一并传递,将已有数组的值复制到新的数组中。

int dyn[], d2[];     //声明动态数组,当前元素数量为0
initial begin
  dyn = new[5];     //分配5个元素的空间
  foreach (dyn[j])  dyn[j] = j;      //对元素初始化
  d2 = dyn;        //复制一个动态数组,只是值一样,空间是一样的,d2开始是0个元素,复制之后是5个元素
  d2[0] = 5;    //修改复制的值,d2和dyn有着各自独立的空间,复制并不会改变其值
$display( dyn[0], d2[0] );     //显示数值0和5
dyn = new[20](dyn);     //增加分配20个数值并进行复制,小括号里为低位填充为dyn, 最后结果为0,1,2,3,4..00000
dyn = new[100];      //重新分配100个数值,而旧值不复存在
dyn.delete();      //删除所有元素也可以为dyn=new[0] 或dyn=’{ };
end

intager整形的默认型为Xint整形的默认型为0

 

7、队列

队列结合了链表(C语言里那样)和数组的优点。

链表:当前元素可以索引到上个元素或者下个元素。

队列:在任何地方可以添加/插入/删除元素,可以通过索引的方式对任何元素进行访问。

队列的声明是使用带美元的符号[$],队列元素的标号是从0到$

队列不需要new[ ]去创建空间,创建队列其默认为空,你只需要使用队列的方法为其增减元素,一开始其空间为0。

队列的一个简单使用即是通过其自带的方法push_back()和pop_front()的结合来实现FIFO的用法。push_back()从后面往里推一个数据,push_frount从前面往里加一个数据,pop_front()从前面往外拿一个数据,pop_back()从后面往外拿一个数据。

 

  int j = 1,  q2[$] = {3,4},  q [$] = {0,2,5};   //队列不使用“’”,声明和初始化

initial begin
   q.insert ( 1 , j );    // {0,1,2,5},在1后插入j,1表示位置1(0-3位置)
   q.insert (3, q2);    //{0,1,2,3,4,5} 在q中插入队列q2
   q.delete (1);       // {0,2,3,4,5} 删除第一个元素

//下列从操作运行速度更快(自有方法)
   q.push_front (6) ;   // 结果为{6,0,2,3,4,5} 从队列头部插入
   j = q.pop_back ();   //结果为{6,0,2,3,4} 从队列尾部弹出
   q.push_back (8);    //结果为{6,0,2,3,4,8} 从队列尾部插入
   j= q.pop_front ( );   //结果为{0,2,3,4,8} 从队列头部弹出次南  foreach ( q[i] )

$display ( q [i] );   //打印整个队列
q.delete () ;     //删除整个队列
end

 

8、关联数组 --- 为了超大容量数组来设计(为了保存稀疏矩阵来设计)

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

如果只是需要偶尔需要创建一个大容量数组,那么动态数组就足够了,但是如果你需要一个超大容量的呢? (比如模拟存储空间,如果有200000个data,对于内存负担很大)(又不如对一个有着几个G字节寻址范围的处理器进行建模,在典型的测试中,这几个处理器可能只访问了用来存放可执行代码核数据的几百行或几千个字节,这种情况下对几G字节的存储空间进行分配和初始化显然是浪费的)。动态数组的限制在于其存储空间在以开始创建时即被固定下来,那么对于超大容量的数组中有相当多的数据不会被存储和访问。

我们在真正的测试里,在很大的数组空间内,不会真正的存放很多数。只是截取非常有限的一部分来测试空间。意味着这些大量的空白部分不会去测试到,不需要白白浪费掉这些空白空间,故设计关联数组。不希望空白空间在内存中被开辟出来。

 

 

如上图,只希望0、3、42、1000、4521、200000空间被开辟出来,其他的空间是有数的,但是不需要访问。

关联数组,可以用来保存稀疏矩阵的元素,当你对一个非常大的地址空间寻址时,该数组只为实际写入的元素分配空间,这种实现方法所需要的空间比定宽或动态数组所占用的空间要小得多。

此外,关联数组有其他灵活的应用,在其他软件语言也有类似的数据存储结构,perl里面被称之为哈希(Hash)或者python里被称作词典(Dictionary),可以灵活赋予键值(Key)和数值(Value)

索引值不需要成为整形,只要是数据类型就OK,即使是字符串。

bit [63:0] assoc [int], idx = 1; //关联数组内容的数据类型为bit[63:0],idx数据类型也相同
repeat (64) begin       //对稀疏元素进行初始化
   assoc[idx] = idx;    //1,2,4,8....
   idx = idx << 1;
end

foreach ( assoc[i] )      //使用foreach遍历数组,不一定按照一定顺序进行遍历,可以利用.sort()进行排序
                         //索引值i是int类型
 $display( “assoc[%h] = %h” , i , assoc[i] );

//使用函数遍历数组
if (assoc.first(idx)) begin  // .first()返回第一个索引值,判断对应的空间是否为空
do
    $display(“assoc[%h]=%h”, idx, assoc[idx]);
while(assoc.next(idx));  //get next index .next()返回下一个索引
end

//找到并删除第一个元素
assoc.first(idx);   //返回第一个数组元素的索引值
assoc.delete(idx);  // 删除该索引值对应的空间

//关联数组也可以用字符串索引进行寻址,使用字符串索引读取文件,并建立关联数组,可以实现字符串到数字的映射。

int switch[string],min_address,max_address;
initial begin
  int i,r,file;
  string s;
file = $fopen(“switch.txt”,r);
 while(! $feof(fire) ) begin
   r=$fscanf (fire, “%d %s”, i, s);
   switch[s] = i;
end
$fclose(fire);
//获取最小地址值,缺省为0
min_address=switch[“min_address”]
//获取最大地址值,缺省为1000
if(switch.exists(“max_address”))
  max_address = switch[“max_address”]
else
 max_address =1000

//打印数组的所有元素
foreach(switch [s])
$display(“switch[ ‘ %s ’ ] = %0d ”, s ,switch[s] );
end

 

9、结构体

Verilog最大缺陷之一是没有数据结构,在SV中可以使用struct语言创建结构,和C语言类似。

不过struct的功能少,它只是一个数据的集合,其通常的使用方式是将若干相关的变量组合到一个struct结构定义中。

伴随typedef(自定义新的数据类型)可以用来创建新的类型,并利用新类型来声明更多变量,常常伴随结构体struct使用。

struct  {bit [7:0] r, g, b;}  pixel;   //创建一个pixel结构体,里面有r,g,b三个变量

//这样做太冗余,我们可以直接创建数据类型,提高复用性
//为了共享该类型,通过typedef来创建类型

typedef  struct {bit [7:0] r, g, b; }  pixel_s;   //把此结构体命名为pixel_s 表示
                                                                //_s 是书写习惯,表示数据类型

pixel_s  my_pixel;    //声明变量
my_pixel = ‘{ ‘h10,  ‘h10,  ‘h10 };   //结构体类型的赋值

什么时候用单引号做初始化,什么时候用单引号做初始化。非合并型,数据不是紧挨着存放的,就用‘{ },如动态数组和结构体。合并型的,数据是紧挨着存放的,用{ },如队列里面

 

10、软件常用类型--枚举类型

规范的操作码和指令例如ADD、WRITE、IDLE等有利于代码的编写和维护,它比直接使用’h01这样的常量使用起来可读性和可维护性更好。

枚举类型enum经常和typedef搭配使用,由此便于用户自定义枚举类型的共享使用。

枚举类型的出现保证了一些非期望值得出现,降低设计风险,多用于写状态机。

typedef  enum {INIT, DECODE, IDLE}  fsmstate_e; //枚举类型是三个确定的字符串?组成的
fsmstate_e  pstate, nstate;    //声明自定义变量pastate, nstate
//他们的数据类型是fsmstate_e;
case (pstate)
   IDLE:  nstate = INIT;      //数值赋值
   INIT:   nstate = DECODE;
   default : nstate = IDLE;
endcase
$display(“Next state is %s”,nstate.name());  // .name()显示状态名

问题:INIT,DECODE,IDLE如果是整型int的话,值是多少呢?0,1,2

如果用整型直接赋值,那么合法的范围是多少呢?该赋值行为不合法,枚举类型可以直接赋值给整型,整型不可以直接赋值给枚举类型,需要进行强制类型转换。

 

10、字符串

 VHDL/Verilog 没有字符串设计,非常难受。

所有与字符串相关的处理,请使用string来保存和处理。

与字符串处理相关的还包括字符串的格式化函数,即如何形成一个你想要的字符串句子呢?可以使用SV系统方法$sformatf(), 如果你只需要将它打印输出,那么就使用$display()吧。

 

string s;  //声明了字符串,SV里面没有类似于C语言空字符“\0”这个概念

initial begin
   s = “IEEE ”;  //设置了5个字符串,有一个空字符 ” ”
   $display ( s.getc(0) );     //字符串函数,显示’I’ 
   $display ( s.tolower() );     //引用字符串函数,全显示小写ieee
   s.putc(s.len()-1,”_”);     // len()表示长度,长度-1表示最后一个字符,将空格变为’-’
   s = {s, ”P1800”};        // ”IEEE-P1800” 字符串拼接
  $display(s.substr(2,5));      //显示 EE-P,起始位置2,终止位置5,拿到子字符串显示
//创建一个临时字符串并将其打印
  my_log ( $sformatf (“%s %5d”,s,42) );  // 格式化声明字符串,$sformatf(相当于printf)会返回一个字符串,生成一个字符串
end
task my_log(string message); //打印消息 $dispaly(“@%0t: %s”, $time,message); endtask

 

11、过程块和方法

硬件过程块initial 和always,initial是不可综合的(不是硬件行为,本身设计就是为了做测试),always是可综合的。

SV有一部分语句是放在硬件里,有一部分是放在软件里用的。

为了区分软件世界和硬件世界,我们先引申出一个概念域(scope),为了区分硬件世界、软件世界,我们将定义的软件变量或者例化的硬件所在的空间成之为域。

因此,module/endmodule, interface/endinterface可以被视为硬件世界,program/endprogram和class/endclass可以被视为软件世界,掌握了这一概念有助于我们接下来分析initial和always的使用域。

 

always

always为了描述硬件行为,而在使用时需要明晰是时序逻辑电路还是组合逻辑电路,时序逻辑电路敏感列表里要有时钟,@(event)是为了模拟硬件信号的触发行为,always可能被综合成三个电路:寄存器、锁存器、组合逻辑电路,always经常被用在module里。

不同always之间是并行执行的,不同的硬件块肯定是并行执行的。

不可以在always里初始化变量,初始化变量是软件的行为,硬件行为叫复位,一般在定义变量时进行初始化。

 

initial

   initial只执行一次。和always块一样,initial模块无法被延迟执行,即在仿真一开始它们都会同时执行,而不同的initialalways之间在执行顺序上是没有顺序可言的,硬件块之间都是并行执行的,initial不可综合,不应该出现在disign文件里,initial就是为了测试而生的,由于测试需要按照时间的顺序即软件的方式来完成,所以initial便可以实现这一要求。

Verilog时代,所有的测试语句都可以放置在initial中,为了便于统一管理测试顺序,建议将有关测试语句都放置在同一个initial中。

initial过程块可以在moduleinterfaceprogram中使用。

对于过程块的书写方式,请记住用begin..end将其作用域包住。这一建议同样适用于稍后提到大的控制语句、循环语句等等,初学者可以将其对应于C语言中的花括号{},便于记忆。

 

function

      SV函数定义同C语言非常类似。可以在参数列表中指定输入参数(input)、输出参数(output)、输入输出参数(inout)或者引用参数(ref)。 可以返回数值或者不返回数值(void)。

 

   function int double(inout a);  //double为函数名,返回值是int
      return 2*a;
   endfunction

   initial begin
     $diaplay(“double of %0d is %0d”, 10 ,double(10));
   end

除此之外,function还有以下属性。

1、默认数据类型为logic,例如inout[7:0] addr

2、数组可以作为形式参数传递。

3function可以返回或者不返回结果,如果返回即需要用关键词return,如果不返回则应该在声明function时采用void function()。

4、如果验证世界里用到了函数和task,记住他们只能传递变量,不能传递线网型、硬件信号、硬件存储器,只有在数据变量可以在形式参数列表中被声明为ref类型,而线网类型则不能被声明为ref类型,也就是说只能传递数据,一定不能传递硬件的信号,存储器,why

5、在使用ref时(和inout有点类似,但不完全类似),有时候为了保护数据对象,只被读取不被写入,可以通过const的方式来限定ref声明的参数。

6、在声明参数时,可以给入默认的值,例如input[7:0] addr=0,同时在调用时如果省略该参数的传递,那么默认值即会被传递给function

 

task

task无法通过return来返回结果,因此只能通过outputinout或者ref的参数来返回。

task内可以置入耗时语句,而function不能,这是最重要的,常见的耗时语句包括@eventwait event#delay等。

  task mytask1 (output logic [31:0] x ,input logic y);

  ....

  endtask

function和task的建议使用方式:

1、对于初学者,傻瓜式用法即全部采用task来定义方法,因为它可以内置常用的耗时语句,返回值可以写在变量列表里。

2、对于有经验的使用者,请今后对这两种方法类型加以区别,在非耗时的方法定义时使用function,在内置耗时语句(等待某些事件)时使用task,这么做的好处是在遇到了这两种方法定义时,就可以知道function只能用于纯粹的数字或者逻辑运算,而task则可能会被用于需要耗时的信号采样或者驱动场景。

3、如果要调用function,function和task均可对其调用;而调用task只能是task,function不能调用task,因为编译器不能将有延时的task编译到有延时的function里,编译会报错。

typedef struct {
    bit [1:0] cmd;
    bit [7:0] addr;
    bit [31:0] data;
} trans;   //trans是结构类型

function automatic void op_copy(trans t, trans s);  //赋值函数,automatic如果不知道先不管,后面会介绍
       t=s;    //为什么失败了呢?函数变量模块没有指明方向,默认是input类型,function automatic void op_copy(input trans t, input trans s)相当于t没有返回到参数t中,因为只是输入,退出之后t仍然停留在000,用ref可以修改它,或者用output明确方向。
endfunction

initial begin
trans s;        //trans类型的变量s
trans t;         //trans类型的变量t
s.cmd = ‘h1; s.addr = ‘h10; s.data = ‘h100; op_copy(t,s); t.cmd = ‘h2; //把t里的cmd赋值为2 end

最后的变量t的数值为{‘h2, ‘h0, ‘h0},为什么传参失败了呢?见上面代码注释

 

12、变量生命周期

 在SV中,我们将数据的生命周期分为动态(局部变量)(automatic)和静态(全局变量)(static)。

局部变量的生命周期同其所在域共存亡,例如function/task中的临时变量,在其方法调用结束后,临时变量的生命也将终结,所以他们是动态生命周期。

wire/reg等硬件元素的都是全局变量,生命周期从仿真到结束都有,在Verilog中不需要被声明static,但是这些变量确实是static类型。

全局变量即伴随着程序执行开始到结束一直存在,例如module中的变量某人情况下全部为全局变量,用户也可以理解为module中的变量由于存在模糊硬件信号,所以它们是静态生命周期。

如果数据变量被声明为automatic,那么在进入该进程/方法后,automatic变量会被创建,而在离开该进程/方法后,automatic变量会被销毁,而static变量在仿真开始时即会被创建,而在进程/方法执行过程中,自身不会被销毁,而可以被多个进程和方法所共享,是全局的。

Veirlog是硬件设计语言所有的肯定都是static类型,而做verification需要用到动态数据类型来做高层次验证。

module  //开始

function automatic int auto_cnt(input a);   //automatic为动态变量,局部变量类型
  int cnt =0;                          //垃圾值会被清除
  cnt +=a;
  return cnt;
endfunction

function static int static_cnt(input a);   //指明了变量类型,静态的,垃圾值不会被清除
  static int cnt =0;                  
  cnt += a;
  return cnt;
endfunciton

function static int def_cnt(input a); //如果不指定变量类型,则自动为静态变量static int cnt =0; //空间未释放,垃圾值会存在 cnt += a; return cnt; endfunciton endmodule //结束 initial begin $display(“@1 auto_cnt = %0d”,auto_cnt(1)); //结果为 @1 auto_cnt =1 $display(“@2 auto_cnt = %0d”,auto_cnt(1)); //结果为 @1 auto_cnt =1 $display(“@1 auto_cnt = %0d”,static_cnt(1)); //结果为 @1 static_cnt =1 $display(“@2 auto_cnt = %0d”,static_cnt(1)); //结果为 @1 static_cnt =2 $display(“@1 auto_cnt = %0d”,def_cnt(1)); //结果为 @1 def_cnt =1 $display(“@2 auto_cnt = %0d”,def_cnt(1)); //结果为 @1 def_cnt =2 end

在module、program、interface、task、function之外声明的变量都是静态生命周期,即存在于整个仿真阶段,这同C定义的静态变量一致。

在module、interface和program内部声明,且在task、process或者function外部声明的变量也是static变量,且作用域在该块中。

在module、program和interface中定义的task、function默认都是static类型。

在过程块中(task、function、process)定义的变量句跟随他的作用域,即过程块的类型。

如果过程块为static,则他们也默认为static,反之亦然,这些变量也可以由用户显式声明为automatic或者static。

为了使得在过程块中声明的变量有同一默认的声明周期,可以在定义module、interface、package或者static来区分,对于上述程序块默认的声明周期类型为static。

总之,该定义为automatic的地方一定要定义成automatic。

电路信号就是静态的,验证是在硬件语言引入软件工程的特征,即动态。

 

13、设计例化和链接

 // modulel为硬件域,在定义时候需要标注方向、位宽和端口名。
module mcdt(

input clk_i,
input rstn_i,
input   [31:0]    ch0_data_i,
input             ch0_valid_i,
output            ch0_ready_o,
output  [4:0]     ch0_margin_o,
input   [31:0]     ch1_data_i,
input              ch1_valid_i,
output            ch1_ready_o,
output [4:0]       ch1_margin_o,
input  [31:0]     ch2_data_i,
input              ch2_valid_i,
output             ch2_ready_o,
output [4:0]       ch2_margin_o,
output [31:0]      mcdt_data_o,
output              mcdt_val_o,
output [1:0]       mcdt_id_o
);

 

// 设计例化,首先给出tb的信号列表,然后给出design名称 例化模块的名称{}
// ,. deisgn的端口名 ( tb的信号列表)
module tb1;
  reg clk;
  reg rstn;
  reg [31:0] ch0_data;
  reg ch0_valid;
  wire ch0_ready;
  wire [4:0] ch0_margin;
  reg [31:0] ch1_data;
  reg  ch1_valid;
  wire ch1_ready;
  wire [4:0] ch1_margin;
  reg [31:0] ch2_data;
  reg ch2_valid;
  wire ch2_ready;
  wire [4:0] ch1_margin;
  reg [31:0] ch2_data;
  reg ch2_valid;
  wire ch2_ready;
  wire [4:0] ch2_margin;
  wire [31:0] mcdt_data;
  wire mcdt_val;
  wire [1:0] mcdt_id;
mcdt dut(
.clk_i(clk)
,.rstn_i(rstn)
,.ch0_data_i(ch0_data)
,.ch0_valid_i(ch0_valid)
,.ch0_ready_o(ch0_ready)
,.ch0_margin_o(ch0_margin)
,.ch1_data_i(ch1_data)
,.ch1_valid_i(ch1_valid)
,.ch1_ready_o(ch1_ready)
,.ch1_margin_o(ch1_margin)
,.ch2_data_i(ch2_data)
,.ch2_vaild_i(ch2_margin)
,.mcdt_data_o(mcdt_data)
,.mcdt_val_o(mcdt_val)
,.mcdt_id_o(mcdt_id)
);

 

 

模块连接

在testbench中的链接(connection)指的是有硬件模块参与作为信号驱动方(drive)或者负载方(load)。

Tb中常见的链接有两个硬件模块中的链接,譬如实例A与实例B的链接,可由logic类型完成链接;如果是硬件模块与TB中发送数据激励的链接,则需要考虑数据激励一端如何正确产生数据并发送至DUT一侧,同时数据激励一端也需要对DUT反馈信号做出相应。

 

 

如上图ready为接收DUT端激励成功的响应,未成功继续发送,发送成功响应为1

转载于:https://www.cnblogs.com/shengming2050/p/11155293.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Block-Level Verification】 SystemVerilog 数据类型_数组操作_队列_结构体_枚举类型_字符串_过程块和方法_变量生命周期_例化和链接... 的相关文章

  • zigbee入网过程分析(mac层分析),附Ubiqua抓包

    普通开关 插座 门锁设备入网流程一般到basic就结束了 灯到identity结束 低功耗传感器设备一般到IAS zone结束 设备入网流程如下 1 beacon request 由endpoint发出 请求入网 设备一般会全信道扫描找网
  • binlog数据恢复

    不管你什么架构 一切都是为数据服务的 数据是一个企业最核心的价值 不过出现问题的话 数据一旦被删除 每过一秒 都将对企业造成巨大的损失 最近 还真的发生了一起删库跑路事件 微盟7 24小时紧急恢复数据 商家赔付1 5亿 一 binlog2s
  • View那些事儿(2) -- 理解MeasureSpec

    View的绘制的三大流程的第一步就是Measure 测量 想要理解View的测量过程 必须要先理解MeasureSpec 从字面上看 MeasureSpec就是 测量规格 的意思 其实它在一定程度上决定了View的测量过程 具体来讲它包含了
  • C++_数据存储_药名整理(代码+注释)

    题目描述 输入输出格式 样例 输入样例 4 AspiRin cisapride 2 PENICILLIN Cefradine 6 输出样例 Aspirin Cisapride 2 penicillin Cefradine 6 代码 注释 a
  • 西门子PLC S7-200系列有哪些基本硬件及优点?

    西门子SIMATIC系列PLC 诞生于1958年 经历了C3 S3 S5 S7系列 已成为应用非常广泛的可编程控制器 西门子S7 200系列PLC的基本硬件组成 SIMATIC S7 200的应用领域从更换继电器和接触器一直扩展到在单机 网
  • 【MySQL】复合查询

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 基本查询 1 查询工资高于500或岗位为MANAGER
  • 智明星通 CEO 唐彬森:创业过程中的几笔学费

    编者按 本文来自创新工场 微信号 chuangxin2009 创业公开课 本期课程是由从海外市场起家的唐彬森老师分享 创业过程中的几笔学费 在移动互联网时代背景下 产品为王 只有真正了解用户 才能发现背后的规律 而互联网的基本规律是幂定律
  • NoSQL数据库的介绍、NoSQL的产品、NoSQL数据库的分类等;

    目录 1 1 什么是NoSQL 1 2 为什么使用NoSQL 1 3 RDBMS 传统关系型数据库 vs NoSQL 特点对比 1 4 NoSQL 简史 1 5 NoSQL 产品 1 6 NoSQL 数据库分类 1 7 目前谁在使用NoSQ
  • 静态链表的基本操作实现

    一 实验目的 巩固线性表的数据结构的存储方法和相关操作 学会针对具体应用 使用线性表的相关知识来解决具体问题 二 实验内容 建立一个由n个学生成绩的顺序表 n 的大小 由自己确 定 每一个学生的成绩信息由自己确定 实现数据的对表进行插入 删
  • python中对配置环境的理解

    在咱们学习python前 老师和书本就已经教我们应该如何配置python环境 1 安装python 安装好后 找到python exe 打开其属性 复制他的路径 2 打开控制面板中的所有控制面板项后 选择系统 点击左边的高级系统设置 在高级
  • 进程管理&&内存管理

    操作系统详述 目录 操作系统详述 一 进程管理 这是重点 1 什么是进程管理 2 如何做好进程调度 1 需要把进程这个抽象的概念用数据表示处理 抽象成一个对象 这就是面向对象思想 2 需要对进程做分区 3 现在手上有等待分配CPU的所有进程
  • Linux下使用OpenCv驱动RGB多款彩色摄像头

    最简单的驱动 cout lt lt Built with OpenCV lt lt CV VERSION lt lt endl VideoCapture capture 0 打开摄像头 if capture isOpened 判断是否打开成
  • 多模态大模型系列论文(ALBEF、BLIP、BLIP-2)

    1 ALBEF ALign the image and text BEfore Fusing 1 1 论文与代码链接 https arxiv org abs 2107 07651 GitHub salesforce ALBEF Code f
  • Linux系统项目测试环境部署步骤及操作流程

    jdk安装 在linux上安装JDK 版本位要与linux版本一致 可以通过winscp工具进行安装 把jdk包下载到windows系统上 通过winscp把jdk包直接拖到linux系统的目录中去 具体linux命令步骤 1 tar zx
  • replaceAll遇到特殊字符无法替换问题的坑

    问题 当出现 或者 或者 符等 会导致 无法替换 在一定程度上 这个也算是 一个坑吧 问题原因 replaceAll支持正则 出现正则的符号 就会被当作是正则 从而无法正常替换 解决办法 网上有一个解决方案 是采用 Matcher quot
  • JetBrains IDEA 的安装与设置

    为什么80 的码农都做不了架构师 gt gt gt JetBrains Toolbox 下载 安装 配置 JetBrains IDEA 下载 安装 配置 JetBrains Toolbox 下载 安装 官方下载地址 https downlo
  • MyBatisPlus条件查询的三种格式于null判定

    DQL编程控制 条件查询 MyBatisPlus将书写复杂的SQL查询条件进行了封装 使用编程的形式完成查询条件的组合 方式一 使用QueryWrapper查询数据 lt是小于的意思 price是数据表的字段名称 price容易写错 不推荐
  • VQGAN2_latent diffusion model

    task1 txt2image 先根据config一层层调用 先是ldm models diffusion ddpm LatentDiffusion 里面super init conditioning key conditioning ke
  • SPI协议详解(Standard SPI、Dual SPI和Queued SPI)

    1 标准SPI 1 1 SPI接口的引脚 1 SCLK 时钟线 2 MOSI master output slave input 主设备输出 从设备输入 单向传输 3 MISO master input slave output 主设备输入
  • 查找算法--二分查找 c++实现

    二分查找也称折半查找 Binary Search 它是一种效率较高的查找方法 但是 折半查找要求线性表必须采用顺序存储结构 而且表中元素按关键字有序排列 vs2017 include

随机推荐

  • Docker学习--Docker仓库之Docker Hub的简单了解

    Docker之所以能这么快的火起来 和Docker Hub的作用是分不开的 Docker构建了像GitHub一样的仓库 用来存放大家构建好的Docker镜像 其中已经包括了15000的镜像 大部分需求 都可以通过在Docker Hub中直接
  • npm不是以管理身份运行遇到的问题

    环境 win10 npm3 10 5 问题 在npm install lodash时 出现下列错误 npm debug log 文件内容 0 info it worked if it ends with ok1 verbose cli C
  • 性能测试相关术语

    性能测试相关术语 一 负载 模拟业务操作对服务器造成压力的过程 比如模拟100个用户发帖 二 性能测试 Performance Testing 模拟用户负载来测试系统在负载情况下 系统的响应时间 吞吐量等指标是否满足性能要求 三 负载测试
  • Makefile = 、:=、?=的区别

    相当于 c 语言中的 预编译的过程 在真正解释Makefile前会先将对应的 号左边的量替换成右边的量 而 则是跟 宏观的 号相似 是简单赋值的运算符号 下面举个例子就可以清楚的知道它们之间有何不同 cross arm linux cc c
  • 开关电源怎么测试文波_为什么开关电源需要测试动态响应

    1 导读概念动态响应一般是指控制系统在典型输入信号的作用下 其输出量从初始状态到最终状态的响应 对某一环节 系统 加入单位阶跃输入x t 时 其响应y t 开始逐渐上升 直到稳定在某一定值上为止 响应y t 在达到一定值之前的变化状态称为过
  • 直播分发选低延迟 RTC 还是 CDN?

    简单来看 一个完整的直播应用实现原理是 主播端采集音视频 推到服务器 再由服务器分发给观众观看 主播端负责推流 需要配置选用 RTC 链路分发直播画面或者用 CDN 链路分发 如果涉及连麦还需要考虑如何做 MCU 合流 观众订阅合流的好处是
  • python 读取配置文件config_python学习-读写配置文件-ConfigParse用法

    一 读取配置文件 config ini read filname 读取文件内容 section 获取所有section 返回list options section 获取该section所有options 返回list items sect
  • IDEA + SSH OA 第一天(Hibernate : Mapping (RESOURCE) not found)

    切入主题 看看今天的错误是如何发生的 首先这是我的项目路径 java 是 Sources Root resources 是 Resources Root 放了所需要的配置文件 其中 Hibernate 的配置 显示的是绿色 说明没有问题 在
  • Java基础知识-Map

    1 Map体系 2 各实现类说明及区别 3 哈希映射技术 哈希映射技术是一种就元素映射到数组的非常简单的技术 由于哈希映射采用的是数组结果 那么必然存在一中用于确定任意键访问数组的索引机制 该机制能够提供一个小于数组大小的整数 我们将该机制
  • Windows和Ubuntu下Firefox账号不能同步的解决办法

    最近开始用Ubuntu系统发现一个问题 我在Ubuntu系统自带的或者软件中心下的Firefox版本下创建一个账号 再在Windows的Firefox下登录发现此账号不存在 原因是Firefox有个全球服务和本地服务 Ubuntu下的Fir
  • vscode c++安装与单文件多文件编译配置(win10)

    vscode c 安装与单文件多文件编译配置 win10 总体思路 1下载Vscode mingw cmake 用于多文件编译 2配置 1 gt mingw vscode cmake环境变量 2 gt vscode 插件Chinese co
  • Redis多线程与ACL

    Redis是单线程吗 Redis 的单线程主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的 这也是 Redis 对外提供键值存储服务的主要流程 但 Redis 的其他功能 比如持久化 异步删除 集群数据同步等 其实是由额
  • JS中正则表达式常用语法总结

    目录 一 正则表达式的创建 二 的区别 三 和 四 d s w 五 六 test match 七 支持正则表达式的 String 对象的方法 一 正则表达式的创建 JS正则的创建有两种方式 new RegExp 和 直接字面量 使用RegE
  • Java Remote Debug(IDEA实现远程调试)

    前言 介绍 Java Remote Debug指的是 Java远程调试 特别是当你在本地开发的时候 你需要调试服务器上的程序时 远程调试就显得非常有用 本人就遇到这样一个问题 本地环境运行一切正常 但是部署到服务器就报错 而且问题一直找不到
  • ctfshow中web入门题关键解题思路

    一 信息搜集 1 web1 查看页面源代码 2 web2 burdsuite抓包 3 web3 burdsuite抓包 4 web4 查看robots txt文件 5 web5 查看index phps文件 6 web6 一般网站开发的源代
  • 开源IaaS云平台的分析与比较

    http blog csdn net jiayuboxin article details 11934223 http blog csdn net jiayuboxin article details 10977429 Project Sa
  • Dirichlet分布的推导与理解

    1 概述 Dirichlet 分布与贝塔分布 伽马分布有着紧密的联系 在贝叶斯统计中经常被用作其它概率分布如多项分布的先验分布 且在LDA分析中得到了广泛应用 本文结合直观理解以及详细的数学推导得到狄利克雷分布具体形式 并结合可视化以加深理
  • 解决Android Studio连接不上逍遥模拟器的问题

    1 打开逍遥模拟器 2 打开Android Studio 所要运行的项目 3 cmd 打开命令提示符 然后输入 adb connect 127 0 0 1 21503 然后回车 OK搞定 屡试不爽 看下图 其他模拟器参考这里点击打开链接
  • 在什么场景下要使用类方法

    静态方法和类方法的比较 静态方法 我们先来创建一个类 假设他是检测报告的相关信息 class Report inspection negative def init self name id number self name name se
  • 【Block-Level Verification】 SystemVerilog 数据类型_数组操作_队列_结构体_枚举类型_字符串_过程块和方法_变量生命周期_例化和链接...

    System Verilog芯片验证 System Verilog语言 1 数据类型 Verilog本身是来做硬件描述 是对硬件本身的行为进行建模 SystemVerilog是Verilog的生命延续 sv是对SystemVerilog进行