高级特性
本章介绍 Verilog 的高级特性,包括任务、函数、生成语句、系统任务等内容。
任务(Task)
任务定义
task task_name;
// 输入/输出声明
input [7:0] data_in;
output [7:0] data_out;
// 内部变量声明
reg [7:0] temp;
begin
// 任务体
temp = data_in;
data_out = ~temp;
end
endtask
任务调用
reg [7:0] input_data;
reg [7:0] output_data;
initial begin
input_data = 8'h55;
task_name(input_data, output_data);
$display("Result: %h", output_data);
end
任务特点
- 可以包含时序控制(#、@、wait)
- 可以调用其他任务
- 可以有多个输出参数
- 不返回值(通过输出参数传递结果)
带时序的任务
task send_byte;
input [7:0] data;
output ack;
begin
// 发送数据
@(posedge clk);
data_out = data;
valid = 1;
// 等待应答
@(posedge ack);
valid = 0;
end
endtask
函数(Function)
函数定义
function [7:0] function_name;
input [7:0] data_in;
begin
function_name = ~data_in; // 函数名作为返回值
end
endfunction
函数调用
wire [7:0] result;
assign result = function_name(8'h55);
函数特点
- 必须有返回值
- 不能包含时序控制
- 至少有一个输入参数
- 可以在表达式中直接调用
函数示例
// 计算奇偶校验位
function parity_bit;
input [7:0] data;
begin
parity_bit = ^data; // 异或归约
end
endfunction
// 计算绝对值
function [15:0] abs_value;
input signed [15:0] value;
begin
abs_value = (value < 0) ? -value : value;
end
endfunction
// 计算对数
function integer log2;
input integer value;
integer i;
begin
log2 = 0;
for (i = value; i > 1; i = i >> 1)
log2 = log2 + 1;
end
endfunction
函数与任务的区别
| 特性 | 函数 | 任务 |
|---|---|---|
| 返回值 | 必须有 | 无 |
| 时序控制 | 不允许 | 允许 |
| 输入参数 | 至少一个 | 可没有 |
| 输出参数 | 无 | 可有多个 |
| 调用位置 | 表达式中 | 独立语句 |
自动任务和函数
自动任务
task automatic auto_task;
input [7:0] data;
begin
// 每次调用分配新的存储空间
// 支持递归调用
end
endtask
自动函数
function automatic [31:0] factorial;
input [7:0] n;
begin
if (n <= 1)
factorial = 1;
else
factorial = n * factorial(n - 1); // 递归
end
endfunction
系统任务
仿真控制
$finish; // 结束仿真
$stop; // 暂停仿真
时间相关
$time // 当前仿真时间(64位整数)
$realtime // 当前仿真时间(实数)
$stime // 当前仿真时间(32位无符号)
随机数生成
$random // 返回 32 位有符号随机数
$urandom // 返回 32 位无符号随机数
$urandom_range(min, max) // 返回指定范围内的随机数
数组操作
$bits(expression) // 返回表达式的位宽
$dimensions(array) // 返回数组的维度
$left(array, dimension) // 返回数组指定维度的左边界
$right(array, dimension) // 返回数组指定维度的右边界
$size(array, dimension) // 返回数组指定维度的大小
系统函数
位操作
$clog2(n) // 返回以 2 为底的对数向上取整
$signed(expr) // 转换为有符号数
$unsigned(expr) // 转换为无符号数
字符串操作
$sformatf(format, ...) // 格式化字符串
$sscanf(str, format, ...) // 从字符串读取
文件 I/O
文件打开和关闭
integer file;
file = $fopen("filename.txt", "r"); // 打开文件
$fclose(file); // 关闭文件
文件读取
integer file;
reg [255:0] line;
file = $fopen("data.txt", "r");
while (!$feof(file)) begin
$fgets(line, file); // 读取一行
$display("%s", line);
end
$fclose(file);
文件写入
$fdisplay(file, "format", ...); // 写入并换行
$fwrite(file, "format", ...); // 写入不换行
$fstrobe(file, "format", ...); // 时间步结束时写入
$fmonitor(file, "format", ...); // 持续监控写入
格式化读写
integer file;
reg [31:0] data;
file = $fopen("output.txt", "w");
$fdisplay(file, "Data: %h", data);
$fclose(file);
存储器初始化
$readmemh
读取十六进制文件到存储器:
reg [7:0] mem [0:255];
initial begin
$readmemh("data.hex", mem); // 读取整个文件
$readmemh("data.hex", mem, 16); // 从地址 16 开始
$readmemh("data.hex", mem, 16, 31); // 地址 16 到 31
end
$readmemb
读取二进制文件到存储器:
reg [7:0] mem [0:255];
initial begin
$readmemb("data.bin", mem);
end
文件格式
十六进制文件格式(data.hex):
// 注释
00 // 地址 0
01 // 地址 1
02 // 地址 2
@10 // 跳转到地址 16
10 // 地址 16
11 // 地址 17
生成语句
generate for
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : gen_block
assign out[i] = in[i] & enable;
end
endgenerate
generate if
generate
if (WIDTH == 8) begin : width_8
assign result = a + b;
end else begin : width_other
assign result = a - b;
end
endgenerate
generate case
generate
case (MODE)
0: assign y = a;
1: assign y = b;
2: assign y = c;
default: assign y = 0;
endcase
endgenerate
块语句
顺序块(begin-end)
initial begin
a = 0;
#10 a = 1;
#10 a = 0;
end
并行块(fork-join)
initial fork
a = 0;
#10 a = 1;
#20 a = 0;
join
块类型
// join:等待所有进程完成
fork
process1();
process2();
join
// join_any:等待任一进程完成
fork
process1();
process2();
join_any
// join_none:不等待任何进程
fork
process1();
process2();
join_none
禁用语句
禁用块
initial begin
fork
begin : timeout_block
#1000 $display("Timeout");
end
begin
wait(done);
disable timeout_block; // 禁用超时块
end
join
end
禁用任务
task my_task;
begin : task_body
// ...
if (error_condition)
disable task_body; // 提前退出任务
// ...
end
endtask
延迟控制
惯性延迟
assign #10 y = a & b; // 惯性延迟,滤除短脉冲
传输延迟
assign #(10, 10) y = a & b; // 上升延迟 10,下降延迟 10
条件延迟
assign #(10:5:2) y = a; // 最小:典型:最大延迟
事件控制
边沿触发
@(posedge clk) // 上升沿
@(negedge clk) // 下降沿
@(clk) // 任意边沿
电平敏感
@(a or b or c) // 等待 a、b 或 c 变化
@(*) // 等待任何信号变化
事件变量
event my_event;
initial begin
->my_event; // 触发事件
end
initial begin
@my_event; // 等待事件
$display("Event triggered");
end
强度建模
信号强度
| 强度等级 | 名称 | 描述 |
|---|---|---|
| 7 | supply | 电源 |
| 6 | strong | 强驱动 |
| 5 | pull | 上拉/下拉 |
| 4 | large | 大电容 |
| 3 | weak | 弱驱动 |
| 2 | medium | 中等电容 |
| 1 | small | 小电容 |
| 0 | highz | 高阻 |
强度赋值
assign (strong1, weak0) y = a;
assign (pull1, pull0) y = b;
用户定义原语(UDP)
组合 UDP
primitive mux2to1(y, d0, d1, sel);
output y;
input d0, d1, sel;
table
// d0 d1 sel : y
0 ? 0 : 0;
1 ? 0 : 1;
? 0 1 : 0;
? 1 1 : 1;
endtable
endprimitive
时序 UDP
primitive d_latch(q, clk, d);
output q;
input clk, d;
reg q;
table
// clk d : q : q+
0 0 : ? : 0;
0 1 : ? : 1;
1 ? : ? : -; // 保持
endtable
endprimitive
小结
本章介绍了 Verilog 的高级特性:
- 任务:可包含时序控制,多输出参数
- 函数:必须有返回值,不能包含时序控制
- 自动任务和函数:支持递归调用
- 系统任务:仿真控制、时间、随机数
- 文件 I/O:文件读写操作
- 生成语句:generate for/if/case
- 块语句:顺序块和并行块
- 事件控制:边沿触发和电平敏感
- 强度建模:信号强度等级
- UDP:用户定义原语
掌握这些高级特性可以编写更灵活、更强大的 Verilog 代码。