//
// -------------------------------------------------------------
//    Copyright 2004-2009 Synopsys, Inc.
//    Copyright 2010 Mentor Graphics Corporation
//    Copyright 2010 Cadence Design Systems, Inc.
//    All Rights Reserved Worldwide
//
//    Licensed under the Apache License, Version 2.0 (the
//    "License"); you may not use this file except in
//    compliance with the License.  You may obtain a copy of
//    the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in
//    writing, software distributed under the License is
//    distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//    CONDITIONS OF ANY KIND, either express or implied.  See
//    the License for the specific language governing
//    permissions and limitations under the License.
// -------------------------------------------------------------
//

typedef class [docs]uvm_reg;
typedef class [docs]uvm_mem;
typedef class [docs]uvm_reg_backdoor;

//------------------------------------------------------------------------------
// Title: Register Callbacks
//
// This section defines the base class used for all register callback
// extensions. It also includes pre-defined callback extensions for use on
// read-only and write-only registers.
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// Class: uvm_reg_cbs
//
// Facade class for field, register, memory and backdoor
// access callback methods. 
//------------------------------------------------------------------------------

virtual class [docs]uvm_reg_cbs extends uvm_callback;

   function [docs]new(string name = "uvm_reg_cbs");
      super.new(name);
   endfunction


   // Task: pre_write
   //
   // Called before a write operation.
   //
   // All registered ~pre_write~ callback methods are invoked after the
   // invocation of the ~pre_write~ method of associated object (<uvm_reg>,
   // <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being
   // written is a <uvm_reg>, all ~pre_write~ callback methods are invoked
   // before the contained <uvm_reg_fields>. 
   //
   // Backdoor - <uvm_reg_backdoor::pre_write>,
   //            <uvm_reg_cbs::pre_write> cbs for backdoor.
   //
   // Register - <uvm_reg::pre_write>,
   //            <uvm_reg_cbs::pre_write> cbs for reg,
   //            then foreach field:
   //              <uvm_reg_field::pre_write>, 
   //              <uvm_reg_cbs::pre_write> cbs for field
   //
   // RegField - <uvm_reg_field::pre_write>,
   //            <uvm_reg_cbs::pre_write> cbs for field
   //
   // Memory   - <uvm_mem::pre_write>,
   //            <uvm_reg_cbs::pre_write> cbs for mem
   //
   // The ~rw~ argument holds information about the operation.
   //
   // - Modifying the ~value~ modifies the actual value written.
   //
   // - For memories, modifying the ~offset~ modifies the offset
   //   used in the operation.
   //
   // - For non-backdoor operations, modifying the access ~path~ or
   //   address ~map~ modifies the actual path or map used in the
   //   operation.
   //
   // If the ~rw.status~ is modified to anything other than <UVM_IS_OK>,
   // the operation is aborted.
   //
   // See <uvm_reg_item> for details on ~rw~ information.
   //
   virtual task [docs]pre_write(uvm_reg_item rw); endtask


   // Task: post_write
   //
   // Called after a write operation.
   //
   // All registered ~post_write~ callback methods are invoked before the
   // invocation of the ~post_write~ method of the associated object (<uvm_reg>,
   // <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being
   // written is a <uvm_reg>, all ~post_write~ callback methods are invoked
   // before the contained <uvm_reg_fields>. 
   //
   // Summary of callback order:
   //
   // Backdoor - <uvm_reg_cbs::post_write> cbs for backdoor,
   //            <uvm_reg_backdoor::post_write>
   //
   // Register - <uvm_reg_cbs::post_write> cbs for reg,
   //            <uvm_reg::post_write>,
   //            then foreach field:
   //              <uvm_reg_cbs::post_write> cbs for field,
   //              <uvm_reg_field::post_read>
   //
   // RegField - <uvm_reg_cbs::post_write> cbs for field,
   //            <uvm_reg_field::post_write>
   //
   // Memory   - <uvm_reg_cbs::post_write> cbs for mem,
   //            <uvm_mem::post_write>
   //
   // The ~rw~ argument holds information about the operation.
   //
   // - Modifying the ~status~ member modifies the returned status.
   //
   // - Modifying the ~value~ or ~offset~ members has no effect, as
   //   the operation has already completed.
   //
   // See <uvm_reg_item> for details on ~rw~ information.
   //
   virtual task [docs]post_write(uvm_reg_item rw); endtask


   // Task: pre_read
   //
   // Callback called before a read operation.
   //
   // All registered ~pre_read~ callback methods are invoked after the
   // invocation of the ~pre_read~ method of associated object (<uvm_reg>,
   // <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being
   // read is a <uvm_reg>, all ~pre_read~ callback methods are invoked before
   // the contained <uvm_reg_fields>. 
   //
   // Backdoor - <uvm_reg_backdoor::pre_read>,
   //            <uvm_reg_cbs::pre_read> cbs for backdoor
   //
   // Register - <uvm_reg::pre_read>,
   //            <uvm_reg_cbs::pre_read> cbs for reg,
   //            then foreach field:
   //              <uvm_reg_field::pre_read>,
   //              <uvm_reg_cbs::pre_read> cbs for field
   //
   // RegField - <uvm_reg_field::pre_read>,
   //            <uvm_reg_cbs::pre_read> cbs for field
   //
   // Memory   - <uvm_mem::pre_read>,
   //            <uvm_reg_cbs::pre_read> cbs for mem
   //
   // The ~rw~ argument holds information about the operation.
   //
   // - The ~value~ member of ~rw~ is not used has no effect if modified.
   //
   // - For memories, modifying the ~offset~ modifies the offset
   //   used in the operation.
   //
   // - For non-backdoor operations, modifying the access ~path~ or
   //   address ~map~ modifies the actual path or map used in the
   //   operation.
   //
   // If the ~rw.status~ is modified to anything other than <UVM_IS_OK>,
   // the operation is aborted.
   //
   // See <uvm_reg_item> for details on ~rw~ information.
   //
   virtual task [docs]pre_read(uvm_reg_item rw); endtask


   // Task: post_read
   //
   // Callback called after a read operation.
   //
   // All registered ~post_read~ callback methods are invoked before the
   // invocation of the ~post_read~ method of the associated object (<uvm_reg>,
   // <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being read
   // is a <uvm_reg>, all ~post_read~ callback methods are invoked before the
   // contained <uvm_reg_fields>. 
   //
   // Backdoor - <uvm_reg_cbs::post_read> cbs for backdoor,
   //            <uvm_reg_backdoor::post_read>
   //
   // Register - <uvm_reg_cbs::post_read> cbs for reg,
   //            <uvm_reg::post_read>,
   //            then foreach field:
   //              <uvm_reg_cbs::post_read> cbs for field,
   //              <uvm_reg_field::post_read>
   //
   // RegField - <uvm_reg_cbs::post_read> cbs for field,
   //            <uvm_reg_field::post_read>
   //
   // Memory   - <uvm_reg_cbs::post_read> cbs for mem,
   //            <uvm_mem::post_read>
   //
   // The ~rw~ argument holds information about the operation.
   //
   // - Modifying the readback ~value~ or ~status~ modifies the actual
   //   returned value and status.
   //
   // - Modifying the ~value~ or ~offset~ members has no effect, as
   //   the operation has already completed.
   //
   // See <uvm_reg_item> for details on ~rw~ information.
   //
   virtual task [docs]post_read(uvm_reg_item rw); endtask


   // Task: post_predict
   //
   // Called by the <uvm_reg_field::predict()> method
   // after a successful UVM_PREDICT_READ or UVM_PREDICT_WRITE prediction.
   //
   // ~previous~ is the previous value in the mirror and
   // ~value~ is the latest predicted value. Any change to ~value~ will
   // modify the predicted mirror value.
   //
   virtual function void [docs]post_predict(input uvm_reg_field  fld,
                                      input uvm_reg_data_t previous,
                                      inout uvm_reg_data_t value,
                                      input uvm_predict_e  kind,
                                      input uvm_path_e     path,
                                      input uvm_reg_map    map);
   endfunction


   // Function: encode
   //
   // Data encoder
   //
   // The registered callback methods are invoked in order of registration
   // after all the ~pre_write~ methods have been called.
   // The encoded data is passed through each invocation in sequence.
   // This allows the ~pre_write~ methods to deal with clear-text data.
   //
   // By default, the data is not modified.
   //
   virtual function void [docs]encode(ref uvm_reg_data_t data[]);
   endfunction


   // Function: decode
   //
   // Data decode
   //
   // The registered callback methods are invoked in ~reverse order~
   // of registration before all the ~post_read~ methods are called.
   // The decoded data is passed through each invocation in sequence.
   // This allows the ~post_read~ methods to deal with clear-text data.
   //
   // The reversal of the invocation order is to allow the decoding
   // of the data to be performed in the opposite order of the encoding
   // with both operations specified in the same callback extension.
   // 
   // By default, the data is not modified.
   //
   virtual function void [docs]decode(ref uvm_reg_data_t data[]);
   endfunction



