blob: 0d5da053f95eb7d619bcd1a2f03633e0281614f4 [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.
// -------------------------------------------------------------
//
import "DPI" function int vmm_xvc_get_next_cmd(output int argc,
output string argv_array[0:99],
output string testfile,
output string linec,
output string errmsg);
import "DPI" function void vmm_xvc_tcl_execute_file(string filename);
typedef class vmm_xvc_stop_on_event;
`include "std_lib/vmm_xvc_event.sv"
`ifdef VCS
(* _vcs_vmm_class = 1 *)
`endif
class vmm_xvc_manager extends xvc_manager;
// Command parsing state variables
local string line;
local string argv[$];
local int linec;
local string testfile;
local string errMsg;
// Logging command state variables
local int iter;
local string pattern;
local bit is_pattern;
local int open_files[string];
local int open_trace_files[string];
// Scenario execution state variables
local vmm_xvc_scenario current_sc;
local vmm_xvc_scenario scenarios[$];
local vmm_xvc_scenario exec_scenarios[$];
/*local*/ vmm_xvc_event events[`VMM_AA_INT];
local vmm_xvc_stop_on_event stop_on[string]; //indexed by fully qualified event name
//of the form <sid>.<sev> or <gev>
local event terminate_ev;
local event terminate_ex;
extern function new(string name = "VMM XVC Manager");
extern task run(string testfile);
extern local function int get_next_cmd();
extern local function bit process_cmd();
extern local function void for_reset(string inst);
extern local function xvc_xactor for_each();
extern local function bit try_verbosity();
extern local function bit try_log();
extern local function bit try_xvctrace();
extern local function bit try_covfile();
extern local function bit try_stoponerror();
extern local function bit try_stoponevent();
extern local function bit try_scenario();
extern local function bit try_mapevent();
extern local function bit try_event();
extern local function bit try_action();
extern local function bit try_interrupt();
extern local function bit try_execute();
extern local function void save_scenario(vmm_xvc_scenario sc);
extern local task execute();
extern local task execute_scenario(vmm_xvc_scenario sc);
extern local task execute_actions(vmm_xvc_scenario sc,
xvc_xactor xvc);
extern /*local*/ function vmm_xvc_scenario lookup_scenario(string name);
extern local task add_stop_on (string ev_fullname,
int count,
int immediate,
string fname,
int linec);
extern local function void terminate_stop_on();
extern local function vmm_xvc_event get_stop_on_event(string ev_str);
extern local function void start_stop_on(ref vmm_xvc_scenario current_sc);
endclass: vmm_xvc_manager
//
// Models the details of the action with associated information
//
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_xvc_action_block;
xvc_action action;
string descr;
xvc_xactor xvc;
vmm_xvc_scenario sc;
vmm_xvc_manager xvc_mgr;
int wait_id; //-1 if there is nothing to wait on
int emitQ[$];
bit is_interrupt;
bit is_interrupt_oneshot;
local event terminate_ev;
extern task emit();
extern task wait_for();
extern function void kill();
extern function void set_emit(ref string emit_ids[$]);
extern function void set_wait(string wait_str);
endclass: vmm_xvc_action_block
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_xvc_stop_on_event;
int count; //no. of occurences to wait before stopping
int immediate; //1 if immediate termination, 0 if wait until scn end
string fname; //fname where cmd was seen
int linec; //lineno. where cmd was seen
extern function new(int count,
int immediate,
string fname,
int linec);
endclass: vmm_xvc_stop_on_event
`ifdef VCS
(* vmm_private_class, _vcs_vmm_class = 1 *)
`endif
class vmm_xvc_scenario;
static `VMM_LOG log = new("vmm_xvc_scenario", "class");
string name;
string descr;
string testfile;
int linec;
vmm_notify notify;
vmm_xvc_event events[`VMM_AA_INT];
vmm_xvc_action_block actionQ[$];
typedef enum {STARTED, DONE} notify_e;
extern function new(string name);
endclass: vmm_xvc_scenario
function vmm_xvc_scenario::new(string name);
this.name = name;
this.notify = new(this.log);
void'(this.notify.configure(STARTED, vmm_notify::ON_OFF));
void'(this.notify.configure(DONE, vmm_notify::ON_OFF));
endfunction: new
function vmm_xvc_manager::new(string name);
super.new(name);
endfunction: new
task vmm_xvc_manager::run(string testfile);
// Start all known xvcs
`foreach (this.xvcQ,i) begin
this.xvcQ[i].start_xactor();
end
// Provide the file to be executed to the C-TCL interpreter
// which pre-processes the file
vmm_xvc_tcl_execute_file(testfile);
// Iterate over every command in the file and process it
begin
while (this.get_next_cmd()) begin
this.process_cmd();
end
end
// Execute only if there are no errors in the testfile
if (this.log.get_message_count(vmm_log::FATAL_SEV + vmm_log::ERROR_SEV,
"/./", "/./") > 0) begin
`vmm_fatal(this.log, "Unable to execute testfile because of errors");
end else
this.execute();
endtask: run
//
// Check if one more command is available.
// Returns 1 when command is available, 0 if completed
//
function int vmm_xvc_manager::get_next_cmd();
string argv_array[0:99];
int argc, i;
string linec_str;
`ifdef VCS2006_06
// Work-around for NYI feature in VCS2006.06
// but IEEE 1800-2009 compliant
this.argv.delete();
`else
`ifdef INCA
this.argv.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.argv = '{};
`endif
`endif
get_next_cmd = vmm_xvc_get_next_cmd(argc,
argv_array,
this.testfile,
linec_str,
this.errMsg);
if (get_next_cmd == 0) return 0;
this.line = "";
this.linec = linec_str.atoi();
for (i = 0; i < argc; i++) begin
this.argv.push_back(argv_array[i]);
this.line = {this.line, argv_array[i], " "};
end
endfunction: get_next_cmd
//
// Process a parsed command and return 1 on success.
//
function bit vmm_xvc_manager::process_cmd();
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::DEBUG_SEV )) begin
void'(this.log.text($psprintf("%s, line %0d:%s",
this.testfile, this.linec, this.line)));
this.log.end_msg();
end
`endif
// is it a known command??
if (this.try_scenario()) return 1;
if (this.try_action()) return 1;
if (this.try_verbosity()) return 1;
if (this.try_log()) return 1;
if (this.try_xvctrace()) return 1;
if (this.try_covfile()) return 1;
if (this.try_stoponerror()) return 1;
if (this.try_stoponevent()) return 1;
if (this.try_event()) return 1;
if (this.try_mapevent()) return 1;
if (this.try_interrupt()) return 1;
if (this.try_execute()) return 1;
`vmm_error(this.log,
$psprintf("%s, line %0d: Unknown command '%s'.",
this.testfile,
this.linec,
this.argv[0])
);
endfunction: process_cmd
function void vmm_xvc_manager::for_reset(string inst);
this.iter = 0;
this.pattern = inst;
if(`vmm_str_match(this.pattern, "^/(.*)/$")) begin
this.pattern = `vmm_str_backref(this.pattern, 0);
this.is_pattern = 1;
end else begin
this.is_pattern = 0;
end
endfunction: for_reset
function xvc_xactor vmm_xvc_manager::for_each();
xvc_xactor xvc;
string name;
string inst;
bit pattern_match;
while (this.iter < xvcQ.size()) begin
xvc = this.xvcQ[this.iter++];
inst = xvc.get_instance();
pattern_match = `vmm_str_match(inst, this.pattern);
if ((this.is_pattern && pattern_match) ||
(!this.is_pattern && (inst == this.pattern))) begin
return xvc;
end
end
for_each = null;
endfunction: for_each
//
// Check if this is a VERBOSITY command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_verbosity();
int severity;
if (this.argv[0].tolower() != "verbosity") begin
return 0;
end
try_verbosity = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
begin
string level = this.argv[this.argv.size-1].tolower;
if (level == "error")
severity = vmm_log::ERROR_SEV;
else if (level == "warning")
severity = vmm_log::WARNING_SEV;
else if (level == "normal")
severity = vmm_log::NORMAL_SEV;
else if (level == "trace")
severity = vmm_log::TRACE_SEV;
else if (level == "debug")
severity = vmm_log::DEBUG_SEV;
else if (level == "verbose")
severity = vmm_log::VERBOSE_SEV;
else begin
`vmm_fatal(this.log,
$psprintf("%s, line %0d: Invalid severity level \"%s\"",
this.testfile,
this.linec,
level)
);
return 1;
end
end
begin
string inst;
inst = this.argv[1];
if (inst.tolower() == "all")
inst = "/./";
this.log.set_verbosity(severity, "/./", inst, 1);
end
endfunction: try_verbosity
//
// Check if this is a LOG command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_log();
int fp;
int stop;
int append;
if (this.argv[0].tolower() != "log") begin
return 0;
end
try_log = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
//log NONE
if (this.argv.size() == 2) begin
string tmpname;
for (int i = this.open_files.first(tmpname);
i != 0;
i = this.open_files.next(tmpname)) begin
this.log.log_stop(this.open_files[tmpname], "/./", "/./", 1);
end
return 1;
end
begin
string fname;
int fname_len;
append = 0;
fname = this.argv[this.argv.size()-1];
fname_len = fname.len();
// Append or stop?
if (fname.getc(0) == 45) begin // '-'
stop = 1;
fname = fname.substr(1,fname_len-1);
end else begin
stop = 0;
if (fname.getc(0) == 43) begin // '+'
append = 1;
fname = fname.substr(1,fname_len-1);
end
end
// Is the file already open?
if (this.open_files.exists(fname)) begin
fp = this.open_files[fname];
end else begin
if (stop) begin
`vmm_fatal(this.log,
$psprintf("%s, line %0d: Unknown file \"%s\"",
this.testfile,
this.linec,
fname)
);
return 1;
end
fp = $fopen(fname, (append)? "a" : "w");
if (!fp) begin
`vmm_fatal(this.log,
$psprintf("%s, line %0d: Unable to open file \"%s\" for %s",
this.testfile,
this.linec,
fname,
(append) ? "appending" : "writing")
);
return 1;
end
this.open_files[fname] = fp;
end
end
begin
string inst;
inst = this.argv[1];
if (inst.tolower() == "all")
inst = "/./";
if (stop) begin
this.log.log_stop(fp, "/./", inst, 1);
end else begin
this.log.log_start(fp, "/./", inst, 1);
end
end
endfunction: try_log
//
// Check if this is a XVC_TRACE command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_xvctrace();
int fp;
int stop;
int append;
if (this.argv[0].tolower() != "xvctrace") begin
return 0;
end
try_xvctrace = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
//xvctrace NONE
if (this.argv.size() == 2) begin
string tmpname;
for (int i = this.open_trace_files.first(tmpname);
i != 0;
i = this.open_trace_files.next(tmpname)) begin
this.trace.log_stop(this.open_trace_files[tmpname], "/./", "/./", 1);
end
return 1;
end
begin
int fname_len;
string fname;
append = 0;
fname = this.argv[this.argv.size()-1];
fname_len = fname.len();
// Append or stop?
if (fname.getc(0) == 45) begin // '-'
stop = 1;
fname = fname.substr(1,fname_len-1);
end else begin
stop = 0;
if (fname.getc(0) == 43) begin // '+'
append = 1;
fname = fname.substr(1,fname_len-1);
end
end
// Is the file already open?
if (this.open_files.exists(fname)) begin
fp = this.open_trace_files[fname];
end else begin
if (stop) begin
`vmm_fatal(this.log,
$psprintf("%s, line %0d: Unknown file \"%s\"",
this.testfile,
this.linec,
fname)
);
return 1;
end
fp = $fopen(fname, (append)? "a" : "w");
if (!fp) begin
`vmm_fatal(this.log,
$psprintf("%s, line %0d: Unable to open file \"%s\" for %s",
this.testfile,
this.linec,
fname,
(append) ? "appending" : "writing")
);
return 1;
end
this.open_trace_files[fname] = fp;
end
end
begin
string inst;
string name;
inst = this.argv[1];
name = "/./";
if (inst.tolower() == "all") begin
inst = "/./";
end
if (inst.tolower() == "manager") begin
inst = "";
name = "";
end
if (stop) begin
this.trace.log_stop(fp, name, inst, 1);
end else begin
this.trace.log_start(fp, name, inst, 1);
end
end
endfunction: try_xvctrace
//
// Check if this is a COVFILE command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_covfile();
if (this.argv[0].tolower() != "covfile") begin
return 0;
end
try_covfile = 1;
if (this.argv[1].tolower() == "none") begin
`vmm_warning(this.log,
$psprintf("%s, line %0d: NYI: Turning coverage OFF... NYI: ignoring",
this.testfile,
this.linec)
);
return 1;
end
$set_coverage_db_name(this.argv[1]);
endfunction: try_covfile
//
// Check if this is a STOPONERROR command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_stoponerror();
int n;
if (this.argv[0].tolower() != "stoponerror") begin
return 0;
end
try_stoponerror = 1;
n = this.argv[1].atoi();
if (n <= 0) begin
`vmm_fatal(this.log,
$psprintf("%s, line %0d: Invalid number of error messages",
this.testfile,
this.linec)
);
return 1;
end
this.log.stop_after_n_errors(n);
endfunction: try_stoponerror
//
// Check if this is a STOPONEVENT command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_stoponevent();
int arg_no;
int count;
int immediate;
string ev_fullname;
if (this.argv[0].tolower() != "stoponevent") begin
return 0;
end
try_stoponevent = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
ev_fullname = this.argv[1];
arg_no = 2;
//get the type of stop: IMMEDIATE or GRACEFUL
if ((this.argv[arg_no].tolower() == "immediate") ||
(this.argv[arg_no].tolower() == "graceful")) begin
immediate = (this.argv[arg_no].tolower() == "immediate");
arg_no++;
end else begin
immediate = 1;
end
count = 1;
if (arg_no < this.argv.size()) begin
count = this.argv[arg_no].atoi();
if (count < 0) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: stoponerror count [%0d] < 0 ",
this.testfile,
this.linec,
count )
);
count = 1; //reset count to 1
end
end
//add to stopwatch
this.add_stop_on(ev_fullname, count, immediate, this.testfile, this.linec);
endfunction: try_stoponevent
//
// Check if this is a SCENARIO command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_scenario();
if (this.argv[0].tolower() != "scenario") begin
return 0;
end
try_scenario = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
if (this.current_sc != null) begin
this.save_scenario(this.current_sc);
this.current_sc = null;
end
this.current_sc = this.lookup_scenario(this.argv[1]);
if (this.current_sc != null) begin
`vmm_fatal(this.log,
$psprintf("%s, line %0d: Scenario \"%s\" already defined at %s, line %0d",
this.testfile,
this.linec,
this.argv[1],
this.current_sc.testfile,
this.current_sc.linec)
);
this.current_sc = null;
return 1;
end
this.current_sc = new(this.argv[1]);
if (this.argv.size() == 3)
this.current_sc.descr = this.argv[2];
else
$sformat(this.current_sc.descr,
"Unnamed Scenario:%s[%0d]",
this.testfile,
this.linec);
this.current_sc.testfile = this.testfile;
this.current_sc.linec = this.linec;
endfunction: try_scenario
//
// Check if this is a MAPEVENT command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_mapevent();
vmm_xvc_event_mapped ev;
string event_id;
string event_descr;
string event_map;
string inst; //xvc_instance
string local_id; //event in the xvc_instance
int event_global;
int event_oneshot;
int arg_no;
if (this.argv[0].tolower() != "mapevent") begin
return 0;
end
try_mapevent = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
//default settings for the event
event_global = 0;
event_oneshot = 0;
arg_no = 1;
if (this.argv[arg_no].tolower() == "oneshot") begin
event_oneshot = 1;
arg_no++;
end
if (this.argv[arg_no].tolower() == "global") begin
event_global = 1;
arg_no++;
end else if (this.argv[arg_no].tolower() == "local") begin
arg_no++;
end else if (this.current_sc == null) begin
event_global = 1;
end
event_id = this.argv[arg_no++];
//Check on event_id be positive
if (event_id.atoi() < 0) begin
`vmm_fatal(this.log,
$psprintf("%s[%0d] Event id should be > 0",
this.testfile,
this.linec)
);
end
//next arg should be an 'IS' command : need not enforce it?
arg_no++;
//next arg is the xvc instance string
inst = this.argv[arg_no++];
//next arg is the "E[VENT]" command, need not enforce it ?
arg_no++;
//next arg is the local event id.
local_id = this.argv[arg_no++];
//Check on local_id be positive
if (local_id.atoi() < 0) begin
`vmm_fatal(this.log,
$psprintf("%s[%0d] Event id should be > 0",
this.testfile,
this.linec)
);
end
//next arg is the optional event description
if (arg_no < this.argv.size())
event_descr = this.argv[arg_no];
else
$sformat(event_descr,
"Indicating %s event %s",
((this.current_sc == null) || (event_global)) ? "global" : "scenario",
event_id);
//More checks: doesnt make sense having a local event
// if the current scenario is null
if (!event_global) begin
if (this.current_sc == null) begin
`vmm_fatal(this.log,
$psprintf("%s[%0d] Local event declared before scenario declaration",
this.testfile,
this.linec)
);
return 1;
end
end
ev = new(event_id.atoi(),
event_descr,
event_global,
event_oneshot,
event_global ? this.log : this.current_sc.log,
event_global ? this.notify : this.current_sc.notify,
event_global ? null : this.current_sc,
this
);
// Identify all target XVCs
begin
xvc_xactor xvc;
this.for_reset(inst);
for (xvc = this.for_each(); xvc != null; xvc = this.for_each()) begin
vmm_xvc_event_local local_ev = new(xvc, local_id.atoi());
ev.xvcQ.push_back(local_ev);
end
if (ev.xvcQ.size() == 0) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: No such XVC %s",
this.testfile, this.linec,
inst)
);
return 1;
end
end
//Add to either global or current scenario assoc array
if (event_global) begin
if (this.events.exists(ev.id)) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Global event %0d already exists...",
this.testfile,
this.linec,
ev.id)
);
return 1;
end else
this.events[ev.id] = ev;
end else begin //scenario event
if (this.current_sc.events.exists(ev.id)) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Scenario-level event %0d already exists in scenario %s started at %s, line %0d...",
this.testfile,
this.linec,
ev.id,
this.current_sc.name,
this.current_sc.testfile,
this.current_sc.linec)
);
return 1;
end else
this.current_sc.events[ev.id] = ev;
end
endfunction: try_mapevent
//
// Check if this is a EVENT command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_event();
vmm_xvc_event_any_all ev;
string event_id;
string event_descr;
string event_map;
int event_global;
int event_oneshot;
int arg_no;
if (this.argv[0].tolower() != "event") begin
return 0;
end
try_event = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
//default settings for the event
event_global = 0;
event_oneshot = 0;
arg_no = 1;
if (this.argv[arg_no].tolower() == "oneshot") begin
event_oneshot = 1;
arg_no++;
end
if (this.argv[arg_no].tolower() == "global") begin
event_global = 1;
arg_no++;
end else if (this.argv[arg_no].tolower() == "local") begin
arg_no++;
end else if (this.current_sc == null) begin
event_global = 1;
end
event_id = this.argv[arg_no++];
//Check on event_id be positive
if (event_id.atoi() < 0) begin
`vmm_fatal(this.log,
$psprintf("%s[%0d] Event id should be > 0",
this.testfile,
this.linec)
);
end
//next arg should be an 'IS' command : need not enforce it?
arg_no++;
//next arg is the event_map string i.,e 2,3.1+4 etc.,
// Assumed that the parser takes care of removing whitespace
event_map = this.argv[arg_no++];
//Event description
if (arg_no < this.argv.size())
event_descr = this.argv[arg_no];
else
$sformat(event_descr,
"Indicating %s event %s",
((this.current_sc == null) || (event_global)) ? "global" : "scenario",
event_id);
//More checks: doesnt make sense having a local event
// if the current scenario is null
if (!event_global) begin
if (this.current_sc == null) begin
`vmm_fatal(this.log,
$psprintf("%s[%0d] Local event declared before scenario declaration",
this.testfile,
this.linec)
);
return 1;
end
end
ev = new(event_id.atoi(),
event_descr,
event_map,
event_global,
event_oneshot,
event_global ? this.log : this.current_sc.log,
event_global ? this.notify : this.current_sc.notify,
event_global ? null: this.current_sc,
this
);
//Add to either global or current scenario assoc array
if (event_global) begin
if (this.events.exists(ev.id)) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Global event %0d already exists...",
this.testfile,
this.linec,
ev.id)
);
return 1;
end else
this.events[ev.id] = ev;
end else begin //scenario event
if (this.current_sc.events.exists(ev.id)) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Scenario-level event %0d already exists in scenario %s started at %s, line %0d...",
this.testfile,
this.linec,
ev.id,
this.current_sc.name,
this.current_sc.testfile,
this.current_sc.linec)
);
return 1;
end else
this.current_sc.events[ev.id] = ev;
end
endfunction: try_event
//
// Check if this is a ACTION command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_action();
string inst;
string action_str;
string wait_for_ev;
string emit_ev_list[$];
int arg_no;
if (this.argv[0].tolower() != "action") begin
return 0;
end
try_action = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
//parse cmd and extract information.
//syntax is of the form:
// A[CTION] <instance> <action> \
// [W[AIT] wait_for] \
// [E[VENT] event [event]]
arg_no = 1;
inst = this.argv[arg_no++];
action_str = this.argv[arg_no++];
while (1) begin
string tmpstr;
//if there are args still left over..
if (arg_no >= this.argv.size())
break;
tmpstr = this.argv[arg_no].tolower;
if (`vmm_str_match(tmpstr, "^(w|wait)")) begin
arg_no++;
//ToDo: error if no wait argument
wait_for_ev = this.argv[arg_no++];
continue;
end
tmpstr = this.argv[arg_no].tolower;
if (`vmm_str_match(tmpstr, "^(e|event)")) begin
arg_no++;
//ToDo: error if dont have atleast 1 arg
while(`vmm_str_match(this.argv[arg_no], "^[0-9.]*$")) begin
emit_ev_list.push_back(this.argv[arg_no]);
arg_no++;
if (arg_no >= this.argv.size())
break;
end
end
end
//Iterate over xactor instances
begin
xvc_xactor xvc;
xvc_action action;
this.for_reset(inst);
xvc = this.for_each();
if (xvc == null) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: No such XVC %s",
this.testfile,
this.linec,
inst)
);
return 1;
end
// Actions can only apply to one XVC
if (this.for_each() != null) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Action matches multiple XVCs %s",
this.testfile,
this.linec,
inst)
);
return 1;
end
//There must be a current scenario for an action/interrupt
if (this.current_sc == null) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Action not preceded by scenario",
this.testfile,
this.linec)
);
return 1;
end
//Split up string , remove white spaces
// and pass it as a argv array to the xvc.parse()
begin
string action_str_args[];
this.split(action_str, action_str_args);
action = xvc.parse(action_str_args);
if (action == null) return 1;
end
//create the action and put it in the Q.
begin
vmm_xvc_action_block act_b = new;
act_b.descr = action_str;
act_b.xvc_mgr = this;
act_b.action = action;
act_b.xvc = xvc;
act_b.sc = this.current_sc;
act_b.set_wait(wait_for_ev);
act_b.set_emit(emit_ev_list);
this.current_sc.actionQ.push_back(act_b);
end
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::DEBUG_SEV )) begin
void'(this.log.text(
$psprintf("%s, line %0d: Adding action %s to scenario %s...",
this.testfile,
this.linec,
action_str,
this.current_sc.name)));
this.log.end_msg();
end
`endif
end
endfunction: try_action
//
// Check if this is a INTERRUPT command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_interrupt();
string inst;
string action_str;
string wait_for_ev;
string emit_ev_list[$];
bit is_oneshot;
int arg_no;
if (this.argv[0].tolower() != "interrupt") begin
return 0;
end
try_interrupt = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
//parse cmd and extract information.
//syntax is of the form:
// I[NTERRUPT] [ONESHOT] <instance> <action> \
// [W[AIT] wait_for] \
// [E[VENT] event [event]]
arg_no = 1;
if (this.argv[arg_no].tolower() == "oneshot") begin
is_oneshot = 1;
arg_no++;
end else begin
is_oneshot = 0;
end
inst = this.argv[arg_no++];
action_str = this.argv[arg_no++];
while (1) begin
string tmpstr;
//if there are args still left over..
if (arg_no >= this.argv.size())
break;
tmpstr = this.argv[arg_no].tolower;
if(`vmm_str_match(tmpstr, "^(w|wait)")) begin
arg_no++;
//ToDo: error if no wait argument
wait_for_ev = this.argv[arg_no++];
continue;
end
tmpstr = this.argv[arg_no].tolower;
if (`vmm_str_match(tmpstr, "^(e|event)")) begin
arg_no++;
//ToDo: error if dont have atleast 1 arg
while (`vmm_str_match(this.argv[arg_no], "^[0-9.]*$")) begin
emit_ev_list.push_back(this.argv[arg_no]);
arg_no++;
if (arg_no >= this.argv.size())
break;
end
end
end
//Iterate over xactor instances
begin
xvc_xactor xvc;
xvc_action action;
this.for_reset(inst);
xvc = this.for_each();
if (xvc == null) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: No such XVC %s",
this.testfile,
this.linec,
inst)
);
return 1;
end
// Actions can only apply to one XVC
if (this.for_each() != null) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Action matches multiple XVCs %s",
this.testfile,
this.linec,
inst)
);
return 1;
end
//There must be a current scenario for an action/interrupt
if (this.current_sc == null) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Action not preceded by scenario",
this.testfile,
this.linec)
);
return 1;
end
//Split up string , remove extra white spaces
// and pass it as a argv array to the xvc.parse()
begin
string action_str_args[];
this.split(action_str, action_str_args);
action = xvc.parse(action_str_args);
if (action == null) return 1;
end
//create the action and put it in the Q.
begin
vmm_xvc_action_block act_b = new;
act_b.xvc_mgr = this;
act_b.descr = action_str;
act_b.action = action;
act_b.xvc = xvc;
act_b.sc = this.current_sc;
act_b.is_interrupt = 1;
act_b.is_interrupt_oneshot = is_oneshot;
act_b.set_wait(wait_for_ev);
act_b.set_emit(emit_ev_list);
this.current_sc.actionQ.push_back(act_b);
end
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::DEBUG_SEV )) begin
void'(this.log.text(
$psprintf("%s, line %0d: Adding interrupt action %s to scenario %s...",
this.testfile,
this.linec,
action_str,
this.current_sc.name)));
this.log.end_msg();
end
`endif
end
endfunction: try_interrupt
//
// Check if this is a EXECUTE command.
// Returns 1 if it is, 0 otherwise.
//
function bit vmm_xvc_manager::try_execute();
if (this.argv[0].tolower() != "execute") begin
return 0;
end
try_execute = 1;
//put out any messages caught in the parser
if (this.errMsg.len() > 0) begin
`vmm_fatal(this.log, this.errMsg);
return 1;
end
// Terminate any scenario being defined
if (this.current_sc != null) begin
this.save_scenario(this.current_sc);
this.current_sc = null;
end
//Put each of the scenario arguments in the list
// of scenarios to be executed.
begin
vmm_xvc_scenario scenario;
for (int i = 1; i < this.argv.size(); i++) begin
//check if its in the defined scenario q
scenario = this.lookup_scenario(this.argv[i]);
if (scenario == null) begin
`vmm_error(this.log,
$psprintf("%s, line %0d: Scenario %s has not been defined !",
this.testfile,
this.linec,
this.argv[i])
);
continue;
end
this.exec_scenarios.push_back(scenario);
end
end
endfunction: try_execute
function void vmm_xvc_manager::save_scenario(vmm_xvc_scenario sc);
//Scenario has to be unique, else its an error.
`foreach (this.scenarios,i) begin
if (this.scenarios[i].name == sc.name) begin
`vmm_error(this.log,
$psprintf("Scenario %s started at %s, line %0d already exists !",
this.current_sc.name,
this.current_sc.testfile,
this.current_sc.linec)
);
break;
end
end
if(sc == null) begin
`vmm_error(this.log,"Trying to save null reference to scenarios");
return;
end
this.scenarios.push_back(sc);
endfunction: save_scenario
// Actually perform the "EXECUTE" commands, in order
task vmm_xvc_manager::execute();
//Initialize all event monitors in scenario space
`foreach (this.exec_scenarios,i) begin
int index;
for (int j = this.exec_scenarios[i].events.first(index);
j != 0;
j = this.exec_scenarios[i].events.next(index))
this.exec_scenarios[i].events[index].init();
end
//Initialize and schedule all the global events
// i.,e watching for other event occurences.
//also start the stoponevent watchers
begin
int index;
for (int i = this.events.first(index);
i != 0;
i = this.events.next(index)) begin
this.events[index].init();
this.events[index].start();
end
end
//initialize the 'stop-on-event' watch
this.start_stop_on( this.current_sc ); //ref vmm_xvc_scenario argument
fork
begin
//Execute scenarios
`foreach (this.exec_scenarios,i) begin
this.current_sc = this.exec_scenarios[i];
this.execute_scenario(current_sc);
end
//Terminate Stopwatch on all events
this.terminate_stop_on();
//Terminate Global events
begin
int index;
for (int i = this.events.first(index);
i != 0;
i = this.events.next(index)) begin
this.events[index].kill();
end
end
disable execute;
end
join_none
// Handle forced termination
@(this.terminate_ex);
disable execute;
endtask: execute
//
// Execute an entire scenario, returning when the entire scenario
// has been executed
//
task vmm_xvc_manager::execute_scenario(vmm_xvc_scenario sc);
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::TRACE_SEV )) begin
void'(this.log.text($psprintf("Executing scenario %s started at %s, line %0d...",
sc.name,
sc.testfile,
sc.linec)));
this.log.end_msg();
end
sc.notify.indicate(vmm_xvc_scenario::STARTED);
// Schedule scenario-level events
// Also start stoponevent watches on scenario events
begin
int index;
for (int i = sc.events.first(index);
i != 0;
i = sc.events.next(index)) begin
sc.events[index].start();
end
end
// First, schedule all interrupt actions
`foreach (sc.actionQ,i) begin
xvc_action act;
xvc_xactor xvc;
vmm_xvc_action_block act_b = sc.actionQ[i];
if (!act_b.is_interrupt) continue;
act = act_b.action;
xvc = act_b.xvc;
if (act_b.wait_id < 0) begin
// Execute interrupt action immediately
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::DEBUG_SEV )) begin
void'(this.log.text(
$psprintf("Requesting interrupt action %s on XVC %s(%s)...",
act.get_name(),
xvc.get_name(),
xvc.get_instance())));
this.log.end_msg();
end
`endif
xvc.interrupt_chan.sneak(act);
//Emit events
act_b.emit();
continue;
end
// Schedule the interrupt action when the trigger occurs
fork
begin
fork
begin
while (1) begin
//wait for the "waited_on event" to occur
act_b.wait_for();
// Execute interrupt action immediately
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::DEBUG_SEV )) begin
void'(this.log.text( $psprintf("Requesting interrupt action %s on XVC %s(%s)...",
act.get_name(),
xvc.get_name(),
xvc.get_instance())));
this.log.end_msg();
end
`endif
xvc.interrupt_chan.sneak(act);
//Emit events
act_b.emit();
//if a one-shot event, done
if (act_b.is_interrupt_oneshot)
break;
end //while (1)
end
begin
//terminate interrupts at the end of the scenario
sc.notify.wait_for(vmm_xvc_scenario::DONE);
end
join_any
disable fork;
end
join_none
end //foreach
begin
// Dispatch regular actions to each XVC, as fast as possible
`foreach (this.xvcQ,i) begin
automatic int j = i;
fork
this.execute_actions(sc, this.xvcQ[j]);
join_none
end
// Wait until the all actions are completed
wait fork;
end
// Unschedule scenario-level events
begin
int index;
for (int i = sc.events.first(index);
i != 0;
i = sc.events.next(index)) begin
sc.events[index].kill();
end
end
// Unschedule interrupt actions:
// Automatically done by the interrupt threads..
// on seeing DONE notification
sc.notify.indicate(vmm_xvc_scenario::DONE);
endtask: execute_scenario
task vmm_xvc_manager::execute_actions(vmm_xvc_scenario sc,
xvc_xactor xvc);
xvc_action act;
vmm_xvc_action_block act_b;
`foreach (sc.actionQ,i) begin
act_b = sc.actionQ[i];
if ((act_b.is_interrupt) || //skip interrupts
(act_b.xvc != xvc)) // This action is not for me
continue; // This action is not for me
act = act_b.action;
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::DEBUG_SEV )) begin
void'(this.log.text( $psprintf("Executing action '%s' on XVC %s(%s)...",
act_b.descr,
xvc.get_name(),
xvc.get_instance())));
this.log.end_msg();
end
`endif
//wait for the specified event to occur
act_b.wait_for();
xvc.action_chan.put(act);
//emit the appropriate events when the action completes
act_b.emit();
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP , vmm_log::DEBUG_SEV )) begin
void'(this.log.text($psprintf("Completed action '%s' on XVC %s(%s)...",
act_b.descr,
xvc.get_name(),
xvc.get_instance())));
this.log.end_msg();
end
`endif
end
endtask: execute_actions
//emit local notifications in the xactor when action is DONE
task vmm_xvc_action_block::emit();
if (emitQ.size() == 0)
return;
fork
begin
this.action.notify.wait_for(xvc_action::ENDED);
foreach (emitQ[i]) begin
this.xvc.notify.indicate(emitQ[i]);
end
end
join_none
endtask: emit
//wait for event wait to occur
task vmm_xvc_action_block::wait_for();
if (wait_id < 0)
return;
fork
begin
if (this.sc.events.exists(this.wait_id)) begin
//check the scenario events to see if the wait exists
sc.notify.wait_for(this.wait_id);
end else if (this.xvc_mgr.events.exists(this.wait_id)) begin
//else, check the global events
xvc_mgr.notify.wait_for(this.wait_id);
end else
//if its in none of the above, its a warning as
// an undeclared event is being waited on.
`vmm_error(this.sc.log,
$psprintf("Event %0d waited on in action %s(scenario %s) does not exist in either scenario or global space",
this.wait_id,
this.action.get_name(),
this.sc.name)
);
end
begin
@(this.terminate_ev);
end
join_any
disable fork;
endtask: wait_for
function void vmm_xvc_action_block::kill();
->this.terminate_ev;
endfunction: kill
//create the list of local emitted events in the xvc
// from the parsed input string obtained from the
// action or interrupt command
function void vmm_xvc_action_block::set_emit(ref string emit_ids[$]);
`ifdef VCS2006_06
// Work-around for NYI feature in VCS2006.06
// but IEEE 1800-2009 compliant
this.emitQ.delete();
`else
`ifdef INCA
this.emitQ.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.emitQ = '{};
`endif
`endif
if (emit_ids.size() == 0) return;
//emit event may already be configured i.,e exist in the
// list of known xactor local events.
//if it does not, configure a new notification and
// add to the xvc
foreach (emit_ids[i]) begin
int emit_id = emit_ids[i].atoi();
`foreach (this.emitQ,j) begin
if (emit_id == this.emitQ[j]) continue;
end
if (!this.xvc.notify.is_configured(emit_id))
void'(this.xvc.notify.configure(emit_id));
//Add to the emitQ of this action Block
this.emitQ.push_back(emit_id);
end
endfunction: set_emit
//Set the id of the waiting event
function void vmm_xvc_action_block::set_wait(string wait_str);
if (wait_str == "")
this.wait_id = -1;
else
this.wait_id = wait_str.atoi();
endfunction: set_wait
function vmm_xvc_stop_on_event::new(int count,
int immediate,
string fname,
int linec);
this.count = count;
this.immediate = immediate;
this.fname = fname;
this.linec = linec;
endfunction: new
function vmm_xvc_scenario vmm_xvc_manager::lookup_scenario(string name);
lookup_scenario = null;
`foreach(this.scenarios,i) begin
if (this.scenarios[i].name == name) begin
lookup_scenario = this.scenarios[i];
break;
end
end
endfunction: lookup_scenario
task vmm_xvc_manager::add_stop_on(string ev_fullname,
int count,
int immediate,
string fname,
int linec);
//if it already exists, it is overridden
this.stop_on[ev_fullname] = new(count,
immediate,
fname,
linec);
endtask: add_stop_on
//
// Terminate all watching threads
//
function void vmm_xvc_manager::terminate_stop_on();
->this.terminate_ev;
endfunction: terminate_stop_on
//
// Return an vmm_xvc_event, given the string representing the event
// The string is of the form gev or <sid>.<sev>
//
function vmm_xvc_event vmm_xvc_manager::get_stop_on_event(string ev_str);
string tmpstr;
int ev_id;
get_stop_on_event = null;
if (`vmm_str_match(ev_str, "[.]")) begin
//if matches <sid.sev>: get sid and sev
string event_info[2];
vmm_xvc_scenario scn;
event_info[0] = `vmm_str_prematch(ev_str);
event_info[1] = `vmm_str_postmatch(ev_str);
//lookup scenario event_info[0]
scn = this.lookup_scenario(event_info[0]);
//fatal if scn is null, continue
if (scn == null) begin
`vmm_fatal(this.log,
$psprintf("Stoponevent: Scenario %s does not exist",
event_info[0])
);
end else begin
//Get a handle to the sid.sev event
ev_id = event_info[1].atoi();
if (!scn.events.exists(ev_id)) begin
`vmm_fatal(this.log,
$psprintf("Stoponevent: Scenario %s does not have any event %0d",
event_info[0],
ev_id
)
);
end else begin
return scn.events[ev_id];
end
end
end else begin
//else get gev
ev_id = ev_str.atoi();
if (!this.events.exists(ev_id)) begin
`vmm_fatal(this.log,
$psprintf("Stoponevent: Xvc_manager does not have any event %0d",
ev_id
)
);
end else begin
return this.events[ev_id];
end
end
endfunction: get_stop_on_event
function void vmm_xvc_manager::start_stop_on(ref vmm_xvc_scenario current_sc);
//for each event in the list of watched events,
//start a thread which waits for the event to
//be notified, with the appropriate no. of occurences
//if the stopwatch associated with the event has the
// immediate attribute, it is stopped immediately,
// else it waits until the current scenario is complete
string index;
vmm_xvc_stop_on_event stop;
vmm_xvc_event xvc_ev;
for (int i = this.stop_on.first(index); i != 0; i = this.stop_on.next(index)) begin
stop = this.stop_on[index];
xvc_ev = this.get_stop_on_event(index);
fork
begin
fork
begin
automatic int count = stop.count;
automatic string local_index = index;
//wait for counter to expire
while (count--) begin
xvc_ev.wait_for();
end
//if graceful termination, wait for DONE
if (!stop.immediate) begin
if (current_sc != null) begin
current_sc.notify.wait_for(vmm_xvc_scenario::DONE);
end
end
// Signal the manager to terminate execution
-> this.terminate_ex;
end
begin
@(this.terminate_ev);
end
join_any
disable fork;
end
join_none
end
endfunction: start_stop_on