uvm_cookbook--sequences--wait for a signal

In the general case, synchronizing to hardware events is taken care of in UVM testbenches by drivers (proxy & BFM) and monitors (proxy & BFM). However, there are some cases where it is useful for a sequence or a component to synchronize to a hardware event such as a clock without interacting with a driver or a monitor. This can be facilitated by adding methods to an object containing a virtual interface BFM handle (usually a configuration object) that call a task in the BFM that blocks until a hardware event occurs. A further refinement is to add a get_config() method which allows a component to retrieve a pointer to its configuration object based on its scope within the UVM testbench component hierarchy.

BFM Methods

Since BFMs are the only items which are allowed to access pins directly, the BFMs have hardware synchronization methods added to them which can then be called by code in a testbench such as configuration objects. Examples of hardware synchronization methods include:

  • wait_for_clock()
  • wait_for_reset()
  • wait_for_interrupt()
  • interrupt_cleared()

These hardware synchronization methods are declared explicitly as automatic because static entities such as interfaces have implicitly static tasks. Since multiple threads/locations in a testbench way want to wait for a number of clock cycles or wait for a reset signal, we need to be able to invoke the tasks multiple times without overwriting the values used for each invocation.

In general the monitor BFM should be targeted for adding the extra methods since the monitor BFM would be present in both active and passive modes.

An example BFM:

interface bidirect_bus_monitor_bfm (bus_if BUS);
 
//
// Task: wait_for_clock
//
// This method waits for n clock cycles. 
task automatic wait_for_clock( int n = 1 ); 
 repeat( n ) @( posedge BUS.clk );
endtask : wait_for_clock
 
//
// Task: wait_for_reset
//
// This method waits for the end of reset. 
task automatic wait_for_reset();
 // Wait for reset to end
 @(posedge BUS.resetn);
endtask : wait_for_reset
 
//
// Task: wait_for_error
//
task automatic wait_for_error;
 @(posedge error);
endtask: wait_for_error
 
//
// Task: wait_for_no_error
//
task automatic wait_for_no_error;
 @(negedge error);
endtask: wait_for_no_error
 
endinterface : bidirect_bus_monitor_bfm

Additions to the configuration object

In order to support the use of a wait_for_interface_signal method, hardware synchronization methods have to be added to a configuration object. These hardware synchronization methods reference methods within the virtual interface BFM(s) which the configuration object contains handles for.

An example:

class bus_agent_config extends uvm_object;
 
`uvm_object_utils(bus_agent_config)
 
virtual bidirect_bus_monitor_bfm mon_bfm;  // This is the virtual interface with the methods to wait on
 
function new(string name = "bus_agent_config");
 super.new(name);
endfunction
 
//
// Task: wait_for_clock
//
// This method waits for n clock cycles. 
task wait_for_clock( int n = 1 );
 mon_bfm.wait_for_clock(n);
endtask
 
//
// Task: wait_for_reset
//
// This method waits for the end of reset. 
task wait_for_reset();
  mon_bfm.wait_for_reset();
endtask : wait_for_reset
 
//
// Task: wait_for_error
//
task wait_for_error();
  mon_bfm.wait_for_error();
endtask : wait_for_error
 
//
// Task: wait_for_no_error
// 
task wait_for_no_error();
  mon_bfm.wait_for_no_error();
endtask : wait_for_no_error
 
endclass: bus_agent_config

Using the wait_for_interface_signal method

In order to use the wait_for_xxx() method, the sequence or component must first ensure that it has a valid pointer to the configuration object. The pointer may already have been set up during construction or it may require the sequence or component to call the get_config() static method. Since sequences are not part of the UVM component hierarchy, they need to reference it via the m_sequencer pointer.

Once the local configuration object pointer is valid, the method can be called via the configuration object handle.

A sequence example:

class bus_seq extends uvm_sequence #(bus_seq_item);
 
`uvm_object_utils(bus_seq)
 
bus_seq_item req;
bus_agent_config m_cfg;
 
rand int limit = 40; // Controls the number of iterations
 
function new(string name = "bus_seq");
  super.new(name);
endfunction
 
task body;
  int i = 5;
  req = bus_seq_item::type_id::create("req");
  // Get the configuration object
  if (m_cfg == null)
    if(!uvm_config_db #(bus_agent_config)::get(null, get_full_name(), "config", m_cfg)) begin
      `uvm_error("BODY", "bus_agent_config not found")
    end
 
 
  repeat(limit)
    begin
      start_item(req);
      // The address is constrained to be within the address of the GPIO function
      // within the DUT, The result will be a request item for a read or a write
      if(!req.randomize() with {addr inside {[32'h0100_0000:32'h0100_001C]};}) begin
        `uvm_error("body", "randomization failed for req")
      end
      finish_item(req);
      // Wait for interface clocks:
      m_cfg.wait_for_clock(i);
      i++;
      // The req handle points to the object that the driver has updated with response data
      uvm_report_info("seq_body", req.convert2string());
    end
endtask: body
 
endclass: bus_seq

A component example:

//
// A coverage monitor that should ignore coverage collected during an error condition:
//
 
class transfer_link_coverage_monitor extends uvm_subscriber #(trans_link_item);
 
`uvm_component_utils(transfer_link_coverage_monitor)
 
T pkt;
 
// Error monitor bit
bit no_error;
// Configuration:
bus_agent_config m_cfg;
 
covergroup tlcm_1;
 HDR: coverpoint pkt.hdr;
 SPREAD: coverpoint pkt.payload {
 bins spread[] {[0:1023], [1024:8095], [8096:$]}
 }
 cross HDR, SPREAD;
endgroup: tlcm_1
 
function new(string name = "transfer_link_coverage_monitor", uvm_component_parent = null);
 super.new(name, parent);
 tlcm_1 = new;
endfunction
 
// The purpose of the run method is to monitor the state of the error
// line
task run_phase( uvm_phase phase );
 no_error = 0;
 // Get the configuration
 if (m_cfg == null)
   if(!uvm_config_db #(bus_agent_config)::get(this, "", "bus_agent_config", m_cfg)) begin
    `uvm_error("run_phase", "Configuration error: unable to find bus_agent_config")
   end
 m_cfg.wait_for_reset(); // Nothing valid until reset is over
 no_error = 1;
 
 forever begin
  m_cfg.wait_for_error(); // Blocks until an error occurs
  no_error = 0;
  m_cfg.wait_for_no_error(); // Blocks until the error is removed
 end
endtask: run_phase
 
function write(T t);
 pkt = t;
 if(no_error == 1) begin // Don't sample if there is an error
  tlcm_1.sample();
 end
endfunction: write
 
endclass: transfer_link_coverage_monitor

在interface中实现wait功能,在config中调用interface指针,在seq中get到config(或在coverage monitor中get到config)

参考:

Stimulus/Signal Wait | Verification Academy

原文地址:https://www.cnblogs.com/zhiminyu/p/14251362.html