// $Id: uvm_report_catcher.svh,v 1.1.2.10 2010/04/09 15:03:25 janick Exp $
//------------------------------------------------------------------------------
//   Copyright 2007-2010 Mentor Graphics Corporation
//   Copyright 2007-2009 Cadence Design Systems, Inc.
//   Copyright 2010 Synopsys, 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.
//------------------------------------------------------------------------------

`ifndef UVM_REPORT_CATCHER_SVH
`define UVM_REPORT_CATCHER_SVH

typedef class [docs]uvm_report_object;
typedef class [docs]uvm_report_handler;
typedef class [docs]uvm_report_server;
typedef class [docs]uvm_report_catcher;

typedef uvm_callbacks    #(uvm_report_object, uvm_report_catcher) [docs]uvm_report_cb;
typedef uvm_callback_iter#(uvm_report_object, uvm_report_catcher) [docs]uvm_report_cb_iter;

class [docs]sev_id_struct;
  bit sev_specified ;
  bit id_specified ;
  uvm_severity sev ;
  string  id ;
  bit is_on ;
endclass

//------------------------------------------------------------------------------
//
// CLASS: uvm_report_catcher
//
// The uvm_report_catcher is used to catch messages issued by the uvm report
// server. Catchers are
// uvm_callbacks#(<uvm_report_object>,uvm_report_catcher) objects,
// so all facilities in the <uvm_callback> and <uvm_callbacks#(T,CB)>
// classes are available for registering catchers and controlling catcher
// state.
// The uvm_callbacks#(<uvm_report_object>,uvm_report_catcher) class is
// aliased to ~uvm_report_cb~ to make it easier to use.
// Multiple report catchers can be 
// registered with a report object. The catchers can be registered as default 
// catchers which catch all reports on all <uvm_report_object> reporters,
// or catchers can be attached to specific report objects (i.e. components). 
//
// User extensions of <uvm_report_catcher> must implement the <catch> method in 
// which the action to be taken on catching the report is specified. The catch 
// method can return ~CAUGHT~, in which case further processing of the report is 
// immediately stopped, or return ~THROW~ in which case the (possibly modified) report 
// is passed on to other registered catchers. The catchers are processed in the order 
// in which they are registered.
//
// On catching a report, the <catch> method can modify the severity, id, action,
// verbosity or the report string itself before the report is finally issued by
// the report server. The report can be immediately issued from within the catcher 
// class by calling the <issue> method.
//
// The catcher maintains a count of all reports with FATAL,ERROR or WARNING severity
// and a count of all reports with FATAL, ERROR or WARNING severity whose severity
// was lowered. These statistics are reported in the summary of the <uvm_report_server>.
//
// This example shows the basic concept of creating a report catching
// callback and attaching it to all messages that get emitted:
//
//| class my_error_demoter extends uvm_report_catcher;
//|   function new(string name="my_error_demoter");
//|     super.new(name);
//|   endfunction
//|   //This example demotes "MY_ID" errors to an info message
//|   function action_e catch();
//|     if(get_severity() == UVM_ERROR && get_id() == "MY_ID")
//|       set_severity(UVM_INFO);
//|     return THROW;
//|   endfunction
//| endclass
//|
//| my_error_demoter demoter = new;
//| initial begin
//|  // Catchers are callbacks on report objects (components are report 
//|  // objects, so catchers can be attached to components).
//|
//|  // To affect all reporters, use ~null~ for the object
//|  uvm_report_cb::add(null, demoter); 
//|
//|  // To affect some specific object use the specific reporter
//|  uvm_report_cb::add(mytest.myenv.myagent.mydriver, demoter);
//|
//|  // To affect some set of components (any "*driver" under mytest.myenv)
//|  // using the component name
//|  uvm_report_cb::add_by_name("*driver", demoter, mytest.myenv);
//| end
//
//
//------------------------------------------------------------------------------

