任务
一个任务就像一个过程,它可以从描述的不同位置执行共同的代码段。共同的代码段用任务定义编写成任务,这样它就能够从设计描述的不同位置通过任务调用被调用。任务可以包含时序控制,即时延控制,并且任务也能调用其它任务和函数。
任务定义
任务定义的形式如下:
task task_id;
[declarations]
procedural_statement
endtask
任务可以没有或有一个或多个参数。值通过参数传入和传出任务。除输入参数外(参数从任务中接收值),任务还能带有输出参数(从任务中返回值)和输入输出参数。任务的定义在模块说明部分中编写。例如:
module Has_Task;
parameter MAXBITS = 8;
task Reverse_Bits;
input[MAXBITS-1:0] Din;
output[MAXBITS-1:0] Dout;
integer K;
begin
for(K=0; K<MAXBITS; K=K+1)
Dout[MAXBITS-K] = Din[K]
end
endtask
endmodule
任务的输入和输出在任务开始处声明。这些输入和输出的顺序决定了它们在任务调用中的顺序。
任务调用
一个任务由任务调用语句调用。任务调用语句给出传入任务的参数值和接收结果的变量值。任务调用语句是过程性语句,可以在 always
语句或 initial
语句中使用。形式如下:
task_id[(expr1, expr2, ..., exprN)];
任务调用语句中参数列表必须与任务定义中的输入、输出和输入输出参数说明的顺序匹配。此外,参数要按值传递,不能按地址传递。下面是调用任务 Reverse_Bits
的实例:
reg[MAXBITS-1:0] Reg_X, New_Reg;
Reverse_Bits(Reg_X, New_Reg)
注意因为任务能够包含定时控制,任务可在被调用后再经过一定时延才返回值。因为任务调用语句是过程性语句,所以任务调用中的输出和输入输出参数必须是寄存器类型的。
通过采用任务 task 完成 4 个 4bits 输入数据的比较排列。
module sort4data(a,b,c,d,ra,rb,rc,rd);
input[3:0] a,b,c,d;
output[3:0] ra,rb,rc,rd;
reg[3:0] ra,rb,rc,rd;
reg[3:0] va,vb,vc,vd;
always@(a or b or c or d) begin
{
va,vb,vc,vd} = {
a,b,c,d};
sort2(va,vc);
sort2(vb,vd);
sort2(va,vb);
sort2(vc,vd);
sort2(vb,vc);
{
ra,rb,rc,rd} = {
va,vb,vc,vd};
end
task sort2;
inout[3:0] x,y;
reg[3:0] tmp;
if(x>y) begin tmp=x; x=y; y=tmp; end
endtask
endmodule
函数
函数,如同任务一样,也可以在模块不同位置执行共同代码。函数与任务的不同之处是函数只能返回一个值,它不能包含任何时延或时序控制(必须立即执行),并且它不能调用其它的任务。此外,函数必须带有至少一个输入,在函数中允许没有输出或输入输出说明。函数可以调用其它的函数。
函数说明部分
函数说明部分可以在模块说明中的任何位置出现,函数的输入是由输入说明指定,形式如下:
function[range] function_id;
input_declaration
other_declarations
procedural_statament
endfunction
函数实例如下:
module Function_Example
parameter MAXBITS = 8;
function[MAXBITS-1:0] Reverse_Bits;
input[MAXBITS-1:0] Din;
integer K;
begin
for(K=0; K<MAXBITS; K=K+1)
Reverse_Bits[MAXBITS-K] = Din[K];
end
endfunction
endmodule
函数定义在函数内部隐式地声明一个寄存器变量,该寄存器变量与函数同名并且取值范围相同。函数通过在函数定义中显式地对该寄存器赋值来返回函数值。对这一寄存器的赋值必须出现在函数定义中。
函数调用
函数调用是表达式的一部分。形式如下:
func_id(expr1, expr2,..., exprN)
以下是函数调用的例子:
reg[MAXBITS-1:0] New_Reg, Reg_X;
New_Reg = Reverse_Bits(Reg_X);
与任务相似,函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值。
设计一个采用调用函数的阶乘运算电路。
module factorial_fun(clk, n, result, reset);
input[3:0] n;
input clk, reset;
output[31:0] result;
reg[31:0] result;
always@(posedge clk or negedge reset)
if(!reset) result <= 0;
else result <= factorial(n);
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
endmodule
值变转储文件
值变转储(VCD)文件包含设计中指定变量取值变化的信息。它的主要目的是为其它后处理工具提供信息。
下面的系统任务用于创建和将信息导入 VCD 文件:
$dumpfile
:本系统任务指定转储文件名,例如
$dumpfile("uart.dump");
dumpvars
:本系统任务指定哪些变量值变化时转储进转储文件。
dumpoff
:本系统任务促使转储任务被挂起。
dumplimit
:本系统任务为 VCD 文件指定最大长度(字节)。转储在达到此界限时停止。
dumpflush
:本系统任务刷新操作系统 VCD 文件缓冲区中的数据,将数据存到 VCD 文件中。执行此系统任务后,转储任务处于唤醒状态。
下面是在 5~12 之间计数的可逆计数器的例子:
module CountUpDown(Clk, Count, Up_Down);
input Clk, Up_Down;
output[0:3] Count;
reg[0:3] Count;
initial Count = 'd5;
always@(posedge Clk) begin
if(Up_Down)
begin
Count = Count + 1;
if(Count > 12)
Count = 12
end
else
begin
Count = Count - 1;
if(Count < 5)
Count = 5;
end
end
endmodule
module Test;
reg Clock, UpDn;
wire[0:3] Cnt_Out;
parameter ON_DELAY = 1, OFF_DELAY = 2;
CountUpDown C1(Clock, Cnt_Out, UpDn);
always begin
Clock = 1;
#ON_DELAY
Clock = 0;
#OFF_DELAY;
end
initial begin
UpDn = 0;
#50 UpDn = 1;
#100 $dumpflush;
$stop;
end
initial begin
$dumpfile("count.dump");
$dumplimit(4096);
$dumpvars(0, Test);
$dumpvars(0, C1.Count, C1.Clk, C1.Up_Down);
end
endmodule