|
7 | 7 | #include <array> |
8 | 8 | #include <cstring> |
9 | 9 | #include <set> |
| 10 | +#include <unordered_set> |
10 | 11 | #include <utility> |
11 | 12 | #include <vector> |
12 | 13 |
|
|
20 | 21 | #include "sta/Clock.hh" |
21 | 22 | #include "sta/DelayFloat.hh" |
22 | 23 | #include "sta/Graph.hh" |
| 24 | +#include "sta/GraphDelayCalc.hh" |
23 | 25 | #include "sta/Liberty.hh" |
24 | 26 | #include "sta/LibertyClass.hh" |
25 | 27 | #include "sta/MinMax.hh" |
26 | 28 | #include "sta/Mode.hh" |
| 29 | +#include "sta/Path.hh" |
| 30 | +#include "sta/PathEnd.hh" |
| 31 | +#include "sta/PathExpanded.hh" |
| 32 | +#include "sta/PathGroup.hh" |
27 | 33 | #include "sta/PowerClass.hh" |
28 | 34 | #include "sta/Scene.hh" |
| 35 | +#include "sta/Sdc.hh" |
29 | 36 | #include "sta/SdcClass.hh" |
30 | 37 | #include "sta/Search.hh" |
| 38 | +#include "sta/SearchClass.hh" |
| 39 | +#include "sta/StringUtil.hh" |
31 | 40 | #include "sta/TimingArc.hh" |
32 | 41 | #include "sta/TimingRole.hh" |
33 | 42 | #include "utl/Logger.h" |
@@ -404,4 +413,236 @@ std::vector<odb::dbMaster*> Timing::equivCells(odb::dbMaster* master) |
404 | 413 | } |
405 | 414 | return master_seq; |
406 | 415 | } |
| 416 | +float Timing::getWorstSlack(MinMax minmax) |
| 417 | +{ |
| 418 | + sta::dbSta* sta = getSta(); |
| 419 | + cmdLinkedNetwork(); |
| 420 | + return sta->worstSlack(getMinMax(minmax)); |
| 421 | +} |
| 422 | + |
| 423 | +float Timing::getTotalNegativeSlack(MinMax minmax) |
| 424 | +{ |
| 425 | + sta::dbSta* sta = getSta(); |
| 426 | + cmdLinkedNetwork(); |
| 427 | + return sta->totalNegativeSlack(getMinMax(minmax)); |
| 428 | +} |
| 429 | + |
| 430 | +int Timing::getEndpointCount() |
| 431 | +{ |
| 432 | + sta::dbSta* sta = getSta(); |
| 433 | + cmdLinkedNetwork(); |
| 434 | + return sta->endpoints().size(); |
| 435 | +} |
| 436 | + |
| 437 | +std::vector<EndpointSlack> Timing::getEndpointSlacks(MinMax minmax) |
| 438 | +{ |
| 439 | + sta::dbSta* sta = getSta(); |
| 440 | + cmdLinkedNetwork(); |
| 441 | + |
| 442 | + std::vector<EndpointSlack> result; |
| 443 | + for (sta::Vertex* vertex : sta->endpoints()) { |
| 444 | + const sta::Pin* pin = vertex->pin(); |
| 445 | + float slack = sta->slack( |
| 446 | + pin, sta::RiseFallBoth::riseFall(), sta->scenes(), getMinMax(minmax)); |
| 447 | + auto [iterm, bterm] = staToDBPin(pin); |
| 448 | + result.push_back({iterm, bterm, slack}); |
| 449 | + } |
| 450 | + return result; |
| 451 | +} |
| 452 | + |
| 453 | +std::vector<ClockInfo> Timing::getClockInfo() |
| 454 | +{ |
| 455 | + sta::dbSta* sta = getSta(); |
| 456 | + cmdLinkedNetwork(); |
| 457 | + |
| 458 | + std::vector<ClockInfo> result; |
| 459 | + for (const sta::Clock* clk : sta->cmdMode()->sdc()->clocks()) { |
| 460 | + ClockInfo info; |
| 461 | + info.name = clk->name(); |
| 462 | + info.period = clk->period(); |
| 463 | + if (clk->waveform()) { |
| 464 | + info.waveform = *clk->waveform(); |
| 465 | + } |
| 466 | + for (const sta::Pin* pin : clk->pins()) { |
| 467 | + auto [iterm, bterm] = staToDBPin(pin); |
| 468 | + if (iterm) { |
| 469 | + info.source_iterms.push_back(iterm); |
| 470 | + } |
| 471 | + if (bterm) { |
| 472 | + info.source_bterms.push_back(bterm); |
| 473 | + } |
| 474 | + } |
| 475 | + result.push_back(std::move(info)); |
| 476 | + } |
| 477 | + return result; |
| 478 | +} |
| 479 | + |
| 480 | +std::vector<TimingPathInfo> Timing::getTimingPaths(MinMax minmax, |
| 481 | + int max_paths, |
| 482 | + float slack_threshold) |
| 483 | +{ |
| 484 | + sta::dbSta* sta = getSta(); |
| 485 | + cmdLinkedNetwork(); |
| 486 | + sta::dbNetwork* network = sta->getDbNetwork(); |
| 487 | + |
| 488 | + const bool is_setup = (minmax == Max); |
| 489 | + sta::SceneSeq scenes = sta->scenes(); |
| 490 | + sta::StringSeq group_names; |
| 491 | + |
| 492 | + sta->ensureGraph(); |
| 493 | + sta->searchPreamble(); |
| 494 | + |
| 495 | + sta::Search* search = sta->search(); |
| 496 | + sta::PathEndSeq path_ends = search->findPathEnds( |
| 497 | + nullptr, // from |
| 498 | + nullptr, // thrus |
| 499 | + nullptr, // to |
| 500 | + false, // unconstrained |
| 501 | + scenes, |
| 502 | + is_setup ? sta::MinMaxAll::max() : sta::MinMaxAll::min(), |
| 503 | + max_paths, // group_count |
| 504 | + 1, // endpoint_count (one per endpoint) |
| 505 | + true, // unique_pins |
| 506 | + true, // unique_edges |
| 507 | + -sta::INF, // slack_min |
| 508 | + slack_threshold, // slack_max |
| 509 | + true, // sort_by_slack |
| 510 | + group_names, |
| 511 | + is_setup, // setup |
| 512 | + !is_setup, // hold |
| 513 | + false, // recovery |
| 514 | + false, // removal |
| 515 | + false, // clk_gating_setup |
| 516 | + false); // clk_gating_hold |
| 517 | + |
| 518 | + std::vector<TimingPathInfo> result; |
| 519 | + auto* graph = sta->graph(); |
| 520 | + const sta::Sdc* sdc = sta->cmdScene()->sdc(); |
| 521 | + sta::Mode* mode = sta->cmdScene()->mode(); |
| 522 | + sta::GraphDelayCalc* gdc = sta->graphDelayCalc(); |
| 523 | + sta::dbNetwork* db_network = sta->getDbNetwork(); |
| 524 | + |
| 525 | + for (auto& path_end : path_ends) { |
| 526 | + TimingPathInfo path_info; |
| 527 | + sta::Path* path = path_end->path(); |
| 528 | + |
| 529 | + path_info.slack = path_end->slack(sta); |
| 530 | + path_info.arrival = path_end->dataArrivalTime(sta); |
| 531 | + path_info.required = path_end->requiredTime(sta); |
| 532 | + path_info.skew = path_end->clkSkew(sta); |
| 533 | + |
| 534 | + auto* path_delay = path_end->pathDelay(); |
| 535 | + path_info.path_delay = path_delay ? path_delay->delay() : 0.0f; |
| 536 | + |
| 537 | + auto* start_clk_edge = path_end->sourceClkEdge(sta); |
| 538 | + path_info.start_clock |
| 539 | + = start_clk_edge ? start_clk_edge->clock()->name() : ""; |
| 540 | + |
| 541 | + auto* end_clk = path_end->targetClk(sta); |
| 542 | + path_info.end_clock = end_clk ? end_clk->name() : ""; |
| 543 | + |
| 544 | + auto* path_group = path_end->pathGroup(); |
| 545 | + path_info.path_group = path_group ? path_group->name() : ""; |
| 546 | + |
| 547 | + // Expand path to get arc detail |
| 548 | + sta::PathExpanded expand(path, sta); |
| 549 | + float arrival_prev = 0.0f; |
| 550 | + float logic_delay_total = 0.0f; |
| 551 | + int logic_depth_count = 0; |
| 552 | + int max_fanout = 0; |
| 553 | + std::unordered_set<sta::Instance*> logic_insts; |
| 554 | + |
| 555 | + for (size_t i = 0; i < expand.size(); i++) { |
| 556 | + const auto* ref = expand.path(i); |
| 557 | + sta::Vertex* vertex = ref->vertex(sta); |
| 558 | + const sta::Pin* pin = vertex->pin(); |
| 559 | + const bool is_rising = ref->transition(sta) == sta::RiseFall::rise(); |
| 560 | + const float arr = sta::delayAsFloat(ref->arrival()); |
| 561 | + const float slw = sta::delayAsFloat(ref->slew(sta)); |
| 562 | + const float pin_delay = arr - arrival_prev; |
| 563 | + |
| 564 | + // Compute fanout |
| 565 | + int node_fanout = 0; |
| 566 | + sta::VertexOutEdgeIterator iter(vertex, graph); |
| 567 | + while (iter.hasNext()) { |
| 568 | + sta::Edge* edge = iter.next(); |
| 569 | + if (edge->isWire()) { |
| 570 | + const sta::Pin* to_pin = edge->to(graph)->pin(); |
| 571 | + if (network->isTopLevelPort(to_pin)) { |
| 572 | + sta::Port* port = network->port(to_pin); |
| 573 | + node_fanout += sdc->portExtFanout(port, sta::MinMax::max()) + 1; |
| 574 | + } else { |
| 575 | + node_fanout++; |
| 576 | + } |
| 577 | + } |
| 578 | + } |
| 579 | + max_fanout = std::max(node_fanout, max_fanout); |
| 580 | + |
| 581 | + // Compute load capacitance |
| 582 | + float cap = 0.0f; |
| 583 | + const bool is_driver = network->isDriver(pin); |
| 584 | + if (is_driver && i > 0) { |
| 585 | + cap = gdc->loadCap( |
| 586 | + pin, ref->transition(sta), ref->scene(sta), ref->minMax(sta)); |
| 587 | + } |
| 588 | + |
| 589 | + // Determine master, net arcs, logic depth, and build arc info |
| 590 | + if (i > 0) { |
| 591 | + const auto* prev_ref = expand.path(i - 1); |
| 592 | + sta::Vertex* prev_vertex = prev_ref->vertex(sta); |
| 593 | + const sta::Pin* prev_pin = prev_vertex->pin(); |
| 594 | + sta::Instance* inst = network->instance(pin); |
| 595 | + sta::Instance* prev_inst = network->instance(prev_pin); |
| 596 | + |
| 597 | + const bool same_inst = (inst == prev_inst && inst != nullptr); |
| 598 | + |
| 599 | + // Track logic depth (non-clock, non-net arcs) |
| 600 | + bool pin_is_clock = sta->isClock(pin, mode); |
| 601 | + if (same_inst && !pin_is_clock) { |
| 602 | + if (logic_insts.find(inst) == logic_insts.end()) { |
| 603 | + logic_insts.insert(inst); |
| 604 | + logic_depth_count++; |
| 605 | + logic_delay_total += pin_delay; |
| 606 | + } |
| 607 | + } |
| 608 | + |
| 609 | + TimingArcInfo arc; |
| 610 | + odb::dbModITerm* mod_iterm; |
| 611 | + db_network->staToDb( |
| 612 | + prev_pin, arc.from_iterm, arc.from_bterm, mod_iterm); |
| 613 | + db_network->staToDb(pin, arc.to_iterm, arc.to_bterm, mod_iterm); |
| 614 | + if (same_inst && arc.to_iterm) { |
| 615 | + arc.master = arc.to_iterm->getInst()->getMaster(); |
| 616 | + } |
| 617 | + arc.delay = pin_delay; |
| 618 | + arc.slew = slw; |
| 619 | + arc.load = cap; |
| 620 | + arc.fanout = node_fanout; |
| 621 | + arc.is_rising = is_rising; |
| 622 | + path_info.arcs.push_back(arc); |
| 623 | + } |
| 624 | + |
| 625 | + arrival_prev = arr; |
| 626 | + } |
| 627 | + |
| 628 | + // Get startpoint/endpoint objects |
| 629 | + odb::dbModITerm* mod_iterm; |
| 630 | + db_network->staToDb(expand.path(0)->vertex(sta)->pin(), |
| 631 | + path_info.start_iterm, |
| 632 | + path_info.start_bterm, |
| 633 | + mod_iterm); |
| 634 | + db_network->staToDb(path_end->vertex(sta)->pin(), |
| 635 | + path_info.end_iterm, |
| 636 | + path_info.end_bterm, |
| 637 | + mod_iterm); |
| 638 | + |
| 639 | + path_info.logic_delay = logic_delay_total; |
| 640 | + path_info.logic_depth = logic_depth_count; |
| 641 | + path_info.fanout = max_fanout; |
| 642 | + |
| 643 | + result.push_back(std::move(path_info)); |
| 644 | + } |
| 645 | + return result; |
| 646 | +} |
| 647 | + |
407 | 648 | } // namespace ord |
0 commit comments