Thursday 16 August 2018

Developing & Delivering KnowHow

Home > Knowhow > Sysverilog > Uvm > Easier Uvm Guidelines > Parameterized Interface Example

How to Access a Parameterized SystemVerilog Interface from UVM







This is an example showing how to access a parameterized SystemVerilog interface from a UVM verification environment by calling the methods of an abstract base class from the UVM environment while making a concrete instantiation of that abstract class within the SystemVerilog interface itself. This technique overcomes the limitations of using virtual interfaces to access parameterized SystemVerilog interfaces. This might be considered an advanced coding technique: the code may look a little more complex than "simply" accessing the SystemVerilog interface using virtual interfaces, but it has the advantage that a single "generic" UVM driver or monitor class can access the contents of a parameterized SystemVerilog interface without the need to create a new version of that class for each and every instantiation of the interface.

This example was inspired by ideas from Jonathan Bromley, Dave Rich, and Gunther Clasen

The complete working example is available on EDA Playground at the following links:

Example of a parameterized interface generated from an Easier UVM interface template file
Example that pulls in a user-defined parameterized interface

The abstract base class to execute operations on a specific type of SystemVerilog interface. (Note that the following base class is not technically an abstract class in the sense that it does not include a pure virtual function, but it is playing the role of an abstract class in that the write task is meant to be overridden.)

class clkndata_if_base extends uvm_component;
  `uvm_component_utils(clkndata_if_base)
    
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
    
  // Methods to be called from the UVM verification env
  // In this example the methods are pseudo-parameterized by using N bits of a longint

  virtual task write(longint addr_arg, data_arg);
    `uvm_error(get_type_name(), "write not implemented")
  endtask

  // Other methods, e.g. read, peek, reset
  ...

Note that the class above extends uvm_component. It would have been sufficient to extend uvm_object, but the advantage of making the class a component is that it can be configured through the configuration database. The concrete instantiation of this abstract class will be created from a UVM env (or possibly from a test), which means that the concrete object has a proper place in the UVM component hierarchy that can be used when setting and getting configuration information.

A UVM driver that calls the methods of the abstract base class above. The same principle would apply for the UVM monitor or any other UVM component that would otherwise have used a virtual interface to access the SystemVerilog interface. The driver gets a reference to the concrete implementation of the interface class from the configuration database.

class clkndata_driver extends uvm_driver #(clkndata_transaction);
  ...
  clkndata_if_base m_clkndata_if;  // Reference to abstract base class   
  ...

  task run_phase(uvm_phase phase);
    ...
    seq_item_port.get_next_item(req);
    if (req.cmd)
      m_clkndata_if.write(req.addr, req.data); // Call methods of abstract base class

The agent (or some other component in the UVM component hierarchy) creates the concrete implementation of the interface object and propagates references to that object to the driver and monitor. The choice of which component actually creates the concrete implementation does not really matter because the instance will be distinguished using its local name and type, but conceptually the agent is the obvious choice because the agent drives and monitors the interface. The string name used to identify the specific instance of the parameterized concrete class can be set locally or can be passed in through the configuration database so that multiple instances of the same agent can use different parameterizations of the interface.

class clkndata_agent extends uvm_agent;
  ...
                     
  clkndata_if_base m_clkndata_if;  // Reference to abstract base class
  ...    
 
  function void build_phase(uvm_phase phase);
    ...
    m_sequencer = clkndata_sequencer::type_id::create("m_sequencer", this);
    m_driver    = clkndata_driver   ::type_id::create("m_driver",    this);
    m_monitor   = clkndata_monitor  ::type_id::create("m_monitor",   this);
    ...

    // The factory method call to 'create' below instantiates the concrete clkndata_class objects.
    // This works because of the factory overrides made from the calls to clkndata_if.use_concrete_class.
    // In this example, m_config.iface_string will be set to "clkndata_if_4_4" or "clkndata_if_8_8"
    
    m_clkndata_if = clkndata_if_base::type_id::create(m_config.iface_string, this); 
  endclass
 
  function void connect_phase(uvm_phase phase);
    ...
    m_driver.m_clkndata_if  = m_clkndata_if;
    m_monitor.m_clkndata_if = m_clkndata_if;
  endclass

(Note that the abstract base class variables name m_clkndata_if differs from the string names "clkndata_if_4_4" and "clkndata_if_8_8" because the string names play a special role in identifying the concrete interface objects, explained below. In general, if this were not the case, it would be better style for the variable names and string names to match.)

The parameterized SystemVerilog interface. This example has two integral parameters, but this technique does not impose any inherent limitations on the number or type of parameters. The interface contains the concrete implementation of the abstract base class within its scope. Because of this, the concrete class implementation can access any variables, wires, or parameters defined in the SystemVerilog interface. It is important that the concrete interface class has the same number and type of parameters as the SystemVerilog interface itself, because this permits a unique instantiation of the interface class for each unique instantiation of the SystemVerilog interface, which in turn allows a single variable of type clkndata_if_base (in this example) to access any instance of the parameterized interface in an unambiguous way (because the variable actually refers to a concrete object the type of which is defined inside the interface). This is the crux of the matter, though it might require some thought to really appreciate what is going on.

