目录

16.-断言SVA立即断言并发断言

16. 断言(SVA):立即断言/并发断言


前言

以下是SystemVerilog断言(SVA)的详细解析,包含立即断言与并发断言的核心概念、实现方式、应用场景及完整仿真示例:


一、断言类型对比

  1. 立即断言(Immediate Assertions)
    • 概念 :立即断言在过程块(如 alwaysinitial )中直接执行,当代码执行到该行时立即检查条件是否成立, 无需依赖时钟信号
    • 通俗理解 :就像在代码中临时插入一个“检查点”,只要执行到这里,就立刻验证某个条件是否满足。
    • 示例
     always_comb begin
       assert_handshake: assert (req -> ack) else $error("握手失败");
     end

req 信号有效时,必须立即检测 ack 是否有效,否则报错

  1. 并发断言(Concurrent Assertions)
    • 概念 :基于时钟周期检查时序逻辑,跨周期验证属性(如信号序列),需通过 propertyassert 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判断生成状态机监控信号
错误报告立即报错在时钟边沿报错

二、立即断言详解

1. 概念与通俗理解

  • 作用 :在代码执行到断言位置时立即检查条件,类似“岗亭检查员”。
  • 示例 :检查FIFO写操作时,写指针是否超过深度限制。

2. 实现方式

立即断言实现

  • 语法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

3. 应用场景

立即断言适用场景

  • 组合逻辑检查 :如输入信号约束( 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!");

4. 常见误区

立即断言误用

  • 误区 :在时序逻辑中使用立即断言,导致竞争条件(如未同步到时钟边沿)。
  • 正确做法 :仅用于组合逻辑或非时钟依赖的检查。
  • 组合逻辑环路 :断言条件影响被检查信号
  // 错误示例:可能导致组合环路
  always_comb begin
      assert (data == processed_data) else data = 0;
  end

三、并发断言详解

1. 概念与通俗理解

  • 作用 :持续监控信号时序关系,类似“交通摄像头”。
  • 示例 :检查AXI握手信号 VALIDREADY 的时序关系。

2. 实现方式

并发断言实现

  • 步骤
    1. 定义 sequence 描述时序事件;
    2. 通过 property 封装序列和触发条件;
    3. 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);

3. 应用场景

并发断言适用场景

  • 协议验证 :如 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

4. 常见误区

并发断言陷阱

  • 误区 :忽略多时钟域同步问题,导致断言误报。
  • 正确做法 :使用 synchronizecross 处理跨时钟域信号。
  • 示例
     property p_cross_clock;
       @(posedge clk1) $rose(sig1) |-> ##[1:2] @(posedge clk2) sig2;
     endproperty

跨时钟域信号需明确时序窗口

  • 时钟域错误 :未正确指定采样时钟
  // 错误:缺少时钟声明
  property p; a |-> b; endproperty
  • 覆盖不全 :未考虑所有可能时序窗口
  // 错误:仅检查最小延迟,未覆盖最大延迟
  a |-> ##2 b;

四、练习任务

任务1:FIFO满标志检查

// 当FIFO满时,写使能必须无效
property fifo_full_check;
    @(posedge clk) 
    fifo_full |-> !write_enable;
endproperty
assert property (fifo_full_check);

任务2:状态机合法跳转

// 状态机不能直接从IDLE跳转到ERROR
property fsm_illegal_transition;
    @(posedge clk)
    (state == IDLE) |=> (state != ERROR);
endproperty
assert property (fsm_illegal_transition);

任务3:立即断言验证握手协议

  • 要求 :当 req 拉高时, ack 必须同一时刻拉高。
  • 代码
  always_comb begin
    if (req) begin
      assert_ack: assert (ack) else $error("req激活时ack未响应");
    end
  end

错误示例:若 reqack 有延迟,立即断言会误报

任务4:并发断言验证数据稳定性

  • 要求 :复位后, 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 不得变化

任务5:设计FIFO的并发断言‌

要求‌:检查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);  

解析‌:通过 |-> 确保条件成立时后续动作符合预期。


五、完整仿真示例

示例一:寄存器ack检查

1. 代码示例

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

2. 仿真指令

xrun -sv sva_demo.sv -access +rw -coverage all

3. 预期输出

Error: Assertion 'req_ack_check' failed at 65ns

示例二:完整示例与xrun仿真

1. 代码示例

代码示例

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

2. 仿真步骤

  1. 编译与仿真命令
   xrun -sv sva_demo.sv -access +rwc -coverage all

启用代码覆盖率和断言检查

  1. 查看结果
    • 断言失败时,日志会输出 $error 信息。
    • 覆盖率报告(如 imc 工具)可显示断言触发情况。

示例三:设计FIFO空满保护断言

1. 代码示例

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

2. 仿真验证

  1. 文件结构
    fifo_assertions.sv    # 断言模块
    tb_fifo.sv            # 测试平台
  1. 使用xrun仿真:

xrun -64bit -access +rwc \
    -sv fifo_assertions.sv tb_fifo.sv \
    +define+ASSERT_ON \
    -covoverwrite \
    -nowarn UEXPSC
  1. 典型输出解析:
Assertion failed: fifo_assertions.p_write_flow
  Time: 550ns  Scope: tb_fifo.fifo_assertions

表示在550ns时检测到写操作越界


六、覆盖率收集

1. 功能覆盖率与断言结合

// 覆盖req到ack的延迟周期数
covergroup ack_latency_cg @(posedge clk);
    latency: coverpoint $time - req_time {
        bins fast = {1,2};
        bins slow = {3};
    }
endgroup

2. 查看覆盖率报告

xrun -covoverwrite sva_demo.sv
imc -exec 'load_test sva_demo; report_coverage'

七、总结

立即断言 适用于组合逻辑的即时检查, 并发断言 擅长时序协议监控。

关键技巧

  1. 为并发断言显式指定时钟
  2. 使用 |-> (重叠蕴含)和 |=> (非重叠蕴含)区分时序关系
  3. 结合覆盖率分析断言触发场景

通过合理使用SVA,可显著提高验证效率和设计可靠性。