endclass


//------------------
// Section: Typedefs
//------------------


// Type: uvm_reg_cb
//
// Convenience callback type declaration for registers
//
// Use this declaration to register the register callbacks rather than
// the more verbose parameterized class
//
typedef uvm_callbacks#(uvm_reg, uvm_reg_cbs) [docs]uvm_reg_cb;


// Type: uvm_reg_cb_iter
//
// Convenience callback iterator type declaration for registers
//
// Use this declaration to iterate over registered register callbacks
// rather than the more verbose parameterized class
//
typedef uvm_callback_iter#(uvm_reg, uvm_reg_cbs) [docs]uvm_reg_cb_iter;


// Type: uvm_reg_bd_cb
//
// Convenience callback type declaration for backdoor
//
// Use this declaration to register register backdoor callbacks rather than
// the more verbose parameterized class
//
typedef uvm_callbacks#(uvm_reg_backdoor, uvm_reg_cbs) [docs]uvm_reg_bd_cb;


// Type: uvm_reg_bd_cb_iter
// Convenience callback iterator type declaration for backdoor
//
// Use this declaration to iterate over registered register backdoor callbacks
// rather than the more verbose parameterized class
//

typedef uvm_callback_iter#(uvm_reg_backdoor, uvm_reg_cbs) [docs]uvm_reg_bd_cb_iter;


