System Verilog driver class connect to different interfaces

Trying to write a driver class which can drive signals in an different interfaces (using the corresponding virtual interface).
The driver class will be used to communicate with different designs, and the interfaces will be different.

I am trying to do this by having a base class called driver, then an extended class called driver_struct. The base class will talk to an interface with logic signals, the extended class will drive structs.

A task in my base class method looks like this:

 virtual task initPktIf();
    begin
      log.logInfo(5,"[running initPktIf]");
      vif.ingress_cb.data  <= 0;
      vif.ingress_cb.data  <= 0;
      vif.ingress_cb.last  <= 0;
      vif.ingress_cb.valid <= 0;
    end
  endtask :initPktIf

The method in the extended class looks like this (changed names for web post):

  virtual task initPktIf();
    begin
      log.logInfo(5,"[running initPktIf]");
      vif.ingress_cb.foo2bar_struct.data  <= 0;
      vif.ingress_cb.foo2bar_struct.first <= 0;
      vif.ingress_cb.foo2bar_struct.last  <= 0;
      vif.ingress_cb.foo2bar_struct.valid <= 0;
    end
  endtask :initPktIf

I instantiate the extended class driver in an environment class, for this particular test suite
the interface (and the DUT) has the structs.

At compile time I get errors, this is the first:
ncelab: *E,CUVUNF (./driver/driver.svh,77|21): Hierarchical name component lookup failed at ‘data’.
vif.ingress_cb.first <= 0;

The tool is unhappy because it can’t find the path of the method in the base class, however I am not instantiating the base class.

What is the way around this, which would allow me to have different implementations of the methods in base
and extended classes where some of the classes hierarchical paths do not make sense to the current
design (which should be ok because I am not calling those methods)?

Would an abstract class and pure virtual methods solve this?

This is not a UVM design, the organization i work for has not yet adopted UVM, this is straight SV.

In reply to mikefitzgerald:

The compiler has to generate code for your task even though you think it never gets called. It may not be that easy to determine that at compile time.

Yes, the abstract/concrete class pattern would solve this. That way you have a standard API to all your SytemVerilog interfaces.

But for the code you have shown, you could just put this task declaration inside the interface.

In reply to dave_59:

Thanks for the reply.

Seems like there are at least 3 ways to try to make a generic/re-usable driver or monitor.
And I have seen and attempted to understand your (Dave’s) papers discussing abstract classes inside interfaces (…Outshine Virtual Interfaces), and factory pattern (the third being abstract class and extension). The paper which discusses factory pattern (…Yin and Yang…) indicates it is preferred over abstract class and extension.

Maybe there is some experienced based judgment for the choice of how to architect these classes.
Any guidance about which method would be better under what circumstances for a re-usable driver or monitor class?

  • Factory Pattern (would be nice to see a complete example of a factory pattern for a driver
    class which uses virtual interfaces w.o, UVM or OVM).
  • Abstract interface (classes inside the interface)

I understand that this has been hashed out in the UVM and OVM prior to that. Unfortunately my organization (and several others I know of have not adopted UVM). So that leaves some of us trying to figure out the best practices using what we have with SV 2012.
It seems like there is a potential paper somewhere “Making the best of SystemVerilog in 2018”

In reply to mikefitzgerald:

Abstract classes and the factory are not exclusive practices. You just have to set up the factory so that there must be an override and does not try to construct an abstract class (BTW, the next version of the UVM supposedly has taken care of this).

Be sure to read this thread about not adopting UVM.