SV过程与子程序

过程语句

概念

  • initial语句
    • 用于初始化行为建模,仅在仿真开始时执行一次
  • always语句
    • 用于持续执行的行为建模,仿真过程中重复执行
    • 需要明确列出敏感事件列表
  • SV扩展了always
    • always_comb:自动推断组合逻辑敏感列表,避免遗漏信号
    • always_ff:明确用于时序逻辑,需指定时钟和复位条件
    • always_latch:专为锁存器设计,明确设计意图,提高可维护性

赋值规则

  • 阻塞赋值=
    • 按代码顺序立即执行,适用于组合逻辑建模
    • 执行时后续语句需等待当前赋值完成
  • 非阻塞赋值<=
    • 赋值操作并行执行,适用于时序逻辑
    • 在时钟边沿捕获当前值,结束后统一更新结果,避免竞争风险
  • 限制
    • 过程赋值的被赋值变量必须是寄存器类型(reglogic),并且只能在过程块中使用
    • 区别于连续赋值assign只能驱动线网类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
module xor_comb(
input logic a, b,
output logic out
);
always_comb begin
out = a ^ b; // 阻塞赋值,组合逻辑
end
endmodule

module d_latch(
input logic enable, d,
output logic q
);
always_latch begin
if (enable) begin
q = d; // 当enable为高时,q锁存d的值
end
end
endmodule

module d_flipflop(
input logic clk, reset, d,
output logic q
);
always_ff @(posedge clk or posedge reset) begin
if (reset) begin
q <= 0; // 复位时q置0
end else begin
q <= d; // 时钟上升沿时q更新为d
end
end
endmodule

子程序

任务与函数

  • 任务用于封装可包含时序控制的行为代码块
    • 延时#、事件触发@、等待wait
  • 无返回值,支持inputoutputinout参数
    • 无输入输出时空括号可省略
    • 只有输入时,input可省略
  • begin ... end不再是必需的,但必须使用通用的输入类型logic
1
2
3
4
task send_data(input logic [7:0] data, output bit ack);
#10; // 延时10个时间单位
ack = 1;
endtask
  • 函数用于纯计算或组合逻辑,无任何时序控制,执行时间为0
  • 必须至少有一个输入参数,通过函数名返回单一值
1
2
3
function int add(input int a, b);
return a + b; // 直接返回计算结果
endfunction

子程序参数

引用传参

  • 在SV中,参数的传递除了可以使用inputoutputinout进行复制外,还可以使用引用的方式传参
  • 核心:使用ref实现对变量的直接引用(类似指针)
    • 避免大型数据(如结构体、数组)的复制开销,提升性能
    • 子程序内部对ref参数的修改立即可见于外部调用者
  • 使用规则
    • 只使用于变量,不支持线网类型
    • 多个并发子程序通过ref访问同一变量时,可能导致数据竞争
      • 为避免竞争,可以使用旗语或信箱等方式进行同步或传递副本
    • const联合使用,声明可强制引用为只读,防止意外修改
1
2
3
4
5
function void print_sum(const ref int arr[]);
int sum = 0;
foreach(arr[i]) sum += arr[i];
$display("Sum: %0d", sum);
endfunction

参数的缺省值

  • 在声明函数时为部分或全部参数预设缺省值(默认值),当调用函数时,若未显示传入,则使用缺省值
    • 使用-1或其他任何越界值作为缺省值,便于获知调用时是否有指定值
  • 显示指定参数名.parameter_name(value)
    • 使用名字进行传参,允许直接关联参数名与值,而不依赖参数在模块定义中的声明顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function automatic void print_csm(const ref bit [31:0] a[],
input bit [31:0] low = 0,
input int high = -1);
bit [31:0] checksum = 0;
if (high == -1 || hign >= a.size())
high = a.size()-1;

for (int i = low; i <= high; i++)
checksum ^= a[i];
$display("The array checksum is %h", checksum);
endfunction

//调用
print_csm(a); //check a[0:size()-1]
print_csm(a, 2, 4); //check a[2:4]
print_csm(a, 1); //check a[1:size()-1]
print_csm(a,, 2); //check a[0:2]
print_csm(); //编译错误,a无缺省值

//显示指定参数名,若low > high or a.size(),for循环不执行
print_csm(a, .c(3)); //check a[3:size()-1]

子程序的返回

显式返回

  • void函数无返回值,非void函数必须提供返回值
    • return关键词适用于单个返回值的函数
    • 多返回值需用output或结构体
  • 在任何函数中,return语句都可用于发现错误提前终止退出
    • SV支持return关键词灵活退出,而Verilog仅能在endfunciton处返回
1
2
3
4
5
6
7
8
9
//显示返回
function int add(int a, int b);
return a + b; // 必须显式返回
endfunction
//提前退出
function void check(int a);
if (a == 0) return; // 提前退出
$display("Valid value");
endfunction

隐式返回

  • 当函数名被赋值时,函数名即返回变量,无需显示return
1
2
3
4
5
6
7
8
9
10
11
12
typedef int array_t [5];  //自定义数组类型
array_t at;
function array_t init(input int start);
foreach (init[i])
init[i] = i + start;
endfunction

initial begin
at = init(5);
foreach (at[i])
$display("at[%0d] = %0d", i ,at[i]);
end

数组的返回

  • 上述数组或结构体的返回除上面的例子外,还可以用引用参数实现
1
2
3
4
5
6
7
8
9
10
11
function automatic void init (ref int f[5], input int start);
foreach (f[i])
f[i] = i + start;
endfunction

int fa[5];
initial begin
init(fa, 5);
foreach (fa[i])
$display("fa[%0d] = %0d", i, fa[i]);
end
  • 也可以将数组包装到一个中,然后返回对象的句柄
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ArrayWrapper;
int data[];

function new(input int size, input int start);
data = new[size];
foreach(data[i]) data[i] = i + start;
endfunction
endclass

function ArrayWrapper create_wrapped_array(input int size, input int start);
return new(size, start); // 返回新对象句柄
endfunction

// 测试代码
initial begin
ArrayWrapper obj;
obj = create_wrapped_array(5, 3); // 初始化数组 [3,4,5,6,7]
foreach(obj.data[i])
$display("obj.data[%0d] = %0d", i, obj.data[i]);
end