SV过程与子程序
过程语句
概念
initial
语句- 用于初始化行为建模,仅在仿真开始时执行一次
always
语句- 用于持续执行的行为建模,仿真过程中重复执行
- 需要明确列出敏感事件列表
- SV扩展了
always
块always_comb
:自动推断组合逻辑敏感列表,避免遗漏信号always_ff
:明确用于时序逻辑,需指定时钟和复位条件always_latch
:专为锁存器设计,明确设计意图,提高可维护性
赋值规则
- 阻塞赋值
=
- 按代码顺序立即执行,适用于组合逻辑建模
- 执行时后续语句需等待当前赋值完成
- 非阻塞赋值
<=
- 赋值操作并行执行,适用于时序逻辑
- 在时钟边沿捕获当前值,结束后统一更新结果,避免竞争风险
- 限制
- 过程赋值的被赋值变量必须是寄存器类型(
reg
或logic
),并且只能在过程块中使用 - 区别于连续赋值
assign
只能驱动线网类型
- 过程赋值的被赋值变量必须是寄存器类型(
1 | module xor_comb( |
子程序
任务与函数
- 任务用于封装可包含时序控制的行为代码块
- 延时
#
、事件触发@
、等待wait
- 延时
- 无返回值,支持
input
、output
、inout
参数- 无输入输出时空括号可省略
- 只有输入时,
input
可省略
begin ... end
不再是必需的,但必须使用通用的输入类型logic
1 | task send_data(input logic [7:0] data, output bit ack); |
- 函数用于纯计算或组合逻辑,无任何时序控制,执行时间为0
- 必须至少有一个输入参数,通过函数名返回单一值
1 | function int add(input int a, b); |
子程序参数
引用传参
- 在SV中,参数的传递除了可以使用
input
、output
、inout
进行复制外,还可以使用引用的方式传参 - 核心:使用
ref
实现对变量的直接引用(类似指针)- 避免大型数据(如结构体、数组)的复制开销,提升性能
- 子程序内部对ref参数的修改立即可见于外部调用者
- 使用规则
- 只使用于变量,不支持线网类型
- 多个并发子程序通过
ref
访问同一变量时,可能导致数据竞争- 为避免竞争,可以使用旗语或信箱等方式进行同步或传递副本
- 与
const
联合使用,声明可强制引用为只读,防止意外修改
1 | function void print_sum(const ref int arr[]); |
参数的缺省值
- 在声明函数时为部分或全部参数预设缺省值(默认值),当调用函数时,若未显示传入,则使用缺省值
- 使用-1或其他任何越界值作为缺省值,便于获知调用时是否有指定值
- 显示指定参数名
.parameter_name(value)
- 使用名字进行传参,允许直接关联参数名与值,而不依赖参数在模块定义中的声明顺序
1 | function automatic void print_csm(const ref bit [31:0] a[], |
子程序的返回
显式返回
void
函数无返回值,非void
函数必须提供返回值return
关键词适用于单个返回值的函数- 多返回值需用
output
或结构体
- 在任何函数中,
return
语句都可用于发现错误提前终止退出- SV支持
return
关键词灵活退出,而Verilog仅能在endfunciton
处返回
- SV支持
1 | //显示返回 |
隐式返回
- 当函数名被赋值时,函数名即返回变量,无需显示
return
1 | typedef int array_t [5]; //自定义数组类型 |
数组的返回
- 上述数组或结构体的返回除上面的例子外,还可以用引用参数实现
1 | function automatic void init (ref int f[5], input int start); |
- 也可以将数组包装到一个类中,然后返回对象的句柄
1 | class ArrayWrapper; |