UVM parametrized agent and driver

Hi,

Does anyone know what would be the best way to accomplish this.
I want to parameterize agents and their drivers/monitors.
Here is my puzzle:

from the test_top I want to define how many agents and what the names/types of the driver classes are.
EX:

function void build_phase(uvm_phase phase);
    string interface_names[] = {
    "adc_parallel",     "adc_serial"};

    // Get test name from UVM_TESTNAME command line argument
    if(!$value$plusargs("UVM_TESTNAME=%s", test_name))
      `uvm_error("TEST", {"Unknown Test name", test_name})
    // Save transcript
    // void'(mti_Cmd($sformatf("transcript file %s.$Sv_Seed.transcript.txt", test_name)));


    // Construct the top level configuration, agent configuration
    env_config = adc_env_configuration::type_id::create("env_config");

    env_config.number_of_agents  = 1;
    env_config.create_agent_configs(env_config.number_of_agents);
    env_config.agent[0].agent_name      = "adc";
    env_config.agent[0].config_type     = agent_config;
    env_config.agent[0].driver_type     = adc_driver;
    env_config.agent[0].monitor_type    = adc_monitor;
    env_config.agent[0].transaction_type= adc_transaction;
    env_config.agent[0].coverage_type   = adc_coverage;
    env_config.agent_config[0].active_passive     = ACTIVE; 
    env_config.agent_config[0].has_coverage       = 1;
    env_config.agent_config[0].enable_transaction_viewing = 1;



    //retrieve interface from config db and put in adc_agent_config
    if( !uvm_config_db #( virtual adc_vif )::get( this , "" , "adc_vi" ,  agent_config.vi ) )
            `uvm_fatal("Config Error" , "uvm_config_db #( virtual adc_vi )::get cannot find resource adc_vif from test top" )

    //set env_cfg.adc_agent_config to adc_agent_config
    env_config.agent_config[0] = agent_config;

     //set the agent configuration
    uvm_config_db #(adc_env_configuration)::set(this,"*","env_config",env_config);

    //create environment
    env   = generic_environment::type_id::create("env",this);


    `uvm_info("TOP_LEVEL_CONFIGURATION",env_config.convert2string(),UVM_HIGH);

    super.build_phase(phase);



  endfunction

Now from the Environment:

virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    //get the environment configuration
    //                          type of value        prefix path  field name               values
     if( !uvm_config_db #( env_configuration )::get( this , "" , "env_config" ,  env_config ) )
            `uvm_fatal("Config Error" , "uvm_config_db #( adc_env_configuration )::get resource env_config not found" )


    //construct/create the agents

    for (int i = 0; i < env_config.number_of_agents;i++)
    begin 
       //set the agent configuration
       uvm_config_db #(agent_configuration)::set(this,"*",$sformatf("agent_config_%2d", i),env_config.agent_config[i]);
       agents[i] =  generic_agent#(env_config.agent_config[i].config_type,
                                   env_config.agent_config[i].monitor_type,
                                   env_config.agent_config[i].driver_type,
                                   env_config.agent_config[i].coverage_type,
                                   env_config.agent_config[i].trans_type,
                                   i
                                   )::type_id::create($sformatf("agent_%2d", i),this);

    end


  endfunction

In the agent

class generic_agent #(
   type CONFIG_T,
   type MONITOR_T,
   type DRIVER_T,
   type COVERAGE_T,
   type TRANS_T,
   int  AGENT_NUM
) extends uvm_agent;

  // Register this component with the factory
  `uvm_component_param_utils(generic_agent #(CONFIG_T,
                                             MONITOR_T,
                                             DRIVER_T,
                                             COVERAGE_T,
                                             TRANS_T,
                                             AGENT_NUM))


  CONFIG_T         config_h;
  COVERAGE_T       coverage_h;
  MONITOR_T        monitor_h;
  TRANS_T          trans_req_h;
  TRANS_T          trans_rsp_h;
  DRIVER_T #(config_h,AGENT_NUM, trans_req_h,trans_rsp_h)   driver_h;
  uvm_sequencer #(TRANS_T) sequencer_h;

  uvm_analysis_port #(TRANS_T) mon_ap;

  // FUNCTION: new
  function new( string name = "", uvm_component parent = null );
     super.new( name, parent );
  endfunction: new


  // FUNCTION: build_phase
  virtual function void build_phase(uvm_phase phase);

      if(!uvm_config_db #(CONFIG_T)::get(this, "",$sformatf("agent_config_%2d", AGENT_NUM),config_h))
     `uvm_fatal("AGENT", "Failed to get config object");  
      
    //send the agent config to all levels below this and the driver and monitor can retriev the interface
     uvm_config_db #(CONFIG_T)::set(this, "*","config_h",config_h);
    


