跳到主要内容

高级特性

本章介绍 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

强度建模

信号强度

强度等级名称描述
7supply电源
6strong强驱动
5pull上拉/下拉
4large大电容
3weak弱驱动
2medium中等电容
1small小电容
0highz高阻

强度赋值

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 代码。