VMM OpenSource - sv/std_lib/vmm_xactor.sv

sv/std_lib/vmm_xactor.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_xactor::new(string name,
00024                          string inst,
00025      	                 int    stream_id = -1
00026                          `VMM_XACTOR_BASE_NEW_EXTERN_ARGS);
00027 
00028 
00029 `ifdef VMM_XACTOR_BASE_NEW_CALL
00030    super.new(`VMM_XACTOR_BASE_NEW_CALL);
00031 `endif
00032 
00033    this.log  = new(name, inst);
00034    this.notify = new(this.log);
00035    `VMM_OBJECT_SET_PARENT(this.notify, this)
00036 
00037    void'(this.notify.configure(XACTOR_IDLE, vmm_notify::ON_OFF));
00038    void'(this.notify.configure(XACTOR_BUSY, vmm_notify::ON_OFF));
00039    void'(this.notify.configure(XACTOR_STARTED));
00040    void'(this.notify.configure(XACTOR_STOPPING, vmm_notify::ON_OFF));
00041    void'(this.notify.configure(XACTOR_IS_STOPPED, vmm_notify::ON_OFF));
00042    void'(this.notify.configure(XACTOR_STOPPED));
00043    void'(this.notify.configure(XACTOR_RESET));
00044 
00045    this.is_stopped = 1;
00046    this.notify.indicate(XACTOR_IS_STOPPED);
00047    this.notify.indicate(XACTOR_IDLE);
00048    this.notify.reset(XACTOR_BUSY);
00049 
00050    this.stream_id   = stream_id;
00051 
00052    this.start_it   = 0;
00053    this.stop_it    = 1;
00054    this.reset_it   = 0;
00055    this.n_threads_to_stop = -1;
00056    this.n_threads_stopped = 0;
00057    this.main_running = 0;
00058 
00059    this._vmm_available_xactor.push_back(this); 
00060 
00061    fork
00062       begin
00063       this.save_main_rng_state    = 1;
00064       this.restore_main_rng_state = 0;
00065       this.main_rng_state = get_randstate();
00066 
00067       while (1) begin
00068          this.main_running = 0;
00069 
00070          // wait to start
00071          while (!this.start_it) @(this.control_event);
00072          this.start_it = 0;
00073          this.stop_it  = 0;
00074          this.reset_it = 0;
00075          this.n_threads_to_stop = -1;
00076          this.n_threads_stopped = 0;
00077          this.is_stopped = 0;
00078 
00079          // We may be held back by on-going reset operation
00080          fork
00081             if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00082                void'(this.log.text("Start delayed by on-going reset activity"));
00083                this.log.end_msg();
00084             end
00085          join_none
00086 
00087          while (this.reset_pending > 0) begin
00088             `vmm_fatal(this.log, "Pending resets not currently supported");
00089             // `wait(@(this.reset_pending)); // Not supported yet.
00090          end
00091          disable fork;
00092 
00093          //
00094          // Fork the main body
00095          //
00096                
00097          if (this.save_main_rng_state) begin
00098             this.main_rng_state = get_randstate();
00099          end
00100          if (this.restore_main_rng_state) begin
00101             set_randstate(main_rng_state);
00102          end
00103          this.save_main_rng_state    = 0;
00104          this.restore_main_rng_state = 0;
00105 
00106          fork
00107             begin
00108                if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV )) begin
00109                   void'(this.log.text("Started"));
00110                   this.log.end_msg();
00111                end
00112 
00113                this.is_stopped = 0;
00114                this.notify.reset(XACTOR_IDLE);
00115                this.notify.reset(XACTOR_IS_STOPPED);
00116                this.notify.indicate(XACTOR_BUSY);
00117                this.notify.indicate(XACTOR_STARTED);
00118                main();
00119             end
00120 
00121             begin
00122                // Check that super.main() was called in all
00123                // extensions of this class
00124                #1;
00125                if (!this.main_running) begin
00126                   `vmm_warning(this.log, "Virtual vmm_xactor::main() does not call super.main()");
00127                end
00128             end
00129          join_none
00130 
00131          // Wait to reset
00132          while (!this.reset_it) @(this.control_event);
00133          this.reset_it  = 0;
00134          this.n_threads_to_stop = -1;
00135          this.n_threads_stopped = 0;
00136          if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00137             void'(this.log.text("Reset"));
00138             this.log.end_msg();
00139          end
00140          this.is_stopped = 1;
00141          this.notify.reset(XACTOR_BUSY);
00142          this.notify.indicate(XACTOR_IDLE);
00143          this.notify.reset(XACTOR_STOPPING);
00144          this.notify.indicate(XACTOR_IS_STOPPED);
00145          this.notify.indicate(XACTOR_STOPPED);
00146          this.notify.indicate(XACTOR_RESET);
00147          disable fork;
00148       end
00149    end
00150    join_none
00151 endfunction: new
00152 
00153 
00154 function string vmm_xactor::get_name();
00155    get_name = this.log.get_name();
00156 endfunction:get_name
00157 
00158 
00159 function string vmm_xactor::get_instance();
00160    get_instance = this.log.get_instance();
00161 endfunction: get_instance
00162 
00163 
00164 function void vmm_xactor::start_xactor();
00165    this.start_it = 1;
00166    this.stop_it  = 0;
00167    -> this.control_event;
00168    this.notify.reset(XACTOR_STOPPING);
00169 endfunction: start_xactor
00170 
00171 
00172 function void vmm_xactor::stop_xactor();
00173    this.start_it = 0;
00174    this.stop_it  = 1;
00175    -> this.control_event;
00176    this.notify.indicate(XACTOR_STOPPING);
00177 
00178    // Is it already stopped?
00179    if (this.is_stopped) begin
00180       if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00181          void'(this.log.text("Already stopped"));
00182          this.log.end_msg();
00183       end
00184       this.notify.indicate(XACTOR_STOPPED);
00185       return;
00186    end
00187 
00188    if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00189       void'(this.log.text("Stop requested"));
00190       this.log.end_msg();
00191    end
00192    this.check_all_threads_stopped();
00193 endfunction: stop_xactor
00194 
00195 
00196 function void vmm_xactor::reset_xactor(vmm_xactor::reset_e rst_typ = SOFT_RST);
00197    this.start_it = 0;
00198    this.reset_it = 1;
00199    -> this.control_event;
00200    this.is_stopped = 1;
00201 
00202    // Reset notifier & RNG state on FIRM and above
00203    if (rst_typ == FIRM_RST ||
00204        rst_typ == HARD_RST) begin
00205       this.notify.reset(-1, vmm_notify::HARD);
00206       this.restore_rng_state();
00207    end
00208    else begin
00209       this.notify.reset(-1, vmm_notify::SOFT);
00210    end
00211 
00212    // Unregister all callbacks on HARD reset
00213    if (rst_typ == HARD_RST) begin
00214 `ifdef VCS2006_06
00215       // Work-around for NYI feature in VCS2006.06
00216       // but IEEE 1800-2009 compliant
00217       this.callbacks.delete();
00218 `else
00219       // Works in VCS2008.03 or later
00220       // IEEE 1800-2005 compliant
00221       this.callbacks = '{};
00222 `endif
00223    end
00224 endfunction: reset_xactor
00225 
00226 
00227 function void vmm_xactor::save_rng_state();
00228    if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00229       void'(this.log.text("Saving RNG state information..."));
00230       this.log.end_msg();
00231    end
00232 
00233    if (!this.is_stopped) begin
00234       `vmm_warning(this.log, "save_rng_state() called while transactor is still running");
00235    end
00236    this.save_main_rng_state = 1;
00237 endfunction: save_rng_state
00238 
00239 
00240 function void vmm_xactor::restore_rng_state();
00241    if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00242       void'(this.log.text("Restoring RNG state information..."));
00243       this.log.end_msg();
00244    end
00245 
00246    if (!this.is_stopped) begin
00247       `vmm_warning(this.log, "restore_rng_state() called while transactor is still running");
00248    end
00249    this.restore_main_rng_state = 1;
00250 endfunction: restore_rng_state
00251 
00252 
00253 function string vmm_xactor::psdisplay(string prefix = "");
00254    $sformat(psdisplay, "%sTransactor %s (%s):", prefix,
00255             this.log.get_name(), this.log.get_instance());
00256 
00257    if (this.is_stopped) begin
00258       return {psdisplay, "\n", prefix, "Transactor is STOPPED"};
00259    end
00260 
00261    if (this.n_threads_stopped < this.n_threads_to_stop) begin
00262       return `vmm_sformatf("%s\n%sTransactor is STOPPING (%0d out of %0d threads stopped)",
00263                            psdisplay, prefix, this.n_threads_stopped,
00264                            this.n_threads_to_stop);
00265    end
00266 
00267    return {psdisplay, "\n", prefix, "Transactor is RUNNING"};
00268 endfunction: psdisplay
00269 
00270 
00271 function void vmm_xactor::xactor_status(string prefix = "");
00272    `vmm_note(this.log, this.psdisplay(prefix));
00273 endfunction: xactor_status
00274 
00275 
00276 task vmm_xactor::main();
00277    this.main_running = 1;
00278 endtask: main
00279 
00280 
00281 function void vmm_xactor::check_all_threads_stopped();
00282    if (this.n_threads_to_stop > 0) begin
00283       if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00284          void'(this.log.text($psprintf("%0d out of %0d threads have now stopped",n_threads_stopped,this.n_threads_to_stop)));
00285          this.log.end_msg();
00286       end
00287 
00288       if (this.n_threads_stopped >= this.n_threads_to_stop) begin
00289          if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00290             void'(this.log.text("Stopped"));
00291             this.log.end_msg();
00292          end
00293          
00294          this.is_stopped = 1;
00295          this.notify.reset(XACTOR_BUSY);
00296          this.notify.indicate(XACTOR_IDLE);
00297          this.notify.reset(XACTOR_STOPPING);
00298          this.notify.indicate(XACTOR_IS_STOPPED);
00299          this.notify.indicate(XACTOR_STOPPED);
00300       end
00301    end
00302 endfunction
00303 
00304 
00305 task vmm_xactor::wait_if_stopped(int unsigned n_threads = 1);
00306    if (n_threads == 0) begin
00307       if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV)) begin
00308          void'(this.log.text("Number of threads to stop specified to vmm_xactor::wait_if_stopped() must be greater than 0"));
00309          this.log.end_msg();
00310       end
00311       n_threads = 1;
00312    end
00313 
00314    if (this.n_threads_to_stop <= 0) this.n_threads_to_stop = n_threads;
00315    else if (this.n_threads_to_stop != n_threads) begin
00316       if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::ERROR_SEV)) begin
00317          void'(this.log.text("All threads must specify the same number of threads to stop to vmm_xactor::wait_if_stopped() and  vmm_xactor::wait_if_stopped_or_empty()"));
00318          this.log.end_msg();
00319       end
00320       if (this.n_threads_to_stop < n_threads) this.n_threads_to_stop = n_threads;
00321    end
00322 
00323    if (this.stop_it) begin
00324       this.n_threads_stopped++;
00325       this.check_all_threads_stopped();
00326 
00327       while (this.stop_it) @(this.control_event);
00328       // Make sure these are done only once if
00329       // there are multiple stopped threads
00330       if (this.is_stopped) begin
00331          if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00332             void'(this.log.text("Restarted"));
00333             this.log.end_msg();
00334          end
00335          this.is_stopped = 0;
00336          this.notify.indicate(XACTOR_STARTED);
00337          this.notify.reset(XACTOR_IS_STOPPED);
00338          this.notify.reset(XACTOR_IDLE);
00339          this.notify.indicate(XACTOR_BUSY);
00340       end
00341       this.n_threads_stopped--;
00342    end
00343 endtask: wait_if_stopped
00344 
00345 
00346 task vmm_xactor::wait_if_stopped_or_empty(vmm_channel  chan,
00347                                           int unsigned n_threads = 1);
00348    this.wait_if_stopped(n_threads); 
00349    while (chan.level() == 0) begin
00350        vmm_data data;
00351 
00352        this.n_threads_stopped++;
00353        // If all other threads are blocked, indicate IDLE
00354        // because we are going to block on the channel
00355        if (this.n_threads_stopped >= this.n_threads_to_stop) begin
00356          this.notify.reset(XACTOR_BUSY);
00357          this.notify.indicate(XACTOR_IDLE);
00358        end
00359 
00360        if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) begin
00361           void'(this.log.text($psprintf("%0d threads have now stopped or blocked",n_threads_stopped)));
00362           this.log.end_msg();
00363        end
00364 
00365        chan.peek(data);
00366        this.n_threads_stopped--;
00367        this.wait_if_stopped(n_threads);
00368   end
00369 
00370   this.notify.reset(XACTOR_IDLE);
00371   this.notify.indicate(XACTOR_BUSY);
00372 endtask: wait_if_stopped_or_empty
00373 
00374 
00375 function void vmm_xactor::prepend_callback(vmm_xactor_callbacks cb);
00376    if (cb == null) begin
00377       `vmm_error(this.log, "Attempting to prepend a NULL callback extension");
00378       return;
00379    end
00380 
00381    foreach(this.callbacks[i]) begin
00382       if (this.callbacks[i] == cb) begin
00383          `vmm_warning(this.log, "Callback has already been registered");
00384          return;
00385       end
00386    end
00387    //Prepend new callback
00388    this.callbacks.push_front(cb);
00389 endfunction: prepend_callback
00390 
00391 
00392 function void vmm_xactor::append_callback(vmm_xactor_callbacks cb);
00393    if (cb == null) begin
00394       `vmm_error(this.log, "Attempting to append a NULL callback extension");
00395       return;
00396    end
00397 
00398    foreach(this.callbacks[i]) begin
00399       if (this.callbacks[i] == cb) begin
00400          `vmm_warning(this.log, "Callback has already been registered");
00401          return;
00402       end
00403    end
00404    //Append new callback
00405    this.callbacks.push_back(cb);
00406 endfunction: append_callback
00407 
00408 
00409 function void vmm_xactor::unregister_callback(vmm_xactor_callbacks cb);
00410    foreach(this.callbacks[i]) begin
00411       if (this.callbacks[i] == cb) begin
00412          // Unregister it
00413          this.callbacks.delete(i);
00414          return;
00415       end
00416    end
00417 
00418    `vmm_warning(this.log, "Callback was not registered");
00419 endfunction: unregister_callback
00420 
00421 
00422 function void vmm_xactor::get_input_channels(ref vmm_channel chans[$]);
00423    chans = this.Xinput_chansX;
00424 endfunction: get_input_channels
00425 
00426 
00427 function void vmm_xactor::get_output_channels(ref vmm_channel chans[$]);
00428    chans = this.Xoutput_chansX;
00429 endfunction: get_output_channels
00430 
00431 
00432 function void vmm_xactor::kill();
00433    vmm_channel ins[$] = this.Xinput_chansX;
00434    vmm_channel outs[$] = this.Xoutput_chansX;
00435 
00436 `ifdef VCS2006_06
00437    // Work-around for NYI feature in VCS2006.06
00438    // but IEEE 1800-2009 compliant
00439    this.Xinput_chansX.delete();
00440    this.Xoutput_chansX.delete();
00441 `else
00442    // Works in VCS2008.03 or later
00443    // IEEE 1800-2005 compliant
00444    this.Xinput_chansX = '{};
00445    this.Xoutput_chansX = '{};
00446 `endif
00447    
00448    foreach(ins[i]) begin
00449       if (ins[i].get_consumer() == this) begin
00450          ins[i].set_consumer(null);
00451          if (ins[i].get_producer() == null) ins[i].kill();
00452       end
00453    end
00454       
00455    foreach(outs[i]) begin
00456       if (outs[i].get_producer() == this) begin
00457          outs[i].set_producer(null);
00458          if (outs[i].get_consumer() == null) outs[i].kill();
00459       end
00460    end
00461       
00462    foreach(this._vmm_available_xactor[i]) begin
00463      if (this._vmm_available_xactor[i] == this) begin
00464         this._vmm_available_xactor.delete(i);
00465         break;
00466      end
00467    end
00468 
00469    this.log.kill();
00470 endfunction: kill
00471 
00472 
00473 
00474 
00475 
00476 `ifdef VMM_SB_DS_IN_STDLIB
00477 function void vmm_xactor::inp_vmm_sb_ds(vmm_data tr);
00478    foreach (this._vmm_sb_ds[i]) begin
00479       if (this._vmm_sb_ds[i].is_in) begin
00480          this._vmm_sb_ds[i].sb.insert(tr);
00481       end
00482    end
00483 endfunction: inp_vmm_sb_ds
00484 
00485 
00486 function void vmm_xactor::exp_vmm_sb_ds(vmm_data tr);
00487    foreach (this._vmm_sb_ds[i]) begin
00488       if (this._vmm_sb_ds[i].is_out) begin
00489          case (this._vmm_sb_ds[i].order)
00490            vmm_sb_ds::IN_ORDER:
00491              this._vmm_sb_ds[i].sb.expect_in_order(tr);
00492 
00493            vmm_sb_ds::WITH_LOSSES: begin
00494               vmm_data p;
00495               vmm_data losses[];
00496               this._vmm_sb_ds[i].sb.expect_with_losses(tr, p, losses);
00497            end
00498 
00499            vmm_sb_ds::OUT_ORDER:
00500              this._vmm_sb_ds[i].sb.expect_out_of_order(tr);
00501 
00502          endcase
00503       end
00504    end
00505 endfunction: exp_vmm_sb_ds
00506 
00507 
00508 function void vmm_xactor::register_vmm_sb_ds(vmm_sb_ds             sb,
00509                                              vmm_sb_ds::kind_e     kind,
00510                                              vmm_sb_ds::ordering_e order = vmm_sb_ds::IN_ORDER);
00511    vmm_sb_ds_registration ds;
00512 
00513    foreach (this._vmm_sb_ds[i]) begin
00514       if (this._vmm_sb_ds[i].sb == sb) begin
00515          `vmm_warning(this.log, "Data stream scoreboard is already registered");
00516          return;
00517       end
00518    end
00519 
00520    ds = new;
00521    ds.sb = sb;
00522    ds.is_in = (kind == vmm_sb_ds::INPUT ||
00523                kind == vmm_sb_ds::EITHER);
00524    ds.is_out = (kind == vmm_sb_ds::EXPECT ||
00525                 kind == vmm_sb_ds::EITHER);
00526    ds.order = order;
00527    this._vmm_sb_ds.push_back(ds);
00528 endfunction: register_vmm_sb_ds
00529 
00530 
00531 function void vmm_xactor::unregister_vmm_sb_ds(vmm_sb_ds sb);
00532    foreach (this._vmm_sb_ds[i]) begin
00533       if (this._vmm_sb_ds[i].sb == sb) begin
00534          this._vmm_sb_ds.delete(i);
00535          return;
00536       end
00537    end
00538 
00539    `vmm_error(this.log, "Data stream scoreboard is not registered");
00540 endfunction: unregister_vmm_sb_ds
00541 `endif
00542 
00543 
00544 function string vmm_xactor::do_psdisplay(string prefix = "");
00545    this.__vmm_done_user = 0;
00546 endfunction
00547 
00548 
00549 function void vmm_xactor::do_start_xactor();
00550    this.__vmm_done_user = 0;
00551 endfunction
00552 
00553 
00554 function void vmm_xactor::do_stop_xactor();
00555    this.__vmm_done_user = 0;
00556 endfunction
00557 
00558 
00559 function void vmm_xactor::do_reset_xactor(vmm_xactor::reset_e rst_typ);
00560    this.__vmm_done_user = 0;
00561 endfunction
00562 
00563 
00564 function void vmm_xactor::do_kill_xactor();
00565    this.__vmm_done_user = 0;
00566 endfunction
00567 
00568 
00569 function vmm_xactor_iter::new(string  name = "",
00570                               string  inst = "");
00571    if (name == "") this.name = ".";
00572    else begin
00573       // Remove "/" surrounding the pattern, if any
00574       if (`vmm_str_match(name, "^/(.*)/$")) name = `vmm_str_backref(name, 0);
00575       this.name = name;
00576    end
00577 
00578    if (inst == "") this.inst = ".";
00579    else begin
00580       // Remove "/" surrounding the pattern, if any
00581       if (`vmm_str_match(inst, "^/(.*)/$")) inst = `vmm_str_backref(inst, 0);
00582       this.inst = inst;
00583    end
00584 
00585    void'(this.first());
00586 endfunction: new
00587 
00588 
00589 function void vmm_xactor_iter::move_iterator();
00590    string xa_name;
00591    string xa_inst;
00592    int n = _vmm_xactor._vmm_available_xactor.size();
00593 
00594    if (this.idx >= n || n <= 1) return;
00595 
00596    for (int x = this.idx+1; x < n; x++) begin
00597       xa_name = _vmm_xactor._vmm_available_xactor[x].log.get_name();
00598       xa_inst = _vmm_xactor._vmm_available_xactor[x].log.get_instance();
00599 
00600       if (`vmm_str_match(xa_name, this.name) &&
00601           `vmm_str_match(xa_inst, this.inst)) begin
00602          this.idx = x;
00603          return;
00604       end
00605    end
00606    this.idx = 0;
00607 endfunction
00608 
00609 
00610 function vmm_xactor vmm_xactor_iter::xactor();
00611    if (this.idx <= 0 ||
00612        this.idx >= _vmm_xactor._vmm_available_xactor.size())
00613      return null;
00614 
00615    return _vmm_xactor._vmm_available_xactor[this.idx];
00616 endfunction
00617 
00618 
00619 function vmm_xactor vmm_xactor_iter::first();
00620    this.idx = 0;
00621    this.move_iterator();
00622    return this.xactor();
00623 endfunction  
00624 
00625   
00626 function vmm_xactor vmm_xactor_iter::next();
00627    this.move_iterator();
00628    return this.xactor();
00629 endfunction