//////////////////////////////////////////////////////////////////////////////////////////////////////


  // Construct a sequencer and driver only if agent is in active mode

     if (config_h.active_passive == ACTIVE) begin
       sequencer_h = new("sequencer_h",this);
       //create specific base instance of driver so we can override with parameterized type
       driver_h = DRIVER_T#(AGENT_NUM, trans_req_h,trans_rsp_h)::type_id::create("driver_h",this);
     end

There is more here but I cut it off.

The error I get is

generic_agent.svh(61): (vlog-13175) Passing parameter to type ‘DRIVER_T’ is not allowed.

Any thoughts,

Thanks,
Scott

In reply to sdeaderick:

There is a couple of things I’d like to talk about.
Starting with the simple ones:

  1. The UVM commandline API helps you passing the testname to your environmet without dealing with the $plusargs. Simply write to the simulator call +UVM_TESTNAME=.
  2. I’m not sure if it is useful to code the simulator logfile in your environment. You can use the UVM reporting mechanism to do this in a more easy way.

The more complicated ones:
I guess you are trying to get a generic UVM environment. To make this configurable you are dealing with a mixed approach of parameterized classe and macros. Finally this is causing your problem.
My professional practice in the UVM space shows me dealing with parameterized classes causes quite soon problems. For this reason I’m trying to avoid this in any case. The UVM configuration DB helps you to deal with parameters in your UVM environment.

I’m not sure why you need such an generic agent. But I guess you want to put together a complete UVM environment using such generic elements. This is not a really flexible approach. You may run into trouble quite soon.
In my professional practice I made the experience the only flexible approach for generating a generic UVM environment is the usage of ‘UVM Framework Generators’.
There are some licensed products on the market. But you find also solutions which are free of charge. Look to the ‘EasierUVM Approach’ at Doulos.com or visit my personal webpage christoph-suehnel.de.

In reply to chr_sue:

Points 1 and 2 Agreed and I will change that. Mentor has a framework generator which is what I used, but it was setup for HW emulation and I didn’t want to use that but I wanted to take some of the generated files for my approach. So I need to fix that.

What I am really trying to do is create a testbench that can be scaled easily without having to use a generator. Just code the pieces you will need to code anyways even if you have a generator.

The test tells how many agents (and eventually what scoreboards the outputs will be connected to). So the person modifying the testbench only needs to code the driver, monitor, transaction,coverage and scoreboards. From the test top they specify what pieces will compose the agent/s.

Some people think of the agent as defined by the transaction data and some people think of the agent as the as defined by the interface it drives. I can see it from both sides and would like the ability to mix and match at will.

Maybe this will end up being impractical but I have done something similar in systemverilog and I found it very quick and easy to modify for my needs (FPGA verification).

Thanks for the reply,
Scott

In reply to sdeaderick:

OK, I understand now a few additional things. I believe we are talking about different things when saying UVM framework generator. What I mean is a utility which generates a complete UVM environment which can execute tests and sequences directly after generation. What you have to implement in a first step is the bus protocol in the driver, the processs to extract transaction from the pin interafce in the monitor, the coverage model, sequences and tests running on the environment. Such an environment can be easily extended to a scalable framework with any number of master and slave agents. And of cause the scoreboard architecture and implementation is always application specific.
In my understanding you are generating single pieces of your environment. And you have to plumb them manually. This plumbing process is very formally and can be automated.
Maybe I can initiate some other ideas.

