分类: 嵌入式
2010-07-28 19:51:49
在verilog中,用户可以定义任务和函数,而且它还内置了一些系统任务和系统函数用于实现某些特定的操作。此外,本章还将介绍一些语法概念,如层次结构、VCD文件和信号强度等。
5.1 任务
任务就是一段封装在“task-endtask”之间的程序。任务是通过调用来执行的,而且只有在调用时才执行,如果定义了任务,但是在整个过程中都没有调用它,那么这个任务是不会执行的。调用某个任务时可能需要它处理某些数据并返回操作结果,所以任务应当有接收数据的输入端和返回数据的输出端。另外,任务可以彼此调用,而且任务内还可以调用函数。
注意:任务是不可综合的,它只能用于仿真。
5.1.1 任务定义
任务定义的形式如下:
task task_id
[declaration]
procedural_statement
endtask
其中,task_id是任务名;可选项declaration是端口声明语句和变量声明语句,任务接收输入值和返回输出值就是通过此处声明的端口进行的;procedural_statement是一段用来完成这个任务操作的过程语句,如果过程语句多于一条,应将其放在语句块内。
5.1.2 任务调用
任务调用语句可以在initial语句和always语句中使用,其语法形式如下:
task_id[(expr1, expr2, ........, exprN)];
task_id是要调用的任务名,expr1, expr2, ........是参数列表。参数列表给出传入任务的数据(进入任务的输入端)和接收返回结果的变量(从任务的输出端接收返回结果),任务调用语句中参数列表的顺序必须与任务定义中的端口声明顺序相同。任务调用语句是过程性语句,所以任务调用中接收返回数据的变量必须是寄存器类型。来看下例:
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
Dout[MAXBITS-K] = Din[K];
end
endtask
end module
下面调用Reverse_Bits的代码:
reg[MAXBITS-1:0] Reg_X, New_Reg;
Reverse_Bits(Reg_X, New_Reg);
其中,Reg_X的值作为输入数据送到任务的输入端Din,任务的返回值从其输出端Dout输出并交给New_Reg,在寄存器new_reg中得到返回值。
调用任务时,可以引用任务声明所在的模块内定义的任何变量。
任务内可以带有时序控制,如时延。但要注意,任务的输出值必须等到整个任务的全部语句都执行完之后才能返回。
5.2 函数
和任务一样,verilog的函数也是一段可以完成特定操作的程序,这段程序处于关键词“function-endfunction”之间。函数与任务的不同之处在于:
·函数只能返回一个值,而任务可以有多个返回值;
·函数一经调用就必须立即执行,其内部不能包含任何时序控制,而任务内部可以有时序控制;
·函数可以调用函数,但是不能调用任务,而任务即可以调用任务也可以调用函数;
·函数至少有一个输入,而任务可以没有输入端。
5.2.1 函数定义
函数定义和任务定义一样,可以出现在模块内的任何位置,其形式如下:
function [range] function_id;
input_declaration
other_declarations
procedural_statement
endfunction
这里的range用于指定函数的取值范围,是可选项,若没有指定,默认缺省值为1。
module Function_Example;
parameter MAXBITS = 8;
function [MAXBITS-1:0] Reverse_Bits;
input [MAXBITS-1:0] Din;
integer K;
begin
for(K=0; K
Reverse_Bits[MAXBITS-K] = Din[K];
end
endfunction
endmodule
注意到没有,函数的定义中并没有声明输出,那么函数执行得到的结果如何返回呢?事实上,函数定义时,在函数内部已经隐性的声明了一个寄存器变量,该寄存器变量与函数名同名并且取值范围也相同。那么函数如何通过这个寄存器返回值?注意上例中的“Rever_Bits[MAXBITS-K] = Din[K];”这条语句,就是通过这条语句把Din[K]的值赋给寄存器Reverse_Bits[MAXBITS-K],同时也实现了值的返回。
5.2.2 函数调用
和任务一样,函数也是在被调用时才被执行的,调用函数的语句形式如下:
func_id(expr1, expr2, ........., exprN)
其中,func_id是要调用的函数名,expr1, expr2, ......exprN是传递给函数的输入参数列表,该输入参数列表的顺序必须与函数定义时声明其输入的顺序相同。
reg[MAXBITS-1:0] New_Reg, Reg_X;
New_Reg = Reverse_Bits(Reg_X);
与任务相似,在函数内部声明的所有寄存器都是静态的,当函数被调用时,这些寄存器的值不能被改变。
5.3 系统任务和函数
verilog提供了一些已经定义好的任务和函数,即系统任务和系统函数,通过直接调用这些系统任务或系统函数可以方便的完成某些操作。
这些系统任务和系统函数可以分为以下几类:
·显示任务
·文件输入/输出任务
·时间标度任务
·仿真控制任务
·时序验证任务
·PLA建模任务
·随即建模任务
·仿真时间函数
·实数变换函数
·随即函数
这些系统任务和系统函数基本都是针对仿真过程的,与硬件模型的功能无关。
5.3.1 显示任务
显示任务可以在仿真过程中用于信息显示和输出。这些显示任务又可以进一步分为3类:
·显示和写入任务
·探测监控任务
·连续监控任务
(1)显示和写入任务
显示任务将特定信息输出到标准输出设备,并且带有行结束符(\n);而写入任务输出特定信息时不带有行结束符。其语法如下:
task_name(format_specification1, argument_list1,
format_specification2, argument_list2,
...........
format_specificationN, argument_listN);
其中,task_name是调用的系统任务名,format_specification是格式定义,armgument_list是传入系统任务的参数。task_name可以是下列指令之一:
· $display $displayb $displayo $displayh
· $write $writeb $writeo $writeh
(2) 探测任务
探测任务用于在指定时间显示仿真数据,共有4种类型:
·$strobe $strobeb $strobeh $strobeo
其语法形式与显示任务相同,如下例所示:
always @ (posedge Rst)
$strobe("the flip-flop value is %b at time %t", Q, $time);
当Rst出现一个上升沿时,$stobe任务将输出当前的Q值和仿真时刻。
探测任务与显示任务的不同之处在于:显示任务是在遇到该语句时执行,而探测任务则要推迟到当前时刻结束时才执行。那什么是当前时刻呢?注意这些系统任务的执行是不占用仿真时间的,所以当前时刻应该在探测语句后的赋值语句,等当这个个赋值语句执行完之后,探测任务才执行。
(3)监控任务
监控任务将连续监控指定的参数,只要参数表中的参数发生变化,整个参数表就在当前仿真时刻结束时显示。监控任务有4种:
· $monitor $monitorb $monitorh $monitoro
监控任务的语法形式与显示任务相同,例如:
initial
$monitor("At %t, D = %d, Clk = %d", $time, D, Clk, "and Q is %b", Q);
该监控任务执行时,将对信号D、Clk和Q进行监控。如果这三个参数中有任何一个的值发生变化,就显示所有参数的值。另外两个系统任务$monitoroff和monitoron把监控任务关闭或开启。
5.3.2 文件输入/输出任务
(1)文件的打开和关闭
系统函数$fopen可以打开一个文件,其形式如下:
integer file_pointer = $fopen(file_name);
$fopen将返回关于文件file_name的整数(指针),并把它赋给整形变量file_pointer。与之相应的是,系统函数$fclose可以通过文件指针关闭文件。形式如下:
$fclose(file_pointer);
(2) 输出到文件
显示、写入、探测和监控系统任务都有用于向文件输出信息的相应版本,可用于将信息写入文件。这些任务在使用时只需要增加一个参数即第一个参数,该参数都是文件指针(指示要把信息写入哪个文件)。
(3)从文件中读取数据
有两个系统任务能够用于从文本文件中读取数据并将数据加载到存储器,它们是:
· $readmemb 读取二进制格式数
· $readmemh 读取十六进制格式数
其语法形式是:
task_id("file_name", mem_name [,start_addr, finish_addr]);
还有一种方式可以把指定的数据放入指定的存储器地址单元内,就是在存放数据的文本文件内,给相应的数据规定其内存地址,形式如下:
@address_in_hexadecimal data
5.3.3 时间标度任务
5.3.4 仿真控制任务
仿真控制任务用于使仿真进程停止,这类系统任务共有两个:
·$finish $stop 二者用法相同。
5.3.5 时序验证任务
5.3.6 仿真时间函数($time $stime $realtime)
5.3.7 实时变换函数
5.3.8 随机函数
5.4 其他重要概念
5.4.1 禁止语句
5.4.2 命名事件