blob: 8452fec1ff98e6df2f8d4d2472e8ed2bdd4e2af3 [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_broadcast::new(string name,
string inst,
vmm_channel source,
bit use_references=1,
int mode=AFAP
`VMM_XACTOR_NEW_EXTERN_ARGS);
super.new(name, inst, -1 `VMM_XACTOR_NEW_CALL);
if (source == null) begin
if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
void'(this.log.text("Cannot create vmm_broadcast instance with a NULL source channel reference"));
this.log.end_msg();
end
end
this.in_chan = source;
this.log.is_above(this.in_chan.log);
this.dflt_use_refs = use_references;
this.mode = mode;
this.n_out_chans = 0;
endfunction : new
function string vmm_broadcast::psdisplay(string prefix = "");
psdisplay = super.psdisplay(prefix);
$sformat(psdisplay, "%s [Mode: %s]", psdisplay,
(this.mode == ALAP) ? "ALAP" : "AFAP");
$sformat(psdisplay, "%s\n%sInpChan: %s(%s) [level=%0d of %0d]",
psdisplay, prefix, this.in_chan.log.get_name(),
this.in_chan.log.get_instance(), this.in_chan.level(),
this.in_chan.full_level());
`foreach (this.out_chans,i) begin
$sformat(psdisplay, "%s\n%sOutChan[%0d/%s/%s]: %s(%s) [level=%0d of %0d]",
psdisplay, prefix, i, (this.is_on[i]) ? "ON " : "OFF",
(this.use_refs[i]) ? "ref" : "cpy",
this.out_chans[i].log.get_name(),
this.out_chans[i].log.get_instance(), this.out_chans[i].level(),
this.out_chans[i].full_level());
end
return psdisplay;
endfunction
task vmm_broadcast::broadcast_mode(bcast_mode_e mode);
if (mode != AFAP && mode != ALAP) begin
if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
void'(this.log.text(`vmm_sformatf("Invalid broadcast mode %0d", mode)));
this.log.end_msg();
end
return;
end
this.mode = mode;
// This MAY create the opportunity for a new broadcast cycle...
-> this.new_cycle;
endtask : broadcast_mode
function int vmm_broadcast::new_output(vmm_channel channel,
logic use_references=1'bx);
int chan_id = this.n_out_chans++;
if(channel == null) begin
`vmm_error(this.log,"Null argument provided to vmm_broadcast::new_output");
return 0;
end
this.out_chans.push_back(channel);
this.is_on.push_back(1);
this.use_refs.push_back((use_references === 1'bx) ?
this.dflt_use_refs : use_references);
// Trigger a new broadcasting cycle whenever the channel becomes
// empty while it is ON
fork
while (1) begin
// A new (presumably empty) channel is added creates
// a broadcast opportunity
if (this.is_on[chan_id]) -> this.new_cycle;
channel.notify.wait_for(vmm_channel::GOT);
end
join_none
new_output = chan_id;
endfunction : new_output
function void vmm_broadcast::bcast_on(int unsigned output_id);
this.bcast_on_off(output_id, 1);
endfunction: bcast_on
function void vmm_broadcast::bcast_off(int unsigned output_id);
this.bcast_on_off(output_id, 0);
endfunction: bcast_off
function void vmm_broadcast::bcast_on_off(int channel_id,
int on_off);
if (channel_id < 0 || channel_id >= this.n_out_chans) begin
if (this.log.start_msg(vmm_log::FAILURE_TYP)) begin
string txt;
$sformat(txt, "Invalid output channel ID %0d", channel_id);
void'(this.log.text(txt));
this.log.end_msg();
end
return;
end
this.is_on[channel_id] = on_off;
// If a non-full channel is turned back on, this triggers a
// new broadcasting cycle
if (on_off && !this.out_chans[channel_id].is_full()) begin
-> this.new_cycle;
end
// If a full channel is turned off, this triggers a
// new broadcasting cycle
if (!on_off && this.out_chans[channel_id].is_full()) begin
-> this.new_cycle;
end
// Flush a channel that has been turned off
if (!on_off) begin
this.out_chans[channel_id].flush();
end
endfunction: bcast_on_off
task vmm_broadcast::bcast_to_output(int channel_id,
int on_off);
this.bcast_on_off(channel_id, on_off);
endtask : bcast_to_output
function bit vmm_broadcast::add_to_output(int unsigned decision_id,
int unsigned output_id,
vmm_channel channel,
vmm_data obj);
add_to_output = 1;
endfunction : add_to_output
function void vmm_broadcast::start_xactor();
super.start_xactor();
endfunction : start_xactor
function void vmm_broadcast::stop_xactor();
super.stop_xactor();
endfunction : stop_xactor
function void vmm_broadcast::reset_xactor(vmm_xactor::reset_e rst_typ=SOFT_RST);
super.reset_xactor(rst_typ);
this.in_chan.flush();
foreach (out_chans[i]) begin
this.out_chans[i].flush();
end
endfunction : reset_xactor
task vmm_broadcast::main();
fork
super.main();
this.broadcast();
this.sink_if_outs();
join_none
endtask : main
task vmm_broadcast::broadcast();
bit run_once = 1;
while (1) begin
int decision_id = 0;
int i;
int go;
vmm_data data, cpy;
vmm_data obj;
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("Waiting for next broadcasting cycle..."));
this.log.end_msg();
end
if (!run_once) @ this.new_cycle;
run_once = 0;
this.wait_if_stopped();
// OK to broadcast?
case (this.mode)
AFAP: begin
// OK to broadcast if just one active output channel
// is not full
int i;
go = 0;
for (i = 0; i < this.n_out_chans; i++) begin
if (this.is_on[i] && !this.out_chans[i].is_full()) begin
go = 1;
break;
end
end
end
ALAP: begin
// OK to broadcast only if ALL active output channel
// are not full
int i;
go = 1;
for (i = 0; i < this.n_out_chans; i++) begin
if (this.is_on[i] && this.out_chans[i].is_full()) begin
go = 0;
break;
end
end
end
default: begin
`vmm_error(this.log, `vmm_sformatf("Internal Error: Invalid broadcasting mode %0d!", this.mode));
continue;
end
endcase
// No go!
if (!go) continue;
this.wait_if_stopped();
this.in_chan.get(data);
this.wait_if_stopped();
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
void'(this.log.text("New broadcasting cycle..."));
this.log.end_msg();
end
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::VERBOSE_SEV)) begin
void'(this.log.text(data.psdisplay("Broadcasting:")));
this.log.end_msg();
end
`endif
for (i = 0; i < this.n_out_chans; i++) begin
if (this.is_on[i]) begin
// Copy or reference?
if (this.use_refs[i]) begin
obj = data;
end
else begin
// Minimize the number of copies made
if (cpy == null) cpy = data.copy();
obj = cpy;
end
if (this.add_to_output(decision_id++, i, this.out_chans[i], obj)) begin
this.out_chans[i].sneak(obj);
`ifdef VMM_DETAILED_MSGS
if (this.log.start_msg(vmm_log::INTERNAL_TYP,
vmm_log::DEBUG_SEV)) begin
string msg;
$sformat(msg, "Broadcasted to output #%0d", i);
void'(this.log.text(msg));
this.log.end_msg();
end
if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::VERBOSE_SEV)) begin
void'(this.log.text(obj.psdisplay("Broadcasted:")));
this.log.end_msg();
end
`endif
// Mark the copy as having been used.
if (!this.use_refs[i]) cpy = null;
end
end
end
end
endtask : broadcast
task vmm_broadcast::sink_if_outs();
bit sink;
vmm_data unused_data;
vmm_data temp_obj;
// Sink the data from the source channel
// if there are no active output channels
while (1) begin
int i;
// Wait for something to be in the source channel
vmm_data peeked;
this.in_chan.peek(peeked);
// Can it go anywhere??
sink = 1;
for (i = 0; i < this.n_out_chans; i++) begin
if (this.is_on[i]) begin
sink = 0;
break;
end
end
// Sink it if there is nowhere to go and it has not
// been removed by some other thread.
this.in_chan.peek( temp_obj);
if (sink && this.in_chan.level() > 0 &&
temp_obj == peeked ) begin
this.in_chan.get( unused_data);
end
if (in_chan.level() > 0) begin
in_chan.notify.wait_for(vmm_channel::GOT);
end
end
endtask : sink_if_outs