1、过程断言刷新点
如果如下情况发生,被定义的过程(进程)会到达过程断言刷新点:
(1)由于到达事件控制或等待(wait)语句,而先前被暂停的进程将恢复执行。
(2)由always_comb和always_latch声明的进程,由于其一个从属信号上的转换,将恢复执行。
(3)进程的最外层由于disable语句被禁用。
以下示例显示了过程并发断言,如何固有地避免由于单个仿真时间步长中的过渡组合值,而导致的多个计算:
assign not_a = !a;
default clocking @(posedge clk); endclocking
always_comb begin : b1
// Probably better to not use consts in this example
// ...but using them to illustrate effects of flushing method
a1: assert property (const'(not_a) != const'(a));
end
在正时钟边沿出现的时间步长内,a发生改变,在评估的持续赋值后,仿真器就会评估断言a1两次:在一次是在a改变时,另一次是在not_a改变时。a1的第一次执行将被安排在进程的过程断言队列中,这将导致报告失败。当not_a改变时,由于b1的激活,过程断言队列被刷新,并且程序并发断言的新的待处理实例现在将使用正确的值排队,因此不会报告a1的失败。
下面这个例子说明了存在时间延迟时,过程并发断言的行为:
default clocking @(posedge clk); endclocking
always @(a or b) begin : b1
a2: assert property (a == b) r.success(0) else r.error(0, a, b);
#1;
a3: assert property (a == b) r.success(1) else r.error(1, a, b);
end
在这个情况下,由于延迟在程序中间,在a2队列后和更新点前,总是会观测到一个区域。因此a2总会成熟,对于a3,在时间步长期间,a或b在排队后发生更改,断言将始终从队列中刷新,并且永远不会成熟,一般来说,过程并发断言混合时间延迟时必须要谨慎使用。
下面这个例子说明了,用cover而不是assert的过程并发断言语句的典型使用方法:
assign a = ...;
assign b = ...;
default clocking @(posedge clk); endclocking
always_comb begin : b1
...
c1: cover property (const'(b) != const'(a));
end
在这个例子中,目标是确保某些测试覆盖了过程代码中a和b在该点具有不同值的情况。由于在仿真器中分配的任意顺序,可能存在这样的情况:在存在正时钟边缘且两个变量被分配相同值的循环中,b1在a被分配但b仍保持其先前值的情况下执行。因此c1将排队,但这实际上是一个小故障,可能不是有用的覆盖信息。但是,当下一次执行b1时(在b也被分配新值之后),该覆盖点将被刷新,并且当该覆盖点成熟时,c2将不会正确地被报告为在该时间步长期间已被覆盖。
2、过程并发断言和故障
断言执行的一个常见问题是小故障,即同一断言在一个时间步中执行多次,并报告尚未收到其步骤最终值的临时值的意外故障。一般来说,过程并发断言不受由于刷新机制而导致的过程执行顺序问题的影响,但仍受区域之间执行循环问题的影响。
例如,如果反应区域中的代码修改信号,并导致另一次传递到活动区域,这可能会产生一些故障行为,因为活动区域中的新通道可能会重新获得过程并发断言,并且可能会将第二次评估尝试添加到成熟的断言队列中。下面的代码说明了这种情况:
always_comb begin : procedural_block_1
if (en)
foo = bar;
end
always_comb begin : procedural_block_2
p1: assert property ( @(posedge clk) (const'(foo) == const'(bar)) );
end
假设bar在别处代码的时钟正边沿被分配了新值,且en为1,所以procedural_block_1的赋值发生;procedural_block_2在反应区会执行两次:一次是初始化为bar时执行;另一次在更新foo的赋值后执行。在第一次执行procedural_block_2时,p1的待处理实例将排队,如果断言成熟,将导致断言失败。
在上述同样的例子中,假设en为0,bar的值通过反应区的VPI(虚拟路径标识符 )代码赋给foo。在这个情况下,观察去已经发生,所以p1已经成熟并执行,由于foo和bar有不同值而报告断言失败。
在反应区后,procedural_block_2会在另外的反应区执行,将有另一个活动区域,其中程序块2将被执行,这次将通过一个新排队的p1实例。但这已经太晚了,无法阻止在时间步长的早期报告故障。
3、禁用过程并发断言
disable语句会与过程并发断言相互作用:
(1)可以禁用特定的过程并发断言,该断言的任何待处理过程实例会从队列中清除,其它断言的任何待处理过程实例都保留在队列中。
(2)当disable应用于有待处理的过程断言队列的过程的最外层范围时,除了正常的禁用之外,待处理的过程断言队列将被刷新,队列上的所有待处理的断言实例都将被清除。
下面这个例子说明,用户代码如何明确地刷新待处理的过程断言实例,在这个情况,a1的实例仅在bad_val_ok的值未设置为1的时间步成熟。
default clocking @(posedge clk); endclocking
always @(bad_val or bad_val_ok) begin : b1
a1: assert property (bad_val) else $fatal(1, "Sorry");
if (bad_val_ok) begin
disable a1;
end
end
下面这个例子说明,用户代码在程序b2的过程断言队列中,如何明确地刷新待处理的过程断言实例:
default clocking @(posedge clk); endclocking
always @(a or b or c) begin : b2
if (c == 8'hff) begin
a2: assert property (a && b);
end else begin
a3: assert property (a || b);
end
end
always @(clear_b2) begin : b3
disable b2;
end