«Бог не меняет того, что (происходит) с людьми, пока они сами не изменят своих помыслов.» Коран, Сура 12:13

UVM/UVM Cookbook/Sequences/Slave

Материал из Wiki

< UVM
Перейти к: навигация, поиск

Содержание

Overview

Пассивная сиквенс используется с драйвером, который отвечает на события на интерфейсе, а не инициирует их. Этот тип функциональности обычно называется как responder.

A responder can be implemented in several ways, for instance a simple bus oriented responder could be implemented as a uvm_component interacting with a slave interface and reading and writing from memory according to the requests from the bus master. The advantage of using a slave sequence is that the way in which the slave responds can be easily changed.

One interesting characteristic of responder functionality is that it is not usually possible to predict when a response to a request will be required. For this reason slave sequences tend to be implemented as long-lasting sequences, i.e. they last for the whole of the simulation providing the responder functionality.

In this article, two approaches to implementing a slave sequence are described:

  • Using a single sequence item
  • Using a sequence item for each slave phase (in the APB example used as an illustration there are two phases).

In both cases, the sequence and the driver loop through the following transitions:

  1. Slave sequence sends a request to the driver - "Tell me what to do"
  2. Driver detects a bus level request and returns the information back to the sequence - "This is what you should do"
  3. Slave sequence does what it needs to do to prepare a response and then sends a response item to the driver - "Here you go"
  4. Driver completes the bus level response with the contents of the response item, completes handshake back to the sequence - "Thank you"

Single Sequence Item Implementation

In this implementation, only one sequence item is used for both the request and response halves of the slave loop.

Sequence item

The response properties of a slave sequence item should be marked as rand and the properties driven during the master request should not. If you were to compare a master sequence_item and a slave sequence_item for the same bus protocol, then you would find that which properties were marked rand and which were not would be reversed.

class apb_slave_seq_item extends uvm_sequence_item;
  `uvm_object_utils(apb_slave_seq_item)
 
  //------------------------------------------
  // Data Members (Outputs rand, inputs non-rand)
  //------------------------------------------
  logic[31:0] addr;
  logic[31:0] wdata;
  logic rw;
 
  rand logic[31:0] rdata;
  rand logic slv_err;
  rand int delay;
 
  //------------------------------------------
  // Constraints
  //------------------------------------------
  constraint delay_bounds {
    delay inside {[0:2]};
  }
 
  constraint error_dist {
    slv_err dist {0 := 80, 1 := 20};
  }
 
  //------------------------------------------
  // Methods
  //------------------------------------------
  extern function new(string name = "apb_slave_seq_item");
  extern function void do_copy(uvm_object rhs);
  extern function bit do_compare(uvm_object rhs, uvm_comparer comparer);
  extern function string convert2string();
  extern function void do_print(uvm_printer printer);
  extern function void do_record(uvm_recorder recorder);
 
endclass:apb_slave_seq_item

Sequence and Driver

A slave device receives a request from the master and responds back with the relevant information. In order for the slave sequence to model this behavior, it needs to use multiple objects of the slave sequence item to accomplish the different phases. Depending on the bus protocol, at least two objects of the slave sequence item need to be used; one to get the request information and the second to return the response.

The process starts by the sequence sending a request object to the driver. The sequence does not need to initialize the contents of this object since it will be populated by the driver when it identifies a valid request on the pin level interface. When the driver calls the item_done() method, the slave sequence is unblocked and can use the contents of the request object to prepare the response object.

Once the slave sequence has processed the request, it creates and populates a response object and calls start/finish_item() to send it to the driver. The driver uses the response data to complete the response part of the pin level transfer and then unblocks the slave sequence execution by calling item_done(). The slave sequence may randomize the response data in some way.

Example slave sequence code:

task apb_slave_sequence::body;
  apb_slave_agent_config m_cfg = apb_slave_agent_config::get_config(m_sequencer);
  apb_slave_seq_item req;
  apb_slave_seq_item rsp;
 
  wait (m_cfg.APB.PRESETn);
  forever begin
 
    req = apb_slave_seq_item::type_id::create("req");
    rsp = apb_slave_seq_item::type_id::create("rsp");
 
    // Slave request:
    start_item(req);
    finish_item(req);
 
    // Slave response:	
    if (req.rw) begin
        memory[req.addr] = req.wdata;
    end	
    start_item(rsp);
    rsp.copy(req);
    assert (rsp.randomize() with {if(!rsp.rw) rsp.rdata == memory[rsp.addr];});
    finish_item(rsp);
  end
 
endtask:body

Example slave driver code:

task apb_slave_driver::run_phase(uvm_phase phase);
  apb_slave_seq_item req;
  apb_slave_seq_item rsp;
 
  forever
   begin
     if (!APB.PRESETn) begin
       APB.PREADY = 1'b0;
       APB.PSLVERR = 1'b0;
       @(posedge APB.PCLK);
     end
     else begin
       // Setup Phase
       seq_item_port.get_next_item(req);
 
       // Setup phase activity
 
       seq_item_port.item_done();
 
       // Access Phase
       seq_item_port.get_next_item(rsp);
 
       // Access phase activity
 
       seq_item_port.item_done();
     end
   end
 
endtask: run_phase
Example Download Link
Complete APB3 Slave Agent
Download-tarball.png Download a complete working example:
(tarball: slave_agent.tgz)

Multiple Sequence Items Implementation

Sequence items

In this implementation, we will use more than one sequence item (usually called phase level sequence items) to implement the slave functionality. Depending on the bus protocol, at least two sequence items will be required; one to implement the request phase and a second to implement the response phase.One way of looking at this is to consider it as the single sequence implementation sliced in two. However, with more complex protocols there could be more than two phase level sequence items.

The request sequence item will contain those data members that are not random and will, consequently, have no constraints. The response sequence item will contain all those random data members in addition to some data members that overlap with the request sequence item. Those overlapping members are needed by the response sequence item to make some decisions, for example a read/write bit is required by the driver to know if it needs to drive the read data bus with valid data.

For example, this is the APB3 slave setup (request) sequence item.

class apb_slave_setup_item extends apb_sequence_item;
  `uvm_object_utils(apb_slave_setup_item)
 
  //------------------------------------------
  // Data Members (Outputs rand, inputs non-rand)
  //------------------------------------------
  logic[31:0] addr;
  logic[31:0] wdata;
  logic rw;
 
