Using SystemVerilog and ModelSim, I want to monitor the values of some signals in my design when the clock is on its negative edge. Strangely, the code responses on both edges (positive and negative). Here is a minimal working example:

timescale 1 ns / 100 ps
module tb_ex;
logic clk;

always
begin
clk = 1;
#5;
clk = 0;
#5;
end

always @(negedge clk)
begin
$monitor($time, "-> clk = %b", clk);
end

endmodule


which results in

VSIM 40> run
#                    5-> clk = 0
#                   10-> clk = 1
#                   15-> clk = 0
#                   20-> clk = 1
#                   25-> clk = 0
#                   30-> clk = 1
#                   35-> clk = 0


It seems that the always @(negedge clk) is not honored. Even if I put an if inside that block,

always @(negedge clk)
begin
if (clk == 0) begin
$monitor($time, "-> clk = %b", clk);
end
end


the result is still the same. How can I make it work as expected? Thanks!

When a $monitor task is invoked with one or more arguments, the simulator sets up a mechanism whereby each time a variable or an expression in the argument list changes value—with the exception of the $time, $stime, or $realtime system functions—the entire argument list is displayed at the end of the time step as if reported by the $display task. If two or more arguments change value at the same time, only one display is produced that shows the new values. Only one $monitor display list can be active at any one time; however, a new $monitor task with a new display list can be issued any number of times during simulation. In other words, A $monitor task is executed it creates a singleton process that displays the value of the listed variables at the end of the time step when ever one of those variables change.
For your purpose, you should use $display or $strobe instead of $monitor. $display prints the immediate value of variables, while $strobe prints the final value in the time step. Neither start an automatic triggering process like $monitor. For details refer to IEEE Std 1800-2012 § 21.2.1 The display and write tasks and § 21.2.2 Strobed monitoring
• Works as expected! Thanks Greg! May I ask, what's the difference betweeen $display and $strobe? – fajar Mar 10 '17 at 5:43
• It is in the last paragraph. Example a=2; $strobe("strobe %0d",a);$display("display %0d",a); a=5;` would print "display 2" then "strobe 5". Try it out on your simulator. – Greg Mar 10 '17 at 6:12