VMM OpenSource - sv/std_lib/vmm_broadcast.sv

sv/std_lib/vmm_broadcast.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_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