// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: report_pli.cc
// Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved
// 4150 Network Circle, Santa Clara, California 95054, U.S.A.
// * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// For the avoidance of doubt, and except that if any non-GPL license
// choice is available it will apply instead, Sun elects to use only
// the General Public License version 2 (GPLv2) at this time for any
// software where a choice of GPL license versions is made
// available with the language indicating that GPLv2 or any later version
// may be used, or where a choice of which version of the GPL is applied is
// otherwise unspecified.
// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
// CA 95054 USA or visit www.sun.com if you need additional information or
// ========== Copyright Header End ============================================
// Test if GNU gcc version is less than 3.x. If so, use pre-standard lib includes
// VSP-modification begin
#endif // else __GNUC__ < 3
extern void parse_format_string(char*, char*, int);
extern int regex_match(const char*, const char*);
HashTable
*g_report_hash_table
;
int g_report_hash_table_initialized
= 0;
ReportClass g_internal_reports
; // for report utility information
int g_cycle_count
= 0, g_death_cycle_count
;
////////////////////////////////////////////////////////////////////////////////
// PLI accessor functions
// PLI accessor for the function ReportClass::report()
void ReportClass_report()
const HashValueListNode
*this_key
, *this_value
;
const char *mip_name
= tf_mipname(); // Stash the module pathname in mip_name.
ReportType report_type
= (ReportType
)tf_getp(1);
// Create a key and retrieve the correct ReportClass* from the hash.
this_key
= new HashValueListNode(mip_name
, report_type
); this_value
= g_report_hash_table
->lookup(*this_key
);
if (this_value
== NULL
) {
tf_error("ReportClass (Internal): Couldn't find value for key with mipname %s!\n",
ReportClass this_report
= *this_value
->report_class
;
if (this_report
.this_will_print(report_type
)) {
char format_str
[REPORT_MAX_LINE_LENGTH
];
char time_str
[REPORT_MAX_LINE_LENGTH
];
char msg_str
[REPORT_MAX_LINE_LENGTH
];
// Parse the format string and argument list to produce a
strncpy(format_str
, tf_getcstringp(2), REPORT_MAX_LINE_LENGTH
); // make a copy so we can modify it in parse_format_string.
format_str
[REPORT_MAX_LINE_LENGTH
-1] = '\0';
parse_format_string(msg_str
, format_str
, 3);
// Generate the time string.
// - tack on simulation time if so desired.
if (ReportClass::get_show_simulation_time())
sprintf(time_str
, "%d,%d", g_cycle_count
, tf_gettime());
sprintf(time_str
, "%d", g_cycle_count
);
// Generate the location string, shortening the pathname if so desired.
if (ReportClass::get_short_pathnames()) {
const char * loc_p
= strrchr(mip_name
, '.');
mip_name
= (loc_p
== NULL
) ? mip_name
: ++loc_p
;
// Actually make the report. Note that msg_str is always a simple
// string at this point, with no format chars.
this_report
.report(report_type
, time_str
, mip_name
, "%s", msg_str
);
// no need for strings that won't print.
this_report
.report(report_type
, "", "", "%s", "");
// This function is called at the beginning of simulation for each call
// made in the design. This allows us to populate an hash table with a
// pointer to each {report_type, instance_id} pair.
void report_verilog_check(void)
if (g_report_hash_table_initialized
== 0) {
// Instantiate a ReportClass object so we can use it to print and such.
//g_internal_reports = new ReportClass();
g_report_hash_table
= new HashTable();
g_report_hash_table_initialized
++;
// check that we have at least 2 parameters
tf_error("$report: (%s) line: %d; Too few arguments to ",
// Check that the report's arguments are valid.
// Check that print format characters belong to the set [hHdDoObBsS]
index
= tf_getcstringp(2);
tf_error("$report: (%s) line: %d; Illegal format to : \"%%%c\"",
__FILE__
,__LINE__
, (char)*index
);
// Check that there are at least as many parameters as formats
if (nformats
> (nparam
- 2)) {
tf_error("$report: (%s) line: %d: Missing arguments to $report call (%d expected, %d found)",
__FILE__
,__LINE__
, nformats
, nparam
-2);
const char *mip_name
= tf_mipname();
ReportType report_type
= (ReportType
)tf_getp(1);
if (!is_report_type_valid(report_type
)) {
tf_error("$report: (%s) line: %d; Invalid report type in %s: %d",
__FILE__
, __LINE__
, mip_name
, report_type
);
// Build a new hash key for this report and insert it into the table.
key
= new HashValueListNode(mip_name
, report_type
);
tf_error("report_verilog_check:: unable to allocate space for new HashValueListNode");
// Insert our element into the list
if (g_report_hash_table
->lookup(*key
) == NULL
) {
key
->report_class
= new ReportClass();
g_report_hash_table
->insert(key
);
} // report_verilog_check
//////////////////////////////////////////////////////////////////
// Wrappers for setting default states.
// Set the *default* print level per type. The change applies to all report
// objects for which set_print_level() has not been called previously.
void ReportClass_set_default_print_level() {
ReportType type
= (ReportType
) tf_getp(1);
PrintLevel pl
= (PrintLevel
) tf_getp(2);
g_internal_reports
.set_default_print_level(type
, pl
);
// Set the *default* error level per type. The change applies to all report
// objects for which set_error_level() has not been called previously.
void ReportClass_set_default_error_level() {
ReportType type
= (ReportType
) tf_getp(1);
ErrorLevel el
= (ErrorLevel
) tf_getp(2);
g_internal_reports
.set_default_error_level(type
, el
);
// Set the *default* table_mode per type. The change applies to all report
// objects for which set_table_mode() has not been called previously.
void ReportClass_set_default_table_mode() {
ReportType type
= (ReportType
) tf_getp(1);
int mode
= (int) tf_getp(2);
g_internal_reports
.set_default_table_mode(type
, mode
);
////////////////////////////////////////////////////////////////////////////////
// PLI accessors for ReportClass instance variables
* ReportClass_set_print_level( regexp, report_type, print_level )
* This function performs the same tasks as "pos_level_control" did for pos_report.
* (1) regular expression -- matches the hierarchical paths for which the
* print level should be changed
* (2) report type -- the message types which should be changed. Using
* ALL_REPORTS as the report type will change the print level for all
* messages that match the regexp.
* (3) print level -- the level to which the matching classes should be set
* to suppress all timeout reporting and failures in the "mdu"
* instance and all instances beneath it:
* (".*\.mdu\..*", RTYP_TIMEOUT, RPRT_NOP);
* note that because periods are special regular expression characters
* If no matches are found for the regex_pattern a warning will be printed. */
void ReportClass_set_print_level() {
char *match_pattern
= tf_getcstringp(1);
ReportType type
= (ReportType
) tf_getp(2);
PrintLevel pl
= (PrintLevel
) tf_getp(3);
const HashValueListNode
*value
= NULL
;
char prefixed_pattern
[REPORT_MAX_FIELD_WIDTH
];
// Check that the arguments are valid.
if (!is_report_type_valid(type
))
tf_error("ReportClass::set_print_level: Invalid report type (%d) to ", type
);
if (!is_print_level_valid(pl
))
tf_error("ReportClass::set_print_level: Invalid print level (%d) to ", pl
);
strcpy(prefixed_pattern
, ReportClass::get_path_prefix());
strncat(prefixed_pattern
, match_pattern
, REPORT_MAX_FIELD_WIDTH
-strlen(prefixed_pattern
));
regex_status
= regex_match(prefixed_pattern
, "");
tf_error("ReportClass::set_print_level: Bad regular expression (%s)", prefixed_pattern
);
// Walk through the hash, finding entries which match the regexp.
g_report_hash_table
->start_walk();
while (value
= g_report_hash_table
->next_element()) {
// Attempt to match the stored pathname to the pre-compiled regular expression.
regex_status
= regex_match(NULL
, value
->mip_name
);
// Actually set the print level if we have a match. We have to
// construct our own report to tell the user about this, since the
// ReportClass itself doesn't know time or location.
char cycle_str
[REPORT_MAX_FIELD_WIDTH
];
value
->report_class
->set_print_level(type
, pl
, TRUE
); // silently -- class doesn't know the path
sprintf(cycle_str
, "%d", g_cycle_count
);
g_internal_reports
.report(RTYP_REPORT_MSG
,
cycle_str
, value
->mip_name
,
"Setting print level for report type %s to %s",
report_type_to_str(value
->report_type
),
tf_warning("ReportClass (Internal): Could not find match for regular expression \"%s\"\n", prefixed_pattern
);
* This function sets the error level for a given instance of ReportClass.
* It follows the same semantics as "set_print_level" above.
void ReportClass_set_error_level()
char *match_pattern
= tf_getcstringp(1);
ReportType type
= (ReportType
) tf_getp(2);
ErrorLevel el
= (ErrorLevel
) tf_getp(3);
const HashValueListNode
*value
= NULL
;
char prefixed_pattern
[REPORT_MAX_FIELD_WIDTH
];
// Check that the arguments are valid.
if (!is_report_type_valid(type
))
tf_error("ReportClass::set_error_level: Invalid report type (%d) to ", type
);
if (!is_error_level_valid(el
))
tf_error("ReportClass::set_error_level: Invalid error level (%d) to ", el
);
strcpy(prefixed_pattern
, ReportClass::get_path_prefix());
strncat(prefixed_pattern
, match_pattern
, REPORT_MAX_FIELD_WIDTH
-strlen(prefixed_pattern
));
regex_status
= regex_match(match_pattern
, "");
tf_error("ReportClass::set_error_level): Bad regular expression (%s)", match_pattern
);
// Walk through the hash, finding entries which match the regexp.
g_report_hash_table
->start_walk();
while (value
= g_report_hash_table
->next_element()) {
// Attempt to match the stored pathname to the pre-compiled regular expression.
regex_status
= regex_match(NULL
, value
->mip_name
);
// Actually set table mode for the matching instances. We do this silently.
char cycle_str
[REPORT_MAX_FIELD_WIDTH
];
value
->report_class
->set_error_level(type
, el
, TRUE
);
sprintf(cycle_str
, "%d", g_cycle_count
);
g_internal_reports
.report(RTYP_REPORT_MSG
,
cycle_str
, value
->mip_name
,
"Setting error level for report type %s to %s",
report_type_to_str(value
->report_type
),
tf_warning("ReportClass (Internal): Could not find match for regular expression \"%s\"\n", match_pattern
);
* ReportClass_set_table_mode
* This function sets the "table mode" parameter for a given instance of ReportClass.
* It follows the same semantics as the "set_print_level" function above.
* Placing a particular message type in table mode will:
* - remove the prefixes from its output
* - remove automatic carriage returns from its output
* This might be useful for (duh) printing tables of information, statistics, etc.
void ReportClass_set_table_mode()
char *match_pattern
= tf_getcstringp(1);
ReportType type
= (ReportType
) tf_getp(2);
int table_mode
= tf_getp(3);
const HashValueListNode
*value
= NULL
;
char prefixed_pattern
[REPORT_MAX_FIELD_WIDTH
];
// Check that the arguments are valid.
if (!is_report_type_valid(type
))
tf_error("ReportClass::set_table_mode: Invalid report type (%d) to ", type
);
strcpy(prefixed_pattern
, ReportClass::get_path_prefix());
strncat(prefixed_pattern
, match_pattern
, REPORT_MAX_FIELD_WIDTH
-strlen(prefixed_pattern
));
regex_status
= regex_match(match_pattern
, "");
tf_error("ReportClass::set_table_mode: Bad regular expression (%s)", match_pattern
);
// Walk through the hash, finding entries which match the regexp.
g_report_hash_table
->start_walk();
while (value
= g_report_hash_table
->next_element()) {
// Attempt to match the stored pathname to the pre-compiled regular expression.
regex_status
= regex_match(NULL
, value
->mip_name
);
// Actually set table mode for the matching instances. We do this silently.
char cycle_str
[REPORT_MAX_FIELD_WIDTH
];
value
->report_class
->set_table_mode(type
, table_mode
, 1); // silently
sprintf(cycle_str
, "%d", g_cycle_count
);
g_internal_reports
.report(RTYP_REPORT_MSG
,
cycle_str
, value
->mip_name
,
"Setting table mode for report type %s to %s",
report_type_to_str(value
->report_type
),
table_mode
? "ON" : "OFF");
tf_warning("ReportClass (Internal): Could not find match for regular expression \"%s\"\n", match_pattern
);
////////////////////////////////////////////////////////////////////////////////
// PLI accessors for ReportClass static variables
* ReportClass_set_global_print_threshold(int print_level, (int locked_by = REPORT_LOCKED_BY_NO_ONE))
void ReportClass_set_global_print_threshold()
PrintLevel pl
= (PrintLevel
) tf_getp(1);
if (!is_print_level_valid(pl
))
tf_error("ReportClass (Internal): Invalid print level %d to ", pl
);
// Lock parameter specified -- pass it along to C++.
ReportClass::set_global_print_threshold(pl
, lock
);
ReportClass::set_global_print_threshold(pl
);
* ReportClass_get_global_print_threshold
void ReportClass_get_global_print_threshold()
tf_putp(0, (int)ReportClass::get_global_print_threshold());
* ReportClass_set_max_error_count()
void ReportClass_set_max_error_count()
ReportClass::set_max_error_count(ec
);
tf_error("ReportClass (Internal): Invalid error count (%d) passed to ", ec
);
* ReportClass_get_max_error_count
void ReportClass_get_max_error_count()
tf_putp(0, ReportClass::get_max_error_count());
* ReportClass_inc_global_error_count()
void ReportClass_inc_global_error_count()
ReportClass::inc_global_error_count();
* ReportClass_get_global_error_count
void ReportClass_get_global_error_count()
tf_putp(0, ReportClass::get_global_error_count());
* ReportClass_inc_global_warning_count()
void ReportClass_inc_global_warning_count()
ReportClass::inc_global_warning_count();
* ReportClass_get_global_warning_count()
void ReportClass_get_global_warning_count()
tf_putp(0, ReportClass::get_global_warning_count());
* ReportClass_set_short_pathnames
void ReportClass_set_short_pathnames()
ReportClass::set_short_pathnames(tf_getp(1));
* ReportClass_get_short_pathnames
void ReportClass_get_short_pathnames()
tf_putp(0, ReportClass::get_short_pathnames());
* ReportClass_set_path_prefix
void ReportClass_set_path_prefix()
ReportClass::set_path_prefix(tf_getcstringp(1));
* ReportClass_get_path_prefix
void ReportClass_get_path_prefix()
// review: How to return a string value via the PLI?
* ReportClass_disable_fatal_errors
void ReportClass_disable_fatal_errors()
ReportClass::disable_fatal_errors(tf_getp(1));
* ReportClass_get_num_remaining_nonfatal_cycles
void ReportClass_get_num_remaining_nonfatal_cycles()
tf_putp(0, ReportClass::get_num_remaining_nonfatal_cycles());
* ReportClass_set_show_simulation_time
void ReportClass_set_show_simulation_time()
ReportClass::set_show_simulation_time(tf_getp(1));
* ReportClass_get_show_simulation_time
void ReportClass_get_show_simulation_time()
tf_putp(0, ReportClass::get_show_simulation_time());
////////////////////////////////////////////////////////////////////////////////
void report_inc_cycle_count()
// Increment the cycle count, which is kept in the PLI as a global.
// If the ReportClass has errors disabled, decrement the its number
// of remaining nonfatal cycles.
if (ReportClass::get_num_remaining_nonfatal_cycles() > 0) {
ReportClass::dec_num_remaining_nonfatal_cycles();
if (g_death_cycle_count
&& (g_cycle_count
>= g_death_cycle_count
)) {
io_printf("Report:(%0d) [PLI] calling for error which occured at cycle %0d\n",
g_cycle_count
, g_cycle_count
- REPORT_DEATH_CYCLE_DELAY
);
* report_set_exit_on_error
void report_set_exit_on_error()
io_printf("Report:() Setting exit on error to %0d\n", x
);
* report_get_exit_on_error
void report_get_exit_on_error()
tf_putp(0, g_exit_on_error
);
////////////////////////////////////////////////////////////////////////////////
* Called at compile time to ensure that each PLI call is made with
* the expected number of arguments. The number of arguments is
* specified in the PLI .tab file as a data parameter to this function.
void check_number_of_args( long expected_args
) {
if( tf_nump() != expected_args
) {
tf_error("(%s) line: %d; wrong number of arguments to pli call. expected %d args",
__FILE__
,__LINE__
,expected_args
);
* This function is called at the end of simulation.
static void do_cleanup(int code
) {
* Called at "elaboration time" as a PLI check function for print_cycles_per_second.
* Places the wall-clock time into a global variable for calculation the CPS of this sim.
void set_start_timestamp()
start_time
= times(&start_tmsbuf
);
* print_cycles_per_second
* Called by report_test_complete() in Vera land to output
* the performance in cycles/sec for this simulation.
void report_print_cycles_per_second()
end_time
= times(&end_tmsbuf
);
// Total CPU Time is sum of user and system CPU time for calling and child processes
elapsed_time_cpu
= (double) ((end_tmsbuf
.tms_utime
- start_tmsbuf
.tms_utime
) + // Calling Process User Time
(end_tmsbuf
.tms_stime
- start_tmsbuf
.tms_stime
) + // Calling Process System Time
(end_tmsbuf
.tms_cutime
- start_tmsbuf
.tms_cutime
) + // Child Processes User Time
(end_tmsbuf
.tms_cstime
- start_tmsbuf
.tms_cstime
)) // Child Processes System Time
/ CLK_TCK
; // Divide by clock ticks per second
// Avoid division by zero, make sure elapsed time is at least 1/CLK_TCK seconds
if (elapsed_time_cpu
<= 0)
elapsed_time_cpu
= 1/CLK_TCK
;
cps_cpu
= g_cycle_count
/elapsed_time_cpu
;
io_printf("Report:: Total CPU Time (seconds): %0.2f\n", elapsed_time_cpu
);
io_printf("Report:: Effective cycles per second: %0.2f\n", cps_cpu
);
io_printf("Report:: Apparent virtual memory size: %d bytes\n", top
);
io_printf("================================================================================\n\n");