In reply to chr_sue:

Hi Chris,

The basic infrastructure is implemented and working for 1 agent. That is with taking out all parameterization and hardcoding driver, monitor and transaction types in the agent.

But My main question is how do I pass a type down through the hierarchy either through config db or parameters? In other words I want to create a driver of variable type.

from the agent
driver_h = DRIVER_T::type_id::create(“driver_h”,this);

where DRIVER_T depends on the driver class I wish to use which is dictated by the test_top.
Can I use the config_db to do this?

Scott

In reply to sdeaderick:

You can only pass objects to the config_db, i.e. parameters, variables, class objects, virtual interfaces etc.

In reply to chr_sue:

ok, how about this approach. Setting up generic agents that have base driver/monitor/transaction classes and the do a set type override at the test_top to have the desired classes.

Do you see any holes in that approach?

Again, main goal in this is to set up a testbench that needs to be modified in as few places as necessary when adding/subtracting an agent/interface.

Scott

In reply to sdeaderick:

Overrirding works only for objects of the same type, because it is based on the inheritance capabilities. You cannot override a PCI-driver by an AHB driver.

In reply to sdeaderick:

Hi Scott,
today I had a chat with a Mentor guy. He helped me to install the UVMF.
I have generated a first example environment and I try to understand what they are doing.
In the toplevel TB there are the instances of the SV interfaces and the BFM for the drivers and the monitors. All 3 types of components are passed to the config_db. In my understanding this architecture with the BFM is not following the UVM User Guide. It looks like this architecture is strongly influenced by the requirements coming from emulation. The question is if this is also your target.
Please let me know what you are thinking.

Christoph

In reply to chr_sue:

Hi Chris,
Thanks for going the extra mile on this.
The short answer to your question is no, UVMF is not the structure I am aiming for.

I started with UVMF framework but found it too cumbersome for the project I am on that is not going to use HW emulation. In fact many projects that I develop testbenches for do not use HW emulation. I have taken their initial structure and simplified it back to a more standard UVM structure.

I give lots of kudos to their UVMF structure for HW emulation but my needs are to have a testbench that doesn’t require going back to their python scripts when I need to add an interface or BFM.

When I need to add a new BFM I would really like to just code the required classes (driver, monitor, transaction) and add/override at the test_top level. No changes to the environment or even the generic agent. There should be a way to do this, but I can’t seem to find it.

Going back to the question previous, if I derive all my drivers from a base class driver I should be able to override it though right? At least that is what I see in the advanced UVM videos. Not exactly my situation but close.

Scott

Scott

In reply to sdeaderick:

Hi Scott,
thanks for explaining some more details.
The structure with the BFMs in the toplevel module is forced by the emulation requirements. It sis not common UVM style.
If you want to add an additional agnet to your enviornment you have to do this in the environment. This is the toplevel component of the class-based testbench. And this means you have to touch this piece of code. There is no other way.
If you want to replace an existing agent with another one, you could do this by parameterizing a agent. If this is a good solution is another question. Because you have always to code your driver, monitor, coverage collector, transaction and sequences.
My professional experience collected in 15+ OVM/UVM projects you should not go this way. Instead make your data structure reuseable by holding all agent related data in one directory tree.
And spend the manual effort to include an additional agent in the env.

In reply to chr_sue:

Hi Chris,

You are right, I think I have convinced myself that there really is no way around the having to touch the environment. I tried parameterizing but here is the problem, I can not create an array of types. If I could, I would be able to create a dynamic array of agent types that I could feed into the environment (through parameters) and simply create agents in a loop based on the agent types.

The number of agents changes and their scoreboard connections change as well but again if I could create an array of types I could handle this.

Further, overriding existing agents won’t work because it does not look like the build phase executes on overrides.

So it looks like I will have to customize the environment for every testbench I create.
Oh well, it was worth a try.

Thanks for your help,
Scott