Display Monitor and Strobe in SystemVerilog

I was adding in the ubiquitous "what is this code doing" debug statements to some SystemVerilog to trace what was happening and finally had a chance to consider deeply the application of: $monitor, $display and $strobe.  All three print to the display and all use the same syntax and formatting in their arguments.  The difference in the three is when they actually execute.  I read a pretty good starting article Verilog subtleties – $monitor vs. $display vs. $strobe that gave me a start to creating my own teaching version of their example.

You can check out the code below on github.

Example Tutorial Code


  module dut;
    reg a,b,c;

    initial begin : abc
      $monitor("%t %m MON0            a: %b b: %b c: %b", $time, a, b, c);
      #100;
      {a,b} = 2'b01;
      c = 1;
      b <= 1'b0;
      #100;
      {a,b} = 2'b10;
      c = 1'bz;
      #100;
      {a,b} = 2'b11;
      c = 0;
      #1000;
      $finish;
    end // block: abc

    initial begin : ext
      #1;
      $monitor("%t %m MON1            a: %b b: %b c: %b", $time, a, b, c);
      $monitor("%t %m MON2            c: %b b: %b c: %b", $time, a, b, c);
    end

    always_comb begin : str
      $strobe("%t %m STROBE+ALWAYS : a: %b b: %b c: %b", $time, a, b, c);
    end

    always_comb begin : alw
      $display("%t %m ALWAYS :        a: %b b: %b c: %b", $time, a, b, c);
    end
  endmodule // dut

Only One $monitor per Simulation

> rm -rf work; qverilog dms.sv -R -c -do "run -all" | grep MON
#                    0 dut.abc MON0            a: x b: x c: x
#                    1 dut.ext MON2            c: x b: x c: x
#                  100 dut.ext MON2            c: 0 b: 0 c: 1
#                  200 dut.ext MON2            c: 1 b: 0 c: z
#                  300 dut.ext MON2            c: 1 b: 1 c: 0
Notice the grep at the end of the command - this is how we make the output easier to understand for the purpose of this post. We only have to see what is important for the simulation output for what we are looking at.

There are a few interesting things here to learn.  The first being, that even though there are three $monitor lines in the example code only one at a time can be active.  That is why we get a message from MON0, but then it gets overwritten by MON1 and finally MON2.  MON1 doesn't even get used because it was immediately overwritten.

The design choice to have only one $monitor statement active at a time is not what I would expect and there isn't a way to activate multiple $monitor.

The Difference Between Strobe and Display

The original article from Verification on Web had a good explanation for showing the difference between $strobe and $display.  I thought i would give my own crack at it too.

> rm -rf work; qverilog dms.sv -R -c -do "run -all" | grep ALWAYS
#                    0 dut.alw ALWAYS :        a: x b: x c: x
#                    0 dut.str STROBE+ALWAYS : a: x b: x c: x
#                  100 dut.alw ALWAYS :        a: 0 b: 1 c: 1
#                  100 dut.alw ALWAYS :        a: 0 b: 0 c: 1
#                  100 dut.str STROBE+ALWAYS : a: 0 b: 0 c: 1
#                  200 dut.alw ALWAYS :        a: 1 b: 0 c: z
#                  200 dut.str STROBE+ALWAYS : a: 1 b: 0 c: z
#                  300 dut.alw ALWAYS :        a: 1 b: 1 c: 0
#                  300 dut.str STROBE+ALWAYS : a: 1 b: 1 c: 0

The operand $display works just as you expect.  Whenever it is called it executes.  But $strobe is a little different, it waits until the end of the time unit to execute.  And since there is only one end of a time unit, $strobe only executes once per time unit.

You can see that at time 100 the always_comb with the standard $display executed twice during that unit and the always_comb - will execute at time 0 unlike the normal always @(*) - with the $strobe executed just once.  Fitting the behavior that $strobe only executes once at the end of the time unit.