Jump to content

Recommended Posts

Posted

Here is another question from an UVM beginner...

 

I am designing/experimenting a simple setup comprising a component environment which includes a bus agent, a register model, etc. I added a configuration for the bus agent, which gets the configuration from the environment. Now the component itself needs a user-defined configuration, which I want to randomize. This configuration (describing the topology) will be passed e.g. to the register model. But in the top level testbench, I need to pass actual parameters (that were randomized) from this configuration.

 

My questions:

* can I do this pre-build randomization of the topology-related attributes with a component configuration ? We might expect that the parameter actual shall be constant, so that it is not possible to randomize them.

* if its possible, from a methodology point of view, where should I then declare the configuration and how should the top level testbench and the top level environment share it ? Or: when using the uvm_config_db, who should set it and who should get it ? My first guess was that since it is recommended to pass configurations from top to bottom, I would naturally set the configuration in the top level tb.

* if its not possible, what is then the appropriate methodology to randomize the parameter actuals of the DUT ?

 

All this is not so clear for a beginner... Thank you in advance !

Posted

Yes, I mean Verilog parameters (actually VHDL generics...). For example, the number of timer instances in a simple protocol timer block or the timer width are typically generics/parameters of the block. At the block verification level, I would like to sweep through all possible (constained) configurations, although in a system these parameters will be fixed.

Are these necessarily fixed at compile time, or is there a way to set them "dynamically" at the beginning of the simulation run, without needing to recompile the system verilog code ?

Posted

These are fixed at elaboration time. Some simulators offer ways to set parameters from the command line to a value different that the one set inside the code. This is conceptually similar to a force on a signal. You'll have to look up the exact switch inside your tool's documentation.

 

If your simulator doesn't have this feature (though I expect all Big Three simulators to have something along these lines), you can work around it by setting up a package in which you define your parameter/generic values and pass those to the DUT. To vary the values of these parameters you'd need to compile different versions of this package. This can be achieved by either generating a different file on the fly from some script or by using define statements. All simulators provide ways to set defines from the command line.

 

Regardless of how you do it, you'll need to set up some scripting infrastructure that passes the proper command line parameters to the simulator call. Something along the lines of:

run_script.sh -param1 <param1_val> -param2 <param2_val> <any other params you need> test_name

Once you have this in place, you'll have to ensure that the values you use for you're DUT's parameters get passed properly to the TB as well. Normally you'd create the configuration object in the test. Since the tests aren't meant to be reused (unlike the env components, sequences, etc.), you can just reach into the DUT via a hierarchical path to get the value of each parameter. Another caveat here: some simulators don't support hierarchical paths through VHDL constructs (yeah, you know who you are!), so in that case it means you'll have to go via the parameter package approach, import the param package in your test package and just use the values from there.

 

After writing this up and thinking aloud, it seems that using a parameter package is actually the most portable way of doing it, since all the TB aspects stay separated in the SystemVerilog language domain. I'll give you some code to get you started as well:

// Some default values
// - you can even leave these out, but that means you'll always
//   need to define them from the command line
`ifndef PARAM1
`define PARAM1 1
`endif

`ifndef PARAM2
`define PARAM2 1
`endif

package dut_params_pkg;
  parameter param1 = `PARAM1;
  parameter param2 = `PARAM2;
endpackage


module tb_top;
  import dut_params_pkg::*;
  
  dut #(
    // params from dut_params_pkg
    .param1 = param1;
    .param2 = param2;
  ) dut_inst(...);
endmodule


package test_pkg;
  import dut_params_pkg::*;

  `include "base_test.svh"
  // ... include other tests that extend the base ...
endpackage


class base_test extends uvm_test;
  some_config cfg;
  
  function void build_phase(...);
    // ... create cfg ...
    
    // set the params
    cfg.param1 = param1;
    cfg.param2 = param2;
    
    // ... randomize config ...
    // ... other stuff ...
  endfunction
endclass
Posted

Hi Jlnagel,

   we do what Tudor suggests (i.e. his first suggestion, supply generics on the command line which we then push into the config_db). This is using the -G or -g arguments to Questa vsim. We use a tcl based flow, so it would be perfectly feasible to randomize those parameter values in Tcl, the supply them as arguments to the simulator.

 

However you do it, you can push the values (in a config object) into the config_db inside your top level module which typically creates the test class. If you use a path of *, you can make those values globally visible in any uvm component.

 

 

regards

Alan

  • 3 weeks later...
Posted

Thank you Tudor and Alan for your answers. That perfectly fits what I try to do.

 

I use the config_db to pass these topology along my testbench. I need it also in the register model, since the register width depends on one of those parameters.

I understand that I can get the config before the build phase. I am trying to do this in the register creation, because that's where I need it:

class regs_counter_e_reg extends uvm_reg;
   rand uvm_reg_field enable;
   `uvm_object_utils(regs_counter_e_reg);

   // My config
   timer_lp_config m_cfg;

   function new(string name = "regs_counter_e_reg");
      // I need the size parameter before calling super.new !
      super.new(name, m_cfg.num_timers,UVM_NO_COVERAGE);
      // That's a bit late...
      if(!uvm_config_db#(timer_lp_config)::get(null,"","timer_lp_cfg",m_cfg))
      `uvm_fatal("NOCONFIG","Timer configuration was not set by test.")
   endfunction

However the call to super.new needs to come first. So how can I get the configuration ealier ?

 

Thanks,

Jean-Luc

Posted

However the call to super.new needs to come first. So how can I get the configuration ealier ?

 

The call to super.new(...) doesn't have to come first. You can call it any time you want (in your case after getting the config).

Posted

Is that behaviour compiler dependent ? QuestaSim doesn't like it:

** Error: timer_lp_regmodel.svh(15): The call to "super.new" must be the first statement in the constructor.

But I fairly agree with the tool in this case, that the constructor needs to call the superclass constructor before doing anything else. The problem is that the constructor has non-default arguments.

 

Posted

Is that behaviour compiler dependent ? QuestaSim doesn't like it:

** Error: timer_lp_regmodel.svh(15): The call to "super.new" must be the first statement in the constructor.

 

You can call a function as an initializer before super.new, for example:

 
timer_lp_config cfg = get_config();
super.new(name, cfg.num_timers, UVM_NO_COVERAGE);
m_cfg = cfg;
 
Not sure though, if this is portable when get_config() is a member function.
 
 
Erling
Posted

Oops, I opened my mouth too soon and I didn't notice we were talking about constructors. Here's what the standard states here:

 

 

A super.new call shall be the first statement executed in the constructor. This is because the superclass

shall be initialized before the current class and, if the user code does not provide an initialization, the
compiler shall insert a call to super.new automatically.

 

This means you can't pass the length to the constructor. I see to ways around it:

 

1. Use the same `define you're using in the top level TB in the constructor call:

super.new(name, `NUM_TIMERS, UVM_NO_COVERAGE);

2. Use a type override. Define a generic regs_counter_e_reg and override it with a specific version where the length is set by a parameter:

class regs_counter_e_reg #(int length) extends abstract_regs_counter_e_reg;
  // ... registration ...

  function new(name);
    super.new(name, length, UVM_NO_COVERAGE);
  endfunction
endclass

This means you have to register both classes with the factory and use create(...) to instantiate them, which is more involved.

 

If Erling's suggestion works for you, you should use that one as that seems the most cleanest. You can worry about portability later, once you're done with your project. Out of experience, any tool migration is anyway going to cause problems (due to all of the gray areas in the standard or just plain inconsistencies) so I wouldn't sweat this.

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...