在 Verilog 中,**function**用于定义可重用的代码块,这些代码块执行计算并返回一个值。函数在仿真时间 0 时执行,不消耗仿真时间。
一、基本语法
function [range] function_name;
input declarations;
other variable declarations;
begin
// 函数体
function_name = expression; // 返回值赋值
end
endfunction
// 或者使用 ANSI C 风格
function [range] function_name (input declarations);
other variable declarations;
begin
// 函数体
function_name = expression;
end
endfunction
二、使用示例
1. 基本函数定义和使用
module math_operations;
// 计算两个数的最大值
function integer max;
input integer a, b;
begin
if (a > b)
max = a;
else
max = b;
end
endfunction
// 计算阶乘
function integer factorial;
input integer n;
integer i;
begin
factorial = 1;
for (i = 2; i <= n; i = i + 1)
factorial = factorial * i;
end
endfunction
// 使用函数
initial begin
integer x = 10, y = 20;
integer result;
result = max(x, y);
$display("Max of %0d and %0d is %0d", x, y, result);
result = factorial(5);
$display("5! = %0d", result);
end
endmodule
2. 位向量操作函数
module bit_operations;
// 计算位向量中 1 的个数(人口计数)
function integer pop_count;
input [31:0] data;
integer i;
begin
pop_count = 0;
for (i = 0; i < 32; i = i + 1)
if (data[i])
pop_count = pop_count + 1;
end
endfunction
// 字节序转换(大端小端转换)
function [31:0] endian_swap;
input [31:0] data;
begin
endian_swap = {data[7:0], data[15:8], data[23:16], data[31:24]};
end
endfunction
// 位反转
function [31:0] bit_reverse;
input [31:0] data;
integer i;
begin
for (i = 0; i < 32; i = i + 1)
bit_reverse[i] = data[31-i];
end
endfunction
initial begin
reg [31:0] test_data = 32'h12345678;
$display("Original data: %h", test_data);
$display("Population count: %0d", pop_count(test_data));
$display("Endian swapped: %h", endian_swap(test_data));
$display("Bit reversed: %h", bit_reverse(test_data));
end
endmodule
三、函数特性
1. 返回值类型
module function_types;
// 返回位向量
function [7:0] byte_to_ascii;
input [3:0] nibble;
begin
if (nibble <= 4'h9)
byte_to_ascii = "0" + nibble;
else
byte_to_ascii = "A" + (nibble - 4'hA);
end
endfunction
// 返回实数
function real celsius_to_fahrenheit;
input real celsius;
begin
celsius_to_fahrenheit = (celsius * 9.0 / 5.0) + 32.0;
end
endfunction
// 返回时间
function time calculate_timeout;
input integer cycles;
input real clock_period;
begin
calculate_timeout = cycles * clock_period;
end
endfunction
initial begin
$display("ASCII of 4'hA: %s", byte_to_ascii(4'hA));
$display("25°C = %0.1f°F", celsius_to_fahrenheit(25.0));
$display("Timeout: %0t", calculate_timeout(100, 10.0));
end
endmodule
2. 自动函数
module automatic_functions;
// 自动函数 - 每次调用都有独立的存储空间
function automatic integer recursive_factorial;
input integer n;
begin
if (n <= 1)
recursive_factorial = 1;
else
recursive_factorial = n * recursive_factorial(n-1);
end
endfunction
// 自动函数的局部变量
function automatic [7:0] fibonacci;
input integer n;
integer i;
reg [7:0] a, b, temp;
begin
if (n == 0)
fibonacci = 0;
else if (n == 1)
fibonacci = 1;
else begin
a = 0;
b = 1;
for (i = 2; i <= n; i = i + 1) begin
temp = a + b;
a = b;
b = temp;
end
fibonacci = b;
end
end
endfunction
initial begin
$display("5! = %0d", recursive_factorial(5));
$display("Fibonacci(10) = %0d", fibonacci(10));
end
endmodule
四、实际应用场景
1. 数据校验函数
module data_checker;
// 计算奇偶校验位
function parity_bit;
input [7:0] data;
integer i;
begin
parity_bit = 0;
for (i = 0; i < 8; i = i + 1)
parity_bit = parity_bit ^ data[i];
end
endfunction
// 计算简单的校验和
function [7:0] checksum;
input [7:0] data [];
integer i;
begin
checksum = 0;
for (i = 0; i < data.size(); i = i + 1)
checksum = checksum + data[i];
end
endfunction
// CRC 计算(简化版)
function [15:0] crc16;
input [7:0] data;
input [15:0] current_crc;
integer i;
begin
crc16 = current_crc;
for (i = 0; i < 8; i = i + 1) begin
crc16 = (crc16 << 1) ^ (((crc16 >> 15) ^ (data >> (7-i))) ? 16'h1021 : 0);
end
end
endfunction
endmodule
2. 协议处理函数
module protocol_processor;
// 提取 IP 包头部字段
function [3:0] get_ip_version;
input [31:0] ip_header;
begin
get_ip_version = ip_header[31:28];
end
endfunction
function [3:0] get_header_length;
input [31:0] ip_header;
begin
get_header_length = ip_header[27:24];
end
endfunction
function [15:0] get_total_length;
input [31:0] ip_header;
begin
get_total_length = ip_header[15:0];
end
endfunction
// 验证 IP 头校验和
function is_valid_ip_header;
input [319:0] ip_header; // 假设 10 * 32bit IP 头
integer i;
reg [31:0] sum;
begin
sum = 0;
for (i = 0; i < 10; i = i + 1) begin
if (i != 5) begin // 跳过校验和字段
sum = sum + ip_header[i*32+:16] + ip_header[i*32+16+:16];
end
end
// 处理进位
while (sum[31:16])
sum = sum[31:16] + sum[15:0];
is_valid_ip_header = (sum[15:0] == 16'hFFFF);
end
endfunction
endmodule
3. 数学运算函数
module dsp_functions;
// 定点数乘法
function [31:0] fixed_point_mult;
input [15:0] a, b;
input [3:0] fractional_bits;
begin
fixed_point_mult = (a * b) >> fractional_bits;
end
endfunction
// 饱和加法
function [15:0] saturated_add;
input [15:0] a, b;
reg [16:0] temp;
begin
temp = a + b;
if (temp[16]) // 溢出
saturated_add = 16'hFFFF;
else
saturated_add = temp[15:0];
end
endfunction
// 查找表插值
function [15:0] linear_interpolate;
input [15:0] x0, x1, y0, y1;
input [15:0] x;
real slope, result;
begin
if (x1 == x0) begin
linear_interpolate = y0;
end else begin
slope = (y1 - y0) / (x1 - x0);
result = y0 + slope * (x - x0);
// 转换回定点数
linear_interpolate = result;
end
end
endfunction
initial begin
reg [15:0] a = 16'h1000, b = 16'h0800; // Q12 格式
reg [15:0] result;
result = fixed_point_mult(a, b, 12);
$display("Fixed point multiply: %h * %h = %h", a, b, result);
result = saturated_add(16'hFFFF, 16'h0001);
$display("Saturated add: FFFF + 0001 = %h", result);
end
endmodule
五、高级用法
1. 函数重载
module function_overloading;
// 不同数据类型的绝对值函数
function integer abs_int;
input integer value;
begin
abs_int = (value < 0) ? -value : value;
end
endfunction
function real abs_real;
input real value;
begin
abs_real = (value < 0.0) ? -value : value;
end
endfunction
function [31:0] abs_fixed;
input [31:0] value;
begin
abs_fixed = value[31] ? (~value + 1) : value;
end
endfunction
initial begin
$display("abs_int(-5) = %0d", abs_int(-5));
$display("abs_real(-3.14) = %0.2f", abs_real(-3.14));
$display("abs_fixed(-10) = %0d", abs_fixed(-10));
end
endmodule
2. 递归函数
module recursive_functions;
// 递归计算最大公约数
function automatic integer gcd;
input integer a, b;
begin
if (b == 0)
gcd = a;
else
gcd = gcd(b, a % b);
end
endfunction
// 递归计算幂运算
function automatic real power;
input real base;
input integer exponent;
begin
if (exponent == 0)
power = 1.0;
else if (exponent < 0)
power = 1.0 / power(base, -exponent);
else
power = base * power(base, exponent - 1);
end
endfunction
initial begin
$display("GCD of 48 and 18: %0d", gcd(48, 18));
$display("2^8 = %0.0f", power(2.0, 8));
$display("2^-3 = %0.3f", power(2.0, -3));
end
endmodule
3. 数组处理函数
module array_functions;
// 查找数组中的最大值
function integer find_max;
input integer arr [];
integer i;
begin
find_max = arr[0];
for (i = 1; i < arr.size(); i = i + 1) begin
if (arr[i] > find_max)
find_max = arr[i];
end
end
endfunction
// 数组求和
function integer array_sum;
input integer arr [];
integer i;
begin
array_sum = 0;
for (i = 0; i < arr.size(); i = i + 1)
array_sum = array_sum + arr[i];
end
endfunction
// 数组排序(冒泡排序)
function automatic void bubble_sort;
inout integer arr [];
integer i, j, temp;
begin
for (i = 0; i < arr.size() - 1; i = i + 1) begin
for (j = 0; j < arr.size() - i - 1; j = j + 1) begin
if (arr[j] > arr[j+1]) begin
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
end
end
end
end
endfunction
initial begin
integer numbers [0:4] = '{5, 2, 8, 1, 9};
$display("Original array: %p", numbers);
$display("Max value: %0d", find_max(numbers));
$display("Sum: %0d", array_sum(numbers));
bubble_sort(numbers);
$display("Sorted array: %p", numbers);
end
endmodule
六、函数限制和最佳实践
1. 函数限制
module function_limitations;
// 函数不能包含时间控制 (#, @)
function integer bad_function;
input integer a;
begin
#10; // 错误:函数中不能有时延
bad_function = a;
end
endfunction
// 函数不能调用任务
function integer another_bad_function;
input integer a;
begin
$display("Hello"); // 错误:函数不能调用系统任务
another_bad_function = a;
end
endfunction
// 正确的方式
function integer good_function;
input integer a;
begin
good_function = a * 2;
end
endfunction
endmodule
2. 最佳实践
module best_practices;
// 1. 使用有意义的函数名
function calculate_circle_area;
input real radius;
begin
calculate_circle_area = 3.14159 * radius * radius;
end
endfunction
// 2. 添加输入验证
function integer safe_divide;
input integer a, b;
begin
if (b == 0) begin
$warning("Division by zero attempted");
safe_divide = 0;
end else begin
safe_divide = a / b;
end
end
endfunction
// 3. 文档注释
// ================================
// 函数: hamming_distance
// 描述: 计算两个位向量之间的汉明距离
// 输入:
// a, b - 要比较的位向量
// 返回: 汉明距离(不同的位数)
// ================================
function integer hamming_distance;
input [31:0] a, b;
integer i, distance;
begin
distance = 0;
for (i = 0; i < 32; i = i + 1) begin
if (a[i] !== b[i])
distance = distance + 1;
end
hamming_distance = distance;
end
endfunction
// 4. 合理的返回值类型
function [7:0] clamp_to_byte;
input integer value;
begin
if (value < 0)
clamp_to_byte = 0;
else if (value > 255)
clamp_to_byte = 255;
else
clamp_to_byte = value;
end
endfunction
endmodule
七、函数 vs 任务
| 特性 | 函数 (function) | 任务 (task) |
|---|---|---|
| 返回值 | 必须返回一个值 | 可以不返回值 |
| 时间控制 | 不允许 | 允许 |
| 调用任务 | 不允许 | 允许 |
| 执行时间 | 零时间 | 可以消耗时间 |
| 用途 | 计算 | 复杂操作 |
函数是 Verilog 中实现复杂计算和代码重用的重要工具,合理使用函数可以大大提高代码的可读性和可维护性。