IEEE SystemVerilog Chapter13 : Tasks and functions (subroutines)
13.2 Overview
任务和函数提供了从描述中的几个不同位置执行通用过程的能力。它们还提供了一种将大型过程分解为小型过程的方法,以便更容易地阅读和调试源代码描述。本小节讨论了任务和函数之间的区别,描述了如何定义和调用任务和函数,并给出了每种任务和函数的示例。任务和函数统称为子例程。
以下规则将任务与函数区分开来,但在13.4.4中指出的例外情况除外:
--函数主体中的语句应以一个仿真时间单位执行;任务可能包含时间控制语句。
--函数无法启用任务;一个任务可以启用其他任务和函数。
--非虚拟函数应返回单个值;任务或void函数不应返回值。
--非虚拟函数可以用作表达式中的操作数;该操作数的值是函数返回的值。
例如:
可以定义任务或函数来交换16位字中的字节。该任务将在输出参数中返回切换后的单词;因此,启用名为switch_bytes的任务的源代码可能看起来像以下示例:
switch_bytes (old_word, new_word);
任务switch_bytes将取old_word中的字节,颠倒它们的顺序,并将颠倒的字节放在new_word中。单词切换函数将返回切换后的单词作为函数的返回值。因此,函数switch_bytes的函数调用可能看起来像以下示例:
new_word = switch_bytes (old_word);
13.3 Tasks
应通过定义要传递给任务的参数值和接收结果的变量的语句启用任务。任务完成后,应将控制权返回启用进程。因此,如果任务内部有时序语句,那么启用任务的时间可能与返回控件的时间不同。一个任务可以启用其他任务,而这些任务又可以启用其他的任务——启用的任务数量没有限制。无论启用了多少任务,在所有启用的任务完成之前,控制都不会返回。
任务声明的语法如下语法13-1所示。
task_declaration ::= task [ lifetime ] task_body_declaration // from A.2.7
task_body_declaration ::=
[ interface_identifier . | class_scope ] task_identifier ;
{ tf_item_declaration }
{ statement_or_null }
endtask [ : task_identifier ]
| [ interface_identifier . | class_scope ] task_identifier ( [ tf_port_list ] ) ;
{ block_item_declaration }
{ statement_or_null }
endtask [ : task_identifier ]
tf_item_declaration ::=
block_item_declaration
| tf_port_declaration
tf_port_list ::=
tf_port_item { , tf_port_item }
tf_port_item23 ::=
{ attribute_instance }
[ tf_port_direction ] [ var ] data_type_or_implicit
[ port_identifier { variable_dimension } [ = expression ] ]
tf_port_direction ::= port_direction | const ref
tf_port_declaration ::=
{ attribute_instance } tf_port_direction [ var ] data_type_or_implicit list_of_tf_variable_identifiers ;
lifetime ::= static | automatic // from A.2.1
signing ::= signed | unsigned // from A.2.2.1
data_type_or_implicit ::=
data_type
| implicit_data_type
implicit_data_type ::= [ signing ] { packed_dimension }
Syntax 13-1—Task syntax (excerpt from Annex A)
在a tf_port_item中,省略显式port_identifier是非法的,除非在函数原型或任务原型中。
任务声明的形式参数要么在括号中(如ANSI C),要么在声明和方向中。
task mytask1 (output int x, input logic y);...
endtasktask mytask2;output x;input y;int x;logic y;...
endtask
每个形式参数都有以下方向之一:
input // copy value in at beginning
output // copy value out at end
inout // copy in at beginning and out at end
ref // pass reference (see 13.5.2)
如果未指定方向,则存在默认的input方向。一旦指定了方向,后续形式将默认为相同的方向。在以下示例中,形式参数a和b默认为input,u和v都是output:
task mytask3(a, b, output logic [15:0] u, v);...
endtask
每个形式参数都有一个数据类型,可以显式声明或从上一个参数继承。如果数据类型没有显式声明,那么如果它是第一个参数或显式指定了参数方向,则默认数据类型为logic。否则,数据类型将继承自上一个参数。数组可以指定为任务的形式参数。
例如:
// the resultant declaration of b is input [3:0][7:0] b[3:0]
task mytask4(input [3:0][7:0] a, b[3:0], output [3:0][7:0] y[1:0]);...
endtask
任务声明和endtask之间可以编写多条语句。语句是按顺序执行的,就像它们被括在一个begin ... end中一样。没有任何声明也是合法的。到达结束任务时,任务将退出。return语句可用于在endtask关键字之前退出任务。对任务的调用也称为任务启用(有关调用任务的更多详细信息,请参阅13.5)。
示例1:以下示例说明了具有五个参数的任务定义的基本结构:
task my_task;input a, b;inout c;output d, e;. . . // statements that perform the work of the task. . .c = a; // the assignments that initialize result outputsd = b;e = c;
endtask
或者使用任务声明的第二种形式,任务可以定义如下:
task my_task (input a, b, inout c, output d, e);. . . // statements that perform the work of the task. . .c = a; // the assignments that initialize result variablesd = b;e = c;
endtask
以下语句调用该任务:
initial
my_task (v, w, x, y, z);
任务调用参数(v、w、x、y和z)对应于任务定义的参数(a、b、c、d和e)。在调用时,input和inout类型参数(a、b和c)接收在v、w和x中传递的值。因此,调用的执行有效地导致以下分配:
a = v;
b = w;
c = x;
作为任务处理的一部分,my_task的任务定义将计算出的结果值放入c、d和e中。任务完成后,将执行以下分配,将计算值返回给调用进程:
x = c;
y = d;
z = e;
示例2:以下示例通过描述红绿灯序列器来说明任务的使用:
module traffic_lights;logic clock, red, amber, green;parameter on = 1, off = 0, red_tics = 350,amber_tics = 30, green_tics = 200;
// initialize colorsinitial red = off;initial amber = off;initial green = off;always begin // sequence to control the lightsred = on; // turn red light onlight(red, red_tics); // and wait.green = on; // turn green light onlight(green, green_tics); // and wait.amber = on; // turn amber light onlight(amber, amber_tics); // and wait.end
// task to wait for 'tics' positive edge clocks
// before turning 'color' light off
task light (output color, input [31:0] tics);repeat (tics) @ (posedge clock);color = off; // turn light off.
endtask: lightalways begin // waveform for the clock#100 clock = 0;#100 clock = 1;
end
endmodule: traffic_lights
13.3.1 Static and automatic tasks
在模块、接口、程序或包中定义的任务默认为静态的,所有声明的项都是静态分配的。这些项目应在同时执行的任务的所有用途中共享。可以通过以下两种方式将任务定义为使用自动存储:
--使用可选的automatic关键字作为任务声明的一部分显式声明。
--通过在定义为automatic的模块、接口、程序或包中定义任务来隐含声明。
类中定义的任务总是automatic的(见8.6)。automatic任务中声明的所有项都会为每次调用动态分配。所有形式参数和局部变量都存储在堆栈中。分层引用无法访问automatic任务项。automatic任务可以通过使用其层次结构名称来调用。特定的局部变量可以在静态任务中声明为automatic变量,也可以在automatic任务中宣布为静态变量。
13.3.2 Task memory usage and concurrent activation
一个任务可以同时启用多次。automatic任务的所有变量应在每次并发任务调用中复制,以存储该调用特定的状态。
静态任务的所有变量都应是静态的,因为无论任务的并发激活次数如何,模块实例中的每个声明的局部变量都应对应一个变量。但是,模块的不同实例中的静态任务应具有彼此独立的存储。
静态任务中声明的变量,包括输入、输出和inout类型参数,应在调用之间保留其值。应将它们初始化为6.8中所述的默认初始化值。
automatic任务中声明的变量,包括输出类型参数,应在执行进入其范围时初始化为默认初始化值。input和inout类型参数应初始化为从任务启用语句中列出的这些参数对应的表达式传递的值。
由于在自动任务中声明的变量在任务调用结束时被释放,因此它们不应用于此后可能引用它们的某些构造中:
--不得使用非阻塞赋值或程序连续赋值对其赋值。
--程序性的连续任务或程序性的强制声明不得引用它们。
--它们不应在非阻塞分配的分配内事件控制中引用。
--它们不应使用$monitor和$dumpvars等系统任务进行跟踪。
13.4 Functions
函数的主要用途是返回要在表达式中使用的值。void函数也可以代替任务来定义在单个时间步长内执行和返回的子例程。本条款的其余部分解释了如何定义和使用函数。
函数有一些限制,可以确保它们在不挂起启用它们的进程的情况下返回。除13.4.4中注明的例外情况外,以下规则应适用于其使用:
a)函数不应包含任何时间控制语句。也就是说,任何包含#, ##, @, fork-join, fork-join_any, wait, wait_order, or expect。
b)函数不应启用任务,无论这些任务是否包含时间控制语句。
c) 函数可以使细粒度过程控制方法能够暂停自己的或另一个进程(见9.7)。
语法13-2给出了定义函数的语法。
function_declaration ::= function [ lifetime ] function_body_declaration // from A.2.6
function_body_declaration ::=
function_data_type_or_implicit
[ interface_identifier . | class_scope ] function_identifier ;
{ tf_item_declaration }
{ function_statement_or_null }
endfunction [ : function_identifier ]
| function_data_type_or_implicit
[ interface_identifier . | class_scope ] function_identifier ( [ tf_port_list ] ) ;
{ block_item_declaration }
{ function_statement_or_null }
endfunction [ : function_identifier ]
function_data_type_or_implicit ::=
data_type_or_void
| implicit_data_type
data_type ::= // from A.2.2.1
integer_vector_type [ signing ] { packed_dimension }
| integer_atom_type [ signing ]
| non_integer_type
| struct_union [ packed [ signing ] ] { struct_union_member { struct_union_member } }
{ packed_dimension }13
| enum [ enum_base_type ] { enum_name_declaration { , enum_name_declaration } }
{ packed_dimension }
| string
| chandle
| virtual [ interface ] interface_identifier [ parameter_value_assignment ] [ . modport_identifier ]
| [ class_scope | package_scope ] type_identifier { packed_dimension }
| class_type
| event
| ps_covergroup_identifier
| type_reference
signing ::= signed | unsigned
lifetime ::= static | automatic // from A.2.1.3
Syntax 13-2—Function syntax (excerpt from Annex A)
13) 当packed 维度与struct或union关键字一起使用时,也应使用packed 关键字。
14) 当在net声明中使用type_reference时,它前面应该有一个net type关键字;当它用于变量声明时,它前面应该有var关键字。
为了指示函数的返回类型,它的声明可以包括显式data_type_or_void,也可以使用仅指示packed 维度范围的隐式语法,以及可选的有符号性。使用隐式语法时,返回类型与隐式语法前面紧跟着logic关键字的情况相同。特别是,隐式语法可以为空,在这种情况下,返回类型是logic标量。函数也可以是void的,没有返回值(见13.4.1)。函数声明的形式参数要么在括号中(如ANSI C),要么在声明和方向中,如下所示:
function logic [15:0] myfunc1(int x, int y);...
endfunctionfunction logic [15:0] myfunc2;input int x;input int y;...
endfunction
函数可以具有与任务相同的形式参数。函数参数方向如下:
input // copy value in at beginning
output // copy value out at end
inout // copy in at beginning and out at end
ref // pass reference (see 13.5.2)
如果未指定方向,则函数声明默认为形式方向输入。一旦指定了方向,后续形式将默认为相同的方向。在以下示例中,形式参数a和b默认为输入,u和v都是输出:
function logic [15:0] myfunc3(int a, int b, output logic [15:0] u, v);...
endfunction
每个形式参数都有一个数据类型,可以显式声明或从上一个参数继承。如果数据类型没有显式声明,那么如果它是第一个参数或显式指定了参数方向,则默认数据类型为logic。否则,数据类型将继承自上一个参数。数组可以指定为函数的形式参数,例如:
function [3:0][7:0] myfunc4(input [3:0][7:0] a, b[3:0]);...
endfunction
在事件表达式、过程连续赋值中的表达式或不在过程语句中的表达式中调用具有output、inout或ref参数的函数是非法的。但是,在这种情况下,const-ref函数参数是合法的(见13.5.2)。
函数头和endfunction之间可以编写多条语句。语句是按顺序执行的,就好像它们被包含在一个begin—end组中一样。完全没有语句也是合法的,在这种情况下,函数返回与函数同名的隐式变量的当前值。
13.4.1 Return values and void functions
函数定义应隐式声明函数内部的变量,该变量与函数名称相同。此变量的类型与函数返回值的类型相同。函数返回值可以通过两种方式指定,一种是使用return语句,另一种是为与函数同名的内部变量赋值。例如:
function [15:0] myfunc1 (input [7:0] x,y);myfunc1 = x * y - 1; // return value assigned to function name
endfunctionfunction [15:0] myfunc2 (input [7:0] x,y);return x * y - 1; //return value is specified using return statement
endfunction
return语句应覆盖分配给函数名称的任何值。使用return语句时,非void函数应指定带有return的表达式。函数返回可以是结构或联合。在这种情况下,函数内部使用的以函数名称开头的层次结构名称被解释为返回值的成员。如果函数名称在函数之外使用,则该名称表示整个函数的范围。如果函数名称在层次结构名称中使用,它还指示整个函数的范围。函数可以声明为类型void,该类型没有返回值。函数调用可以用作表达式,除非类型为void,否则为语句:
a = b + myfunc1(c, d); // call myfunc1 (defined above) as an expression
myprint(a); // call myprint (defined below) as a statement
function void myprint (int a);...
endfunction
返回值的函数可以用于赋值或表达式。调用一个无返回值的非虚拟函数是合法的,但应该发出警告。函数可以用作语句,通过将函数调用强制转换为void类型,返回值将被丢弃而不会发出警告。
void'(some_function());
在声明或显式导入函数的范围内声明与该函数同名的另一个对象是非法的。在函数范围内声明与函数同名的另一个对象也是非法的。
13.4.2 Static and automatic functions
在模块、接口、程序或包中定义的函数默认为静态的,所有声明的项都是静态分配的。这些项目应在同时执行的函数的所有使用中共享。可以通过以下两种方式定义使用自动存储的功能:
--使用可选的automatic关键字作为函数声明的一部分显式声明。
--通过在定义为automatic的模块、接口、程序或包中定义函数来隐含声明
类中定义的函数总是automatic的(见8.6)。一个automatic函数是可重入的,所有的函数声明都是为每个并发函数调用动态分配的。分层引用无法访问automatic函数项。automatic函数可以通过使用其层次结构名称来调用。特定的局部变量可以在静态函数中声明为automatic变量,也可以在automatic函数中声明为静态变量。
以下示例定义了一个名为factorial的函数,该函数返回一个整数值。阶乘函数被迭代调用,结果被打印出来。
module tryfact;
// define the functionfunction automatic integer factorial (input [31:0] operand);if (operand >= 2)factorial = factorial (operand - 1) * operand;elsefactorial = 1;endfunction: factorial
// test the functioninteger result;initial beginfor (int n = 0; n <= 7; n++) beginresult = factorial(n);$display("%0d factorial=%0d", n, result);endend
endmodule: tryfact
仿真结果如下:
0 factorial=1
1 factorial=1
2 factorial=2
3 factorial=6
4 factorial=24
5 factorial=120
6 factorial=720
7 factorial=5040
13.4.3 Constant functions
常数函数是应满足以下约束的正规函数的子集:
--常量函数不应具有output、inout或ref参数。
--void 函数不应为常数函数。
--DPI导入函数(见35.2.1)不应为常量函数。
--常量函数不应包含直接安排事件在函数返回后执行的语句。
--常量函数不应包含任何fork构造。
--常量函数不应包含层次引用。
--常量函数中调用的任何函数都应是当前模块本地的常量函数。
--调用constant_expression中允许的任何系统函数都是合法的(见11.2.1)。这包括$bits和数组查询函数。对其他系统函数的调用是非法的。
--应忽略常量函数中的所有系统任务调用。
--函数中使用的所有参数值应在使用调用常量函数调用之前定义(即,在评估常量函数调用时使用的任何参数都构成在原始常量函数调用位置使用该参数)。常量函数可以引用包或$unit中定义的参数。
--所有非参数或函数的标识符应在本地声明为当前函数。
--如果常量函数使用任何直接或间接受defparam语句影响的参数值(见23.10.1),则结果应为未定义。这可能会产生错误,或者常量函数可能返回不确定的值。
--常量函数不应在生成块内声明(见第27条)。
--常量函数本身不应在任何需要常量表达式的上下文中使用常量函数。
--常量函数可能具有默认参数值(见13.5.3),但任何此类默认参数值都应为常量表达式。常量函数调用用于支持在elaboration时构建复杂的值计算(见3.12)。
常量函数调用是调用模块本地的常量函数的函数调用,或者是来自包或$unit的常量函数调用,其中函数的参数都是常量表达式(见11.2.1)。
常量函数调用在elaboration时进行评估。它们的执行对仿真时使用的变量的初始值没有影响,也不会对elaboration时函数的多次调用产生影响。在每种情况下,变量都会像正常仿真一样进行初始化。
以下示例定义了一个名为clogb2的函数,该函数返回一个整数,该整数的值为对数基数2的上限值。
module ram_model (address, write, chip_select, data);parameter data_width = 8;parameter ram_depth = 256;localparam addr_width = clogb2(ram_depth);input [addr_width - 1:0] address;input write, chip_select;inout [data_width - 1:0] data;//define the clogb2 functionfunction integer clogb2 (input [31:0] value);value = value - 1;for (clogb2 = 0; value > 0; clogb2 = clogb2 + 1)value = value >> 1;endfunctionlogic [data_width - 1:0] data_store[0:ram_depth - 1];//the rest of the ram model
endmodule: ram_model
此ram_model的一个实例(已分配参数)如下所示:
ram_model #(32,421) ram_a0(a_addr,a_wr,a_cs,a_data);
13.4.4 Background processes spawned by function calls(函数调用产生的后台进程)
函数应立即执行。因此,调用函数的进程应立即返回。非阻塞的语句应允许在函数内部使用;具体地说,非阻塞分配、事件触发器、时钟驱动器和fork-join_none构造应该被允许在函数内部。
如果调用函数的线程是由initial进程、always 进程或其中一个进程的fork块创建的,并且在允许副作用的上下文中,则应允许调用试图调度事件的函数,该事件在该函数返回之后才能变为活动。
当不满足这些规定时,实现应在编译时或运行时发出错误。在函数中,fork-join_none构造可以包含任务中合法的任何语句。函数中合法和非法使用fork-join_none的示例如下:
class IntClass;int a;
endclassIntClass address=new(), stack=new();function automatic bit watch_for_zero( IntClass p );forkforever @p.a beginif ( p.a == 0 ) $display (“Unexpected zero”);endjoin_nonereturn ( p.a == 0 );endfunctionfunction bit start_check();return ( watch_for_zero( address ) | watch_for_zero( stack ) );
endfunctionbit y = watch_for_zero( stack ); // illegalinitial if ( start_check() ) $display ( “OK”); // legalinitial forkif (start_check() ) $display( “OK too”); // legaljoin_none
13.5 Subroutine calls and argument passing
任务和void函数被称为过程块中的语句(见9.2)。非虚拟函数调用可以是表达式中的操作数。将子程序作为语句调用的语法如语法13-3所示:
subroutine_call_statement ::= // from A.6.9subroutine_call ;| void ' ( function_subroutine_call ) ;
subroutine_call ::= // from A.8.2tf_call| system_tf_call| method_call| [ std :: ] randomize_call
tf_call37 ::= ps_or_hierarchical_tf_identifier { attribute_instance } [ ( list_of_arguments ) ]
list_of_arguments ::=[ expression ] { , [ expression ] } { , . identifier ( [ expression ] ) }| . identifier ( [ expression ] ) { , . identifier ( [ expression ] ) }
ps_or_hierarchical_tf_identifier ::= // from A.9.3[ package_scope ] tf_identifier| hierarchical_tf_identifier
Syntax 13-3—Task or function call syntax (excerpt from Annex A)
除非子例程是任务、void函数或类方法,否则在tf_call中省略括号是非法的。如果子例程是非void类函数方法,则如果调用是直接递归的,则省略括号是非法的。
如果子例程中的参数被声明为input,那么子例程调用中对应的表达式可以是任何表达式。参数列表中表达式的求值顺序未定义。
如果子程序中的自变量被声明为output或inout,则子程序调用中的相应表达式应限制为过程赋值左侧有效的表达式(见10.4)。
子程序调用的执行应传递调用参数中列出的表达式的输入值。子程序返回的执行应将output和inout类型参数的值传递给子程序调用中的相应变量。
SystemVerilog提供了两种向任务和函数传递参数的方法:通过值和通过引用。参数也可以通过名称和位置进行绑定。子例程参数也可以被赋予默认值,从而允许对子例程的调用不传递参数。
13.5.1 Pass by value
值传递是将参数传递给子例程的默认机制。这种参数传递机制通过将每个参数复制到子例程区域来工作。如果子例程是automatic的,则子例程在其堆栈中保留参数的本地副本。如果参数在子例程内发生了更改,则这些更改在子例程外不可见。当参数较大时,可能不希望复制这些参数。此外,程序有时需要共享一段未声明为全局的公共数据。
例如,每次调用以下函数都会复制1000个字节。
function automatic int crc( byte packet [1000:1] );for( int j= 1; j <= 1000; j++ ) begincrc ^= packet[j];end
endfunction
13.5.2 Pass by reference
通过引用传递的参数不会复制到子例程区域,而是将对原始参数的引用传递到子例程。然后,子例程可以通过引用访问参数数据。通过引用传递的参数应与等效的数据类型匹配(见6.22.2)。不允许转换。为了指示参数通过引用传递,参数声明前面有ref关键字。对于生命周期为静态的子例程,使用通过引用传递的参数是非法的。一般语法如下:
subroutine( ref type argument );
例如,前面的例子可以写如下:
function automatic int crc( ref byte packet [1000:1] );for( int j= 1; j <= 1000; j++ ) begincrc ^= packet[j];end
endfunction
如前面的示例所示,除了添加ref关键字之外,不需要进行任何更改。编译器知道数据包现在是通过引用来寻址的,但用户不需要在被调用者或调用点显式地进行这些引用。换句话说,对crc函数的任一版本的调用保持不变:
byte packet1[1000:1];
int k = crc( packet1 ); // pass by value or by reference: call is the same
当参数通过引用传递时,调用方和子例程共享参数的相同表示形式;因此,在调用程序或子例程中对参数所做的任何更改都应该是彼此可见的。通过引用传递的变量赋值的语义是,在子程序返回之前,可以立即在子程序外部看到更改。只有以下引用传递参数是合法的:
-- 一个变量;
--一个类的属性;
-- 一个非打包结构的成员(unpacked structure);
--一个非打包数组的元素(unpacked array);
不得通过引用传递net变量和net变量的部分选择。由于引用传递的变量可能是automatic变量,因此在任何禁止automatic变量使用的上下文中都不应使用ref参数。
通过引用传递的动态数组、队列和关联数组的元素可能会从数组中删除,或者在调用的子例程完成之前调整数组的大小。通过引用传递的特定数组元素应继续存在于被调用子程序的范围内,直到它们完成为止。如果在进行更改之前从数组中删除了数组元素,则被调用的子例程对数组元素值所做的更改在这些子例程的范围之外不可见。
这些引用应称为过时引用。对可变大小数组的以下操作将导致对该数组元素的现有引用变为过时引用:
—使用隐式或显式new[]调整动态数组的大小。
—使用delete()方法删除动态数组。
—正在引用的关联数组的元素将使用delete()方法删除。
—包含引用元素的队列或动态数组通过赋值进行更新。
—正在引用的队列的元素由队列方法删除。
通过引用传递参数是一个唯一的参数传递限定符,不同于input、output或inout。将ref与任何其他方向限定符组合在一起是非法的。例如,以下声明会导致编译器错误:
task automatic incr( ref input int a );// incorrect: ref cannot be qualified
ref参数类似于inout参数,不同之处在于inout参数被复制两次:当调用子例程时,从实际值到参数一次,当子例程返回时,从参数到实际值一次。传递对象句柄也不例外,并且在作为ref或inout参数传递时具有类似的语义。因此,除了修改对象的内容外,对象句柄的ref还允许更改对象句柄(例如,分配新对象)。
为了保护通过引用传递的参数不被子例程修改,const限定符可以与ref一起使用,以表明该参数虽然通过引用传递,但却是只读变量。
task automatic show ( const ref byte data [] );for ( int j = 0; j < data.size ; j++ )$display( data[j] ); // data can be read but not written
endtask
当形式参数被声明为const ref时,子例程无法更改变量,尝试这样做会产生编译器错误。
13.5.3 Default argument values
为了处理常见情况或允许未使用的参数,SystemVerilog允许子例程声明为单个参数指定默认值。在子例程中声明默认参数的语法如下:
subroutine( [ direction ] [ type ] argument = default_expression);
可选方向可以是input、inout、output或ref。
每次使用默认值进行调用时,都会在包含子例程声明的作用域中计算default_expression。如果未使用默认值,则不会计算默认表达式。只有ANSI样式声明才允许使用默认值。
当调用子例程时,可以从调用中省略具有默认值的参数,编译器应插入它们相应的值。未指定(或空)的参数可以用作默认参数的占位符。如果未指定的参数用于没有默认值的参数,则应发出编译器错误。
task read(int j = 0, int k, int data = 1 );...
endtask
本例声明了一个带有两个默认参数j和data的任务read()。然后可以使用各种默认参数调用该任务:
read( , 5 ); // is equivalent to read( 0, 5, 1 );
read( 2, 5 ); // is equivalent to read( 2, 5, 1 );
read( , 5, ); // is equivalent to read( 0, 5, 1 );
read( , 5, 7 ); // is equivalent to read( 0, 5, 7 );
read( 1, 5, 2 ); // is equivalent to read( 1, 5, 2 );
read( ); // error; k has no default value
read( 1, , 7 ); // error; k has no default value
以下示例显示了具有默认表达式的输出参数:
module m;
logic a, w;task t1 (output o = a) ; // default binds to m.a...endtask :t1task t2 (output o = b) ; // illegal, b cannot be resolved...endtask :t2task t3 (inout io = w) ; // default binds to m.w...endtask :t1endmodule :mmodule n;logic a;initial beginm.t1(); // same as m.t1(m.a), not m.t1(n.a);// at end of task, value of t1.o is copied to m.am.t3(); // same as m.t3(m.w)// value of m.w is copied to t3.io at start of task and// value of t3.io is copied to m.w at end of taskend
endmodule :n
13.5.4 Argument binding by name
SystemVerilog允许通过名称和位置绑定任务和函数的参数。这允许指定非连续的默认参数,并轻松指定要在调用时传递的参数。
例如:
function int fun( int j = 1, string s = "no" );...
endfunction
fun函数可以调用如下:
fun( .j(2), .s("yes") ); // fun( 2, "yes" );
fun( .s("yes") ); // fun( 1, "yes" );
fun( , "yes" ); // fun( 1, "yes" );
fun( .j(2) ); // fun( 2, "no" );
fun( .s("yes"), .j(2) ); // fun( 2, "yes" );
fun( .s(), .j() ); // fun( 1, "no" );
fun( 2 ); // fun( 2, "no" );
fun( ); // fun( 1, "no" );
如果参数具有默认值,则将它们视为模块实例的参数。如果参数没有默认值,则应给出它们,或者编译器应发出错误。如果在单个子例程调用中同时指定了位置参数和命名参数,则所有位置参数都应位于命名参数之前。然后,使用与上面相同的示例:
fun( .s("yes"), 2 ); // illegal
fun( 2, .s("yes") ); // OK
13.5.5 Optional argument list
当void函数或类函数方法没有指定参数时,子例程名称后面的空括号()应是可选的。当所有参数都指定了默认值时,对于需要参数的任务、void函数和类方法也是如此。在没有层次限定的直接递归非虚拟类函数方法调用中省略括号是非法的。
13.6 Import and export functions
SystemVerilog提供了一个直接编程接口(DPI),允许将foreign language子程序(如C函数)导入SystemVerilog。导入的子例程的调用方式与SystemVerilog子例程相同。SystemVerilog任务和函数也可以导出为foreign language。有关DPI的详细信息,请参见第35条。
13.7 Task and function names
任务和函数名称的解析规则与其他引用略有不同。即使用作简单名称,任务或函数名称也遵循向上分层名称解析规则的修改形式。这意味着可以解析对稍后在同一范围或封闭范围中定义的任务或函数的“转发”引用。有关管理任务和函数名称解析的规则,请参见23.8.1。
13.8 Parameterized tasks and functions
SystemVerilog提供了一种创建参数化任务和函数的方法,也称为参数化子例程。参数化子例程允许用户一般性地指定或定义实现。当使用该子程序时,可以提供完全定义其行为的参数。这只允许编写和维护一个定义,而不是使用不同数组大小、数据类型和可变宽度的多个子例程。
实现参数化子例程的方法是通过在参数化类中使用静态方法(见8.10和8.25)。下面的通用编码器和解码器示例显示了如何使用静态类方法以及类参数化来实现参数化子例程。该示例有一个类,其中包含两个子例程,在本例中,这两个子例程共享参数化。该类可以被声明为虚拟的,以防止对象构造并强制该方法的严格静态使用。
virtual class C#(parameter DECODE_W, parameter ENCODE_W = $clog2(DECODE_W));static function logic [ENCODE_W-1:0] ENCODER_f(input logic [DECODE_W-1:0] DecodeIn);ENCODER_f = '0;for (int i=0; i<DECODE_W; i++) beginif(DecodeIn[i]) beginENCODER_f = i[ENCODE_W-1:0];break;endendendfunctionstatic function logic [DECODE_W-1:0] DECODER_f(input logic [ENCODE_W-1:0] EncodeIn);DECODER_f = '0;DECODER_f[EncodeIn] = 1'b1;endfunction
endclass
类C包含两个静态子例程,ENCODER_f和DECODER_f。每个子程序都是通过重用类参数DECODE_W和ENCODE_W来参数化的。参数ENCODE_W的默认值通过使用系统函数$clog2确定(见20.8.1)。这些参数在子程序中用于指定编码器的大小和解码器的大小。
module top ();
logic [7:0] encoder_in;
logic [2:0] encoder_out;
logic [1:0] decoder_in;
logic [3:0] decoder_out;
// Encoder and Decoder Input Assignmentsassign encoder_in = 8'b0100_0000;assign decoder_in = 2'b11;// Encoder and Decoder Function callsassign encoder_out = C#(8)::ENCODER_f(encoder_in);assign decoder_out = C#(4)::DECODER_f(decoder_in);initial begin#50;$display("Encoder input = %b Encoder output = %b\n",encoder_in, encoder_out );$display("Decoder input = %b Decoder output = %b\n",decoder_in, decoder_out );end
endmodule
顶层模块首先定义本例中使用的一些中间变量,然后将常数值分配给编码器和解码器输入。通用编码器的子程序调用ENCODER_f使用表示编码器的特定实例的解码器宽度值的专用类参数值8,同时传递输入编码值ENCODER_in。此表达式使用静态类作用域解析运算符::(请参见8.23)来访问编码器子例程。表达式被分配给一个输出变量,以保存运算的结果。通用解码器的子程序调用decoder_f类似,使用参数值4。
相关文章:
IEEE SystemVerilog Chapter13 : Tasks and functions (subroutines)
13.2 Overview 任务和函数提供了从描述中的几个不同位置执行通用过程的能力。它们还提供了一种将大型过程分解为小型过程的方法,以便更容易地阅读和调试源代码描述。本小节讨论了任务和函数之间的区别,描述了如何定义和调用任务和函数,并给出…...

