五、子程序

子程序

systemveilog中函数和任务的使用方法如下:

  1. 一般情况下,不带参数的子程序在定义或调用时不需要带空括号();

  2. begin…end为可选的,因为task…endtask和function…endfunction关键词足以定义这些程序的边界,
    但是在verilog-1995中,除了单行以外的子程序是必须的;

  3. 可以采用简明的c语言风格,缺省的类型和方向是logic
    input;但是在verilog中,对参数要进行两次声明,一次是方向声明,一次是类型声明;

  4. task 和 function 的声明在module内部,且不需要先于调用之前。

//systemverilog风格
task test(a, b, output bit [15:0] c, d)//其中a,b为input logic a,b; c,d为output bit [15:0];
//verilog风格
task test2;
	output [31:0] x;
	reg 	[31:0] x;
	input y;
endtask
  1. 可以在参数列表中指定输入参数(input),输出参数(output)、输入输出参数(inout)或引用参数(ref);只有数组变量可以在形式参数列表中声明为ref类型,而线网类型不能声明为ref类型;在使用ref时,有时候为了保护数据对象不被写入,可以同通过const方式来限定ref声明的参数
    但是在verilog中,对参数的处理方式很简单,在子程序的开头把input或inout的值复制给本地变量,在子程序退出时,则复制output或inout的值。除了标量以外,没有任何把存储器传递给verilog子程序的方法;
  2. 数组可以作为形式参数传递;
  3. 可以为参数指定一个缺省值,如果在调用时不指明参数,则使用缺省值。
function void print_checksum(ref bit [31:0] a[], input bit [31:0] low, input int high = -1);
	bit [31:0] checksum = 0;
	if(high = -1 || high >= a.size())
		high = a.size() - 1;
	for(int i = low, i < high; i++)
		checksum += a[i];
	$display("The array checksum is %0d", checksum);
endfunction
print_checksum(a);			//a[0:size()-1]中所有元素的校验和---缺省情况
print_checksum(a, 2, 4);	//a[2:4]中所有元素的校验和
print_checksum(a, 1);		//a[1;$]中所有元素的校验和
print_checksum(a,,2)		//a[0:2]中所有元素的校验和
  1. 采用名字进行参数传递;
task many(input int a = 1, b = 2; c = 3, d = 4);
	$display("%0d %0d %0d %0d", a, b, c, d);
endtask
initial begin
	many(6, 7, 8, 9);	//a b c d的值分别为 6 7 8 9
	many();				//a b c d全部使用默认值
	many(.c(8));		//c的值为8,其他全部用默认值
	many(,6, , d(8));	// 1 6 3 8混合指定方式
end
  1. 需要注意的其他问题:
task sticky(ref int array[10], int a, b);

上述a和b参数类型会采用与前一个参数一致的ref类型,对简单的int变量使用ref并无必要,但编译器不会对此做出任何反应,可能连警告都没有,这是使用了一个错误的方向类型。
如果你在子程序中使用了非缺省输入类型的参数,应该明确指明所有参数的方向,如下所示:

task sticky(ref int array[10], input int a, b);

function

function的独特使用规则如下:
1、可以返回数值或不返回数值,如果返回需要使用关键词return,如果不返回,则应该声明函数为void function;
2、在systemveilog中,允许函数调用任务,但是只能在由fork…join_none语句生成的线程中调用。

void函数如下

function void print_state();
	$display("@%0t: time = %s", $time);
endfunction

4、从函数返回一个数组有两种方法:
第一中方法是定义一个数组类型,然后在函数的声明中使用该类型。

typedef int fixed_array5[5];
fixed_array5 f5;
function fixed_array5 init(int start);
	foreach(init[i])
		init[i] = i + start;
endfunction
initial begin
	f5 = init(5);
	foreach(f5[i[)
		$display("fd[%0d] = %0d", i, f5[i]);
end

上述代码存在的问题是,函数init创建了一个数组,给数组的值被复制到数组f5中。如果数组很大,那么可能会引起一个性能上的问题。
第二种方法是通过引用来进行数组参数的传递。

function void init(ref int f[5], input int start);
	foreach(f[i])
		f[i] = i + start;
endfunction
int fa[5];
initial begin
	init(fa, 5);
	foreach(fa[i]
		$display("fa[%0d] = %0d", i, fa[i]);
end

从函数中返回数组的最后一种方式是将数组包装到一个类中,然后返回对象的句柄。

task

task的独特使用规则如下:
1、task无法通过return返回结果,因此只能通过output, inout或者ref参数来返回;
2、任务中可以使用return,提前返回;
关于task和function使用的例题:

	typedef struct {
		bit [1:0] cmd;
		bit [7:0] addr;
		bit [31:0] data;
	} trans;
	 
	function automatic void cp_copy(trans s, trans t);
		t = s;
	endfunction
	 
	initial beign
		trans s, t;
		s.cmd = 'h1;
		s.addr = 'h10;
		s.data = 'h100;
		op_copy(t, s);
		t.cmd = 'h2;
	end
	//result
	t.cmd = 'h2;
	t.addr = 'h0;
	t.data = 'h0;

task的调用方法:

logic red;
parameter red_tics = 100;
always begin //控制灯光
    red = on; //打开红灯
    light(red,red_tics) ; //等待end
//等待‘tics’时钟上升沿的任务
//在关掉‘color’灯之前
task light (output color,input [31:0] tics) ;
    repeat (tics)@ (posedge clock);
    color = off;//关灯
endtask: light

区别

task:只能在procedural 内部调用,task的调用方式是statement本身,而不是表达式。
function:可以在module的任何地方调用,是作为一种operands来使用。

task
(1)可以有0个或者多个任何类型的输入输出端口。
(2)任务中可以有延时语句、敏感事件控制语句等事件控制语句。(repeat(5) @posedge(clk) 等待5个上升沿时钟)
(3)任务可以没有返回值,也可以通过输出端口或者双端输出端口有1个或多个返回值。
(4)任务可以调用任务和函数也能调用任务本身。
(5)任务中不能出现always和initial块。
(6)任务中允许出现disable中断任务,程序会回到引用任务的地方继续下去。

function有比较多的约束条件:
(1)函数能调用函数但是不能调用任务。
(2)函数只能返回一个值,不能有任意类型的output和inout类型端口。
(3)不能使用任何延时,即任何用#、@、或wait来标识的语句。
(4)至少有一个输入变量,可以有多个输出变量。
(5)函数一定不能包含任何延迟、事件或者时序控制声明语句。
function声明返回值时,如果不定义返回值类型或范围则默认是1位寄存器。
function和task内部的局部变量是放在寄存器中是静态变量,即多次调用不改变的

至于task和funciton能不能综合的问题

函数任务
函数能调用另一个函数,但不能调用另一个任务任务能调用另一个任务,也能调用另一个函数
函数总是在仿真时刻0就开始执行任务可以在非0仿真时刻执行
函数一定不能包含任何延迟、时间或者时序控制声明语句任务可以包含延时、时间或者时序控制声明语句
函数至少有一个输出变量,可以有多个输入变量任务可以没有或者有多个输入(input)、输出(output)和双向(inout)变量
函数只能返回一个值,函数不能有输出或者双向变量任务不返回值,任务可以通过输出或者双向变量传递多个值
函数不能单独作为一条语句出现,它只能以语句的一部分的形式出现任务调用只能出现在过程块中
函数调用可以出现在过程块或连续赋值语句中任务调用只能出现在过程块中
函数的执行不允许由disable语句进行中断任务的执行可以由disable语句进行中断
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值