VMM OpenSource - sv/std_lib/vmm_channel.sv

sv/std_lib/vmm_channel.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 // ToDo: Fill level based on bytes
00024 
00025 function vmm_channel::new(string       name,
00026                           string       inst,
00027                           int unsigned full=1,
00028                           int unsigned empty=0,
00029                           bit          fill_as_bytes=1'b0);
00030 `ifdef VMM_CHANNEL_BASE_NEW_CALL
00031    super.new(`VMM_CHANNEL_BASE_NEW_CALL);
00032 `endif
00033 
00034   if (this.shared_log == null) begin
00035      this.one_log = _vmm_opts.get_bit("channel_shared_log",
00036                                       "All VMM channels share the same vmm_log instance");
00037      this.shared_log = new("VMM Channel", "[shared]");
00038   end
00039 
00040    if (this.one_log) this.log = shared_log;
00041    else this.log = new(name, inst);
00042    this.shared_log = this.log;
00043 
00044    this.notify = new(this.log);
00045    `VMM_OBJECT_SET_PARENT(this.notify, this)
00046 
00047    void'(this.notify.configure(FULL,  vmm_notify::ON_OFF));
00048    void'(this.notify.configure(EMPTY, vmm_notify::ON_OFF));
00049    void'(this.notify.configure(PUT));
00050    void'(this.notify.configure(GOT));
00051    void'(this.notify.configure(PEEKED));
00052    void'(this.notify.configure(ACTIVATED));
00053    void'(this.notify.configure(ACT_STARTED));
00054    void'(this.notify.configure(ACT_COMPLETED));
00055    void'(this.notify.configure(ACT_REMOVED));
00056    void'(this.notify.configure(LOCKED));
00057    void'(this.notify.configure(UNLOCKED));
00058    void'(this.notify.configure(GRABBED));
00059    void'(this.notify.configure(UNGRABBED));
00060    void'(this.notify.configure(RECORDING, vmm_notify::ON_OFF));
00061    void'(this.notify.configure(PLAYBACK, vmm_notify::ON_OFF));
00062    void'(this.notify.configure(PLAYBACK_DONE, vmm_notify::ON_OFF));
00063 
00064    if (full <= 0) full = 1;
00065    if (empty < 0 || empty > full) empty = full;
00066 
00067    this.full = full;
00068    this.empty = empty;
00069    this.is_sunk  = 0;
00070 
00071    this.active = null;
00072    this.active_status = INACTIVE;
00073    this.tee_on = 0;
00074    this.downstream = null;
00075    this.locks  = 2'b00;
00076 
00077    this.full_chan = 0;
00078    this.notify.indicate(EMPTY);
00079 
00080    this.iterator = 0;
00081    this.is_put = 0;
00082    this.is_playback = 0;
00083    this.record_fp = -1;
00084 
00085    //
00086    // Thread to manage connection requests
00087    //
00088    fork: connection_requests
00089       while (1)
00090       begin : new_while_loop
00091          vmm_data data = null;
00092 
00093          // Broken connection?
00094          if (this.downstream != null)
00095          begin
00096             if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV))
00097             begin
00098                string txt;
00099                txt = {"Channel connection established: ",
00100                       this.log.get_name(),
00101                       "(", this.log.get_instance(), ") -> ",
00102                       this.downstream.log.get_name(),
00103                       "(", this.downstream.log.get_instance(), ")"};
00104 
00105                void'(this.log.text(txt));
00106                this.log.end_msg();
00107             end // if debug level
00108 
00109             // Fork the data mover thread
00110             fork
00111                while (1)
00112                begin : inner_while_loop
00113                   // Simple blocking interface
00114                   data = null;
00115                   this.peek(data);
00116                   this.downstream.put(data);
00117                   this.get(data);
00118                end // inner_while_loop
00119             join_none
00120          end // if downstream != null
00121 
00122          // Wait for new connection requests
00123          @this.new_connection;
00124 
00125          // Stop the data mover thread
00126          disable fork;
00127 
00128          // Remove any datum that was forwarded
00129          // to the downstream channel but not removed
00130          // from this channel because of the blocking
00131          // model. Otherwise, the same datum will be
00132          // obtained from this channel twice.
00133          if (data != null) this.get(data);
00134      end // while (1)
00135    join_none // connection_requests
00136 endfunction : new
00137 
00138 
00139 function void vmm_channel::reconfigure(int   full=-1,
00140                                        int   empty=-1,
00141                                        logic fill_as_bytes=1'bx);
00142    if (full < 0) full = this.full;
00143    if (full == 0) full = 1;
00144    if (empty < 0) empty = this.empty;
00145 
00146    if (full < empty)
00147    begin
00148       `vmm_error(this.log, "Cannot reconfigure channel with FULL < EMPTY");
00149       return;
00150    end
00151 
00152    this.full = full;
00153    this.empty = empty;
00154 
00155    if (this.level() >= this.full)
00156    begin
00157       this.full_chan = 1;
00158       this.notify.indicate(FULL);
00159       this.notify.reset(EMPTY);
00160    end
00161    else if (this.level() <= this.empty)
00162    begin
00163       this.full_chan = 0;
00164       -> this.item_taken;
00165       this.notify.indicate(EMPTY);
00166       this.notify.reset(FULL);
00167    end
00168    else
00169    begin
00170       this.full_chan = 0;
00171       -> this.item_taken;
00172       this.notify.reset(EMPTY);
00173       this.notify.reset(FULL);
00174    end
00175 endfunction: reconfigure
00176 
00177 
00178 function int unsigned vmm_channel::full_level();
00179    full_level = this.full;
00180 endfunction: full_level
00181 
00182 
00183 function int unsigned vmm_channel::empty_level();
00184    empty_level = this.empty;
00185 endfunction: empty_level
00186 
00187 
00188 function int unsigned vmm_channel::level();
00189    level = this.data.size() + ((this.active == null) ? 0 : 1);
00190 endfunction: level
00191 
00192 
00193 function int unsigned vmm_channel::size();
00194    size = this.data.size() + ((this.active == null) ? 0 : 1);
00195 endfunction : size
00196 
00197 
00198 function bit vmm_channel::is_full();
00199    is_full = full_chan;
00200 endfunction : is_full
00201 
00202 
00203 function void vmm_channel::flush();
00204    vmm_data obj;
00205    if (this.downstream != null)
00206       this.downstream.flush();
00207 
00208 `ifdef VCS2006_06
00209    // Work-around required by VCS 2006.06
00210    // but IEEE 1800-2009 compliant
00211    this.data.delete();
00212    this.tee_data.delete();
00213 `else
00214    // Works in VCS2008.03 or later
00215    // IEEE 1800-2005 compliant
00216    this.data = '{};
00217    this.tee_data = '{};
00218 `endif
00219    full_chan = 0;
00220    this.active = null;
00221    this.active_status = INACTIVE ;
00222    -> this.item_taken;
00223    this.notify.reset(FULL);
00224    this.notify.indicate(EMPTY);
00225 endfunction: flush
00226 
00227 `ifndef VMM_GRAB_DISABLED
00228 function void vmm_channel::reset_grabbers();
00229  `ifdef VCS2006_06
00230    // Work-around required by VCS 2006.06
00231    // but IEEE 1800-2009 compliant
00232    this.grab_owners.delete();
00233 `else
00234    // Works in VCS2008.03 or later
00235    // IEEE 1800-2005 compliant
00236    this.grab_owners = '{};
00237 `endif
00238 endfunction: reset_grabbers
00239 `endif
00240 
00241 function void vmm_channel::sink();
00242    this.flush();
00243    this.is_sunk = 1;
00244 endfunction: sink
00245 
00246 
00247 function void vmm_channel::flow();
00248    this.is_sunk = 0;
00249 endfunction: flow
00250 
00251 
00252 function void vmm_channel::reset();
00253    this.flush();
00254 `ifndef VMM_GRAB_DISABLED
00255    this.reset_grabbers();
00256 `endif
00257 endfunction: reset
00258 
00259 
00260 function void vmm_channel::lock(bit [1:0] who);
00261    this.locks |= who;
00262    this.notify.indicate(LOCKED);
00263 endfunction: lock
00264 
00265 
00266 function void vmm_channel::unlock(bit [1:0] who);
00267    this.locks &= ~who;
00268    this.notify.indicate(UNLOCKED);
00269    // May cause a consumer or producer to unblock
00270    -> this.item_taken;
00271    -> this.item_added;
00272 endfunction: unlock
00273 
00274 
00275 function bit vmm_channel::is_locked(bit [1:0] who);
00276    is_locked = (this.locks & who) ? 1 : 0;
00277 endfunction: is_locked
00278 
00279 
00280 `ifndef VMM_GRAB_DISABLED
00281 function bit vmm_channel::check_grab_owners(`VMM_SCENARIO grabber);
00282    `VMM_SCENARIO current_parent;
00283 
00284    current_parent = grabber;
00285 
00286    while (current_parent != null) begin
00287       if (this.grab_owners[0] == current_parent) begin
00288          return 1;
00289       end
00290       current_parent = current_parent.get_parent_scenario();
00291    end
00292    return 0;
00293 endfunction: check_grab_owners
00294 
00295 
00296 function bit vmm_channel::check_all_grab_owners(`VMM_SCENARIO grabber);
00297    foreach (this.grab_owners[i]) begin
00298       if (grabber == this.grab_owners[i]) return 1;
00299    end
00300    return 0;
00301 endfunction: check_all_grab_owners
00302 `endif
00303 
00304 
00305 function bit vmm_channel::try_grab(`VMM_SCENARIO grabber);
00306 `ifndef VMM_GRAB_DISABLED
00307    if (this.notify.is_on(PLAYBACK)) begin
00308       `vmm_warning(this.log, "Cannot grab a channel during playback");
00309       return 0;
00310    end
00311 
00312    if (this.check_all_grab_owners(grabber)) begin
00313       `vmm_warning(this.log, "Cannot grab a channel that is already grabbed by you");
00314       return 0;
00315    end
00316    
00317    if ((this.grab_owners.size() == 0) ||
00318        (this.check_grab_owners(grabber))) begin
00319         this.grab_owners.push_front(grabber);
00320         this.notify.indicate(GRABBED);
00321       return 1;
00322    end
00323 
00324    return 0;
00325 `else
00326    `vmm_error(this.log, "grab/ungrab functionality is not enabled");
00327 `endif
00328 endfunction: try_grab
00329 
00330 
00331 task vmm_channel::grab(`VMM_SCENARIO grabber);
00332 `ifndef VMM_GRAB_DISABLED
00333    if (this.notify.is_on(PLAYBACK)) begin
00334        this.notify.wait_for(vmm_channel::PLAYBACK_DONE);
00335    end
00336 
00337    if (this.grab_owners.size()==0) begin
00338       this.grab_owners.push_front(grabber);
00339       this.notify.indicate(GRABBED);
00340    end
00341    else begin
00342       if (this.check_all_grab_owners(grabber)) begin
00343          `vmm_error(this.log, "Cannot grab a channel that is already grabbed by you");
00344       end
00345       else if (this.check_grab_owners(grabber)) begin
00346          this.grab_owners.push_front(grabber);
00347          this.notify.indicate(GRABBED);
00348       end
00349       else begin
00350          this.notify.wait_for(vmm_channel::UNGRABBED);
00351          this.grab(grabber);
00352       end
00353    end
00354 `else
00355    `vmm_error(this.log, "grab/ungrab functionality is not enabled");
00356 `endif
00357 endtask: grab
00358 
00359 
00360 function void vmm_channel::ungrab(`VMM_SCENARIO grabber);
00361 `ifndef VMM_GRAB_DISABLED
00362    if (this.grab_owners.size()==0) begin
00363       `vmm_error(this.log, "Cannot ungrab a channel that is not grabbed!");
00364    end
00365    else begin
00366       if (grabber == this.grab_owners[0]) begin
00367          void'(this.grab_owners.pop_front());
00368          this.notify.indicate(UNGRABBED);
00369       end
00370       else begin
00371          `vmm_error(this.log, "Cannot ungrab a channel that you did not grab!");
00372       end
00373    end
00374 `else
00375    `vmm_error(this.log, "grab/ungrab functionality is not enabled");
00376 `endif
00377 endfunction: ungrab
00378 
00379 
00380 function bit vmm_channel::is_grabbed();
00381 `ifndef VMM_GRAB_DISABLED
00382    return (this.grab_owners.size() > 0);
00383 `else
00384    `vmm_error(this.log, "grab/ungrab functionality is not enabled");
00385    return 0;
00386 `endif
00387 endfunction: is_grabbed
00388 
00389 
00390 function void vmm_channel::display(string prefix="");
00391    $display(this.psdisplay(prefix));
00392 endfunction: display
00393 
00394 
00395 function string vmm_channel::psdisplay(string prefix="");
00396    $sformat(psdisplay, "%sChannel %s(%s): Level = %0d of %0d (empty=%0d)",
00397             prefix, this.log.get_name(), this.log.get_instance(),
00398             this.level(), this.full, this.empty);
00399    case (this.locks)
00400       SOURCE+SINK : psdisplay = {psdisplay, " [src+snk locked]"};
00401       SOURCE:       psdisplay = {psdisplay, " [src locked]"};
00402       SINK:         psdisplay = {psdisplay, " [snk locked]"};
00403    endcase
00404    if (this.is_sunk) psdisplay = {psdisplay, " (sunk)"};
00405 `ifndef VMM_GRAB_DISABLED
00406    if (this.is_grabbed()) psdisplay = {psdisplay, " (grabbed by %s)",
00407                                        this.grab_owners[0].scenario_name(0)};
00408 `endif
00409 
00410    foreach(this.data[i]) begin
00411       $sformat(psdisplay, "%s\n%s", psdisplay, this.data[i].psdisplay(`vmm_sformatf("%s   [%0d] ",
00412                                                                                     prefix, i)));
00413    end
00414 endfunction: psdisplay
00415 
00416 
00417 `ifndef VMM_GRAB_DISABLED
00418 task vmm_channel::put(vmm_data obj,
00419                       int offset=-1,
00420                       `VMM_SCENARIO grabber=null);
00421    if (obj == null)
00422    begin
00423       `vmm_error(this.log, "Attempted to put null instance into channel");
00424       return;
00425    end // if obj == null
00426 
00427 `ifndef VMM_GRAB_DISABLED
00428    this.block_producer(grabber);
00429    this.is_put = 1'b1;
00430    this.sneak(obj, offset, grabber);
00431    this.is_put = 1'b0;
00432    this.block_producer(grabber);
00433 `else
00434    this.block_producer();
00435    this.is_put = 1'b1;
00436    this.sneak(obj, offset);
00437    this.is_put = 1'b0;
00438    this.block_producer();
00439 `endif
00440 endtask: put
00441 `endif
00442 
00443 `ifndef VMM_GRAB_DISABLED
00444 task vmm_channel::XputX(vmm_data obj,
00445                        int offset=-1,
00446                        `VMM_SCENARIO grabber=null);
00447 `else
00448 task vmm_channel::XputX(vmm_data obj,
00449                        int offset=-1);
00450 `endif
00451    while (this.full_chan)
00452       @this.item_taken;
00453    this.is_playback = 1'b1;
00454 `ifndef VMM_GRAB_DISABLED
00455     this.sneak(obj, offset, grabber);
00456 `else
00457    this.sneak(obj, offset);
00458 `endif
00459    this.is_playback = 1'b0;
00460    while (this.full_chan)
00461       @this.item_taken;
00462 
00463 endtask: XputX
00464 
00465 `ifndef VMM_GRAB_DISABLED
00466 function void vmm_channel::sneak(vmm_data obj,
00467                                  int offset=-1,
00468                                  `VMM_SCENARIO grabber=null);
00469 `else
00470 function void vmm_channel::sneak(vmm_data obj,
00471                                  int offset=-1);
00472 `endif
00473    string txt;
00474    time diff_time;
00475 
00476    if (obj == null)
00477    begin
00478       `vmm_error(this.log, "Attempted to sneak null instance into channel");
00479       return;
00480    end // obj == null
00481 
00482 `ifndef VMM_GRAB_DISABLED
00483    if (this.grab_owners.size() &&
00484        (this.check_grab_owners(grabber) == 0)) begin
00485       `vmm_error(this.log, "Attempted to sneak in a grabbed channel from a scenario that is not a current grabber");
00486       return;
00487    end
00488 `endif
00489 
00490    if (this.is_sunk) return;
00491 
00492    if (offset == -1 || (offset == 0 && this.data.size() == 0))
00493    begin
00494       this.data.push_back(obj);
00495    end
00496    else
00497    begin
00498       int idx = this.index(offset);
00499       if (idx < 0) return;
00500 
00501       this.data.insert(offset, obj);
00502    end // if offset
00503 
00504    if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV))
00505    begin
00506      $sformat(txt, "New instance added to channel @%0d (level %0d of %0d/%0d)\n%s",
00507               offset, this.level(), this.full, this.empty,
00508               obj.psdisplay("    "));
00509       void'(this.log.text(txt));
00510       this.log.end_msg();
00511    end // if dbg
00512 
00513    this.notify.indicate(PUT, obj);
00514 
00515    if (this.level() >= this.full)
00516    begin
00517       this.full_chan = 1;
00518       this.notify.indicate(FULL);
00519    end
00520 
00521    if (this.level() > this.empty)
00522    begin
00523       this.notify.reset(EMPTY);
00524    end
00525    //recording
00526    if(this.notify.is_on(RECORDING))
00527    begin
00528 
00529      diff_time = $time - this.last_record_time;
00530      if(this.is_put == 1'b0)
00531        this.Xrecord_to_fileX(8'h02,offset,diff_time);
00532      else
00533        this.Xrecord_to_fileX(8'h01,offset,diff_time);
00534 
00535      //save object
00536      obj.save(this.record_fp);
00537      this.last_record_time = $time;
00538    end
00539 
00540    //Playback
00541    if(this.notify.is_on(PLAYBACK) && !this.is_playback)
00542    begin
00543      `vmm_warning(this.log,"vmm_channel::sneak accessed by source while playback is ON");
00544    end
00545 
00546    -> this.item_added;
00547 endfunction: sneak
00548 
00549 
00550 function vmm_data vmm_channel::unput(int offset=-1);
00551 
00552    time diff_time;
00553 
00554    if (this.size() == 0)
00555    begin
00556       `vmm_error(this.log, "Cannot unput from an empty channel");
00557       return null;
00558    end // if size == 0
00559 
00560    if (offset == -1)
00561    begin
00562      unput = this.data.pop_back();
00563    end
00564    else
00565    begin
00566      int idx = this.index(offset);
00567      if (idx < 0)
00568      begin
00569         unput = null;
00570      end
00571      else
00572      begin
00573         unput = this.data[idx];
00574         this.data.delete(idx);
00575      end
00576    end // if offset != -1
00577 
00578    if (unput != null) begin
00579       this.notify.indicate(GOT, unput);
00580       if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV))
00581       begin
00582          string txt;
00583          $sformat(txt, "Instance unput from channel @%0d (level %0d of %0d/%0d)\n%s",
00584                   offset, this.level(), this.full, this.empty,
00585                   unput.psdisplay("    "));
00586          void'(this.log.text(txt));
00587          this.log.end_msg();
00588       end // if dbg_lvl
00589 
00590       //recording
00591       if(this.notify.is_on(RECORDING))
00592       begin
00593         diff_time = $time - this.last_record_time;
00594         this.Xrecord_to_fileX(8'h03,offset,diff_time);
00595         this.last_record_time = $time;
00596       end
00597 
00598    end
00599 
00600    this.unblock_producer();
00601 endfunction: unput
00602 
00603 function void vmm_channel::Xrecord_to_fileX(bit [7:0] op_code,
00604                                             int offset,
00605                                             time diff_time);
00606 
00607    bit [7:0] bytes[13];
00608    bit [63:0] bit_conv;
00609 
00610    bytes[0] = op_code;
00611 
00612    bit_conv[31:0] = offset;
00613    bytes[1] = bit_conv[7:0];
00614    bytes[2] = bit_conv[15:8];
00615    bytes[3] = bit_conv[23:16];
00616    bytes[4] = bit_conv[31:24];
00617 
00618    bit_conv[63:0] = diff_time;
00619    bytes[5] = bit_conv[7:0];
00620    bytes[6] = bit_conv[15:8];
00621    bytes[7] = bit_conv[23:16];
00622    bytes[8] = bit_conv[31:24];
00623    bytes[9] = bit_conv[39:32];
00624    bytes[10] = bit_conv[47:40];
00625    bytes[11] = bit_conv[55:48];
00626    bytes[12] = bit_conv[63:56];
00627 
00628    //<1byte type><4byte offset><8byte time>
00629    foreach(bytes[i]) begin
00630      $fwrite(this.record_fp, "%c", bytes[i]);
00631    end
00632    //space
00633    if(op_code == 8'h03)
00634      $fwrite(this.record_fp, "\n");
00635    else
00636      $fwrite(this.record_fp, " ");
00637 
00638 endfunction: Xrecord_to_fileX
00639 
00640 task vmm_channel::get1(output vmm_data obj,
00641                        input  int      offset=0);
00642    if (offset == 0)
00643    begin
00644       obj = this.data.pop_front();
00645    end
00646    else
00647    begin
00648       int idx = this.index(offset);
00649       if (idx < 0)
00650       begin
00651          obj = null;
00652       end
00653       else
00654       begin
00655          obj = this.data[idx];
00656          this.data.delete(idx);
00657       end // else if idx < 0
00658     end // else if offset
00659 
00660    if (obj != null)
00661    begin
00662       this.notify.indicate(GOT, obj);
00663       if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV))
00664       begin
00665          string txt;
00666          $sformat(txt, "New instance removed from channel @%0d (level %0d of %0d/%0d)\n%s",
00667                   offset, this.level(), this.full, this.empty,
00668                   obj.psdisplay("    "));
00669          void'(this.log.text(txt));
00670          this.log.end_msg();
00671       end // if dbg_lvl
00672 
00673       if (this.tee_on)
00674       begin
00675          this.tee_data.push_back(obj);
00676          -> this.teed;
00677       end // tee_on
00678 
00679 `ifdef VMM_SB_DS_IN_STDLIB
00680       foreach (this._vmm_sb_ds[i]) begin
00681          if (this._vmm_sb_ds[i].is_in) begin
00682             this._vmm_sb_ds[i].sb.insert(obj);
00683          end
00684 
00685          if (this._vmm_sb_ds[i].is_out) begin
00686             case (this._vmm_sb_ds[i].order)
00687               vmm_sb_ds::IN_ORDER:
00688                 this._vmm_sb_ds[i].sb.expect_in_order(obj);
00689 
00690               vmm_sb_ds::WITH_LOSSES: begin
00691                  vmm_data p;
00692                  vmm_data losses[];
00693                  this._vmm_sb_ds[i].sb.expect_with_losses(obj, p, losses);
00694               end
00695 
00696               vmm_sb_ds::OUT_ORDER:
00697                 this._vmm_sb_ds[i].sb.expect_out_of_order(obj);
00698 
00699             endcase
00700          end
00701       end
00702 `endif
00703    end // if obj != null
00704 endtask: get1
00705 
00706 
00707 task vmm_channel::get(output vmm_data obj,
00708                       input  int      offset=0);
00709    this.block_consumer();
00710    this.get1(obj, offset);
00711    this.unblock_producer();
00712 endtask: get
00713 
00714 task vmm_channel::peek(output vmm_data obj,
00715                        input  int      offset=0);
00716    string txt;
00717    int    idx;
00718 
00719    this.block_consumer();
00720 
00721    idx = this.index(offset);
00722    if (idx < 0)
00723    begin
00724       obj = null;
00725    end
00726    else
00727    begin
00728       obj = this.data[idx];
00729    end
00730 
00731    if (obj != null)
00732    begin
00733       this.notify.indicate(PEEKED, obj);
00734       if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV))
00735       begin
00736          $sformat(txt, "New instance peeked from channel @%0d (level %0d of %0d/%0d)\n%s",
00737                   offset, this.level(), this.full, this.empty,
00738                   obj.psdisplay("    "));
00739          void'(this.log.text(txt));
00740          this.log.end_msg();
00741       end // if dbg_lvl
00742    end // obj != null
00743 
00744    this.unblock_producer();
00745 endtask: peek
00746 
00747 
00748 task vmm_channel::activate(output vmm_data obj,
00749                            input  int      offset=0);
00750    string txt;
00751 
00752    // Empty the active slot
00753    if (active != null)
00754       void'(this.remove());
00755 
00756    while (this.size() == 0)
00757       @this.item_added;
00758 
00759    if (offset == 0)
00760    begin
00761       obj = this.data.pop_front();
00762    end
00763    else
00764    begin
00765       int idx = this.index(offset);
00766       if (idx < 0)
00767       begin
00768         obj = null;
00769       end
00770       else
00771       begin
00772         obj = this.data[idx];
00773         this.data.delete(idx);
00774       end
00775    end // else if offset == 0
00776 
00777 
00778    if (obj != null)
00779    begin
00780       this.active = obj;
00781       this.active_status = PENDING ;
00782       this.notify.indicate(ACTIVATED, obj);
00783       this.notify.indicate(PEEKED, obj);
00784 
00785       if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV))
00786       begin
00787          $sformat(txt, "New instance activated from channel @%0d (level %0d of %0d/%0d)\n%s",
00788                   offset, this.level(), this.full, this.empty,
00789                   obj.psdisplay("    "));
00790          void'(this.log.text(txt));
00791          this.log.end_msg();
00792       end // if dbg_lvl
00793 
00794       if (this.tee_on)
00795       begin
00796          this.tee_data.push_back(obj);
00797          -> this.teed;
00798       end
00799    end // if obj != null
00800 endtask: activate
00801 
00802 
00803 function vmm_data vmm_channel::active_slot();
00804    active_slot = this.active;
00805 endfunction: active_slot
00806 
00807 
00808 function vmm_data vmm_channel::start();
00809    if (this.active == null)
00810    begin
00811       `vmm_fatal(this.log, "Cannot start() without prior activate()");
00812       return null;
00813    end // if active == null
00814 
00815    if (this.active_status == STARTED)
00816    begin
00817       `vmm_warning(this.log, "Active slot already start()'ed");
00818    end // if STARTED
00819 
00820    this.active_status = STARTED;
00821    this.notify.indicate(ACT_STARTED, this.active);
00822    this.active.notify.indicate(vmm_data::STARTED);
00823    start = this.active;
00824 endfunction: start
00825 
00826 
00827 function vmm_data vmm_channel::complete(vmm_data status=null);
00828    if (this.active == null)
00829    begin
00830       `vmm_fatal(this.log, "Cannot complete() without prior activate()");
00831       return null;
00832    end
00833    if (this.active_status != STARTED)
00834    begin
00835       `vmm_warning(this.log, "complete() called without start()");
00836    end
00837 
00838    this.active_status = COMPLETED;
00839    this.notify.indicate(ACT_COMPLETED, this.active);
00840    this.active.notify.indicate(vmm_data::ENDED, status);
00841    complete = this.active;
00842 endfunction: complete
00843 
00844 
00845 function vmm_data vmm_channel::remove();
00846    if (this.active == null)
00847    begin
00848       `vmm_fatal(this.log, "Cannot remove() without prior activate()");
00849       return null;
00850    end
00851 
00852    // OK to remove if not started or completed
00853    if (this.active_status == STARTED)
00854    begin
00855       `vmm_warning(this.log, "remove() called without complete()");
00856    end
00857 
00858    this.notify.indicate(ACT_REMOVED, this.active);
00859    this.notify.indicate(GOT, this.active);
00860    if (this.active_status != COMPLETED)
00861    begin
00862       this.active.notify.indicate(vmm_data::ENDED);
00863    end
00864    this.active_status = INACTIVE;
00865    remove = this.active;
00866    this.active = null;
00867 
00868    this.unblock_producer();
00869 endfunction: remove
00870 
00871 
00872 function vmm_channel::active_status_e vmm_channel::status();
00873    status = this.active_status;
00874 endfunction: status
00875 
00876 
00877 function bit vmm_channel::tee_mode(bit is_on);
00878    string txt;
00879    if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV))
00880    begin
00881       $sformat(txt, "tee branch turned %0s", (is_on) ? "ON" : "OFF");
00882       void'(this.log.text(txt));
00883       this.log.end_msg();
00884    end
00885 
00886    tee_mode = this.tee_on;
00887    this.tee_on = is_on;
00888 endfunction: tee_mode
00889 
00890 
00891 task vmm_channel::tee(output vmm_data obj);
00892    string txt;
00893    while (this.tee_data.size() == 0)
00894       @this.teed;
00895 
00896    obj = this.tee_data.pop_front();
00897 
00898    if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV))
00899    begin
00900       $sformat(txt, "New instance teed from channel (level %0d of %0d/%0d)\n%s",
00901                   this.level(), this.full, this.empty,
00902                   obj.psdisplay("    "));
00903       void'(this.log.text(txt));
00904       this.log.end_msg();
00905    end
00906 endtask: tee
00907 
00908 
00909 function void vmm_channel::connect(vmm_channel downstream);
00910    // Do not disrupt an already-established connection
00911    if (this.downstream == downstream) return;
00912 
00913    // Indicate a new connection
00914    this.downstream = downstream;
00915    -> this.new_connection;
00916 endfunction: connect
00917 
00918 
00919 function vmm_data vmm_channel::for_each(bit reset=0);
00920    if (reset) this.iterator = 0;
00921    else this.iterator++;
00922 
00923    if (this.iterator >= this.data.size()) for_each = null;
00924    else for_each = this.data[this.iterator];
00925 endfunction: for_each
00926 
00927 
00928 function int unsigned vmm_channel::for_each_offset();
00929    for_each_offset = this.iterator;
00930 endfunction: for_each_offset
00931 
00932 
00933 function bit vmm_channel::record(string filename);
00934    if (filename == "")
00935    begin
00936       if(this.notify.is_on(RECORDING))
00937       begin
00938         $fclose(this.record_fp);
00939         this.notify.reset(RECORDING);
00940         return 1;
00941       end
00942       else
00943       begin
00944         `vmm_warning(this.log,"A valid 'filename' must be specified to start recording. vmm_channel::record() is ignoring call");
00945         return 0;
00946       end
00947    end
00948    else
00949    begin
00950      if(this.notify.is_on(RECORDING))
00951      begin
00952        `vmm_warning(this.log,"vmm_channel::record() is already started. Ignoring call");
00953        return 0;
00954      end
00955      else
00956      begin
00957        this.record_fp = $fopen(filename,"wb");
00958        if(this.record_fp == 0)
00959        begin
00960          `vmm_error(this.log,$psprintf("vmm_channel::record() is not able to open file: %s for recording",filename));
00961          this.record_fp = -1;
00962          return 0;
00963        end
00964        this.notify.indicate(RECORDING);
00965        this.last_record_time = $time;
00966        return 1;
00967      end
00968    end
00969 
00970 endfunction: record
00971 
00972 `ifndef VMM_GRAB_DISABLED
00973 task vmm_channel::playback(output bit      success,
00974                            input  string   filename,
00975                            input  vmm_data factory,
00976                            input  bit      metered=0,
00977                            `VMM_SCENARIO   grabber=null);
00978 `else
00979 task vmm_channel::playback(output bit      success,
00980                            input  string   filename,
00981                            input  vmm_data factory,
00982                            input  bit      metered=0);
00983 `endif
00984    int playback_fp;
00985    bit [7:0] bytes[14];
00986    bit [7:0] op_code;
00987    bit [63:0] bit_conv;
00988    int offset;
00989    time time_delay;
00990 
00991 `ifndef VMM_GRAB_DISABLED
00992    if (this.grab_owners.size() &&
00993       (this.check_grab_owners(grabber) == 0)) begin
00994       `vmm_error(this.log, "Cannot playback on a grabbed channel");
00995       return;
00996    end
00997 `endif
00998 
00999    this.notify.indicate(PLAYBACK);
01000 
01001    if(filename == "")
01002    begin
01003      success = 0;
01004      `vmm_error(this.log,"vmm_channel::playback() found null on input argument filename");
01005      this.notify.reset(PLAYBACK);
01006      this.notify.indicate(PLAYBACK_DONE);
01007      return;
01008    end
01009 
01010    if(factory == null)
01011    begin
01012      success = 0;
01013      `vmm_error(this.log,"vmm_channel::playback() found null on input argument factory");
01014      this.notify.reset(PLAYBACK);
01015      this.notify.indicate(PLAYBACK_DONE);
01016      return;
01017    end
01018 
01019    playback_fp = $fopen(filename,"rb");
01020 
01021    if(playback_fp == 0)
01022    begin
01023      success = 0;
01024      `vmm_error(this.log,$psprintf("vmm_channel::playback() can not open file %s: file does not exist",filename));
01025      this.notify.reset(PLAYBACK);
01026      this.notify.indicate(PLAYBACK_DONE);
01027      return;
01028    end
01029 
01030    if($feof(playback_fp))
01031    begin
01032      $fclose(playback_fp);
01033      success = 0;
01034      `vmm_error(this.log,$psprintf("vmm_channel::playback() file %s is empty",filename));
01035      this.notify.reset(PLAYBACK);
01036      this.notify.indicate(PLAYBACK_DONE);
01037      return;
01038    end
01039 
01040    success = 1;
01041 
01042    this.lock(SOURCE);
01043 
01044    while(!$feof(playback_fp))
01045    begin
01046      foreach(bytes[i])
01047      begin
01048        int c = $fgetc(playback_fp);
01049        bytes[i] = c;
01050      end
01051 
01052      if(bytes[0] == 8'hff)
01053        break;
01054 
01055      op_code    = bytes[0];
01056      offset     = {bytes[4],bytes[3],bytes[2],bytes[1]};
01057      time_delay = {bytes[12],bytes[11],bytes[10],bytes[9],bytes[8],bytes[7],bytes[6],bytes[5]};
01058 
01059      if(op_code == 8'h01)
01060      begin
01061        if(!factory.load(playback_fp))
01062        begin
01063          `vmm_error(this.log,"vmm_data::load() failed");
01064          success = 0;
01065          break;
01066        end
01067 
01068        if(metered)
01069        begin
01070          #time_delay;
01071          this.is_playback = 1'b1;
01072 `ifndef VMM_GRAB_DISABLED
01073          this.sneak(factory.copy(), offset, grabber);
01074 `else
01075          this.sneak(factory.copy(), offset);
01076 `endif
01077          this.is_playback = 1'b0;
01078        end
01079        else
01080 `ifndef VMM_GRAB_DISABLED
01081          this.XputX(factory.copy(), offset, grabber);
01082 `else
01083          this.XputX(factory.copy(), offset);
01084 `endif
01085      end
01086      else if(op_code == 8'h02)
01087      begin
01088        if(!factory.load(playback_fp))
01089        begin
01090          `vmm_error(this.log,"vmm_data::load() failed");
01091          success = 0;
01092          break;
01093        end
01094        if(metered)
01095          #time_delay;
01096 
01097        this.is_playback = 1'b1;
01098 `ifndef VMM_GRAB_DISABLED
01099        this.sneak(factory.copy(),offset, grabber);
01100 `else
01101        this.sneak(factory.copy(),offset);
01102 `endif
01103        this.is_playback = 1'b0;
01104      end
01105      else if(op_code == 8'h03)
01106      begin
01107        if(metered)
01108          #time_delay;
01109        if(this.unput(offset) == null)
01110          `vmm_warning(this.log,"vmm_channel::playback() found improper offset for unput operation");
01111      end
01112      else
01113      begin
01114        `vmm_error(this.log,$psprintf("vmm_channel::playback() file %s is corrupted",filename));
01115        success = 0;
01116        break;
01117      end
01118    end
01119 
01120    $fclose(playback_fp);
01121    this.unlock(SOURCE);
01122    this.notify.reset(PLAYBACK);
01123    this.notify.indicate(PLAYBACK_DONE);
01124 
01125 endtask: playback
01126 
01127 
01128 function int vmm_channel::index(int offset);
01129    string txt;
01130    index = offset;
01131    if (offset < 0) index += this.data.size() + offset + 1;
01132    if (index < 0 || index >= this.data.size())
01133    begin
01134       if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV))
01135       begin
01136          $sformat(txt, "Invalid offset %0d specified. Not in {0:%0d}.\n",
01137                   offset, this.data.size()-1);
01138          void'(this.log.text(txt));
01139          this.log.end_msg();
01140       end
01141       index = -1;
01142    end
01143 endfunction: index
01144 
01145 
01146 `ifndef VMM_GRAB_DISABLED
01147 task vmm_channel::block_producer(`VMM_SCENARIO grabber);
01148     while (this.full_chan || this.is_locked(SOURCE) ||
01149           (this.grab_owners.size() &&
01150            (this.check_grab_owners(grabber) == 0))) begin
01151        fork
01152           begin
01153              if (this.grab_owners.size() &&
01154                  (this.check_grab_owners(grabber) == 0)) begin
01155                 this.notify.wait_for(vmm_channel::UNGRABBED);
01156              end
01157           end
01158           begin
01159              if(this.full_chan || this.is_locked(SOURCE)) begin
01160                 @this.item_taken;
01161              end
01162           end
01163        join
01164    end
01165 `else
01166 task vmm_channel::block_producer();
01167     while (this.full_chan || this.is_locked(SOURCE))
01168        @this.item_taken;
01169 `endif
01170 endtask : block_producer
01171 
01172 
01173 task vmm_channel::block_consumer();
01174    while (this.size() == 0 || this.is_locked(SINK))
01175       @this.item_added;
01176 endtask: block_consumer
01177 
01178 
01179 function void vmm_channel::unblock_producer();
01180    if (this.level() <= this.empty)
01181    begin
01182       this.full_chan = 0;
01183       this.notify.indicate(EMPTY);
01184    end
01185 
01186    if (this.level() < this.full)
01187    begin
01188       this.notify.reset(FULL);
01189    end
01190 
01191    -> this.item_taken;
01192 endfunction: unblock_producer
01193 
01194 
01195 function vmm_xactor vmm_channel::get_producer();
01196    get_producer = this.producer;
01197 endfunction: get_producer
01198 
01199 
01200 function vmm_xactor vmm_channel::get_consumer();
01201    get_consumer = this.consumer;
01202 endfunction: get_consumer
01203 
01204 
01205 function void vmm_channel::set_producer(vmm_xactor producer);
01206    if (producer == null && this.producer == null) begin
01207       `vmm_error(this.log, "Attempted to set null producer");
01208       return;
01209    end
01210 
01211    if (this.producer == producer) return;
01212    
01213    if (this.producer != null) begin
01214       if(producer != null) begin
01215 	 `vmm_warning(this.log, "Producer is already set");
01216       end
01217       foreach(this.producer.Xoutput_chansX[i]) begin  
01218 	 if (this.producer.Xoutput_chansX[i] == this) begin
01219 	    this.producer.Xoutput_chansX.delete(i);
01220     	    break;
01221 	 end
01222       end
01223    end
01224    this.producer = producer;
01225    if(producer != null) begin
01226      this.producer.Xoutput_chansX.push_back(this);
01227    end
01228 endfunction: set_producer
01229 
01230 
01231 function void vmm_channel::set_consumer(vmm_xactor consumer);
01232    if (consumer == null && this.consumer == null) begin
01233       `vmm_error(this.log, "Attempted to set null consumer");
01234       return;
01235    end
01236    
01237    if (this.consumer == consumer) return;
01238 
01239    if (this.consumer != null) begin
01240       if (consumer != null) begin
01241 	`vmm_warning(this.log, "Consumer is already set");
01242       end
01243       foreach(this.consumer.Xinput_chansX[i]) begin
01244 	 if (this.consumer.Xinput_chansX[i] == this) begin
01245     	    this.consumer.Xinput_chansX.delete(i);
01246     	    break;
01247 	 end
01248       end
01249    end
01250    this.consumer = consumer;
01251    if (consumer != null) begin
01252       this.consumer.Xinput_chansX.push_back(this);
01253    end
01254 endfunction: set_consumer
01255 
01256 
01257 function void vmm_channel::kill();
01258    if (this.consumer != null) begin
01259       foreach(this.consumer.Xinput_chansX[i]) begin
01260          if(this.consumer.Xinput_chansX[i] == this) begin
01261     	    this.consumer.Xinput_chansX.delete(i);
01262     	    break;
01263          end
01264       end
01265    end
01266 
01267    if (this.producer != null) begin
01268       foreach(this.producer.Xoutput_chansX[i]) begin  
01269          if (this.producer.Xoutput_chansX[i] == this) begin
01270 	    this.producer.Xoutput_chansX.delete(i);
01271     	    break;
01272          end
01273       end
01274    end
01275 
01276    this.downstream = null;
01277    -> this.new_connection;
01278 
01279    this.producer = null;
01280    this.consumer = null;
01281 
01282    if (!this.shared_log) this.log.kill();
01283 endfunction: kill   
01284 
01285 
01286 
01287 
01288 
01289 `ifdef VMM_SB_DS_IN_STDLIB
01290 function void vmm_channel::register_vmm_sb_ds(vmm_sb_ds             sb,
01291                                               vmm_sb_ds::kind_e     kind,
01292                                               vmm_sb_ds::ordering_e order=vmm_sb_ds::IN_ORDER);
01293    vmm_sb_ds_registration ds;
01294 
01295    foreach (this._vmm_sb_ds[i]) begin
01296       if (this._vmm_sb_ds[i].sb == sb) begin
01297          `vmm_warning(this.log, "Data stream scoreboard is already registered");
01298          return;
01299       end
01300    end
01301 
01302    ds = new;
01303    ds.sb = sb;
01304    ds.is_in = (kind == vmm_sb_ds::INPUT ||
01305                kind == vmm_sb_ds::EITHER);
01306    ds.is_out = (kind == vmm_sb_ds::EXPECT ||
01307                 kind == vmm_sb_ds::EITHER);
01308    ds.order = order;
01309    this._vmm_sb_ds.push_back(ds);
01310 endfunction: register_vmm_sb_ds
01311 
01312 
01313 function void vmm_channel::unregister_vmm_sb_ds(vmm_sb_ds sb);
01314    foreach (this._vmm_sb_ds[i]) begin
01315       if (this._vmm_sb_ds[i].sb == sb) begin
01316          this._vmm_sb_ds.delete(i);
01317          return;
01318       end
01319    end
01320 
01321    `vmm_error(this.log, "Data stream scoreboard is not registered");
01322 endfunction: unregister_vmm_sb_ds
01323 `endif
01324