上传了网站标志怎么弄,360建筑网上怎么删除投递信息,惠济区建设局网站,网站首页模版13.2 Overview 任务和函数提供了从描述中的几个不同位置执行通用过程的能力。它们还提供了一种将大型过程分解为小型过程的方法#xff0c;以便更容易地阅读和调试源代码描述。本小节讨论了任务和函数之间的区别#xff0c;描述了如何定义和调用任务和函数#xff0c;并给出…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默认为inputu和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.3Syntax 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 factorial1
1 factorial1
2 factorial2
3 factorial6
4 factorial24
5 factorial120
6 factorial720
7 factorial5040
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 addressnew(), stacknew();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 :n13.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) ); // OK13.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 i0; iDECODE_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] 1b1;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 8b0100_0000;assign decoder_in 2b11;// 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。