You are on page 1of 23

Please!

Can someone make


UVM easy to use?
Rich Edelman Raghu Ardeishar
Mentor Graphics

© Accellera Systems Initiative 1


Main Concerns
• Unnecessary use of Parameterized classes
• Confusing sequences
• Config DB issues
• Heavy use of Macros

© Accellera Systems Initiative 2


Parameterized Classes
• Parameterized classes are very powerful
• …but misunderstood
• Not needed in all cases
• Overriding becomes problematic
• Layering UVM makes is harder
• Complicated use model with “factory”
• Use it only when needed

© Accellera Systems Initiative 3


Parameterized Classes
• A couple of examples…
– Class parameterized by value
class classValue #(int V = 3)
int delay = V;
….
endclass

– Class parameterized by type

class classType #(type T = int)


T delay;
….
endclass

• First Principles : Lets look at polymorphism…

© Accellera Systems Initiative 4


Parameterized Classes
• Basic Polymorphism
class classValue;
…. classValue baseclass
endclass
class classValueNew extends classValue
….
endclass
classValueNew
classValue cV = new;
classValueNew cVN = new;

cV = cVN;

• newClassValue is an extension of classValue


• Can be assigned to classValue
– They are type compatible

© Accellera Systems Initiative 5


Parameterized Classes
• Now with value parameters
class classValue (int V = 3); classValue baseclass classValue
….
endclass

classValue #(3) cV3 = new(); classValue#(3) classValue#(4)


classValue #(4) cV4 = new();
Not type
cV3 = cV4; //ERRRRROR compatible

• Not Possible
– Will compile and load
– Will result in a run time Fatal
• cV3 and cV4 are not type compatible

© Accellera Systems Initiative 6


Parameterized Classes
• Now with type parameters
class classType (type T = int); classType baseclass classType
….
endclass

classType #(int) cInt = new(); classType#(int) classType#(integer)


classType #(integer) cInteger = new();
Not type
cInt = cInteger; //ERRRRROR compatible

• Not Possible
– Will compile and load
– Will result in a run time Fatal
• cInt and cInteger are not type compatible

© Accellera Systems Initiative 7


Parameterized Classes
• Do you really need a parameter?
– Only if you need a elaboration time constant
– Most likely you need a dynamic variable
class classValue;
int T = 3;
….
endclass
class classValueNew extends classValue
int T = 4;
….
endclass
classValue cV = new;
classValueNew cVN = new;

cV = cVN;

© Accellera Systems Initiative 8


Parameterized Classes
• Layer on UVM and you raise the level of complication
• Add the factory and you get a perfect storm
• Macros (in parameterized classes) don’t work as
expected
– Or at least as most people expect !!
class packet extends uvm_object ;

• Lets first look at `uvm_object_utils(packet)


…..
endclass
regular UVM classes
class packetD extends packet;
• “p” and “pD” are `uvm_object_utils(packetD)
endclass
type compatible packet p = new();
packetD pD = new();

p = pD; //Works!!
© Accellera Systems Initiative 9
Parameterized Classes
• Util Macros work well in non-param classes
• Use it to register with factory
• Use uvm_top.print_topology() and factory.print() to get
details
virtual function end_of_elaboration_phase(uvm_phase phase) ;
uvm_top.print_topology();
factory.print();
…..
endclass

• Macros (in parameterized classes) will not create all the


necessary routines!!
• Factory override print will NOT show anything!
• To see details you will need to NEED to register MANUALLY

© Accellera Systems Initiative 10


Parameterized Classes
• Don’t use *_param_utils, It will not help
• Write this simple code as shown below
• factory.print() will show overrides

class driverB #(type T = int) extends uvm_driver #(T);


//`uvm_component_param_utils(driverB#(T))

localparam type_name = $sformatf("driverB#(%s)", T::type_name);


typedef uvm_component_registry #(driverB#(T), type_name) type_id;

static function type_id get_type();


return type_id::get();
endfunction
virtual function uvm_object_wrapper get_object_type();
return type_id::get();
endfunction
virtual function string get_type_name();
return type_name;
endfunction
endclass

© Accellera Systems Initiative 11


Parameterized Classes
• But the inherent issues remain…
• driverD2packet and driverD2packetD are not type
compatible
class driverD2 #(type T = uvm_object) extends driverB #(T);
driverB baseclass driverB
…..
endclass

typedef driverD2#(packet) driverD2packet;


typedef driverD2#(packetD) driverD2packetD;
driverD2packet driverD2packetD

Not type
compatible

• driverD2 cannot accept packets of different types w/o


some more work

© Accellera Systems Initiative 12


Parameterized Classes
• Deparameterized the class…
class classType #(type T = int);
T myDelay;
function calcDelay(); class config env_config extends uvm_object
rand int delay;
…. endclass
endclass
class classType;
int myDelay ;
env_config e;
uvm_config_db :: get(…“e”,e);
function new ( );
myDelay = e.delay;
endfunction
….
endclass

• Use uvm_config_db to get the parameter from config objects set


in the environment

© Accellera Systems Initiative 13


