task(任务)和function(函数)的学习

前言:如果程序中有一段语句需要执行多次,则重复性的语句非常多,代码会变得冗长且难懂,维护难度也很大。通过将很大的模块分解为许多小的任务和函数,任务和函数具备将重复性语句聚合起来的能力,可以在程序模块中多次调。通常任务和函数来代替重复性语句,也有效简化程序结构,增加代码的可读性。因此,task和function都是可综合的,不过综合出来的都是组合逻辑电路。

task

  • 任务就是封装在task-endtask之间的一段语句
  • 任务是通过调用来执行的,也只能通过调用来执行,如果定义了任务,但整个过程都没有调用它,它是不会执行的
  • 调用任务时可能需要它来处理某些数据并返回操作结果,所以任务应当有接受数据的输入端和返回数据的输出端
  • 任务可以彼此调用,任务还可以调用函数,可以启动的任务数是没有限制的。不管有多少任务启动,只有当所有的启动任务完成以后,控制才能返回
  • 任务内部有定时控制,则启动的时间可以与控制返回的时间不同

task定义

定义任务的语法如下:

task <任务名>;
<端口及数据类型声明语句>
<语句1>
<语句2>

<语句n>
endtask

在定义任务时,有下列六点需要注意:

1、在第一行task语句中不能列出端口名称。

2、任务的输入、输出和双向端口数量不受限制,甚至可以没有输入、输出和双向端口。

3、在任务定义的描述语句中,可以出现不可综合操作符合语句,但这样会造成任务不可综合。

4、在任务中可以调用其他的任务或函数,也可以调用自身。

5、在任务定义结构中不可出现initial和always语句。

6、在任务定义中可以出现“disable中止语句“,将中断正在执行的任务,但其是不可综合的。当任务被中断后,程序流程将返回调用任务的地方继续执行。

task定义的举例1:

 task my_task;
    	input a, b;   //输入
    	inout c;      //双向
    	output d, e;  //输出
   		 …
    	<语句>        //执行任务工作相应的语句
    	…
    	c = foo1;     //赋初始值
    	d = foo2;     //对任务的输出变量赋值t
    	e = foo3;
endtask

task定义的举例2:

task light;
	input[31:0] tics;   //输入
 	output color;       //输出

 	begin
  		repeat(tics) @(posedge clock);  //等待tics个时钟的上升沿
  		color=off;                      //关灯
 	end
endtask

task调用

<任务名>(端口1,端口2,…,端口n);

在调用task时,必须要注意一下几点:

1、task的调用是过程性语句,因此只能出现在always过程块和initial过程块中,调用task的输出与输入参数必须是reg类型的

2、task调用语句中的列表必须与task定于时的输入,输出,双向端口参数说明的顺序相匹配

3、在调用task时,参数按照值传递,而不能按照地址传递

4、在一个task中,可也直接访问上一级调用模块中的任何寄存器

5、可以使用循环中断控制语句disable来终端任务执行,当task被中断后,程序流程将返回调用任务的地方继续执行。

下面的例子说明定义的任务如何调用:

task 1 的调用:

my_task(v,w,x,y,z);

任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)之间是一一对应的。当任务启动时,由v,w,和x.传入的变量赋给了a,b,和c,而当任务完成后的输出又通过c,d和e赋给了x,y和z。

下面是一个具体的例子用来说明怎样在模块的设计中使用任务,使程序容易读懂,也就是task 2 的调用:

module traffic_lights;
    reg clock, red, amber, green;
    parameter on=1, off=0, red_tics=350,
              amber_tics=30,green_tics=200;
     //交通灯初始化
     initial red=off;
     initial amber=off;
     initial green=off;
     //交通灯控制时序
    always
    begin
        red=on; //开红灯
        light(red,red_tics); //调用等待任务
        green=on; //开绿灯
        light(green,green_tics); //等待
        amber=on; //开黄灯
        light(amber,amber_tics); //等待
    end

    //定义交通灯开启时间的任务
    task light(color,tics);
        output color;
        input[31:0] tics;
        begin
            repeat(tics) @(posedge clock);//等待tics个时钟的上升沿
            color=off;//关灯
        end
    endtask
//产生时钟脉冲的always块
    always
    begin
        #100 clock=0;
        #100 clock=1;
    end
endmodule

function

  • 函数定义是嵌入在关键字function和endfunction之间的,其中关键词function标志着一个函数定义结构的开端,endfunction标志着一个函数定义结构的结束。
  • “<函数名>”是给被定义函数取的名称。这个函数名在函数定义结构内部还代表着一个内部变量函数调用后的返回值是通过这个函数名变量传递给调用语句的。
  • 函数的目的是返回一个用于表达式的值。

function定义

function <返回值的类型或范围> (函数名);
<输入端口说明语句>
<变量类型说明语句>
begin
<语句>

end
endfunction

在定义function时要注意:

(1) function定义结构不能出现在任何一个always块或者initial过程块中,与task定义相同

(2) 不允许输出output端口声明(包括输出和双向端口) ,但可以有多个input输入端口

