A problem when I use SVA to check duty cycle of a clock

Your code is confusing. You have an always within an initial, that is illegal.
Since it compiled OK, you must have shown a snippet. In that case, your assertion will fire at every posedge. Below is code that compiles OK, but I did not simulate. It should work.
That code fires once, but the testing of the clock is done after the clock settles for a while. I gave it 2 cycles, but you may want to increase that number. I also used an immediate assertion.


module mtime;
  timeunit 1ns;   timeprecision 100ps;
  bit clk;
  time t_now, low, high, duty_diff_abs, tolerance;
  initial begin
    repeat(2) @(posedge clk) t_now=$time;  // let clock work for a while    
    tolerance = 1000ps;
    @(negedge clk) high=$time - t_now; 
    t_now=$time; 
    @(posedge clk) low =$time - t_now;   
    duty_diff_abs = (high - low >= 0)? (high - low) : (low - high);
    duty_cycle_check : assert(duty_diff_abs <= tolerance)
      else $error($psprintf("rise = %0t, fall = %0t, low = %0t, high = %0t, duty_diff_abs = %0t, tolerance = %0t", rise, fall, low, high, duty_diff_abs, tolerance));
  end
endmodule 

Ben Cohen http://www.systemverilog.us/

  • SystemVerilog Assertions Handbook, 3rd Edition, 2013
  • A Pragmatic Approach to VMM Adoption
  • Using PSL/SUGAR … 2nd Edition
  • Real Chip Design and Verification
  • Cmpt Design by Example
  • VHDL books