// Type: uvm_mem_cb
//
// Convenience callback type declaration for memories
//
// Use this declaration to register memory callbacks rather than
// the more verbose parameterized class
//
typedef uvm_callbacks#(uvm_mem, uvm_reg_cbs) [docs]uvm_mem_cb;


// Type: uvm_mem_cb_iter
//
// Convenience callback iterator type declaration for memories
//
// Use this declaration to iterate over registered memory callbacks
// rather than the more verbose parameterized class
//
typedef uvm_callback_iter#(uvm_mem, uvm_reg_cbs) [docs]uvm_mem_cb_iter;


// Type: uvm_reg_field_cb
//
// Convenience callback type declaration for fields
//
// Use this declaration to register field callbacks rather than
// the more verbose parameterized class
//
typedef uvm_callbacks#(uvm_reg_field, uvm_reg_cbs) [docs]uvm_reg_field_cb;


// Type: uvm_reg_field_cb_iter
//
// Convenience callback iterator type declaration for fields
//
// Use this declaration to iterate over registered field callbacks
// rather than the more verbose parameterized class
//
typedef uvm_callback_iter#(uvm_reg_field, uvm_reg_cbs) [docs]uvm_reg_field_cb_iter;


//-----------------------------
// Group: Predefined Extensions
//-----------------------------

//------------------------------------------------------------------------------
// Class: uvm_reg_read_only_cbs
//
// Pre-defined register callback method for read-only registers
// that will issue an error if a write() operation is attempted.
//
//------------------------------------------------------------------------------

