VMM OpenSource - sv/std_lib/vmm_consensus.sv

sv/std_lib/vmm_consensus.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 `ifndef VMM_CONSENSUS__SV
00024 `define VMM_CONSENSUS__SV
00025 
00026 
00027 function vmm_consensus::new(string        name,
00028                             string        inst);
00029 `ifdef VMM_CONSENSUS_BASE_NEW_CALL
00030    super.new(`VMM_CONSENSUS_BASE_NEW_CALL);
00031 `endif
00032 
00033    this.log = new(name, inst);
00034 
00035    this.n_dissenters = 0;
00036    this.n_forcing    = 0;
00037 endfunction: new
00038 
00039 
00040 function vmm_voter vmm_consensus::register_voter(string name);
00041    vmm_voter voter = new(name, this);
00042 
00043    // Voters object by default
00044    this.n_dissenters++;
00045 
00046    this.voters.push_back(voter);
00047 
00048    return voter;
00049 endfunction: register_voter
00050 
00051 
00052 function void vmm_consensus::unregister_voter(vmm_voter voter);
00053    foreach (this.voters[i]) begin
00054       if (this.voters[i] == voter) begin
00055          if (!voter.get_vote()) voter.consent("Dead voter consents by default");
00056          voter.kill_voter();
00057          this.voters.delete(i);
00058          return;
00059       end
00060    end
00061    `vmm_error(this.log, {voter.get_name(), " is not a registered voter"});
00062 endfunction: unregister_voter
00063 
00064 
00065 function void vmm_consensus::register_xactor(vmm_xactor xact);
00066    vmm_voter voter = this.register_voter({"Transactor ", xact.get_name(),
00067                                           "(", xact.get_instance(), ")"});
00068    voter.xactor(xact);
00069 endfunction: register_xactor
00070 
00071 
00072 function void vmm_consensus::unregister_xactor(vmm_xactor xact);
00073    foreach (this.voters[i]) begin
00074       vmm_voter voter = this.voters[i];
00075       if (voter.get_xactor() == xact) begin
00076          if (!voter.get_vote()) voter.consent("Dead voter consents by default");
00077          voter.kill_voter();
00078          this.voters.delete(i);
00079          return;
00080       end
00081    end
00082    `vmm_error(this.log, {"Transactor ", xact.get_name(), "(",
00083                          xact.get_instance(), ") is not a registered voter"});
00084 endfunction: unregister_xactor
00085 
00086 
00087 function void vmm_consensus::register_channel(vmm_channel chan);
00088    vmm_voter voter = this.register_voter({"Channel ",
00089                                           chan.log.get_name(), "(",
00090                                           chan.log.get_instance(), ")"});
00091    voter.channel(chan);
00092 endfunction: register_channel
00093 
00094 
00095 function void vmm_consensus::unregister_channel(vmm_channel chan);
00096    foreach (this.voters[i]) begin
00097       vmm_voter voter = this.voters[i];
00098       if (voter.get_channel() == chan) begin
00099          if (!voter.get_vote()) voter.consent("Dead voter consents by default");
00100          voter.kill_voter();
00101          this.voters.delete(i);
00102          return;
00103       end
00104    end
00105    `vmm_error(this.log, {"Channel ", chan.log.get_name(), "(",
00106                          chan.log.get_instance(),
00107                          ") is not a registered voter"});
00108 endfunction: unregister_channel
00109 
00110 
00111 function void vmm_consensus::register_notification(vmm_notify notify,
00112                                                    int notification);
00113 
00114    vmm_voter voter;
00115    string    name;
00116    int       mode;
00117 
00118    if (notify == null) begin
00119       `vmm_error(this.log, "Cannot register NULL vmm_notify reference");
00120       return;
00121    end
00122 
00123    mode = notify.is_configured(notification);
00124    if (!mode) begin
00125       `vmm_error(this.log, "Cannot register unconfigured notification");
00126       return;
00127    end
00128    if (mode != vmm_notify::ON_OFF) begin
00129       `vmm_error(this.log, "Can only register ON_OFF notification");
00130       return;
00131    end
00132 
00133    $sformat(name, "Notification #%0d %s(%s)",
00134             notification, notify.log.get_name(),
00135             notify.log.get_instance());
00136    voter = this.register_voter(name);
00137    voter.notify(notify, notification, 1);
00138 endfunction: register_notification
00139 
00140 
00141 function void vmm_consensus::register_no_notification(vmm_notify notify,
00142                                                       int notification);
00143 
00144    vmm_voter voter;
00145    string    name;
00146    int       mode;
00147 
00148    if (notify == null) begin
00149       `vmm_error(this.log, "Cannot register NULL vmm_notify reference");
00150       return;
00151    end
00152 
00153    mode = notify.is_configured(notification);
00154    if (!mode) begin
00155       `vmm_error(this.log, "Cannot register unconfigured notification");
00156       return;
00157    end
00158    if (mode != vmm_notify::ON_OFF) begin
00159       `vmm_error(this.log, "Can only register ON_OFF notification");
00160       return;
00161    end
00162 
00163    $sformat(name, "Notification #%0d %s(%s)",
00164             notification, notify.log.get_name(),
00165             notify.log.get_instance());
00166    voter = this.register_voter(name);
00167    voter.notify(notify, notification, 0);
00168 endfunction: register_no_notification
00169 
00170 
00171 function void vmm_consensus::unregister_notification(vmm_notify notify,
00172                                                      int notification);
00173    foreach (this.voters[i]) begin
00174       vmm_voter voter = this.voters[i];
00175       if (voter.get_notify() == notify &&
00176           voter.get_notification() == notification) begin
00177          if (!voter.get_vote()) voter.consent("Dead voter consents by default");
00178          voter.kill_voter();
00179          this.voters.delete(i);
00180          return;
00181       end
00182    end
00183    begin
00184       string msg;
00185       $sformat(msg, "Notification #%0d %s(%s)",
00186                notification, notify.log.get_name(),
00187                notify.log.get_instance());
00188       `vmm_error(this.log, msg);
00189    end
00190 endfunction: unregister_notification
00191 
00192 
00193 function void vmm_consensus::register_consensus(vmm_consensus vote,
00194                                                 bit force_through=0);
00195    vmm_voter voter = this.register_voter({"Consensus ",
00196                                           vote.log.get_instance()});
00197    voter.sub_consensus(vote, force_through);
00198 endfunction: register_consensus
00199 
00200 
00201 function void vmm_consensus::unregister_consensus(vmm_consensus vote);
00202    foreach (this.voters[i]) begin
00203       vmm_voter voter = this.voters[i];
00204       if (voter.get_consensus() == vote) begin
00205          if (!voter.get_vote()) voter.consent("Dead voter consents by default");
00206          voter.kill_voter();
00207          this.voters.delete(i);
00208          return;
00209       end
00210    end
00211    `vmm_error(this.log, {"Consensus ", vote.log.get_instance(),
00212                          " is not a registered voter"});
00213 endfunction: unregister_consensus
00214 
00215 
00216 function void vmm_consensus::unregister_all();
00217    foreach (this.voters[i]) begin
00218       vmm_voter voter = this.voters[i];
00219       voter.kill_voter();
00220    end
00221 `ifdef VCS2006_06
00222    // Work-around required by VCS 2006.06
00223    // but IEEE 1800-2009 compliant
00224    this.voters.delete();
00225 `else
00226    // Works in VCS2008.03 or later
00227    // IEEE 1800-2005 compliant
00228    this.voters = '{};
00229 `endif
00230 
00231    this.n_dissenters = 0;
00232    this.n_forcing    = 0;
00233 endfunction: unregister_all
00234 
00235 
00236 task vmm_consensus::wait_for_consensus();
00237    wait (this.n_forcing > 0 || this.n_dissenters == 0);
00238 endtask: wait_for_consensus
00239 
00240 
00241 task vmm_consensus::wait_for_no_consensus();
00242    wait (this.n_forcing == 0 && this.n_dissenters != 0);
00243 endtask: wait_for_no_consensus
00244 
00245 
00246 function bit vmm_consensus::is_reached();
00247    return this.n_forcing > 0 || this.n_dissenters == 0;
00248 endfunction: is_reached
00249 
00250 
00251 function bit vmm_consensus::is_forced();
00252    return this.n_forcing > 0;
00253 endfunction: is_forced
00254 
00255 
00256 function string vmm_consensus::psdisplay(string prefix="");
00257    $sformat(psdisplay, "%sConsensus %s(%s) is %0s", prefix,
00258             this.log.get_name(), this.log.get_instance(),
00259             (this.is_reached() ? (this.is_forced() ? "forced" : "reached")
00260                                : "NOT reached"));
00261    if (this.voters.size() == 0) begin
00262       psdisplay = {psdisplay, " by default"};
00263    end
00264    else begin
00265       foreach (this.voters[i]) begin
00266          vmm_consensus subvote;
00267          $sformat(psdisplay, "%s\n%s   %s %0s because %s", psdisplay, prefix,
00268                   this.voters[i].get_name(),
00269                   (this.voters[i].get_vote() ?
00270                    (this.voters[i].get_forced() ? "forces" : "consents")
00271                    : "opposes"),
00272                   this.voters[i].get_reason());
00273          subvote = this.voters[i].get_consensus();
00274          if (subvote != null) begin
00275             psdisplay = {psdisplay, "\n", subvote.psdisplay({prefix, "      "})};
00276          end
00277       end
00278    end
00279 endfunction: psdisplay
00280 
00281 
00282 function void vmm_consensus::yeas(ref string who[],
00283                                   ref string why[]);
00284    int n = 0;
00285 
00286    foreach (this.voters[i]) begin
00287       if (this.voters[i].get_vote()) n++;
00288    end
00289 
00290    who = new [n];
00291    why = new [n];
00292 
00293    n = 0;
00294    foreach (this.voters[i]) begin
00295       if (this.voters[i].get_vote()) begin
00296          who[n] = this.voters[i].get_name();
00297          why[n] = this.voters[i].get_reason();
00298          n++;
00299       end
00300    end
00301 endfunction: yeas
00302 
00303 
00304 function void vmm_consensus::nays(ref string who[],
00305                                   ref string why[]);
00306    int n = 0;
00307 
00308    foreach (this.voters[i]) begin
00309       if (!this.voters[i].get_vote()) n++;
00310    end
00311 
00312    who = new [n];
00313    why = new [n];
00314 
00315    n = 0;
00316    foreach (this.voters[i]) begin
00317       if (!this.voters[i].get_vote()) begin
00318          who[n] = this.voters[i].get_name();
00319          why[n] = this.voters[i].get_reason();
00320          n++;
00321       end
00322    end
00323 endfunction: nays
00324 
00325 
00326 function void vmm_consensus::forcing(ref string who[],
00327                                      ref string why[]);
00328    int n = 0;
00329 
00330    foreach (this.voters[i]) begin
00331       if (this.voters[i].get_vote() &&
00332           this.voters[i].get_forced()) n++;
00333    end
00334 
00335    who = new [n];
00336    why = new [n];
00337 
00338    n = 0;
00339    foreach (this.voters[i]) begin
00340       if (this.voters[i].get_vote() &&
00341           this.voters[i].get_forced()) begin
00342          who[n] = this.voters[i].get_name();
00343          why[n] = this.voters[i].get_reason();
00344          n++;
00345       end
00346    end
00347 endfunction: forcing
00348    
00349 
00350 function void vmm_consensus::XvoteX(bit was_agree,
00351                                     bit agree,
00352                                     bit was_forced,
00353                                     bit forced);
00354    if (agree && !was_agree) begin
00355       this.n_dissenters--;
00356       if (this.n_dissenters == 0) ->this.new_results;
00357    end
00358    else if (!agree && was_agree) begin
00359       if (this.n_dissenters == 0) ->this.new_results;
00360       this.n_dissenters++;
00361    end
00362 
00363    if (forced && !was_forced) begin
00364       if (this.n_forcing == 0) ->this.new_results;
00365       this.n_forcing++;
00366    end
00367    else if (!forced && was_forced) begin
00368       this.n_forcing--;
00369       if (this.n_forcing == 0) ->this.new_results;
00370    end
00371 
00372 endfunction: XvoteX
00373 
00374 
00375 function vmm_voter::new(string        name,
00376                         vmm_consensus vote);
00377    this.name      = name;
00378    this.consensus = vote;
00379 
00380    this.vote      = 0;
00381    this.is_forced = 0;
00382    this.why       = "Opposes by default";
00383 
00384    this.xactor_voter = null;
00385    this.channel_voter = null;
00386    this.sub_vote = null;
00387 endfunction: new
00388 
00389 
00390 function void vmm_voter::oppose(string why="No reason specified");
00391    if (this.consensus != null) begin
00392       this.consensus.XvoteX(this.vote, 0, this.is_forced, 0);
00393    end
00394    this.vote = 0;
00395    this.is_forced = 0;
00396    this.why = why;
00397 endfunction: oppose
00398 
00399 
00400 function void vmm_voter::consent(string why="No reason specified");
00401    if (this.consensus != null) begin
00402       this.consensus.XvoteX(this.vote, 1, this.is_forced, 0);
00403    end
00404    this.vote = 1;
00405    this.is_forced = 0;
00406    this.why = why;
00407 endfunction: consent
00408 
00409 
00410 function void vmm_voter::forced(string why="No reason specified");
00411    if (this.consensus != null) begin
00412       this.consensus.XvoteX(this.vote, 1, this.is_forced, 1);
00413    end
00414    this.vote = 1;
00415    this.is_forced = 1;
00416    this.why = why;
00417 endfunction: forced
00418 
00419 
00420 function string vmm_voter::get_name();
00421    return this.name;
00422 endfunction: get_name
00423 
00424 
00425 function bit vmm_voter::get_vote();
00426    return this.vote;
00427 endfunction: get_vote
00428 
00429 
00430 function bit vmm_voter::get_forced();
00431    return this.is_forced;
00432 endfunction: get_forced
00433 
00434 
00435 function string vmm_voter::get_reason();
00436    return this.why;
00437 endfunction: get_reason
00438 
00439 
00440 function void vmm_voter::xactor(vmm_xactor xact);
00441    this.xactor_voter = xact;
00442    if (xact.notify.is_on(vmm_xactor::XACTOR_BUSY)) begin
00443       this.oppose("Transactor is BUSY");
00444    end
00445    else this.consent("Transactor is IDLE");
00446    fork
00447       begin
00448          fork
00449             begin
00450                // The transactor might have become busy while
00451                // the forked thread was getting started...
00452                if (xact.notify.is_on(vmm_xactor::XACTOR_BUSY)) begin
00453                   this.oppose("Transactor is BUSY");
00454                end
00455                forever begin
00456                   // Wait for transactor to be IDLE
00457                   xact.notify.wait_for(vmm_xactor::XACTOR_IDLE);
00458                   this.consent("Transactor is IDLE");
00459                   // Prevent an infinite loop
00460                   if (xact.notify.is_on(vmm_xactor::XACTOR_BUSY)) begin
00461                      `vmm_fatal(this.xactor_voter.log,
00462                                 "Transactor is indicating both IDLE and BUSY");
00463                   end
00464                   // Wait for transactor to be BUSY
00465                   xact.notify.wait_for(vmm_xactor::XACTOR_BUSY);
00466                   this.oppose("Transactor is BUSY");
00467                   // Prevent an infinite loop
00468                   if (xact.notify.is_on(vmm_xactor::XACTOR_IDLE)) begin
00469                      `vmm_fatal(this.xactor_voter.log,
00470                                 "Transactor is indicating both IDLE and BUSY");
00471                   end
00472                end
00473             end
00474          join_none
00475 
00476          @(this.killme);
00477          disable fork;
00478       end
00479    join_none
00480 endfunction: xactor
00481 
00482 
00483 function void vmm_voter::channel(vmm_channel chan);
00484    this.channel_voter = chan;
00485    if (!chan.notify.is_on(vmm_channel::EMPTY)) begin
00486       this.oppose("Channel is not empty");
00487    end
00488    else this.consent("Channel is empty");
00489    fork
00490       begin
00491          fork
00492             forever begin
00493                // Wait for channel to be empty
00494                chan.notify.wait_for(vmm_channel::EMPTY);
00495                this.consent("Channel is empty");
00496                // Wait for channel to be not empty
00497                chan.notify.wait_for_off(vmm_channel::EMPTY);
00498                this.oppose("Channel is not empty");
00499             end
00500          join_none
00501 
00502          @(this.killme);
00503          disable fork;
00504       end
00505    join_none
00506 endfunction: channel
00507 
00508 
00509 function void vmm_voter::notify(vmm_notify ntfy,
00510                                 int notification,
00511                                 bit is_on);
00512    this.notify_voter = ntfy;
00513    if (is_on) begin
00514       if (!ntfy.is_on(notification)) begin
00515          this.oppose("Notification is not indicated");
00516       end
00517       else this.consent("Notification is indicated");
00518    end
00519    else begin
00520       if (ntfy.is_on(notification)) begin
00521          this.oppose("Notification is indicated");
00522       end
00523       else this.consent("Notification is not indicated");
00524    end
00525    fork
00526       begin
00527          fork
00528             if (is_on) begin
00529                // Check again as it could have change while
00530                // the thread was forked
00531                if (!ntfy.is_on(notification)) begin
00532                   this.oppose("Notification is not indicated");
00533                end
00534                else this.consent("Notification is indicated");
00535 
00536                forever begin
00537                   // Wait for indication
00538                   ntfy.wait_for(notification);
00539                   this.consent("Notification is indicated");
00540                   // Wait for indication to be reset
00541                   ntfy.wait_for_off(notification);
00542                   this.oppose("Notification is not indicated");
00543                end
00544             end
00545             if (!is_on) begin
00546                // Check again as it could have change while
00547                // the thread was forked
00548                if (ntfy.is_on(notification)) begin
00549                   this.oppose("Notification is indicated");
00550                end
00551                else this.consent("Notification is not indicated");
00552 
00553                forever begin
00554                   // Wait for indication
00555                   ntfy.wait_for_off(notification);
00556                   this.consent("Notification is not indicated");
00557                   // Wait for indication to be reset
00558                   ntfy.wait_for(notification);
00559                   this.oppose("Notification is indicated");
00560                end
00561             end
00562          join_none
00563 
00564          @(this.killme);
00565          disable fork;
00566       end
00567    join_none
00568 endfunction: notify
00569 
00570 
00571 function void vmm_voter::sub_consensus(vmm_consensus vote,
00572                                        bit force_through);
00573    this.sub_vote = vote;
00574    if (!vote.is_reached()) begin
00575       this.oppose("Sub-consensus is not reached");
00576    end
00577    else this.consent("Sub-consensus is reached");
00578 
00579    fork
00580       begin
00581          fork
00582             forever begin
00583                if (vote.is_forced() && force_through) begin
00584                   this.forced("Sub-consensus forces");
00585                end
00586                else if (vote.is_reached()) this.consent("Sub-consensus consents");
00587                else this.oppose("Sub-consensus opposes");
00588                // Wait for sub-consensus to reach new results
00589                @vote.new_results;
00590             end
00591          join_none
00592 
00593          @(this.killme);
00594          disable fork;
00595       end
00596    join_none
00597 endfunction: sub_consensus
00598 
00599 
00600 function void vmm_voter::kill_voter();
00601    ->this.killme;
00602    this.consensus = null;
00603 endfunction: kill_voter
00604 
00605 
00606 function vmm_xactor vmm_voter::get_xactor();
00607    return this.xactor_voter;
00608 endfunction: get_xactor
00609 
00610 
00611 function vmm_channel vmm_voter::get_channel();
00612    return this.channel_voter;
00613 endfunction: get_channel
00614 
00615 
00616 function vmm_notify vmm_voter::get_notify();
00617    return this.notify_voter;
00618 endfunction: get_notify
00619 
00620 
00621 function int vmm_voter::get_notification();
00622    return this.notification;
00623 endfunction: get_notification
00624 
00625 
00626 function vmm_consensus vmm_voter::get_consensus();
00627    return this.sub_vote;
00628 endfunction: get_consensus
00629 
00630 
00631 `endif // RVM_CONSENSUS__SV