com um clique
assertion-based-verification
断言验证技能 - SVA (SystemVerilog Assertion) 断言设计和验证最佳实践
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
断言验证技能 - SVA (SystemVerilog Assertion) 断言设计和验证最佳实践
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
物理设计全流程技能 - 从floorplan到GDSII,完整数字芯片物理设计指导
芯片架构设计技能 - 从需求定义到模块划分,完整指导芯片架构开发
EDA工具脚本开发技能 - TCL/Python/Makefile 自动化流程最佳实践
功能安全分析技能 - FMEDA 分析、安全机制设计、ISO 26262 符合
静态时序分析技能 - 时序诊断、收敛优化、签收标准
UVM 验证方法论技能 - 从验证计划到功能覆盖率,完整 UVM 验证平台开发
| name | assertion-based-verification |
| description | 断言验证技能 - SVA (SystemVerilog Assertion) 断言设计和验证最佳实践 |
| description.zh | 断言验证技能 - SVA (SystemVerilog Assertion) 断言设计和验证最佳实践 |
| origin | ICER |
| categories | verification |
当用户需要使用 SVA (SystemVerilog Assertion) 进行断言验证时,启用此技能。提供断言设计、放置、调试最佳实践。
立即断言:
// 立即断言:在过程块中
always_comb begin
// 检查条件必须满足
assert (fifo_full || !wr_en) else
$error("Write when FIFO full!");
end
并发断言:
// 并发断言:描述时序属性
property req_ack;
@(posedge clk) req |-> ##[1:10] ack;
endproperty
assert property(req_ack);
| 场景 | 断言类型 | 说明 |
|---|---|---|
| 检查组合逻辑条件 | 立即断言 | 立即检查 |
| 检查协议时序 | 并发断言 | 描述时序序列 |
| 检查状态机转换 | 并发断言 | 描述状态序列 |
| 检查复位期间状态 | 并发断言 | 带条件禁用 |
| 检查不变量 | 并发断言 | 始终为真的属性 |
做什么: 编写最基本的状态检查断言。
// 检查复位后状态正确
assert property (@(posedge clk) !rst_n |-> state == ST_IDLE)
else $error("State not IDLE during reset");
// 检查 FIFO 不空时才能读
assert property (@(posedge clk) rd_en |-> !empty)
else $error("Read when FIFO empty");
// 检查 FIFO 不满时才能写
assert property (@(posedge clk) wr_en |-> !full)
else $error("Write when FIFO full");
做什么: 使用 |-> 和 |=> 描述因果关系。
| 操作符 | 含义 | 示例 |
|---|---|---|
| ` | ->` | 同一周期 |
| ` | =>` | 下一周期 |
// 请求后必须有应答(1-10 周期)
property req_ack;
@(posedge clk) req |-> ##[1:10] ack;
endproperty
assert property(req_ack);
// 请求后下一周期开始处理
property req_start;
@(posedge clk) req |=> processing;
endproperty
assert property(req_start);
做什么: 使用序列操作符描述复杂时序。
| 操作符 | 含义 | 示例 |
|---|---|---|
##n | 延迟 n 周期 | a ##3 b:a 后 3 周期 b |
##[m:n] | 延迟 m-n 周期范围 | a ##[1:5] b:a 后 1-5 周期 b |
##[*] | 延迟任意周期 | a ##[*] b:a 后任意周期 b |
##[+] | 延迟至少 1 周期 | a ##[+] b:a 后至少 1 周期 b |
// 写后必须等待 2 周期才能读
property wr_rd_delay;
@(posedge clk) wr_en |-> ##2 rd_en;
endproperty
// 请求后 1-10 周期必须有应答
property req_ack_range;
@(posedge clk) req |-> ##[1:10] ack;
endproperty
// 开始后最终必须结束
property start_end;
@(posedge clk) start |-> ##[*] done;
endproperty
场景: 请求发生后必须在一定周期内得到应答。
// 请求后 1-10 周期必须有应答
property req_must_ack;
@(posedge clk) req |-> ##[1:10] ack;
endproperty
assert property(req_must_ack)
else $error("No ACK received within 10 cycles after REQ");
// 请求必须保持直到应答
property req_stable_until_ack;
@(posedge clk) $rose(req) |-> req throughout (##[1:$] ack);
endproperty
assert property(req_stable_until_ack)
else $error("REQ must be stable until ACK");
场景: valid/ready 握手协议。
// valid 为高时,data 必须稳定
property valid_data_stable;
@(posedge clk) valid |-> $stable(data);
endproperty
assert property(valid_data_stable)
else $error("DATA must be stable when VALID is high");
// ready 为低时,valid 不能变为高(背压)
property backpressure;
@(posedge clk) !ready |-> !valid;
endproperty
assert property(backpressure)
else $error("VALID must be low when READY is low");
// valid 和 ready 同时为高时,传输成功
property handshake_success;
@(posedge clk) (valid && ready) |-> ##1 !valid;
endproperty
// 可选:取决于协议
场景: 两个信号不能同时为高。
// 读写不能同时发生
property rw_exclusive;
@(posedge clk) not (rd_en && wr_en);
endproperty
assert property(rw_exclusive)
else $error("Read and Write cannot happen simultaneously");
// 两个状态机不能同时活跃
property state_mutex;
@(posedge clk) not (state_a_active && state_b_active);
endproperty
assert property(state_mutex);
场景: 检查状态机只能进行合法转换。
// 定义合法状态转换
property legal_state_transition;
@(posedge clk)
state == ST_IDLE |-> ##1 state inside {ST_IDLE, ST_DATA, ST_ERROR};
endproperty
assert property(legal_state_transition)
else $error("Illegal state transition from IDLE");
// 不能直接从 IDLE 到 DONE
property no_idle_to_done;
@(posedge clk) not (state == ST_IDLE ##1 state == ST_DONE);
endproperty
assert property(no_idle_to_done);
场景: FIFO 相关属性检查。
// FIFO 不满才能写
property wr_when_not_full;
@(posedge clk) wr_en |-> !full;
endproperty
assert property(wr_when_not_full)
else $error("Write when FIFO full");
// FIFO 不空才能读
property rd_when_not_empty;
@(posedge clk) rd_en |-> !empty;
endproperty
assert property(rd_when_not_empty)
else $error("Read when FIFO empty");
// FIFO 满时不能再写
property no_wr_when_full;
@(posedge clk) full |-> !wr_en;
endproperty
assert property(no_wr_when_full);
做什么: 复位期间断言不检查。
// 复位期间禁用断言
property reset_correct;
@(posedge clk) disable iff (!rst_n)
!rst_n ##1 rst_n |-> state == ST_IDLE;
endproperty
assert property(reset_correct)
else $error("State not IDLE after reset");
做什么: 检查复位释放后状态正确。
// 复位释放后状态必须正确
property state_after_reset;
@(posedge clk)
!rst_n ##1 rst_n |-> (state == ST_IDLE && counter == 0);
endproperty
assert property(state_after_reset)
else $error("State not correct after reset release");
做什么: 在接口协议层放置断言,检查协议遵守。
module axi_interface (
input wire clk,
input wire rst_n,
// AXI 信号
input wire [31:0] awaddr,
input wire awvalid,
output logic awready,
// ...
);
// AXI 协议断言
// AWVALID 必须保持直到 AWREADY
property aw_stable;
@(posedge clk) disable iff (!rst_n)
awvalid |-> awvalid throughout (##[1:$] awready);
endproperty
assert property(aw_stable);
// WVALID 必须在 AWVALID 之后或同时
property w_after_aw;
@(posedge clk) disable iff (!rst_n)
$rose(awvalid) |-> ##[0:$] wvalid;
endproperty
assert property(w_after_aw);
// BVALID 必须在 WVALID 和 WREADY 之后
property b_after_w;
@(posedge clk) disable iff (!rst_n)
(wvalid && wready) |-> ##[1:$] bvalid;
endproperty
assert property(b_after_w);
endmodule
做什么: 检查状态机转换正确性。
module state_machine (
input wire clk,
input wire rst_n,
input wire start,
input wire done,
output logic [2:0] state
);
// 状态定义
localparam ST_IDLE = 3'b000;
localparam ST_DATA = 3'b001;
localparam ST_WAIT = 3'b010;
localparam ST_DONE = 3'b011;
localparam ST_ERROR = 3'b100;
// 状态机逻辑
// ...
// 状态机断言
// IDLE 只能转换到 DATA 或 ERROR
property idle_transition;
@(posedge clk) disable iff (!rst_n)
state == ST_IDLE |-> ##1 state inside {ST_IDLE, ST_DATA, ST_ERROR};
endproperty
assert property(idle_transition);
// DATA 只能转换到 WAIT 或 ERROR
property data_transition;
@(posedge clk) disable iff (!rst_n)
state == ST_DATA |-> ##1 state inside {ST_WAIT, ST_ERROR};
endproperty
assert property(data_transition);
// 必须从 IDLE 开始
property start_from_idle;
@(posedge clk) disable iff (!rst_n)
$rose(start) |-> state == ST_IDLE;
endproperty
assert property(start_from_idle);
endmodule
做什么: 收集断言覆盖率,确保所有断言都触发过。
// 收集断言覆盖率
cover property (@(posedge clk) req && ack);
cover property (@(posedge clk) fifo_full);
cover property (@(posedge clk) state == ST_ERROR);
// 收集序列覆盖率
sequence s_req_ack;
@(posedge clk) req ##[1:10] ack;
endsequence
cover sequence(s_req_ack);
做什么: 断言失败时打印详细信息。
property req_ack;
@(posedge clk) req |-> ##[1:10] ack;
endproperty
assert property(req_ack)
else begin
$error("REQ-ACK protocol violation at time %0t", $time);
$display("REQ = %b, ACK = %b", req, ack);
end
做什么: 在特定情况下禁用断言。
// 禁用所有断言
initial begin
$assertoff(); // 禁用
// 复位期间
#100;
$asserton(); // 启用
end
// 禁用特定断言
assert property(req_ack)
else $error("...");
// 可以在仿真中禁用特定断言
做什么: 检查所有断言是否都被触发过。
// 在仿真结束时打印覆盖率
final begin
$display("Assertion Coverage Report:");
// 工具特定命令
end
| 断言类型 | 放置位置 |
|---|---|
| 协议断言 | 接口模块 |
| 状态机断言 | 状态机模块 |
| 数据完整性断言 | 数据通路模块 |
| 不变量断言 | 相关模块内部 |
// ✅ 好:断言有有意义的名字
assert property(req_ack)
else $error("...");
// 名字自动为 req_ack
// ✅ 好:属性有有意义的名字
property p_req_must_get_ack;
@(posedge clk) req |-> ##[1:10] ack;
endproperty
assert property(p_req_must_get_ack);
// ❌ 不好:断言没有名字,难调试
assert property(@(posedge clk) req |-> ack);
规则: 每个断言只检查一个属性,不要过于复杂。
// ❌ 不好:一个断言检查多个属性
assert property(@(posedge clk)
req |-> ##[1:10] ack && data_valid && !error);
// ✅ 好:分开检查
assert property(@(posedge clk) req |-> ##[1:10] ack);
assert property(@(posedge clk) ack |-> data_valid);
assert property(@(posedge clk) ack |-> !error);
场景: 使用形式验证工具证明断言在所有输入下都成立。
适用场景:
不适用场景:
// 形式验证友好的断言
// 1. 有界属性更好(##[1:10] 比 ##[*] 更好)
// 2. 避免复杂序列组合
// 3. 明确时钟和复位
property bounded_check;
@(posedge clk) disable iff (!rst_n)
req |-> ##[1:10] ack; // 有界,形式验证友好
endproperty
断言添加完成后检查:
❌ 断言过于复杂:一个断言检查太多属性
// ❌ 不好
assert property(@(posedge clk) req |-> ack && data_valid && !error);
// ✅ 好:分开
assert property(@(posedge clk) req |-> ack);
assert property(@(posedge clk) ack |-> data_valid);
❌ 忘记处理复位:复位期间断言会失败
// ❌ 不好:复位期间也检查
assert property(@(posedge clk) state == ST_IDLE);
// ✅ 好:复位期间禁用
assert property(@(posedge clk) disable iff (!rst_n) state == ST_IDLE);
❌ 无界属性:形式验证无法处理
// ❌ 形式验证困难
assert property(@(posedge clk) req |-> ##[*] ack);
// ✅ 好:有界
assert property(@(posedge clk) req |-> ##[1:100] ack);
❌ 没有收集覆盖率:不知道断言是否触发
// 添加 cover
cover property (@(posedge clk) req && ack);
| 工具 | 公司 | 用途 |
|---|---|---|
| VCS | Synopsys | 仿真 + 断言检查 |
| Xcelium | Cadence | 仿真 + 断言检查 |
| Questa | Siemens | 仿真 + 断言检查 |
| VC Formal | Synopsys | 形式验证 |
| JasperGold | Cadence | 形式验证 |
| Verilator | 开源 | Lint + 基本断言 |
verification-engineer 代理开发验证平台