Jump to content

Unreferenced classes in packages


Recommended Posts

When importing a class from a package, do other classes in that package need to be loadable at point of import? Say I have a driver and a monitor in a package, both components originally written to operate on the following interface:

interface MyIf;

logic any;

endinterface

What I want to do is to import the monitor only, and have it operate on a modified version of the interface:

interface MyIf(input logic any);

endinterface

The simulator I am using does not allow this, because the driver isn't loadable with the modified interface. This seems to be irrelevant, since the driver isn't referenced in the importing context. Is the simulator in error or is the behavior to be expected?

Regards,

Erling

Link to comment
Share on other sites

Although you didn't mention it, I assume that your problem is because both driver and monitor class are referencing the same virtual interface. Unless the compiler has some very sophisticated dead code analysis, it not going to know that that your driver is not going to be used - especially if the driver is registered with the factory.

I'll use this as another plug to get people to think about using abstract classes instead of virtual interfaces to connect to the DUT. See the following papers

http://www.dvcon.org/2010/proceedings/papers/06_1.pdf

http://www.dvcon.org/2011/proceedings/papers/01_4.pdf

http://ovmworld.org/forums/showthread.php?100-OVM-wrapper-for-Verilog-Bfms&p=350#post350

Dave Rich

Link to comment
Share on other sites

I'll use this as another plug to get people to think about using abstract classes instead of virtual interfaces to connect to the DUT. See the following papers

Dave Rich

In what way would abstract class interfacing help? The workaround I am using right now is to keep components in separate files, and then use include-statements to compose or rearrange packages. Hardly elegant, but it works. With abstract class interfacing there will be even more components and layers to think about, and I would not know how to put it in a package to be reused easily in another context.

Regards,

Erling

Link to comment
Share on other sites

Because it is a more elegant solution. :) This is a standard OO programming patern and it means you don't have to have an RTL interface around if you don't need it.

The solution is more elegant, but it seems to come with its own set of problems. A driver, for example, will not have access to the dut-pins, and there will be a level of indirection. The driver will either have to forward all transactions to the interface class or there will be a job division between the driver and the interface helper class, which means that driver functionality will have to be maintained in two places. Also, the handle to the helper class in the interface still has to be distributed via the config_db, just like a virtual interface.

It seems that slightly modified component relationships will enable component placement in such a way that there is no need for an explicit interface handle, neither virtual nor abstract. What follows is probably trivial, so for what it's worth, consider this driver:

class MyDrv extends uvm_driver #(MyItem);

function new(...);

task run_phase(...);

endclass: MyDrv

The inherited infrastructure is obviously needed by the agent to create and connect MyDrv, but the run_phase does not have to be here, it is virtual and can be placed elsewhere. So the driver (and monitor/etc that need pin-access) can be split in two, with the base class in a package and the actual implementation in an interface (or module), for example:

package MyPkg;

class MyDrv extends uvm_driver #(MyItem);

function new(...);

endclass: MyDrv

endpackage: MyPkg

interface MyIf;

// pin signals here

import MyPkg::*;

class MyDrvImp extends MyDrv;

function new(...);

task run_phase(...);

endclass: MyDrvImp

endinterface: MyIf

The is-a relationship between the MyDrv and MyDrvImp allows factory override for MyDrv so that the (unchanged) agent, when creating MyDrv, actually creates an instance of MyDrvImp:

module top;

initial factory.set_type_override_by_name("MyDrv", "MyDrvImp");

endmodule: top

