VMM - std_lib/vmm_scheduler.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
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