在 Verilog 中,**task**用于定义可重用的代码块,这些代码块可以包含时间控制、调用其他任务和函数,并且可以包含输入、输出和双向端口。
一、基本语法
task task_name;
input declarations;
output declarations;
inout declarations;
local variable declarations;
begin
// 任务体
// 可以包含时间控制语句
end
endtask
// 或者使用 ANSI C 风格
task task_name (
input declarations,
output declarations,
inout declarations
);
local variable declarations;
begin
// 任务体
end
endtask
二、使用示例
1. 基本任务定义和使用
module basic_tasks;
reg [7:0] data;
reg valid;
// 简单的数据打印任务
task print_data;
input [7:0] data_value;
begin
$display("Time %0t: Data = %h (%0d)", $time, data_value, data_value);
end
endtask
// 带有时延的任务
task apply_reset;
input duration;
output rst;
begin
$display("Applying reset for %0t units", duration);
rst = 1'b1;
#duration;
rst = 1'b0;
$display("Reset released at time %0t", $time);
end
endtask
// 使用任务
initial begin
reg reset_signal;
// 调用打印任务
print_data(8'hFF);
print_data(8'h55);
// 调用复位任务
apply_reset(100, reset_signal);
$display("Reset signal is now: %b", reset_signal);
end
endmodule
2. 输入输出参数
module io_tasks;
// 交换两个变量的值
task swap_values;
inout integer a, b;
integer temp;
begin
$display("Before swap: a = %0d, b = %0d", a, b);
temp = a;
a = b;
b = temp;
$display("After swap: a = %0d, b = %0d", a, b);
end
endtask
// 计算统计信息
task calculate_stats;
input real data [];
output real mean, variance;
integer i;
real sum, sum_squares;
begin
sum = 0.0;
sum_squares = 0.0;
for (i = 0; i < data.size(); i = i + 1) begin
sum = sum + data[i];
sum_squares = sum_squares + data[i] * data[i];
end
mean = sum / data.size();
variance = (sum_squares / data.size()) - (mean * mean);
end
endtask
initial begin
integer x = 10, y = 20;
real values [0:4] = '{1.0, 2.0, 3.0, 4.0, 5.0};
real avg, var;
swap_values(x, y);
calculate_stats(values, avg, var);
$display("Mean: %0.2f, Variance: %0.2f", avg, var);
end
endmodule
三、任务特性
1. 时间控制
module timing_tasks;
reg clk;
reg [7:0] data_bus;
// 时钟生成任务
task generate_clock;
input integer cycles;
input real period;
integer i;
begin
clk = 0;
for (i = 0; i < cycles; i = i + 1) begin
#(period / 2.0) clk = ~clk;
#(period / 2.0) clk = ~clk;
end
end
endtask
// 总线写任务
task bus_write;
input [7:0] address;
input [7:0] data;
begin
// 等待时钟上升沿
@(posedge clk);
// 建立地址和数据
data_bus = address;
#10;
// 发出写信号
$display("Time %0t: Writing data %h to address %h", $time, data, address);
#20;
// 完成写操作
data_bus = 8'bz;
end
endtask
// 总线读任务
task bus_read;
input [7:0] address;
output [7:0] data;
begin
// 等待时钟上升沿
@(posedge clk);
// 建立地址
data_bus = address;
#10;
// 采样数据
#15;
data = data_bus;
$display("Time %0t: Read data %h from address %h", $time, data, address);
// 完成读操作
data_bus = 8'bz;
end
endtask
initial begin
reg [7:0] read_data;
// 启动时钟
fork
generate_clock(10, 20.0);
join_none
// 执行总线操作
#30;
bus_write(8'h10, 8'hAA);
#50;
bus_read(8'h10, read_data);
#100 $finish;
end
endmodule
2. 自动任务
module automatic_tasks;
// 自动任务 - 每次调用都有独立的存储空间
task automatic recursive_countdown;
input integer n;
begin
if (n > 0) begin
$display("Time %0t: Countdown = %0d", $time, n);
#10;
recursive_countdown(n - 1);
end else begin
$display("Time %0t: Countdown complete!", $time);
end
end
endtask
// 并发调用自动任务
task automatic delayed_message;
input string msg;
input integer delay;
begin
#delay;
$display("Time %0t: %s", $time, msg);
end
endtask
initial begin
// 递归调用
recursive_countdown(3);
// 并发调用
fork
delayed_message("Message 1", 10);
delayed_message("Message 2", 20);
delayed_message("Message 3", 30);
join
$display("All messages completed");
end
endmodule
四、实际应用场景
1. 存储器初始化任务
module memory_tasks;
reg [7:0] memory [0:255];
reg [7:0] data_bus;
reg cs, we, oe;
// 存储器初始化任务
task initialize_memory;
input [7:0] pattern;
integer i;
begin
$display("Initializing memory with pattern %h", pattern);
for (i = 0; i < 256; i = i + 1) begin
memory[i] = pattern + i;
end
$display("Memory initialization complete");
end
endtask
// 存储器写任务
task memory_write;
input [7:0] address;
input [7:0] data;
begin
@(posedge clk);
data_bus = address;
cs = 1'b1;
we = 1'b1;
#10;
data_bus = data;
#20;
we = 1'b0;
cs = 1'b0;
data_bus = 8'bz;
memory[address] = data;
$display("Time %0t: Wrote %h to address %h", $time, data, address);
end
endtask
// 存储器读任务
task memory_read;
input [7:0] address;
output [7:0] data;
begin
@(posedge clk);
data_bus = address;
cs = 1'b1;
oe = 1'b1;
#25;
data = data_bus;
oe = 1'b0;
cs = 1'b0;
data_bus = 8'bz;
$display("Time %0t: Read %h from address %h", $time, data, address);
end
endtask
// 存储器测试任务
task memory_test;
integer i;
reg [7:0] test_data, read_back;
begin
$display("Starting memory test...");
// 写入测试模式
for (i = 0; i < 16; i = i + 1) begin
test_data = i * 16;
memory_write(i, test_data);
end
// 验证读取
for (i = 0; i < 16; i = i + 1) begin
memory_read(i, read_back);
if (read_back !== (i * 16)) begin
$error("Memory test failed at address %h", i);
end
end
$display("Memory test completed successfully");
end
endtask
endmodule
2. 通信协议任务
module uart_tasks;
reg tx, rx;
reg clk;
// UART 发送任务
task uart_transmit;
input [7:0] data;
input integer baud_rate;
real bit_time;
integer i;
begin
bit_time = 1000000000.0 / baud_rate; // 纳秒为单位
$display("Time %0t: UART transmitting %h", $time, data);
// 起始位
tx = 1'b0;
#bit_time;
// 数据位
for (i = 0; i < 8; i = i + 1) begin
tx = data[i];
#bit_time;
end
// 停止位
tx = 1'b1;
#bit_time;
$display("Time %0t: UART transmit complete", $time);
end
endtask
// UART 接收任务
task uart_receive;
output [7:0] data;
input integer baud_rate;
real bit_time, half_bit_time;
integer i;
begin
bit_time = 1000000000.0 / baud_rate;
half_bit_time = bit_time / 2.0;
$display("Time %0t: Waiting for UART start bit", $time);
// 等待起始位
wait (rx == 1'b0);
#half_bit_time; // 采样点在位中间
if (rx == 1'b0) begin // 确认起始位
#bit_time; // 移动到第一个数据位
// 接收数据位
for (i = 0; i < 8; i = i + 1) begin
data[i] = rx;
#bit_time;
end
// 验证停止位
if (rx !== 1'b1) begin
$warning("Invalid stop bit received");
end
$display("Time %0t: UART received %h", $time, data);
end else begin
$display("Time %0t: False start bit detected", $time);
end
end
endtask
// UART 回环测试任务
task uart_loopback_test;
input [7:0] test_data;
input integer baud_rate;
reg [7:0] received_data;
begin
fork
begin
uart_receive(received_data, baud_rate);
end
begin
#100; // 稍等片刻再发送
uart_transmit(test_data, baud_rate);
end
join
if (received_data === test_data) begin
$display("UART loopback test PASSED");
end else begin
$error("UART loopback test FAILED: sent %h, received %h",
test_data, received_data);
end
end
endtask
endmodule
3. 测试平台任务
module testbench_tasks;
reg clk, reset;
reg [31:0] test_results [0:99];
integer test_count;
// 测试用例生成任务
task generate_test_case;
output [31:0] stimulus;
output [31:0] expected;
integer test_type;
begin
test_type = $random % 4;
case (test_type)
0: begin // 算术测试
stimulus = $random;
expected = stimulus + 1;
end
1: begin // 逻辑测试
stimulus = $random;
expected = ~stimulus;
end
2: begin // 移位测试
stimulus = $random;
expected = stimulus << 1;
end
3: begin // 比较测试
stimulus = $random;
expected = (stimulus > 32'h7FFF_FFFF) ? 1 : 0;
end
endcase
$display("Generated test: stimulus=%h, expected=%h",
stimulus, expected);
end
endtask
// 应用激励任务
task apply_stimulus;
input [31:0] data;
begin
@(posedge clk);
dut_input = data;
$display("Time %0t: Applied stimulus %h", $time, data);
end
endtask
// 检查结果任务
task check_result;
input [31:0] expected;
input [31:0] actual;
input integer test_id;
begin
@(posedge clk);
if (actual === expected) begin
$display("Test %0d PASSED: expected=%h, actual=%h",
test_id, expected, actual);
test_results[test_id] = 1;
end else begin
$error("Test %0d FAILED: expected=%h, actual=%h",
test_id, expected, actual);
test_results[test_id] = 0;
end
end
endtask
// 运行测试套件任务
task run_test_suite;
input integer num_tests;
integer i;
reg [31:0] stimulus, expected, actual;
begin
$display("Starting test suite with %0d tests", num_tests);
test_count = 0;
for (i = 0; i < num_tests; i = i + 1) begin
generate_test_case(stimulus, expected);
apply_stimulus(stimulus);
#10;
actual = dut_output;
check_result(expected, actual, i);
test_count = test_count + 1;
#20; // 测试间隔
end
report_test_results();
end
endtask
// 测试结果报告任务
task report_test_results;
integer i, passed, failed;
begin
passed = 0;
failed = 0;
for (i = 0; i < test_count; i = i + 1) begin
if (test_results[i])
passed = passed + 1;
else
failed = failed + 1;
end
$display("=== TEST RESULTS ===");
$display("Total tests: %0d", test_count);
$display("Passed: %0d", passed);
$display("Failed: %0d", failed);
$display("Success rate: %0.1f%%", (passed * 100.0) / test_count);
if (failed == 0) begin
$display("*** ALL TESTS PASSED ***");
end else begin
$display("*** SOME TESTS FAILED ***");
end
end
endtask
endmodule
五、高级用法
1. 任务重载
module overloaded_tasks;
// 不同数据类型的显示任务
task display_value;
input integer value;
begin
$display("Integer value: %0d", value);
end
endtask
task display_value;
input real value;
begin
$display("Real value: %0.2f", value);
end
endtask
task display_value;
input [31:0] value;
begin
$display("Hex value: %h", value);
end
endtask
task display_value;
input string value;
begin
$display("String value: %s", value);
end
endtask
initial begin
display_value(42); // 调用整数版本
display_value(3.14159); // 调用实数版本
display_value(32'hDEADBEEF); // 调用十六进制版本
display_value("Hello"); // 调用字符串版本
end
endmodule
2. 递归任务
module recursive_tasks;
// 递归计算斐波那契数列
task automatic fibonacci;
input integer n;
output integer result;
integer fib1, fib2;
begin
if (n <= 1) begin
result = n;
end else begin
fibonacci(n - 1, fib1);
fibonacci(n - 2, fib2);
result = fib1 + fib2;
end
end
endtask
// 递归遍历链表(概念示例)
task automatic traverse_list;
input integer node_ptr;
integer next_ptr, data;
begin
if (node_ptr != 0) begin
// 假设有函数获取节点数据
// get_node_data(node_ptr, data, next_ptr);
$display("Node data: %0d", data);
traverse_list(next_ptr);
end
end
endtask
initial begin
integer fib_result;
fibonacci(10, fib_result);
$display("Fibonacci(10) = %0d", fib_result);
end
endmodule
3. 并发任务控制
module concurrent_tasks;
reg [7:0] shared_resource;
semaphore sem; // 信号量用于同步
// 生产者任务
task automatic producer;
input integer id;
integer data;
begin
forever begin
#($random % 50 + 10); // 随机延迟
data = $random;
sem.get(1); // 获取信号量
shared_resource = data;
$display("Time %0t: Producer %0d produced data %h",
$time, id, data);
sem.put(1); // 释放信号量
#10;
end
end
endtask
// 消费者任务
task automatic consumer;
input integer id;
reg [7:0] data;
begin
forever begin
#($random % 30 + 5); // 随机延迟
sem.get(1); // 获取信号量
data = shared_resource;
$display("Time %0t: Consumer %0d consumed data %h",
$time, id, data);
sem.put(1); // 释放信号量
end
end
endtask
// 监视器任务
task monitor_shared_resource;
begin
forever begin
#10;
$display("Time %0t: Shared resource = %h",
$time, shared_resource);
end
end
endtask
initial begin
sem = new(1); // 创建信号量,初始值为1
fork
producer(1);
producer(2);
consumer(1);
consumer(2);
monitor_shared_resource;
// 运行一段时间后停止
begin
#1000;
$display("Simulation completed");
$finish;
end
join
end
endmodule
六、任务限制和最佳实践
1. 任务限制
module task_limitations;
// 任务不能从函数中调用
function integer bad_function;
input integer a;
begin
some_task(a); // 错误:函数中不能调用任务
bad_function = a;
end
endfunction
// 任务可以有输出,但函数必须返回值
task valid_task;
input integer a;
output integer b;
begin
b = a * 2;
end
endtask
endmodule
2. 最佳实践
module best_practices;
// 1. 使用有意义的任务名
task initialize_system_components;
begin
$display("Initializing system components...");
// 初始化代码
end
endtask
// 2. 添加输入验证
task safe_divide;
input integer dividend, divisor;
output integer result;
output bit success;
begin
if (divisor == 0) begin
$warning("Division by zero attempted");
success = 1'b0;
result = 0;
end else begin
result = dividend / divisor;
success = 1'b1;
end
end
endtask
// 3. 文档注释
// ================================
// 任务: perform_memory_test
// 描述: 执行完整的存储器测试序列
// 输入:
// start_addr - 起始地址
// end_addr - 结束地址
// test_pattern - 测试模式
// 输出:
// error_count - 发现的错误数量
// ================================
task perform_memory_test;
input [31:0] start_addr, end_addr;
input [7:0] test_pattern;
output integer error_count;
integer addr;
reg [7:0] read_data;
begin
error_count = 0;
$display("Starting memory test from %h to %h",
start_addr, end_addr);
for (addr = start_addr; addr <= end_addr; addr = addr + 1) begin
// 写入测试模式
memory_write(addr, test_pattern ^ addr[7:0]);
// 读取验证
memory_read(addr, read_data);
if (read_data !== (test_pattern ^ addr[7:0])) begin
$error("Memory error at address %h", addr);
error_count = error_count + 1;
end
end
$display("Memory test completed with %0d errors", error_count);
end
endtask
// 4. 合理的参数组织
task configure_uart;
input integer baud_rate;
input integer data_bits;
input bit parity_enable;
input bit stop_bits;
begin
$display("Configuring UART:");
$display(" Baud rate: %0d", baud_rate);
$display(" Data bits: %0d", data_bits);
$display(" Parity: %s", parity_enable ? "Enabled" : "Disabled");
$display(" Stop bits: %0d", stop_bits);
// 配置逻辑
end
endtask
// 5. 错误处理和恢复
task robust_operation;
input integer max_retries;
output bit success;
integer retry_count;
begin
retry_count = 0;
success = 1'b0;
while (retry_count < max_retries && !success) begin
fork
begin : operation_block
// 执行可能失败的操作
#100;
if ($random % 4 != 0) begin // 75% 成功率
success = 1'b1;
$display("Operation succeeded on attempt %0d",
retry_count + 1);
end else begin
$display("Operation failed on attempt %0d",
retry_count + 1);
end
end
begin : timeout_block
#500; // 超时时间
$display("Operation timed out on attempt %0d",
retry_count + 1);
disable operation_block;
end
join_any
disable fork;
retry_count = retry_count + 1;
#10; // 重试间隔
end
if (!success) begin
$error("Operation failed after %0d attempts", max_retries);
end
end
endtask
endmodule
七、任务 vs 函数
| 特性 | 任务 (task) | 函数 (function) |
|---|---|---|
| 返回值 | 可以不返回值或通过output返回 | 必须返回一个值 |
| 时间控制 | 允许 (#, @, wait) | 不允许 |
| 调用任务 | 允许 | 不允许 |
| 调用函数 | 允许 | 允许 |
| 执行时间 | 可以消耗仿真时间 | 零时间 |
| 用途 | 复杂操作、测试、协议 | 计算、转换 |
任务是 Verilog 中实现复杂操作、测试序列和协议处理的重要工具,合理使用任务可以大大提高测试平台和复杂逻辑的可读性和可维护性。