With this arrangement, every vif.signalName in the driver run_phase can be replaced with signalName, and the interface can be replaced with a module to have the compiler locate all instances of virtual interface distribution, since this is not needed anymore (and can't compile with a module). The result is a 100% interface free testbench held together by uvm as usual, it's just that the low level activities have been moved to the machine room, while the rest of the testbench is on the bridge directing operations.

Experiments seems to indicate that it is surprisingly simple to convert an ubus-like testbench to a world without interfaces with this method, but there may be issues that I have overlooked. One problem could be name clashes when removing the vif qualification in the low level tasks.

Regards,

Erling

Link to comment
Share on other sites

Erling,

I like your solution. What you have just described is essentially making MyDrv an abstract class without explicitly using the keyword "virtual" in front of the class declaration. In both cases you have to extend MyDrv to get a working driver implementation. Adding "virtual" to the class means that the compiler could check that it was never constructed directly, but then you would not be able to register it with the UVM factory.

The other thing it does is keep all the signal and timing related references inside the interface or module where the class is declared. This eliminates all the indirect references that normally would be through a virtual interface reference.

Dave

Link to comment
Share on other sites

Erling,

I like your solution. What you have just described is essentially making MyDrv an abstract class without explicitly using the keyword "virtual" in front of the class declaration. In both cases you have to extend MyDrv to get a working driver implementation. Adding "virtual" to the class means that the compiler could check that it was never constructed directly, but then you would not be able to register it with the UVM factory.

And, if virtual, the class definition would not fix the original problem, i.e my package would not elaborate.

There is one issue to think about, though. The solution does not work as is if an interface is intantiated multiple times, because there will be multiple factory registrations with the same name. First try in order to fix this was to copy the %m trick from your abstract sample code. That didn't work, however, because there is a parameterized driver now in the interface, thus %m isn't available in the class. It is available in the enclosing module, though, but %m would add hierachical paths, and that was not according to plan. The solution I came up with for this problem was to add a localparam string suffix to the interface (or module), and then create unique names for classes in the interface by appending the suffix to the class name for each factory registration. For a single interface application, the suffix can be empty, and things will work as before. When instantiating multiple interfaces, the suffix has to be set to something unique, for example, in a generate loop, the suffix can be a string representation of an interface index parameter, and the class names will be "MyDrvImp[0]", "MyDrvImp[1]" and so on, and these names can either be used to add instance overrides to the factory, or used directly with create_component(...) in a loop instantiating components in the interface. Thus hierarchical paths is not required, but it seems to be a good idea to think about how to name things for ease of reuse.

Other than that, no problems so far. Quite the contrary, the solution works well and feels right from a software point of view.

Regards,

Erling

Link to comment
Share on other sites

Dave,

You wrote (on the ovm forum) about a related issue that:

"The only issue that may be tool dependent is that if you have a package with a class that registers itself with the factory, but that package is never imported, does the package exist? Depending on each tool's separate compilation flow, there may be different answers to that question."

With "package is never imported", do you mean that there is a wildcard import, but nothing is directly referenced, for example:

module top;

import TestPkg::*;

initial run_test();

endmodule

and that the effect of this code is implementation defined?

Regards,

Erling

Link to comment
Share on other sites

I mean any reference to a package by name implies that the package exists. The LRM says "The compilation of a package shall precede the compilation of scopes in which the package is imported." When you do a wildcard import, you have to look inside the package to build a list of candidates to imports, or identify collisions with wildcard imports from other packages. To do that, the package must exist.

But what does existence mean to a simulation run? Certainly you have to compile it, but not everything you have ever compiled is included in a simulation run.

Link to comment
Share on other sites

... but what does existence mean to a simulation run? Certainly you have to compile it, but not everything you have ever compiled is included in a simulation run.

No, but it seems static initializers have to be included even if nothing is actually imported from the package. The LRM says that "Variable declaration assignments within the package shall occur before any initial (and other) blocks are started, in the the same way as variables declared in a compilation unit or module". I find nothing in the LRM stating that such variables have to be referenced in any way to have the init happen. Factory registration uses static init, and one should therefore think that components in the package would be included in the simulation with a wildcard import alone, and that this is a portable solution. The alternative is including components explicitly with the preprocessor, but this seems to be a poor-man solution had the language not supported compiled packages.

However, the ubus code (for example) does not package the test components, the source is included inside a module instead. There is perhaps a valid reason for this, I don't know.

Regards,

Erling

Link to comment
Share on other sites

Erling,

If you do a wildcard import of a package, that package has to exist, and the static initializations inside that package must be executed. I believe that should be a portable solution.

The question that usually comes up is what happens if I have a package with static init's and there is no mention of the package anywhere. Let's say there are two packages (A and B) with static init's in a single file. I compile that file, but only import package A. There is no reference to package B anywhere else in the design. Will the init's in package B get executed? That answer is tool flow dependent.

With Questa, and other tools that support a separate compilation flow, the answer is the same as what happens with modules: a module will be loaded into a design if there is a reference to it and the module that references it contained in the design. As part of the elaboration step, you need to tell the tool the top-level modules and packages that should be loaded into the design, and it will recursively descend through the design to find the other packages and modules that need to be load in as well.

Dave

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...