尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从仲裁器实战出发:手把手教你用SystemVerilog SVA写断言(附完整代码)

从仲裁器实战出发:手把手教你用SystemVerilog SVA写断言(附完整代码) 从仲裁器实战出发手把手教你用SystemVerilog SVA写断言附完整代码在数字芯片验证领域断言Assertion已经成为保障设计可靠性的重要手段。本文将从一个真实的仲裁器案例出发带你深入理解如何用SystemVerilog AssertionSVA编写高效验证代码。不同于传统的概念讲解我们将采用代码驱动的方式让你在实战中掌握SVA的精髓。1. 仲裁器设计与验证需求分析我们以一个四端口仲裁器为例它具有以下核心功能接收4个代理agent的请求信号req[3:0]根据操作码opcode输出授权信号gnt[3:0]检测非法操作码并输出错误信号op_errortypedef enum logic[2:0] { NOP, FORCE0, FORCE1, FORCE2, FORCE3, ACCESS_OFF, ACCESS_ON } t_opcode; module arbiter( input logic [3:0] req, input t_opcode opcode, input logic clk, rst, output logic [3:0] gnt, output logic op_error );1.1 验证目标分解针对这个仲裁器我们需要验证以下几个关键属性安全属性确保不会同时授权给多个代理公平性在NOP模式下请求应按轮询方式处理功能正确性强制授权模式应正确响应错误处理非法操作码应触发错误信号2. SVA基础与实战技巧2.1 并发断言的基本结构SVA断言的核心是并发断言其基本语法如下assert_name: assert property ( (posedge clk) disable iff (rst) property_expression ) else $error(Error message);关键要素时钟定义(posedge clk)复位条件disable iff (rst)属性表达式描述期望的行为错误处理else $error2.2 采样值与时钟边沿理解采样值是编写正确断言的关键。在时钟上升沿SVA检查的是前一个时间步的信号值// 正确的授权检查 safe_grant: assert property ( (posedge clk) gnt[0] - req[0] ) else $error(Agent 0 granted without request);这个断言检查的是如果gnt[0]为高那么在前一个时钟周期req[0]必须为高。3. 仲裁器核心断言实现3.1 安全属性验证首先确保仲裁器最基本的安全属性 - 不会同时授权给多个代理// 安全属性1最多只有一个授权 safety_onehot: assert property ( $onehot0(gnt) ) else $error(Multiple grants detected); // 安全属性2授权必须对应请求 generate for (genvar i 0; i 4; i) begin grant_check: assert property ( gnt[i] |- req[i] ) else $error(Grant without request for agent %0d, i); end endgenerate3.2 操作码功能验证针对不同的操作码我们需要验证其功能正确性// FORCE模式验证 generate for (genvar i 0; i 4; i) begin force_check: assert property ( (opcode t_opcode(FORCE0 i)) | gnt[i] ) else $error(Force mode failed for agent %0d, i); end endgenerate // ACCESS_OFF模式验证 access_off_check: assert property ( (opcode ACCESS_OFF) | (gnt 4b0) ) else $error(ACCESS_OFF mode failed); // 操作码错误检测 op_error_check: assert property ( !(opcode inside {FORCE0,FORCE1,FORCE2,FORCE3,ACCESS_OFF,ACCESS_ON,NOP}) | op_error ) else $error(Opcode error not detected);3.3 轮询公平性验证NOP模式下的轮询算法是验证的重点和难点// 辅助函数计算下一个应该授权的代理 function logic [3:0] get_next_grant(logic [3:0] req, logic [3:0] gnt); if (gnt 4b0) begin // 无当前授权选择最低优先级请求 case (1b1) req[0]: return 4b0001; req[1]: return 4b0010; req[2]: return 4b0100; req[3]: return 4b1000; default: return 4b0000; endcase end else begin // 有当前授权选择下一个请求 case (1b1) gnt[0]: begin if (req[1]) return 4b0010; else if (req[2]) return 4b0100; else if (req[3]) return 4b1000; else if (req[0]) return 4b0001; else return 4b0000; end gnt[1]: begin if (req[2]) return 4b0100; else if (req[3]) return 4b1000; else if (req[0]) return 4b0001; else if (req[1]) return 4b0010; else return 4b0000; end gnt[2]: begin if (req[3]) return 4b1000; else if (req[0]) return 4b0001; else if (req[1]) return 4b0010; else if (req[2]) return 4b0100; else return 4b0000; end gnt[3]: begin if (req[0]) return 4b0001; else if (req[1]) return 4b0010; else if (req[2]) return 4b0100; else if (req[3]) return 4b1000; else return 4b0000; end endcase end endfunction // 轮询公平性验证 round_robin_check: assert property ( (opcode NOP) (|req) (##1 $past(|req)) |- ##1 (gnt get_next_grant($past(req), $past(gnt))) ) else $error(Round robin policy violation);4. 高级SVA技巧与调试方法4.1 覆盖点设计良好的覆盖点是验证完整性的重要指标// 基本功能覆盖 generate for (genvar i 0; i 4; i) begin // 各代理请求授权覆盖 cover_agent: cover property ( req[i] ##[1:10] gnt[i] ); // 强制模式覆盖 cover_force: cover property ( (opcode t_opcode(FORCE0 i)) gnt[i] ); end endgenerate // 特殊场景覆盖 cover_all_requests: cover property ( req 4b1111 ); cover_no_requests: cover property ( req 4b0000 ); cover_op_error: cover property ( !(opcode inside {FORCE0,FORCE1,FORCE2,FORCE3,ACCESS_OFF,ACCESS_ON,NOP}) ##1 op_error );4.2 调试技巧当断言失败时有效的调试方法能节省大量时间使用$past调试时序问题debug_grant: assert property ( gnt[0] |- $past(req[0]) ) else $error(Grant0%b, PastReq0%b, gnt[0], $past(req[0]));临时添加辅助信号logic [3:0] expected_gnt; always_ff (posedge clk) begin if (opcode NOP) expected_gnt get_next_grant(req, gnt); else expected_gnt 4b0; end使用SVA的assume限制输入空间assume_req_stable: assume property ( $stable(req) || $changed(req) $rose(clk) );5. 完整断言代码示例以下是仲裁器验证环境的完整断言模块module arbiter_assertions( input logic [3:0] req, input t_opcode opcode, input logic clk, rst, input logic [3:0] gnt, input logic op_error ); default clocking (posedge clk); endclocking default disable iff (rst); // 1. 安全属性 safety_onehot: assert property ($onehot0(gnt)) else $error(Multiple grants detected); generate for (genvar i 0; i 4; i) begin grant_check: assert property (gnt[i] |- req[i]) else $error(Grant without request for agent %0d, i); end endgenerate // 2. 操作码功能验证 generate for (genvar i 0; i 4; i) begin force_check: assert property ( (opcode t_opcode(FORCE0 i)) | gnt[i] ) else $error(Force mode failed for agent %0d, i); end endgenerate access_off_check: assert property ( (opcode ACCESS_OFF) | (gnt 4b0) ) else $error(ACCESS_OFF mode failed); op_error_check: assert property ( !(opcode inside {FORCE0,FORCE1,FORCE2,FORCE3,ACCESS_OFF,ACCESS_ON,NOP}) | op_error ) else $error(Opcode error not detected); // 3. 轮询公平性验证 function logic [3:0] get_next_grant(logic [3:0] req, logic [3:0] gnt); // ... (同上文函数实现) endfunction round_robin_check: assert property ( (opcode NOP) (|req) (##1 $past(|req)) |- ##1 (gnt get_next_grant($past(req), $past(gnt))) ) else $error(Round robin policy violation); // 4. 覆盖点 generate for (genvar i 0; i 4; i) begin cover_agent: cover property (req[i] ##[1:10] gnt[i]); cover_force: cover property ( (opcode t_opcode(FORCE0 i)) gnt[i] ); end endgenerate cover_all_requests: cover property (req 4b1111); cover_no_requests: cover property (req 4b0000); cover_op_error: cover property ( !(opcode inside {FORCE0,FORCE1,FORCE2,FORCE3,ACCESS_OFF,ACCESS_ON,NOP}) ##1 op_error ); endmodule // 使用bind将断言模块连接到设计 bind arbiter arbiter_assertions arbiter_assertions_inst(.*);6. 形式验证与仿真协同在实际项目中SVA断言可以同时用于仿真和形式验证6.1 仿真环境中的断言在仿真中断言会实时监测设计行为在违反时立即报告错误收集覆盖点统计6.2 形式验证中的断言在形式验证中断言成为证明目标数学上证明断言在所有可能情况下都成立可以找出极端条件下的设计缺陷提供完整的覆盖证明6.3 断言优化建议性能考量// 性能较差的写法 poor_perf: assert property ( (req[0] !gnt[0])[*1:$] ##1 gnt[0] ); // 优化后的写法 better_perf: assert property ( req[0] !gnt[0] |- ##[1:32] gnt[0] );可读性提升// 定义辅助序列 sequence grant_within_32(agent); req[agent] !gnt[agent] |- ##[1:32] gnt[agent]; endsequence // 使用命名序列 fairness_check: assert property ( grant_within_32(0) and grant_within_32(1) and grant_within_32(2) and grant_within_32(3) );在实际项目中我们通常会遇到一些复杂的仲裁场景比如请求信号在不同时钟域的情况这时候就需要特别注意跨时钟域的断言处理。一个实用的技巧是使用同步器模型来验证跨时钟域信号的正确性但这需要根据具体设计需求来调整断言策略。
返回列表