interface clkndata_if #(parameter AW = 1, DW = 1);
  ...
  bit [AW-1:0] addr;
  bit [DW-1:0] data;
  // Other variables, wires, and parameters
  ...

  // Concrete implementation of interface class within the scope of the SystemVerilog interface
  class clkndata_class #(int AW = 0, DW = 0) extends clkndata_if_base;
    `uvm_component_param_utils(clkndata_class#(AW,DW))

    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
    
    // Implement dummy methods from abstract base class
    task write(longint addr_arg, data_arg);
      ...
      addr[AW-1:0] <= addr_arg[AW-1:0];
      data[DW-1:0] <= data_arg[DW-1:0];
      ...
    endtask
    
    // Other methods
    ...
  endclass
  
  function void use_concrete_class;
    string path_name;
    path_name = $sformatf("*.clkndata_if_%0d_%0d", AW, DW);
    clkndata_if_base::type_id::set_inst_override( clkndata_class#(AW,DW)::get_type(), path_name, null);
  endfunction

endinterface

The module that instantiates this parameterized SystemVerilog interface can call the convenience function use_concrete_class for each parameterized interface instance to set factory overrides to instantiate the concrete objects in place of the abstract base class.

module top;
  ...
  clkndata_if #(4, 4) clkndata_if_4 ();
  clkndata_if #(8, 8) clkndata_if_8 ();
  
  dut dut_inst ( .ifport_4(clkndata_if_4), .ifport_8(clkndata_if_8) );
  
  initial
  begin
    clkndata_if_4.use_concrete_class();
    clkndata_if_8.use_concrete_class(); // Call for each instance of the parameterized interface
    
    uvm_top.run_test();
  end
endmodule

The trick here is that use_concrete_class sets a path name for the factory instance override that uniquely identifies the values of the parameters to the particular interface instantiation. The final part of the path name used for the factory override, e.g.

"clkndata_if_8_8"

must be used as the string name of the concrete interface object when it is instantiated within the UVM verification environment using the factory as shown in the example above, that is:

clkndata_if_base::type_id::create("clkndata_if_8_8", this)

The factory instance override replaces type clkndata_if_base with type clkndata_class#(AW,DW) when the factory creates the object, and that class has access to the variables declared within precisely the correct instantiation of the interface, i.e. the one with AW = 8 and DW = 8.




Links

Easier UVM Coding Guidelines
Introduction to the Easier UVM Coding Guidelines
Summary of the Easier UVM Coding Guidelines
Detailed Explanation of the Easier UVM Coding Guidelines
Easier UVM Glossary
Easier UVM Coding Guidelines - Download

Easier UVM - Deeper Explanations
Coverage-Driven Verification Methodology
Requests, Responses, Layered Protocols and Layered Agents
How to Access a Parameterized SystemVerilog Interface from UVM

Easier UVM Code Generator
Easier UVM Code Generator - Download
Easier UVM Code Generator - Tutorial Part 1: Getting Started
Easier UVM Code Generator - Tutorial Part 2: Adding User-Defined Code
Easier UVM Code Generator - Tutorial Part 3: Adding the Register Layer
Easier UVM Code Generator - Tutorial Part 4: Hierarchical Verification Environments
Easier UVM Code Generator - Tutorial Part 5: Split Transactors
Easier UVM Code Generator - Frequently Asked Questions (FAQ)
Easier UVM Code Generator - Reference Guide

Easier UVM Video Tutorial
Introducing Easier UVM
Easier UVM - The Big Picture
Key Concepts of the Easier UVM Code Generator
Running Easier UVM in EDA Playground
Easier UVM - Components and Phases
Easier UVM - Configuration
TLM Connections in UVM
Easier UVM - Transaction Classes
Easier UVM - Sequences
Easier UVM - Tests
Easier UVM - Reporting
Easier UVM - Register Layer
Easier UVM - Parameterized Interfaces
Easier UVM - Scoreboards
The Finer Points of UVM Sequences (Recorded Webinar)
UVM Run-Time Phasing (Recorded Webinar)

A YouTube playlist with all the above videos and more

Easier UVM Paper and Poster
Easier UVM - Coding Guidelines and Code Generation - as presented at DVCon 2014

Easier UVM Q&A Forum
Easier UVM Google Group

Easier UVM Examples Ready-to-Run on EDA Playground
Minimal example with driver
Minimal example with coverage in a subscriber as well as driver and monitor.
Minimal example with register sequence and register block
Example with four interfaces/agents, two of which use a register model.
Minimal example with dual-top modules and split transactors
Minimal example showing a UVM sequence getting information from the config database
Minimal example showing features of objections and the command line processor
Minimal example showing the reporting features of UVM.
Example that drops an objection when coverage exceeds some threshold
Example that sends a response transaction from the driver back to the uvm_reg_adapter
Example that uses a frontdoor sequence to pass a response object back to the register sequence that called read/write
Example of a parameterized interface generated from an Easier UVM interface template file
Example that pulls in a user-defined parameterized interface
Example of a reference model with the Syosil scoreboard

Back to the full list of UVM Resources

Privacy Policy Site Map Contact Us