对任务和函数的改进
1、任务和函数的隐式语句组
SystemVerilog会推断出begin…end
SystemVerilog简化了任务和函数的定义,有多条语句时不在需要begin …end对多条语句进行打包。打包省略之后,任务或函数中的语句将会顺序执行,就像仍然在begin…end中一样。
function states_t NextState(State_t State);NextState = State;//默认次态case(State)WAITE:if(start) NextState = LOAD;LOAD:if(done) NextState = STORE;STORE: NextState = WAITE;endcase
endfunction
2、返回函数值
函数会创建一个与其名称和类型一样的隐含变量
SystemVerilog增加一条return语句,和C语言一样,函数可以像C语言中一样,使用return语句返回数值。
function int add_and_inc(input int a,b)return a+b+1;
endfunction
return语句的优先级高于返回函数名的值
如果执行return语句就会返回一个值。如果函数执行到最后也没有遇到return语句,那么与Verilog一样,最后赋给函数名变量的值就是函数的返回值。即使使用了return语句,函数名仍然是一个变量,可以执行return语句之前当作临时内存使用。
funciton int add_and_inc(input int a,b);add_and_inc = a + b;return ++ add_and_inc;
endfunction
3、在任务和函数结束前返回
return语句可以用来在到达结尾前退出
在SystemVerilog中,使用return语句可以在执行流的任何时刻退出任务或函数,而不需要到达任务或函数的结尾。
function automatic int log2(input int n);if(n <= 1) return 1; //异常中止函数log2 = 0;while(n>1)beginn = n/2;log2++;end
endfunction
4、空函数
SystemVerilog增加了一个类似C语言的类型–void。函数可以显式地声明为void数据类型,表示函数没有返回值。空函数的调用方式和任务一样。但同样有函数在语法和语义上的限制。例如,函数不能包含任何类型的延迟或事件控制,也不能使用非阻塞赋值语句。空函数的另一个优点是克服了函数不能调用任务,这改善了难以在复杂函数添加编码结构的缺陷。不管怎样,函数总可以调用其他函数,调用空函数就可以得到和使用任务相同的结构化编码风格。
SystemVerilog的另一个改进之处是函数可以有output和inout的形式参数。这样一来,虽然空函数没有返回值,仍然可以传送调用函数所产生的变化。
typedef struct{logic valid;logic [7:0] check;logic [63:0] data;
}packet_t;function void fill_packet(input logic [63:0] data_in;output packe_t data_out
);data_out.data = data_in;for(int i = 0; i <= 7; i++)data_out.check[i] =^data_in[8*i +:8];data_out.valid = 1;endfunction
在可综合的模块中,使用空函数来代替任务
空函数的优势在于它可以像任务一样被调用,但必须遵循函数编码规则,例如函数不能包含事件控制,这样的限制有助于确保能综合处正确的结果。
5、使用名称传递任务/函数的参数
SystemVerilog可以通过名称传递变量值
在SystemVerilog中,增加了使用形式参数的名称而不是顺序来传递参数值的功能。通过参数名称传递可以用任何顺序,而且显式的传递给指定的形式参数。
always@(posedge clock)result <= divide(.denominator(b),.numerator(a));function int divide(input int numerator,denominator);if(denominator == 0)beginif(denominator == 0)begin$display("Error!divide by zero");return 0;endendelsereturn = numerator / demoinator;endfunction
参数名称传递可以减少错误
使用参数名称传递的方式消除了原先那个数组应该传给那个参数的不确定性。任务或函数的调用代码明确地指明了设计的意图,而且减少了那些由于疏忽造成的不易察觉和调试的设计错误。
6、增强型函数形式参数
SystemVerilog的函数有输入和输出端口
SystemVerilog允许函数的形式参数像任务一样,可以声明为input、output和inout。函数可以有任意个输出以及函数返回值,这极大地扩展了函数的建模范围。
//SystemVerilog风格的函数形式参数
function [63:0] add(input [63:0] a,boutput overflow);
{overflow,add} = a +b;
endfunction
对有输出的函数的调用限制
为了防止出现不合要求的以及不可综合的边缘效应,SystemVerilog对何处能调用有output或inout参数的函数进行了一定的限制。有输出或输入输出参数的函数不能在一下几种情况中调用:
- 事件表达式
- 使用过程持续赋值的表达式
- 不在过程语句内的表达式
7、无形式参数的函数
SystemVerilog的函数可以没有参数
8、形式参数的缺省方向和类型
缺省的形式参数方向是输入
SystemVerilog简化了任务和函数声明的语法,形式参数的缺省方向为input。也就说,如果没有声明形式参数方向,所有参数假定是输入的。一旦声明了一个方向,后面的参数为声明的方向。
function int compare(int a,b);
...
endfunciton
//a,b是输入,y1,y2是输出
task mytask(a,b,output y1,y2);
...
endtask
缺省的形式参数类型是logic
9、缺省的形式参数值
SystemVerilog允许任务与函数为每个形式参数定义一个可选的缺省值。设定缺省值的语法与设置变量的初始值的语法类似。
形式参数count的缺省值是0,形式参数step的缺省值是1function int incrementer(int count = 0,step = 1);incrementer = count + step;
endfunction
调用任务或函数时可以对一些参数不指定
当调用任务或函数时,不一定非得给有缺省值的参数传递参数值。如果没有给这种参数传递任何值,则在任务或函数调用时使用缺省值。
函数incrementer在调用时只给了一个值,
这个值会传递给函数的第一个形式参数。
而第二个形式参数step,会使用缺省值1always@(posedge clock)result = incrementer(data_bus);
缺省的形式参数值允许任务或函数调用时只传递与该次调用相关的参数
SystemVerilog允许任务或函数的参数表达式数目小于形式的参数数目。
如果在调用中,没有给任务或函数的参数传递数值,则该参数的形式定义中必须有一个缺省值。如果存在一个没有缺省值,也没有传递值的形式参数,系统就会报告一个错误。
10、数组、结构体和联合体作为形式参数
形式参数可以是结构体联合体或数组
SystemVerilog允许非压缩数组、压缩或非压缩结构体和压缩、非压缩或标签联合体传递出/出任务和函数。对于结构体和联合体,形式参数必须定义为结构体或联合体类型(用typedef来定义这种类型)。压缩数组在传递给任务或函数时被看做向量。如果调用的压缩数组参数的宽度与形式参数不匹配,该向量就会按照Verilog向量赋值规则进行截断或扩展。对于非压缩数组,传递给任务或函数的调用数组参数必须与数组形式参数定义的结构和元素类型精确匹配。为了匹配,调用参数和形式参数必须具有相同的数组维数和维度宽度,并且每个元素有效相同的压缩宽度。
typedef struct packed{logic valid;logic [7:0] check;logic [63:0] data;
}packet_t;
function void fill_packed(input logic [7:0] data_in [0:7];//数组参数output packed_t data_out //结构体参数
);
for(int i = 0;i <= 7; i++)
begindata_out.data[(8*i) + :8] = data_in[i];data_out.check[i] = ^data_in[i];
enddata_out.valid = 1;
endtask
11、用引用取代复制来传递参数值
数值通过复制传递给任务和函数
当调用任务或函数时,系统会将输入值复制到任务或函数中于是这些值变为任务或函数的局部数值。当任务或函数执行到末尾返回时,系统再将所有的输出复制到任务或函数调用程序。
SystemVerilog可以通过引进任务/函数参数进行显式传递
SystemVerilog对自动任务和函数进行扩展,可以使用引用而不是复制的方法传递数值。为了通过引用传递数值,形式参数用关键字ref取代方向关键字input,output,或inout进行声明。ref参数名就成了对传递任务或函数的值的实际储存区进行层次化引进的别名。任务或函数内使用的是局部参数名而非外部信号名。这种引用提供了类似Verilog外部信号引用的能力,但没有外部信号名必须硬编码到任务或函数一样。
ref形式参数是实际值的别名
通过引进传递,变量只需要在调用部分声明,而不需要在任务或函数描述时再次声明。这样,任务或函数只在它调用的范围内引用这个变量。引用不传递到任务或函数内的信号就像对外部信号的引用已经隐含传递给任务或函数一样。
注意:只有自动任务和函数可以具有ref参数
为了具有ref参数,任务或函数必需是自动的。任务或函数可以显式声明为自动的,也可以通过为定义为自动的模块、接口或程序中声明来推断为自动的。
module chip(...);typedef struct{logic valid;logic [7:0] check;logic [63:0] data}packet_t;packet_t data_packet;bit[7:0] raw_data [0:7];always@(posedge clock)if(data_ready) fill_packet(.data_in(raw_data),.data_out(data_packet));function automatic void fill_packet(ref logic[7:0] data_in [0:7];//ref参数ref packed_t data_out //ref参数);for(int i = 0;i <= 7;i++)begindata_out.data[(8*i)+:8]=data_in[i];data_out.check[i] = ^data_in[i];end
endfunction
...
endmodule
只读引用参数
通过引用传递的参数为只读
通过将形式参数声明为const ref类型,可以将引用形式参数声明为只允许对引用的对象进行读操作。这样可以使任务或函数只能引用调用部分的信息,而禁止任务或函数改动信息的内容。
function automatic void fill_packet(const ref logic[7:0] data_in [0:7];//ref参数ref packed_t data_out //ref参数);...endfunction
任务ref参数对变化敏感
通过引用传递的参数允许对变化敏感
ref参数的一个重要特点是任务中的逻辑会对信号在调用程序内发生的变化敏感。对变化的这种敏感对函数ref参数不适用。由于函数必需零延时执行,函数不能包含对参数变化敏感的时间控制。
typedef struct packed{logic valid;logic [7:0] check;logic [63:0] data;
}packet_t;
packet_t send_packet,receive_packed;task automatic check_results(input packet_t sent,ref packet_t received,ref logic done
);static int error_count;wait(done)if(sent !== receive)beginerror_count++;$display("ERROR! receive bad packet");end
endtask
ref参数可以读取当前值
ref参数可以立即传播变化
如果任务的输出通过复制来传递,需等到任务退出时才会将输出值复制回调用模块,假如在任务的输出参数值发生变化时间和任务退出的时间之间存在时序控制或者事件控制,调用程序只能在任务退出时才能看到变量的这个变化,而不能在其发生变化时立即察觉。
如果任务的输出通过引用来传递,任务将对调用模块中变量直接赋值。因此调用模块中对这个变量敏感的事件控制都会第一时间检测到变化,而不是要等到任务执行完将输出参数复制回调用模块时。
对使用ref参数进行函数调用的限制
带有ref形式参数的函数可以修改函数外部变量的值,因此与带有输出参数的函数有相同的使用限制。有output、inout或ref参数的函数不能被以下几种情况调用:
- 事件表达式
- 持续赋值中的表达式
- 过程持续赋值内的表达式
- 不在过程语句内的表达式
命名的任务和函数结尾
SystemVerilog允许用关键字endtask和endfunction指定名称。
endtask:<task_name>
endfunciton:<funciton_name>
冒号前后的空格可以省略。结尾处指定的名称必须与对应的任务或函数的名称一致。
function int add_and_inc (int a,b);return a+b+1;
endfunciton:add_and_incrask autormatic check_result(input packet_t sent,ref packet_t received,ref logic done
);
static int error_count;
...
endtask:check_results
13、空任务和函数
任务或函数可以为空
SystemVerilog允许任务和函数完全为空,即不包含任何语句或语句组。空函数将返回表示函数名的隐含变量的当前值。
空任务和空函数在非完整的代码中预留出了空间。对于自顶向下的设计流程而言,可以在模块的描述中创建空任务和函数作为模型文件,而在后面的设计步骤中逐步将其内容添加完整。