function讲解:
在verilog语言中,函数与任务是可综合的。
可以用function与task,将重复性的行为级设计(就是rtl描述)进行提取,并在多个地方调用,来避免重复代码的多次编写,可使代码更加的简洁易懂。
函数特点说明:
- 函数只能在模块内部,任意位置,定义与使用。作用范围也仅限于此模块;
- 不能有任何延迟,时序或者时序逻辑控制;
- 至少有一个输入变量;
- 只有一个返回值,没有输出;
- 不能含有非阻塞赋值语句;
- 函数可以调用其他函数,不能调用任务。
函数定义格式:
// 定义格式:
function [range-1:0] function_id ; // 没有输出但是有一个返回值,就是这个函数名称(function_id),range 定义位宽。
// 函数的返回值通过这个变量进行传递。当该寄存器变量没有指定位宽时,默认位宽为 1。
input input_declaration ; // 输入端口。可以有多个输入。
other_declaration ; // 其他描述
procedural_statement; // 工序过程要素
endfunction
函数调用格式:
// 函数调用格式:
function_id(input_data1, input_data2);
下面用函数实现一个数据大小端转换的功能:
// 大小端转换,就是把低位变高位,高位变低位。
module endian_rvs #(parameter N = 4) (
input wire en , //enable control
input wire [N-1:0] a ,
output wire [N-1:0] b
);
// reg signal descrioption
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 wire [N-1:0] data_in ;
parameter MASK = 32'h3 ;
integer k ;
begin
for(k=0; k<N; k=k+1) begin
data_rvs[N-k-1] = data_in[k] ;
end
end
endfunction
endmodule
里面的参数也可以改写为:
defparam data_rvs.MASK = 32'd7 ;
函数声明还可以在函数名称后面加上括号,把输入端口包含进入:
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
automatic 函数:
用得很少。
在 Verilog 中,一般函数的局部变量是静态的,即函数的每次调用,函数的局部变量都会使用同一个存储空间。若某个函数在两个不同的地方同时并发的调用,那么两个函数调用行为同时对同一块地址进行操作,会导致不确定的函数结果。
Verilog 用关键字 automatic 来对函数进行说明,此类函数在调用时是可以自动分配新的内存空间的,也可以理解为是可递归的。因此,automatic 函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。