VMM OpenSource - sv/std_lib/vmm_scheduler.sv

sv/std_lib/vmm_scheduler.sv expanded source

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