blob: d76104dc28625b9d9658d03496afe2530000521c [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_notification_config;
event the_event;
bit the_event_bit; //for ON_OFF_TRIGGERING (1:ON, 0:OFF)
time stamp;
int n_waiting_for;
event reset;
event abort;
vmm_notification watch;
int unsigned trig_mode;
vmm_data status;
vmm_notify_callbacks cbs[$];
`ifdef VMM_SB_DS_IN_STDLIB
vmm_sb_ds_registration _vmm_sb_ds[$];
`endif
endclass
function vmm_notify::new(vmm_log log);
`ifdef VMM_NOTIFY_BASE_NEW_CALL
super.new(`VMM_NOTIFY_BASE_NEW_CALL);
`endif
this.log = log;
endfunction
function void vmm_notify::display(string prefix = "");
$write(this.psdisplay(prefix), "\n");
endfunction
function string vmm_notify::psdisplay(string prefix = "");
int i, ok;
$sformat(psdisplay, "%sConfigured%s: [",
prefix, (log == null) ? "" : `vmm_sformatf(" in %s(%s)",
log.get_name(),
log.get_instance()));
for (ok = this.configs.first(i);
ok;
ok = this.configs.next(i)) begin
$sformat(psdisplay, "%0s %0d", psdisplay, i);
end
psdisplay = {psdisplay, "]"};
endfunction: psdisplay
function vmm_notify vmm_notify::copy(vmm_notify to = null);
int i, ok;
if (to == null) to = new(this.log);
to.last_notification_id = this.last_notification_id;
for (ok = this.configs.first(i); ok;
ok = this.configs.next(i)) begin
vmm_notification_config cfg = new;
cfg.trig_mode = this.configs[i].trig_mode;
cfg.stamp = 0;
cfg.status = null;
cfg.n_waiting_for = 0;
cfg.watch = null;
to.configs[i] = cfg;
end
return to;
endfunction : copy
function int vmm_notify::configure(int notification_id = -1,
sync_e sync = ONE_SHOT);
// Warn if an event is being re-configured
if (this.configs.exists(notification_id))
begin
if (log == null) begin
$write("vmm_notify::WARNING: Reconfiguring notification #%0d...\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::WARNING_SEV))
begin
string txt;
$sformat(txt, "Reconfiguring notification #%0d...", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
end
else if (notification_id > 1_000_000)
begin
// New notification ID > 1,000,000 are reserved
if (log == null) begin
$write("vmm_notify::FATAL: Notification notification IDs > 1,000,000 are reserved\n");
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
void'(this.log.text("Notification notification IDs > 1,000,000 are reserved"));
this.log.end_msg();
end
return -1;
end
// Automatically generate a unique notification ID if notification ID
// is not positive.
if (notification_id < 0)
begin
last_notification_id++;
notification_id = last_notification_id;
end
configure = notification_id;
`ifdef VMM_DETAILED_MSGS
if (this.log == null) begin
$write("vmm_notify::DEBUG: Configuring notification notification #%0d...\n", notification_id);
end
else if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV)) begin
string txt;
$sformat(txt, "Configuring notification notification #%0d...", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
`endif
begin
vmm_notification_config cfg = new;
cfg.trig_mode = sync;
cfg.stamp = 0;
cfg.status = null;
cfg.n_waiting_for = 0;
cfg.the_event_bit = 0;
this.configs[notification_id] = cfg;
end
endfunction: configure
function int vmm_notify::is_configured(int notification_id);
if (!this.configs.exists(notification_id)) return 0;
is_configured = this.configs[notification_id].trig_mode;
endfunction: is_configured
function bit vmm_notify::is_on(int notification_id);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Checking undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Checking undefined notification #%0d", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return 0;
end
cfg = this.configs[notification_id];
if (cfg.trig_mode != ON_OFF) begin
if (this.log == null) begin
$write("vmm_notify::WARNING: Cannot check non-ON_OFF notification #%0d\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::WARNING_SEV)) begin
string txt;
$sformat(txt, "Cannot check non-ON_OFF notification #%0d", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return 0;
end
is_on = cfg.the_event_bit;
endfunction: is_on
task vmm_notify::wait_for(int notification_id);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL Waiting for undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Waiting for undefined notification #%0d", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
cfg = this.configs[notification_id];
cfg.n_waiting_for++;
case(cfg.trig_mode)
ON_OFF : while (cfg.the_event_bit !== 1) begin
@(cfg.the_event);
end
ONE_SHOT : @(cfg.the_event);
BLAST : wait (cfg.the_event.triggered);
default : begin
if (this.log == null) begin
$write("Invalid notification type\n");
end else `vmm_fatal(this.log, "Invalid notification type");
end
endcase
if (cfg.n_waiting_for > 0) cfg.n_waiting_for--;
endtask: wait_for
task vmm_notify::wait_for_off(int notification_id);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Waiting for undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Waiting for undefined notification #%0d", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
cfg = this.configs[notification_id];
cfg.n_waiting_for++;
case(cfg.trig_mode)
ON_OFF : while (cfg.the_event_bit !== 0) begin
@(cfg.the_event);
end
default : begin
if (this.log == null) begin
$write("Cannot use vmm_notify::wait_for_off() on non-ON/OFF notification\n");
end else `vmm_fatal(this.log, "Cannot use vmm_notify::wait_for_off() on non-ON/OFF notification");
end
endcase
if (cfg.n_waiting_for > 0) cfg.n_waiting_for--;
endtask: wait_for_off
function bit vmm_notify::is_waited_for(int notification_id);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: is_waited_for() called for undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "vmm_notify::is_waited_for called for undefined notification #%0d",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return 0;
end
cfg = this.configs[notification_id];
is_waited_for = (cfg.n_waiting_for > 0);
endfunction: is_waited_for
function void vmm_notify::terminated(int notification_id);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: terminated() called for undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "vmm_notify::terminated called for undefined notification #%0d",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
cfg = this.configs[notification_id];
if (cfg.n_waiting_for > 0) cfg.n_waiting_for--;
endfunction: terminated
function vmm_data vmm_notify::status(int notification_id);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Requesting status for undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Requesting status for undefined notification #%0d",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return null;
end
cfg = this.configs[notification_id];
status = cfg.status;
endfunction: status
function time vmm_notify::timestamp(int notification_id);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Requesting timestamp for undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Requesting timestamp for undefined notification #%0d",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return 0;
end
cfg = this.configs[notification_id];
timestamp = cfg.stamp;
endfunction: timestamp
function void vmm_notify::indicate(int notification_id,
vmm_data status = null);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Indicating undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Indicating undefined notification #%0d",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
cfg = this.configs[notification_id];
if (cfg.trig_mode == ON_OFF) cfg.the_event_bit = 1;
-> cfg.the_event;
cfg.stamp = $realtime;
cfg.status = status;
`foreach (cfg.cbs,i) begin
cfg.cbs[i].indicated(status);
end
`ifdef VMM_SB_DS_IN_STDLIB
`foreach (cfg._vmm_sb_ds,i) begin
if (cfg._vmm_sb_ds[i].is_in) begin
cfg._vmm_sb_ds[i].sb.insert(status);
end
if (cfg._vmm_sb_ds[i].is_out) begin
case (cfg._vmm_sb_ds[i].order)
vmm_sb_ds::IN_ORDER:
cfg._vmm_sb_ds[i].sb.expect_in_order(status);
vmm_sb_ds::WITH_LOSSES: begin
vmm_data p;
vmm_data losses[];
cfg._vmm_sb_ds[i].sb.expect_with_losses(status, p, losses);
end
vmm_sb_ds::OUT_ORDER:
cfg._vmm_sb_ds[i].sb.expect_out_of_order(status);
endcase
end
end
`endif
endfunction: indicate
function void vmm_notify::set_notification(int notification_id,
vmm_notification ntfy = null);
vmm_notification_config cfg;
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Setting notification on undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Setting notification on undefined notification #%0d", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
cfg = this.configs[notification_id];
// Was there an event before?
if (cfg.watch != null)
begin
// Terminate it
->cfg.abort;
end
if (ntfy == null)
begin
cfg.watch = null;
return;
end
// Watch for the specified event
cfg.watch = ntfy;
fork
begin
fork
while (1)
begin
fork
while (1)
begin
vmm_data status;
ntfy.indicate(status);
this.indicate(notification_id, status);
end
while (cfg.trig_mode == ON_OFF) // persistent?
begin
ntfy.reset();
cfg.the_event_bit = 0;
-> cfg.the_event;
end
join_none
@(cfg.reset);
disable fork;
end
join_none
@(cfg.abort);
disable fork;
end
join_none
endfunction: set_notification
function vmm_notification vmm_notify::get_notification(int notification_id);
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Requesting notification for undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Requesting notification for undefined notification #%0d",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return null;
end
get_notification = this.configs[notification_id].watch;
endfunction: get_notification
function void vmm_notify::reset(int notification_id = -1,
reset_e rst_typ = SOFT);
vmm_notification_config cfg;
if (notification_id < 0)
begin
int i, ok;
for (ok = this.configs.first(i); ok; ok = this.configs.next(i))
begin
cfg = this.configs[i];
if (cfg.trig_mode == ON_OFF) begin
cfg.the_event_bit = 0;
->cfg.the_event;
end
if (cfg.watch != null)
begin
->cfg.reset;
if (rst_typ == HARD)
begin
->cfg.abort;
cfg.watch = null;
end
end
if (rst_typ == HARD)
begin
cfg.stamp = 0;
cfg.status = null;
cfg.n_waiting_for = 0;
end
end
return;
end
if (!this.configs.exists(notification_id))
begin
if (this.log == null) begin
$write("vmm_notify::FATAL: Reseting undefined notification #%0d\n",
notification_id);
$finish();
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
begin
string txt;
$sformat(txt, "Reseting undefined notification #%0d", notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
cfg = this.configs[notification_id];
->cfg.the_event;
cfg.the_event_bit = 0;
if (cfg.watch != null)
begin
->cfg.reset;
if (rst_typ == HARD)
begin
->cfg.abort;
cfg.watch = null;
end
end
if (rst_typ == HARD)
begin
cfg.stamp = 0;
cfg.status = null;
cfg.n_waiting_for = 0;
end
endfunction: reset
function void vmm_notify::append_callback(int notification_id,
vmm_notify_callbacks cbs);
if (!this.configs.exists(notification_id))
begin
if (log == null) begin
$write("vmm_notify::ERROR: Unknown notification #%0d to append callback to.\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV))
begin
string txt;
$sformat(txt, "Unkown notification #%0d to append callback to.",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
// Append new callback
this.configs[notification_id].cbs.push_back(cbs);
endfunction: append_callback
function void vmm_notify::unregister_callback(int notification_id,
vmm_notify_callbacks cbs);
if (!this.configs.exists(notification_id))
begin
if (log == null) begin
$write("vmm_notify::ERROR: Unknown notification #%0d to remove callback from.\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV))
begin
string txt;
$sformat(txt, "Unkown notification #%0d to remove callback from.",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
begin
vmm_notification_config cfg = this.configs[notification_id];
`foreach(cfg.cbs,i) begin
if (cfg.cbs[i] == cbs) begin
// Unregister it
cfg.cbs.delete(i);
return;
end
end
end
if (log == null) begin
$write("vmm_notify::WARNING: Callback was not registered with notification #%0d.\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::WARNING_SEV))
begin
string txt;
$sformat(txt, "Callback was not registered with notification #%0d.",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
endfunction: unregister_callback
`ifdef VMM_SB_DS_IN_STDLIB
function void vmm_notify::register_vmm_sb_ds(int notification_id,
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;
if (!this.configs.exists(notification_id))
begin
if (log == null) begin
$write("vmm_notify::ERROR: Unknown notification #%0d to append callback to.\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV))
begin
string txt;
$sformat(txt, "Unkown notification #%0d to append callback to.",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
if(sb == null)
begin
`vmm_error(this.log, "Trying to register null reference to register_vmm_sb_ds");
return;
end
`foreach (this.configs[notification_id]._vmm_sb_ds,i) begin
if (this.configs[notification_id]._vmm_sb_ds[i].sb == sb) begin
`vmm_warning(this.log, "Data stream scoreboard is already registered");
return;
end
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.configs[notification_id]._vmm_sb_ds.push_back(ds);
endfunction: register_vmm_sb_ds
function void vmm_notify::unregister_vmm_sb_ds(int notification_id,
vmm_sb_ds sb);
if (!this.configs.exists(notification_id))
begin
if (log == null) begin
$write("vmm_notify::ERROR: Unknown notification #%0d to remove callback from.\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV))
begin
string txt;
$sformat(txt, "Unkown notification #%0d to remove callback from.",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
`foreach (this.configs[notification_id]._vmm_sb_ds,i) begin
if (this.configs[notification_id]._vmm_sb_ds[i].sb == sb) begin
this.configs[notification_id]._vmm_sb_ds.delete(i);
return;
end
end
if (log == null) begin
$write("vmm_notify::WARNING: Scoreboard was not registered with notification #%0d.\n",
notification_id);
end
else if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::WARNING_SEV))
begin
string txt;
$sformat(txt, "Scoreboard was not registered with notification #%0d.",
notification_id);
void'(this.log.text(txt));
this.log.end_msg();
end
endfunction: unregister_vmm_sb_ds
`endif