一.过程语句
在SystemVerilog中,过程语句(Procedural Statements)用于在模块或任务中执行特定的操作。以下是一些常见的SystemVerilog过程语句:
1.阻塞和非阻塞语句
阻塞和非阻塞赋值语句(Blocking and Non-blocking Assignment Statements):用于将值赋给变量或寄存器。(继承于verilog)
- 阻塞赋值语句:使用`=`符号,将右侧表达式的值赋给左侧变量,等待右侧表达式计算完成,才进行赋值操作。
- 非阻塞赋值语句:使用`<=`符号,将右侧表达式的值赋给左侧变量,瞬间进行赋值操作,不受后续语句的影响。
2.顺序语句
顺序语句(Sequential Statements):用于按顺序执行操作。
- `if`语句:根据条件判断执行不同的语句块。
- `case`语句:根据表达式值的不同,执行不同的语句块。
- `for`循环语句:重复执行一段代码块,可以设置初始化、条件和迭代操作。
- `while`循环语句:根据条件重复执行一段代码块。
- `do-while`循环语句:先执行一段代码块,然后根据条件重复执行。
3.并发语句
并发语句(Concurrent Statements):用于并行执行操作。
- `fork-join`语句:用于创建并发执行的代码块。(其在SV中体现更为丰富,将在如下细化模块进行说明)
fork join辨析https://blog.csdn.net/weixin_48957185/article/details/131897410
- `begin-end`语句块:用于分组一系列语句,将其视为一个整体。
4.跳转语句
跳转语句(Jump Statements):用于控制程序的执行流程。
- `break`语句:用于退出当前循环。(新增)
- `continue`语句:用于跳过当前循环中的剩余语句,继续下一次循环。(新增)
- `return`语句:用于退出任务或函数,并返回一个值。
5.其他(等待/事件触发)
此外,SystemVerilog还提供了其他一些过程语句,如延时语句、等待语句、事件触发语句等,可以根据具体需求选择合适的过程语句来实现所需的功能。
二.任务和函数(task/function)
1.作用
- 类似于C语言,函数(function)和任务(task)可以提高代码的复用性和整洁度。
- 它们的目的都在于将大型的过程块切分为更细小的片段,而便于阅读和代码维护。
- 相比于大家更为熟悉的函数,SV引入了任务的概念。function与task之间有相同点和不同点
2.区别
3.使用方法
3.1. 类C的参数声明风格
在verilog中声明一个task的示例如下:
task task_name;
input [31:0] x ;
output [31:0] y ;
wire [31:0] x ;
reg [31:0] y ;
// task 内容
endtask
而在system verilog中,除了可以继续使用verilog中task/function的声明风格外,还提供了一种类似C语言的声明风格:
task task_name(
input [31:0] x,
output logic [31:0] y //可以使用logic替代reg与wire
);
// task 内容
endtask
在声明task和function时,可以设置默认的参数值,这样当使用task和function时对应参数缺省时,可以自动带入默认参数。
function multi(
input int a = 10 ,
input int b = 2
);
int c ;
return c = a*b ;
endfunction
3.2.形式参数
在高级计算机语言中,形式参数是一种非常好的工具,使用形式参数可以节约内存且很多时候可以使程序更加灵活化,然而在verilog中并没有使用形式参数的方式,而在systemverilog中引入了形式参数。
function void print _checksum(const ref [31:0] a[]);
bit [31:0] checksum=0;
for(int i=0; i<a.size();i++)
checksum^=a[i] ;
$display("the array checksum is %0d",checksum);
endfunction
在上例中,关键字ref表示该参数为形式引用。函数体内如果直接改变形式参数,会引起改变量的全局性改变,有时这种改变会比较“危险”,因为我们并不总希望某个函数能改变某个全局变量,特别是该全局变量比较重要的时候。为了解决这个问题,可以使用示例中的关键字const,使用const修饰的形式参数,对于函数来说依然具有形式参数的性质,同时在该函数的作用域内,该参数又可以看作是一个不可改变的常量。如下小节对各位前辈的指导做一个汇总。
3.3.子程序参数
3.3.1.SV中的ref的理解
参考:(以下大部分内容引用自如下帖子)
System Verilog ref参数的理解_systemverilog中ref_簡時光℃的博客-CSDN博客ystemVerilog提供了一个ref关键字作为函数参数的前缀。当使用ref时,表明参数是使用引用传递,'ref’语法类似C++中的引用。有两种情况下使用’ref’做参数比较有意义:第一种情况:由于函数只能有一个返回值(不考虑传统Verilog上的input/output参数端口声明),任务没有返回值。当函数需要返回多个值或者任务需要返回一个以上值的时候,通过引用传递就用得上。第二种情况是运行效率的考虑:当大量的数据需要作为参数传递的时候,值传递效率很低。所有的数据需要在每次函数调用的时候被复制。如_systemverilog中refhttps://blog.csdn.net/weixin_44969124/article/details/108164227system verilog中关于ref 的求解答 - IC验证讨论 - EETOP 创芯网论坛 (原名:电子顶级开发网) -各位兄弟姐妹,小弟在看system verilog的时候,看到这样ref有些不明白,求教一下大家。 1 SystemVerilog也允许通过传值的方式来传递数组参数,这样数组将 ... system verilog中关于ref 的求解答 ,EETOP 创芯网论坛 (原名:电子顶级开发网)https://bbs.eetop.cn/thread-353820-1-1.html
可以看到上面第二个例子中,SV提供了ref关键字作为函数参数的前缀。
当使用ref时,表明参数是使用引用传递,'ref’语法类似C++中的引用。
那么什么时候用到ref参数前缀是必要的呢?(两种情况)
- 第一种情况:由于函数只能有一个返回值(不考虑传统Verilog上的input/output参数端口声明),任务没有返回值。当函数需要返回多个值或者任务需要返回一个以上值的时候,通过引用传递就用得上。
- 第二种情况是运行效率的考虑:当大量的数据需要作为参数传递的时候,值传递效率很低。所有的数据需要在每次函数调用的时候被复制。如果参数使用’ref’前缀,可以不需要进行数据复制。但是这样会使得参数的数据容易被函数/任务中的代码修改。此危险可以通过声明ref参数为常量来解决.
这里简单举例解释一下,需要结合C的指针概念理解:
task/function xxxxx(ref logic arguments);
endtask/endfunction
上面的task/function,其“形参”是:ref logic arguments
ref的意思你自己已经看过了,那么,这个task/function被调用的时候发生了什么呢?
那就是logic型变量arguments的指针被复制并传递至task/function内部,在task/function内部的一切
对arguments的操作,都是对变量arguments对应的指针,所指向的内容做操作。
这和c语言的指针是一个概念。也就是说task/function参数是复制一份送入task/function的,这个行为是没有改变的,加了ref,那么参数的指针被复制,不加ref则参数本身被复制,当你对arguments操作的时候,你改变的,是指针所指向地址的内容而非指针本身。(这就是所谓的:在子程序修改ref参数变量的时候,其变化对于外部是立即可见的,因为指针指向的地址没变而该地址本身指向的内容可能改变了。如果sv允许修改指针本身,那么,这句话立刻就不成立了,但是sv限制了对指针本身的修改。)
const限定符限定了指针指向的内容是不可修改的,这个限定符可以防止在task/function内部修改了某个参数,引起意外错误(比如,在其他地方也用了这个指针指向的内容,但他不知道在task/function内部这个指针指向的内容已经被改变了,这是一种非常有用保护措施).
就是说你一加ref,你程序操作的目标数据,都是存储器中同一片地址数据,这样凡是用到这片数据的程序,都会牵一发动全身。
使用注意:见绿皮书P121
1)在需要方法中修改类中参数值得时候,需要在参数前加上ref,尤其是句柄,否则在方法内部对参数的修改 不会被调用该方法的代码看到。P123
2) ref 参数只能被用于带自动存储的子程序中,若程序或模块指明了automatic属性,则其子程序都是自动存储的(P55);如果不希望子程序修改数组的值,则使用const ref类型。
总结:ref
ref 只能被用于带自动存储的子程序中,不希望被修改则用const ref(P55)
在任务里修改变量对调用他的函数随时可见(P123)
3.3.2. SV中的automatic
class中成员和方法默认为动态的即automatic。
program、module、interface、package中函数和任务和变量默认为静态的即static。
首先需要了解如下几个概念:
(1)全局变量和局部变量
在讨论静态变量和动态变量之前,我们先说全局变量和局部变量的概念。
- 局部变量的生命周期同其所在域,例如function/task中的变量,在方法调用结束后,这些变量的也将消失,所以它们是动态生命周期;
- 全局变量是从仿真开始到结束一直存在的,例如module中的变量默认情况下全部为全局变量,这也可以理解为module中的变量是硬件电路中实际存在的信号和连接,所以它们是静态生命周期;
(2)静态变量和动态变量
变量可以分为动态(automatic)和静态(static)
静态变量的特点:
- 该变量将被这个类的所有实例所共享,并且使用范围仅限这个类。
- 静态变量在声明时就应该对其初始化,它只初始化一次,也就是在仿真0时刻就存在。
- 可以认为声明在类中的静态成员变量,它是保存在类中,而不是在对象中,它会一直存在的,不会因为对象被销毁而消失。
- 静态变量可以在类没有被实例化的时候调用,通过 class::static_variable 的方式获取静态变量。
动态变量的特点:
- 动态变量是类在实例化时,即调用构造函数new()才会初始化。
- 它的声明周期随着对象而存在,当对象被销毁时,这个变量就也就消失了。
- 必须要在类被实例化之后才可以调用。
参考:
class Transaction;
static int count = 0;
int id;
function new();
id = count++;
endfunction
endclass
Transaction t1,t2;
initial begin
t1 = new(); // t1 中:count = 1,id =0
t2 = new(); // t2 中:count = 2,id =1
$display("t2中count=%0d和id=%0d",t2.count,t2.id);
$display("Transaction中count=%0d",Transaction::count);
end//Transaction类中:count = 2
解释代码:
每例化一次transaction,count就自加1
第一次在例化时,cout值为0,所以对象 t1 中的 id = 0;
类的构造函数调用完后,使得count自加1,count = 1;
第二次在例化时,cout值为1,所以对象 t2 中的 id = 1;
可以通过Transaction::count的方式,访问类中的静态变量,此时类中的count = 2;
(3)静态方法和动态方法
static 和 automatic 除了可以修饰类中的成员变量,还可以修饰 function 和 task,被static修饰的方法称为静态方法。
- 静态方法的特点:
如果方法被static修饰,那么其内部所有的声明的变量都是 static 的;
静态方法可以在类没有被实例化时被调用,通过 :: 操作符获取,具有全局的静态生命周期;
如果方法被声明为static,那么在仿真开始时即会被创建,且可以被多个进程和方法共享;
- 动态方法的特点:
如果方法被修饰为 automatic,那么其内部所有的声明的变量默认都是 automatic的;
如果被修饰为 automatic,那么在进入该方法后,automatic变量会被创建,而离开该进程/方法后就被销毁;
如下举例说明了两种方法的区别:
module static_test;
function automatic int cnt1(input a); // 函数内所有的变量均为automatic
int cnt = 0; //这个变量在每次调用时都会初始化
cnt += a ;
return cnt;
endfunction
function static int cnt2(input a); // 函数内所有的变量均为static
static int cnt = 0; //这个static 变量 cnt只会初始化一次
cnt += a ;
return cnt;
endfunction
function int cnt3(input a); // module中的方法默认为static
int cnt = 0; //这个static 变量 cnt只会初始化一次
cnt += a ;
return cnt;
endfunction
endmodule
initial begin
$display("%0d",cnt1(1)); // 输出1
$display("%0d",cnt1(1)); // 输出1
$display("%0d",cnt2(1)); // 输出1
$display("%0d",cnt2(1)); // 输出2
$display("%0d",cnt3(1)); // 输出1
$display("%0d",cnt3(1)); // 输出2
end
解释代码:
- 函数声明为automatic或static时,其块内所有的变量都是automatic或static的;
- automatic类型的变量随函数调用结束后就销毁了,下次再调用时,又会初始化;
- static类型的变量随函数调用结束后不会被销毁,下次调用时也不需要重新初始化;
- module内的方法默认是static的;
四.SV中的时间函数和调度(待细化总结)
SystemVerilog中scheduler(调度)SV中调度机理简析https://mp.weixin.qq.com/s/QDUCofxdDAMT8SJvJgaXMASystemVerilog学习之路(6)— 最小时间和时间片_什么叫delta-cycle_Willliam_william的博客-CSDN博客UVM学习之路(6)— SystemVerilog中的最小时间和时间片一、前言delta-cycle(无限最小时间):默认情况下,时钟对于组合电路的驱动会添加一个无限最小时间(delta-cycle)的延迟,而该延迟无法用绝对时间单位衡量,它比最小时间单位精度还小。time-slot(时间片): 在仿真器中敲入命令run 0,即是让仿真器运行一个时间片的时间,在一个时间片中可以发生很多事情,一个时间片里面包含无数个delta-cycle。二、代码实现编写代码如下所示`timescale 1n_什么叫delta-cyclehttps://blog.csdn.net/qq_38113006/article/details/120212477
SV中主要有三个获取仿真时间的系统函数:$time,$stime,$realtime。
三个系统函数的返回值=当前仿真时间/当前作用域的time unit。
参考:
SystemVerilog----任务(task)和函数(function)_function systemverilog_一点一点的进步的博客-CSDN博客目录1.概述2.任务和函数的区别3. 任务和函数中的参数传递(值传递和引用传递)4.任务函数返回值5、program块和module模块的区别1.概述类似于C语言,函数(function)和任务(task)可以提高代码的复用性和整洁度。它们的目的都在于将大型的过程块切分为更细小的片段,而便于阅读和代码维护。相比于大家更为熟悉的函数,SV引入了任务的概念。function与task之间有相同点和不同点。2.任务和函数的区别函数function ..._function systemveriloghttps://blog.csdn.net/yh13572438258/article/details/121453101System Verilog 学习笔记2:task/function - 知乎2 Task and Function task和function在verilog中就已经存在,然而systemverilog为了便于工程使用对它们增加了许多新的特性。 1 task与function最大的区别有两点(1)task可以添加消耗时间的语句,而function不可以…https://zhuanlan.zhihu.com/p/110690241SV中的automatic与static_sv automatic_better_xiaoxuan的博客-CSDN博客module breakpoint;int val1;int val2;int result1;//定义在这里,或者定义在未特殊声明的function/task中都是静态的方法function int incr_static(input int a); $display("result1 = %0d", result1); result1 = a + 1; $display("re_sv automatichttps://blog.csdn.net/better_xiaoxuan/article/details/79015165SV笔记:static 和 automatic 概念及相关规定_sv中function automatic_小汪的IC自习室的博客-CSDN博客目录1 首先总结如下:2 全局变量和局部变量3 静态变量和动态变量4 静态方法和动态方法class中成员和方法默认为automatic,program、module、interface、package中函数和任务和变量默认为静态的。在讨论静态变量和动态变量之前,我们先说全局变量和局部变量的概念。变量可以分为动态(automatic)和静态(static),静态变量的特点:动态变量的特点:如下,使用静态变量count来计数所创建的实例数目:解释代码:static 和 automatic 除了可以修饰类中_sv中function automatichttps://blog.csdn.net/weixin_42294124/article/details/125493766?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-125493766-blog-79015165.235%5Ev38%5Epc_relevant_anti_t3&spm=1001.2101.3001.4242.1&utm_relevant_index=3