virtual class [docs]uvm_report_catcher extends uvm_callback;

  `uvm_register_cb(uvm_report_object,uvm_report_catcher)

  typedef enum { UNKNOWN_ACTION, THROW, CAUGHT} [docs]action_e;

  local static uvm_report_message m_modified_report_message;
  local static uvm_report_message m_orig_report_message;

  local static bit m_set_action_called;

  // Counts for the demoteds and caughts
  local static int m_demoted_fatal;
  local static int m_demoted_error;
  local static int m_demoted_warning;
  local static int m_caught_fatal;
  local static int m_caught_error;
  local static int m_caught_warning;

  // Flag counts
  const static int DO_NOT_CATCH      = 1; 
  const static int DO_NOT_MODIFY     = 2; 
  local static int m_debug_flags;

  local static  bit do_report;

  
  // Function: new
  //
  // Create a new report catcher. The name argument is optional, but
  // should generally be provided to aid in debugging.

  function [docs]new(string name = "uvm_report_catcher");
    super.new(name);
    do_report = 1;
  endfunction    

  // Group: Current Message State

  // Function: get_client
  //
  // Returns the <uvm_report_object> that has generated the message that
  // is currently being processed.

  function uvm_report_object [docs]get_client();
    return m_modified_report_message.get_report_object(); 
  endfunction

  // Function: get_severity
  //
  // Returns the <uvm_severity> of the message that is currently being
  // processed. If the severity was modified by a previously executed
  // catcher object (which re-threw the message), then the returned 
  // severity is the modified value.

  function uvm_severity [docs]get_severity();
    return this.m_modified_report_message.get_severity();
  endfunction
  
  // Function: get_context
  //
  // Returns the context name of the message that is currently being
  // processed. This is typically the full hierarchical name of the component
  // that issued the message. However, if user-defined context is set from
  // a uvm_report_message, the user-defined context will be returned.

  function string [docs]get_context();
    string context_str;
    
    context_str = this.m_modified_report_message.get_context();
    if (context_str == "") begin
      uvm_report_handler rh = this.m_modified_report_message.get_report_handler();
      context_str = rh.get_full_name();
    end

    return context_str;
  endfunction
  
  // Function: get_verbosity
  //
  // Returns the verbosity of the message that is currently being
  // processed. If the verbosity was modified by a previously executed
  // catcher (which re-threw the message), then the returned 
  // verbosity is the modified value.
  
  function int [docs]get_verbosity();
    return this.m_modified_report_message.get_verbosity();
  endfunction
  
  // Function: get_id
  //
  // Returns the string id of the message that is currently being
  // processed. If the id was modified by a previously executed
  // catcher (which re-threw the message), then the returned 
  // id is the modified value.
  
  function string [docs]get_id();
    return this.m_modified_report_message.get_id();
  endfunction
  
  // Function: get_message
  //
  // Returns the string message of the message that is currently being
  // processed. If the message was modified by a previously executed
  // catcher (which re-threw the message), then the returned 
  // message is the modified value.
  
  function string [docs]get_message();
     return this.m_modified_report_message.get_message();
  endfunction
  
  // Function: get_action
  //
  // Returns the <uvm_action> of the message that is currently being
  // processed. If the action was modified by a previously executed
  // catcher (which re-threw the message), then the returned 
  // action is the modified value.
  
  function uvm_action [docs]get_action();
    return this.m_modified_report_message.get_action();
  endfunction
  
  // Function: get_fname
  //
  // Returns the file name of the message.
  
  function string [docs]get_fname();
    return this.m_modified_report_message.get_filename();
  endfunction             

  // Function: get_line
  //
  // Returns the line number of the message.

  function int [docs]get_line();
    return this.m_modified_report_message.get_line();
  endfunction

  // Function: get_element_container
  //
  // Returns the element container of the message.

  function uvm_report_message_element_container [docs]get_element_container();
    return this.m_modified_report_message.get_element_container();
  endfunction

  
  // Group: Change Message State

  // Function: set_severity
  //
  // Change the severity of the message to ~severity~. Any other
  // report catchers will see the modified value.
  
  protected function void set_severity(uvm_severity severity);
    this.m_modified_report_message.set_severity(severity);
  endfunction
  
  // Function: set_verbosity
  //
  // Change the verbosity of the message to ~verbosity~. Any other
  // report catchers will see the modified value.

  protected function void set_verbosity(int verbosity);
    this.m_modified_report_message.set_verbosity(verbosity);
  endfunction      

  // Function: set_id
  //
  // Change the id of the message to ~id~. Any other
  // report catchers will see the modified value.

  protected function void set_id(string id);
    this.m_modified_report_message.set_id(id);
  endfunction
  
  // Function: set_message
  //
  // Change the text of the message to ~message~. Any other
  // report catchers will see the modified value.

  protected function void set_message(string message);
    this.m_modified_report_message.set_message(message);
  endfunction
  
  // Function: set_action
  //
  // Change the action of the message to ~action~. Any other
  // report catchers will see the modified value.
  
  protected function void set_action(uvm_action action);
    this.m_modified_report_message.set_action(action);
    this.m_set_action_called = 1;
  endfunction

  // Function: set_context
  //
  // Change the context of the message to ~context_str~. Any other
  // report catchers will see the modified value.

  protected function void set_context(string context_str);
    this.m_modified_report_message.set_context(context_str);
  endfunction

  // Function: add_int
  //
  // Add an integral type of the name ~name~ and value ~value~ to
  // the message.  The required ~size~ field indicates the size of ~value~.
  // The required ~radix~ field determines how to display and
  // record the field. Any other report catchers will see the newly
  // added element.
  //

  protected function void add_int(string name,
				  uvm_bitstream_t value,
				  int size,
				  uvm_radix_enum radix,
				  uvm_action action = (UVM_LOG|UVM_RM_RECORD));
    this.m_modified_report_message.add_int(name, value, size, radix, action);
  endfunction


  // Function: add_string
  //
  // Adds a string of the name ~name~ and value ~value~ to the
  // message. Any other report catchers will see the newly
  // added element.
  //

  protected function void add_string(string name,
				     string value,
                                     uvm_action action = (UVM_LOG|UVM_RM_RECORD));
    this.m_modified_report_message.add_string(name, value, action);
  endfunction


  // Function: add_object
  //
  // Adds a uvm_object of the name ~name~ and reference ~obj~ to
  // the message. Any other report catchers will see the newly
  // added element.
  //

  protected function void add_object(string name,
				     uvm_object obj,
                                     uvm_action action = (UVM_LOG|UVM_RM_RECORD));
    this.m_modified_report_message.add_object(name, obj, action);
  endfunction

  
  // Group: Debug
     
  // Function: get_report_catcher
  //
  // Returns the first report catcher that has ~name~. 
  
  static function uvm_report_catcher [docs]get_report_catcher(string name);
    static uvm_report_cb_iter iter = new(null);
    get_report_catcher = iter.first();
    while(get_report_catcher != null) begin
      if(get_report_catcher.get_name() == name)
        return get_report_catcher;
      get_report_catcher = iter.next();
    end
    return null;
  endfunction


  // Function: print_catcher
  //
  // Prints information about all of the report catchers that are 
  // registered. For finer grained detail, the <uvm_callbacks #(T,CB)::display>
  // method can be used by calling uvm_report_cb::display(<uvm_report_object>).

  static function void [docs]print_catcher(UVM_FILE file = 0);
	  string msg;
	  string enabled;
	  uvm_report_catcher catcher;
	  static uvm_report_cb_iter iter = new(null);
	  string q[$];

	  q.push_back("-------------UVM REPORT CATCHERS----------------------------\n");

	  catcher = iter.first();
	  while(catcher != null) begin
		  if(catcher.callback_mode())
			  enabled = "ON";        
		  else
			  enabled = "OFF";        

		  q.push_back($sformatf("%20s : %s\n", catcher.get_name(),enabled));
		  catcher = iter.next();
	  end
	  q.push_back("--------------------------------------------------------------\n");

	  `uvm_info_context("UVM/REPORT/CATCHER",`UVM_STRING_QUEUE_STREAMING_PACK(q),UVM_LOW,uvm_top)
  endfunction
  
  // Funciton: debug_report_catcher
  //
  // Turn on report catching debug information. ~what~ is a bitwise AND of
  // * DO_NOT_CATCH  -- forces catch to be ignored so that all catchers see the
  //   the reports.
  // * DO_NOT_MODIFY -- forces the message to remain unchanged

  static function void [docs]debug_report_catcher(int what= 0);
    m_debug_flags = what;
  endfunction        
  
  // Group: Callback Interface
 
  // Function: catch
  //
  // This is the method that is called for each registered report catcher.
  // There are no arguments to this function. The <Current Message State>
  // interface methods can be used to access information about the 
  // current message being processed.

  pure virtual function action_e [docs]catch();
     

  // Group: Reporting

   // Function: uvm_report_fatal
   //
   // Issues a fatal message using the current message's report object.
   // This message will bypass any message catching callbacks.
   
   protected function void uvm_report_fatal(string id,
					    string message, 
					    int verbosity,
					    string fname = "",
					    int line = 0,
					    string context_name = "",
					    bit report_enabled_checked = 0);

     this.uvm_report(UVM_FATAL, id, message, UVM_NONE, fname, line,
                     context_name, report_enabled_checked);
   endfunction  


   // Function: uvm_report_error
   //
   // Issues an error message using the current message's report object.
   // This message will bypass any message catching callbacks.
   
   protected function void uvm_report_error(string id,
					    string message, 
					    int verbosity,
					    string fname = "",
					    int line = 0,
					    string context_name = "",
					    bit report_enabled_checked = 0);

     this.uvm_report(UVM_ERROR, id, message, UVM_NONE, fname, line,
                     context_name, report_enabled_checked);
   endfunction  
     

   // Function: uvm_report_warning
   //
   // Issues a warning message using the current message's report object.
   // This message will bypass any message catching callbacks.
   
   protected function void uvm_report_warning(string id,
					      string message,
					      int verbosity,
					      string fname = "",
					      int line = 0, 
					      string context_name = "",
					      bit report_enabled_checked = 0);

     this.uvm_report(UVM_WARNING, id, message, UVM_NONE, fname, line,
                     context_name, report_enabled_checked);
   endfunction  


   // Function: uvm_report_info
   //
   // Issues a info message using the current message's report object.
   // This message will bypass any message catching callbacks.
   
   protected function void uvm_report_info(string id,
					   string message, 
					   int verbosity,
					   string fname = "",
					   int line = 0,
					   string context_name = "",
					   bit report_enabled_checked = 0);

     this.uvm_report(UVM_INFO, id, message, verbosity, fname, line,
                     context_name, report_enabled_checked);
   endfunction  

   // Function: uvm_report
   //
   // Issues a message using the current message's report object.
   // This message will bypass any message catching callbacks.

   protected function void uvm_report(uvm_severity severity,
				      string id,
				      string message,
				      int verbosity,
				      string fname = "",
				      int line = 0,
				      string context_name = "",
				      bit report_enabled_checked = 0);

     uvm_report_message l_report_message;
     if (report_enabled_checked == 0) begin
       if (!uvm_report_enabled(verbosity, severity, id))
         return;
     end

     l_report_message = uvm_report_message::new_report_message();
     l_report_message.set_report_message(severity, id, message, 
					 verbosity, fname, line, context_name);
     this.uvm_process_report_message(l_report_message);
   endfunction

   protected function void uvm_process_report_message(uvm_report_message msg);
     uvm_report_object ro = m_modified_report_message.get_report_object();
     uvm_action a = ro.get_report_action(msg.get_severity(), msg.get_id());

     if(a) begin
       string composed_message;
       uvm_report_server rs = m_modified_report_message.get_report_server();

       msg.set_report_object(ro);
       msg.set_report_handler(m_modified_report_message.get_report_handler());
       msg.set_report_server(rs);
       msg.set_file(ro.get_report_file_handle(msg.get_severity(), msg.get_id()));
       msg.set_action(a);

       // no need to compose when neither UVM_DISPLAY nor UVM_LOG is set
       if (a & (UVM_LOG|UVM_DISPLAY))
         composed_message = rs.compose_report_message(msg);
       rs.execute_report_message(msg, composed_message);
     end
   endfunction


  // Function: issue
  // Immediately issues the message which is currently being processed. This
  // is useful if the message is being ~CAUGHT~ but should still be emitted.
  //
  // Issuing a message will update the report_server stats, possibly multiple 
  // times if the message is not ~CAUGHT~.

  protected function void issue();
     string composed_message;
     uvm_report_server rs = m_modified_report_message.get_report_server();

     if(uvm_action_type'(m_modified_report_message.get_action()) != UVM_NO_ACTION)
     begin
       // no need to compose when neither UVM_DISPLAY nor UVM_LOG is set
       if (m_modified_report_message.get_action() & (UVM_LOG|UVM_DISPLAY))
         composed_message = rs.compose_report_message(m_modified_report_message);
       rs.execute_report_message(m_modified_report_message, composed_message);
     end
  endfunction


  //process_all_report_catchers
  //method called by report_server.report to process catchers
  //

  static function int [docs]process_all_report_catchers(uvm_report_message rm);
    int iter;
    uvm_report_catcher catcher;
    int thrown = 1;
    uvm_severity orig_severity;
    static bit in_catcher;
    uvm_report_object l_report_object = rm.get_report_object();

    if(in_catcher == 1) begin
        return 1;
    end
    in_catcher = 1;    
    uvm_callbacks_base::m_tracing = 0;  //turn off cb tracing so catcher stuff doesn't print

    orig_severity = uvm_severity'(rm.get_severity());
    m_modified_report_message = rm;

    catcher = uvm_report_cb::get_first(iter,l_report_object);
    if (catcher != null) begin
      if(m_debug_flags & DO_NOT_MODIFY) begin
        process p = process::self(); // Keep random stability
        string randstate;
        if (p != null)
          randstate = p.get_randstate();
        $cast(m_orig_report_message, rm.clone()); //have to clone, rm can be extended type
        if (p != null)
          p.set_randstate(randstate);
      end
    end
    while(catcher != null) begin
      uvm_severity prev_sev;

      if (!catcher.callback_mode()) begin
        catcher = uvm_report_cb::get_next(iter,l_report_object);
        continue;
      end

      prev_sev = m_modified_report_message.get_severity();
      m_set_action_called = 0;
      thrown = catcher.process_report_catcher();

      // Set the action to the default action for the new severity
      // if it is still at the default for the previous severity,
      // unless it was explicitly set.
      if (!m_set_action_called && 
          m_modified_report_message.get_severity() != prev_sev && 
          m_modified_report_message.get_action() == 
            l_report_object.get_report_action(prev_sev, "*@&*^*^*#")) begin

         m_modified_report_message.set_action(
           l_report_object.get_report_action(m_modified_report_message.get_severity(), "*@&*^*^*#"));
      end

      if(thrown == 0) begin 
        case(orig_severity)
          UVM_FATAL:   m_caught_fatal++;
          UVM_ERROR:   m_caught_error++;
          UVM_WARNING: m_caught_warning++;
         endcase   
         break;
      end 
      catcher = uvm_report_cb::get_next(iter,l_report_object);
    end //while

    //update counters if message was returned with demoted severity
    case(orig_severity)
      UVM_FATAL:    
        if(m_modified_report_message.get_severity() < orig_severity)
          m_demoted_fatal++;
      UVM_ERROR:
        if(m_modified_report_message.get_severity() < orig_severity)
          m_demoted_error++;
      UVM_WARNING:
        if(m_modified_report_message.get_severity() < orig_severity)
          m_demoted_warning++;
    endcase

    in_catcher = 0;
    uvm_callbacks_base::m_tracing = 1;  //turn tracing stuff back on

    return thrown; 
  endfunction


  //process_report_catcher
  //internal method to call user <catch()> method
  //

  local function int process_report_catcher();

    action_e act;

    act = this.catch();

    if(act == UNKNOWN_ACTION)
      this.uvm_report_error("RPTCTHR", {"uvm_report_this.catch() in catcher instance ",
        this.get_name(), " must return THROW or CAUGHT"}, UVM_NONE, `uvm_file, `uvm_line);

    if(m_debug_flags & DO_NOT_MODIFY) begin
      m_modified_report_message.copy(m_orig_report_message);
    end     

    if(act == CAUGHT  && !(m_debug_flags & DO_NOT_CATCH)) begin
      return 0;
    end  

    return 1;

  endfunction


  // Function: summarize
  //
  // This function is called automatically by <uvm_report_server::report_summarize()>.
  // It prints the statistics for the active catchers.


  static function void [docs]summarize();
    string s;
    string q[$];
    if(do_report) begin
      q.push_back("\n--- UVM Report catcher Summary ---\n\n\n");
      q.push_back($sformatf("Number of demoted UVM_FATAL reports  :%5d\n", m_demoted_fatal));
      q.push_back($sformatf("Number of demoted UVM_ERROR reports  :%5d\n", m_demoted_error));
      q.push_back($sformatf("Number of demoted UVM_WARNING reports:%5d\n", m_demoted_warning));
      q.push_back($sformatf("Number of caught UVM_FATAL reports   :%5d\n", m_caught_fatal));
      q.push_back($sformatf("Number of caught UVM_ERROR reports   :%5d\n", m_caught_error));
      q.push_back($sformatf("Number of caught UVM_WARNING reports :%5d\n", m_caught_warning));

 	 `uvm_info_context("UVM/REPORT/CATCHER",`UVM_STRING_QUEUE_STREAMING_PACK(q),UVM_LOW,uvm_top)
    end
  endfunction

endclass

`endif // UVM_REPORT_CATCHER_SVH