第6章 6-6任务和函数
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
endmodule 如果某一个任务有可能在程 序代码的两处被同时调用, 建议使用自动任务。
6.6.2 函数
函数与任务相比,其功能要弱一些,主要表现在 以下几点:
(1)在函数中可以嵌套调用函数,但不可以调用任务;而 任务既可以调用函数,也可以调用任务 (2)函数中不允许出现延时和事件控制语句,也就是说函 数必须马上执行完;而任务可以在执行过程中挂起 (3)函数至少需要一个参数,且参数必须都为输入端口,不 可以包含输出或者双向端口 (4)函数必须有一个返回值,返回值被赋给和函数名同名的 变量,这也决定了函数只能存在一个返回值
任务使用关键字task...endtask 进行申 明,如果子程序满足下面任一条件,必须使用 任务而不能使用函数:
•子程序中包含有延迟、时序或者事件控制结构。 •没有输出或者输出变量的数目大于1。 •没有输入变量。
task 任务名;
∥定义端口以及内部变量
input 输入端出端口,也可以没有
difference, //操作数1与操作数2的差 product, input [1:0] a,b; output [2:0] sum; output [1:0]difference; output [3:0]product;
output [3:0] result;
wire [1:0] a,b; reg [2:0] sum; reg [1:0]difference; reg [3:0]product;
采用ANSI C(美国国家协会)风格的变量声明进行任务定义
//define task bitwise_oper
task bitwise_oper( output [15:0] ab_and, ab_or, ab_xor, input [15:0] a,b );
begin #delay ab_and = a & b; ab_or = a | b; ab_xor = a ^ b;
endtask …
endmodule
自动(可重入)任务
任务在本质上时静态的,任务中的所有声明项的 地址空间是静态分配的,同时并发执行的多个任务 共享这些存储区。
如果在多个地方同时调用同一任务,则两个任务 将对同一块地址空间进行操作,结果可能出错 解决方法 自动任务:在task的关键字前添加automatic关键 字,使任务成为可重入的。 每次调用时,在动态任务中声明的所有模块项的 存储空间都是动态分配的,每个调用都对各自独立的 地址空间进行操作。
∥定义端口以及内部变量
函数名;
∥ DataWidth是返回值的位宽
input 输入端口名; ∥至少要有一个输入端口作为参数 wire 内部变量名; ∥定义内部变量 reg 内部变量名; ∥定义内部变量
begin … end endfunction
∥函数主体
函数具有的独特性质:
1、当函数声明的时候,在Verilog的内部隐含地声明了一个名 为function_identifier(函数标识符)的寄存器类型变量,函数的输 出结果将通过这个寄存器类型变量被传递回来。 2、函数的调用通过指明函数名和输入变量来进行。 3、在函数执行结束时,返回值被传回到调用处。 4、可选项range_or_type(类型或范围)说明了内部寄存器的 位宽。如果没有指定返回值的类型或位宽,则默认位宽为1。
2)任务或函数内定义的变量是局部变量,对外是不可见的。 3)在程序中使用任务和函数可以使程序结构更加清晰, 增加了程序的可读性和可维护性。
任务和函数在使用上的一些区别如表6-9
表6-9 函数和任务的对比
6.6.1 任务
Verilog HDL中的任务(task)与高级语言中的 过程类似,它不带返回值,因此不可以将它用 于表达式中,在调用时直接调用就可以了。尽 管任务不带有返回值,但任务的参数可以定义 为输出端口或者双向端口,因此实际上任务可 以返回多个值。
参数delay指定。
end
endtask
… endmodule
任务结束后,输出值被传递回调用任务 时使用的输出变量。因此在任务结束时, 仿真器会执行AB_AND=ab_and, AB_OR =ab_or, AB_XOR =ab_xor.
注意:任务调用语句是过程性语句,所以任务调用中接收返 回数据的变量必须是寄存器类型。
§ 6.6 任务和函数
本节重点:
Verilog HDL的行为建模中使用两种方式 任务(task)
掌握 •定义任务和函数所需要 的条件 •任务和函数的声明和 调用
函数(function)
§ 6.6 任务和函数
任务和函数的作用与功能模块相似,在设计 中使用任务和函数有3点好处:
1)在一个设计中有些行为可能会被多次调用。
[1:0] a; [1:0] b; [2:0] sum; [1:0]difference; [3:0]product; [3:0] result;
∥内部寄存器定义,在计算a^2-b的值时储存中间结果a^2
reg [3:0] tmp;
begin
∥执行运算
sum=a+b; difference=a-b; product=a*b; tmp=a*a; result=tmp-b; end
end
endtask
直接对寄存器变量进行操作
//定义包含名为asymmetric_sequence的任务的模块
module sequence; … reg clock;
… initial init_sequence;
在任务中可以直接对任 务所在模块中声明的寄 存器变量进行操作。
… always begin asymmetric_sequence; //启动asymmetric_sequence任务 end
…
//启动init_sequence任务来完成初始化
task init_sequence; //定义init_sequence(初始化序列)任务 begin clock=1’b0; end endtask //定义asymmetric_sequence(非对称序列发生器)任务 task asymmetric_sequence; begin #12 clock=1’b0; #5 clock=1’b1; //直接操作时钟(clock)信号 #3 clock=1’b0; #10 clock=1’b1; end
output 输出端口名;
inout wire reg begin end 双向端口名;
∥可以有一个或多个双向端口,也可以没有
内部变量名;
∥可以有一个或多个内部变量,也可以没有
内部变量名; …
∥任务主体
endtask;
在任务内部定义的变量,作用域是task与 endtask之间,它们对调用任务的模块不可见。
endtask
endmodule
例 任务中的输入和输出变量
//定义一个名为operation的模块,内部有一个名为 //bitwise_oper的任务
module operation; … parameter delay = 10; reg [15:0] A,B; reg [15:0] AB_AND, AB_OR, AB_XOR;
always @ (A or B ) ∥无论何时只要A或B的值发生改变 begin
∥启动 bitwise_oper任务,该任务提供两个输入变量A和B //有三个输出变量AB_AND, AB_OR, AB_XOR //变量的指定必须按照任务定义时的声明次序
bitwise_oper(AB_AND,AB_OR,AB_XOR,A,B); end; … 传递给任务的输入值 … 是A和B,因此在任务
注意:在函数声明中必须至少有一个输入声明,同时由于隐含
的寄存器变量function_identifier包含了函数的返回值,因此, 函数是没有输出变量的。另外在函数中不能调用任务,只能 调用其他函数。
举例1 【例6—14】 使用函数描述运算单元 module alu( a. ∥操作数1,位宽为2 b, //操作数2,位宽为2 sum, ∥操作数1与操作数2的和
例6.13使用任务描述了一个运算单元 module alu( a, b, sum, difference, product, result );
∥操作数1,位宽为2 ∥操作数2,位宽为2 //操作数1与操作数2的和 ∥操作数1与操作数2的差 ∥操作数1与操作数2的积 ∥四则运算a2-b的结果
input [1:0] a,b; output [2:0] sum; output [1:0] difference; output [3:0] product; output [3:0] result; wire [1:0] a,b; reg [2:0] sum; reg [1:0] difference; reg [3:0] product; reg [3:0] result; always @(a or b) begin
//操作数1与操作数2的积 result ∥四则运算a^2-b的结果 );
reg [3:0] result;
∥定义1 3位变量all_result,接收所有计算结果 reg [12:0] all_result; always @ (a or b) begin ∥注意调用函数的方法和任务不同 all_result=cal (a,b); //将函数作为表达式调用 sum=all_result[12:10]; ∥返回值的第12位到第10位为sum difference=all_result[9:8]; ∥返回值的第9位到第8位为difference product=all_result[7:4]; ∥返回值的第7位到第4位为product result=all_result[3:0]; // 返回值的第3位到第0位为result end
函数使用关键字function...endfunction 进 行申明,如果子程序满足下面所有条件,则可 以使用函数来完成:
•在子程序内不含有延迟、时序或者控制结构。 •子程序只有一个返回值。 •没有输出或者双向变量。 •至少有一个输入变量。
•不含有非阻塞赋值语句。
定义函数使用关键词function…endfunction 关键 词,其函数的定义式如下: function[DataWidth -1:0]
∥按照任务中定义的端口顺序调用任务
cal (a, b,sum,difference, product,result);
end
∥定义任务cal 输出变量的数目大于1,必须 使用任务而不能使用函数
task cal;
∥任务端口列表
input input output output output output
∥定义bitwise_oper任务
task bitwise_oper;
执行时首先将A赋予a, 将B赋予b。
output [15:0] ab_and, ab_or, ab_xor;
∥bitwise_oper任务的输出变量
input [15:0] a,b; ∥输入到任务中的变量 begin
#delay ab_and = a & b; ab_or = a | b; 在经过一段延迟之后,输 ab_xor = a ^ b; 出值被计算出来。延迟由
//包含自动(可重入)任务的模块
module top; reg[15:0] cd_xor,ef_xor; //顶层模块中的变量 reg[15:0] c,d,e,f; //顶层模块中的变量
task automatic bitwise_xor; output[15:0] ab_xor; //从任务输出 //输入到任务中的变量 input[15:0] a,b; begin #delay ab_and=a & b; ab_or = a | b; ab_xor = a ^ b; end end task …
//本模块中有两个时钟。Clk2的频率是clk的两倍,并与clk同步
//下面两个always块将在clk正跳变沿同时调用bitwise_xor任务 //因为该任务是可以重入的,所以并发的同时调用能正常运行
always @ (posedge clk) bitwise_xor(ef_xor , e, f); always @ (posedge clk2) bitwise_xor(cd_xor , c, d);
6.6.2 函数
函数与任务相比,其功能要弱一些,主要表现在 以下几点:
(1)在函数中可以嵌套调用函数,但不可以调用任务;而 任务既可以调用函数,也可以调用任务 (2)函数中不允许出现延时和事件控制语句,也就是说函 数必须马上执行完;而任务可以在执行过程中挂起 (3)函数至少需要一个参数,且参数必须都为输入端口,不 可以包含输出或者双向端口 (4)函数必须有一个返回值,返回值被赋给和函数名同名的 变量,这也决定了函数只能存在一个返回值
任务使用关键字task...endtask 进行申 明,如果子程序满足下面任一条件,必须使用 任务而不能使用函数:
•子程序中包含有延迟、时序或者事件控制结构。 •没有输出或者输出变量的数目大于1。 •没有输入变量。
task 任务名;
∥定义端口以及内部变量
input 输入端出端口,也可以没有
difference, //操作数1与操作数2的差 product, input [1:0] a,b; output [2:0] sum; output [1:0]difference; output [3:0]product;
output [3:0] result;
wire [1:0] a,b; reg [2:0] sum; reg [1:0]difference; reg [3:0]product;
采用ANSI C(美国国家协会)风格的变量声明进行任务定义
//define task bitwise_oper
task bitwise_oper( output [15:0] ab_and, ab_or, ab_xor, input [15:0] a,b );
begin #delay ab_and = a & b; ab_or = a | b; ab_xor = a ^ b;
endtask …
endmodule
自动(可重入)任务
任务在本质上时静态的,任务中的所有声明项的 地址空间是静态分配的,同时并发执行的多个任务 共享这些存储区。
如果在多个地方同时调用同一任务,则两个任务 将对同一块地址空间进行操作,结果可能出错 解决方法 自动任务:在task的关键字前添加automatic关键 字,使任务成为可重入的。 每次调用时,在动态任务中声明的所有模块项的 存储空间都是动态分配的,每个调用都对各自独立的 地址空间进行操作。
∥定义端口以及内部变量
函数名;
∥ DataWidth是返回值的位宽
input 输入端口名; ∥至少要有一个输入端口作为参数 wire 内部变量名; ∥定义内部变量 reg 内部变量名; ∥定义内部变量
begin … end endfunction
∥函数主体
函数具有的独特性质:
1、当函数声明的时候,在Verilog的内部隐含地声明了一个名 为function_identifier(函数标识符)的寄存器类型变量,函数的输 出结果将通过这个寄存器类型变量被传递回来。 2、函数的调用通过指明函数名和输入变量来进行。 3、在函数执行结束时,返回值被传回到调用处。 4、可选项range_or_type(类型或范围)说明了内部寄存器的 位宽。如果没有指定返回值的类型或位宽,则默认位宽为1。
2)任务或函数内定义的变量是局部变量,对外是不可见的。 3)在程序中使用任务和函数可以使程序结构更加清晰, 增加了程序的可读性和可维护性。
任务和函数在使用上的一些区别如表6-9
表6-9 函数和任务的对比
6.6.1 任务
Verilog HDL中的任务(task)与高级语言中的 过程类似,它不带返回值,因此不可以将它用 于表达式中,在调用时直接调用就可以了。尽 管任务不带有返回值,但任务的参数可以定义 为输出端口或者双向端口,因此实际上任务可 以返回多个值。
参数delay指定。
end
endtask
… endmodule
任务结束后,输出值被传递回调用任务 时使用的输出变量。因此在任务结束时, 仿真器会执行AB_AND=ab_and, AB_OR =ab_or, AB_XOR =ab_xor.
注意:任务调用语句是过程性语句,所以任务调用中接收返 回数据的变量必须是寄存器类型。
§ 6.6 任务和函数
本节重点:
Verilog HDL的行为建模中使用两种方式 任务(task)
掌握 •定义任务和函数所需要 的条件 •任务和函数的声明和 调用
函数(function)
§ 6.6 任务和函数
任务和函数的作用与功能模块相似,在设计 中使用任务和函数有3点好处:
1)在一个设计中有些行为可能会被多次调用。
[1:0] a; [1:0] b; [2:0] sum; [1:0]difference; [3:0]product; [3:0] result;
∥内部寄存器定义,在计算a^2-b的值时储存中间结果a^2
reg [3:0] tmp;
begin
∥执行运算
sum=a+b; difference=a-b; product=a*b; tmp=a*a; result=tmp-b; end
end
endtask
直接对寄存器变量进行操作
//定义包含名为asymmetric_sequence的任务的模块
module sequence; … reg clock;
… initial init_sequence;
在任务中可以直接对任 务所在模块中声明的寄 存器变量进行操作。
… always begin asymmetric_sequence; //启动asymmetric_sequence任务 end
…
//启动init_sequence任务来完成初始化
task init_sequence; //定义init_sequence(初始化序列)任务 begin clock=1’b0; end endtask //定义asymmetric_sequence(非对称序列发生器)任务 task asymmetric_sequence; begin #12 clock=1’b0; #5 clock=1’b1; //直接操作时钟(clock)信号 #3 clock=1’b0; #10 clock=1’b1; end
output 输出端口名;
inout wire reg begin end 双向端口名;
∥可以有一个或多个双向端口,也可以没有
内部变量名;
∥可以有一个或多个内部变量,也可以没有
内部变量名; …
∥任务主体
endtask;
在任务内部定义的变量,作用域是task与 endtask之间,它们对调用任务的模块不可见。
endtask
endmodule
例 任务中的输入和输出变量
//定义一个名为operation的模块,内部有一个名为 //bitwise_oper的任务
module operation; … parameter delay = 10; reg [15:0] A,B; reg [15:0] AB_AND, AB_OR, AB_XOR;
always @ (A or B ) ∥无论何时只要A或B的值发生改变 begin
∥启动 bitwise_oper任务,该任务提供两个输入变量A和B //有三个输出变量AB_AND, AB_OR, AB_XOR //变量的指定必须按照任务定义时的声明次序
bitwise_oper(AB_AND,AB_OR,AB_XOR,A,B); end; … 传递给任务的输入值 … 是A和B,因此在任务
注意:在函数声明中必须至少有一个输入声明,同时由于隐含
的寄存器变量function_identifier包含了函数的返回值,因此, 函数是没有输出变量的。另外在函数中不能调用任务,只能 调用其他函数。
举例1 【例6—14】 使用函数描述运算单元 module alu( a. ∥操作数1,位宽为2 b, //操作数2,位宽为2 sum, ∥操作数1与操作数2的和
例6.13使用任务描述了一个运算单元 module alu( a, b, sum, difference, product, result );
∥操作数1,位宽为2 ∥操作数2,位宽为2 //操作数1与操作数2的和 ∥操作数1与操作数2的差 ∥操作数1与操作数2的积 ∥四则运算a2-b的结果
input [1:0] a,b; output [2:0] sum; output [1:0] difference; output [3:0] product; output [3:0] result; wire [1:0] a,b; reg [2:0] sum; reg [1:0] difference; reg [3:0] product; reg [3:0] result; always @(a or b) begin
//操作数1与操作数2的积 result ∥四则运算a^2-b的结果 );
reg [3:0] result;
∥定义1 3位变量all_result,接收所有计算结果 reg [12:0] all_result; always @ (a or b) begin ∥注意调用函数的方法和任务不同 all_result=cal (a,b); //将函数作为表达式调用 sum=all_result[12:10]; ∥返回值的第12位到第10位为sum difference=all_result[9:8]; ∥返回值的第9位到第8位为difference product=all_result[7:4]; ∥返回值的第7位到第4位为product result=all_result[3:0]; // 返回值的第3位到第0位为result end
函数使用关键字function...endfunction 进 行申明,如果子程序满足下面所有条件,则可 以使用函数来完成:
•在子程序内不含有延迟、时序或者控制结构。 •子程序只有一个返回值。 •没有输出或者双向变量。 •至少有一个输入变量。
•不含有非阻塞赋值语句。
定义函数使用关键词function…endfunction 关键 词,其函数的定义式如下: function[DataWidth -1:0]
∥按照任务中定义的端口顺序调用任务
cal (a, b,sum,difference, product,result);
end
∥定义任务cal 输出变量的数目大于1,必须 使用任务而不能使用函数
task cal;
∥任务端口列表
input input output output output output
∥定义bitwise_oper任务
task bitwise_oper;
执行时首先将A赋予a, 将B赋予b。
output [15:0] ab_and, ab_or, ab_xor;
∥bitwise_oper任务的输出变量
input [15:0] a,b; ∥输入到任务中的变量 begin
#delay ab_and = a & b; ab_or = a | b; 在经过一段延迟之后,输 ab_xor = a ^ b; 出值被计算出来。延迟由
//包含自动(可重入)任务的模块
module top; reg[15:0] cd_xor,ef_xor; //顶层模块中的变量 reg[15:0] c,d,e,f; //顶层模块中的变量
task automatic bitwise_xor; output[15:0] ab_xor; //从任务输出 //输入到任务中的变量 input[15:0] a,b; begin #delay ab_and=a & b; ab_or = a | b; ab_xor = a ^ b; end end task …
//本模块中有两个时钟。Clk2的频率是clk的两倍,并与clk同步
//下面两个always块将在clk正跳变沿同时调用bitwise_xor任务 //因为该任务是可以重入的,所以并发的同时调用能正常运行
always @ (posedge clk) bitwise_xor(ef_xor , e, f); always @ (posedge clk2) bitwise_xor(cd_xor , c, d);