Search code examples
system-verilogassertionsystem-verilog-assertions

Systemverilog assertion to check bad signal transition


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?


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.