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