blob: 283388235ccad7b0aa0276b0bab03d70810687c6 [file] [log] [blame]
//
// -------------------------------------------------------------
// Copyright 2004-2008 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.
// -------------------------------------------------------------
//
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_log_modifier;
int typ;
int severity;
string pattern;
int new_typ;
int new_severity;
int handling;
static local vmm_log log;
function new();
// Initialize all static properties
if (log == null) this.log = new("vmm_log_modifier", "class");
endfunction: new
extern function string psdisplay(string prefix = "");
endclass: vmm_log_modifier
typedef class vmm_log_msg;
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_log_watchpoint;
int typ;
int severity;
string pattern;
logic issued;
event seen;
vmm_log_msg msg;
static local vmm_log log;
function new();
// Initialize all static properties
if (log == null) this.log = new("vmm_log_watchpoint", "class");
endfunction: new
extern function void display(string prefix = "");
extern function string psdisplay(string prefix = "");
endclass:vmm_log_watchpoint
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_log_msg;
vmm_log log;
bit invalid;
time timestamp;
int original_typ;
int original_severity;
int effective_typ;
int effective_severity;
string fname;
int line;
string text[$];
logic issued;
int handling;
/*local*/ int flushed;
function new(vmm_log log);
this.log = log;
this.invalid = 1;
endfunction: new
extern function void display(string prefix = "");
extern function string psdisplay(string prefix = "");
extern function vmm_log_msg copy();
endclass: vmm_log_msg
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_log_catcher_descr;
vmm_log_catcher catcher;
string text;
string name;
string inst;
int typs;
int severity;
bit recurse;
endclass: vmm_log_catcher_descr
//
// vmm_log_modifier
//
function string vmm_log_modifier::psdisplay(string prefix="");
$sformat(psdisplay, "%s%s [%s] with \"%s\" -> %s [%s] then %s",
prefix, this.log.typ_image(this.typ),
this.log.sev_image(this.severity),
(this.pattern == "") ? "/./" : this.pattern,
this.log.typ_image(this.new_typ),
this.log.sev_image(this.new_severity),
this.log.handling_image(this.handling));
endfunction: psdisplay
//
// vmm_log_watchpoint
//
function void vmm_log_watchpoint::display(string prefix="");
$write("%s\n", this.psdisplay(prefix));
endfunction: display
function string vmm_log_watchpoint::psdisplay(string prefix="");
$sformat(psdisplay, "%s%s [%s] with \"%s\"%s",
prefix, this.log.typ_image(this.typ),
this.log.sev_image(this.severity),
(this.pattern == "") ? "/./" : this.pattern,
(this.issued === 1'bx) ? "" : (this.issued) ? "if issued" : "if not issued");
endfunction: psdisplay
//
// vmm_log_msg
//
function void vmm_log_msg::display(string prefix="");
$write("%s\n", this.psdisplay(prefix));
endfunction: display
function string vmm_log_msg::psdisplay(string prefix="");
$sformat(psdisplay, "%s%s [%s] at %0t", prefix,
this.log.typ_image(this.effective_typ),
this.log.sev_image(this.effective_severity),
this.timestamp);
`foreach(this.text,i) begin
$sformat(psdisplay, "%s\n%s %s", psdisplay, prefix,
this.text[i]);
end
endfunction: psdisplay
function vmm_log_msg vmm_log_msg::copy();
copy = new(this.log);
copy.timestamp = this.timestamp;
copy.original_typ = this.original_typ;
copy.original_severity = this.original_severity;
copy.effective_typ = this.effective_typ;
copy.effective_severity = this.effective_severity;
copy.text = this.text;
copy.issued = this.issued;
copy.handling = this.handling;
copy.flushed = this.flushed;
endfunction: copy
//
// vmm_log;
//
function bit vmm_log::uses_hier_inst_name();
return !this.is_orig;
endfunction: uses_hier_inst_name
function void vmm_log::use_hier_inst_name();
this.recurse_id++;
`foreach (this.known,i) begin
if (this.known[i].parent_count == 0) this.make_hier_inst_name();
end
this.is_orig = 0;
endfunction: use_hier_inst_name
function void vmm_log::use_orig_inst_name();
`foreach (this.known,i) begin
this.known[i].inst = this.known[i].orig_inst;
end
this.is_orig = 1;
endfunction: use_orig_inst_name
function void vmm_log::make_hier_inst_name(string prefix = "");
string name;
// Detect cycles in log trees
if (this.visited == this.recurse_id) return;
this.visited = this.recurse_id;
name = this.orig_inst;
// In case no instance names were specified...
if (name == "") name = this.name;
if (name == "") name = "[Anonymous]";
if (prefix == "") prefix = name;
else begin
this.inst = {prefix, name};
prefix = this.inst;
end
prefix = {prefix, "."};
`foreach (this.below,i) begin
this.below[i].make_hier_inst_name(prefix);
end
endfunction: make_hier_inst_name
function void vmm_log::reset(string name="/./",
string inst="/./",
bit recurse=0);
this.is_self = 0;
this.is_all = 0;
this.known_idx = 0;
this.recurse = recurse;
this.recurse_id++;
`ifdef VCS2006_06
// Work-around for NYI feature in 2006.06
// but IEEE 1800-2009 compliant
this.recurse_stack.delete();
`else
`ifdef INCA
this.recurse_stack.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.recurse_stack = '{};
`endif
`endif
// Trivial iterators?
if (name == "" && inst == "") begin
this.is_self = 1;
return;
end
if (name == "/./" && inst == "/./") begin
this.is_all = 1;
this.recurse = 0; // No point in recursion
return;
end
if (name == "") name = this.name;
if (inst == "") inst = this.inst;
this.pattern[0] = name;
this.is_pattern[0] = `vmm_str_match(this.pattern[0], "^/(.*)/$");
if (is_pattern[0]) begin
this.pattern[0] = `vmm_str_backref(this.pattern[0], 0);
end
this.pattern[1] = inst;
this.is_pattern[1] = `vmm_str_match(this.pattern[1], "^/(.*)/$");
if (is_pattern[1]) begin
this.pattern[1] = `vmm_str_backref(this.pattern[1], 0);
end
endfunction: reset
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_log_below_iter;
local vmm_log log;
local int idx;
function new(vmm_log log);
this.log = log;
this.idx = 0;
endfunction
function vmm_log data();
if (this.idx >= this.log.below.size()) data = null;
else data = this.log.below[idx];
endfunction
function vmm_log start();
this.idx = 0;
start = this.data();
endfunction
function vmm_log next();
if (idx < this.log.below.size()) idx++;
next = this.data();
endfunction
endclass
function vmm_log vmm_log::for_each();
if (this.is_self) begin
if (this.is_self == 1) begin
this.is_self = 2;
if (this.recurse) begin
vmm_log_below_iter j = new(this);
this.visited = this.recurse_id;
this.recurse_stack.push_back(j);
end
return this;
end else if (!this.recurse) begin
return null;
end
end
while (this.recurse && this.recurse_stack.size() > 0) begin
vmm_log_below_iter i = recurse_stack[$];
while (i.data() != null) begin
vmm_log that = i.data();
void'(i.next());
if (that.visited != this.recurse_id) begin
vmm_log_below_iter j = new(that);
that.visited = this.recurse_id;
this.recurse_stack.push_back(j);
return that;
end
end
void'(this.recurse_stack.pop_back());
end
if (this.is_self) begin
return null;
end
while (this.known_idx < this.known.size()) begin
vmm_log that = this.known[this.known_idx++];
bit name_ok;
bit inst_ok;
if (this.is_all) begin
return that;
end
if (is_pattern[0]) name_ok = `vmm_str_match(that.name, this.pattern[0]);
else name_ok = (that.name == this.pattern[0]);
if (is_pattern[1]) inst_ok = `vmm_str_match(that.inst, this.pattern[1]);
else inst_ok = (that.inst == this.pattern[1]);
if (name_ok && inst_ok) begin
if (that.visited != this.recurse_id) begin
that.visited = this.recurse_id;
if (this.recurse) begin
vmm_log_below_iter j = new(that);
this.recurse_stack.push_back(j);
end
return that;
end
end
end
for_each = null;
endfunction: for_each
function vmm_log::new(string name,
string inst,
vmm_log under=null);
`ifdef VMM_LOG_BASE_NEW_CALL
super.new(`VMM_LOG_BASE_NEW_CALL);
`endif
if (_vmm_opts == null) _vmm_opts = new;
this.name = name;
this.inst = inst;
this.orig_inst = inst;
this.parent_count = 0;
if (under != null) under.is_above(this);
this.msg = new(this);
this.n_msg[FATAL_SEV] = 0;
this.n_msg[ERROR_SEV] = 0;
this.n_msg[WARNING_SEV] = 0;
this.n_msg[NORMAL_SEV] = 0;
this.n_msg[TRACE_SEV] = 0;
this.n_msg[DEBUG_SEV] = 0;
this.n_msg[VERBOSE_SEV] = 0;
this.n_msg[HIDDEN_SEV] = 0;
this.n_msg[IGNORE_SEV] = 0;
this.has_text_modifiers = 0;
this.n_demoted[ERROR_SEV] = 0;
this.n_demoted[WARNING_SEV] = 0;
this.known.push_back(this);
this.enabled_typs = ALL_TYPS;
if (this.known.size() == 1) begin
this.type_list.push_back(FAILURE_TYP);
this.type_list.push_back(NOTE_TYP);
this.type_list.push_back(DEBUG_TYP);
this.type_list.push_back(REPORT_TYP);
this.type_list.push_back(NOTIFY_TYP);
this.type_list.push_back(TIMING_TYP);
this.type_list.push_back(XHANDLING_TYP);
this.type_list.push_back(PROTOCOL_TYP);
this.type_list.push_back(TRANSACTION_TYP);
this.type_list.push_back(COMMAND_TYP);
this.type_list.push_back(CYCLE_TYP);
this.type_list.push_back(USER_TYP_0);
this.type_list.push_back(USER_TYP_1);
this.type_list.push_back(USER_TYP_2);
this.type_list.push_back(INTERNAL_TYP);
this.sev_list.push_back(FATAL_SEV);
this.sev_list.push_back(ERROR_SEV);
this.sev_list.push_back(WARNING_SEV);
this.sev_list.push_back(NORMAL_SEV);
this.sev_list.push_back(TRACE_SEV);
this.sev_list.push_back(DEBUG_SEV);
this.sev_list.push_back(VERBOSE_SEV);
// Define default images
this.type_images[FAILURE_TYP ] = "FAILURE";
this.type_images[NOTE_TYP ] = "NOTE";
this.type_images[DEBUG_TYP ] = "DEBUG";
this.type_images[REPORT_TYP ] = "REPORT";
this.type_images[NOTIFY_TYP ] = "NOTIFY";
this.type_images[TIMING_TYP ] = "TIMING";
this.type_images[XHANDLING_TYP ] = "XHANDLING";
this.type_images[PROTOCOL_TYP ] = "PROTOCOL";
this.type_images[TRANSACTION_TYP] = "XACTION";
this.type_images[COMMAND_TYP ] = "COMMAND";
this.type_images[CYCLE_TYP ] = "CYCLE";
this.type_images[USER_TYP_0 ] = "USER_0";
this.type_images[USER_TYP_1 ] = "USER_1";
this.type_images[USER_TYP_2 ] = "USER_2";
this.type_images[INTERNAL_TYP ] = "INTERNAL";
`ifdef VMM_LOG_ANSI_COLOR
this.sev_images[FATAL_SEV ] = "\033[41m*FATAL*\033[0m";
this.sev_images[ERROR_SEV ] = "\033[31m!ERROR!\033[0m";
this.sev_images[WARNING_SEV] = "\033[33mWARNING\033[0m";
`else
this.sev_images[FATAL_SEV ] = "*FATAL*";
this.sev_images[ERROR_SEV ] = "!ERROR!";
this.sev_images[WARNING_SEV] = "WARNING";
`endif
this.sev_images[NORMAL_SEV ] = "Normal";
this.sev_images[TRACE_SEV ] = "Trace";
this.sev_images[DEBUG_SEV ] = "Debug";
this.sev_images[VERBOSE_SEV] = "Verbose";
// Process command-line options
if ($test$plusargs("rvm_log_debug")) begin
this.plus_debug = 1;
end
// Cache those options here so they'll show up in the help
void'(_vmm_opts.get_bit("log_nowarn_at_200", "Supress warning message for more than 200 vmm_log instances creation"));
void'(_vmm_opts.get_bit("log_nofatal_at_1000", "Supress fatal message for more than 1000 vmm_log instances creation"));
begin
bit plusarg;
string arg;
string level;
plusarg = $value$plusargs("rvm_log_default=%s", arg);
if (!plusarg) begin
arg = _vmm_opts.get_string("log_default", , "Sets the default message verbosity");
if (arg != "") plusarg = 1;
end
if (plusarg) begin
level = arg.substr(0, 1); // Only look at the 1st 2 chars
level = level.tolower();
if (level == "er")
this.dflt_lvl = ERROR_SEV;
else if (level == "wa")
this.dflt_lvl = WARNING_SEV;
else if (level == "no")
this.dflt_lvl = NORMAL_SEV;
else if (level == "tr")
this.dflt_lvl = TRACE_SEV;
else if (level == "de")
this.dflt_lvl = DEBUG_SEV;
else if (level == "ve")
this.dflt_lvl = VERBOSE_SEV;
else if (level == "hi")
this.dflt_lvl = HIDDEN_SEV;
else
$write("Warning: Invalid +vmm_log_default specification: \"%s\"\n",
arg);
end
// Sometimes, VCS screws up static initialization order
else this.dflt_lvl = NORMAL_SEV;
plusarg = $value$plusargs("rvm_force_verbosity=%s", arg);
if (!plusarg) begin
arg = _vmm_opts.get_string("force_verbosity", , "Overrides the message verbosity level with the specified one");
if (arg != "") plusarg = 1;
end
if (plusarg) begin
level = arg.substr(0, 1); // Only look at the 1st 2 chars
level = level.tolower();
if (level == "er")
this.force_lvl = ERROR_SEV;
else if (level == "wa")
this.force_lvl = WARNING_SEV;
else if (level == "no")
this.force_lvl = NORMAL_SEV;
else if (level == "tr")
this.force_lvl = TRACE_SEV;
else if (level == "de")
this.force_lvl = DEBUG_SEV;
else if (level == "ve")
this.force_lvl = VERBOSE_SEV;
else if (level == "hi")
this.force_lvl = HIDDEN_SEV;
else
$write("Warning: Invalid +vmm_force_verbosity level: \"%s\"\n",
arg);
end
end
end
this.log_lvl = this.dflt_lvl;
this.log_start(STDOUT);
//
// Catch a common usage error
///
if (this.known.size() == 200) begin
if (!_vmm_opts.get_bit("log_nowarn_at_200") &&
!$test$plusargs("rvm_log_nowarn_at_200")) begin
`ifdef VMM_LOG_FORMAT_FILE_LINE
if (this.start_msg(FAILURE_TYP, WARNING_SEV, `__FILE__, `__LINE__)) begin
`else
if (this.start_msg(FAILURE_TYP, WARNING_SEV)) begin
`endif
void'(this.text("Over 200 vmm_log instances have been created."));
void'(this.text("Check that all vmm_data extensions use a static instance"));
void'(this.text("or use +vmm_log_nowarn_at_200 to disable this warning."));
this.end_msg();
end
end
end
if (this.known.size() == 1000) begin
if (!_vmm_opts.get_bit("log_nofatal_at_1000") &&
!$test$plusargs("rvm_log_nofatal_at_1000")) begin
`ifdef VMM_LOG_FORMAT_FILE_LINE
if (this.start_msg(FAILURE_TYP, FATAL_SEV, `__FILE__, `__LINE__)) begin
`else
if (this.start_msg(FAILURE_TYP, FATAL_SEV)) begin
`endif
void'(this.text("Over 1000 vmm_log instances have been created."));
void'(this.text("Check that all vmm_data extensions use a static instance"));
void'(this.text("or use +vmm_log_nofatal_at_1000 to disable this failure."));
this.end_msg();
end
end
end
endfunction: new
function void vmm_log::is_above(vmm_log log);
if (log == null || log == this) return;
// Make sure it is added only once
`foreach (this.below,i) begin
if (this.below[i] == log) return;
end
this.below.push_back(log);
log.parent_count++;
endfunction: is_above
function void vmm_log::is_not_above(vmm_log log);
if (log == null || log == this) return;
`foreach (this.below,i) begin
if (this.below[i] == log) begin
this.below.delete(i);
if (log.parent_count > 0) log.parent_count--;
return;
end
end
endfunction: is_not_above
function vmm_log vmm_log::copy(vmm_log to = null);
if (to == null) to = new(this.name, this.inst);
else begin
to.name = this.name;
to.inst = this.inst;
end
to.enabled_typs = this.enabled_typs;
to.log_lvl = this.log_lvl;
to.fp = this.fp;
copy = to;
endfunction: copy
function void vmm_log::set_name(string name);
this.name = name;
endfunction: set_name
function string vmm_log::get_name();
get_name = this.name;
endfunction: get_name
function void vmm_log::set_instance(string inst);
this.inst = inst;
endfunction: set_instance
function string vmm_log::get_instance();
get_instance = this.inst;
endfunction: get_instance
function void vmm_log::list(string name="/./",
string inst="/./",
bit recurse=0);
this.reset(name, inst, recurse);
for (vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
$write("%s(%s) [%s] F/E/W/e/w=%0d/%0d/%0d/%0d/%0d\n", log.name, log.inst, this.sev_image(log.log_lvl), log.n_msg[FATAL_SEV], log.n_msg[ERROR_SEV], log.n_msg[WARNING_SEV], log.n_demoted[ERROR_SEV], log.n_demoted[WARNING_SEV]);
for(int i = 0; i < log.below.size(); i++) begin
$write(" +--- %s(%s)\n", log.below[i].name, log.below[i].inst);
end
end
endfunction: list
function void vmm_log::display(string prefix="");
$display("%s", this.psdisplay(prefix));
endfunction
function string vmm_log::psdisplay(string prefix="");
$sformat(psdisplay, "%s%s(%s) [%s]", prefix, this.name, this.inst,
this.sev_image(this.log_lvl));
for (int i = 0; i < this.below.size(); i++) begin
string name, inst;
name = this.below[i].name; inst = this.below[i].inst;
$sformat(psdisplay, "%s\n%s +--- %s(%s)", psdisplay, prefix,
name, inst);
end
for (int i = 0; i < this.modifier_ids.size(); i++) begin
$sformat(psdisplay, "%s\n%s", psdisplay, this.modifier_cache[this.modifier_ids[i]].psdisplay({prefix, " "}));
end
endfunction
function void vmm_log::kill();
`foreach(this.known,i) begin
if (this.known[i] == this) this.known.delete(i);
end
endfunction: kill
function vmm_log_format vmm_log::set_format(vmm_log_format fmt);
if (fmt == null) begin
`vmm_error(this, "Cannot use NULL formatter in vmm_log::set_format(). Unchanged");
return null;
end
set_format = this.fmt;
this.fmt = fmt;
endfunction: set_format
function string vmm_log::set_typ_image(int typ,
string image);
if (!this.type_images.exists(typ)) begin
`vmm_error(this, "Invalid message type specified to vmm_log::set_typ_image()");
return "";
end
set_typ_image = this.type_images[typ];
this.type_images[typ] = image;
endfunction: set_typ_image
function string vmm_log::typ_image(int typ);
string sep = "";
if (this.type_images.exists(typ)) begin
return this.type_images[typ];
end
// Special types
if (typ == DEFAULT) begin
return "(default)";
end
if (typ == UNCHANGED) begin
return "(unchanged)";
end
// Composite type?
typ_image = "";
`foreach(this.type_list,i) begin
if (typ & this.type_list[i]) begin
typ_image = {typ_image, sep, this.type_images[this.type_list[i]]};
sep = "/";
end
end
if (typ_image == "") typ_image = "?MSG_TYP?";
endfunction: typ_image
function string vmm_log::set_sev_image(int severity,
string image);
if (!this.sev_images.exists(severity)) begin
`vmm_error(this, "Invalid message severity specified to vmm_log::set_sev_image()");
return "";
end
set_sev_image = this.sev_images[severity];
this.sev_images[severity] = image;
endfunction: set_sev_image
function string vmm_log::sev_image(int severity);
string sep = "";
if (this.sev_images.exists(severity)) begin
return this.sev_images[severity];
end
// Special severities
if (severity == DEFAULT) begin
return "(default)";
end
if (severity == UNCHANGED) begin
return "(unchanged)";
end
if (severity == IGNORE_SEV) begin
return "(ignored)";
end
// Composite severity?
sev_image = "";
`foreach(this.sev_list,i) begin
if (severity & this.sev_list[i]) begin
sev_image = {sev_image, sep, this.sev_images[this.sev_list[i]]};
sep = "/";
end
end
if (sev_image == "") sev_image = "?SEV_TYP?";
endfunction: sev_image
function string vmm_log::handling_image(int handling);
case (handling)
ABORT_SIM : handling_image = "ABORT";
COUNT_ERROR : handling_image = "ERROR";
STOP_PROMPT : handling_image = "STOP";
DEBUGGER : handling_image = "DEBUGGER";
DUMP_STACK : handling_image = "DUMPSTACK";
CONTINUE : handling_image = "CONTINUE";
IGNORE : handling_image = "IGNORE";
DEFAULT : handling_image = "(default)";
UNCHANGED : handling_image = "(unchanged)";
default : handling_image = "?HANDLING?";
endcase
endfunction: handling_image
function int vmm_log::default_handling(int severity);
case (severity)
FATAL_SEV : default_handling = ABORT_SIM;
ERROR_SEV : default_handling = COUNT_ERROR;
default : default_handling = CONTINUE;
endcase
endfunction: default_handling
function void vmm_log::report(string name="/./",
string inst="/./",
bit recurse=0);
vmm_log log;
int n_fatals = 0;
int n_errs = 0;
int n_warns = 0;
int n_derrs = 0;
int n_dwarns = 0;
string msg;
this.reset(name, inst, recurse);
for(log = this.for_each(); log != null; log = this.for_each()) begin
n_fatals += log.n_msg[FATAL_SEV];
n_errs += log.n_msg[ERROR_SEV];
n_warns += log.n_msg[WARNING_SEV];
n_derrs += log.n_demoted[ERROR_SEV];
n_dwarns += log.n_demoted[WARNING_SEV];
end
msg = this.fmt.pass_or_fail(n_fatals == 0 && n_errs == 0,
name, inst, n_fatals, n_errs, n_warns,
n_derrs, n_dwarns);
if (msg != "") $display("%s", msg);
endfunction: report
function bit vmm_log::start_msg(int typ,
int severity = DEFAULT_SEV
`ifdef VMM_LOG_FORMAT_FILE_LINE
, string fname = ""
, int line = -1
`endif
);
if (this.in_catcher) begin
string msg;
string txt[$];
txt.push_back("Cannot issue a new message from vmm_log_catcher::caught()");
msg = this.fmt.format_msg(this.name, this.inst,
this.typ_image(FAILURE_TYP),
this.sev_image(FATAL_SEV),
`ifdef VMM_LOG_FORMAT_FILE_LINE
fname, line,
`endif
txt);
$display("%s\n", msg);
this.n_msg[ERROR_SEV]++;
this.report(this.name, this.inst, 1);
$finish;
end
if (this.msg != null && !this.msg.invalid && this.msg.issued !== 1'b0) this.end_msg();
// Provide a default severity if none specified
if (severity < 0) begin
case (typ)
FAILURE_TYP : severity = ERROR_SEV;
NOTE_TYP : severity = NORMAL_SEV;
DEBUG_TYP : severity = DEBUG_SEV;
REPORT_TYP : severity = DEBUG_SEV;
NOTIFY_TYP : severity = HIDDEN_SEV;
TIMING_TYP : severity = WARNING_SEV;
XHANDLING_TYP : severity = WARNING_SEV;
PROTOCOL_TYP : severity = DEBUG_SEV;
TRANSACTION_TYP: severity = TRACE_SEV;
COMMAND_TYP : severity = TRACE_SEV;
CYCLE_TYP : severity = VERBOSE_SEV;
default : severity = NORMAL_SEV;
endcase
end
// Perform a quick, less expensive filtering here for loggers without
// promotion/demotion or watchpoints. Return immediately if the
// message is not printed based on severity and enabled categories
if (this.modifier_ids.size() == 0 &&
this.watchpoint_ids.size() == 0) begin
if ((this.force_lvl != DEFAULT_SEV && severity > this.force_lvl) || // Forced?
(this.force_lvl == DEFAULT_SEV && severity > this.log_lvl) || // Above?
(!(typ & this.enabled_typs) && (severity >= WARNING_SEV)) // Disabled?
) begin
this.msg.invalid = 1;
return 0;
end
end
this.msg.invalid = 0;
this.msg.original_typ = typ;
this.msg.original_severity = severity;
this.msg.effective_typ = typ;
this.msg.effective_severity = severity;
`ifdef VMM_LOG_FORMAT_FILE_LINE
this.msg.fname = fname;
this.msg.line = line;
`endif
this.msg.flushed = 0;
`ifdef VCS2006_06
// Work-around for NYI feature in 2006.06
// but IEEE 1800-2009 compliant
this.msg.text.delete();
`else
`ifdef INCA
this.msg.text.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.msg.text = '{};
`endif
`endif
this.msg.handling = DEFAULT;
this.msg.issued = 1'bx;
start_msg = 1;
// Do property-based promotion and filtering
// if there are no text-based filters
if (!this.has_text_modifiers) begin
this.promote();
this.filter();
if (this.msg.issued === 1'b0) begin
start_msg = 0;
// Record risky demotions
if (this.msg.effective_severity > this.msg.original_severity) begin
case (this.msg.original_severity)
ERROR_SEV : this.n_demoted[ERROR_SEV]++;
WARNING_SEV: this.n_demoted[WARNING_SEV]++;
endcase
end
this.msg.invalid = 1;
end
end
endfunction: start_msg
function bit vmm_log::text(string msg = "");
if (this.msg.invalid)
begin
`vmm_error(this, "Malformed message: vmm_log::text() called before vmm_log::start_msg()");
return 0;
end
text = 1;
if (msg == "")
begin
this.flush_msg();
return 1;
end
this.msg.text.push_back(msg);
endfunction: text
function void vmm_log::end_msg();
int handling;
if (this.msg.invalid)
begin
`vmm_error(this, "Malformed message: vmm_log::end_msg() called before vmm_log::start_msg()");
return;
end
this.flush_msg();
// If the message was not issued, it does not exists
if (!this.msg.issued) begin
this.msg.invalid = 1;
return;
end
handling = this.msg.handling;
if (handling == DEFAULT) handling = default_handling(this.msg.effective_severity);
// Avoid recursive handling in callbacks
if (this.in_callbacks) handling = CONTINUE;
case (handling)
ABORT_SIM: begin
bit finished = 0;
this.in_callbacks = 1;
`vmm_callback(vmm_log_callbacks, pre_abort(this));
`vmm_callback(vmm_log_callbacks, pre_finish(this, finished));
this.in_callbacks = 0;
if (!finished) begin
this.report();
$finish;
end
end
DUMP_STACK: begin
this.in_callbacks = 1;
`vmm_callback(vmm_log_callbacks, pre_debug(this));
this.in_callbacks = 0;
`ifdef VCS
$stack;
`endif
$stop;
end
DEBUGGER: begin
this.in_callbacks = 1;
`vmm_callback(vmm_log_callbacks, pre_debug(this));
this.in_callbacks = 0;
$stop;
end
STOP_PROMPT: begin
this.in_callbacks = 1;
`vmm_callback(vmm_log_callbacks, pre_stop(this));
this.in_callbacks = 0;
$stop;
end
COUNT_ERROR: begin
this.error_count++;
if (this.error_limit > 0 && this.error_count >= this.error_limit) begin
bit finished = 0;
string msg = this.fmt.abort_on_error(this.error_count,
this.error_limit);
if (msg != "") $display("%s", msg);
this.in_callbacks = 1;
`vmm_callback(vmm_log_callbacks, pre_abort(this));
`vmm_callback(vmm_log_callbacks, pre_finish(this, finished));
this.in_callbacks = 0;
if (!finished) begin
this.report();
$finish;
end
end
end
endcase
this.msg.invalid = 1;
endfunction: end_msg
function void vmm_log::process_catch(vmm_log_msg msg);
`foreach (this.catcher_ids,i) begin
vmm_log_catcher_descr catcher_item;
catcher_item = this.catcher_cache[this.catcher_ids[i]];
if (!(catcher_item.severity & msg.effective_severity))
continue;
if (!(catcher_item.typs & msg.effective_typ))
continue;
if (catcher_item.text != "") begin
bit match = 0;
`foreach (msg.text,j) begin
if (`vmm_str_match(msg.text[j], catcher_item.text)) begin
match = 1;
break;
end
end
if (!match) continue;
end
catcher_item.catcher.thrown = 0;
catcher_item.catcher.issued = 0;
catcher_item.catcher.caught(msg);
if (catcher_item.catcher.thrown &&
catcher_item.catcher.issued) begin
string img;
string txt[$];
txt.push_back("Cannot call vmm_log_catcher::issue() AND vmm_log_catcher::throw() in the same vmm_log_catcher::caught() implementation. Ignoring throw.");
catcher_item.catcher.thrown = 0;
img = this.fmt.format_msg(this.name, this.inst,
this.typ_image(FAILURE_TYP),
this.sev_image(WARNING_SEV),
`ifdef VMM_LOG_FORMAT_FILE_LINE
msg.fname, msg.line,
`endif
txt);
$write("%s\n", img);
this.n_msg[WARNING_SEV]++;
end
if (!catcher_item.catcher.thrown) begin
this.msg.issued = catcher_item.catcher.issued;
break;
end
end
endfunction
function void vmm_log::flush_msg();
string msg;
if (this.msg.flushed == 0) begin
// Perform promotion/demotion if there are text filters
// (it will have been done in start_msg() if there were none)
if (this.has_text_modifiers) begin
this.promote();
this.filter();
end
// Record risky demotions
if (this.msg.effective_severity > this.msg.original_severity) begin
case (this.msg.original_severity)
ERROR_SEV : this.n_demoted[ERROR_SEV]++;
WARNING_SEV: this.n_demoted[WARNING_SEV]++;
endcase
end
if (this.msg.issued === 1'b0) begin
this.notify();
return;
end
this.in_catcher = 1;
this.process_catch(this.msg);
this.in_catcher = 0;
this.notify();
if (this.msg.issued === 1'b0) return;
msg = this.fmt.format_msg(this.name, this.inst,
this.typ_image(this.msg.effective_typ),
this.sev_image(this.msg.effective_severity),
`ifdef VMM_LOG_FORMAT_FILE_LINE
this.msg.fname, this.msg.line,
`endif
this.msg.text);
if (msg != "") begin
`foreach(this.fp,i) begin
$fdisplay(this.fp[i], "%s", msg);
end
// Did we just send an ERROR or FATAL message to /dev/null??
if (this.fp.size() == 0 && this.msg.effective_severity <= ERROR_SEV) begin
// Force it to appear on STDOUT
$display("%s", msg);
end
end
this.msg.flushed++;
`ifdef VCS2006_06
// Work-around for NYI feature in 2006.06
// but IEEE 1800-2009 compliant
this.msg.text.delete();
`else
`ifdef INCA
this.msg.text.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.msg.text = '{};
`endif
`endif
end
else begin
if (this.msg.text.size() > 0) begin
msg = this.fmt.continue_msg(this.name, this.inst,
this.typ_image(this.msg.effective_typ),
this.sev_image(this.msg.effective_severity),
`ifdef VMM_LOG_FORMAT_FILE_LINE
this.msg.fname, this.msg.line,
`endif
this.msg.text);
if (msg != "") begin
`foreach(this.fp,i) begin
$fdisplay(this.fp[i], "%s", msg);
end
// Did we just send an ERROR or FATAL message to /dev/null??
if (this.fp.size() == 0 && this.msg.effective_severity <= ERROR_SEV) begin
// Force it to appear on STDOUT
$display("%s", msg);
end
end
this.msg.flushed++;
`ifdef VCS2006_06
// Work-around for NYI feature in 2006.06
// but IEEE 1800-2009 compliant
this.msg.text.delete();
`else
`ifdef INCA
this.msg.text.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.msg.text = '{};
`endif
`endif
end
return;
end
this.n_msg[this.msg.effective_severity]++;
endfunction: flush_msg
function void vmm_log::enable_types(int typs,
string name = "",
string inst = "",
bit recursive = 0);
if (typs == DEFAULT_TYP ) typs = ALL_TYPS ;
if (typs < 0) begin
`vmm_error(this, "Invalid message type specified to vmm_log::enable_types");
return;
end
//
// Enable specified types in all specified log insts
//
this.reset(name, inst, recursive);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
log.enabled_typs |= typs;
end
endfunction: enable_types
function void vmm_log::disable_types(int typs,
string name = "",
string inst = "",
bit recursive = 0);
if (typs < 0) begin
`vmm_error(this, "Invalid message type specified to vmm_log::disable_types");
return;
end
// Cannot disable failure messages
if (typs & FAILURE_TYP) begin
`vmm_warning(this, "Cannot disable FAILURE_TYP messages");
typs -= FAILURE_TYP;
end
//
// Disable specified types in all specified log insts
//
this.reset(name, inst, recursive);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
log.enabled_typs &= ~(typs);
end
endfunction: disable_types
function int vmm_log::modify(string name = "",
string inst = "",
bit recursive = 0,
int typ = ALL_TYPS,
int severity = ALL_SEVS,
string text = "",
int new_typ = UNCHANGED,
int new_severity = UNCHANGED,
int handling = UNCHANGED);
vmm_log_modifier modifier;
int mod_id;
// Some severities cannot be demoted too far
if (severity == FATAL_SEV &&
new_severity > ERROR_SEV) begin
`vmm_error(this, "Cannot demote FATAL_SEV severity to less than ERROR_SEV");
return -2;
end
if (severity == ERROR_SEV &&
new_severity > WARNING_SEV) begin
`vmm_error(this, "Cannot demote ERROR severity to less than WARNING");
return -2;
end
//
// Add a description of the modification to the cache
//
modifier = new;
modifier.typ = typ;
modifier.severity = severity;
modifier.pattern = text;
modifier.new_typ = new_typ;
modifier.new_severity = new_severity;
modifier.handling = handling;
// Remove "/" surrounding the pattern, if any
if (`vmm_str_match(modifier.pattern, "^/(.*)/$")) begin
modifier.pattern = `vmm_str_backref(modifier.pattern, 0);
end
mod_id = this.modifier_cache.num();
this.modifier_cache[mod_id] = modifier;
//
// Link all affected log instances
//
this.reset(name, inst, recursive);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
log.modifier_ids.push_back(mod_id);
if (modifier.pattern != "") log.has_text_modifiers++;
end
modify = mod_id;
endfunction: modify
function void vmm_log::unmodify(int modification_id = -1,
string name = "",
string inst = "",
bit recursive = 0);
if (modification_id < -1) begin
`vmm_error(this, `vmm_sformatf("Invalid modification ID %0d specified to vmm_log::unmodify()",
modification_id));
return;
end
// Does it exist?
if (modification_id >= 0) begin
if (!this.modifier_cache.exists(modification_id)) begin
`vmm_error(this, `vmm_sformatf("Unknown modification ID %0d specified to vmm_log::unmodify()",
modification_id));
return;
end
end
//
// Unlink all affected log instances
//
this.reset(name, inst, recursive);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
// Find the specified modifier...
`foreach(log.modifier_ids,i) begin
if (modification_id >= 0 && log.modifier_ids[i] != modification_id) continue;
if (this.modifier_cache[log.modifier_ids[i]].pattern != "") begin
log.has_text_modifiers--;
if (log.has_text_modifiers < 0) begin
$write("***** vmm_log Internal ERROR: has_text_modifiers < 0\n");
log.has_text_modifiers = 0;
end
end
if (modification_id >= 0) begin
log.modifier_ids.delete(i);
break;
end
end
if (modification_id < 0) begin
`ifdef VCS2006_06
// Work-around for NYI feature in 2006.06
// but IEEE 1800-2009 compliant
log.modifier_ids.delete();
`else
`ifdef INCA
log.modifier_ids.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
log.modifier_ids = '{};
`endif
`endif
end
end
endfunction: unmodify
function void vmm_log::promote();
// Apply modifiers in the order they were created
`foreach(this.modifier_ids,i) begin
vmm_log_modifier mod;
mod = this.modifier_cache[this.modifier_ids[i]];
// Does it apply to this message?
// Message type must be included
if (!(mod.typ & this.msg.effective_typ)) continue;
// Message severity must be included
if (!(mod.severity & this.msg.effective_severity)) continue;
// If specified, the text pattern must match
if (mod.pattern != "") begin
bit matched = 0;
int idx;
`foreach (this.msg.text,idx) begin
if (`vmm_str_match(this.msg.text[idx], mod.pattern)) begin
matched = 1;
break;
end
end
if (!matched) continue;
end
// Promote message!
if (mod.new_typ != UNCHANGED) begin
if (mod.new_typ == DEFAULT) begin
this.msg.effective_typ = this.msg.original_typ;
end else begin
this.msg.effective_typ = mod.new_typ;
end
end
if (mod.new_severity != UNCHANGED) begin
if (mod.new_severity == DEFAULT) begin
this.msg.effective_severity = this.msg.original_severity;
end else begin
this.msg.effective_severity = mod.new_severity;
end
// Some severities cannot be demoted too far
if (this.msg.original_severity == FATAL_SEV &&
this.msg.effective_severity > ERROR_SEV) begin
this.msg.effective_severity = ERROR_SEV ;
end
if (this.msg.original_severity == ERROR_SEV &&
this.msg.effective_severity > WARNING_SEV) begin
this.msg.effective_severity = WARNING_SEV;
end
end
if (mod.handling != UNCHANGED) begin
this.msg.handling = mod.handling;
end
end
endfunction: promote
function void vmm_log::filter();
if (this.msg.issued === 1'b0 || // Already filtered out
this.msg.effective_severity == IGNORE_SEV || // Demoted to be ignored
// Cannot disable any types with severity FATAL or ERROR
(!(this.msg.effective_typ & this.enabled_typs) && // Disabled
(this.msg.effective_severity >= WARNING_SEV)) ||
(this.force_lvl != DEFAULT_SEV && this.msg.effective_severity > this.force_lvl) ||
(this.force_lvl == DEFAULT_SEV && this.log_lvl < this.msg.effective_severity)) begin
this.msg.issued = 1'b0;
return;
end
this.msg.issued = 1'b1;
endfunction: filter
function void vmm_log::notify();
// Check notifiers in the order they were created
`foreach(this.watchpoint_ids,i) begin
vmm_log_watchpoint wp;
wp = this.watchpoint_cache[this.watchpoint_ids[i]];
// Does it apply to this message?
// Message type must be included
if (!(wp.typ & this.msg.effective_typ)) continue;
// Message severity must be included
if (!(wp.severity & this.msg.effective_severity)) continue;
// The message must be issued or not
if (wp.issued !== 1'bx && wp.issued !== this.msg.issued) begin
continue;
end
// If specified, the text pattern must match
if (wp.pattern != "") begin
bit matched = 0;
integer idx;
`foreach(this.msg.text,idx) begin
if (`vmm_str_match(this.msg.text[idx], wp.pattern)) begin
matched = 1;
break;
end
end
if (!matched) continue; // to the next watchpt
end
// This is a watched message
wp.msg = this.msg.copy();
-> wp.seen;
end
endfunction: notify
function void vmm_log::set_verbosity(int severity,
string name = "",
string inst = "",
bit recursive = 0);
if (!this.sev_images.exists(severity) ||
severity < ERROR_SEV ||
severity > VERBOSE_SEV) begin
`vmm_error(this, "Invalid severity specified to vmm_log::set_verbosity()");
return;
end
this.reset(name, inst, recursive);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
log.log_lvl = severity;
end
endfunction: set_verbosity
function int vmm_log::get_verbosity();
if(this.force_lvl != DEFAULT_SEV)
get_verbosity = this.force_lvl;
else
get_verbosity = this.log_lvl;
endfunction: get_verbosity
function void vmm_log::log_start(int file,
string name = "",
string inst = "",
bit recurse = 0);
// Find the loggers in question
vmm_log log;
this.reset(name, inst, recurse);
for(log = this.for_each(); log != null; log = this.for_each()) begin
// Check that we are not already logging to this file
`foreach(log.fp,i) begin
if (log.fp[i] == file) return;
end
log.fp.push_back(file);
end
endfunction: log_start
function void vmm_log::log_stop(int file,
string name = "",
string inst = "",
bit recurse = 0);
// Find the loggers in question
vmm_log log;
this.reset(name, inst, recurse);
for(log = this.for_each(); log != null; log = this.for_each()) begin
// Check that we are indeed logging to this file
`foreach(log.fp,i) begin
if (log.fp[i] == file) log.fp.delete(i);
// Cannot close this file because it may still be used by other loggers
end
end
endfunction: log_stop
function void vmm_log::stop_after_n_errors(int n);
this.error_count = 0;
this.error_limit = n;
endfunction: stop_after_n_errors
function int vmm_log::get_message_count(int severity = ALL_SEVS,
string name = "",
string inst = "",
bit recurse = 0);
get_message_count = 0;
this.reset(name, inst, recurse);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
if (severity & FATAL_SEV) get_message_count += log.n_msg[FATAL_SEV];
if (severity & ERROR_SEV) get_message_count += log.n_msg[ERROR_SEV];
if (severity & WARNING_SEV) get_message_count += log.n_msg[WARNING_SEV];
if (severity & NORMAL_SEV) get_message_count += log.n_msg[NORMAL_SEV];
if (severity & TRACE_SEV) get_message_count += log.n_msg[TRACE_SEV];
if (severity & DEBUG_SEV) get_message_count += log.n_msg[DEBUG_SEV];
if (severity & VERBOSE_SEV) get_message_count += log.n_msg[VERBOSE_SEV];
if (severity & HIDDEN_SEV) get_message_count += log.n_msg[HIDDEN_SEV];
if (severity & IGNORE_SEV) get_message_count += log.n_msg[IGNORE_SEV];
end
endfunction: get_message_count
task vmm_log::wait_for_msg(string name = "",
string inst = "",
bit recurse = 0,
int typs = ALL_TYPS,
int severity = ALL_SEVS,
string text = "",
logic issued = 1'bx,
ref vmm_log_msg msg);
int wp_id;
wp_id = this.create_watchpoint(typs, severity, text, issued);
this.add_watchpoint(wp_id, name, inst, recurse);
this.wait_for_watchpoint(wp_id, msg);
this.remove_watchpoint(wp_id, name, inst, recurse);
endtask: wait_for_msg
function int vmm_log::create_watchpoint(int typs = ALL_TYPS,
int severity = ALL_SEVS,
string text = "",
logic issued = 1'bx);
vmm_log_watchpoint wp = new;
int wp_id;
//
// Add a description of the watchpoint to the cache
//
wp.typ = typs;
wp.severity = severity;
wp.pattern = text;
wp.issued = issued;
// Remove "/" surrounding the pattern, if any
if (`vmm_str_match(wp.pattern, "^/(.*)/$")) begin
wp.pattern = `vmm_str_backref(wp.pattern, 0);
end
wp_id = this.watchpoint_cache.num();
this.watchpoint_cache[wp_id] = wp;
create_watchpoint = wp_id;
endfunction: create_watchpoint
function void vmm_log::add_watchpoint(int watchpoint_id,
string name = "",
string inst = "",
bit recurse = 0);
vmm_log log;
vmm_log_watchpoint wp;
if (!this.watchpoint_cache.exists(watchpoint_id)) begin
`vmm_warning(this, `vmm_sformatf("Watchpoint #%0d does not exist", watchpoint_id));
return;
end
wp = this.watchpoint_cache[watchpoint_id];
//
// Link all affected log insts
//
this.reset(name, inst, recurse);
for(log = this.for_each(); log != null; log = this.for_each()) begin
log.watchpoint_ids.push_back(watchpoint_id);
if (wp.pattern != "") log.has_text_modifiers++;
end
endfunction: add_watchpoint
function void vmm_log::remove_watchpoint(int watchpoint_id = -1,
string name = "",
string inst = "",
bit recurse = 0);
vmm_log log;
//
// Unlink all affected log insts
//
this.reset(name, inst, recurse);
for(log = this.for_each(); log != null; log = this.for_each()) begin
// Find the specified watchpoint...
`foreach(log.watchpoint_ids,i) begin
if (watchpoint_id < 0 || log.watchpoint_ids[i] == watchpoint_id) begin
if (this.watchpoint_cache[log.watchpoint_ids[i]].pattern != "") begin
log.has_text_modifiers--;
if (log.has_text_modifiers < 0) begin
$write("***** vmm_log Internal ERROR: has_text_modifiers < 0\n");
log.has_text_modifiers = 0;
end
end
log.watchpoint_ids.delete(i);
if (watchpoint_id >= 0) break;
end
end
end
endfunction: remove_watchpoint
task vmm_log::wait_for_watchpoint(int watchpoint_id,
ref vmm_log_msg msg);
vmm_log_watchpoint wp;
if (!this.watchpoint_cache.exists(watchpoint_id)) begin
`vmm_warning(this, `vmm_sformatf("Watchpoint #%0d does not exist", watchpoint_id));
msg = null;
end
else begin
//
// Wait for a triggering of the watchpoint
//
wp = this.watchpoint_cache[watchpoint_id];
@wp.seen;
msg = wp.msg;
end
endtask: wait_for_watchpoint
function int vmm_log::catch(vmm_log_catcher catcher,
string name = "",
string inst = "",
bit recurse = 0,
int typs = ALL_TYPS,
int severity = ALL_SEVS,
string text = "");
int catcher_id = 0;
vmm_log_catcher_descr catcher_item;
if (catcher == null) begin
`vmm_error(this,"Null vmm_log_catcher object passed to vmm_log::catch");
return -1;
end
catcher_item = new();
catcher_item.catcher = catcher;
// Remove "/" surrounding the pattern, if any
if (`vmm_str_match(text, "^/(.*)/$")) begin
catcher_item.text = `vmm_str_backref(text, 0);
end
else catcher_item.text = text;
catcher_item.name = name;
catcher_item.inst = inst;
catcher_item.recurse = recurse;
catcher_item.typs = typs;
catcher_item.severity = severity;
catcher_id = this.catcher_cache.num();
this.catcher_cache[catcher_id] = catcher_item;
this.reset(name, inst, recurse);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
log.catcher_ids.push_front(catcher_id);
end
return catcher_id;
endfunction: catch
function bit vmm_log::uncatch (int catcher_id);
vmm_log_catcher_descr catcher_item;
int index = 0;
int match = 0;
if((catcher_id < 0 )) begin
`vmm_warning(this,"Invalid catcher_id given to vmm_log::uncatch. Cannot uncatch");
return 0;
end
// Does it exist?
if (catcher_id >= 0) begin
if (!this.catcher_cache.exists(catcher_id)) begin
`vmm_error(this, `vmm_sformatf("Unknown catcher ID %0d specified to vmm_log::uncatch()",
catcher_id));
return 0;
end
end
catcher_item = this.catcher_cache[catcher_id];
this.reset(catcher_item.name, catcher_item.inst, catcher_item.recurse);
for(vmm_log log = this.for_each(); log != null; log = this.for_each()) begin
`foreach(log.catcher_ids,i) begin
if (log.catcher_ids[i] == catcher_id) begin
log.catcher_ids.delete(i);
break;
end
end
end
endfunction: uncatch
function void vmm_log::uncatch_all();
this.catcher_cache.delete();
`foreach (this.known,i) begin
`ifdef VCS2006_06
// Work-around for NYI feature in 2006.06
// but IEEE 1800-2009 compliant
this.known[i].catcher_ids.delete();
`else
`ifdef INCA
this.known[i].catcher_ids.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.known[i].catcher_ids = '{};
`endif
`endif
end
endfunction: uncatch_all
function void vmm_log::prepend_callback(vmm_log_callbacks cb);
// Check if callback has already been registered...
`foreach (this.callbacks,i) begin
if (this.callbacks[i] == cb) begin
`ifdef VMM_LOG_FORMAT_FILE_LINE
if (this.start_msg(FAILURE_TYP, WARNING_SEV, `__FILE__, `__LINE__)) begin
`else
if (this.start_msg(FAILURE_TYP, WARNING_SEV)) begin
`endif
void'(this.text("Callback has already been registered"));
this.end_msg();
end
return;
end
end
// Register new callback
this.callbacks.push_front(cb);
endfunction: prepend_callback
function void vmm_log::append_callback(vmm_log_callbacks cb);
// Check if callback has already been registered...
`foreach (this.callbacks,i) begin
if (this.callbacks[i] == cb) begin
`ifdef VMM_LOG_FORMAT_FILE_LINE
if (this.start_msg(FAILURE_TYP, WARNING_SEV, `__FILE__, `__LINE__)) begin
`else
if (this.start_msg(FAILURE_TYP, WARNING_SEV)) begin
`endif
void'(this.text("Callback has already been registered"));
this.end_msg();
end
return;
end
end
// Register new callback
this.callbacks.push_back(cb);
endfunction: append_callback
function void vmm_log::unregister_callback(vmm_log_callbacks cb);
// Look for callback
`foreach (this.callbacks,i) begin
if (this.callbacks[i] == cb) begin
// Unregister it
this.callbacks.delete(i);
return;
end
end
`ifdef VMM_LOG_FORMAT_FILE_LINE
if (this.start_msg(FAILURE_TYP, WARNING_SEV, `__FILE__, `__LINE__)) begin
`else
if (this.start_msg(FAILURE_TYP, WARNING_SEV)) begin
`endif
void'(this.text("Callback was not registered"));
this.end_msg();
end
endfunction: unregister_callback
//
// vmm_log_format
//
function string vmm_log_format::format_msg(string name,
string inst,
string msg_typ,
string severity,
`ifdef VMM_LOG_FORMAT_FILE_LINE
string fname,
int line,
`endif
ref string lines[$]);
`ifdef VMM_LOG_FORMAT_FILE_LINE
if (line >= 0)
$sformat(format_msg, "%s[%s] on %s(%s) at %t from %s, line %0d:",
severity, msg_typ, name, inst, $realtime, fname, line);
else
`endif
$sformat(format_msg, "%s[%s] on %s(%s) at %t:",
severity, msg_typ, name, inst, $realtime);
foreach(lines[i]) begin
string line = lines[i];
string t;
format_msg = {format_msg, "\n "};
while (1) begin
string pre;
if (!`vmm_str_match(line, "\n")) begin
format_msg = {format_msg, line};
break;
end
pre = `vmm_str_prematch(line);
format_msg = {format_msg, pre, "\n "};
line = `vmm_str_postmatch(line);
end
end
endfunction: format_msg
function string vmm_log_format::continue_msg(string name,
string inst,
string msg_typ,
string severity,
`ifdef VMM_LOG_FORMAT_FILE_LINE
string fname,
int line,
`endif
ref string lines[$]);
continue_msg = "";
foreach (lines[i]) begin
if (i > 0) continue_msg = {continue_msg, "\n"};
continue_msg = { continue_msg, " ", lines[i] };
end
endfunction: continue_msg
function string vmm_log_format::abort_on_error(int count,
int limit);
abort_on_error = "Maximum number of error messages exceeded. Aborting\nUse method stop_after_n_errors() of vmm_log to increase threshold.";
endfunction: abort_on_error
function string vmm_log_format::pass_or_fail(bit pass,
string name,
string inst,
int fatals,
int errors,
int warnings,
int dem_errs,
int dem_warns);
if (pass) begin
$sformat(pass_or_fail, "Simulation PASSED on %s (%s) at %t (%0d warnings, %0d demoted errors & %0d demoted warnings)",
name, inst, $realtime, warnings, dem_errs, dem_warns);
end else begin
$sformat(pass_or_fail, "Simulation *FAILED* on %s (%s) at %t: %0d errors, %0d warnings",
name, inst, $realtime, fatals + errors, warnings);
end
endfunction: pass_or_fail
function void vmm_log_catcher::issue(vmm_log_msg msg);
this.issued = 1;
endfunction:issue
function void vmm_log_catcher::throw(vmm_log_msg msg);
this.thrown = 1;
endfunction:throw