class [docs]uvm_reg_read_only_cbs extends uvm_reg_cbs;

   function [docs]new(string name = "uvm_reg_read_only_cbs");
      super.new(name);
   endfunction

   `uvm_object_utils(uvm_reg_read_only_cbs)

   
   // Function: pre_write
   //
   // Produces an error message and sets status to <UVM_NOT_OK>.
   //
   virtual task [docs]pre_write(uvm_reg_item rw);
      string name = rw.element.get_full_name();
      
      if (rw.status != UVM_IS_OK)
         return;

      if (rw.element_kind == UVM_FIELD) begin
         uvm_reg_field fld;
         uvm_reg rg;
         $cast(fld, rw.element);
         rg = fld.get_parent();
         name = rg.get_full_name();
      end
      
      `uvm_error("UVM/REG/READONLY",
                 {name, " is read-only. Cannot call write() method."});

      rw.status = UVM_NOT_OK;
   endtask

   local static uvm_reg_read_only_cbs m_me;
   local static function uvm_reg_read_only_cbs get();
      if (m_me == null) m_me = new;
      return m_me;
   endfunction


   // Function: add
   //
   // Add this callback to the specified register and its contained fields.
   //
   static function void [docs]add(uvm_reg rg);
      uvm_reg_field flds[$];
      
      uvm_reg_cb::add(rg, get());
      rg.get_fields(flds);
      foreach (flds[i]) begin
         uvm_reg_field_cb::add(flds[i], get());
      end
   endfunction


   // Function: remove
   //
   // Remove this callback from the specified register and its contained fields.
   //
   static function void [docs]remove(uvm_reg rg);
      uvm_reg_cb_iter cbs = new(rg);
      uvm_reg_field flds[$];

      void'(cbs.first());
      while (cbs.get_cb() != get()) begin
         if (cbs.get_cb() == null)
            return;
         void'(cbs.next());
      end
      uvm_reg_cb::delete(rg, get());
      rg.get_fields(flds);
      foreach (flds[i]) begin
         uvm_reg_field_cb::delete(flds[i], get());
      end
   endfunction
endclass


//------------------------------------------------------------------------------
// Class: uvm_reg_write_only_cbs
//
// Pre-defined register callback method for write-only registers
// that will issue an error if a read() operation is attempted.
//
//------------------------------------------------------------------------------

class [docs]uvm_reg_write_only_cbs extends uvm_reg_cbs;

   function [docs]new(string name = "uvm_reg_write_only_cbs");
      super.new(name);
   endfunction

   `uvm_object_utils(uvm_reg_write_only_cbs)
   
   // Function: pre_read
   //
   // Produces an error message and sets status to <UVM_NOT_OK>.
   //
   virtual task [docs]pre_read(uvm_reg_item rw);
      string name = rw.element.get_full_name();
      
      if (rw.status != UVM_IS_OK)
         return;

      if (rw.element_kind == UVM_FIELD) begin
         uvm_reg_field fld;
         uvm_reg rg;
         $cast(fld, rw.element);
         rg = fld.get_parent();
         name = rg.get_full_name();
      end
      
      `uvm_error("UVM/REG/WRTEONLY",
                 {name, " is write-only. Cannot call read() method."});

      rw.status = UVM_NOT_OK;
   endtask

   local static uvm_reg_write_only_cbs m_me;
   local static function uvm_reg_write_only_cbs get();
      if (m_me == null) m_me = new;
      return m_me;
   endfunction


   // Function: add
   //
   // Add this callback to the specified register and its contained fields.
   //
   static function void [docs]add(uvm_reg rg);
      uvm_reg_field flds[$];
      
      uvm_reg_cb::add(rg, get());
      rg.get_fields(flds);
      foreach (flds[i]) begin
         uvm_reg_field_cb::add(flds[i], get());
      end
   endfunction


   // Function: remove
   //
   // Remove this callback from the specified register and its contained fields.
   //
   static function void [docs]remove(uvm_reg rg);
      uvm_reg_cb_iter cbs = new(rg);
      uvm_reg_field flds[$];

      void'(cbs.first());
      while (cbs.get_cb() != get()) begin
         if (cbs.get_cb() == null)
            return;
         void'(cbs.next());
      end
      uvm_reg_cb::delete(rg, get());
      rg.get_fields(flds);
      foreach (flds[i]) begin
         uvm_reg_field_cb::delete(flds[i], get());
      end
   endfunction
endclass