Jump to content

Returning information from a sub-sequence.


Recommended Posts

I am planning on writing several uvm_reg_sequences to model firmware controlling my dut. I will have a top level virtual sequence which acts as a manager. It will start sequences, which will possibly in turn start other sub-sequences. Beings these are register sequences, it is highly likely that they have interrogated the design and learned something useful for my testbench. How should that get reported back to the manager?

The problem is, I'm trying to create some vertically reusable register sequences. The block level team will define a Manager sequence, however it will not be vertically reusable. It will be specific to their testbench and not portable. I will develop my own Manager for the SoC level. Beings they will deliver their sequences to me, my manager class will know all about their sequences. But they will not know about my Manager class. I'd like to avoid crafting abstract classes to communicate, as it's unclear what the API should be at this stage.

Illustration:

Sequences: Manager(virtual), IrqHandler(parent), ProcessEventA(child), ProcessEventB(child)

task Manager::body()
    fork
    begin
        IrqHandler irq_handler;
        wait_for_interrupt();
        `uvm_do(irq_handler);
        // how do I determine which irq_handler sub-sequence ran, and get the information out?
    end
    fork
        // start some other sequences
    end
    join
endtask

task IrqHandler::body()
    ProcessEventA process_event_a;
    ProcessEventB process_event_b;
    model.irq_cause.read(...,cause,...);
    case (cause)
        EVENT_A: `uvm_do(process_event_a)
        EVENT_B: `uvm_do(process_event_
    endcase
endtask

task ProcessEventA::body()
    model.some_status.read(...,status,...);
    // how do I report status back up to the Manager?
endtask

My proposed solution is to change the Manager and IrqHandler like this:

task Manager::body()
    fork
    begin
        IrqHandler irq_handler;
        wait_for_interrupt();
        `uvm_do(irq_handler);
        //                                    add this foreach block
        foreach (irq_handler.m_called_sequences[i]) begin
            ProcessEventA process_event_a;
            ProcessEventB process_event_b;
            if ($cast(process_event_a,m_called_sequences[i]))
                // poke around inside process_event_a to get resulting status
            else if ($cast(process_event_b,m_called_sequences[i]))
                // poke around inside process_event_b to get resulting status
        end
    end
    fork
        // start some other sequences
    end
    join
endtask