day39反转字符串总结
反转字符串原理其实就是交换位置,以中间为分隔点; 基本套路:遍历前一般字符,互换位置; for循环模板 void reverseString(char* s, int sSize){char temp;for (int i 0, j sSize - 1; i < sSize/2; i, j--) {temp…...

使用Socket实现TCP版的回显服务器
文章目录 1. Socket简介2. ServerSocket3. Socket4. 服务器端代码5. 客户端代码 1. Socket简介 Socket(Java套接字)是Java编程语言提供的一组类和接口,用于实现网络通信。它基于Socket编程接口,提供了一种简单而强大的方式来实现…...

【Nacos篇】Nacos基本操作及配置
官方文档:https://nacos.io/zh-cn/docs/v2/ecology/use-nacos-with-spring-cloud.html 前置条件:SpringCloud脚手架 单机模式下的Nacos控制台: <dependencies><!-- Registry 注册中心相关 --><dependency><groupId>…...

Dockerfile构建Tomcat镜像
准备apache包和jdk并解压 [rootlocalhost tomcat]# ll 总用量 196728 -rw-r--r--. 1 root root 9690027 7月 17 2020 apache-tomcat-8.5.40.tar.gz -rw-r--r--. 1 root root 674 8月 2 20:19 Dockerfile -rw-r--r--. 1 root root 191753373 7月 17 2020 jdk-8u191-…...
k8s的介绍
简介 Kubernetes,简称K8s,是用8代替名字中间的8个字符“ubernete”而成的缩写。是一个开源的,用于管理云平台中多个主机上的容器化的应用, K8s的目标是让部署容器化的应用简单并且高效,K8s提供了应用部署,规划,更新,维护的一种机制。 K8s是Google开源的一个容器编排引…...
mysql sql语句 需要使用like 场景,解决方案
mysql 多重like 解决方案 方案一、使用like 方案二、使用REGEXP 正则匹配 方案三、使用group_concat多重模糊匹配 方案一、使用like 查询user包含小李并且小王的相关数据 SELECT * FROM user WHERE name LIKE %小王% or name like %小王% 方案二、使用REGEXP 正则匹配 查询use…...

通过C语言设计的贪吃蛇游戏(控制台终端)
一、项目介绍 当前通过控制台终端实现一个贪吃蛇小游戏,实现游戏的绘制、更新、控制等功能。 二、实现效果 三、完整代码 下面贴出的代码在Windows系统上编译运行,需要使用conio.h头文件中的getch()函数来获取键盘输入,用于控制蛇的移动。…...

c++实现Qt信号和槽机制
文章目录 简介信号槽信号与槽的连接 特点观察者模式定义观察者模式结构图 实现简单的信号和槽 简介 信号槽机制与Windows下消息机制类似,消息机制是基于回调函数,Qt中用信号与槽来代替函数指针,使程序更安全简洁。 信号和槽机制是 Qt 的核心…...

【Linux】五、进程
一、冯诺依曼体系结构 存储器:指的是内存; 输入设备:键盘、摄像头、话筒,磁盘,网卡; 输出设备:显示器、音响、磁盘、网卡; 中央处理器(CPU):运算器…...

使用 OpenCV 和 Python 卡通化图像-附源码
介绍 在本文中,我们将构建一个有趣的应用程序,它将卡通化提供给它的图像。为了构建这个卡通化器应用程序,我们将使用 python 和 OpenCV。这是机器学习令人兴奋的应用之一。在构建此应用程序时,我们还将了解如何使用 easygui、Tkinter 等库。在这里,您必须选择图像,然后应…...
GitLab不同角色对应的权限
Owner(拥有者): 拥有者是项目或组的创建者,拥有最高级别的权限。他们可以添加、删除项目成员,修改项目设置,管理访问权限,并进行项目转让。在组级别,他们还可以添加或删除子组和项目…...

手写一个简易的布隆过滤器
1.什么是布隆过滤器 布隆过滤器(Bloom Filter)是1970年由布隆(人名)提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,…...

阿里云快速部署开发环境 (Apache + Mysql8.0)
本文章的内容截取于云服务器管理控制台提供的安装步骤,再整合前人思路而成,文章末端会提供原文连接 ApacheMysql 8.0部署MySQL数据库(Linux)步骤一:安装MySQL步骤二:配置MySQL步骤三:远程访问My…...

侧边栏的打开与收起
侧边栏的打开与收起 <template><div class"box"><div class"sideBar" :class"showBox ? : controller-box-hide"><div class"showBnt" click"showBox!showBox"><i class"el-icon-arrow-r…...

贝叶斯学习
贝叶斯 贝叶斯学习的背景贝叶斯定理举例 概览选择假设— MAPMAP举例 选择假设 — 极大似然 MLML 举例: 抛硬币问题 极大似然 & 最小二乘Nave Bayesian Classifier (朴素贝叶斯分类器)举例1:词义消歧 (Word Sense Disambiguation)举例 2: 垃圾邮件过滤 从垃圾邮件…...

Java并发系列之六:CountDownLatch
CountDownLatch作为开发中最常用的组件,今天我们来聊聊它的作用以及内部构造。 首先尝试用一句话对CountDownLatch进行概括: CountDownLatch基于AQS,它实现了闩锁,在开发中可以将其用作任务计数器。 若想要较为系统地去理解这些特性ÿ…...

24数据结构-图的基本概念与存储结构
目录 第六章 图6.1 图的基本概念知识回顾 6.2 图的储存结构(邻接矩阵法)1. 数组表示法(1) 有向图,无向图的邻接矩阵 2. 定义邻接矩阵的结构3. 定义图的结构4. 构造图G5. 特点 第六章 图 6.1 图的基本概念 图是一种非线性结构 图的特点&am…...

自然语言处理学习笔记(三)————HanLP安装与使用
目录 1.HanLP安装 2.HanLP使用 (1)预下载 (2)测试 (3)命令行 (4)测试样例 3.pyhanlp可视化 4. HanLP词性表 1.HanLP安装 HanLP的 Python接口由 pyhanlp包提供,其安装…...

CS 144 Lab Five -- the network interface
CS 144 Lab Five -- the network interface TCP报文的数据传输方式地址解析协议 ARPARP攻击科普 Network Interface 具体实现测试tcp_ip_ethernet.ccTCPOverIPv4OverEthernetAdapterTCPOverIPv4OverEthernetSpongeSocket通信过程 对应课程视频: 【计算机网络】 斯坦福大学CS144…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...