endclass

And this is the access (response) sequence item. Note the rand data members and the constraints.

class apb_slave_access_item extends apb_sequence_item;
  `uvm_object_utils(apb_slave_setup_item)
 
  //------------------------------------------
  // Data Members (Outputs rand, inputs non-rand)
  //------------------------------------------
  rand logic rw;
 
  rand logic[31:0] rdata;
  rand logic slv_err;
  rand int delay;
 
  constraint delay_bounds {
    delay inside {[0:2]};
  }
 
  constraint error_dist {
    slv_err dist {0 := 80, 1 := 20};
  }
endclass

Sequence and Driver

The slave sequence and driver in this implementation are very similar to that described above. However, the major difference is that the sequencer and the driver are parameterised with the apb_sequence_item base class so that both request and response sequence item type objects can be passed between the sequence and the driver. The driver casts the received sequence item object to the appropriate request or response sequence item type before it accesses the contents.

For the sequence, the only difference will be in the parameterization of the class template and no casting is required at all. The sequence body remains the same.

class apb_slave_sequence extends uvm_sequence #(apb_sequence_item);

As a consequence, the Sequencer's class definition will change as well.

class apb_slave_sequencer extends uvm_sequencer #(apb_sequence_item);

The driver's class definition will change too.

class apb_slave_driver extends uvm_driver #(apb_sequence_item, apb_sequence_item);

The run_phase of the driver will always use the uvm_sequence_item to get_next_item and then cast the received sequence item to the appropriate/correct type.

task apb_slave_driver::run_phase(uvm_phase phase);
  apb_sequence_item item;
  apb_slave_setup_item req;
  apb_slave_access_item rsp;
 
  forever
   begin
     if (!APB.PRESETn) begin
         ...
	 end
	 else begin
	   seq_item_port.get_next_item(item);
	   if ($cast(req, item)) 
	   begin
           ....
	   end
	   else
	     `uvm_error("CASTFAIL", "The received sequence item is not a request seq_item");
  	   seq_item_port.item_done();
 
	   // Access Phase
	   seq_item_port.get_next_item(item);
	   if ($cast(rsp, item))
	   begin
           ....
	   end
	   else
	     `uvm_error("CASTFAIL", "The received sequence item is not a response seq_item");
  	   seq_item_port.item_done();
 
	 end
   end
endtask: run_phase
Example Download Link
Complete APB3 Slave Agent using multiple sequence items
Download-tarball.png Download a complete working example:
(tarball: slave_agent_multi_seq_item.tgz)