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_PERF_ANALYZER__SV
00024 `define VMM_PERF_ANALYZER__SV
00025
00026 `include "vmm.sv"
00027 `include "perf/vmm_perf_tenure.sv"
00028 `include "perf/vmm_sql.sv"
00029
00030 class vmm_perf_data;
00031 time start_time;
00032 time end_time;
00033 time resume_time;
00034 time active_time = 0;
00035 string more_data;
00036
00037 bit aborted;
00038
00039 vmm_perf_tenure tenure;
00040 endclass: vmm_perf_data
00041
00042
00043 typedef class vmm_perf_analyzer;
00044
00045
00046 class vmm_perf_analyzer_callbacks;
00047 virtual function void analyze_tenure(vmm_perf_analyzer analyzer,
00048 vmm_perf_tenure tenure,
00049 ref time start_time,
00050 ref time end_time,
00051 ref time active_time,
00052 ref bit aborted,
00053 ref string more_data,
00054 ref bit filtered);
00055 endfunction
00056 endclass
00057
00058
00059 class vmm_perf_analyzer;
00060
00061 typedef enum {SOFT, HARD} reset_e;
00062
00063 vmm_log log;
00064
00065 local int unsigned max_n_initiators;
00066 local int unsigned max_n_targets;
00067 local int unsigned max_n_concurrent;
00068
00069 local int unsigned known_initiators[*];
00070 local int unsigned known_targets[*];
00071
00072 local vmm_perf_data active[$];
00073 local vmm_perf_data suspended[$];
00074 local vmm_perf_data completed[$];
00075
00076 local vmm_sql_table sql_table;
00077 local string user_schema;
00078
00079 local vmm_perf_analyzer_callbacks cbs[$];
00080
00081 // For simple report
00082 local time min_time;
00083 local time max_time;
00084 local time active_time;
00085 local time min_abort_time;
00086 local time max_abort_time;
00087 local time abort_time;
00088 local int n;
00089
00090 extern function new(string name = "",
00091 vmm_sql_db sql_db,
00092 int unsigned max_n_initiators = 0,
00093 int unsigned max_n_targets = 0,
00094 int unsigned max_n_concurrent = 1,
00095 string user_schema = "");
00096
00097 extern virtual function time now();
00098
00099 extern function void start_tenure(vmm_perf_tenure tenure);
00100 extern function void suspend_tenure(vmm_perf_tenure tenure);
00101 extern function void resume_tenure(vmm_perf_tenure tenure);
00102 extern function void end_tenure(vmm_perf_tenure tenure,
00103 string more_data = "");
00104 extern function void abort_tenure(vmm_perf_tenure tenure,
00105 string more_data = "");
00106 extern function bit add_tenure(int initiator_id = -1,
00107 int target_id = -1,
00108 time start_time,
00109 time end_time,
00110 vmm_data tr = null,
00111 time active_time = 0,
00112 bit aborted = 0,
00113 string more_data = "");
00114
00115 extern virtual function string psdisplay(string prefix = "");
00116 extern function void report(string name = "",
00117 bit brief = 1);
00118
00119 extern function vmm_sql_db get_db();
00120 extern function void save_db();
00121 extern function void save_db_txt(string name = "");
00122
00123 extern function void reset(reset_e rst_typ = SOFT);
00124
00125 extern function void append_callback(vmm_perf_analyzer_callbacks cb);
00126 extern function void prepend_callback(vmm_perf_analyzer_callbacks cb);
00127 extern function void unregister_callback(vmm_perf_analyzer_callbacks cb);
00128 endclass: vmm_perf_analyzer
00129
00130 function vmm_perf_analyzer::new(string name,
00131 vmm_sql_db sql_db,
00132 int unsigned max_n_initiators,
00133 int unsigned max_n_targets,
00134 int unsigned max_n_concurrent,
00135 string user_schema);
00136 string schema;
00137
00138 this.log = new("Performance Analyzer", name);
00139
00140 this.max_n_initiators = max_n_initiators;
00141 this.max_n_targets = max_n_targets;
00142
00143 this.max_n_concurrent = max_n_concurrent;
00144 if (this.max_n_concurrent == 0) this.max_n_concurrent = (1<<31)-1;
00145
00146 schema = "tenureID integer, initiatorID integer, targetID integer, start bigint, end bigint, active bigint, Abort tinyint";
00147 this.user_schema = user_schema;
00148 if (user_schema != "") schema = {schema, ", ", user_schema};
00149 this.sql_table = sql_db.create_table(name, schema, 0);
00150
00151 this.active_time = 0;
00152 this.abort_time = 0;
00153 this.max_time = 0;
00154 this.min_time = '1;
00155 this.max_abort_time = 0;
00156 this.min_abort_time = '1;
00157 this.n = 0;
00158 endfunction: new
00159
00160
00161 function time vmm_perf_analyzer::now();
00162 return $time();
00163 endfunction: now
00164
00165
00166 function void vmm_perf_analyzer::start_tenure(vmm_perf_tenure tenure);
00167 vmm_perf_data datum;
00168
00169 if (tenure == null) begin
00170 `vmm_error(this.log, "Attempting to start a NULL tenure");
00171 return;
00172 end
00173
00174 foreach (this.active[i]) begin
00175 if (this.active[i].tenure == tenure) begin
00176 `vmm_error(this.log, {"Attempting to start an already-active tenure: ",
00177 tenure.psdisplay()});
00178 return;
00179 end
00180 end
00181
00182 if (this.max_n_initiators > 0) begin
00183 if (!this.known_initiators.exists(tenure.get_initiator_id())) begin
00184 this.known_initiators[tenure.get_initiator_id()] = 1;
00185 if (this.known_initiators.num() > this.max_n_initiators) begin
00186 `vmm_error(this.log, $psprintf("Too many initiators: %0d > %0d",
00187 this.known_initiators.num(),
00188 this.max_n_initiators));
00189 end
00190 end
00191 end
00192
00193 if (this.max_n_targets > 0) begin
00194 if (!this.known_targets.exists(tenure.get_initiator_id())) begin
00195 this.known_targets[tenure.get_target_id()] = 1;
00196 if (this.known_targets.num() > this.max_n_targets) begin
00197 `vmm_error(this.log, $psprintf("Too many targets: %0d > %0d",
00198 this.known_targets.num(),
00199 this.max_n_targets));
00200 end
00201 end
00202 end
00203
00204 datum = new;
00205 datum.tenure = tenure;
00206
00207 datum.start_time = this.now();
00208 datum.resume_time = datum.start_time;
00209
00210 this.active.push_back(datum);
00211
00212 if (this.active.size() > this.max_n_concurrent) begin
00213 `vmm_error(this.log, $psprintf("Too many active tenures: %0d > %0d",
00214 this.active.size(), this.max_n_concurrent));
00215 foreach (this.active[i]) begin
00216 `vmm_debug(this.log, this.active[i].tenure.psdisplay("Active: "));
00217 end
00218 end
00219 endfunction: start_tenure
00220
00221
00222 function void vmm_perf_analyzer::suspend_tenure(vmm_perf_tenure tenure);
00223 if (tenure == null) begin
00224 `vmm_error(this.log, "Attempting to suspend a NULL tenure");
00225 return;
00226 end
00227
00228 foreach (this.active[i]) begin
00229 if (this.active[i].tenure == tenure) begin
00230 vmm_perf_data datum = this.active[i];
00231
00232 this.active.delete(i);
00233 this.suspended.push_back(datum);
00234 datum.active_time += this.now() - datum.resume_time;
00235
00236 return;
00237 end
00238 end
00239
00240 `vmm_error(this.log, {"Attempting to suspend a non-active tenure: ",
00241 tenure.psdisplay()});
00242 endfunction: suspend_tenure
00243
00244
00245 function void vmm_perf_analyzer::resume_tenure(vmm_perf_tenure tenure);
00246 if (tenure == null) begin
00247 `vmm_error(this.log, "Attempting to resume a NULL tenure");
00248 return;
00249 end
00250
00251 foreach (this.suspended[i]) begin
00252 if (this.suspended[i].tenure == tenure) begin
00253 vmm_perf_data datum = this.suspended[i];
00254
00255 this.suspended.delete(i);
00256 this.active.push_back(datum);
00257
00258 datum.resume_time = datum.start_time;
00259
00260 if (this.active.size() > this.max_n_concurrent) begin
00261 `vmm_error(this.log, $psprintf("Too many active tenures: %0d > %0d",
00262 this.active.size(), this.max_n_concurrent));
00263 end
00264 return;
00265 end
00266 end
00267 `vmm_error(this.log, {"Attempting to resume a non-suspended tenure: ",
00268 tenure.psdisplay()});
00269 endfunction: resume_tenure
00270
00271
00272 function void vmm_perf_analyzer::end_tenure(vmm_perf_tenure tenure,
00273 string more_data);
00274 if (tenure == null) begin
00275 `vmm_error(this.log, "Attempting to end a NULL tenure");
00276 return;
00277 end
00278
00279 foreach (this.active[i]) begin
00280 if (this.active[i].tenure == tenure) begin
00281 vmm_perf_data datum = this.active[i];
00282 bit filtered = 0;
00283
00284 this.active.delete(i);
00285
00286 datum.end_time = this.now();
00287 datum.active_time += this.now() - datum.resume_time;
00288 datum.aborted = 0;
00289 datum.more_data = more_data;
00290
00291 foreach (this.cbs[i]) begin
00292 this.cbs[i].analyze_tenure(this,
00293 datum.tenure,
00294 datum.start_time,
00295 datum.end_time,
00296 datum.active_time,
00297 datum.aborted,
00298 datum.more_data,
00299 filtered);
00300 end
00301
00302 if (filtered) return;
00303
00304 if (datum.end_time < datum.start_time) begin
00305 `vmm_warning(this.log, $psprintf("Tenure modified with end time (%0d) < start time (%0d)", datum.end_time, datum.start_time));
00306 return;
00307 end
00308
00309 if (datum.active_time > datum.end_time - datum.start_time) begin
00310 `vmm_error(this.log, $psprintf("Tenure modified with active time (%0d) > end time - start time (%0d)", datum.active_time, datum.end_time - datum.start_time));
00311 return;
00312 end
00313
00314 this.completed.push_back(datum);
00315
00316 this.active_time += datum.active_time;
00317 if (this.min_time > datum.active_time) this.min_time = datum.active_time;
00318 if (this.max_time < datum.active_time) this.max_time = datum.active_time;
00319 this.n++;
00320
00321 return;
00322 end
00323 end
00324
00325 `vmm_error(this.log, {"Attempting to end a non-active tenure: ",
00326 tenure.psdisplay()});
00327 endfunction: end_tenure
00328
00329
00330 function void vmm_perf_analyzer::abort_tenure(vmm_perf_tenure tenure,
00331 string more_data);
00332 if (tenure == null) begin
00333 `vmm_error(this.log, "Attemtping to abort a NULL tenure");
00334 return;
00335 end
00336
00337 foreach (this.active[i]) begin
00338 if (this.active[i].tenure == tenure) begin
00339 vmm_perf_data datum = this.active[i];
00340 bit filtered = 0;
00341
00342 this.active.delete(i);
00343
00344 datum.end_time = this.now();
00345 datum.active_time += this.now() - datum.resume_time;
00346 datum.aborted = 1;
00347 datum.more_data = more_data;
00348
00349 foreach (this.cbs[i]) begin
00350 this.cbs[i].analyze_tenure(this,
00351 datum.tenure,
00352 datum.start_time,
00353 datum.end_time,
00354 datum.active_time,
00355 datum.aborted,
00356 datum.more_data,
00357 filtered);
00358 end
00359
00360 if (filtered) return;
00361
00362 if (datum.end_time < datum.start_time) begin
00363 `vmm_warning(this.log, $psprintf("Tenure modified with end time (%0d) < start time (%0d)", datum.end_time, datum.start_time));
00364 return;
00365 end
00366
00367 if (datum.active_time > datum.end_time - datum.start_time) begin
00368 `vmm_error(this.log, $psprintf("Tenure modified with active time (%0d) > end time - start time (%0d)", datum.active_time, datum.end_time - datum.start_time));
00369 return;
00370 end
00371
00372 this.completed.push_back(datum);
00373
00374 this.active_time += datum.active_time;
00375 if (this.min_time > datum.active_time) this.min_time = datum.active_time;
00376 if (this.max_time < datum.active_time) this.max_time = datum.active_time;
00377
00378 this.abort_time += datum.active_time;
00379 if (this.min_abort_time > datum.active_time) this.min_abort_time = datum.active_time;
00380 if (this.max_abort_time < datum.active_time) this.max_abort_time = datum.active_time;
00381 this.n++;
00382
00383 return;
00384 end
00385 end
00386
00387 `vmm_error(this.log, {"Attempting to abort a non-active tenure: ",
00388 tenure.psdisplay()});
00389 endfunction: abort_tenure
00390
00391
00392 function bit vmm_perf_analyzer::add_tenure(int initiator_id,
00393 int target_id,
00394 time start_time,
00395 time end_time,
00396 vmm_data tr,
00397 time active_time,
00398 bit aborted,
00399 string more_data);
00400 if (end_time < start_time) begin
00401 `vmm_error(this.log, $psprintf("Attempting to add tenure with end time (%0d) < start time (%0d)", end_time, start_time));
00402 return 0;
00403 end
00404
00405 if (active_time > end_time - start_time) begin
00406 `vmm_error(this.log, $psprintf("Attempting to add tenure with active time (%0d) > end time - start time (%0d)", active_time, end_time - start_time));
00407 return 0;
00408 end
00409
00410 if (active_time == 0) active_time = end_time - start_time;
00411
00412 begin
00413 vmm_perf_tenure tenure = new(initiator_id, target_id, tr);
00414 bit filtered = 0;
00415
00416 foreach (this.cbs[i]) begin
00417 this.cbs[i].analyze_tenure(this,
00418 tenure,
00419 start_time,
00420 end_time,
00421 active_time,
00422 aborted,
00423 more_data,
00424 filtered);
00425 end
00426
00427 if (filtered) return 1;
00428
00429 if (end_time < start_time) begin
00430 `vmm_error(this.log, $psprintf("Tenure modified with end time (%0d) < start time (%0d)", end_time, start_time));
00431 return 0;
00432 end
00433
00434 if (active_time > end_time - start_time) begin
00435 `vmm_error(this.log, $psprintf("Tenure modified with active time (%0d) > end time - start time (%0d)", active_time, end_time - start_time));
00436 return 0;
00437 end
00438
00439 begin
00440 vmm_perf_data datum = new;
00441
00442 datum.tenure = tenure;
00443 datum.start_time = start_time;
00444 datum.end_time = end_time;
00445 datum.active_time = active_time;
00446 datum.aborted = aborted;
00447 datum.more_data = more_data;
00448
00449 this.completed.push_back(datum);
00450
00451 this.active_time += datum.active_time;
00452 if (this.min_time > datum.active_time) this.min_time = datum.active_time;
00453 if (this.max_time < datum.active_time) this.max_time = datum.active_time;
00454
00455 if (aborted) begin
00456 this.abort_time += datum.active_time;
00457 if (this.min_abort_time > datum.active_time) this.min_abort_time = datum.active_time;
00458 if (this.max_abort_time < datum.active_time) this.max_abort_time = datum.active_time;
00459 end
00460 this.n++;
00461
00462 end
00463 end
00464
00465 return 1;
00466 endfunction: add_tenure
00467
00468
00469 function string vmm_perf_analyzer::psdisplay(string prefix);
00470 $sformat(psdisplay, "%s-- Performance Analyzer Status --", prefix);
00471 $sformat(psdisplay, "%s\n%sMax init/targ/conc = %0d/%0d/%0d",
00472 psdisplay, prefix, this.max_n_initiators, this.max_n_targets,
00473 this.max_n_concurrent);
00474 $sformat(psdisplay, "%s\n%sActive tenures:", psdisplay, prefix);
00475 foreach (this.active[i]) begin
00476 $sformat(psdisplay, "%s\n%s", psdisplay, this.active[i].tenure.psdisplay({prefix, " "}));
00477 end
00478 $sformat(psdisplay, "%s\n%sSuspended tenures:", psdisplay, prefix);
00479 foreach (this.suspended[i]) begin
00480 $sformat(psdisplay, "%s\n%s", psdisplay, this.suspended[i].tenure.psdisplay({prefix, " "}));
00481 end
00482 endfunction: psdisplay
00483
00484
00485 function void vmm_perf_analyzer::report(string name,
00486 bit brief);
00487 int fp = 32'h8000_0001; // STDOUT
00488
00489 if (name != "") begin
00490 fp = $fopen(name, "w");
00491 if (!fp) begin
00492 `vmm_error(this.log, {"Unable to open ", name, " for writing."});
00493 return;
00494 end
00495 end
00496
00497 $fwrite(fp, "\nPerformance Analysis Report for %s (based on %0d data points)\n", this.log.get_instance(), n);
00498 $fwrite(fp, "====================================================================\n");
00499 $fwrite(fp, " All Completed | Aborted\n");
00500
00501 $fwrite(fp, "Min Tenure Duration: %d | %d\n",
00502 this.min_time, this.min_abort_time);
00503 $fwrite(fp, "Avg Tenure Duration: %d | %d\n",
00504 this.active_time / n, this.abort_time / n);
00505 $fwrite(fp, "Max Tenure Duration: %d | %d\n",
00506 this.max_time, this.max_abort_time);
00507 $fwrite(fp, "Utilization : %d%% | %d%%\n",
00508 (this.active_time * 100) / this.now(),
00509 (this.abort_time * 100) / this.now());
00510
00511 if (name != "") begin
00512 $fclose(fp);
00513 end
00514
00515 if (this.active.size() != 0 ||
00516 this.suspended.size() != 0) begin
00517 `vmm_warning(this.log, $psprintf("There are %0d active/suspended tenures not included in the report",
00518 this.active.size() + this.suspended.size()));
00519 end
00520 endfunction: report
00521
00522 function vmm_sql_db vmm_perf_analyzer::get_db();
00523 return this.sql_table.get_db();
00524 endfunction: get_db
00525
00526 function void vmm_perf_analyzer::save_db();
00527 foreach (this.completed[i]) begin
00528 vmm_perf_data datum = this.completed[i];
00529 vmm_perf_tenure tenure = datum.tenure;
00530 string row;
00531
00532 $sformat(row, "%0d,%0d,%0d,%0d,%0d,%0d,%b",
00533 tenure.get_tenure_id(),
00534 tenure.get_initiator_id(),
00535 tenure.get_target_id(),
00536 datum.start_time,
00537 datum.end_time,
00538 datum.active_time,
00539 datum.aborted);
00540
00541 if (this.user_schema != "") row = {row, ",", datum.more_data};
00542
00543 this.sql_table.insert(row);
00544 end
00545 this.completed.delete();
00546
00547 begin
00548 vmm_sql_db sql_db = this.sql_table.get_db();
00549 sql_db.commit();
00550 end
00551
00552 if (this.active.size() != 0 ||
00553 this.suspended.size() != 0) begin
00554 `vmm_warning(this.log, $psprintf("There are %0d active/suspended tenures not included in the report",
00555 this.active.size() + this.suspended.size()));
00556 end
00557 endfunction: save_db
00558
00559 function void vmm_perf_analyzer::save_db_txt(string name);
00560 int fp;
00561
00562 if (name == "") begin
00563 name = {this.log.get_instance(), ".perf.db"};
00564 end
00565
00566 fp = $fopen(name, "w");
00567 if (!fp) begin
00568 `vmm_error(this.log, {"Unable to open ", name, " for writing."});
00569 return;
00570 end
00571
00572 $fwrite("TenureID InitiatorID TargetID Start End Active Abort\n");
00573 foreach (this.completed[i]) begin
00574 vmm_perf_data datum = this.completed[i];
00575 vmm_perf_tenure tenure = datum.tenure;
00576
00577 $fwrite(fp, "%0d %0d %0d %0d %0d %0d %b\n", tenure.get_tenure_id(),
00578 tenure.get_initiator_id(), tenure.get_target_id(),
00579 datum.start_time, datum.end_time, datum.active_time, datum.aborted);
00580 end
00581
00582 $fclose(fp);
00583
00584 if (this.active.size() != 0 ||
00585 this.suspended.size() != 0) begin
00586 `vmm_warning(this.log, $psprintf("There are %0d active/suspended tenures not included in the DB",
00587 this.active.size() + this.suspended.size()));
00588 end
00589 endfunction: save_db_txt
00590
00591 function void vmm_perf_analyzer::reset(reset_e rst_typ);
00592 this.known_initiators.delete();
00593 this.known_targets.delete();
00594 this.active.delete();
00595 this.completed.delete();
00596 begin
00597 vmm_sql_db sql_db = this.sql_table.get_db();
00598 sql_db.statement(
00599 $psprintf(
00600 "DELETE FROM TABLE %s;",
00601 this.sql_table.get_tblname)
00602 );
00603 end
00604
00605 this.active_time = 0;
00606 this.abort_time = 0;
00607 this.max_time = 0;
00608 this.min_time = '1;
00609 this.max_abort_time = 0;
00610 this.min_abort_time = '1;
00611 this.n = 0;
00612
00613 if (rst_typ == HARD) begin
00614 this.cbs.delete();
00615 end
00616 endfunction: reset
00617
00618
00619 function void vmm_perf_analyzer::append_callback(vmm_perf_analyzer_callbacks cb);
00620 foreach (this.cbs[i]) begin
00621 if (this.cbs[i] == cb) begin
00622 `vmm_warning(this.log, "Callback has already been registered");
00623 return;
00624 end
00625 end
00626
00627 // Append new callback
00628 this.cbs.push_back(cb);
00629 endfunction: append_callback
00630
00631
00632 function void vmm_perf_analyzer::prepend_callback(vmm_perf_analyzer_callbacks cb);
00633 foreach (this.cbs[i]) begin
00634 if (this.cbs[i] == cb) begin
00635 `vmm_warning(this.log, "Callback has already been registered");
00636 return;
00637 end
00638 end
00639
00640 // Prepend new callback
00641 this.cbs.push_front(cb);
00642 endfunction: prepend_callback
00643
00644
00645 function void vmm_perf_analyzer::unregister_callback(vmm_perf_analyzer_callbacks cb);
00646 foreach (this.cbs[i]) begin
00647 if (this.cbs[i] == cb) begin
00648 int j = i;
00649 // Unregister it
00650 this.cbs.delete(j);
00651 return;
00652 end
00653 end
00654
00655 `vmm_warning(this.log, "Callback was not registered");
00656 endfunction: unregister_callback
00657
00658 `endif // VMM_PERF_ANALYZER__SV