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, 00028 int unsigned empty, 00029 bit fill_as_bytes); 00030 `ifdef VMM_CHANNEL_BASE_NEW_CALL 00031 super.new(`VMM_CHANNEL_BASE_NEW_CALL); 00032 `endif 00033 00034 if (this.one_log) begin 00035 if (this.shared_log == null) begin 00036 this.shared_log = new("VMM Channel", "[shared]"); 00037 end 00038 this.log = shared_log; 00039 end else this.log = new(name, inst); 00040 00041 this.notify = new(this.log); 00042 this.notify.configure(FULL, vmm_notify::ON_OFF); 00043 this.notify.configure(EMPTY, vmm_notify::ON_OFF); 00044 this.notify.configure(PUT); 00045 this.notify.configure(GOT); 00046 this.notify.configure(PEEKED); 00047 this.notify.configure(ACTIVATED); 00048 this.notify.configure(ACT_STARTED); 00049 this.notify.configure(ACT_COMPLETED); 00050 this.notify.configure(ACT_REMOVED); 00051 this.notify.configure(LOCKED); 00052 this.notify.configure(UNLOCKED); 00053 00054 if (full <= 0) full = 1; 00055 if (empty < 0 || empty > full) empty = full; 00056 00057 this.full = full; 00058 this.empty = empty; 00059 this.is_sunk = 0; 00060 00061 this.active = null; 00062 this.active_status = INACTIVE; 00063 this.tee_on = 0; 00064 this.downstream = null; 00065 this.locks = 2'b00; 00066 00067 this.full_chan = 0; 00068 this.notify.indicate(EMPTY); 00069 00070 this.iterator = 0; 00071 00072 // 00073 // Thread to manage connection requests 00074 // 00075 fork: connection_requests 00076 while (1) 00077 begin : new_while_loop 00078 vmm_data data = null; 00079 00080 // Broken connection? 00081 if (this.downstream != null) 00082 begin 00083 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) 00084 begin 00085 string txt; 00086 txt = {"Channel connection established: ", 00087 this.log.get_name(), 00088 "(", this.log.get_instance(), ") -> ", 00089 this.downstream.log.get_name(), 00090 "(", this.downstream.log.get_instance(), ")"}; 00091 00092 this.log.text(txt); 00093 this.log.end_msg(); 00094 end // if debug level 00095 00096 // Fork the data mover thread 00097 fork 00098 while (1) 00099 begin : inner_while_loop 00100 // Simple blocking interface 00101 data = null; 00102 this.peek(data); 00103 this.downstream.put(data); 00104 this.get(data); 00105 end // inner_while_loop 00106 join_none 00107 end // if downstream != null 00108 00109 // Wait for new connection requests 00110 @this.new_connection; 00111 00112 // Stop the data mover thread 00113 disable fork; 00114 00115 // Remove any datum that was forwarded 00116 // to the downstream channel but not removed 00117 // from this channel because of the blocking 00118 // model. Otherwise, the same datum will be 00119 // obtained from this channel twice. 00120 if (data != null) this.get(data); 00121 end // while (1) 00122 join_none // connection_requests 00123 endfunction : new 00124 00125 00126 function void vmm_channel::reconfigure(int full, 00127 int empty, 00128 logic fill_as_bytes); 00129 if (full < 0) full = this.full; 00130 if (full == 0) full = 1; 00131 if (empty < 0) empty = this.empty; 00132 00133 if (full < empty) 00134 begin 00135 `vmm_error(this.log, "Cannot reconfigure channel with FULL < EMPTY"); 00136 return; 00137 end 00138 00139 this.full = full; 00140 this.empty = empty; 00141 00142 if (this.level() >= this.full) 00143 begin 00144 this.full_chan = 1; 00145 this.notify.indicate(FULL); 00146 this.notify.reset(EMPTY); 00147 end 00148 else if (this.level() <= this.empty) 00149 begin 00150 this.full_chan = 0; 00151 -> this.item_taken; 00152 this.notify.indicate(EMPTY); 00153 this.notify.reset(FULL); 00154 end 00155 else 00156 begin 00157 this.full_chan = 0; 00158 -> this.item_taken; 00159 this.notify.reset(EMPTY); 00160 this.notify.reset(FULL); 00161 end 00162 endfunction: reconfigure 00163 00164 00165 function int unsigned vmm_channel::full_level(); 00166 full_level = this.full; 00167 endfunction: full_level 00168 00169 00170 function int unsigned vmm_channel::empty_level(); 00171 empty_level = this.empty; 00172 endfunction: empty_level 00173 00174 00175 function int unsigned vmm_channel::level(); 00176 level = this.data.size() + ((this.active == null) ? 0 : 1); 00177 endfunction: level 00178 00179 00180 function int unsigned vmm_channel::size(); 00181 size = this.data.size() + ((this.active == null) ? 0 : 1); 00182 endfunction : size 00183 00184 00185 function bit vmm_channel::is_full(); 00186 is_full = full_chan; 00187 endfunction : is_full 00188 00189 00190 function void vmm_channel::flush(); 00191 vmm_data obj; 00192 if (this.downstream != null) 00193 this.downstream.flush(); 00194 00195 `ifdef VCS2006_06 00196 // Work-around required by VCS 2006.06 00197 // *NOT* IEEE compliant :-( 00198 this.data.delete(); 00199 this.tee_data.delete(); 00200 `else 00201 // This works in VCS 2008.03 00202 this.data = '{}; 00203 this.tee_data = '{}; 00204 `endif 00205 full_chan = 0; 00206 this.active = null; 00207 this.active_status = INACTIVE ; 00208 -> this.item_taken; 00209 this.notify.reset(FULL); 00210 this.notify.indicate(EMPTY); 00211 endfunction: flush 00212 00213 00214 function void vmm_channel::sink(); 00215 this.flush(); 00216 this.is_sunk = 1; 00217 endfunction: sink 00218 00219 00220 function void vmm_channel::flow(); 00221 this.is_sunk = 0; 00222 endfunction: flow 00223 00224 00225 function void vmm_channel::reset(); 00226 this.flush(); 00227 endfunction: reset 00228 00229 00230 function void vmm_channel::lock(bit [1:0] who); 00231 this.locks |= who; 00232 this.notify.indicate(LOCKED); 00233 endfunction: lock 00234 00235 00236 function void vmm_channel::unlock(bit [1:0] who); 00237 this.locks &= ~who; 00238 this.notify.indicate(UNLOCKED); 00239 // May cause a consumer or producer to unblock 00240 -> this.item_taken; 00241 -> this.item_added; 00242 endfunction: unlock 00243 00244 00245 function bit vmm_channel::is_locked(bit [1:0] who); 00246 is_locked = (this.locks & who) ? 1 : 0; 00247 endfunction: is_locked 00248 00249 00250 function void vmm_channel::display(string prefix); 00251 $display(this.psdisplay(prefix)); 00252 endfunction: display 00253 00254 00255 function string vmm_channel::psdisplay(string prefix); 00256 $sformat(psdisplay, "%sChannel %s(%s): Level = %0d of %0d (empty=%0d)", 00257 prefix, this.log.get_name(), this.log.get_instance(), 00258 this.level(), this.full, this.empty); 00259 case (this.locks) 00260 SOURCE+SINK : psdisplay = {psdisplay, " [src+snk locked]"}; 00261 SOURCE: psdisplay = {psdisplay, " [src locked]"}; 00262 SINK: psdisplay = {psdisplay, " [snk locked]"}; 00263 endcase 00264 if (this.is_sunk) psdisplay = {psdisplay, " (sunk)"}; 00265 00266 foreach(this.data[i]) begin 00267 $sformat(psdisplay, "%s\n%s", psdisplay, this.data[i].psdisplay(`vmm_sformatf("%s [%0d] ", 00268 prefix, i))); 00269 end 00270 endfunction: psdisplay 00271 00272 00273 `ifndef VMM_OV_INTEROP 00274 task vmm_channel::put(vmm_data obj, 00275 int offset); 00276 if (obj == null) 00277 begin 00278 `vmm_error(this.log, "Attempted to put null instance into channel"); 00279 return; 00280 end // if obj == null 00281 00282 this.block_producer(); 00283 this.sneak(obj, offset); 00284 this.block_producer(); 00285 endtask: put 00286 `endif 00287 00288 function void vmm_channel::sneak(vmm_data obj, 00289 int offset); 00290 string txt; 00291 00292 if (obj == null) 00293 begin 00294 `vmm_error(this.log, "Attempted to sneak null instance into channel"); 00295 return; 00296 end // obj == null 00297 00298 if (this.is_sunk) return; 00299 00300 if (offset == -1 || (offset == 0 && this.data.size() == 0)) 00301 begin 00302 this.data.push_back(obj); 00303 end 00304 else 00305 begin 00306 int idx = this.index(offset); 00307 if (idx < 0) return; 00308 00309 this.data.insert(offset, obj); 00310 end // if offset 00311 00312 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV)) 00313 begin 00314 $sformat(txt, "New instance added to channel @%0d (level %0d of %0d/%0d)\n%s", 00315 offset, this.level(), this.full, this.empty, 00316 obj.psdisplay(" ")); 00317 this.log.text(txt); 00318 this.log.end_msg(); 00319 end // if dbg 00320 00321 this.notify.indicate(PUT, obj); 00322 00323 if (this.level() >= this.full) 00324 begin 00325 this.full_chan = 1; 00326 this.notify.indicate(FULL); 00327 end 00328 00329 if (this.level() > this.empty) 00330 begin 00331 this.notify.reset(EMPTY); 00332 end 00333 00334 -> this.item_added; 00335 endfunction: sneak 00336 00337 00338 function vmm_data vmm_channel::unput(int offset); 00339 if (this.size() == 0) 00340 begin 00341 `vmm_error(this.log, "Cannot unput from an empty channel"); 00342 return null; 00343 end // if size == 0 00344 00345 if (offset == -1) 00346 begin 00347 unput = this.data.pop_back(); 00348 end 00349 else 00350 begin 00351 int idx = this.index(offset); 00352 if (idx < 0) 00353 begin 00354 unput = null; 00355 end 00356 else 00357 begin 00358 unput = this.data[idx]; 00359 this.data.delete(idx); 00360 end 00361 end // if offset != -1 00362 00363 if (unput != null) begin 00364 this.notify.indicate(GOT, unput); 00365 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV)) 00366 begin 00367 string txt; 00368 $sformat(txt, "Instance unput from channel @%0d (level %0d of %0d/%0d)\n%s", 00369 offset, this.level(), this.full, this.empty, 00370 unput.psdisplay(" ")); 00371 this.log.text(txt); 00372 this.log.end_msg(); 00373 end // if dbg_lvl 00374 end 00375 00376 this.unblock_producer(); 00377 endfunction: unput 00378 00379 00380 task vmm_channel::get1(output vmm_data obj, 00381 input int offset); 00382 if (offset == 0) 00383 begin 00384 obj = this.data.pop_front(); 00385 end 00386 else 00387 begin 00388 int idx = this.index(offset); 00389 if (idx < 0) 00390 begin 00391 obj = null; 00392 end 00393 else 00394 begin 00395 obj = this.data[idx]; 00396 this.data.delete(idx); 00397 end // else if idx < 0 00398 end // else if offset 00399 00400 if (obj != null) 00401 begin 00402 this.notify.indicate(GOT, obj); 00403 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV)) 00404 begin 00405 string txt; 00406 $sformat(txt, "New instance removed from channel @%0d (level %0d of %0d/%0d)\n%s", 00407 offset, this.level(), this.full, this.empty, 00408 obj.psdisplay(" ")); 00409 this.log.text(txt); 00410 this.log.end_msg(); 00411 end // if dbg_lvl 00412 00413 if (this.tee_on) 00414 begin 00415 this.tee_data.push_back(obj); 00416 -> this.teed; 00417 end // tee_on 00418 00419 `ifdef VMM_SB_DS_IN_STDLIB 00420 foreach (this._vmm_sb_ds[i]) begin 00421 if (this._vmm_sb_ds[i].is_in) begin 00422 this._vmm_sb_ds[i].sb.insert(obj); 00423 end 00424 00425 if (this._vmm_sb_ds[i].is_out) begin 00426 case (this._vmm_sb_ds[i].order) 00427 vmm_sb_ds::IN_ORDER: 00428 this._vmm_sb_ds[i].sb.expect_in_order(obj); 00429 00430 vmm_sb_ds::WITH_LOSSES: begin 00431 vmm_data p; 00432 vmm_data losses[]; 00433 this._vmm_sb_ds[i].sb.expect_with_losses(obj, p, losses); 00434 end 00435 00436 vmm_sb_ds::OUT_ORDER: 00437 this._vmm_sb_ds[i].sb.expect_out_of_order(obj); 00438 00439 endcase 00440 end 00441 end 00442 `endif 00443 end // if obj != null 00444 endtask: get1 00445 00446 00447 task vmm_channel::get(output vmm_data obj, 00448 input int offset); 00449 this.block_consumer(); 00450 this.get1(obj, offset); 00451 this.unblock_producer(); 00452 endtask: get 00453 00454 task vmm_channel::peek(output vmm_data obj, 00455 input int offset); 00456 string txt; 00457 int idx; 00458 00459 this.block_consumer(); 00460 00461 idx = this.index(offset); 00462 if (idx < 0) 00463 begin 00464 obj = null; 00465 end 00466 else 00467 begin 00468 obj = this.data[idx]; 00469 end 00470 00471 if (obj != null) 00472 begin 00473 this.notify.indicate(PEEKED, obj); 00474 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV)) 00475 begin 00476 $sformat(txt, "New instance peeked from channel @%0d (level %0d of %0d/%0d)\n%s", 00477 offset, this.level(), this.full, this.empty, 00478 obj.psdisplay(" ")); 00479 this.log.text(txt); 00480 this.log.end_msg(); 00481 end // if dbg_lvl 00482 end // obj != null 00483 00484 this.unblock_producer(); 00485 endtask: peek 00486 00487 00488 task vmm_channel::activate(output vmm_data obj, 00489 input int offset); 00490 string txt; 00491 00492 // Empty the active slot 00493 if (active != null) 00494 this.remove(); 00495 00496 while (this.size() == 0) 00497 @this.item_added; 00498 00499 if (offset == 0) 00500 begin 00501 obj = this.data.pop_front(); 00502 end 00503 else 00504 begin 00505 int idx = this.index(offset); 00506 if (idx < 0) 00507 begin 00508 obj = null; 00509 end 00510 else 00511 begin 00512 obj = this.data[idx]; 00513 this.data.delete(idx); 00514 end 00515 end // else if offset == 0 00516 00517 00518 if (obj != null) 00519 begin 00520 this.active = obj; 00521 this.active_status = PENDING ; 00522 this.notify.indicate(ACTIVATED, obj); 00523 this.notify.indicate(PEEKED, obj); 00524 00525 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV)) 00526 begin 00527 $sformat(txt, "New instance activated from channel @%0d (level %0d of %0d/%0d)\n%s", 00528 offset, this.level(), this.full, this.empty, 00529 obj.psdisplay(" ")); 00530 this.log.text(txt); 00531 this.log.end_msg(); 00532 end // if dbg_lvl 00533 00534 if (this.tee_on) 00535 begin 00536 this.tee_data.push_back(obj); 00537 -> this.teed; 00538 end 00539 end // if obj != null 00540 endtask: activate 00541 00542 00543 function vmm_data vmm_channel::active_slot(); 00544 active_slot = this.active; 00545 endfunction: active_slot 00546 00547 00548 function vmm_data vmm_channel::start(); 00549 if (this.active == null) 00550 begin 00551 `vmm_fatal(this.log, "Cannot start() without prior activate()"); 00552 return null; 00553 end // if active == null 00554 00555 if (this.active_status == STARTED) 00556 begin 00557 `vmm_warning(this.log, "Active slot already start()'ed"); 00558 end // if STARTED 00559 00560 this.active_status = STARTED; 00561 this.notify.indicate(ACT_STARTED, this.active); 00562 this.active.notify.indicate(vmm_data::STARTED); 00563 start = this.active; 00564 endfunction: start 00565 00566 00567 function vmm_data vmm_channel::complete(vmm_data status); 00568 if (this.active == null) 00569 begin 00570 `vmm_fatal(this.log, "Cannot complete() without prior activate()"); 00571 return null; 00572 end 00573 if (this.active_status != STARTED) 00574 begin 00575 `vmm_warning(this.log, "complete() called without start()"); 00576 end 00577 00578 this.active_status = COMPLETED; 00579 this.notify.indicate(ACT_COMPLETED, this.active); 00580 this.active.notify.indicate(vmm_data::ENDED, status); 00581 complete = this.active; 00582 endfunction: complete 00583 00584 00585 function vmm_data vmm_channel::remove(); 00586 if (this.active == null) 00587 begin 00588 `vmm_fatal(this.log, "Cannot remove() without prior activate()"); 00589 return null; 00590 end 00591 00592 // OK to remove if not started or completed 00593 if (this.active_status == STARTED) 00594 begin 00595 `vmm_warning(this.log, "remove() called without complete()"); 00596 end 00597 00598 this.notify.indicate(ACT_REMOVED, this.active); 00599 this.notify.indicate(GOT, this.active); 00600 if (this.active_status != COMPLETED) 00601 begin 00602 this.active.notify.indicate(vmm_data::ENDED); 00603 end 00604 this.active_status = INACTIVE; 00605 remove = this.active; 00606 this.active = null; 00607 00608 this.unblock_producer(); 00609 endfunction: remove 00610 00611 00612 function vmm_channel::active_status_e vmm_channel::status(); 00613 status = this.active_status; 00614 endfunction: status 00615 00616 00617 function bit vmm_channel::tee_mode(bit is_on); 00618 string txt; 00619 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::TRACE_SEV)) 00620 begin 00621 $sformat(txt, "tee branch turned %0s", (is_on) ? "ON" : "OFF"); 00622 this.log.text(txt); 00623 this.log.end_msg(); 00624 end 00625 00626 tee_mode = this.tee_on; 00627 this.tee_on = is_on; 00628 endfunction: tee_mode 00629 00630 00631 task vmm_channel::tee(output vmm_data obj); 00632 string txt; 00633 while (this.tee_data.size() == 0) 00634 @this.teed; 00635 00636 obj = this.tee_data.pop_front(); 00637 00638 if (this.log.start_msg(vmm_log::INTERNAL_TYP, vmm_log::DEBUG_SEV)) 00639 begin 00640 $sformat(txt, "New instance teed from channel (level %0d of %0d/%0d)\n%s", 00641 this.level(), this.full, this.empty, 00642 obj.psdisplay(" ")); 00643 this.log.text(txt); 00644 this.log.end_msg(); 00645 end 00646 endtask: tee 00647 00648 00649 function void vmm_channel::connect(vmm_channel downstream); 00650 // Do not disrupt an already-established connection 00651 if (this.downstream == downstream) return; 00652 00653 // Indicate a new connection 00654 this.downstream = downstream; 00655 -> this.new_connection; 00656 endfunction: connect 00657 00658 00659 function vmm_data vmm_channel::for_each(bit reset); 00660 if (reset) this.iterator = 0; 00661 else this.iterator++; 00662 00663 if (this.iterator >= this.data.size()) for_each = null; 00664 else for_each = this.data[this.iterator]; 00665 endfunction: for_each 00666 00667 00668 function int unsigned vmm_channel::for_each_offset(); 00669 for_each_offset = this.iterator; 00670 endfunction: for_each_offset 00671 00672 00673 function bit vmm_channel::record(string filename); 00674 if (filename == "") 00675 begin 00676 return 1; 00677 end 00678 00679 `vmm_warning(this.log, "vmm_channel::record() not implemented yet"); 00680 record = 0; 00681 endfunction: record 00682 00683 00684 task vmm_channel::playback(output bit success, 00685 input string filename, 00686 input vmm_data loader, 00687 input bit metered); 00688 `vmm_fatal(this.log, "vmm_channel::playback() not implemented yet"); 00689 success = 0; 00690 endtask: playback 00691 00692 00693 function int vmm_channel::index(int offset); 00694 string txt; 00695 index = offset; 00696 if (offset < 0) index += this.data.size() + offset + 1; 00697 if (index < 0 || index >= this.data.size()) 00698 begin 00699 if (this.log.start_msg(vmm_log::FAILURE_TYP, vmm_log::FATAL_SEV)) 00700 begin 00701 $sformat(txt, "Invalid offset %0d specified. Not in {0:%0d}.\n", 00702 offset, this.data.size()-1); 00703 this.log.text(txt); 00704 this.log.end_msg(); 00705 end 00706 index = -1; 00707 end 00708 endfunction: index 00709 00710 00711 `ifndef VMM_OV_INTEROP 00712 task vmm_channel::block_producer(); 00713 while (this.full_chan || this.is_locked(SOURCE)) 00714 @this.item_taken; 00715 endtask : block_producer 00716 00717 00718 task vmm_channel::block_consumer(); 00719 while (this.size() == 0 || this.is_locked(SINK)) 00720 @this.item_added; 00721 endtask: block_consumer 00722 `endif 00723 00724 00725 function void vmm_channel::unblock_producer(); 00726 if (this.level() <= this.empty) 00727 begin 00728 this.full_chan = 0; 00729 this.notify.indicate(EMPTY); 00730 end 00731 00732 if (this.level() < this.full) 00733 begin 00734 this.notify.reset(FULL); 00735 end 00736 00737 -> this.item_taken; 00738 endfunction: unblock_producer 00739 00740 00741 00742 00743 00744 00745 `ifdef VMM_SB_DS_IN_STDLIB 00746 function void vmm_channel::register_vmm_sb_ds(vmm_sb_ds sb, 00747 vmm_sb_ds::kind_e kind, 00748 vmm_sb_ds::ordering_e order); 00749 vmm_sb_ds_registration ds; 00750 00751 foreach (this._vmm_sb_ds[i]) begin 00752 if (this._vmm_sb_ds[i].sb == sb) begin 00753 `vmm_warning(this.log, "Data stream scoreboard is already registered"); 00754 return; 00755 end 00756 end 00757 00758 ds = new; 00759 ds.sb = sb; 00760 ds.is_in = (kind == vmm_sb_ds::INPUT || 00761 kind == vmm_sb_ds::EITHER); 00762 ds.is_out = (kind == vmm_sb_ds::EXPECT || 00763 kind == vmm_sb_ds::EITHER); 00764 ds.order = order; 00765 this._vmm_sb_ds.push_back(ds); 00766 endfunction: register_vmm_sb_ds 00767 00768 00769 function void vmm_channel::unregister_vmm_sb_ds(vmm_sb_ds sb); 00770 foreach (this._vmm_sb_ds[i]) begin 00771 if (this._vmm_sb_ds[i].sb == sb) begin 00772 this._vmm_sb_ds.delete(i); 00773 return; 00774 end 00775 end 00776 00777 `vmm_error(this.log, "Data stream scoreboard is not registered"); 00778 endfunction: unregister_vmm_sb_ds 00779 `endif 00780