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_scheduler::new(string name,
00024 string inst,
00025 vmm_channel destination,
00026 int instance_id = -1
00027 `VMM_XACTOR_NEW_EXTERN_ARGS);
00028 super.new(name, inst, instance_id `VMM_XACTOR_NEW_CALL);
00029
00030 if (destination == null) begin
00031 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00032 void'(this.log.text("Cannot create vmm_scheduler instance with a NULL destination channel reference"));
00033 this.log.end_msg();
00034 end
00035 return;
00036 end
00037 this.out_chan = destination;
00038 this.log.is_above(this.out_chan.log);
00039
00040 this.randomized_sched = new;
00041
00042 this.instance_id = instance_id;
00043 this.election_count = 0;
00044 endfunction : new
00045
00046
00047 function string vmm_scheduler::psdisplay(string prefix = "");
00048 psdisplay = super.psdisplay(prefix);
00049 $sformat(psdisplay, "%s\n%sOutChan: %s(%s) [level=%0d of %0d]",
00050 psdisplay, prefix, this.out_chan.log.get_name(),
00051 this.out_chan.log.get_instance(), this.out_chan.level(),
00052 this.out_chan.full_level());
00053 foreach (this.sources[i]) begin
00054 $sformat(psdisplay, "%s\n%sInChan[%0d/%s]: %s(%s) [level=%0d of %0d]",
00055 psdisplay, prefix, i, (this.is_on[i]) ? "ON " : "OFF",
00056 this.sources[i].log.get_name(),
00057 this.sources[i].log.get_instance(), this.sources[i].level(),
00058 this.sources[i].full_level());
00059 end
00060 return psdisplay;
00061 endfunction
00062
00063
00064 function int vmm_scheduler::new_source(vmm_channel channel);
00065 if (channel == null) begin
00066 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::WARNING_SEV)) begin
00067 void'(this.log.text("Attempting to add a NULL source channel"));
00068 this.log.end_msg();
00069 end
00070 return -1;
00071 end
00072
00073 new_source = this.sources.size();
00074
00075 this.sources.push_back(channel);
00076 this.is_on.push_back(1);
00077 if (channel.level() > 0) begin
00078 -> this.next_cycle;
00079 end
00080
00081 // Watch for new additions to the newly added source
00082 // to potentially trigger new scheduling cycles
00083 fork
00084 while (1) begin
00085 // The input channel may have been filled
00086 // before the forked thread has had a chance
00087 // to wait on the PUT indication
00088 if (channel.level() > 0) begin
00089 -> this.next_cycle;
00090 end
00091 channel.notify.wait_for(vmm_channel::PUT);
00092 -> this.next_cycle;
00093 end
00094 join_none
00095
00096 endfunction : new_source
00097
00098
00099 task vmm_scheduler::sched_from_input(int channel_id,
00100 int on_off);
00101 if (channel_id < 0 || channel_id >= this.sources.size()) begin
00102 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::WARNING_SEV)) begin
00103 string msg;
00104 $sformat(msg, "Invalid source channel ID specified in vmm_scheduler::sched_from_input(): %0d", channel_id);
00105 void'(this.log.text(msg));
00106 this.log.end_msg();
00107 end
00108 return;
00109 end
00110
00111 this.is_on[channel_id] = on_off;
00112
00113 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00114 string msg;
00115 $sformat(msg, "Scheduling from channel #%0d turned %s", channel_id,
00116 (on_off) ? "ON" : "OFF");
00117 void'(this.log.text(msg));
00118 this.log.end_msg();
00119 end
00120
00121 if (on_off && this.sources[channel_id].level() > 0) begin
00122 -> this.next_cycle;
00123 end
00124 endtask : sched_from_input
00125
00126
00127 task vmm_scheduler::schedule(output vmm_data obj,
00128 input vmm_channel sources[$],
00129 input int unsigned input_ids[$]);
00130 int id;
00131
00132 this.randomized_sched.instance_id = this.instance_id;
00133 this.randomized_sched.election_id = this.election_count++;
00134 this.randomized_sched.n_sources = sources.size();
00135 this.randomized_sched.sources = sources;
00136 this.randomized_sched.ids = input_ids;
00137
00138 // Round-robin scheduler
00139 this.randomized_sched.next_idx = 0;
00140 if (this.randomized_sched.id_history.size() > 0) begin
00141 int last_id;
00142 // Find the next ID that follows (numerically) the last one
00143 // that was picked or use the first ID in the list of IDs.
00144 // Note: IDs will be stored in increasing numerical values.
00145 last_id = this.randomized_sched.id_history[$];
00146 foreach (input_ids[i]) begin
00147 if (input_ids[i] > last_id) begin
00148 this.randomized_sched.next_idx = i;
00149 break;
00150 end
00151 end
00152 end
00153
00154 obj = null;
00155 if (!this.randomized_sched.randomize()) begin
00156 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00157 void'(this.log.text("Unable to randomize vmm_scheduler::randomized_sched"));
00158 this.log.end_msg();
00159 end
00160 return;
00161 end
00162 if (this.randomized_sched.source_idx < 0) return;
00163
00164 if (this.randomized_sched.source_idx >= input_ids.size()) begin
00165 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00166 void'(this.log.text("vmm_scheduler::randomized_sched randomized to an invalid choice"));
00167 this.log.end_msg();
00168 end
00169 return;
00170 end
00171
00172 id = input_ids[this.randomized_sched.source_idx];
00173
00174 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00175 string msg;
00176 $sformat(msg, "Scheduled data from source #%0d, offset %0d",
00177 id, this.randomized_sched.obj_offset);
00178 void'(this.log.text(msg));
00179 this.log.end_msg();
00180 end
00181
00182 begin
00183 vmm_channel src = this.sources[id];
00184 if (src == null || src.level() == 0 ||
00185 src.level() <= this.randomized_sched.obj_offset) begin
00186 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00187 void'(this.log.text("vmm_scheduler::randomized_sched randomized to an invalid source"));
00188 this.log.end_msg();
00189 end
00190 return;
00191 end
00192 this.get_object(obj, src, id, this.randomized_sched.obj_offset);
00193 end
00194
00195 this.randomized_sched.id_history.push_back(id);
00196 this.randomized_sched.obj_history.push_back(obj);
00197 if (this.randomized_sched.id_history.size() > 10) begin
00198 void'(this.randomized_sched.id_history.pop_front());
00199 void'(this.randomized_sched.obj_history.pop_front());
00200 end
00201 endtask : schedule
00202
00203
00204 task vmm_scheduler::get_object(output vmm_data obj,
00205 input vmm_channel source,
00206 input int unsigned input_id,
00207 input int offset);
00208 obj = null;
00209
00210 if (source == null) begin
00211 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00212 void'(this.log.text("vmm_scheduler::get_object called with invalid source"));
00213 this.log.end_msg();
00214 end
00215 return;
00216 end
00217
00218 if (offset >= source.level()) begin
00219 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) begin
00220 void'(this.log.text("vmm_scheduler::get_object called with invalid offset"));
00221 this.log.end_msg();
00222 end
00223 return;
00224 end
00225
00226 source.get( obj, offset);
00227 endtask : get_object
00228
00229
00230 function void vmm_scheduler::start_xactor();
00231 super.start_xactor();
00232 // This MAY cause a new scheduling cycle
00233 -> this.next_cycle;
00234 endfunction : start_xactor
00235
00236
00237 function void vmm_scheduler::stop_xactor();
00238 super.stop_xactor();
00239 endfunction : stop_xactor
00240
00241
00242 function void vmm_scheduler::reset_xactor(vmm_xactor::reset_e rst_typ = SOFT_RST);
00243 super.reset_xactor(rst_typ);
00244
00245 this.out_chan.flush();
00246 foreach (sources[i]) begin
00247 this.sources[i].flush();
00248 end
00249
00250 this.instance_id = instance_id;
00251 this.election_count = 0;
00252
00253 if (rst_typ == HARD_RST ) begin
00254 this.randomized_sched = new;
00255 end
00256 endfunction
00257
00258
00259 task vmm_scheduler::main();
00260 fork
00261 super.main();
00262 this.schedule_cycle();
00263 join_none
00264 endtask
00265
00266
00267 task vmm_scheduler::schedule_cycle();
00268 vmm_data data;
00269 vmm_channel srcs[$];
00270 int unsigned ids[$];
00271
00272 while (1) begin
00273 `ifdef VCS2006_06
00274 // Work-around for NYI feature in VCS2006.06
00275 // but IEEE 1800-2009 compliant
00276 srcs.delete();
00277 ids.delete();
00278 `else
00279 // Works in VCS2008.03 or later
00280 // IEEE 1800-2005 compliant
00281 srcs = '{};
00282 ids = '{};
00283 `endif
00284
00285 super.wait_if_stopped();
00286
00287 // Identify all non-empty, active sources
00288 foreach (this.sources[i]) begin
00289 if (this.is_on[i] && this.sources[i] != null &&
00290 this.sources[i].level() > 0) begin
00291 srcs.push_back(this.sources[i]);
00292 ids.push_back(i);
00293 end
00294 end
00295 if (srcs.size() == 0) data = null;
00296 else this.schedule(data, srcs, ids);
00297
00298 if (data == null) begin
00299 // Delay the next scheduling cycle until
00300 // A new channel is added, new data is put into
00301 // a channel, a channel is turned on or the scheduler
00302 // is restarted
00303 `vmm_trace(this.log, "Waiting for next scheduling cycle...");
00304 @ ( this.next_cycle);
00305 continue;
00306 end
00307 this.out_chan.put(data);
00308 end
00309 endtask
00310