(3) [range]参数指定函数返回值的类型或位宽,是一个可选项,若没有指定,默认缺省值为宽度 1 bit的寄存器数据

(4) function_name为所定义函数的名称,对函数的调用也是通过函数名完成的,并在函数结构体内部代表一个内部变量,函数调用的返回值就是通过函数名变量传递给调用语句。函数定义在函数内部会隐式定义一个寄存器变量,该寄存器变量和函数同名并且位宽也一致,函数通过在函数定义中对该寄存器的显式赋值来返回函数计算结果。

(5) input_declaration为各个输入端口的位宽和类型进行说明,在函数定义中至少要有一个输入端口

(6) function定义不能包括有任何时间控制语句,即任何#,@或者wait来标识的语句,和task不同

function定义举例1

其中getbyte所赋予的值就是函数的返回值

function [7:0] getbyte;
	input [15:0] address;
	begin
    	<说明语句> //从地址字中提取低字节的程序
    	getbyte = result_expression; //把结果赋予函数的返回字节
	end
endfunction

function定义举例2

下面的例子中定义了一个可进行阶乘运算的名为factorial的函数,该函数返回一个32位的寄存器类型的值,该函数可后向调用自身,并且打印出部分结果值。

function [31:0] factorial;
	input [3:0] operand;
 	reg [3:0] index;
 	begin
  		factorial = operand ? 1 : 0;  //1的阶乘为1,2的阶乘为2*1,3的阶乘为3*2*1
  		for(index = 2; index <= operand; index = index + 1)
  			factorial = index * factorial;
	 end
endfunction

function定义举例3

例:统计输入数据中“0”的个数

	function[3:0] out0;
		input[7:0] x;
		reg[3:0] count;
		integer i;
		begin
			count=0;
			for(i=0;i<=7;i=i+1) begin
				if(x[i]==1’b0)	 
					count = count+1;
				else
					count = count;
			end
			out0=count;
	end
	endfunction

function调用

函数的调用是通过将函数作为表达式中的操作数来实现的。

其调用格式如下:

<函数名> (<表达式><,<表达式>>*)

在调用function时,要注意一下几点:

1、 函数的调用可以在过程块中完成,也可以在assign这样的连续赋值语句中出现,这与task有些许不同

2、函数调用语句不能单独作为一条语句出现,只能作为赋值语句的右端操作数

3 、函数可以调用函数,但是不能调用任务

4、函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值。

调用实例1:

下面的例子中通过对两次调用函数getbyte的结果值进行位拼接运算来生成一个字。

assign word = control? {getbyte(msbyte),getbyte(lsbyte)} : 0;

调用实例2:

module tryfact;
 //函数定义------------------------------
	function [31:0] factorial;
		input [3:0] operand;
 		reg [3:0] index;
 		begin
  		factorial = operand ? 1 : 0;
  		for(index = 2; index <= operand; index = index + 1)
  			factorial = index * factorial;
 		end
	endfunction
	//函数的测试----------------------------------
	reg [31:0] result;
 	reg [3:0] n;
 	
	initial begin
 		result = 1;
 		//  0的阶乘为1, 1的阶乘也为1
 		for (n = 2; n < 9; n = n + 1) begin
 			result = factorial(n);
			$display("正整数 n= %d的阶乘为 result= %d", n, result);
 		end
		$display ("Finalresult = %d",result);
	end
endmodule//模块结束

仿真结果:
在这里插入图片描述
其中阶乘自递归方式实现:

其中可以看到function的定义中添加了automatic的关键字。

在 Verilog 中,一般函数的局部变量是静态的,即函数的每次调用,函数的局部变量都会使用同一个存储空间。若某个函数在两个不同的地方同时并发的调用,那么两个函数调用行为同时对同一块地址进行操作,会导致不确定的函数结果。

Verilog 用关键字 automatic来对函数进行说明,此类函数在调用时是可以自动分配新的内存空间的,也可以理解为是可递归的。因此,automatic函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。

module tryfact;
 //函数定义------------------------------
	function automatic  integer  factorial;
		input [3:0] operand;   //4*8 = 32bit
 		begin
  		if(operand >= 2)
  			factorial = operand * factorial(operand - 1);
  		else 
  			factorial = 1
 		end
	endfunction
	//函数的测试----------------------------------
	integer result;
 	reg [3:0] n;
 	
	initial begin
 		result = 1;
 		//  0的阶乘为1, 1的阶乘也为1
 		for (n = 2; n < 9; n = n + 1) begin
 			result = factorial(n);
			$display("正整数 n= %d的阶乘为 result= %d", n, result);
 		end
	end
endmodule//模块结束

仿真结果:

在这里插入图片描述

task和function的区别

在这里插入图片描述

参考
https://zhuanlan.zhihu.com/p/72083112
https://blog.csdn.net/u010668547/article/details/80338477
https://blog.csdn.net/gsjthxy/article/details/103701716
https://blog.csdn.net/weixin_44502896/article/details/104911518
https://www.runoob.com/w3cnote/verilog-function.html

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值