class IrqHandler extends uvm_reg_sequence;
    uvm_reg_sequence m_called_sequences[$];  // <- add this
    virtual task body();
        ProcessEventA process_event_a;
        ProcessEventB process_event_b;
        model.irq_cause.read(...,cause,...);
        case (cause)
            EVENT_A: begin
                `uvm_do(process_event_a)
                m_called_sequences.push_back(process_event_a); // <- add this
            end
            EVENT_B: begin
                `uvm_do(process_event_
                m_called_sequences.push_back(process_event_; // <- add this
            end
        endcase
    endtask
endclass

Two thoughts on this. First, the change to my Manager is functional, but ugly. Second, shouldn't tracking sub-sequence execution like this be built into uvm_sequence_base?

-Ryan

Link to comment
Share on other sites

I have made a polished solution. Rather than having the parent thread explicitly push things onto a queue, I've implemented the post_do callback. And rather than adding that to every class I'd like to do that for, I made a base class that does it. I have rather obnoxiously named this my uvm_plus_pkg.

`ifndef UVM_PLUS_PKG
`define UVM_PLUS_PKG

package uvm_plus_pkg;

import uvm_pkg::*;

// parameterized utility functions
class uvm_type #(type TGT=uvm_object);

   typedef TGT queue[$];

   // $cast doesn't handle $cast(lhs_q,rhs_q), so this does
   static function queue_cast(ref TGT lhs[$], const ref uvm_object rhs[$]);
      TGT tmp;
      lhs.delete();
      foreach (rhs[i]) begin
         $cast(tmp,rhs[i]);
         lhs.push_back(tmp);
      end
   endfunction // cast

   // takes rhs queue and filters for items that are extensions of TGT
   static function queue queue_filter(const ref uvm_object rhs[$]);
      TGT tmp;
      uvm_object filtered[$];
      filtered = rhs.find(x) with ($cast(tmp,x));
      queue_cast(queue_filter,filtered);
   endfunction

endclass

typedef uvm_sequence_item uvm_sequence_item_queue[$];

// To make your parent sequence record completed items or subsequences, extend this class.
class uvm_recording_sequence #(type REQ=uvm_sequence_item, type RSP=REQ) extends uvm_sequence#(REQ,RSP);
   local uvm_sequence_item_queue m_completed_items;

   function new ( string name = "SubSeq" );
      super.new(name);
   endfunction: new

   function uvm_sequence_item_queue get_completed_items();
      get_completed_items = m_completed_items;
   endfunction:get_completed_items

   // if you overload this, be sure to call super.pre_start()
   virtual task pre_start();
      m_completed_items.delete();
   endtask

   // if you overload this, be sure to call super.post_do()
   virtual function void post_do(uvm_sequence_item this_item);
      m_completed_items.push_back(this_item);
   endfunction

endclass: uvm_recording_sequence

// That last class replaces regular uvm_sequences, this typedef replaces uvm_reg_sequences
typedef uvm_reg_sequence#(uvm_recording_sequence#(uvm_reg_item)) uvm_recording_reg_sequence;

endpackage: uvm_plus_pkg

// Convenience macro, gets completed items from src and returns a type filtered queue.
`define uvm_completed_item_filter(T,tgt,src) \
   begin \
      uvm_object _ci[$]; \
      _ci = src.get_completed_items(); \
      tgt = uvm_type#(T)::queue_filter(_ci); \
   end

`endif //  `ifndef UVM_PLUS_PKG

I can then use that with sequences that look like this:

class RalRwr extends uvm_reg_sequence;

   `uvm_object_utils(RalRwr)

   uvm_reg_pkg::ral_block_slave model;

   virtual task body();
      bit [31:0] rdata;
      bit [31:0] wdata;
      uvm_status_e     uvm_status;
      uvm_config_db#(uvm_reg_pkg::ral_block_slave)::get(null, "uvm_ral", "model", model);
      model.AUX1.read(uvm_status, rdata, .parent(this));
      model.AUX1.write(uvm_status, rdata+1, .parent(this));
      model.AUX1.read(uvm_status, wdata, .parent(this));
   endtask // body

   virtual function void post_do(uvm_sequence_item this_item);
      this_item.print();
   endfunction

endclass // RWR

class RalRw extends uvm_reg_sequence;

   `uvm_object_utils(RalRw)

   uvm_reg_pkg::ral_block_slave model;

   virtual task body();
      bit [31:0] rdata;
      bit [31:0] wdata;
      uvm_status_e     uvm_status;
      uvm_config_db#(uvm_reg_pkg::ral_block_slave)::get(null, "uvm_ral", "model", model);
      model.AUX1.read(uvm_status, rdata, .parent(this));
      model.AUX1.write(uvm_status, rdata+1, .parent(this));
   endtask // body

   virtual function void post_do(uvm_sequence_item this_item);
      this_item.print();
   endfunction

endclass // RW

class RalParentSeq extends uvm_recording_reg_sequence;

   `uvm_object_utils(RalParentSeq)

   virtual task body();
      RalRw rw;
      RalRwr rwr;
      `uvm_do(rw)
      `uvm_do(rwr)
      rwr = null;
      `uvm_do(rwr)
   endtask // body

endclass

And the code starting the RalParentSeq can interrogate this like this:

rp = RalParentSeq::type_id::create("rp",this);
      rp.start(env.apb_master_agent.sqr);
      `uvm_completed_item_filter(RalRwr,rsq,rp);
      foreach (rsq[i]) begin
         rsq[i].print();
      end

So all-in-all the ugliness is fairly well hidden. I'm still not sure if UVM already had something in mind for allowing grandparents to interrogate grandchild sequence items.

-Ryan

Link to comment
Share on other sites

And now for a different approach. Rather than recording items, I'm promoting them. Such that my post_do function calls the parent's post_do.

// Rather than recording items, this sequence promotes the post_do to the parent.
// That means the parent sequence will be notified of sub-sequence items completing.
class uvm_promoting_sequence #(type REQ=uvm_sequence_item, type RSP=REQ) extends uvm_sequence#(REQ,RSP);

   function new ( string name = "uvm_promoting_sequence" );
      super.new(name);
   endfunction: new

   // if you overload this, be sure to call super.post_do()
   virtual function void post_do(uvm_sequence_item this_item);
      if (m_parent_sequence != null)
        m_parent_sequence.post_do(this_item);
   endfunction

endclass: uvm_promoting_sequence

// That last class replaces regular uvm_sequences, this typedef replaces uvm_reg_sequences
typedef uvm_reg_sequence#(uvm_promoting_sequence#(uvm_reg_item)) uvm_promoting_reg_sequence;

Now I write some sequences that use this like so:

class RalPromoParentSeq extends uvm_promoting_reg_sequence;

   `uvm_object_utils(RalPromoParentSeq)

   virtual task body();
      RalRw rw;
      RalRwr rwr;
      `uvm_do(rw)
      `uvm_do(rwr)
      rwr = null;
      `uvm_do(rwr)
   endtask // body

endclass

class RalManager extends uvm_reg_sequence;
   `uvm_object_utils(RalManager)
   virtual task body();
      RalPromoParentSeq pp;
      `uvm_do(pp)
   endtask // body

   virtual function void post_do(uvm_sequence_item this_item);
      RalPromoParentSeq tst;
      if (!$cast(tst,this_item)) begin
         uvm_default_printer.knobs.prefix = "RalManager subseq pp executed: ";
         this_item.print();
         uvm_default_printer.knobs.prefix = "";
      end
   endfunction

endclass

The RalManager's post_do will now be called at the completion of each sub-sequence of RalPromoParentSeq. This allows RalPromoParentSeq to reactive in a more timely manner.

-Ryan

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...