blob: 63d6b9d866024cb6bc83af302b64ea6cd92b7f0d [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.
// -------------------------------------------------------------
//
function vmm_xactor::new(string name,
string inst,
int stream_id = -1
`VMM_XACTOR_BASE_NEW_EXTERN_ARGS);
`ifdef VMM_XACTOR_BASE_NEW_CALL
super.new(`VMM_XACTOR_BASE_NEW_CALL);
`endif
this.log = new(name, inst);
this.notify = new(this.log);
`VMM_OBJECT_SET_PARENT(this.notify, this)
void'(this.notify.configure(XACTOR_IDLE, vmm_notify::ON_OFF));
void'(this.notify.configure(XACTOR_BUSY, vmm_notify::ON_OFF));
void'(this.notify.configure(XACTOR_STARTED));
void'(this.notify.configure(XACTOR_STOPPING, vmm_notify::ON_OFF));
void'(this.notify.configure(XACTOR_IS_STOPPED, vmm_notify::ON_OFF));
void'(this.notify.configure(XACTOR_STOPPED));
void'(this.notify.configure(XACTOR_RESET));
this.is_stopped = 1;
this.notify.indicate(XACTOR_IS_STOPPED);
this.notify.indicate(XACTOR_IDLE);
this.notify.reset(XACTOR_BUSY);
this.stream_id = stream_id;
this.start_it = 0;
this.stop_it = 1;
this.reset_it = 0;
this.n_threads_to_stop = -1;
this.n_threads_stopped = 0;
this.main_running = 0;
// Dummy first entry in list of known transactors
if (this._vmm_available_xactor.size() == 0) begin
this._vmm_available_xactor.push_back(null);
end
this._vmm_available_xactor.push_back(this);
fork
begin
this.save_main_rng_state = 1;
this.restore_main_rng_state = 0;
this.main_rng_state = get_randstate();
while (1) begin
this.main_running = 0;
// wait to start
while (!this.start_it) @(this.control_event);
this.start_it = 0;
this.stop_it = 0;
this.reset_it = 0;
this.n_threads_to_stop = -1;
this.n_threads_stopped = 0;
this.is_stopped = 0;
// We may be held back by on-going reset operation
fork
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Start delayed by on-going reset activity"));
this.log.end_msg();
end
join_none
while (this.reset_pending > 0) begin
`vmm_fatal(this.log, "Pending resets not currently supported");
// `wait(@(this.reset_pending)); // Not supported yet.
end
disable fork;
//
// Fork the main body
//
if (this.save_main_rng_state) begin
this.main_rng_state = get_randstate();
end
if (this.restore_main_rng_state) begin
set_randstate(main_rng_state);
end
this.save_main_rng_state = 0;
this.restore_main_rng_state = 0;
fork
begin
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV )) begin
void'(this.log.text("Started"));
this.log.end_msg();
end
this.is_stopped = 0;
this.notify.reset(XACTOR_IDLE);
this.notify.reset(XACTOR_IS_STOPPED);
this.notify.indicate(XACTOR_BUSY);
this.notify.indicate(XACTOR_STARTED);
main();
end
begin
// Check that super.main() was called in all
// extensions of this class
repeat (10) #0;
if (!this.main_running) begin
`vmm_warning(this.log, "Virtual vmm_xactor::main() does not call super.main()");
end
end
join_none
// Wait to reset
while (!this.reset_it) @(this.control_event);
this.reset_it = 0;
this.n_threads_to_stop = -1;
this.n_threads_stopped = 0;
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Reset"));
this.log.end_msg();
end
this.is_stopped = 1;
this.notify.reset(XACTOR_BUSY);
this.notify.indicate(XACTOR_IDLE);
this.notify.reset(XACTOR_STOPPING);
this.notify.indicate(XACTOR_IS_STOPPED);
this.notify.indicate(XACTOR_STOPPED);
this.notify.indicate(XACTOR_RESET);
disable fork;
end
end
join_none
endfunction: new
function string vmm_xactor::get_name();
get_name = this.log.get_name();
endfunction:get_name
function string vmm_xactor::get_instance();
get_instance = this.log.get_instance();
endfunction: get_instance
function void vmm_xactor::start_xactor();
this.start_it = 1;
this.stop_it = 0;
-> this.control_event;
this.notify.reset(XACTOR_STOPPING);
endfunction: start_xactor
function void vmm_xactor::stop_xactor();
this.start_it = 0;
this.stop_it = 1;
-> this.control_event;
this.notify.indicate(XACTOR_STOPPING);
// Is it already stopped?
if (this.is_stopped) begin
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Already stopped"));
this.log.end_msg();
end
this.notify.indicate(XACTOR_STOPPED);
return;
end
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Stop requested"));
this.log.end_msg();
end
this.check_all_threads_stopped();
endfunction: stop_xactor
function void vmm_xactor::reset_xactor(vmm_xactor::reset_e rst_typ = SOFT_RST);
this.start_it = 0;
this.reset_it = 1;
-> this.control_event;
this.is_stopped = 1;
// Reset notifier & RNG state on FIRM and above
if (rst_typ == FIRM_RST ||
rst_typ == HARD_RST) begin
this.notify.reset(-1, vmm_notify::HARD);
this.restore_rng_state();
end
else begin
this.notify.reset(-1, vmm_notify::SOFT);
end
// Unregister all callbacks on HARD reset
if (rst_typ == HARD_RST) begin
`ifdef VCS2006_06
// Work-around for NYI feature in VCS2006.06
// but IEEE 1800-2009 compliant
this.callbacks.delete();
`else
`ifdef INCA
this.callbacks.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.callbacks = '{};
`endif
`endif
end
endfunction: reset_xactor
function void vmm_xactor::save_rng_state();
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Saving RNG state information..."));
this.log.end_msg();
end
if (!this.is_stopped) begin
`vmm_warning(this.log, "save_rng_state() called while transactor is still running");
end
this.save_main_rng_state = 1;
endfunction: save_rng_state
function void vmm_xactor::restore_rng_state();
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Restoring RNG state information..."));
this.log.end_msg();
end
if (!this.is_stopped) begin
`vmm_warning(this.log, "restore_rng_state() called while transactor is still running");
end
this.restore_main_rng_state = 1;
endfunction: restore_rng_state
function string vmm_xactor::psdisplay(string prefix = "");
$sformat(psdisplay, "%sTransactor %s (%s):", prefix,
this.log.get_name(), this.log.get_instance());
if (this.is_stopped) begin
return {psdisplay, "\n", prefix, "Transactor is STOPPED"};
end
if (this.n_threads_stopped < this.n_threads_to_stop) begin
return `vmm_sformatf("%s\n%sTransactor is STOPPING (%0d out of %0d threads stopped)",
psdisplay, prefix, this.n_threads_stopped,
this.n_threads_to_stop);
end
return {psdisplay, "\n", prefix, "Transactor is RUNNING"};
endfunction: psdisplay
function void vmm_xactor::xactor_status(string prefix = "");
`vmm_note(this.log, this.psdisplay(prefix));
endfunction: xactor_status
task vmm_xactor::main();
this.main_running = 1;
endtask: main
function void vmm_xactor::check_all_threads_stopped();
if (this.n_threads_to_stop > 0) begin
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text($psprintf("%0d out of %0d threads have now stopped",n_threads_stopped,this.n_threads_to_stop)));
this.log.end_msg();
end
if (this.n_threads_stopped >= this.n_threads_to_stop) begin
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Stopped"));
this.log.end_msg();
end
this.is_stopped = 1;
this.notify.reset(XACTOR_BUSY);
this.notify.indicate(XACTOR_IDLE);
this.notify.reset(XACTOR_STOPPING);
this.notify.indicate(XACTOR_IS_STOPPED);
this.notify.indicate(XACTOR_STOPPED);
end
end
endfunction
task vmm_xactor::wait_if_stopped(int unsigned n_threads = 1);
if (n_threads == 0) begin
if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV)) begin
void'(this.log.text("Number of threads to stop specified to vmm_xactor::wait_if_stopped() must be greater than 0"));
this.log.end_msg();
end
n_threads = 1;
end
if (this.n_threads_to_stop <= 0) this.n_threads_to_stop = n_threads;
else if (this.n_threads_to_stop != n_threads) begin
if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV)) begin
void'(this.log.text("All threads must specify the same number of threads to stop to vmm_xactor::wait_if_stopped() and vmm_xactor::wait_if_stopped_or_empty()"));
this.log.end_msg();
end
if (this.n_threads_to_stop < n_threads) this.n_threads_to_stop = n_threads;
end
if (this.stop_it) begin
this.n_threads_stopped++;
this.check_all_threads_stopped();
while (this.stop_it) @(this.control_event);
// Make sure these are done only once if
// there are multiple stopped threads
if (this.is_stopped) begin
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Restarted"));
this.log.end_msg();
end
this.is_stopped = 0;
this.notify.indicate(XACTOR_STARTED);
this.notify.reset(XACTOR_IS_STOPPED);
this.notify.reset(XACTOR_IDLE);
this.notify.indicate(XACTOR_BUSY);
end
this.n_threads_stopped--;
end
endtask: wait_if_stopped
task vmm_xactor::wait_if_stopped_or_empty(vmm_channel chan,
int unsigned n_threads = 1);
if(chan == null) begin
`vmm_error(this.log,"Passed null reference for channel to wait_if_stopped_or_empty");
return;
end
this.wait_if_stopped(n_threads);
while (chan.level() == 0) begin
vmm_data data;
this.n_threads_stopped++;
// If all other threads are blocked, indicate IDLE
// because we are going to block on the channel
if (this.n_threads_stopped >= this.n_threads_to_stop) begin
this.notify.reset(XACTOR_BUSY);
this.notify.indicate(XACTOR_IDLE);
end
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text($psprintf("%0d threads have now stopped or blocked",n_threads_stopped)));
this.log.end_msg();
end
chan.peek(data);
this.n_threads_stopped--;
this.wait_if_stopped(n_threads);
end
this.notify.reset(XACTOR_IDLE);
this.notify.indicate(XACTOR_BUSY);
endtask: wait_if_stopped_or_empty
function void vmm_xactor::prepend_callback(vmm_xactor_callbacks cb);
if (cb == null) begin
`vmm_error(this.log, "Attempting to prepend a NULL callback extension");
return;
end
`foreach(this.callbacks,i) begin
if (this.callbacks[i] == cb) begin
`vmm_warning(this.log, "Callback has already been registered");
return;
end
end
//Prepend new callback
this.callbacks.push_front(cb);
endfunction: prepend_callback
function void vmm_xactor::append_callback(vmm_xactor_callbacks cb);
if (cb == null) begin
`vmm_error(this.log, "Attempting to append a NULL callback extension");
return;
end
`foreach(this.callbacks,i) begin
if (this.callbacks[i] == cb) begin
`vmm_warning(this.log, "Callback has already been registered");
return;
end
end
//Append new callback
this.callbacks.push_back(cb);
endfunction: append_callback
function void vmm_xactor::unregister_callback(vmm_xactor_callbacks cb);
`foreach(this.callbacks,i) begin
if (this.callbacks[i] == cb) begin
// Unregister it
this.callbacks.delete(i);
return;
end
end
`vmm_warning(this.log, "Callback was not registered");
endfunction: unregister_callback
function void vmm_xactor::get_input_channels(ref vmm_channel chans[$]);
chans = this.Xinput_chansX;
endfunction: get_input_channels
function void vmm_xactor::get_output_channels(ref vmm_channel chans[$]);
chans = this.Xoutput_chansX;
endfunction: get_output_channels
function void vmm_xactor::kill();
vmm_channel ins[$] = this.Xinput_chansX;
vmm_channel outs[$] = this.Xoutput_chansX;
`ifdef VCS2006_06
// Work-around for NYI feature in VCS2006.06
// but IEEE 1800-2009 compliant
this.Xinput_chansX.delete();
this.Xoutput_chansX.delete();
`else
`ifdef INCA
this.Xinput_chansX.delete();
this.Xoutput_chansX.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.Xinput_chansX = '{};
this.Xoutput_chansX = '{};
`endif
`endif
foreach(ins[i]) begin
if (ins[i].get_consumer() == this) begin
ins[i].set_consumer(null);
if (ins[i].get_producer() == null) ins[i].kill();
end
end
foreach(outs[i]) begin
if (outs[i].get_producer() == this) begin
outs[i].set_producer(null);
if (outs[i].get_consumer() == null) outs[i].kill();
end
end
`foreach(this._vmm_available_xactor,i) begin
if (this._vmm_available_xactor[i] == this) begin
this._vmm_available_xactor.delete(i);
break;
end
end
this.log.kill();
endfunction: kill
`ifdef VMM_SB_DS_IN_STDLIB
function void vmm_xactor::inp_vmm_sb_ds(vmm_data tr);
`foreach (this._vmm_sb_ds,i) begin
if (this._vmm_sb_ds[i].is_in) begin
this._vmm_sb_ds[i].sb.insert(tr);
end
end
endfunction: inp_vmm_sb_ds
function void vmm_xactor::exp_vmm_sb_ds(vmm_data tr);
`foreach (this._vmm_sb_ds,i) begin
if (this._vmm_sb_ds[i].is_out) begin
case (this._vmm_sb_ds[i].order)
vmm_sb_ds::IN_ORDER:
this._vmm_sb_ds[i].sb.expect_in_order(tr);
vmm_sb_ds::WITH_LOSSES: begin
vmm_data p;
vmm_data losses[];
this._vmm_sb_ds[i].sb.expect_with_losses(tr, p, losses);
end
vmm_sb_ds::OUT_ORDER:
this._vmm_sb_ds[i].sb.expect_out_of_order(tr);
endcase
end
end
endfunction: exp_vmm_sb_ds
function void vmm_xactor::register_vmm_sb_ds(vmm_sb_ds sb,
vmm_sb_ds::kind_e kind,
vmm_sb_ds::ordering_e order = vmm_sb_ds::IN_ORDER);
vmm_sb_ds_registration ds;
`foreach (this._vmm_sb_ds,i) begin
if (this._vmm_sb_ds[i].sb == sb) begin
`vmm_warning(this.log, "Data stream scoreboard is already registered");
return;
end
end
if(sb == null) begin
`vmm_error(this.log,"Trying to register null reference to register_vmm_sb_ds");
return;
end
ds = new;
ds.sb = sb;
ds.is_in = (kind == vmm_sb_ds::INPUT ||
kind == vmm_sb_ds::EITHER);
ds.is_out = (kind == vmm_sb_ds::EXPECT ||
kind == vmm_sb_ds::EITHER);
ds.order = order;
this._vmm_sb_ds.push_back(ds);
endfunction: register_vmm_sb_ds
function void vmm_xactor::unregister_vmm_sb_ds(vmm_sb_ds sb);
`foreach (this._vmm_sb_ds,i) begin
if (this._vmm_sb_ds[i].sb == sb) begin
this._vmm_sb_ds.delete(i);
return;
end
end
`vmm_error(this.log, "Data stream scoreboard is not registered");
endfunction: unregister_vmm_sb_ds
`endif
function string vmm_xactor::do_psdisplay(string prefix = "");
this.__vmm_done_user = 0;
return "";
endfunction
function void vmm_xactor::do_start_xactor();
this.__vmm_done_user = 0;
endfunction
function void vmm_xactor::do_stop_xactor();
this.__vmm_done_user = 0;
endfunction
function void vmm_xactor::do_reset_xactor(vmm_xactor::reset_e rst_typ);
this.__vmm_done_user = 0;
endfunction
function void vmm_xactor::do_kill_xactor();
this.__vmm_done_user = 0;
endfunction
function vmm_xactor_iter::new(string name = "",
string inst = "");
if (name == "") this.name = ".";
else begin
// Remove "/" surrounding the pattern, if any
if (`vmm_str_match(name, "^/(.*)/$")) name = `vmm_str_backref(name, 0);
this.name = name;
end
if (inst == "") this.inst = ".";
else begin
// Remove "/" surrounding the pattern, if any
if (`vmm_str_match(inst, "^/(.*)/$")) inst = `vmm_str_backref(inst, 0);
this.inst = inst;
end
void'(this.first());
endfunction: new
function void vmm_xactor_iter::move_iterator();
string xa_name;
string xa_inst;
`ifdef NO_STATIC_METHODS
int n;
if (_vmm_xactor == null) begin
_vmm_xactor = new("vmm_xactor_iter::_vmm_xactor", "static");
// Make sure it is the first one on the list of known transactors
if (_vmm_xactor._vmm_available_xactor[0] == null) begin
void'(_vmm_xactor._vmm_available_xactor.pop_back());
_vmm_xactor._vmm_available_xactor[0] = _vmm_xactor;
end
end
n = _vmm_xactor._vmm_available_xactor.size();
`else
int n = vmm_xactor::_vmm_available_xactor.size();
`endif
if (this.idx >= n || n <= 1) return;
for (int x = this.idx+1; x < n; x++) begin
`ifdef NO_STATIC_METHODS
xa_name = _vmm_xactor._vmm_available_xactor[x].log.get_name();
xa_inst = _vmm_xactor._vmm_available_xactor[x].log.get_instance();
`else
xa_name = vmm_xactor::_vmm_available_xactor[x].log.get_name();
xa_inst = vmm_xactor::_vmm_available_xactor[x].log.get_instance();
`endif
if (`vmm_str_match(xa_name, this.name) &&
`vmm_str_match(xa_inst, this.inst)) begin
this.idx = x;
return;
end
end
this.idx = 0;
endfunction
function vmm_xactor vmm_xactor_iter::xactor();
`ifdef NO_STATIC_METHODS
if (_vmm_xactor == null) begin
_vmm_xactor = new("vmm_xactor_iter::_vmm_xactor", "static");
end
`endif
if (this.idx <= 0 ||
`ifdef NO_STATIC_METHODS
this.idx >= _vmm_xactor._vmm_available_xactor.size())
`else
this.idx >= vmm_xactor::_vmm_available_xactor.size())
`endif
return null;
`ifdef NO_STATIC_METHODS
return _vmm_xactor._vmm_available_xactor[this.idx];
`else
return vmm_xactor::_vmm_available_xactor[this.idx];
`endif
endfunction
function vmm_xactor vmm_xactor_iter::first();
this.idx = 0;
this.move_iterator();
return this.xactor();
endfunction
function vmm_xactor vmm_xactor_iter::next();
this.move_iterator();
return this.xactor();
endfunction