Can a class have a data member that is a pointer/ref to a logic?

I know a class can have a virtual interface, which contains lot’s of signals. I can put a signal in a package and reference it from there. I can use UVM back door methods to reference a signal from a class. I can write a task which takes in a ref to a signal. Such a wealth of ways to do things :)

But …

Can I store a reference/pointer to just a basic type (like a logic or bit)

i.e.

  // Clock monitor class to remember stuff about the clock
  class clock_mon;
    ref logic clk; // ***not real syntax****

    time period =0;
    time first_re =-1ns;
    time last_re =0;
    time last_fe =0;
    time expected_period =1ns;

    function new (ref logic clk);  // *** does this magically store clk as the pointer?***
      this.clk = clk;
    endfunction    

I can work around this by not storing the refernce to clk in the class by having a task that does…

    task run (ref logic clk);
      fork
        forever begin
          @(posedge clk);
          period = $time -last_re;
          last_re = $time;
          if (first_re < 0) first_re = last_re;
          @(negedge clk);
          period = $time -last_fe;
          last_fe = $time;
        end
      join_none
    endtask

but it seems weird that I have to put a logic in an interface to store a reference to it.

** Edited **

My linter complains about

  task run (ref logic clk);

It claims

error in line ***: arguments passed by reference cannot be used within fork-join_any or fork_join_none blocks

But Questa seemed to allow it just fine. I’m really hoping Questa is right, because it’s very very useful to be able to do this.

Also, a corollary observation.

Apparently it’s illegal to pass a bit of a packed array into that run task, but an unpacked array is ok. That seems quite arbitrary and annoying! I just changed it to an unpacked array, and it went through, but I bet someone here knows why that is a thing…
See comment bellow for an example and the message in comments. Very good clear message Siemens :) (Not sarcastic. I really appreciate good error messages!)

      foreach (tb3_clk_to_git_mon[i]) begin
        // you can only pass a non packed arraay bit in here, or you get the folllowing error
        //The actual for ref formal "clk" is not permitted to be a select into a packed value.    
        tb3_clk_to_git_mon[i].run(a_clkreset.tb3_clk_to_git[i]);
      end

There are no pointers in SystemVerilog. The class based constructs in SystemVerilog have roots in Java,(both developed at Sun Microsystems–what is now Oracle). They incorporate a safe memory management mechanism that ensures references always point to the correct object. This prevents references from pointing to deallocated memory or accessing objects that were never intended to be referenced.

If we could have eliminated Verilog from SystemVerilog and had RTL written using just classes, there would be no need for virtual interfaces. But that would have been too big a leap for people to tackle. Virtual interfaces were a compromise to link the class based world with the static RTL verilog world. They have lots of restrictions in how they can be used, which keeps the number of interface references down to a manageable level.

Pass by reference to was easy to add to the language because many implementations already do that as an optimization. But to keep memory management safe, there are restrictions on ref arguments as well. Some of that was recently eliminated in the IEEE 1800-2023 SystemVerilog LRM

Addressing your last observation, a bit or part-select of a packed object is considered an expression, not a seperate variable. Members of unpacked arrays or structures are considered distinct variables; so they can be passed by reference.

1 Like