以下是SystemVerilog断言(SVA)的详细解析,包含立即断言与并发断言的核心概念、实现方式、应用场景及完整仿真示例:
always
、initial
)中直接执行,当代码执行到该行时立即检查条件是否成立,无需依赖时钟信号。 always_comb begin
assert_handshake: assert (req -> ack) else $error("握手失败");
end
当 req
信号有效时,必须立即检测 ack
是否有效,否则报错。
property
和 assert property
定义。 property p_data_stable;
@(posedge clk) disable iff (reset) $stable(data);
endproperty
assert_data: assert property (p_data_stable);
每个时钟上升沿检查 data
是否稳定(除非复位)。
类型 | 立即断言 | 并发断言 |
---|---|---|
触发时机 | 在程序执行到该语句时立即检查 | 持续监控信号,与时钟同步 |
适用场景 | 组合逻辑条件检查 | 时序逻辑检查(跨时钟周期) |
语法结构 | assert (condition) |
assert property (@(event) sequence) |
仿真行为 | 类似普通if判断 | 生成状态机监控信号 |
错误报告 | 立即报错 | 在时钟边沿报错 |
立即断言实现
assert (条件) [else 动作]
,可嵌入过程块中。if
检查,但失败时会触发仿真错误。 always @(posedge clk) begin
if (start) begin
assert (data_valid) else $error("启动时数据无效");
end
end
启动时若 data_valid
无效则报错。
always_comb begin
// 立即断言:检查写指针不越界
assert (wr_ptr < FIFO_DEPTH)
else $error("Write pointer overflow!");
end
立即断言适用场景
组合逻辑检查:如输入信号约束(if (a > 0) assert (b != 0)
)。
实时错误捕捉:如握手协议的非时序性检查。
组合逻辑检查:输入信号合法性检查
assert (a != b) else $error("a and b cannot be equal!");
assert (data !== 'x) else $warning("Data is undefined!");
立即断言误用
误区:在时序逻辑中使用立即断言,导致竞争条件(如未同步到时钟边沿)。
正确做法:仅用于组合逻辑或非时钟依赖的检查。
组合逻辑环路:断言条件影响被检查信号
// 错误示例:可能导致组合环路
always_comb begin
assert (data == processed_data) else data = 0;
end
VALID
与READY
的时序关系。并发断言实现
sequence
描述时序事件;property
封装序列和触发条件;assert property
绑定属性。 sequence s_ack_delay;
req ##[1:3] ack;
endsequence
property p_ack_valid;
@(posedge clk) req |-> s_ack_delay;
endproperty
assert_ack: assert property (p_ack_valid);
请求 req
发出后,1~3周期内必须收到应答 ack
。
// 并发断言:AXI协议握手检查
property axi_handshake;
@(posedge clk)
$rose(valid) |-> ##[1:5] $rose(ready);
endproperty
assert property (axi_handshake);
并发断言适用场景
协议验证:如 AXI 总线中的时序序列检查(addr
有效后 data
必须在指定周期内有效)。
状态机监控:确保状态转换符合预期(如状态 S1
必须跳转到 S2
而非 S3
)。
协议时序检查:
// AXI写响应必须在写请求后1-8周期返回
property axi_write_response;
@(posedge clk)
$rose(awvalid) |-> ##[1:8] $rose(bvalid);
endproperty
// 状态机必须从IDLE跳转到WORK后再进入DONE
property fsm_transition;
@(posedge clk)
(state == IDLE) |=> (state == WORK) |=> (state == DONE);
endproperty
并发断言陷阱
synchronize
或 cross
处理跨时钟域信号。 property p_cross_clock;
@(posedge clk1) $rose(sig1) |-> ##[1:2] @(posedge clk2) sig2;
endproperty
跨时钟域信号需明确时序窗口。
// 错误:缺少时钟声明
property p; a |-> b; endproperty
// 错误:仅检查最小延迟,未覆盖最大延迟
a |-> ##2 b;
// 当FIFO满时,写使能必须无效
property fifo_full_check;
@(posedge clk)
fifo_full |-> !write_enable;
endproperty
assert property (fifo_full_check);
// 状态机不能直接从IDLE跳转到ERROR
property fsm_illegal_transition;
@(posedge clk)
(state == IDLE) |=> (state != ERROR);
endproperty
assert property (fsm_illegal_transition);
req
拉高时,ack
必须同一时刻拉高。 always_comb begin
if (req) begin
assert_ack: assert (ack) else $error("req激活时ack未响应");
end
end
错误示例:若 req
与 ack
有延迟,立即断言会误报。
data
需在 2 个周期内保持稳定。 property p_data_post_reset;
@(posedge clk) disable iff (reset)
$fell(reset) |-> ##2 $stable(data);
endproperty
assert_data: assert property (p_data_post_reset);
复位结束后的两个周期内,data
不得变化。
要求:检查FIFO满时push操作被阻塞。
代码:
property fifo_full_block_push;
@(posedge clk) fifo_full |-> !fifo_push; // FIFO满时禁止push:ml-citation{ref="2" data="citationList"}
endproperty
assert property (fifo_full_block_push);
解析:通过|->
确保条件成立时后续动作符合预期。
module sva_demo;
bit clk;
logic req, ack;
// 时钟生成
always #5 clk = ~clk;
// 并发断言:req拉高后ack必须在1-3周期内响应
property req_ack_check;
@(posedge clk)
$rose(req) |-> ##[1:3] $rose(ack);
endproperty
assert property (req_ack_check) else $error("ACK timeout!");
// 测试逻辑
initial begin
// 正常场景
#10 req = 1;
#10 ack = 1; // 2周期后响应(符合要求)
#20;
// 异常场景
req = 0; ack = 0;
#10 req = 1;
#40 ack = 1; // 4周期后响应(触发断言报错)
#20 $finish;
end
endmodule
xrun -sv sva_demo.sv -access +rw -coverage all
Error: Assertion 'req_ack_check' failed at 65ns
代码示例:
module sva_demo (
input logic clk, reset, req, ack,
input logic [7:0] data
);
// 立即断言:检查握手信号
always_comb begin
if (req) begin
assert_handshake: assert (ack) else $error("Error: ack未响应");
end
end
// 并发断言:检查复位后数据稳定
property p_data_stable;
@(posedge clk) disable iff (reset)
$fell(reset) |-> ##2 $stable(data);
endproperty
assert_data: assert property (p_data_stable);
endmodule
xrun -sv sva_demo.sv -access +rwc -coverage all
启用代码覆盖率和断言检查。
$error
信息。imc
工具)可显示断言触发情况。module fifo_assertions(
input clk, rst_n,
input wr_en, rd_en,
input [7:0] wdata, rdata,
input full, empty
);
// 立即断言:写满时禁止写入
always @(posedge clk) begin
if (full) begin
assert (!wr_en) else $error("满状态写入!");
end
end
// 并发断言:连续写不越界
property p_write_flow;
@(posedge clk) disable iff (!rst_n)
wr_en && !full |=> !full until rd_en;
endproperty
assert property(p_write_flow);
endmodule
fifo_assertions.sv # 断言模块
tb_fifo.sv # 测试平台
xrun -64bit -access +rwc \
-sv fifo_assertions.sv tb_fifo.sv \
+define+ASSERT_ON \
-covoverwrite \
-nowarn UEXPSC
Assertion failed: fifo_assertions.p_write_flow
Time: 550ns Scope: tb_fifo.fifo_assertions
表示在550ns时检测到写操作越界
// 覆盖req到ack的延迟周期数
covergroup ack_latency_cg @(posedge clk);
latency: coverpoint $time - req_time {
bins fast = {1,2};
bins slow = {3};
}
endgroup
xrun -covoverwrite sva_demo.sv
imc -exec 'load_test sva_demo; report_coverage'
立即断言适用于组合逻辑的即时检查,并发断言擅长时序协议监控。
关键技巧:
|->
(重叠蕴含)和|=>
(非重叠蕴含)区分时序关系通过合理使用SVA,可显著提高验证效率和设计可靠性。