在 Verilog 中,可以利用任务(关键字为 task)或函数(关键字为 function),将重复性的行为级设计进行提取,并在多个地方调用,来避免重复代码的多次编写,使代码更加的简洁、易懂。
函数只能在模块中定义,位置任意,并在模块的任何地方引用,作用范围也局限于此模块。函数主要有以下几个特点:
Verilog 函数声明格式如下:
- function [range-1:0] function_id ;
- input_declaration ;
- other_declaration ;
- procedural_statement ;
- endfunction
函数在声明时,会隐式的声明一个宽度为 range、 名字为 function_id 的寄存器变量,函数的返回值通过这个变量进行传递。当该寄存器变量没有指定位宽时,默认位宽为 1。函数通过指明函数名与输入变量进行调用。函数结束时,返回值被传递到调用处。
函数调用格式如下:
function_id(input1, input2, …);
下面用函数实现一个数据大小端转换的功能。当输入为 4'b0011 时,输出可为 4'b1100。例如:
- module endian_rvs
- #(parameter N = 4)
- (
- input en, //enable control
- input [N-1:0] a ,
- output [N-1:0] b
- );
-
- reg [N-1:0] b_temp ;
- always @(*) begin
- if (en) begin
- b_temp = data_rvs(a);
- end
- else begin
- b_temp = 0 ;
- end
- end
- assign b = b_temp ;
-
- //function entity
- function [N-1:0] data_rvs ;
- input [N-1:0] data_in ;
- parameter MASK = 32'h3 ;
- integer k ;
- begin
- for(k=0; k
- data_rvs[N-k-1] = data_in[k] ;
- end
- end
- endfunction
-
- endmodule
函数里的参数也可以改写,例如:
defparam data_rvs.MASK = 32'd7 ;
但是仿真时发现,此种写法编译可以通过,仿真结果中,函数里的参数 MASK 实际并没有改写成功,仍然为 32'h3。这可能和编译器有关,有兴趣的学者可以用其他 Verilog 编译器进行下实验。函数在声明时,也可以在函数名后面加一个括号,将 input 声明包起来。
例如上述大小端声明函数可以表示为:
- function [N-1:0] data_rvs(
- input [N-1:0] data_in
- ......
- ) ;
常数函数是指在仿真开始之前,在编译期间就计算出结果为常数的函数。常数函数不允许访问全局变量或者调用系统函数,但是可以调用另一个常数函数。这种函数能够用来引用复杂的值,因此可用来代替常量。
例如下面一个常量函数,可以来计算模块中地址总线的宽度:
- parameter MEM_DEPTH = 256 ;
- reg [logb2(MEM_DEPTH)-1: 0] addr ; //可得addr的宽度为8bit
-
- function integer logb2;
- input integer depth ;
- //256为9bit,我们最终数据应该是8,所以需depth=2时提前停止循环
- for(logb2=0; depth>1; logb2=logb2+1) begin
- depth = depth >> 1 ;
- end
- endfunction
在 Verilog 中,一般函数的局部变量是静态的,即函数的每次调用,函数的局部变量都会使用同一个存储空间。若某个函数在两个不同的地方同时并发的调用,那么两个函数调用行为同时对同一块地址进行操作,会导致不确定的函数结果。
Verilog 用关键字 automatic 来对函数进行说明,此类函数在调用时是可以自动分配新的内存空间的,也可以理解为是可递归的。因此,automatic 函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。
下面用 automatic 函数,实现阶乘计算:
- wire [31:0] results3 = factorial(4);
- function automatic integer factorial ;
- input integer data ;
- integer i ;
- begin
- factorial = (data>=2)? data * factorial(data-1) : 1 ;
- end
- endfunction // factorial