00001 //
00002 // -------------------------------------------------------------
00003 // Copyright 2004-2008 Synopsys, Inc.
00004 // All Rights Reserved Worldwide
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the
00007 // "License"); you may not use this file except in
00008 // compliance with the License. You may obtain a copy of
00009 // the License at
00010 //
00011 // http://www.apache.org/licenses/LICENSE-2.0
00012 //
00013 // Unless required by applicable law or agreed to in
00014 // writing, software distributed under the License is
00015 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
00016 // CONDITIONS OF ANY KIND, either express or implied. See
00017 // the License for the specific language governing
00018 // permissions and limitations under the License.
00019 // -------------------------------------------------------------
00020 //
00021
00022
00023 function vmm_broadcast::new(string name,
00024 string inst,
00025 vmm_channel source,
00026 bit use_references=1,
00027 int mode=AFAP
00028 `VMM_XACTOR_NEW_EXTERN_ARGS);
00029 super.new(name, inst, -1 `VMM_XACTOR_NEW_CALL);
00030
00031 if (source == null) begin
00032 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00033 void'(this.log.text("Cannot create vmm_broadcast instance with a NULL source channel reference"));
00034 this.log.end_msg();
00035 end
00036 end
00037
00038 this.in_chan = source;
00039 this.log.is_above(this.in_chan.log);
00040 this.dflt_use_refs = use_references;
00041 this.mode = mode;
00042
00043 this.n_out_chans = 0;
00044 endfunction : new
00045
00046
00047 function string vmm_broadcast::psdisplay(string prefix = "");
00048 psdisplay = super.psdisplay(prefix);
00049 $sformat(psdisplay, "%s [Mode: %s]", psdisplay,
00050 (this.mode == ALAP) ? "ALAP" : "AFAP");
00051 $sformat(psdisplay, "%s\n%sInpChan: %s(%s) [level=%0d of %0d]",
00052 psdisplay, prefix, this.in_chan.log.get_name(),
00053 this.in_chan.log.get_instance(), this.in_chan.level(),
00054 this.in_chan.full_level());
00055 foreach (this.out_chans[i]) begin
00056 $sformat(psdisplay, "%s\n%sOutChan[%0d/%s/%s]: %s(%s) [level=%0d of %0d]",
00057 psdisplay, prefix, i, (this.is_on[i]) ? "ON " : "OFF",
00058 (this.use_refs[i]) ? "ref" : "cpy",
00059 this.out_chans[i].log.get_name(),
00060 this.out_chans[i].log.get_instance(), this.out_chans[i].level(),
00061 this.out_chans[i].full_level());
00062 end
00063 return psdisplay;
00064 endfunction
00065
00066
00067 task vmm_broadcast::broadcast_mode(bcast_mode_e mode);
00068 if (mode != AFAP && mode != ALAP) begin
00069 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00070 void'(this.log.text(`vmm_sformatf("Invalid broadcast mode %0d", mode)));
00071 this.log.end_msg();
00072 end
00073 return;
00074 end
00075
00076 this.mode = mode;
00077 // This MAY create the opportunity for a new broadcast cycle...
00078 -> this.new_cycle;
00079 endtask : broadcast_mode
00080
00081
00082 function int vmm_broadcast::new_output(vmm_channel channel,
00083 logic use_references=1'bx);
00084 int chan_id = this.n_out_chans++;
00085
00086
00087 this.out_chans.push_back(channel);
00088 this.is_on.push_back(1);
00089 this.use_refs.push_back((use_references === 1'bx) ?
00090 this.dflt_use_refs : use_references);
00091
00092
00093 // Trigger a new broadcasting cycle whenever the channel becomes
00094 // empty while it is ON
00095 fork
00096 while (1) begin
00097 // A new (presumably empty) channel is added creates
00098 // a broadcast opportunity
00099 if (this.is_on[chan_id]) -> this.new_cycle;
00100 channel.notify.wait_for(vmm_channel::GOT);
00101 end
00102 join_none
00103
00104 new_output = chan_id;
00105 endfunction : new_output
00106
00107
00108 function void vmm_broadcast::bcast_on(int unsigned output_id);
00109 this.bcast_on_off(output_id, 1);
00110 endfunction: bcast_on
00111
00112
00113 function void vmm_broadcast::bcast_off(int unsigned output_id);
00114 this.bcast_on_off(output_id, 0);
00115 endfunction: bcast_off
00116
00117
00118 function void vmm_broadcast::bcast_on_off(int channel_id,
00119 int on_off);
00120 if (channel_id < 0 || channel_id >= this.n_out_chans) begin
00121 if (this.log.start_msg(vmm_log::FAILURE_TYP)) begin
00122 string txt;
00123 $sformat(txt, "Invalid output channel ID %0d", channel_id);
00124 void'(this.log.text(txt));
00125 this.log.end_msg();
00126 end
00127 return;
00128 end
00129
00130 this.is_on[channel_id] = on_off;
00131
00132 // If a non-full channel is turned back on, this triggers a
00133 // new broadcasting cycle
00134 if (on_off && !this.out_chans[channel_id].is_full()) begin
00135 -> this.new_cycle;
00136 end
00137
00138 // If a full channel is turned off, this triggers a
00139 // new broadcasting cycle
00140 if (!on_off && this.out_chans[channel_id].is_full()) begin
00141 -> this.new_cycle;
00142 end
00143
00144 // Flush a channel that has been turned off
00145 if (!on_off) begin
00146 this.out_chans[channel_id].flush();
00147 end
00148 endfunction: bcast_on_off
00149
00150
00151 task vmm_broadcast::bcast_to_output(int channel_id,
00152 int on_off);
00153 this.bcast_on_off(channel_id, on_off);
00154 endtask : bcast_to_output
00155
00156
00157 function bit vmm_broadcast::add_to_output(int unsigned decision_id,
00158 int unsigned output_id,
00159 vmm_channel channel,
00160 vmm_data obj);
00161 add_to_output = 1;
00162 endfunction : add_to_output
00163
00164
00165 function void vmm_broadcast::start_xactor();
00166 super.start_xactor();
00167 endfunction : start_xactor
00168
00169
00170 function void vmm_broadcast::stop_xactor();
00171 super.stop_xactor();
00172 endfunction : stop_xactor
00173
00174
00175 function void vmm_broadcast::reset_xactor(vmm_xactor::reset_e rst_typ=SOFT_RST);
00176 super.reset_xactor(rst_typ);
00177
00178 this.in_chan.flush();
00179 foreach (out_chans[i]) begin
00180 this.out_chans[i].flush();
00181 end
00182 endfunction : reset_xactor
00183
00184
00185 task vmm_broadcast::main();
00186 fork
00187 super.main();
00188 this.broadcast();
00189 this.sink_if_outs();
00190 join_none
00191 endtask : main
00192
00193
00194 task vmm_broadcast::broadcast();
00195 bit run_once = 1;
00196
00197 while (1) begin
00198 int decision_id = 0;
00199 int i;
00200 int go;
00201
00202 vmm_data data, cpy;
00203 vmm_data obj;
00204
00205 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00206 void'(this.log.text("Waiting for next broadcasting cycle..."));
00207 this.log.end_msg();
00208 end
00209 if (!run_once) @ this.new_cycle;
00210 run_once = 0;
00211 this.wait_if_stopped();
00212
00213 // OK to broadcast?
00214 case (this.mode)
00215
00216 AFAP: begin
00217 // OK to broadcast if just one active output channel
00218 // is not full
00219 int i;
00220
00221 go = 0;
00222 for (i = 0; i < this.n_out_chans; i++) begin
00223 if (this.is_on[i] && !this.out_chans[i].is_full()) begin
00224 go = 1;
00225 break;
00226 end
00227 end
00228 end
00229
00230 ALAP: begin
00231 // OK to broadcast only if ALL active output channel
00232 // are not full
00233 int i;
00234
00235 go = 1;
00236 for (i = 0; i < this.n_out_chans; i++) begin
00237 if (this.is_on[i] && this.out_chans[i].is_full()) begin
00238 go = 0;
00239 break;
00240 end
00241 end
00242 end
00243
00244 default: begin
00245 `vmm_error(this.log, `vmm_sformatf("Internal Error: Invalid broadcasting mode %0d!", this.mode));
00246 continue;
00247 end
00248 endcase
00249 // No go!
00250 if (!go) continue;
00251
00252 this.wait_if_stopped();
00253 this.in_chan.get(data);
00254 this.wait_if_stopped();
00255
00256 `ifdef VMM_DETAILED_MSGS
00257 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00258 void'(this.log.text("New broadcasting cycle..."));
00259 this.log.end_msg();
00260 end
00261 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::VERBOSE_SEV)) begin
00262 void'(this.log.text(data.psdisplay("Broadcasting:")));
00263 this.log.end_msg();
00264 end
00265 `endif
00266
00267 for (i = 0; i < this.n_out_chans; i++) begin
00268 if (this.is_on[i]) begin
00269
00270 // Copy or reference?
00271 if (this.use_refs[i]) begin
00272 obj = data;
00273 end
00274 else begin
00275 // Minimize the number of copies made
00276 if (cpy == null) cpy = data.copy();
00277 obj = cpy;
00278 end
00279
00280 if (this.add_to_output(decision_id++, i, this.out_chans[i], obj)) begin
00281 this.out_chans[i].sneak(obj);
00282 `ifdef VMM_DETAILED_MSGS
00283 if (this.log.start_msg(vmm_log::INTERNAL_TYP,
00284 vmm_log::DEBUG_SEV)) begin
00285 string msg;
00286 $sformat(msg, "Broadcasted to output #%0d", i);
00287 void'(this.log.text(msg));
00288 this.log.end_msg();
00289 end
00290 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::VERBOSE_SEV)) begin
00291 void'(this.log.text(obj.psdisplay("Broadcasted:")));
00292 this.log.end_msg();
00293 end
00294 `endif
00295 // Mark the copy as having been used.
00296 if (!this.use_refs[i]) cpy = null;
00297 end
00298 end
00299 end
00300 end
00301 endtask : broadcast
00302
00303
00304 task vmm_broadcast::sink_if_outs();
00305 bit sink;
00306 vmm_data unused_data;
00307
00308 vmm_data temp_obj;
00309 // Sink the data from the source channel
00310 // if there are no active output channels
00311 while (1) begin
00312 int i;
00313
00314 // Wait for something to be in the source channel
00315 vmm_data peeked;
00316 this.in_chan.peek(peeked);
00317
00318 // Can it go anywhere??
00319 sink = 1;
00320 for (i = 0; i < this.n_out_chans; i++) begin
00321 if (this.is_on[i]) begin
00322 sink = 0;
00323 break;
00324 end
00325 end
00326
00327 // Sink it if there is nowhere to go and it has not
00328 // been removed by some other thread.
00329 this.in_chan.peek( temp_obj);
00330 if (sink && this.in_chan.level() > 0 &&
00331 temp_obj == peeked ) begin
00332 this.in_chan.get( unused_data);
00333 end
00334
00335 if (in_chan.level() > 0) begin
00336 in_chan.notify.wait_for(vmm_channel::GOT);
00337 end
00338 end
00339 endtask : sink_if_outs