Parameterized Tests/Sequences
• Sequences and Parameterization  Not always needed
• Tempting to parameterize tests/sequences based on
bus width, LANES etc
• Will create issues while creating sequences to run on
interfaces with different parameters
• Solution: Instantiate with max possible bus widths and
control individual dimensions using environment
configs

© Accellera Systems Initiative 14


Parameterized Sequences
class test #(Int LANES = 2, int PIPE_BYTES_MAX = 1, int NUM_OF_FUNCTIONS = 1) extends uvm_test;
typedef pcieSeq #(LANES,PIPE_BYTE_MAX,NUM_OF_FUNCTIONS) pcieSeqT;
….
task run_phase;
pcieSeqT pcieSeq = pcieSeqT::type_id…;
pcieSeq.start(sequencer);
endtask
endclass

• Task and sequence have become more complicated to


extend and override
• Cannot run it on agents/sequencers with different
parameters
• Will need to create a new sequence for each variation of
parameters
• Code Bloat

© Accellera Systems Initiative 15


Parameterized test simplified
class env_config extends uvm_object
rand int LANES; env_config
rand int PIPE_BYTE_MAX; class test extends uvm_test;
rand int NUM_OF_FUNCTIONS; int LANES;
endclass int PIPE_BYTE_MAX;
Set env_config in int NUM_OF_FUNCTIONS;
module top; config_db typedef pcieSeq pcieSeqT;
initial begin ….
env_config eC = new(); task run_phase;
randomize(eC) with …; pcieSeqT pcieSeq = pcieSeqT::type_id…;
uvm_config_db #(env_config):: Get env_config env_config eC;
set(uvm_root::get(),“*”,“eC”, eC); uvm_config_db :: get(…“eC”,e);
end
from config_db LANES = eC.LANES;
endmodule PIPE_BYTE_MAX = eC.PIPE_BYTE_MAX;
pcieSeq.start(sequencer);
endtask
Extract properties endclass
from env_config

• Remove parameters from test or sequences


• Use configs to set and retrieve parameters.
• Use the same sequence/test

© Accellera Systems Initiative 16


Config DB …
• Very useful BUT very Often Misused/Misunderstood
• Very useful but expensive during lookups
• Use to set and get interfaces
• Use to set and get configuration objects
• Do not use to set and get integers, strings
• Do not call “get” multiple times eg, in a for/foreach
loop

© Accellera Systems Initiative 17


Config DB …
• Can you get it to work?
– Sure, But it all the effort with paths worth it?
static function void set ( uvm_component cntxt,
string inst_name,
string field_name,
T value)

• Inside a class to set the value:


– uvm_config_db #(type)::set(this,”*.pathname”, “label”,value);
• Outside a class to set the value:
– uvm_config_db #(type)::set(uvm_root::get(),”*.pathname”, “label”,value);
– Inside a class to set the value:
• To get the value
– uvm_config_db #(type)::get(this,””,”label”,value)

© Accellera Systems Initiative 18


Config DB …
• Use unique names for labels
• Avoid variables with same names in different instance paths
• Use “*” for instance names avoiding paths….
– Big Hammer but worthwhile in the long run
– Puts the variable in Global space
uvm_config_db #(virtual interfaceName) ::set (uvm_root::get(),“*”,“pcieIntf1”, pcieIntf1);
uvm_config_db #(virtual interfaceName) ::set (uvm_root::get(),“*”,“pcieIntf2”, pcieIntf2);

• Get the interface


– uvm_config_db #(type)::get(uvm_root::get(),”*”, “pcieIntf1”, pcieIntf);
• Use +UVM_CONFIG_DB_TRACE (simulator command line argument) to
debug set/get issues

© Accellera Systems Initiative 19


Config DB …
• Avoid using automated macros with config_db
• Example: uvm_agent “is_active”
• Mistaken assumption that `uvm_component_utils
implements uvm_config_db::get
• Implement “get” manually in the env or test OR
• Use `uvm_field_enum(uvm_active_passive_enum,
is_active, UVM_ALL_ON)

© Accellera Systems Initiative 20


Finally…Macros!
• To use or not to use? Well, Depends on the macro
• `uvm_do… AVOID
– You don’t need a macro to execute sequences
– They expand into complicated code
– Do the following instead
task sequence::body; task sequence::body;
myItem item; myItem item = myItem::type_id::create(…);
`uvm_do(item) // AVOID start_item(item);
endtask randomize(item);
finish_item(item);
endtask

task sequence::body;
mySeq seq; task sequence::body;
`uvm_do(seq) // AVOID mySeq seq = mySeq::type_id::create(…);
endtask seq.start(…);
endtask

© Accellera Systems Initiative 21


Finally…Macros!
• `uvm_field… AVOID like the Plague
– Implements copy, compare, pack, unpack etc…
– Code bloat and very hard to debug
– Simulators have optimized a lot for performance but still is a
debug issue
– Write the routines manually
– LOT easier to debug
– Refer to “Are OVM and UVM Macros Evil? A Cost-Benefit Analysis” by Adam Erikson

© Accellera Systems Initiative 22


Questions

© Accellera Systems Initiative 23

You might also like