alangloria Posted July 17, 2014 Report Share Posted July 17, 2014 Hi UVM and SystemVerilog users, I've stumbled upon a particular pattern of writing a "utility" class, which I have called "mixin". class derived_class#(type BASE=base_class) extends BASE; .... endclass : derived_class This pattern was inspired by some C++ Boost code I saw, where the base class is templated. The reasoning was that under some compilers, multiple inheritance had higher overhead than chains of inheritance (specifically, an "empty" base class might be allocated the minimum size, so multiple inheritance would increase the size of the object, but if you used a chain of inheritance and a derived class in the chain was "empty" (i.e. did not add any data members), it would not increase the object size). So instead of inheriting from multiple C++ classes, you'd typedef a class like foo<bar<nitz> > and derive from that. Since SystemVerilog has no multiple inheritance, I thought this pattern would be appropriate for use in SV, to at least ease some of the "oh no SV has no multiple inheritance oh no" pain. I've defined a simple utility class, utility::slave_sequence_item (utility is a package) defined like so: class slave_sequence_item#(type BASE=uvm_sequence_item) extends BASE; local uvm_barrier wait_barrier_; local uvm_barrier fin_barrier_; `uvm_object_param_utils_begin(utility::slave_sequence_item#(BASE)) `uvm_field_object(wait_barrier_, UVM_ALL_ON) `uvm_field_object(fin_barrier_, UVM_ALL_ON) `uvm_ojbect_utils_end function new(string name=""); super.new(name); wait_barrier_ = new("wait_barrier_", 2); fin_barrier_ = new("fin_barrier_", 2); endfunction // to be called by sequence task wait_for_transaction; wait_barrier_.wait_for; endtask task finish_transaction; finish_barrier_.wait_for; endtask // to be called by driver task indicate_transaction; wait_barrier_.wait_for; finish_barrier_.wait_for; endtask endclass : slave_sequence_item Basically, this slave sequence item class adds three new methods. By default, you just derive from utility::slave_sequence_item. But if you already have an existing sequence item type derived from uvm_sequence_item, you just do typedef utility::slave_sequence_item#(my_sequence_item) my_slave_sequence_item; and the added methods and variables will get "mixed in" the my_slave_sequence_item type. What do you think? I've tested it on Cadence IUS10.20-s103, and it seems to work properly. From my understanding of the IEEE standard, the above is not specifically disallowed (but then it might not be well supported on actual simulators). enchanter and tudor.timi 2 Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted July 17, 2014 Report Share Posted July 17, 2014 This is a pretty cool use model. I've tried it out with Questa 10.2 and it works there as well. I'm definitely going to incorporate this in my coding! Quote Link to comment Share on other sites More sharing options...
alangloria Posted July 18, 2014 Author Report Share Posted July 18, 2014 Thank you for checking on other simulators. Our shop has only one simulator, so I couldn't check. I fear that even if the pattern is technically valid under IEEE 1800-2005, it might not be a common use model and simulators might not handle it correctly. Even our IUS10.2 occasionally crashes if you make an otherwise-unrelated syntax error if I use this pattern; if I remove this pattern and leave in the syntax error the compiler will report the syntax error correctly. Using mixin classes and a few helper classes, it's possible to emulate multiple inheritance to the point that you can treat a class as being derived from multiple classes, with a lot of coding overhead. For example, see below: // This is the "unrelated" class that can be converted to. // Notice it is not derived from a UVM base class. virtual class frobnicatable_base; pure virtual task frobnicate; endclass // This is a helper class to translate from your class // to frobnicatable_base class frobnicatable_base_param#(type this_type = frobnicatable_base) extends frobnicatable_base; // this is where the magic happens! this_type actual; function new(this_type actual); this.actual = actual; endfunction virtual task frobnicate; actual.frobnicate; endtask endclass // You can derive from this class via frobnicatable#(uvm_component) // to get multiple inheritance of frobnicatable and uvm_component: class frobnicatable#(type BASE=uvm_component) extends BASE; typedef frobnicatable#(BASE) this_type; `uvm_component_param_utils(this_type) function new(string name, uvm_component parent); super.new(name, parent); endfunction // perform frobnication virtual task frobnicate; `uvm_info("FROBNICATE", "The component was frobnicated!", UVM_NONE); endtask : frobnicate // conversion to frobnicatable base function frobnicatable_base as_frobnicatable_base; frobnicatable_base_param#(this_type) rv; rv = new(this); return this; endfunction endclass : frobnicatable With this, you can create a queue of frobnicatable objects, which can be monitors, drivers, sequencers, agents, scoreboards, etc., but all of which are frobnicatable. frobnicatable_base frobnicatables[$]; virtual function build_phase(uvm_phase phase); super.build_phase(phase); monitor = my_frobnicatable_monitor::type_id::create("monitor", this); driver = my_frobnicatable_driver::type_id::create("monitor", this); sequencer = my_frobnicatable_sequencer::type_id::create("sequencer", this); .... frobnicatables.push_back(monitor.as_frobnicatable_base); frobnicatables.push_back(driver.as_frobnicatable_base); frobnicatables.push_back(sequencer.as_frobnicatable_base); endfunction : build_phase This technique is called "proxy object", and is another technique I picked up from Boost C++ library. Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted July 18, 2014 Report Share Posted July 18, 2014 IUS 10.2 is pretty old. Did you consider upgrading? Quote Link to comment Share on other sites More sharing options...
ddayan Posted July 20, 2014 Report Share Posted July 20, 2014 Nice threat! You might want to check "interface classes", a new feature in SV 2012 LRM. Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted July 20, 2014 Report Share Posted July 20, 2014 It's not really the same thing as interface classes, because here you can also inherit the implementation, not just the interface. Quote Link to comment Share on other sites More sharing options...
dave_59 Posted July 20, 2014 Report Share Posted July 20, 2014 The 1800-2009 LRM added an explicit example to clarify that parameterizing the base class form was legal. See http://www.eda.org/svdb/view.php?id=2142 Although multiple inheritance is not the same as multiple interfaces, you could still use it to simplify the code that needs to be written here. Quote Link to comment Share on other sites More sharing options...
ddayan Posted July 21, 2014 Report Share Posted July 21, 2014 It's not really the same thing as interface classes, because here you can also inherit the implementation, not just the interface. True But using interfaces allows you to implement as many as you like thus providing a many "can do" implementations to a single class. I agree this is a different approach (that I'm still trying to find a use for) Quote Link to comment Share on other sites More sharing options...
alangloria Posted July 21, 2014 Author Report Share Posted July 21, 2014 IUS 10.2 is pretty old. Did you consider upgrading? Yeah, and the answer was, "when our overseas mother company upgrades". ^^ Quote Link to comment Share on other sites More sharing options...
alangloria Posted July 21, 2014 Author Report Share Posted July 21, 2014 True But using interfaces allows you to implement as many as you like thus providing a many "can do" implementations to a single class. I agree this is a different approach (that I'm still trying to find a use for) UVM 1.2 Visitors: class visitor_capable_wrapper#(type NODE=uvm_void, TYPE=uvm_component) extends uvm_visitor#(NODE); local TYPE actual; function new(TYPE actual); this.actual = actual; endfunction virtual function void begin_v(); this.actual.begin_v(); endfunction virtual function void visit(NODE node); this.actual.visit(node); endfunction virtual function void end_v(); this.actual.begin_v(); endfunction endclass virtual class visitor_capable#(type NODE=uvm_void, BASE=uvm_component) extends BASE; `uvm_component_param_utils(visitor_capable#(NODE,BASE)) function new(string name, uvm_component parent); super.new(name, parent); endfunction virtual function void begin_v(); endfunction pure virtual function void visit(NODE node); virtual function void end_v(); endfunction function uvm_visitor as_uvm_visitor; visitor_capable_wrapper#(NODE,visitor_capable#(NODE, BASE)) rv; rv = new(this); return rv; endfunction endclass : visitor_capable This way, you can make any component (sequencer, driver, monitor, agent, scoreboard...) be a visitor. You probably need another visitor_capable_object so that UVM objects can also be converted to UVM Visitors, but well. Sincerely, AmkG Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted August 14, 2014 Report Share Posted August 14, 2014 I just noticed that uvm_reg_sequence uses the same pattern, so I would take this to mean that it is intended to work. Quote Link to comment Share on other sites More sharing options...
ddayan Posted August 17, 2014 Report Share Posted August 17, 2014 Also used in TLM ports: virtual class uvm_port_base #(type IF=uvm_void) extends IF; Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.