// 
// -------------------------------------------------------------
// Copyright 2010-2011 Mentor Graphics Corporation
// Copyright 2012 Semifore
// Copyright 2018 Qualcomm, Inc.
// Copyright 2004-2013 Synopsys, Inc.
// Copyright 2010-2018 Cadence Design Systems, Inc.
// Copyright 2010 AMD
// Copyright 2014-2018 NVIDIA Corporation
//    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.
// -------------------------------------------------------------
// 

//
// class -- NODOCS -- uvm_reg_hw_reset_seq
// Test the hard reset values of registers
//
// The test sequence performs the following steps
//
// 1. resets the DUT and the
// block abstraction class associated with this sequence.
//
// 2. reads all of the registers in the block,
// via all of the available address maps,
// comparing the value read with the expected reset value.
//
// If bit-type resource named
// "NO_REG_TESTS" or "NO_REG_HW_RESET_TEST"
// in the "REG::" namespace
// matches the full name of the block or register,
// the block or register is not tested.
//
//| uvm_resource_db#(bit)::set({"REG::",regmodel.blk.get_full_name(),".*"},
//|                            "NO_REG_TESTS", 1, this);
//
// This is usually the first test executed on any DUT.
//

// @uvm-ieee 1800.2-2017 auto E.1.1
class uvm_reg_hw_reset_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));

   `uvm_object_utils(uvm_reg_hw_reset_seq)

   // @uvm-ieee 1800.2-2017 auto E.1.2.1.1
   function new(string name="uvm_reg_hw_reset_seq");
     super.new(name);
   endfunction


   // Variable -- NODOCS -- model
   //
   // The block to be tested. Declared in the base class.
   //
   //| uvm_reg_block model; 


   // Variable -- NODOCS -- body
   //
   // Executes the Hardware Reset sequence.
   // Do not call directly. Use seq.start() instead.

   // @uvm-ieee 1800.2-2017 auto E.1.2.1.2
   virtual task body();

      if (model == null) begin
         `uvm_error("uvm_reg_hw_reset_seq", "Not block or system specified to run sequence on")
         return;
      end
      `uvm_info("STARTING_SEQ",{"\n\nStarting ",get_name()," sequence...\n"},UVM_LOW)
      
      this.reset_blk(model);
      model.reset();

      do_block(model);
   endtask: body

// Task -- NODOCS -- do_block
   //
   // Test all of the registers in a given ~block~
   //
   protected virtual task do_block(uvm_reg_block blk);
      uvm_reg_map maps[$];
      uvm_reg_map sub_maps[$];
	  uvm_reg regs[$];

      if (uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_TESTS", 0) != null ||
          uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_HW_RESET_TEST", 0) != null ) begin
            return;

      end

      blk.get_registers(regs, UVM_NO_HIER);
                                             
      foreach(regs[ridx]) begin
	                if (uvm_resource_db#(bit)::get_by_name({"REG::",regs[ridx].get_full_name()},
                                                 "NO_REG_TESTS", 0) != null ||
                      regs[ridx].has_reset() == 0 ||
		                uvm_resource_db#(bit)::get_by_name({"REG::",regs[ridx].get_full_name()},
                                                 "NO_REG_HW_RESET_TEST", 0) != null )
			                	continue;
	      
	      begin
		      uvm_reg_map rm[$];
		      uvm_status_e status;
            uvm_reg_field fields[$];
            uvm_check_e field_check_restore[uvm_reg_field];
		      
		      regs[ridx].get_maps(rm);
		      
            regs[ridx].get_fields(fields);
            
            foreach(fields[fidx]) begin
               if (fields[fidx].has_reset() == 0 ||
                   fields[fidx].get_compare() == UVM_NO_CHECK || 
                   uvm_resource_db#(bit)::get_by_name({"REG::",fields[fidx].get_full_name()},
                                                       "NO_REG_HW_RESET_TEST", 0) != null) begin
                  field_check_restore[fields[fidx]] = fields[fidx].get_compare();  
                  fields[fidx].set_compare(UVM_NO_CHECK);
               end
            end  
            // if there are some fields to check
            if(fields.size() != field_check_restore.size()) begin
               foreach(rm[midx]) begin
                  `uvm_info(get_type_name(),
                     $sformatf("Verifying reset value of register %s in map \"%s\"...",
                        regs[ridx].get_full_name(), rm[midx].get_full_name()), UVM_LOW)
               
                  regs[ridx].mirror(status, UVM_CHECK, UVM_FRONTDOOR, rm[midx], this);
               
                  if (status != UVM_IS_OK) begin
                      `uvm_error(get_type_name(),
                         $sformatf("Status was %s when reading reset value of register \"%s\" through map \"%s\".",
                          status.name(), regs[ridx].get_full_name(), rm[midx].get_full_name()))
                  end   
               end
            end
            // restore compare setting
            foreach(field_check_restore[field]) begin
               field.set_compare(field_check_restore[field]);
            end
      	end
      end	
      
      begin
         uvm_reg_block blks[$];
         
         blk.get_blocks(blks);
         foreach (blks[i]) begin
            do_block(blks[i]);
         end
      end

   endtask:do_block


   //
   // task -- NODOCS -- reset_blk
   // Reset the DUT that corresponds to the specified block abstraction class.
   //
   // Currently empty.
   // Will rollback the environment's phase to the ~reset~
   // phase once the new phasing is available.
   //
   // In the meantime, the DUT should be reset before executing this
   // test sequence or this method should be implemented
   // in an extension to reset the DUT.
   //
   virtual task reset_blk(uvm_reg_block blk);
   endtask

endclass: uvm_reg_hw_reset_seq
