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