Search code examples
system-veriloguvm

Grab Transactions inside UVM_Sequencer Run Phase


I want to grab the transactions inside my uvm_sequencer's run_phase to check if the transactions are crossing 4KB boundary. In case they cross the 4KB boundary I want to split that transaction into multiple transactions and then send it to the driver. When the response is received from the driver, all the split transactions response should be merged back into the original transaction and returned back to the sequence which generated the original transaction. Is there a way to do this? And is uvm_sequencer the right place to do this job? Any help would be greatly appreciated.

Edit:

Tudor's solution does work. I added a few edits to his code (translation_sequence) as below:

up_sequencer.get_next_item(req);
$cast(up_trans, req.clone());
// do the splitting.
start_item(up_trans);
finish_item(up_trans);
up_sequencer.item_done();
get_response(rsp);
rsp.set_sequence_id(req.get_sequence_id());
up_sequencer.put_response(rsp);

Solution

  • You can't do this in the sequencer, because sending an item is performed in 2 steps: start_item(...) and finish_item(...). If it were only one method, you could have done there.

    The way to go, IMO, is to implement a layering scheme. Your upper layer is the one where you start your sequences, where you don't care about the length of the transaction. Your second layer is the one where the maximum length is 4K. The flow of transactions would be:

    your sequence -> upper sequencer -> translation sequence -> bus sequencer -> driver

    This means that in your agent, you'll need two sequencers, where only one is connected to the driver. A translation sequence might look like this:

    class translation_sequence extends uvm_sequence #(some_trans);
      uvm_sequencer #(some_trans) up_sequencer;
    
      task body();
        while (1) begin
          some_trans up_trans;
          up_sequencer.get_next_item(up_trans);
    
          if (up_trans.size <= 4096) begin
            `uvm_info("SPLIT", "No need to split", UVM_LOW);
            start_item(up_trans);
            finish_item(up_trans);
          end
          else begin
            // implement the splitting
            int unsigned size = up_trans.size;
    
            while (1) begin
              req = new();
              req.size = size > 4096 ? 4096 : size;
              start_item(req);
              finish_item(req);
    
              if (size < 4096)
                break;
              else
                size -= 4096;
            end
          end
    
          up_sequencer.item_done();
        end
      endtask
    endclass
    

    Notice that it has a pointer to the upper level sequencer and it gets items from it and signals their completion. In the agent you need to start this sequence, also giving it a pointer to the upper sequencer:

    class some_agent extends uvm_agent;
      //...
    
      // start the translation sequence
      task run_phase(uvm_phase phase);
        translation_sequence seq = new();
        seq.up_sequencer = sequencer;
        seq.start(bus_sequencer);
      endtask
    endclass
    

    If you want to read more on layering, have a look at this article on Verification Horizons.