Question

I am trying to write an assertion that will fire only if a signal transitions on the rising edge of 'clk'. I wrote below code to test out my ideas

module test();

bit clk, clkb;
int d;

assign clkb = ~clk;

initial begin
   clk = 0;
   forever #100 clk = ~clk;
end

initial begin
   d = 10;
   #150 d = 20;
end

sva_d_chgd: assert property (@(posedge clk) $stable(d,@(clkb))) 
   else $error($psprintf("err: time = %0d, clk = %b, d = %0d", $time, clk, d));

always @ (d or clk) begin
   $display("time = %0d, clk = %b, d = %0d", $time, clk, d);
   if ($time > 200) $finish;
end
endmodule

Above code returns the following output in VCS:

time = 0, clk = 0, d = 10
time = 100, clk = 1, d = 10
"test.vs", 18: test.sva_d_chgd: started at 100s failed at 100s
        Offending '$stable(d, @(clkb))'
Error: "test.vs", 18: test.sva_d_chgd: at time 100
err: time = 100, clk = 1, d = 10
time = 150, clk = 1, d = 20
time = 200, clk = 0, d = 20
time = 300, clk = 1, d = 20
$finish called from file "test.vs", line 23.
$finish at simulation time                  300

Why did the assertion fire at time 100 when 'd' remained stable until time 150?

Was it helpful?

Solution

In your code, stable checks at every posedge of clk to see if the value of "d" has changed or not between the previous two edges of clkb. Since at the very first posedge of clk there was no previous clkb edge value of "d", stable returns "unknown" instead of "true" or "false" which causes your assertion to fail.

I've added a reset signal to your code and disabled the assertion until after the first posedge of clk. I also moved when "d" changes.

module test();

bit clk, clkb, rst;
int d;

assign clkb = ~clk;

initial begin
   clk = 0;
   forever #100 clk = ~clk;
end

initial begin
   rst = 1;
   #150 rst = 0;
end

initial begin
   d = 10;
   #250 d = 20;
end

sva_d_chgd: assert property (@(posedge clk)
                            disable iff (rst)
                            $stable(d,@(clkb))) 
   else $error($psprintf("err: time = %0d, clk = %b, d = %0d", $time, clk, d));

always @ (d or clk) begin
   $display("time = %0d, clk = %b, d = %0d", $time, clk, d);
   if ($time > 400) $finish;
end
endmodule

Here's the output:

# time = 0, clk = 0, d = 10
# time = 100, clk = 1, d = 10
# time = 200, clk = 0, d = 10
# time = 250, clk = 0, d = 20
# time = 300, clk = 1, d = 20
# ** Error: err: time = 300, clk = 1, d = 20
#    Time: 300 ns Started: 300 ns  Scope: test.sva_d_chgd File: assert_test.sv Line: 26
# time = 400, clk = 0, d = 20
# time = 500, clk = 1, d = 20
# ** Note: $finish    : assert_test.sv(30)
#    Time: 500 ns  Iteration: 1  Instance: /test

This gets around the first unwanted failure of your assertion but I think the way you coded your assertion it is still not actually trapping the condition you're looking for.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top