From f49dc75d321e015ebda5803b721e8c6cdbfd5d5f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 5 Dec 2018 14:18:41 -0800 Subject: [PATCH] sync --- configure.ac | 21 +- dcalc/GraphDelayCalc1.cc | 53 ++--- doc/OpenSTA.odt | Bin 72031 -> 72212 bytes doc/OpenSTA.pdf | Bin 147431 -> 148099 bytes graph/Delay.hh | 48 ++--- graph/DelayFloat.cc | 24 ++- graph/DelayNormal2.cc | 45 ++-- graph/Graph.cc | 33 ++- graph/Makefile.am | 2 - liberty/EquivCells.cc | 88 +++----- liberty/InternalPower.cc | 29 +-- liberty/InternalPower.hh | 3 +- liberty/Liberty.cc | 403 +++++++++++++----------------------- liberty/Liberty.hh | 114 +++++++--- liberty/LibertyBuilder.cc | 67 ++---- liberty/LibertyClass.hh | 4 +- liberty/LibertyReader.cc | 105 +++++----- liberty/LibertyReaderPvt.hh | 2 + liberty/TableModel.cc | 6 +- liberty/TimingArc.cc | 139 +++++-------- liberty/TimingArc.hh | 17 +- liberty/Units.hh | 3 + sdc/Sdc.cc | 51 ++--- sdc/WriteSdc.cc | 8 +- sdf/SdfReader.cc | 21 +- sdf/SdfWriter.cc | 21 +- search/CheckMaxSkews.cc | 7 +- search/CheckSlewLimits.cc | 2 + search/Corner.cc | 12 +- search/Crpr.cc | 8 +- search/FindRegister.cc | 83 +++----- search/PathEnd.cc | 2 +- search/PathEnum.cc | 1 - search/PathEnum.hh | 1 - search/PathGroup.cc | 17 +- search/PathGroup.hh | 3 +- search/Power.cc | 36 ++-- search/Power.hh | 5 +- search/ReportPath.cc | 18 +- search/Search.cc | 165 +++++++++------ search/Search.hh | 43 ++-- search/SearchClass.hh | 1 + search/Sim.cc | 8 +- search/Sta.cc | 46 ++-- search/Sta.hh | 17 +- search/WorstSlack.cc | 301 ++++++++++++--------------- search/WorstSlack.hh | 82 +++++++- tcl/Cmds.tcl | 20 ++ tcl/Search.tcl | 26 ++- tcl/StaTcl.i | 61 ++++-- verilog/VerilogReader.cc | 12 +- 51 files changed, 1109 insertions(+), 1175 deletions(-) diff --git a/configure.ac b/configure.ac index ac18ea6e..08625c15 100644 --- a/configure.ac +++ b/configure.ac @@ -148,6 +148,15 @@ AC_ARG_ENABLE(asan, esac], [asan=false]) +AC_ARG_ENABLE(ssta, + [AS_HELP_STRING([--enable-ssta],[enable ssta])], + [case "${enableval}" in + yes) ssta=true ;; + no) ssta=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-ssta) ;; + esac], + [ssta=false]) + AC_ARG_WITH(include, [AS_HELP_STRING([--with-include=dirs],[directories to search for include files])], [INCLUDE_DIRS="${withval}"], @@ -188,6 +197,14 @@ AC_ARG_WITH(optimize, [], []) +#-------------------------------------------------------------------- + +if test x$ssta = xtrue; then + AC_DEFINE(SSTA, 1, "SSTA support") +else + AC_DEFINE(SSTA, 0, "SSTA support") +fi + #-------------------------------------------------------------------- # Locate the Tcl package #-------------------------------------------------------------------- @@ -411,8 +428,8 @@ AC_FUNC_ERROR_AT_LINE #-------------------------------------------------------------------- if test $CXX = clang++; then - # -Wno-deprecated-register to suppress warnings in flex files - CXXFLAGS="-std=c++11 -Wall -Wno-deprecated-register -Wcast-qual" + # suppress deprecated-register warnings in flex files. + CXXFLAGS="-std=c++11 -Wall -Wcast-qual -Wunused-parameter -Wno-deprecated-register" CXX_OPT="-O3" CXX_DEBUG="-g" CXX_ASAN="-fsanitize=address -O1 -fno-omit-frame-pointer" diff --git a/dcalc/GraphDelayCalc1.cc b/dcalc/GraphDelayCalc1.cc index c8a1ad75..f350bfed 100644 --- a/dcalc/GraphDelayCalc1.cc +++ b/dcalc/GraphDelayCalc1.cc @@ -739,9 +739,9 @@ GraphDelayCalc1::driveCellDefaultFromPort(LibertyCell *cell, { LibertyPort *from_port = 0; int from_port_index = 0; - CellTimingArcSetIterator *set_iter = cell->timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *arc_set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell); + while (set_iter.hasNext()) { + TimingArcSet *arc_set = set_iter.next(); if (arc_set->to() == to_port) { LibertyPort *set_from_port = arc_set->from(); int set_from_port_index = findPortIndex(cell, set_from_port); @@ -752,7 +752,6 @@ GraphDelayCalc1::driveCellDefaultFromPort(LibertyCell *cell, } } } - delete set_iter; return from_port; } @@ -762,16 +761,13 @@ GraphDelayCalc1::findPortIndex(LibertyCell *cell, LibertyPort *port) { int index = 0; - LibertyCellPortIterator *port_iter = cell->libertyPortIterator(); - while (port_iter->hasNext()) { - LibertyPort *cell_port = port_iter->next(); - if (cell_port == port) { - delete port_iter; + LibertyCellPortIterator port_iter(cell); + while (port_iter.hasNext()) { + LibertyPort *cell_port = port_iter.next(); + if (cell_port == port) return index; - } index++; } - delete port_iter; internalError("port not found in cell"); return 0; } @@ -789,24 +785,22 @@ GraphDelayCalc1::findInputDriverDelay(LibertyCell *drvr_cell, debugPrint2(debug_, "delay_calc", 2, " driver cell %s %s\n", drvr_cell->name(), tr->asString()); - CellTimingArcSetIterator *set_iter = drvr_cell->timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *arc_set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(drvr_cell); + while (set_iter.hasNext()) { + TimingArcSet *arc_set = set_iter.next(); if (arc_set->from() == from_port && arc_set->to() == to_port) { - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); if (arc->toTrans()->asRiseFall() == tr) { float from_slew = from_slews[arc->fromTrans()->index()]; findInputArcDelay(drvr_cell, drvr_pin, drvr_vertex, arc, from_slew, dcalc_ap); } } - delete arc_iter; } } - delete set_iter; } // Driving cell delay is the load dependent delay, which is the gate @@ -1026,9 +1020,9 @@ GraphDelayCalc1::findDriverEdgeDelays(LibertyCell *drvr_cell, const Pvt *pvt = sdc_->pvt(drvr_inst, dcalc_ap->constraintMinMax()); if (pvt == NULL) pvt = dcalc_ap->operatingConditions(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); const TransRiseFall *tr = arc->toTrans()->asRiseFall(); Parasitic *parasitic; bool delete_parasitic; @@ -1054,7 +1048,6 @@ GraphDelayCalc1::findDriverEdgeDelays(LibertyCell *drvr_cell, arc_delay_calc->finish(drvr_pin, tr, dcalc_ap, parasitic, delete_parasitic); } - delete arc_iter; } if (delay_changed && observer_) { @@ -1369,9 +1362,9 @@ GraphDelayCalc1::findMultiDrvrGateDelay(MultiDrvrNet *multi_drvr, Edge *edge1 = edge_iter.next(); TimingArcSet *arc_set1 = edge1->timingArcSet(); const LibertyPort *related_out_port = arc_set1->relatedOut(); - TimingArcSetArcIterator *arc_iter = arc_set1->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc1 = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set1); + while (arc_iter.hasNext()) { + TimingArc *arc1 = arc_iter.next(); TransRiseFall *drvr_tr1 = arc1->toTrans()->asRiseFall(); if (drvr_tr1 == drvr_tr) { Vertex *from_vertex1 = edge1->from(graph_); @@ -1420,7 +1413,6 @@ GraphDelayCalc1::findMultiDrvrGateDelay(MultiDrvrNet *multi_drvr, parasitic1, delete_parasitic1); } } - delete arc_iter; } } } @@ -1570,9 +1562,9 @@ GraphDelayCalc1::findCheckEdgeDelays(Edge *edge, Instance *inst = network_->instance(to_pin); const LibertyCell *cell = network_->libertyCell(inst); bool delay_changed = false; - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); TransRiseFall *from_tr = arc->fromTrans()->asRiseFall(); TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); if (from_tr && to_tr) { @@ -1631,7 +1623,6 @@ GraphDelayCalc1::findCheckEdgeDelays(Edge *edge, } } } - delete arc_iter; if (delay_changed && observer_) observer_->checkDelayChangedTo(to_vertex); } diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 048d53afcb4376aedd88bd61bfbe7e61fa3c94e8..bc0eec2680dc6105ec9047af731eab0faffad165 100644 GIT binary patch delta 39044 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 OKnLc;)1GO=yjcJlH+=iK{H_f_rRUR{gb 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 c=<4ZZ. +#include "config.h" + #ifndef STA_DELAY_H #define STA_DELAY_H -// Define one of the following: - -// Define DELAY_FLOAT to use the float definitions. -#define DELAY_FLOAT - -// Define DELAY_FLOAT_CLASS to use the DelayClass definitions. -//#define DELAY_FLOAT_CLASS - -// Define DELAY_NORMAL2 to use the DelayNormal2 definitions. -//#define DELAY_NORMAL2 - -#ifdef DELAY_FLOAT +#if SSTA + // Delays are Normal PDFs with early/late sigma. + #include "DelayNormal2.hh" +#else + // Delays are floats. #include "DelayFloat.hh" #endif -#ifdef DELAY_FLOAT_CLASS - #include "DelayFloatClass.hh" -#endif - -#ifdef DELAY_NORMAL2 - #include "DelayNormal2.hh" -#endif - +// API common to DelayFloat and DelayNormal2. namespace sta { class Units; @@ -59,6 +47,13 @@ makeDelay(float delay, float sigma_late); float delayAsFloat(const Delay &delay); +// mean late+/early- sigma +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late); +float +delaySigma(const Delay &delay, + const EarlyLate *early_late); const char * delayAsString(const Delay &delay, const Units *units); @@ -69,16 +64,11 @@ const char * delayAsString(const Delay &delay, const Units *units, int digits); -// mean late+/early- sigma -// early_late = NULL returns mean. -float -delayMeanSigma(const Delay &delay, - const EarlyLate *early_late); const char * -delayMeanSigmaString(const Delay &delay, - const EarlyLate *early_late, - const Units *units, - int digits); +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const Units *units, + int digits); const Delay & delayInitValue(const MinMax *min_max); bool diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc index 1ab8b4b2..cca8198c 100644 --- a/graph/DelayFloat.cc +++ b/graph/DelayFloat.cc @@ -14,14 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include "config.h" #include "Machine.hh" #include "Fuzzy.hh" #include "Units.hh" #include "StaState.hh" #include "Delay.hh" -// Conditional compilation based on delay abstraction from Delay.hh. -#ifdef DELAY_FLOAT +// Non-SSTA compilation. +#if !SSTA namespace sta { @@ -154,21 +155,28 @@ delayAsString(const Delay &delay, } float -delayMeanSigma(const Delay &delay, - const EarlyLate *) +delayAsFloat(const Delay &delay, + const EarlyLate *) { return delay; } const char * -delayMeanSigmaString(const Delay &delay, - const EarlyLate *, - const Units *units, - int digits) +delayAsString(const Delay &delay, + const EarlyLate *, + const Units *units, + int digits) { const Unit *unit = units->timeUnit(); return unit->asString(delay, digits); } +float +delaySigma(const Delay &, + const EarlyLate *) +{ + return 0.0; +} + } // namespace #endif diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc index 7a5218a9..b58af8bc 100644 --- a/graph/DelayNormal2.cc +++ b/graph/DelayNormal2.cc @@ -14,16 +14,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include "config.h" #include // sqrt #include "Machine.hh" +#include "Error.hh" #include "StringUtil.hh" #include "Fuzzy.hh" #include "Units.hh" #include "StaState.hh" #include "Delay.hh" -// Conditional compilation based on delay abstraction from Delay.hh. -#ifdef DELAY_NORMAL2 +// SSTA compilation. +#if SSTA namespace sta { @@ -338,6 +340,25 @@ delayRatio(const Delay &delay1, return delay1.mean() / delay2.mean(); } +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late) +{ + if (early_late == EarlyLate::early()) + return delay.mean() - delay.sigmaEarly(); + else if (early_late == EarlyLate::late()) + return delay.mean() + delay.sigmaLate(); + else + internalError("unknown early/late value."); +} + +float +delaySigma(const Delay &delay, + const EarlyLate *early_late) +{ + return delay.sigma(early_late); +} + const char * delayAsString(const Delay &delay, const Units *units, @@ -360,10 +381,10 @@ delayAsString(const Delay &delay, } const char * -delayMeanSigmaString(const Delay &delay, - const EarlyLate *early_late, - const Units *units, - int digits) +delayAsString(const Delay &delay, + const EarlyLate *early_late, + const Units *units, + int digits) { float mean_sigma = delay.mean(); if (early_late == EarlyLate::early()) @@ -373,17 +394,5 @@ delayMeanSigmaString(const Delay &delay, return units->timeUnit()->asString(mean_sigma, digits); } -float -delayMeanSigma(const Delay &delay, - const EarlyLate *early_late) -{ - if (early_late == EarlyLate::early()) - return delay.mean() - delay.sigmaEarly(); - else if (early_late == EarlyLate::late()) - return delay.mean() + delay.sigmaLate(); - else - return delay.mean(); -} - } // namespace #endif diff --git a/graph/Graph.cc b/graph/Graph.cc index 53b91b00..2490a156 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -144,10 +144,9 @@ Graph::vertexAndEdgeCounts(const Instance *inst, vertex_count++; if (cell) { LibertyPort *port = network_->libertyPort(pin); - CellTimingArcSetIterator *set_iter = - cell->timingArcSetFromIterator(port); - while (set_iter->hasNext()) { - TimingArcSet *arc_set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell, port, NULL); + while (set_iter.hasNext()) { + TimingArcSet *arc_set = set_iter.next(); LibertyPort *to_port = arc_set->to(); if (network_->findPin(inst, to_port)) { if (dir->isBidirect()) { @@ -161,7 +160,6 @@ Graph::vertexAndEdgeCounts(const Instance *inst, } } } - delete set_iter; } // Count wire edges from driver pins. if (network_->isDriver(pin)) @@ -284,9 +282,9 @@ Graph::makePortInstanceEdges(const Instance *inst, LibertyCell *cell, LibertyPort *from_to_port) { - CellTimingArcSetIterator *timing_iter = cell->timingArcSetIterator(); - while (timing_iter->hasNext()) { - TimingArcSet *arc_set = timing_iter->next(); + LibertyCellTimingArcSetIterator timing_iter(cell); + while (timing_iter.hasNext()) { + TimingArcSet *arc_set = timing_iter.next(); LibertyPort *from_port = arc_set->from(); LibertyPort *to_port = arc_set->to(); if (from_to_port == NULL @@ -327,7 +325,6 @@ Graph::makePortInstanceEdges(const Instance *inst, } } } - delete timing_iter; } void @@ -915,31 +912,27 @@ Graph::removeDelayAnnotated(Edge *edge) { edge->setDelayAnnotationIsIncremental(false); TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) { setArcDelayAnnotated(edge, arc, ap_index, false); } } - delete arc_iter; } bool Graph::delayAnnotated(Edge *edge) { TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) { - if (arcDelayAnnotated(edge, arc, ap_index)) { - delete arc_iter; + if (arcDelayAnnotated(edge, arc, ap_index)) return true; - } } } - delete arc_iter; return false; } diff --git a/graph/Makefile.am b/graph/Makefile.am index 9825a8fc..2ffc4ba5 100644 --- a/graph/Makefile.am +++ b/graph/Makefile.am @@ -19,7 +19,6 @@ lib_LTLIBRARIES = libgraph.la include_HEADERS = \ Delay.hh \ DelayFloat.hh \ - DelayFloatClass.hh \ DelayNormal2.hh \ Graph.hh \ GraphClass.hh \ @@ -28,7 +27,6 @@ include_HEADERS = \ libgraph_la_SOURCES = \ Delay.cc \ DelayFloat.cc \ - DelayFloatClass.cc \ DelayNormal2.cc \ Graph.cc \ GraphCmp.cc diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc index 19e95e59..bbb44131 100644 --- a/liberty/EquivCells.cc +++ b/liberty/EquivCells.cc @@ -87,9 +87,9 @@ findEquivCells1(const LibertyLibrary *library) { LibertyCellHashMap cell_hash; LibertyCellEquivMap *cell_equivs = new LibertyCellEquivMap; - LibertyLibraryCellIterator *cell_iter = library->libertyCellIterator(); - while (cell_iter->hasNext()) { - LibertyCell *cell = cell_iter->next(); + LibertyCellIterator cell_iter(library); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); if (!cell->dontUse()) { bool found_equiv = false; unsigned hash = hashCell(cell); @@ -121,7 +121,6 @@ findEquivCells1(const LibertyLibrary *library) } } } - delete cell_iter; LibertyCellHashMap::Iterator hash_iter(cell_hash); while (hash_iter.hasNext()) { @@ -159,13 +158,13 @@ float cellDriveResistance(const LibertyCell *cell) { float max_drive = 0.0; - CellTimingArcSetIterator *set_iter = cell->timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); if (!set->role()->isTimingCheck()) { - TimingArcSetArcIterator *arc_iter = set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); GateTimingModel *model = dynamic_cast(arc->model()); if (model) { float drive = model->driveResistance(cell, NULL); @@ -173,10 +172,8 @@ cellDriveResistance(const LibertyCell *cell) max_drive = drive; } } - delete arc_iter; } } - delete set_iter; return max_drive; } @@ -190,14 +187,13 @@ static unsigned hashCellPorts(const LibertyCell *cell) { unsigned hash = 0; - LibertyCellPortIterator *port_iter = cell->libertyPortIterator(); - while (port_iter->hasNext()) { - LibertyPort *port = port_iter->next(); + LibertyCellPortIterator port_iter(cell); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); hash += hashPort(port); hash += hashFuncExpr(port->function()) * 3; hash += hashFuncExpr(port->tristateEnable()) * 5; } - delete port_iter; return hash; } @@ -212,9 +208,9 @@ static unsigned hashCellSequentials(const LibertyCell *cell) { unsigned hash = 0; - CellSequentialIterator *seq_iter = cell->sequentialIterator(); - while (seq_iter->hasNext()) { - Sequential *seq = seq_iter->next(); + LibertyCellSequentialIterator seq_iter(cell); + while (seq_iter.hasNext()) { + Sequential *seq = seq_iter.next(); hash += hashFuncExpr(seq->clock()) * 3; hash += hashFuncExpr(seq->data()) * 5; hash += hashPort(seq->output()) * 7; @@ -224,7 +220,6 @@ hashCellSequentials(const LibertyCell *cell) hash += seq->clearPresetOutput() * 17; hash += seq->clearPresetOutputInv() * 19; } - delete seq_iter; return hash; } @@ -275,9 +270,9 @@ equivCellPortsAndFuncs(const LibertyCell *cell1, if (cell1->portCount() != cell2->portCount()) return false; else { - LibertyCellPortIterator *port_iter1 = cell1->libertyPortIterator(); - while (port_iter1->hasNext()) { - LibertyPort *port1 = port_iter1->next(); + LibertyCellPortIterator port_iter1(cell1); + while (port_iter1.hasNext()) { + LibertyPort *port1 = port_iter1.next(); const char *name = port1->name(); LibertyPort *port2 = cell2->findLibertyPort(name); if (!(port2 @@ -285,11 +280,9 @@ equivCellPortsAndFuncs(const LibertyCell *cell1, && FuncExpr::equiv(port1->function(), port2->function()) && FuncExpr::equiv(port1->tristateEnable(), port2->tristateEnable()))){ - delete port_iter1; return false; } } - delete port_iter1; return true; } } @@ -301,17 +294,14 @@ equivCellPorts(const LibertyCell *cell1, if (cell1->portCount() != cell2->portCount()) return false; else { - LibertyCellPortIterator *port_iter1 = cell1->libertyPortIterator(); - while (port_iter1->hasNext()) { - LibertyPort *port1 = port_iter1->next(); + LibertyCellPortIterator port_iter1(cell1); + while (port_iter1.hasNext()) { + LibertyPort *port1 = port_iter1.next(); const char* name = port1->name(); LibertyPort *port2 = cell2->findLibertyPort(name); - if (!(port2 && LibertyPort::equiv(port1, port2))) { - delete port_iter1; + if (!(port2 && LibertyPort::equiv(port1, port2))) return false; - } } - delete port_iter1; return true; } } @@ -320,27 +310,20 @@ static bool equivCellSequentials(const LibertyCell *cell1, const LibertyCell *cell2) { - bool eq = true; - CellSequentialIterator *seq_iter1 = cell1->sequentialIterator(); - CellSequentialIterator *seq_iter2 = cell2->sequentialIterator(); - while (seq_iter1->hasNext() && seq_iter2->hasNext()) { - Sequential *seq1 = seq_iter1->next(); - Sequential *seq2 = seq_iter2->next(); + LibertyCellSequentialIterator seq_iter1(cell1); + LibertyCellSequentialIterator seq_iter2(cell2); + while (seq_iter1.hasNext() && seq_iter2.hasNext()) { + Sequential *seq1 = seq_iter1.next(); + Sequential *seq2 = seq_iter2.next(); if (!(FuncExpr::equiv(seq1->clock(), seq2->clock()) && FuncExpr::equiv(seq1->data(), seq2->data()) && LibertyPort::equiv(seq1->output(), seq2->output()) && LibertyPort::equiv(seq1->outputInv(), seq2->outputInv()) && FuncExpr::equiv(seq1->clear(), seq2->clear()) - && FuncExpr::equiv(seq1->preset(), seq2->preset()))) { - eq = false; - break; - } + && FuncExpr::equiv(seq1->preset(), seq2->preset()))) + return false; } - if (seq_iter1->hasNext() || seq_iter2->hasNext()) - eq = false; - delete seq_iter1; - delete seq_iter2; - return eq; + return !seq_iter1.hasNext() && !seq_iter2.hasNext(); } bool @@ -350,16 +333,13 @@ equivCellTimingArcSets(const LibertyCell *cell1, if (cell1->timingArcSetCount() != cell2->timingArcSetCount()) return false; else { - CellTimingArcSetIterator *set_iter1 = cell1->timingArcSetIterator(); - while (set_iter1->hasNext()) { - TimingArcSet *set1 = set_iter1->next(); + LibertyCellTimingArcSetIterator set_iter1(cell1); + while (set_iter1.hasNext()) { + TimingArcSet *set1 = set_iter1.next(); TimingArcSet *set2 = cell2->findTimingArcSet(set1); - if (!(set2 && TimingArcSet::equiv(set1, set2))) { - delete set_iter1; + if (!(set2 && TimingArcSet::equiv(set1, set2))) return false; - } } - delete set_iter1; return true; } } diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc index 2caadaf7..2f131df1 100644 --- a/liberty/InternalPower.cc +++ b/liberty/InternalPower.cc @@ -31,6 +31,21 @@ InternalPowerAttrs::InternalPowerAttrs() : InternalPowerAttrs::~InternalPowerAttrs() { +} + +void +InternalPowerAttrs::deleteContents() +{ + TransRiseFallIterator tr_iter; + while (tr_iter.hasNext()) { + TransRiseFall *tr = tr_iter.next(); + int tr_index = tr->index(); + InternalPowerModel *model = models_[tr_index]; + if (model) + delete model; + } + if (when_) + when_->deleteSubexprs(); stringDelete(related_pg_pin_); } @@ -63,7 +78,7 @@ InternalPower::InternalPower(LibertyCell *cell, port_(port), related_port_(related_port), when_(attrs->when()), - related_pg_pin_(stringCopy(attrs->relatedPgPin())) + related_pg_pin_(attrs->relatedPgPin()) { TransRiseFallIterator tr_iter; while (tr_iter.hasNext()) { @@ -76,17 +91,7 @@ InternalPower::InternalPower(LibertyCell *cell, InternalPower::~InternalPower() { - TransRiseFallIterator tr_iter; - while (tr_iter.hasNext()) { - TransRiseFall *tr = tr_iter.next(); - int tr_index = tr->index(); - InternalPowerModel *model = models_[tr_index]; - if (model) - delete model; - } - if (when_) - when_->deleteSubexprs(); - stringDelete(related_pg_pin_); + // models_, when_ and related_pg_pin_ are owned by InternalPowerAttrs. } LibertyCell * diff --git a/liberty/InternalPower.hh b/liberty/InternalPower.hh index 9310d52d..efa86b53 100644 --- a/liberty/InternalPower.hh +++ b/liberty/InternalPower.hh @@ -30,7 +30,8 @@ class InternalPowerAttrs { public: InternalPowerAttrs(); - ~InternalPowerAttrs(); + virtual ~InternalPowerAttrs(); + void deleteContents(); FuncExpr *when() const { return when_; } FuncExpr *&whenRef() { return when_; } void setModel(TransRiseFall *tr, diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 9a1cb5bf..68dba11c 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -117,7 +117,7 @@ LibertyLibrary::LibertyLibrary(const char *name, LibertyLibrary::~LibertyLibrary() { bus_dcls_.deleteContents(); - templates_.deleteContents(); + template_map_.deleteContents(); scale_factors_map_.deleteContents(); delete scale_factors_; @@ -147,7 +147,7 @@ void LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern, LibertyCellSeq *cells) { - LibertyLibraryCellIterator cell_iter(this); + LibertyCellIterator cell_iter(this); while (cell_iter.hasNext()) { LibertyCell *cell = cell_iter.next(); if (pattern->match(cell->name())) @@ -155,12 +155,6 @@ LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern, } } -LibertyLibraryCellIterator * -LibertyLibrary::libertyCellIterator() const -{ - return new LibertyLibraryCellIterator(this); -} - void LibertyLibrary::setDelayModelType(DelayModelType type) { @@ -182,25 +176,19 @@ LibertyLibrary::findBusDcl(const char *name) const void LibertyLibrary::addTableTemplate(TableTemplate *tbl_template) { - templates_[tbl_template->name()] = tbl_template; + template_map_[tbl_template->name()] = tbl_template; } void LibertyLibrary::deleteTableTemplate(TableTemplate *tbl_template) { - templates_.eraseKey(tbl_template->name()); + template_map_.eraseKey(tbl_template->name()); } TableTemplate * LibertyLibrary::findTableTemplate(const char *name) { - return templates_[name]; -} - -TableTemplateIterator * -LibertyLibrary::tableTemplateIterator() -{ - return new TableTemplateIterator(templates_); + return template_map_[name]; } void @@ -600,12 +588,6 @@ LibertyLibrary::findOperatingConditions(const char *name) return operating_conditions_.findKey(name); } -OperatingConditionsIterator * -LibertyLibrary::operatingConditionsIterator() -{ - return new OperatingConditionsIterator(operating_conditions_); -} - OperatingConditions * LibertyLibrary::defaultOperatingConditions() const { @@ -697,15 +679,14 @@ LibertyLibrary::makeCornerMap(LibertyLibrary *lib, int ap_index, Network *network) { - LibertyLibraryCellIterator *cell_iter = lib->libertyCellIterator(); - while (cell_iter->hasNext()) { - LibertyCell *cell = cell_iter->next(); + LibertyCellIterator cell_iter(lib); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); const char *name = cell->name(); LibertyCell *link_cell = network->findLibertyCell(name); if (link_cell) makeCornerMap(link_cell, cell, ap_index); } - delete cell_iter; } // Map a cell linked in the network to the corresponding liberty cell @@ -717,35 +698,30 @@ LibertyLibrary::makeCornerMap(LibertyCell *link_cell, { link_cell->setCornerCell(corner_cell, ap_index); - LibertyCellPortBitIterator *port_iter = link_cell->libertyPortBitIterator(); - while (port_iter->hasNext()) { - LibertyPort *link_port = port_iter->next(); + LibertyCellPortBitIterator port_iter(link_cell); + while (port_iter.hasNext()) { + LibertyPort *link_port = port_iter.next(); const char *port_name = link_port->name(); LibertyPort *corner_port = corner_cell->findLibertyPort(port_name); if (corner_port) link_port->setCornerPort(corner_port, ap_index); } - delete port_iter; - CellTimingArcSetIterator *set_iter = link_cell->timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *link_arc_set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(link_cell); + while (set_iter.hasNext()) { + TimingArcSet *link_arc_set = set_iter.next(); TimingArcSet *corner_arc_set = corner_cell->findTimingArcSet(link_arc_set); if (corner_arc_set) { - TimingArcSetArcIterator *arc_iter = link_arc_set->timingArcIterator(); - TimingArcSetArcIterator *corner_arc_iter = - corner_arc_set->timingArcIterator(); - while (arc_iter->hasNext() && corner_arc_iter->hasNext()) { - TimingArc *link_arc = arc_iter->next(); - TimingArc *corner_arc = corner_arc_iter->next(); + TimingArcSetArcIterator arc_iter(link_arc_set); + TimingArcSetArcIterator corner_arc_iter(corner_arc_set); + while (arc_iter.hasNext() && corner_arc_iter.hasNext()) { + TimingArc *link_arc = arc_iter.next(); + TimingArc *corner_arc = corner_arc_iter.next(); if (TimingArc::equiv(link_arc, corner_arc)) link_arc->setCornerArc(corner_arc, ap_index); } - delete arc_iter; - delete corner_arc_iter; } } - delete set_iter; } //////////////////////////////////////////////////////////////// @@ -788,20 +764,20 @@ LibertyLibrary::addOcvDerate(OcvDerate *derate) //////////////////////////////////////////////////////////////// -LibertyLibraryCellIterator::LibertyLibraryCellIterator(const LibertyLibrary * +LibertyCellIterator::LibertyCellIterator(const LibertyLibrary * library): iter_(library->cell_map_) { } bool -LibertyLibraryCellIterator::hasNext() +LibertyCellIterator::hasNext() { return iter_.hasNext(); } LibertyCell * -LibertyLibraryCellIterator::next() +LibertyCellIterator::next() { return dynamic_cast(iter_.next()); } @@ -849,13 +825,13 @@ LibertyCell::~LibertyCell() latch_d_to_q_map_.deleteContents(); + deleteTimingArcAttrs(); if (timing_arc_sets_) { - deleteTimingModels(); timing_arc_sets_->deleteContents(); delete timing_arc_sets_; delete timing_arc_set_map_; - LibertyPortTimingArcSetMap::Iterator port_map_iter(port_timing_arc_set_map_); + LibertyPortPairTimingArcMap::Iterator port_map_iter(port_timing_arc_set_map_); while (port_map_iter.hasNext()) { LibertyPortPair *port_pair; TimingArcSetSeq *sets; @@ -872,6 +848,7 @@ LibertyCell::~LibertyCell() delete timing_arc_set_to_map_; } + deleteInternalPowerAttrs(); if (internal_powers_) { internal_powers_->deleteContents(); delete internal_powers_; @@ -900,68 +877,17 @@ LibertyCell::~LibertyCell() } // Multiple timing arc sets (buses bits or a related_ports list) -// can share the same model, cond, and sdf_conds, so collect them -// into a set so they are only deleted once. +// can share the same TimingAttrs values (model, cond, and sdf_conds), +// so collect them into a set so they are only deleted once. void -LibertyCell::deleteTimingModels() +LibertyCell::deleteTimingArcAttrs() { - TimingModelSet models; - FuncExprSet exprs; - StringSetEq strings; - - TimingArcSetSeq::Iterator set_iter(timing_arc_sets_); - while (set_iter.hasNext()) { - TimingArcSet *set = set_iter.next(); - TimingArcSetArcIterator *arc_iter = set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); - TimingModel *model = arc->model(); - if (model) - models.insert(model); - } - delete arc_iter; - - FuncExpr *cond = set->cond(); - if (cond) - exprs.insert(cond); - - const char *str = set->sdfCondStart(); - if (str) - strings.insert(str); - - str = set->sdfCondEnd(); - if (str) - strings.insert(str); - - str = set->modeName(); - if (str) - strings.insert(str); - - str = set->modeValue(); - if (str) - strings.insert(str); - + TimingArcAttrsSeq::Iterator attr_iter(timing_arc_attrs_); + while (attr_iter.hasNext()) { + TimingArcAttrs *attrs = attr_iter.next(); + attrs->deleteContents(); + delete attrs; } - models.deleteContents(); - - FuncExprSet::Iterator expr_iter(exprs); - while (expr_iter.hasNext()) { - FuncExpr *expr = expr_iter.next(); - expr->deleteSubexprs(); - } - deleteContents(&strings); -} - -LibertyCellPortIterator * -LibertyCell::libertyPortIterator() const -{ - return new LibertyCellPortIterator(this); -} - -LibertyCellPortBitIterator * -LibertyCell::libertyPortBitIterator() const -{ - return new LibertyCellPortBitIterator(this); } LibertyPort * @@ -1097,9 +1023,9 @@ LibertyCell::addTimingArcSet(TimingArcSet *arc_set) if (timing_arc_sets_ == NULL) { timing_arc_sets_ = new TimingArcSetSeq; timing_arc_set_map_ = new TimingArcSetMap; - port_timing_arc_set_map_ = new LibertyPortTimingArcSetMap; - timing_arc_set_from_map_ = new LibertyPortTimingArcSetSeqMap; - timing_arc_set_to_map_ = new LibertyPortTimingArcSetSeqMap; + port_timing_arc_set_map_ = new LibertyPortPairTimingArcMap; + timing_arc_set_from_map_ = new LibertyPortTimingArcMap; + timing_arc_set_to_map_ = new LibertyPortTimingArcMap; } int set_index = timing_arc_sets_->size(); if (set_index > timing_arc_set_index_max) @@ -1142,6 +1068,12 @@ LibertyCell::addTimingArcSet(TimingArcSet *arc_set) return set_index; } +void +LibertyCell::addTimingArcAttrs(TimingArcAttrs *attrs) +{ + timing_arc_attrs_.push_back(attrs); +} + void LibertyCell::addInternalPower(InternalPower *power) { @@ -1150,10 +1082,22 @@ LibertyCell::addInternalPower(InternalPower *power) internal_powers_->push_back(power); } -LibertyCellInternalPowerIterator * -LibertyCell::internalPowerIterator() -{ - return new LibertyCellInternalPowerIterator(internal_powers_); +void +LibertyCell::addInternalPowerAttrs(InternalPowerAttrs *attrs) +{ + internal_power_attrs_.push_back(attrs); +} + +void +LibertyCell::deleteInternalPowerAttrs() +{ + InternalPowerAttrsSeq::Iterator attr_iter(internal_power_attrs_); + while (attr_iter.hasNext()) { + InternalPowerAttrs *attrs = attr_iter.next(); + attrs->deleteContents(); + delete attrs; + } + } void @@ -1164,18 +1108,6 @@ LibertyCell::addLeakagePower(LeakagePower *power) leakage_powers_->push_back(power); } -LibertyCellLeakagePowerIterator * -LibertyCell::leakagePowerIterator() -{ - return new LibertyCellLeakagePowerIterator(leakage_powers_); -} - -float -LibertyCell::leakagePower() const -{ - return leakage_power_; -} - void LibertyCell::setLeakagePower(float leakage) { @@ -1198,7 +1130,7 @@ LibertyCell::finish(bool infer_latches, void LibertyCell::findDefaultCondArcs() { - LibertyPortTimingArcSetMap::Iterator set_iter1(port_timing_arc_set_map_); + LibertyPortPairTimingArcMap::Iterator set_iter1(port_timing_arc_set_map_); while (set_iter1.hasNext()) { LibertyPortPair *port_pair; TimingArcSetSeq *sets; @@ -1230,18 +1162,17 @@ void LibertyCell::translatePresetClrCheckRoles() { LibertyPortSet pre_clr_ports; - CellTimingArcSetIterator *set_iter = timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(this); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); if (set->role() == TimingRole::regSetClr()) pre_clr_ports.insert(set->from()); } - delete set_iter; if (!pre_clr_ports.empty()) { - set_iter = timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(this); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); if (pre_clr_ports.findKey(set->to())) { if (set->role() == TimingRole::setup()) set->setRole(TimingRole::recovery()); @@ -1249,10 +1180,26 @@ LibertyCell::translatePresetClrCheckRoles() set->setRole(TimingRole::removal()); } } - delete set_iter; } } +TimingArcSetSeq * +LibertyCell::timingArcSets(const LibertyPort *from, + const LibertyPort *to) const +{ + if (timing_arc_sets_) { + if (from && to) { + LibertyPortPair port_pair(from, to); + return port_timing_arc_set_map_->findKey(&port_pair); + } + else if (from) + return timing_arc_set_from_map_->findKey(from); + else if (to) + return timing_arc_set_to_map_->findKey(to); + } + return NULL; +} + TimingArcSet * LibertyCell::findTimingArcSet(TimingArcSet *key) const { @@ -1277,36 +1224,6 @@ LibertyCell::timingArcSetCount() const return 0; } -CellTimingArcSetIterator * -LibertyCell::timingArcSetIterator() const -{ - return new CellTimingArcSetIterator(timing_arc_sets_); -} - -CellTimingArcSetIterator * -LibertyCell::timingArcSetIterator(LibertyPort *from, - LibertyPort *to) const -{ - if (timing_arc_sets_) { - LibertyPortPair port_pair(from, to); - TimingArcSetSeq *sets = port_timing_arc_set_map_->findKey(&port_pair); - return new CellTimingArcSetIterator(sets); - } - else - return new CellTimingArcSetIterator(NULL); -} - -CellTimingArcSetIterator * -LibertyCell::timingArcSetFromIterator(LibertyPort *from) const -{ - if (timing_arc_sets_) { - TimingArcSetSeq *sets = timing_arc_set_from_map_->findKey(from); - return new CellTimingArcSetIterator(sets); - } - else - return new CellTimingArcSetIterator(NULL); -} - bool LibertyCell::hasTimingArcs(LibertyPort *port) const { @@ -1315,17 +1232,6 @@ LibertyCell::hasTimingArcs(LibertyPort *port) const || timing_arc_set_to_map_->findKey(port)); } -CellTimingArcSetIterator * -LibertyCell::timingArcSetToIterator(LibertyPort *to) const -{ - if (timing_arc_sets_) { - TimingArcSetSeq *sets = timing_arc_set_to_map_->findKey(to); - return new CellTimingArcSetIterator(sets); - } - else - return new CellTimingArcSetIterator(NULL); -} - void LibertyCell::makeSequential(int size, bool is_register, @@ -1386,12 +1292,6 @@ LibertyCell::hasSequentials() const return sequentials_ && !sequentials_->empty(); } -CellSequentialIterator * -LibertyCell::sequentialIterator() const -{ - return new CellSequentialIterator(sequentials_); -} - void LibertyCell::addScaledCell(OperatingConditions *op_cond, LibertyCell *scaled_cell) @@ -1400,37 +1300,31 @@ LibertyCell::addScaledCell(OperatingConditions *op_cond, scaled_cells_ = new ScaledCellMap; (*scaled_cells_)[op_cond] = scaled_cell; - LibertyCellPortBitIterator *port_iter1 = libertyPortBitIterator(); - LibertyCellPortBitIterator *port_iter2=scaled_cell->libertyPortBitIterator(); - while (port_iter1->hasNext() && port_iter2->hasNext()) { - LibertyPort *port = port_iter1->next(); - LibertyPort *scaled_port = port_iter2->next(); + LibertyCellPortBitIterator port_iter1(this); + LibertyCellPortBitIterator port_iter2(scaled_cell); + while (port_iter1.hasNext() && port_iter2.hasNext()) { + LibertyPort *port = port_iter1.next(); + LibertyPort *scaled_port = port_iter2.next(); port->addScaledPort(op_cond, scaled_port); } - delete port_iter1; - delete port_iter2; - CellTimingArcSetIterator *set_iter1 = timingArcSetIterator(); - CellTimingArcSetIterator *set_iter2 = scaled_cell->timingArcSetIterator(); - while (set_iter1->hasNext() && set_iter2->hasNext()) { - TimingArcSet *arc_set1 = set_iter1->next(); - TimingArcSet *arc_set2 = set_iter2->next(); - TimingArcSetArcIterator *arc_iter1 = arc_set1->timingArcIterator(); - TimingArcSetArcIterator *arc_iter2 = arc_set2->timingArcIterator(); - while (arc_iter1->hasNext() && arc_iter2->hasNext()) { - TimingArc *arc = arc_iter1->next(); - TimingArc *scaled_arc = arc_iter2->next(); + LibertyCellTimingArcSetIterator set_iter1(this); + LibertyCellTimingArcSetIterator set_iter2(scaled_cell); + while (set_iter1.hasNext() && set_iter2.hasNext()) { + TimingArcSet *arc_set1 = set_iter1.next(); + TimingArcSet *arc_set2 = set_iter2.next(); + TimingArcSetArcIterator arc_iter1(arc_set1); + TimingArcSetArcIterator arc_iter2(arc_set2); + while (arc_iter1.hasNext() && arc_iter2.hasNext()) { + TimingArc *arc = arc_iter1.next(); + TimingArc *scaled_arc = arc_iter2.next(); if (TimingArc::equiv(arc, scaled_arc)) { TimingModel *model = scaled_arc->model(); model->setIsScaled(true); arc->addScaledModel(op_cond, model); } } - delete arc_iter1; - delete arc_iter2; } - delete set_iter1; - delete set_iter2; } void @@ -1519,6 +1413,20 @@ LibertyCell::addOcvDerate(OcvDerate *derate) //////////////////////////////////////////////////////////////// +LibertyCellTimingArcSetIterator::LibertyCellTimingArcSetIterator(const LibertyCell *cell) : + TimingArcSetSeq::Iterator(cell->timing_arc_sets_) +{ +} + +LibertyCellTimingArcSetIterator::LibertyCellTimingArcSetIterator(const LibertyCell *cell, + const LibertyPort *from, + const LibertyPort *to): + TimingArcSetSeq::Iterator(cell->timingArcSets(from, to)) +{ +} + +//////////////////////////////////////////////////////////////// + // Latch enable port/function for a latch D->Q timing arc set. class LatchEnable { @@ -1581,30 +1489,28 @@ LibertyCell::makeLatchEnables(Report *report, { if (hasSequentials() || hasInferedRegTimingArcs()) { - CellTimingArcSetIterator *set_iter = timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *en_to_q = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(this); + while (set_iter.hasNext()) { + TimingArcSet *en_to_q = set_iter.next(); if (en_to_q->role() == TimingRole::latchEnToQ()) { LibertyPort *en = en_to_q->from(); LibertyPort *q = en_to_q->to(); - CellTimingArcSetIterator *to_iter = timingArcSetToIterator(q); - while (to_iter->hasNext()) { - TimingArcSet *d_to_q = to_iter->next(); + LibertyCellTimingArcSetIterator to_iter(this, NULL, q); + while (to_iter.hasNext()) { + TimingArcSet *d_to_q = to_iter.next(); if (d_to_q->role() == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); - CellTimingArcSetIterator *check_iter = timingArcSetIterator(en, d); - while (check_iter->hasNext()) { - TimingArcSet *setup_check = check_iter->next(); + LibertyCellTimingArcSetIterator check_iter(this, en, d); + while (check_iter.hasNext()) { + TimingArcSet *setup_check = check_iter.next(); if (setup_check->role() == TimingRole::setup()) { LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); - TimingArcSetArcIterator *check_arc_iter = - setup_check->timingArcIterator(); - if (check_arc_iter->hasNext()) { - TimingArc *check_arc = check_arc_iter->next(); - delete check_arc_iter; + TimingArcSetArcIterator check_arc_iter( setup_check); + if (check_arc_iter.hasNext()) { + TimingArc *check_arc = check_arc_iter.next(); TransRiseFall *en_tr = latch_enable->enableTransition(); TransRiseFall *check_tr = check_arc->fromTrans()->asRiseFall(); if (check_tr == en_tr) { @@ -1640,16 +1546,12 @@ LibertyCell::makeLatchEnables(Report *report, } break; } - delete check_arc_iter; } } - delete check_iter; } } - delete to_iter; } } - delete set_iter; } } @@ -1657,19 +1559,16 @@ FuncExpr * LibertyCell::findLatchEnableFunc(LibertyPort *data, LibertyPort *enable) const { - CellSequentialIterator *iter = sequentialIterator(); - while (iter->hasNext()) { - Sequential *seq = iter->next(); + LibertyCellSequentialIterator iter(this); + while (iter.hasNext()) { + Sequential *seq = iter.next(); if (seq->isLatch() && seq->data() && seq->data()->hasPort(data) && seq->clock() - && seq->clock()->hasPort(enable)) { - delete iter; + && seq->clock()->hasPort(enable)) return seq->clock(); - } } - delete iter; return NULL; } @@ -1703,16 +1602,16 @@ LibertyCell::inferLatchRoles(Debug *debug) if (hasInferedRegTimingArcs()) { // Hunt down potential latch D/EN/Q triples. LatchEnableSet latch_enables; - CellTimingArcSetIterator *set_iter = timingArcSetIterator(); - while (set_iter->hasNext()) { - TimingArcSet *en_to_q = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(this); + while (set_iter.hasNext()) { + TimingArcSet *en_to_q = set_iter.next(); // Locate potential d->q arcs from reg clk->q arcs. if (en_to_q->role() == TimingRole::regClkToQ()) { LibertyPort *en = en_to_q->from(); LibertyPort *q = en_to_q->to(); - CellTimingArcSetIterator *to_iter = timingArcSetToIterator(q); - while (to_iter->hasNext()) { - TimingArcSet *d_to_q = to_iter->next(); + LibertyCellTimingArcSetIterator to_iter(this, NULL, q); + while (to_iter.hasNext()) { + TimingArcSet *d_to_q = to_iter.next(); // Look for combinational d->q arcs. TimingRole *d_to_q_role = d_to_q->role(); if ((d_to_q_role == TimingRole::combinational() @@ -1724,22 +1623,19 @@ LibertyCell::inferLatchRoles(Debug *debug) || d_to_q_role == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); // Check for setup check from en -> d. - CellTimingArcSetIterator *check_iter = timingArcSetIterator(en, d); - while (check_iter->hasNext()) { - TimingArcSet *setup_check = check_iter->next(); + LibertyCellTimingArcSetIterator check_iter(this, en, d); + while (check_iter.hasNext()) { + TimingArcSet *setup_check = check_iter.next(); if (setup_check->role() == TimingRole::setup()) { makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); d_to_q->setRole(TimingRole::latchDtoQ()); en_to_q->setRole(TimingRole::latchEnToQ()); } } - delete check_iter; } } - delete to_iter; } } - delete set_iter; } } @@ -1869,12 +1765,6 @@ LibertyPort::setDirection(PortDirection *dir) liberty_cell_->setHasInternalPorts(true); } -LibertyPortMemberIterator * -LibertyPort::libertyMemberIterator() const -{ - return new LibertyPortMemberIterator(this); -} - LibertyPort * LibertyPort::findLibertyMember(int index) const { @@ -1903,12 +1793,11 @@ LibertyPort::setCapacitance(const TransRiseFall *tr, { capacitance_.setValue(tr, min_max, cap); if (hasMembers()) { - LibertyPortMemberIterator *member_iter = libertyMemberIterator(); - while (member_iter->hasNext()) { - LibertyPort *port_bit = member_iter->next(); + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); port_bit->setCapacitance(tr, min_max, cap); } - delete member_iter; } } @@ -1958,15 +1847,14 @@ LibertyPort::setFunction(FuncExpr *func) { function_ = func; if (is_bus_ || is_bundle_) { - LibertyPortMemberIterator *member_iter = libertyMemberIterator(); + LibertyPortMemberIterator member_iter(this); int bit_offset = 0; - while (member_iter->hasNext()) { - LibertyPort *port_bit = member_iter->next(); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); FuncExpr *sub_expr = (func) ? func->bitSubExpr(bit_offset) : NULL; port_bit->setFunction(sub_expr); bit_offset++; } - delete member_iter; } } @@ -1975,14 +1863,13 @@ LibertyPort::setTristateEnable(FuncExpr *enable) { tristate_enable_ = enable; if (hasMembers()) { - LibertyPortMemberIterator *member_iter = libertyMemberIterator(); - while (member_iter->hasNext()) { - LibertyPort *port_bit = member_iter->next(); + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); FuncExpr *sub_expr = (enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : NULL; port_bit->setTristateEnable(sub_expr); } - delete member_iter; } } diff --git a/liberty/Liberty.hh b/liberty/Liberty.hh index 256b92c6..d8cb89a6 100644 --- a/liberty/Liberty.hh +++ b/liberty/Liberty.hh @@ -28,7 +28,7 @@ namespace sta { -class LibertyLibraryCellIterator; +class LibertyCellIterator; class LibertyCellPortIterator; class LibertyCellPortBitIterator; class LibertyPortMemberIterator; @@ -41,34 +41,33 @@ class Debug; class LibertyBuilder; class LibertyReader; class OcvDerate; +class TimingArcAttrs; +class InternalPowerAttrs; typedef Set LibrarySet; typedef Map TableTemplateMap; -typedef TableTemplateMap::Iterator TableTemplateIterator; typedef Map BusDclMap; typedef Map ScaleFactorsMap; typedef Map WireloadMap; typedef Map WireloadSelectionMap; typedef Map OperatingConditionsMap; -typedef OperatingConditionsMap::Iterator OperatingConditionsIterator; typedef Map PortToSequentialMap; typedef Vector TimingArcSetSeq; -typedef TimingArcSetSeq::ConstIterator CellTimingArcSetIterator; typedef Set TimingArcSetMap; +typedef Map LibertyPortPairTimingArcMap; typedef Vector InternalPowerSeq; -typedef InternalPowerSeq::Iterator LibertyCellInternalPowerIterator; typedef Vector LeakagePowerSeq; -typedef LeakagePowerSeq::Iterator LibertyCellLeakagePowerIterator; -typedef Map LibertyPortTimingArcSetSeqMap; +typedef Map LibertyPortTimingArcMap; typedef Map ScaledCellMap; typedef Map ScaledPortMap; typedef Map ModeDefMap; typedef Map ModeValueMap; typedef Map LatchEnableMap; typedef Map OcvDerateMap; -typedef Map LibertyPortTimingArcSetMap; +typedef Vector TimingArcAttrsSeq; +typedef Vector InternalPowerAttrsSeq; typedef enum { clock_gate_none, @@ -132,14 +131,12 @@ public: LibertyCell *findLibertyCell(const char *name) const; void findLibertyCellsMatching(PatternMatch *pattern, LibertyCellSeq *cells); - LibertyLibraryCellIterator *libertyCellIterator() const; DelayModelType delayModelType() const { return delay_model_type_; } void setDelayModelType(DelayModelType type); void addBusDcl(BusDcl *bus_dcl); BusDcl *findBusDcl(const char *name) const; void addTableTemplate(TableTemplate *tbl_template); TableTemplate *findTableTemplate(const char *name); - TableTemplateIterator *tableTemplateIterator(); float nominalProcess() { return nominal_process_; } void setNominalProcess(float process); float nominalVoltage() const { return nominal_voltage_; } @@ -254,7 +251,6 @@ public: void setDefaultWireloadSelection(WireloadSelection *selection); OperatingConditions *findOperatingConditions(const char *name); - OperatingConditionsIterator *operatingConditionsIterator(); OperatingConditions *defaultOperatingConditions() const; void addOperatingConditions(OperatingConditions *op_cond); void setDefaultOperatingConditions(OperatingConditions *op_cond); @@ -291,7 +287,7 @@ protected: Units *units_; DelayModelType delay_model_type_; BusDclMap bus_dcls_; - TableTemplateMap templates_; + TableTemplateMap template_map_; float nominal_process_; float nominal_voltage_; float nominal_temperature_; @@ -340,22 +336,40 @@ protected: private: DISALLOW_COPY_AND_ASSIGN(LibertyLibrary); - friend class LibertyLibraryCellIterator; + friend class LibertyCellIterator; + friend class TableTemplateIterator; + friend class OperatingConditionsIterator; }; -class LibertyLibraryCellIterator : public Iterator +class LibertyCellIterator : public Iterator { public: - explicit LibertyLibraryCellIterator(const LibertyLibrary *library); + explicit LibertyCellIterator(const LibertyLibrary *library); bool hasNext(); LibertyCell *next(); private: - DISALLOW_COPY_AND_ASSIGN(LibertyLibraryCellIterator); + DISALLOW_COPY_AND_ASSIGN(LibertyCellIterator); ConcreteCellMap::ConstIterator iter_; }; +class TableTemplateIterator : public TableTemplateMap::ConstIterator +{ +public: + TableTemplateIterator(const LibertyLibrary *library) : + TableTemplateMap::ConstIterator(library->template_map_) {} +}; + +class OperatingConditionsIterator : public OperatingConditionsMap::ConstIterator +{ +public: + OperatingConditionsIterator(const LibertyLibrary *library) : + OperatingConditionsMap::ConstIterator(library->operating_conditions_) {} +}; + +//////////////////////////////////////////////////////////////// + class LibertyCell : public ConcreteCell { public: @@ -365,8 +379,6 @@ public: virtual ~LibertyCell(); const LibertyLibrary *libertyLibrary() const { return liberty_library_; } LibertyLibrary *libertyLibrary() { return liberty_library_; } - LibertyCellPortIterator *libertyPortIterator() const; - LibertyCellPortBitIterator *libertyPortBitIterator() const; LibertyPort *findLibertyPort(const char *name) const; void findLibertyPortsMatching(PatternMatch *pattern, LibertyPortSeq *ports) const; @@ -395,27 +407,26 @@ public: bool isClockGate() const; void setClockGateType(ClockGateType clock_gate_type); virtual unsigned addTimingArcSet(TimingArcSet *set); + void addTimingArcAttrs(TimingArcAttrs *attrs); virtual void addInternalPower(InternalPower *power); + void addInternalPowerAttrs(InternalPowerAttrs *attrs); virtual void addLeakagePower(LeakagePower *power); // Call after cell is finished being constructed. virtual void finish(bool infer_latches, Report *report, Debug *debug); + // Internal to LibertyCellTimingArcSetIterator. + TimingArcSetSeq *timingArcSets(const LibertyPort *from, + const LibertyPort *to) const; size_t timingArcSetCount() const; // Find a timing arc set equivalent to key. TimingArcSet *findTimingArcSet(TimingArcSet *key) const; TimingArcSet *findTimingArcSet(unsigned arc_set_index) const; - CellTimingArcSetIterator *timingArcSetIterator() const; - CellTimingArcSetIterator *timingArcSetIterator(LibertyPort *from, - LibertyPort *to) const; - CellTimingArcSetIterator *timingArcSetFromIterator(LibertyPort *from) const; - CellTimingArcSetIterator *timingArcSetToIterator(LibertyPort *to) const; bool hasTimingArcs(LibertyPort *port) const; - LibertyCellInternalPowerIterator *internalPowerIterator(); - LibertyCellLeakagePowerIterator *leakagePowerIterator(); - float leakagePower() const; + InternalPowerSeq *internalPowers() const { return internal_powers_; } + LeakagePowerSeq *leakagePowers() const { return leakage_powers_; } + float leakagePower() const { return leakage_power_; } bool hasSequentials() const; - CellSequentialIterator *sequentialIterator() const; void makeSequential(int size, bool is_register, FuncExpr *clk, @@ -463,7 +474,7 @@ protected: virtual void addPort(ConcretePort *port); void setHasInternalPorts(bool has_internal); void setLibertyLibrary(LibertyLibrary *library); - void deleteTimingModels(); + void deleteTimingArcAttrs(); void makeLatchEnables(Report *report, Debug *debug); FuncExpr *findLatchEnableFunc(LibertyPort *data, @@ -478,6 +489,7 @@ protected: void findDefaultCondArcs(); virtual void translatePresetClrCheckRoles(); virtual void inferLatchRoles(Debug *debug); + void deleteInternalPowerAttrs(); LibertyLibrary *liberty_library_; float area_; @@ -488,12 +500,15 @@ protected: bool interface_timing_; ClockGateType clock_gate_type_; TimingArcSetSeq *timing_arc_sets_; + // Used to find matching arc sets in equivalent cells. TimingArcSetMap *timing_arc_set_map_; - LibertyPortTimingArcSetMap *port_timing_arc_set_map_; - LibertyPortTimingArcSetSeqMap *timing_arc_set_from_map_; - LibertyPortTimingArcSetSeqMap *timing_arc_set_to_map_; + LibertyPortPairTimingArcMap *port_timing_arc_set_map_; + LibertyPortTimingArcMap *timing_arc_set_from_map_; + LibertyPortTimingArcMap *timing_arc_set_to_map_; + TimingArcAttrsSeq timing_arc_attrs_; bool has_infered_reg_timing_arcs_; InternalPowerSeq *internal_powers_; + InternalPowerAttrsSeq internal_power_attrs_; LeakagePowerSeq *leakage_powers_; SequentialSeq *sequentials_; PortToSequentialMap *port_to_seq_map_; @@ -522,6 +537,8 @@ private: friend class LibertyCellPortIterator; friend class LibertyPort; friend class LibertyBuilder; + friend class LibertyCellTimingArcSetIterator; + friend class LibertyCellSequentialIterator; }; class LibertyCellPortIterator : public Iterator @@ -551,11 +568,42 @@ private: ConcreteCellPortBitIterator *iter_; }; +class LibertyCellTimingArcSetIterator : public TimingArcSetSeq::Iterator +{ +public: + LibertyCellTimingArcSetIterator(const LibertyCell *cell); + LibertyCellTimingArcSetIterator(const LibertyCell *cell, + const LibertyPort *from, + const LibertyPort *to); +}; + +class LibertyCellSequentialIterator : public SequentialSeq::ConstIterator +{ +public: + LibertyCellSequentialIterator(const LibertyCell *cell) : + SequentialSeq::ConstIterator(cell->sequentials_) {} +}; + +class LibertyCellLeakagePowerIterator : public LeakagePowerSeq::Iterator +{ +public: + LibertyCellLeakagePowerIterator(const LibertyCell *cell) : + LeakagePowerSeq::Iterator(cell->leakagePowers()) {} +}; + +class LibertyCellInternalPowerIterator : public InternalPowerSeq::Iterator +{ +public: + LibertyCellInternalPowerIterator(const LibertyCell *cell) : + InternalPowerSeq::Iterator(cell->internalPowers()) {} +}; + +//////////////////////////////////////////////////////////////// + class LibertyPort : public ConcretePort { public: LibertyCell *libertyCell() const { return liberty_cell_; } - LibertyPortMemberIterator *libertyMemberIterator() const; LibertyPort *findLibertyMember(int index) const; LibertyPort *findLibertyBusBit(int index) const; float capacitance(const TransRiseFall *tr, diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index 0ee13853..3d9d4be8 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -276,36 +276,28 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, if (to_rise) { to_tr = TransRiseFall::rise(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); - attrs->setModelRef(to_tr, true); - } } if (to_fall) { to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); - attrs->setModelRef(to_tr, true); - } } break; case timing_sense_negative_unate: if (to_fall) { to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); - attrs->setModelRef(to_tr, true); - } } if (to_rise) { to_tr = TransRiseFall::rise(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); - attrs->setModelRef(to_tr, true); - } } break; case timing_sense_non_unate: @@ -320,7 +312,6 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, if (model) { makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); - attrs->setModelRef(to_tr, true); } } if (to_rise) { @@ -329,7 +320,6 @@ LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, if (model) { makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); - attrs->setModelRef(to_tr, true); } } break; @@ -355,7 +345,6 @@ LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, TransRiseFall *from_tr = (sense == timing_sense_negative_unate) ? to_tr->opposite() : to_tr; makeTimingArc(arc_set, from_tr, to_tr, model); - attrs->setModelRef(to_tr, true); } to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); @@ -363,7 +352,6 @@ LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, TransRiseFall *from_tr = (sense == timing_sense_negative_unate) ? to_tr->opposite() : to_tr; makeTimingArc(arc_set, from_tr, to_tr, model); - attrs->setModelRef(to_tr, true); } return arc_set; } @@ -420,16 +408,12 @@ LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, TransRiseFall *to_tr; to_tr = TransRiseFall::rise(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, from_tr, to_tr, model); - attrs->setModelRef(to_tr, true); - } to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, from_tr, to_tr, model); - attrs->setModelRef(to_tr, true); - } return arc_set; } @@ -450,17 +434,14 @@ LibertyBuilder::makePresetClrArcs(LibertyCell *cell, switch (attrs->timingSense()) { case timing_sense_positive_unate: makeTimingArc(arc_set, to_tr, to_tr, model); - attrs->setModelRef(to_tr, true); break; case timing_sense_negative_unate: makeTimingArc(arc_set, opp_tr, to_tr, model); - attrs->setModelRef(to_tr, true); break; case timing_sense_non_unate: case timing_sense_unknown: makeTimingArc(arc_set, to_tr, to_tr, model); makeTimingArc(arc_set, opp_tr, to_tr, model); - attrs->setModelRef(to_tr, true); break; case timing_sense_none: break; @@ -494,36 +475,28 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, if (to_rise) { to_tr = TransRiseFall::rise(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); - attrs->setModelRef(to_tr, true); - } } if (to_fall) { to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); - attrs->setModelRef(to_tr, true); - } } break; case timing_sense_negative_unate: if (to_rise) { to_tr = TransRiseFall::rise(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); - attrs->setModelRef(to_tr, true); - } } if (to_fall) { to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); - attrs->setModelRef(to_tr, true); - } } break; case timing_sense_non_unate: @@ -534,7 +507,6 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, if (model) { makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); - attrs->setModelRef(to_tr, true); } } if (to_fall) { @@ -543,7 +515,6 @@ LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, if (model) { makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); - attrs->setModelRef(to_tr, true); } } break; @@ -577,36 +548,28 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, if (to_rise) { to_tr = TransRiseFall::rise(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); - attrs->setModelRef(to_tr, true); - } } if (to_fall) { to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); - attrs->setModelRef(to_tr, true); - } } break; case timing_sense_negative_unate: if (to_rise) { to_tr = TransRiseFall::rise(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); - attrs->setModelRef(to_tr, true); - } } if (to_fall) { to_tr = TransRiseFall::fall(); model = attrs->model(to_tr); - if (model) { + if (model) makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); - attrs->setModelRef(to_tr, true); - } } break; case timing_sense_non_unate: @@ -617,7 +580,6 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, if (model) { makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); - attrs->setModelRef(to_tr, true); } } if (to_fall) { @@ -626,7 +588,6 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, if (model) { makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); - attrs->setModelRef(to_tr, true); } } break; diff --git a/liberty/LibertyClass.hh b/liberty/LibertyClass.hh index 6f1978d5..babfebe1 100644 --- a/liberty/LibertyClass.hh +++ b/liberty/LibertyClass.hh @@ -55,14 +55,14 @@ class TimingRole; class Transition; class TransRiseFall; class TransRiseFallBoth; +class LibertyCellSequentialIterator; typedef Vector LibertyCellSeq; typedef Vector SequentialSeq; -typedef SequentialSeq::ConstIterator CellSequentialIterator; typedef Map LibertyCellEquivMap; typedef Vector LibertyPortSeq; typedef Set LibertyPortSet; -typedef std::pair LibertyPortPair; +typedef std::pair LibertyPortPair; typedef Set LibertyCellSet; typedef Vector FloatSeq; typedef Vector FloatTable; diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index d0ff2762..05dfed82 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -131,6 +131,7 @@ LibertyReader::readLibertyFile(const char *filename, mode_def_ = NULL; mode_value_ = NULL; ocv_derate_ = NULL; + have_resistance_unit_ = false; TransRiseFallIterator tr_iter; while (tr_iter.hasNext()) { @@ -169,6 +170,7 @@ LibertyReader::defineVisitors() defineAttrVisitor("time_unit", &LibertyReader::visitTimeUnit); defineAttrVisitor("pulling_resistance_unit", &LibertyReader::visitPullingResistanceUnit); + defineAttrVisitor("resistance_unit", &LibertyReader::visitResistanceUnit); defineAttrVisitor("capacitive_load_unit", &LibertyReader::visitCapacitiveLoadUnit); defineAttrVisitor("voltage_unit", &LibertyReader::visitVoltageUnit); @@ -580,6 +582,12 @@ LibertyReader::endLibrary(LibertyGroup *group) void LibertyReader::endLibraryAttrs(LibertyGroup *group) { + // Default resistance_unit to pulling_resistance_unit. + if (!have_resistance_unit_) { + Units *units = library_->units(); + units->resistanceUnit()->copy(units->pullingResistanceUnit()); + } + // These attributes reference named groups in the library so // wait until the end of the library to resolve them. if (default_wireload_) { @@ -640,7 +648,17 @@ void LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) { if (library_) + parseUnits(attr, "ohm", res_scale_, + library_->units()->pullingResistanceUnit()); +} + +void +LibertyReader::visitResistanceUnit(LibertyAttr *attr) +{ + if (library_) { parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); + have_resistance_unit_ = true; + } } void @@ -700,8 +718,8 @@ LibertyReader::parseUnits(LibertyAttr *attr, } float scale_mult = 1.0F; - if (stringEq(s + 1, unit_suffix)) { - char scale_char = *s; + if (*s && stringEqual(s + 1, unit_suffix)) { + char scale_char = tolower(*s); if (scale_char == 'k') scale_mult = 1E+3F; else if (scale_char == 'm') @@ -717,7 +735,7 @@ LibertyReader::parseUnits(LibertyAttr *attr, else libWarn(attr, "unknown unit scale %c.\n", scale_char); } - else if (!stringEq(s, unit_suffix)) + else if (!stringEqual(s, unit_suffix)) libWarn(attr, "unknown unit suffix %s.\n", s + 1); scale_var = scale_mult * mult; @@ -738,9 +756,9 @@ LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) value = value_iter.next(); if (value->isString()) { const char *suffix = value->stringValue(); - if (stringEq(suffix, "ff")) + if (stringEqual(suffix, "ff")) cap_scale_ = scale * 1E-15F; - else if (stringEq(suffix, "pf")) + else if (stringEqual(suffix, "pf")) cap_scale_ = scale * 1E-12F; else libWarn(attr, "capacitive_load_units are not ff or pf.\n"); @@ -1814,15 +1832,7 @@ LibertyReader::makeTimingArcs(PortGroup *port_group) LibertyPort *port = port_iter.next(); makeTimingArcs(port, timing); } - - // Delete unreferenced models to prevent memory leak. - TransRiseFallIterator tr_iter; - while (tr_iter.hasNext()) { - TransRiseFall *tr = tr_iter.next(); - TimingModel *model = timing->model(tr); - if (model && !timing->modelRef(tr)) - delete model; - } + cell_->addTimingArcAttrs(timing); } } @@ -1837,6 +1847,7 @@ LibertyReader::makeInternalPowers(PortGroup *port_group) LibertyPort *port = port_iter.next(); makeInternalPowers(port, power_group); } + cell_->addInternalPowerAttrs(power_group); } } @@ -2205,27 +2216,25 @@ LibertyReader::makeTimingArcs(const char *from_port_name, // one -> bus if (from_port_iter.hasNext()) { LibertyPort *from_port = from_port_iter.next(); - LibertyPortMemberIterator *bit_iter = to_port->libertyMemberIterator(); - while (bit_iter->hasNext()) { - LibertyPort *to_port_bit = bit_iter->next(); + LibertyPortMemberIterator bit_iter(to_port); + while (bit_iter.hasNext()) { + LibertyPort *to_port_bit = bit_iter.next(); builder_->makeTimingArcs(cell_, from_port, to_port_bit, related_out_port, timing); } - delete bit_iter; } } else { // bus -> bus if (timing->isOneToOne()) { if (static_cast(from_port_iter.size()) == to_port->size()) { - LibertyPortMemberIterator *to_iter = to_port->libertyMemberIterator(); - while (from_port_iter.hasNext() && to_iter->hasNext()) { + LibertyPortMemberIterator to_iter(to_port); + while (from_port_iter.hasNext() && to_iter.hasNext()) { LibertyPort *from_port_bit = from_port_iter.next(); - LibertyPort *to_port_bit = to_iter->next(); + LibertyPort *to_port_bit = to_iter.next(); builder_->makeTimingArcs(cell_, from_port_bit, to_port_bit, related_out_port, timing); } - delete to_iter; } else libWarn(timing->line(), @@ -2236,13 +2245,12 @@ LibertyReader::makeTimingArcs(const char *from_port_name, else { while (from_port_iter.hasNext()) { LibertyPort *from_port_bit = from_port_iter.next(); - LibertyPortMemberIterator *to_iter = to_port->libertyMemberIterator(); - while (to_iter->hasNext()) { - LibertyPort *to_port_bit = to_iter->next(); + LibertyPortMemberIterator to_iter(to_port); + while (to_iter.hasNext()) { + LibertyPort *to_port_bit = to_iter.next(); builder_->makeTimingArcs(cell_, from_port_bit, to_port_bit, related_out_port, timing); } - delete to_iter; } } } @@ -2270,12 +2278,11 @@ LibertyReader::makeInternalPowers(LibertyPort *port, } else { if (port->hasMembers()) { - LibertyPortMemberIterator *bit_iter = port->libertyMemberIterator(); - while (bit_iter->hasNext()) { - LibertyPort *port_bit = bit_iter->next(); + LibertyPortMemberIterator bit_iter(port); + while (bit_iter.hasNext()) { + LibertyPort *port_bit = bit_iter.next(); builder_->makeInternalPower(cell_, port_bit, NULL, power_group); } - delete bit_iter; } else builder_->makeInternalPower(cell_, port, NULL, power_group); @@ -2306,25 +2313,23 @@ LibertyReader::makeInternalPowers(LibertyPort *port, // one -> bus if (related_port_iter.hasNext()) { LibertyPort *related_port = related_port_iter.next(); - LibertyPortMemberIterator *bit_iter = port->libertyMemberIterator(); - while (bit_iter->hasNext()) { - LibertyPort *port_bit = bit_iter->next(); + LibertyPortMemberIterator bit_iter(port); + while (bit_iter.hasNext()) { + LibertyPort *port_bit = bit_iter.next(); builder_->makeInternalPower(cell_, port_bit, related_port, power_group); } - delete bit_iter; } } else { // bus -> bus if (power_group->isOneToOne()) { if (static_cast(related_port_iter.size()) == port->size()) { - LibertyPortMemberIterator *to_iter = port->libertyMemberIterator(); - while (related_port_iter.hasNext() && to_iter->hasNext()) { + LibertyPortMemberIterator to_iter(port); + while (related_port_iter.hasNext() && to_iter.hasNext()) { LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPort *port_bit = to_iter->next(); + LibertyPort *port_bit = to_iter.next(); builder_->makeInternalPower(cell_, port_bit, related_port_bit, power_group); } - delete to_iter; } else libWarn(power_group->line(), @@ -2335,12 +2340,11 @@ LibertyReader::makeInternalPowers(LibertyPort *port, else { while (related_port_iter.hasNext()) { LibertyPort *related_port_bit = related_port_iter.next(); - LibertyPortMemberIterator *to_iter = port->libertyMemberIterator(); - while (to_iter->hasNext()) { - LibertyPort *port_bit = to_iter->next(); + LibertyPortMemberIterator to_iter(port); + while (to_iter.hasNext()) { + LibertyPort *port_bit = to_iter.next(); builder_->makeInternalPower(cell_, port_bit, related_port_bit, power_group); } - delete to_iter; } } } @@ -2562,12 +2566,11 @@ LibertyReader::endPorts(LibertyGroup *) if (in_bus_ || in_bundle_) { // Do not clobber member port capacitances by setting the capacitance // on a bus or bundle. - LibertyPortMemberIterator *member_iter = port->libertyMemberIterator(); - while (member_iter->hasNext()) { - LibertyPort *member = member_iter->next(); + LibertyPortMemberIterator member_iter(port); + while (member_iter.hasNext()) { + LibertyPort *member = member_iter.next(); setPortCapDefault(member); } - delete member_iter; } else setPortCapDefault(port); @@ -4447,7 +4450,7 @@ LibertyReader::visitRelatedPowerPin(LibertyAttr *attr) LibertyPortSeq::Iterator port_iter(ports_); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); - port->setRelatedPowerPin(stringCopy(related_power_pin)); + port->setRelatedPowerPin(related_power_pin); } } } @@ -4699,8 +4702,8 @@ PortGroup::PortGroup(LibertyPortSeq *ports, PortGroup::~PortGroup() { - timings_.deleteContents(); - internal_power_groups_.deleteContents(); + // TimingGroups and IntternalPower are NOT deleted because ownership is transfered + // to LibertyCell::timing_arc_attrs_ by LibertyReader::makeTimingArcs. delete ports_; } @@ -4846,6 +4849,8 @@ TimingGroup::TimingGroup(int line) : TimingGroup::~TimingGroup() { + // TimingAttrs contents are not deleted because they are referenced + // by TimingArcSets. if (related_output_port_name_) stringDelete(related_output_port_name_); } @@ -5000,7 +5005,7 @@ PortNameBitIterator::init(const char *port_name) LibertyPort *port = visitor_->findPort(cell_, port_name); if (port) { if (port->isBus()) - bit_iterator_ = port->libertyMemberIterator(); + bit_iterator_ = new LibertyPortMemberIterator(port); else port_ = port; size_ = port->size(); diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 2a59e29b..9ec1d655 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -78,6 +78,7 @@ public: virtual void visitAttr(LibertyAttr *attr); virtual void visitTimeUnit(LibertyAttr *attr); virtual void visitCapacitiveLoadUnit(LibertyAttr *attr); + virtual void visitResistanceUnit(LibertyAttr *attr); virtual void visitPullingResistanceUnit(LibertyAttr *attr); virtual void visitVoltageUnit(LibertyAttr *attr); virtual void visitCurrentUnit(LibertyAttr *attr); @@ -546,6 +547,7 @@ protected: float curr_scale_; float power_scale_; float energy_scale_; + bool have_resistance_unit_; private: DISALLOW_COPY_AND_ASSIGN(LibertyReader); diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index a16deb7c..1b68b038 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -25,7 +25,6 @@ namespace sta { static void reportPvt(const LibertyLibrary *library, - const LibertyCell *cell, const Pvt *pvt, int digits, string *result); @@ -124,7 +123,7 @@ GateTableModel::reportGateDelay(const LibertyCell *cell, string *result) const { const LibertyLibrary *library = cell->libertyLibrary(); - reportPvt(library, cell, pvt, digits, result); + reportPvt(library, pvt, digits, result); reportTableLookup("Delay", library, cell, pvt, delay_model_, in_slew, load_cap, related_out_cap, digits, result); if (delay_sigma_models_[EarlyLate::earlyIndex()]) @@ -386,7 +385,7 @@ CheckTableModel::reportCheckDelay(const LibertyCell *cell, findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2, axis_value3); const LibertyLibrary *library = cell->libertyLibrary(); - reportPvt(library, cell, pvt, digits, result); + reportPvt(library, pvt, digits, result); model_->reportValue("Check", library, cell, pvt, axis_value1, from_slew_annotation, axis_value2, axis_value3, digits, result); @@ -586,7 +585,6 @@ TableModel::reportValue(const char *result_name, static void reportPvt(const LibertyLibrary *library, - const LibertyCell *cell, const Pvt *pvt, int digits, string *result) diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index 5a07fa7f..df88373a 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -18,6 +18,7 @@ #include "FuncExpr.hh" #include "TimingRole.hh" #include "Liberty.hh" +#include "TimingModel.hh" #include "TimingArc.hh" namespace sta { @@ -41,18 +42,29 @@ TimingArcAttrs::TimingArcAttrs() : mode_name_(NULL), mode_value_(NULL), ocv_arc_depth_(0.0), - models_{NULL, NULL}, - model_refs_{false, false} + models_{NULL, NULL} { } +// Destructor does NOT delete contents because it is a component +// of TimingGroup (that is deleted after building the LibertyCell) +// and (potentially) multiple TimingArcSets. TimingArcAttrs::~TimingArcAttrs() { +} + +void +TimingArcAttrs::deleteContents() +{ + if (cond_) + cond_->deleteSubexprs(); stringDelete(sdf_cond_); stringDelete(sdf_cond_start_); stringDelete(sdf_cond_end_); stringDelete(mode_name_); stringDelete(mode_value_); + delete models_[0]; + delete models_[1]; } void @@ -115,19 +127,6 @@ TimingArcAttrs::setModel(TransRiseFall *tr, models_[tr->index()] = model; } -bool -TimingArcAttrs::modelRef(TransRiseFall *tr) const -{ - return model_refs_[tr->index()]; -} - -void -TimingArcAttrs::setModelRef(TransRiseFall *tr, - bool ref) -{ - model_refs_[tr->index()] = ref; -} - void TimingArcAttrs::setOcvArcDepth(float depth) { @@ -150,51 +149,36 @@ TimingArcSet::TimingArcSet(LibertyCell *cell, role_(role), cond_(attrs->cond()), is_cond_default_(false), - sdf_cond_start_(NULL), - sdf_cond_end_(NULL), - mode_name_(stringCopy(attrs->modeName())), - mode_value_(stringCopy(attrs->modeValue())), + sdf_cond_start_(attrs->sdfCondStart()), + sdf_cond_end_(attrs->sdfCondEnd()), + mode_name_(attrs->modeName()), + mode_value_(attrs->modeValue()), ocv_arc_depth_(attrs->ocvArcDepth()), index_(0), is_disabled_constraint_(false) { const char *sdf_cond = attrs->sdfCond(); - const char *sdf_cond_start = attrs->sdfCondStart(); - const char *sdf_cond_end = attrs->sdfCondEnd(); if (sdf_cond) - sdf_cond_start = sdf_cond_end = sdf_cond; - if (sdf_cond_start) - sdf_cond_start_ = stringCopy(sdf_cond_start); - if (sdf_cond_end) - sdf_cond_end_ = stringCopy(sdf_cond_end); + sdf_cond_start_ = sdf_cond_end_ = sdf_cond; init(cell); } -TimingArcSet::TimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - TimingRole *role, - FuncExpr *cond, - const char *sdf_cond_start, - const char *sdf_cond_end, - const char *mode_name, - const char *mode_value) : - from_(from), - to_(to), - related_out_(related_out), +TimingArcSet::TimingArcSet(TimingRole *role) : + from_(NULL), + to_(NULL), + related_out_(NULL), role_(role), - cond_(cond), + cond_(NULL), is_cond_default_(false), - sdf_cond_start_(stringCopy(sdf_cond_start)), - sdf_cond_end_(stringCopy(sdf_cond_end)), - mode_name_(stringCopy(mode_name)), - mode_value_(stringCopy(mode_value)), + sdf_cond_start_(NULL), + sdf_cond_end_(NULL), + mode_name_(NULL), + mode_value_(NULL), index_(0), is_disabled_constraint_(false) { - init(cell); + init(NULL); } void @@ -283,12 +267,6 @@ TimingArcSet::setIsCondDefault(bool is_default) is_cond_default_ = is_default; } -TimingArcSetArcIterator * -TimingArcSet::timingArcIterator() const -{ - return new TimingArcSetArcIterator(this); -} - void TimingArcSet::arcsFrom(const TransRiseFall *from_tr, // Return values. @@ -373,21 +351,15 @@ static bool timingArcsEquiv(const TimingArcSet *set1, const TimingArcSet *set2) { - TimingArcSetArcIterator *arc_iter1 = set1->timingArcIterator(); - TimingArcSetArcIterator *arc_iter2 = set2->timingArcIterator(); - while (arc_iter1->hasNext() && arc_iter2->hasNext()) { - TimingArc *arc1 = arc_iter1->next(); - TimingArc *arc2 = arc_iter2->next(); - if (!TimingArc::equiv(arc1, arc2)) { - delete arc_iter1; - delete arc_iter2; + TimingArcSetArcIterator arc_iter1(set1); + TimingArcSetArcIterator arc_iter2(set2); + while (arc_iter1.hasNext() && arc_iter2.hasNext()) { + TimingArc *arc1 = arc_iter1.next(); + TimingArc *arc2 = arc_iter2.next(); + if (!TimingArc::equiv(arc1, arc2)) return false; - } } - bool eq = !arc_iter1->hasNext() && !arc_iter2->hasNext(); - delete arc_iter1; - delete arc_iter2; - return eq; + return !arc_iter1.hasNext() && !arc_iter2.hasNext(); } bool @@ -461,42 +433,27 @@ static bool timingArcsLess(const TimingArcSet *set1, const TimingArcSet *set2) { - TimingArcSetArcIterator *arc_iter1 = set1->timingArcIterator(); - TimingArcSetArcIterator *arc_iter2 = set2->timingArcIterator(); - while (arc_iter1->hasNext() && arc_iter2->hasNext()) { - TimingArc *arc1 = arc_iter1->next(); - TimingArc *arc2 = arc_iter2->next(); + TimingArcSetArcIterator arc_iter1(set1); + TimingArcSetArcIterator arc_iter2(set2); + while (arc_iter1.hasNext() && arc_iter2.hasNext()) { + TimingArc *arc1 = arc_iter1.next(); + TimingArc *arc2 = arc_iter2.next(); int from_index1 = arc1->fromTrans()->index(); int from_index2 = arc2->fromTrans()->index(); - if (from_index1 < from_index2) { - delete arc_iter1; - delete arc_iter2; + if (from_index1 < from_index2) return true; - } - if (from_index1 > from_index2) { - delete arc_iter1; - delete arc_iter2; + if (from_index1 > from_index2) return false; - } // from_index1 == from_index2 int to_index1 = arc1->toTrans()->index(); int to_index2 = arc2->toTrans()->index(); - if (to_index1 < to_index2) { - delete arc_iter1; - delete arc_iter2; + if (to_index1 < to_index2) return true; - } - if (to_index1 > to_index2) { - delete arc_iter1; - delete arc_iter2; + if (to_index1 > to_index2) return false; - } // Continue if arc transitions are equal. } - bool less = !arc_iter1->hasNext() && arc_iter2->hasNext(); - delete arc_iter1; - delete arc_iter2; - return less; + return !arc_iter1.hasNext() && arc_iter2.hasNext(); } //////////////////////////////////////////////////////////////// @@ -510,9 +467,7 @@ TimingArcSet::wireArcIndex(const TransRiseFall *tr) void TimingArcSet::init() { - wire_timing_arc_set_ = new TimingArcSet(NULL,NULL,NULL,NULL, - TimingRole::wire(), - NULL, NULL, NULL, NULL, NULL); + wire_timing_arc_set_ = new TimingArcSet(TimingRole::wire()); new TimingArc(wire_timing_arc_set_, Transition::rise(), Transition::rise(), NULL); new TimingArc(wire_timing_arc_set_, Transition::fall(), diff --git a/liberty/TimingArc.hh b/liberty/TimingArc.hh index 1519495d..ff432210 100644 --- a/liberty/TimingArc.hh +++ b/liberty/TimingArc.hh @@ -94,6 +94,7 @@ class TimingArcAttrs public: TimingArcAttrs(); virtual ~TimingArcAttrs(); + void deleteContents(); TimingType timingType() const { return timing_type_; } void setTimingType(TimingType type); TimingSense timingSense() const { return timing_sense_; } @@ -113,9 +114,6 @@ public: TimingModel *model(TransRiseFall *tr) const; void setModel(TransRiseFall *tr, TimingModel *model); - bool modelRef(TransRiseFall *tr) const; - void setModelRef(TransRiseFall *tr, - bool ref); float ocvArcDepth() const { return ocv_arc_depth_; } void setOcvArcDepth(float depth); @@ -130,7 +128,6 @@ protected: const char *mode_value_; float ocv_arc_depth_; TimingModel *models_[TransRiseFall::index_count]; - bool model_refs_[TransRiseFall::index_count]; private: DISALLOW_COPY_AND_ASSIGN(TimingArcAttrs); @@ -150,16 +147,6 @@ public: LibertyPort *related_out, TimingRole *role, TimingArcAttrs *attrs); - TimingArcSet(LibertyCell *cell, - LibertyPort *from, - LibertyPort *to, - LibertyPort *related_out, - TimingRole *role, - FuncExpr *cond, - const char *sdf_cond_start, - const char *sdf_cond_end, - const char *mode_name, - const char *mode_value); virtual ~TimingArcSet(); LibertyCell *libertyCell() const; LibertyPort *from() const { return from_; } @@ -170,7 +157,6 @@ public: // Rise/fall if the arc set is rising_edge or falling_edge. TransRiseFall *isRisingFallingEdge() const; size_t arcCount() const { return arcs_.size(); } - TimingArcSetArcIterator *timingArcIterator() const; TimingArcSeq &arcs() { return arcs_; } // Return 1 or 2 arcs matching from transition. void arcsFrom(const TransRiseFall *from_tr, @@ -217,6 +203,7 @@ public: protected: void init(LibertyCell *cell); + TimingArcSet(TimingRole *role); LibertyPort *from_; LibertyPort *to_; diff --git a/liberty/Units.hh b/liberty/Units.hh index 2d43d60f..1320b35f 100644 --- a/liberty/Units.hh +++ b/liberty/Units.hh @@ -66,6 +66,8 @@ public: const Unit *voltageUnit() const { return &voltage_unit_; } Unit *resistanceUnit() { return &resistance_unit_; } const Unit *resistanceUnit() const { return &resistance_unit_; } + Unit *pullingResistanceUnit() { return &pulling_resistance_unit_; } + const Unit *pullingResistanceUnit() const {return &pulling_resistance_unit_;} Unit *currentUnit() { return ¤t_unit_; } const Unit *currentUnit() const { return ¤t_unit_; } Unit *powerUnit() { return &power_unit_; } @@ -82,6 +84,7 @@ private: Unit capacitance_unit_; Unit voltage_unit_; Unit resistance_unit_; + Unit pulling_resistance_unit_; Unit current_unit_; Unit power_unit_; Unit distance_unit_; diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index a625febb..93cbc370 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -401,14 +401,13 @@ Sdc::removeLibertyAnnotations() LibertyPortPairSet::Iterator from_to_iter(disable->fromTo()); while (from_to_iter.hasNext()) { LibertyPortPair *pair = from_to_iter.next(); - LibertyPort *from = pair->first; - LibertyPort *to = pair->second; - CellTimingArcSetIterator *arc_iter = cell->timingArcSetIterator(from, to); - while (arc_iter->hasNext()) { - TimingArcSet *arc_set = arc_iter->next(); + const LibertyPort *from = pair->first; + const LibertyPort *to = pair->second; + LibertyCellTimingArcSetIterator arc_iter(cell, from, to); + while (arc_iter.hasNext()) { + TimingArcSet *arc_set = arc_iter.next(); arc_set->setIsDisabledConstraint(false); } - delete arc_iter; } } @@ -3704,12 +3703,11 @@ Sdc::disable(LibertyCell *cell, } if (from && to) { disabled_cell->setDisabledFromTo(from, to); - CellTimingArcSetIterator *arc_iter = cell->timingArcSetIterator(from, to); - while (arc_iter->hasNext()) { - TimingArcSet *arc_set = arc_iter->next(); + LibertyCellTimingArcSetIterator arc_iter(cell, from, to); + while (arc_iter.hasNext()) { + TimingArcSet *arc_set = arc_iter.next(); arc_set->setIsDisabledConstraint(true); } - delete arc_iter; } else if (from) { disabled_cell->setDisabledFrom(from); @@ -3734,12 +3732,11 @@ Sdc::removeDisable(LibertyCell *cell, if (disabled_cell) { if (from && to) { disabled_cell->removeDisabledFromTo(from, to); - CellTimingArcSetIterator *arc_iter = cell->timingArcSetIterator(from, to); - while (arc_iter->hasNext()) { - TimingArcSet *arc_set = arc_iter->next(); + LibertyCellTimingArcSetIterator arc_iter(cell, from, to); + while (arc_iter.hasNext()) { + TimingArcSet *arc_set = arc_iter.next(); arc_set->setIsDisabledConstraint(false); } - delete arc_iter; } else if (from) { disabled_cell->removeDisabledFrom(from); @@ -4212,16 +4209,13 @@ Sdc::exceptionToInvalid(const Pin *pin) LibertyPort *port = network_->libertyPort(pin); if (port) { LibertyCell *cell = port->libertyCell(); - CellTimingArcSetIterator *set_iter = cell->timingArcSetToIterator(port); - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell, NULL, port); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); TimingRole *role = set->role(); - if (role->genericRole() == TimingRole::regClkToQ()) { - delete set_iter; + if (role->genericRole() == TimingRole::regClkToQ()) return true; - } } - delete set_iter; } return false; } @@ -4381,15 +4375,12 @@ Sdc::hasLibertyChecks(const Pin *pin) if (cell) { LibertyPort *port = network_->libertyPort(pin); if (port) { - CellTimingArcSetIterator *timing_iter=cell->timingArcSetToIterator(port); - while (timing_iter->hasNext()) { - TimingArcSet *arc_set = timing_iter->next(); - if (arc_set->role()->isTimingCheck()) { - delete timing_iter; + LibertyCellTimingArcSetIterator timing_iter(cell, NULL, port); + while (timing_iter.hasNext()) { + TimingArcSet *arc_set = timing_iter.next(); + if (arc_set->role()->isTimingCheck()) return true; - } } - delete timing_iter; } } return false; @@ -6463,8 +6454,8 @@ Sdc::setEdgeDisabledInstPorts(DisabledPorts *disabled_port, LibertyPortPairSet::Iterator from_to_iter(disabled_port->fromTo()); while (from_to_iter.hasNext()) { LibertyPortPair *pair = from_to_iter.next(); - LibertyPort *from_port = pair->first; - LibertyPort *to_port = pair->second; + const LibertyPort *from_port = pair->first; + const LibertyPort *to_port = pair->second; Pin *from_pin = network_->findPin(inst, from_port); Pin *to_pin = network_->findPin(inst, to_port); if (from_pin && network_->direction(from_pin)->isAnyInput() diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc index 5877bbdc..b4894474 100644 --- a/sdc/WriteSdc.cc +++ b/sdc/WriteSdc.cc @@ -1059,8 +1059,8 @@ WriteSdc::writeDisabledCells() const LibertyPortPairSeq::Iterator pair_iter(pairs); while (pair_iter.hasNext()) { LibertyPortPair *from_to = pair_iter.next(); - LibertyPort *from = from_to->first; - LibertyPort *to = from_to->second; + const LibertyPort *from = from_to->first; + const LibertyPort *to = from_to->second; fprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", from->name(), to->name()); @@ -1151,8 +1151,8 @@ WriteSdc::writeDisabledInstances() const LibertyPortPairSeq::Iterator pair_iter(pairs); while (pair_iter.hasNext()) { LibertyPortPair *from_to = pair_iter.next(); - LibertyPort *from_port = from_to->first; - LibertyPort *to_port = from_to->second; + const LibertyPort *from_port = from_to->first; + const LibertyPort *to_port = from_to->second; fprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", from_port->name(), to_port->name()); diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc index 161865ef..5a8b1f6c 100644 --- a/sdf/SdfReader.cc +++ b/sdf/SdfReader.cc @@ -295,9 +295,9 @@ SdfReader::setEdgeDelays(Edge *edge, if (triple_count == 1 || triple_count == 2) { TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); size_t triple_index; if (triple_count == 1) triple_index = 0; @@ -306,7 +306,6 @@ SdfReader::setEdgeDelays(Edge *edge, SdfTriple *triple = (*triples)[triple_index]; setEdgeArcDelays(edge, arc, triple); } - delete arc_iter; } else if (triple_count == 0) sdfError("%s with no triples.\n", sdf_cmd); @@ -404,9 +403,9 @@ SdfReader::iopath(SdfPortSpec *from_edge, // condelse matches the default (unconditional) arc. || (condelse && lib_cond == NULL))) { matched = true; - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); if ((from_edge->transition() == Transition::riseFall()) || (arc->fromTrans() == from_edge->transition())) { size_t triple_index = arc->toTrans()->sdfTripleIndex(); @@ -425,7 +424,6 @@ SdfReader::iopath(SdfPortSpec *from_edge, } } } - delete arc_iter; } } if (!matched) @@ -551,9 +549,9 @@ SdfReader::annotateCheckEdges(Pin *data_pin, && edge_role->genericRole() == sdf_role->genericRole())) && cond_matches) { TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); if (((data_edge->transition() == Transition::riseFall()) || (arc->toTrans() == data_edge->transition())) && ((clk_edge->transition() == Transition::riseFall()) @@ -561,7 +559,6 @@ SdfReader::annotateCheckEdges(Pin *data_pin, setEdgeArcDelays(edge, arc, triple); } } - delete arc_iter; matched = true; } } diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index f6954fdd..26481497 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -414,9 +414,9 @@ SdfWriter::writeArcDelays(Edge *edge) { RiseFallMinMax delays; TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); TransRiseFall *tr = arc->toTrans()->asRiseFall(); ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); delays.setValue(tr, MinMax::min(), delayAsFloat(min_delay)); @@ -424,7 +424,6 @@ SdfWriter::writeArcDelays(Edge *edge) ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); delays.setValue(tr, MinMax::max(), delayAsFloat(max_delay)); } - delete arc_iter; if (delays.hasValue(TransRiseFall::rise(), MinMax::min()) && delays.hasValue(TransRiseFall::fall(), MinMax::min())) { @@ -570,14 +569,13 @@ SdfWriter::writeCheck(Edge *edge, // Examine the arcs to see if the check requires clk or data edge specifiers. TimingArc *arcs[TransRiseFall::index_count][TransRiseFall::index_count] = {{NULL, NULL}, {NULL, NULL}}; - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); TransRiseFall *clk_tr = arc->fromTrans()->asRiseFall(); TransRiseFall *data_tr = arc->toTrans()->asRiseFall();; arcs[clk_tr->index()][data_tr->index()] = arc; } - delete arc_iter; if (arcs[TransRiseFall::fallIndex()][TransRiseFall::riseIndex()] == NULL && arcs[TransRiseFall::fallIndex()][TransRiseFall::fallIndex()] == NULL) @@ -587,12 +585,11 @@ SdfWriter::writeCheck(Edge *edge, writeEdgeCheck(edge, sdf_check, TransRiseFall::fallIndex(), arcs); else { // No special case; write all the checks with data and clock edge specifiers. - arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); writeCheck(edge, arc, sdf_check, true, true); } - delete arc_iter; } } diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc index 83ff9774..16b832a0 100644 --- a/search/CheckMaxSkews.cc +++ b/search/CheckMaxSkews.cc @@ -196,9 +196,9 @@ CheckMaxSkews:: visitMaxSkewChecks(Vertex *vertex, if (edge->role() == TimingRole::skew()) { Vertex *ref_vertex = edge->from(graph); TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); TransRiseFall *clk_tr = arc->fromTrans()->asRiseFall(); TransRiseFall *ref_tr = arc->toTrans()->asRiseFall(); VertexPathIterator clk_path_iter(vertex, clk_tr, clk_min_max, search); @@ -218,7 +218,6 @@ CheckMaxSkews:: visitMaxSkewChecks(Vertex *vertex, } } } - delete arc_iter; } } } diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc index 75fef7eb..05f21a13 100644 --- a/search/CheckSlewLimits.cc +++ b/search/CheckSlewLimits.cc @@ -109,6 +109,8 @@ CheckSlewLimits::checkSlews(const Pin *pin, { corner1 = NULL; tr = NULL; + slew = 0.0; + limit = 0.0; slack = MinMax::min()->initValue(); if (corner) checkSlews1(pin, corner, min_max, diff --git a/search/Corner.cc b/search/Corner.cc index 50410cc9..127c5dcf 100644 --- a/search/Corner.cc +++ b/search/Corner.cc @@ -420,17 +420,7 @@ Corner::findDcalcAnalysisPt(const MinMax *min_max) const PathAnalysisPt * Corner::findPathAnalysisPt(const MinMax *min_max) const { - int ap_count = path_analysis_pts_.size(); - if (ap_count == 0) - return NULL; - else if (ap_count == 1) - return path_analysis_pts_[0]; - else if (ap_count == 2) - return path_analysis_pts_[min_max->index()]; - else { - internalError("unknown analysis point count"); - return NULL; - } + return path_analysis_pts_[min_max->index()]; } void diff --git a/search/Crpr.cc b/search/Crpr.cc index 0e895005..e6d722bf 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -88,8 +88,9 @@ Crpr::maxCrpr(ClkInfo *clk_info) if (!crpr_clk_path.isNull()) { PathVertex crpr_clk_vpath(crpr_clk_path, this); Arrival other_arrival = otherMinMaxArrival(&crpr_clk_vpath); - float crpr_diff = abs(delayAsFloat(crpr_clk_vpath.arrival(this) - - other_arrival)); + float crpr_diff = abs(delayAsFloat(crpr_clk_vpath.arrival(this), + EarlyLate::late()) + - delayAsFloat(other_arrival, EarlyLate::early())); return crpr_diff; } else @@ -371,7 +372,8 @@ float Crpr::crprArrivalDiff(const PathVertex *path) { Arrival other_arrival = otherMinMaxArrival(path); - float crpr_diff = abs(delayAsFloat(path->arrival(this)-other_arrival)); + float crpr_diff = abs(delayAsFloat(path->arrival(this), EarlyLate::late()) + - delayAsFloat(other_arrival, EarlyLate::early())); return crpr_diff; } diff --git a/search/FindRegister.cc b/search/FindRegister.cc index 4ef86404..5416ad77 100644 --- a/search/FindRegister.cc +++ b/search/FindRegister.cc @@ -243,10 +243,10 @@ FindRegVisitor::findSequential(const Pin *clk_pin, { has_seqs = false; matches = false; - CellSequentialIterator *seq_iter = cell->sequentialIterator(); - while (seq_iter->hasNext()) { + LibertyCellSequentialIterator seq_iter(cell); + while (seq_iter.hasNext()) { has_seqs = true; - Sequential *seq = seq_iter->next(); + Sequential *seq = seq_iter.next(); if ((seq->isRegister() && edge_triggered) || (seq->isLatch() && latches)) { if (clk_tr == TransRiseFallBoth::riseFall()) { @@ -270,7 +270,6 @@ FindRegVisitor::findSequential(const Pin *clk_pin, } } } - delete seq_iter; } bool @@ -281,13 +280,12 @@ FindRegVisitor::findInferedSequential(LibertyCell *cell, bool latches) { bool matches = false; - CellTimingArcSetIterator *set_iter = cell->timingArcSetIterator(); const TransRiseFall *clk_tr1 = clk_tr->asRiseFall(); - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); - TimingArcSetArcIterator *arc_iter = set->timingArcIterator(); - TimingArc *arc = arc_iter->next(); - delete arc_iter; + LibertyCellTimingArcSetIterator set_iter(cell); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + TimingArcSetArcIterator arc_iter(set); + TimingArc *arc = arc_iter.next(); TransRiseFall *arc_clk_tr = arc->fromTrans()->asRiseFall(); bool tr_matches = (clk_tr == TransRiseFallBoth::riseFall() || (arc_clk_tr == clk_tr1 @@ -304,7 +302,6 @@ FindRegVisitor::findInferedSequential(LibertyCell *cell, break; } } - delete set_iter; return matches; } @@ -313,18 +310,14 @@ FindRegVisitor::hasTimingCheck(LibertyCell *cell, LibertyPort *clk, LibertyPort *d) { - bool has_check = false; - CellTimingArcSetIterator *set_iter = cell->timingArcSetIterator(clk, d); - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell, clk, d); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); TimingRole *role = set->role(); - if (role->isTimingCheck()) { - has_check = true; - break; - } + if (role->isTimingCheck()) + return true; } - delete set_iter; - return has_check; + return false; } class FindRegInstances : public FindRegVisitor @@ -560,18 +553,14 @@ FindRegClkPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - CellTimingArcSetIterator *set_iter = cell->timingArcSetFromIterator(port); - bool match = false; - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell, port, NULL); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); TimingRole *role = set->role(); - if (role->isTimingCheck()) { - match = true; - break; - } + if (role->isTimingCheck()) + return true; } - delete set_iter; - return match; + return false; } @@ -622,18 +611,14 @@ FindRegAsyncPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - CellTimingArcSetIterator *set_iter = cell->timingArcSetFromIterator(port); - bool match = false; - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell, port, NULL); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); TimingRole *role = set->role(); - if (role == TimingRole::regSetClr()) { - match = true; - break; - } + if (role == TimingRole::regSetClr()) + return true; } - delete set_iter; - return match; + return false; } PinSet * @@ -676,20 +661,16 @@ FindRegOutputPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); - CellTimingArcSetIterator *set_iter = cell->timingArcSetToIterator(port); - bool match = false; - while (set_iter->hasNext()) { - TimingArcSet *set = set_iter->next(); + LibertyCellTimingArcSetIterator set_iter(cell, NULL, port); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); TimingRole *role = set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ() - || role == TimingRole::latchDtoQ()) { - match = true; - break; - } + || role == TimingRole::latchDtoQ()) + return true; } - delete set_iter; - return match; + return false; } void diff --git a/search/PathEnd.cc b/search/PathEnd.cc index c6a5b4d1..b548e79c 100644 --- a/search/PathEnd.cc +++ b/search/PathEnd.cc @@ -75,7 +75,7 @@ PathEnd::pathEarlyLate(const StaState *sta) const } const EarlyLate * -PathEnd::clkEarlyLate(const StaState *sta) const +PathEnd::clkEarlyLate(const StaState *) const { return NULL; } diff --git a/search/PathEnum.cc b/search/PathEnum.cc index 799c4611..bcbfa336 100644 --- a/search/PathEnum.cc +++ b/search/PathEnum.cc @@ -103,7 +103,6 @@ PathEnum::PathEnum(int group_count, int endpoint_count, bool unique_pins, bool cmp_slack, - const MinMax *min_max, const StaState *sta) : StaState(sta), cmp_slack_(cmp_slack), diff --git a/search/PathEnum.hh b/search/PathEnum.hh index 88dd970c..ec88e998 100644 --- a/search/PathEnum.hh +++ b/search/PathEnum.hh @@ -57,7 +57,6 @@ public: int endpoint_count, bool unique_pins, bool cmp_slack, - const MinMax *min_max, const StaState *sta); // Insert path ends that are enumerated in slack/arrival order. void insert(PathEnd *path_end); diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 30ba9059..bc48bd83 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -757,7 +757,7 @@ PathGroups::makeGroupPathEnds(ExceptionTo *to, group_path_iter->next(name, groups); PathGroup *group = findPathGroup(name, path_min_max); if (group) - enumPathEnds(group, group_count, endpoint_count, unique_pins, true, path_min_max); + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); } delete group_path_iter; @@ -766,22 +766,22 @@ PathGroups::makeGroupPathEnds(ExceptionTo *to, Clock *clk = clk_iter->next(); PathGroup *group = findPathGroup(clk, path_min_max); if (group) - enumPathEnds(group, group_count, endpoint_count, unique_pins, true, path_min_max); + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); } delete clk_iter; PathGroup *group = unconstrained_[mm_index]; if (group) - enumPathEnds(group, group_count, endpoint_count, unique_pins, false, path_min_max); + enumPathEnds(group, group_count, endpoint_count, unique_pins, false); group = path_delay_[mm_index]; if (group) - enumPathEnds(group, group_count, endpoint_count, unique_pins, true, path_min_max); + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); group = gated_clk_[mm_index]; if (group) - enumPathEnds(group, group_count, endpoint_count, unique_pins, true, path_min_max); + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); group = async_[mm_index]; if (group) - enumPathEnds(group, group_count, endpoint_count, unique_pins, true, path_min_max); + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); } } } @@ -791,12 +791,11 @@ PathGroups::enumPathEnds(PathGroup *group, int group_count, int endpoint_count, bool unique_pins, - bool cmp_slack, - const MinMax *min_max) + bool cmp_slack) { // Insert the worst max_path path ends in the group into a path // enumerator. - PathEnum path_enum(group_count, endpoint_count, unique_pins, cmp_slack, min_max, this); + PathEnum path_enum(group_count, endpoint_count, unique_pins, cmp_slack, this); PathGroupIterator *end_iter = group->iterator(); while (end_iter->hasNext()) { PathEnd *end = end_iter->next(); diff --git a/search/PathGroup.hh b/search/PathGroup.hh index b4db883d..d5c04174 100644 --- a/search/PathGroup.hh +++ b/search/PathGroup.hh @@ -149,8 +149,7 @@ protected: int group_count, int endpoint_count, bool unique_pins, - bool cmp_slack, - const MinMax *min_max); + bool cmp_slack); void pushGroupPathEnds(PathEndSeq *path_ends); void pushUnconstrainedPathEnds(PathEndSeq *path_ends, diff --git a/search/Power.cc b/search/Power.cc index ac37651b..59a789cf 100644 --- a/search/Power.cc +++ b/search/Power.cc @@ -23,7 +23,6 @@ #include "Liberty.hh" #include "InternalPower.hh" #include "LeakagePower.hh" -#include "InternalPower.hh" #include "TimingArc.hh" #include "FuncExpr.hh" #include "PortDirection.hh" @@ -103,6 +102,7 @@ Power::power(const Corner *corner, total.incr(inst_power); } } + delete inst_iter; } //////////////////////////////////////////////////////////////// @@ -138,10 +138,10 @@ Power::power(const Instance *inst, bool is_clk; activity(to_pin, activity1, is_clk); if (to_port->direction()->isAnyOutput()) { - findSwitchingPower(inst, cell, to_pin, to_port, activity1, load_cap, + findSwitchingPower(cell, to_port, activity1, load_cap, dcalc_ap, result); } - findInternalPower(inst, cell, to_pin, to_port, activity1, is_clk, + findInternalPower(inst, cell, to_port, activity1, is_clk, load_cap, dcalc_ap, result); } delete pin_iter; @@ -151,7 +151,6 @@ Power::power(const Instance *inst, void Power::findInternalPower(const Instance *inst, LibertyCell *cell, - const Pin *to_pin, const LibertyPort *to_port, float activity, bool is_clk, @@ -173,9 +172,9 @@ Power::findInternalPower(const Instance *inst, units_->capacitanceUnit()->asString(load_cap), activity * 1e-9, duty); - LibertyCellInternalPowerIterator *pwr_iter = cell->internalPowerIterator(); - while (pwr_iter->hasNext()) { - InternalPower *pwr = pwr_iter->next(); + LibertyCellInternalPowerIterator pwr_iter(cell); + while (pwr_iter.hasNext()) { + InternalPower *pwr = pwr_iter.next(); const char *related_pg_pin = pwr->relatedPgPin(); if (pwr->port() == to_port && ((related_pg_pin == NULL || pwr_pin == NULL) @@ -189,7 +188,7 @@ Power::findInternalPower(const Instance *inst, while (tr_iter.hasNext()) { TransRiseFall *to_tr = tr_iter.next(); // Need unateness to find from_tr. - float slew = sta_->vertexSlew(from_vertex, to_tr, dcalc_ap); + float slew = delayAsFloat(sta_->vertexSlew(from_vertex, to_tr, dcalc_ap)); float energy, tr_internal; if (from_port) { float energy1 = pwr->power(to_tr, pvt, slew, load_cap); @@ -216,7 +215,6 @@ Power::findInternalPower(const Instance *inst, } } } - delete pwr_iter; debugPrint1(debug_, "power", 2, " internal = %.5g\n", port_internal); result.setInternal(result.internal() + port_internal); } @@ -232,13 +230,12 @@ Power::loadCap(const Pin *to_pin, while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); ceff_sum += graph_delay_calc_->ceff(edge, arc, dcalc_ap); ceff_count++; } - delete arc_iter; } return ceff_sum / ceff_count; } @@ -250,9 +247,9 @@ Power::findLeakagePower(const Instance *inst, PowerResult &result) { float leakage = cell->leakagePower(); - LibertyCellLeakagePowerIterator *pwr_iter = cell->leakagePowerIterator(); - while (pwr_iter->hasNext()) { - LeakagePower *leak = pwr_iter->next(); + LibertyCellLeakagePowerIterator pwr_iter(cell); + while (pwr_iter.hasNext()) { + LeakagePower *leak = pwr_iter.next(); FuncExpr *when = leak->when(); if (when) { LogicValue when_value = sim_->evalExpr(when, inst); @@ -267,14 +264,11 @@ Power::findLeakagePower(const Instance *inst, } } } - delete pwr_iter; result.setLeakage(leakage); } void -Power::findSwitchingPower(const Instance *inst, - LibertyCell *cell, - const Pin *to_pin, +Power::findSwitchingPower(LibertyCell *cell, const LibertyPort *to_port, float activity, float load_cap, @@ -307,7 +301,7 @@ Power::activity(const Pin *pin, float Power::voltage(LibertyCell *cell, - const LibertyPort *port, + const LibertyPort *, const DcalcAnalysisPt *dcalc_ap) { // Should use cell pg_pin voltage name to voltage. diff --git a/search/Power.hh b/search/Power.hh index 9a69b44e..3146a526 100644 --- a/search/Power.hh +++ b/search/Power.hh @@ -51,7 +51,6 @@ protected: PowerResult &result); void findInternalPower(const Instance *inst, LibertyCell *cell, - const Pin *to_pin, const LibertyPort *to_port, float activity, bool is_clk, @@ -63,9 +62,7 @@ protected: LibertyCell *cell, // Return values. PowerResult &result); - void findSwitchingPower(const Instance *inst, - LibertyCell *cell, - const Pin *to_pin, + void findSwitchingPower(LibertyCell *cell, const LibertyPort *to_port, float activity, float load_cap, diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 19eb30b5..b5af8b64 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2713,8 +2713,8 @@ ReportPath::reportPath5(const Path *path, // from the input to the loads. Report the wire delay on the // input pin instead. Arrival next_time = next_path->arrival(this) + time_offset; - incr = delayMeanSigma(next_time, min_max) - - delayMeanSigma(time, min_max); + incr = delayAsFloat(next_time, min_max) + - delayAsFloat(time, min_max); time = next_time; line_case = "input_drive"; } @@ -2761,13 +2761,13 @@ ReportPath::reportPath5(const Path *path, line_case = "clk_ideal"; } else if (is_clk && !is_clk_start) { - incr = delayMeanSigma(time, min_max) - - delayMeanSigma(prev_time, min_max); + incr = delayAsFloat(time, min_max) + - delayAsFloat(prev_time, min_max); line_case = "clk_prop"; } else { - incr = delayMeanSigma(time, min_max) - - delayMeanSigma(prev_time, min_max); + incr = delayAsFloat(time, min_max) + - delayAsFloat(prev_time, min_max); line_case = "normal"; } if (report_input_pin_ @@ -3264,7 +3264,7 @@ ReportPath::reportTotalDelay(Delay value, const EarlyLate *early_late, string &result) { - const char *str = delayMeanSigmaString(value, early_late, units_, digits_); + const char *str = delayAsString(value, early_late, units_, digits_); if (stringEq(str, minus_zero_)) // Filter "-0.00" fields. str = plus_zero_; @@ -3281,7 +3281,7 @@ ReportPath::reportFieldDelayMinus(Delay value, if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, result); else { - float mean_sigma = delayMeanSigma(value, early_late); + float mean_sigma = delayAsFloat(value, early_late); const char *str = units_->timeUnit()->asString(-mean_sigma, digits_); if (stringEq(str, plus_zero_)) // Force leading minus sign. @@ -3299,7 +3299,7 @@ ReportPath::reportFieldDelay(Delay value, if (delayAsFloat(value) == field_blank_) reportFieldBlank(field, result); else { - const char *str = delayMeanSigmaString(value, early_late, units_, digits_); + const char *str = delayAsString(value, early_late, units_, digits_); if (stringEq(str, minus_zero_)) // Filter "-0.00" fields. str = plus_zero_; diff --git a/search/Search.cc b/search/Search.cc index 39a6fae7..7a875f64 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -3052,7 +3052,7 @@ Search::deratedDelay(Vertex *from_vertex, DcalcAPIndex ap_index = dcalc_ap->index(); float derate = timingDerate(from_vertex, arc, edge, is_clk, path_ap); ArcDelay delay = graph_->arcDelay(edge, arc, ap_index); - return delay + Delay((derate - 1.0) * delayAsFloat(delay)); + return delay * derate; } float @@ -3706,13 +3706,40 @@ Search::exceptionTo(ExceptionPathType type, Slack Search::totalNegativeSlack(const MinMax *min_max) +{ + tnsPreamble(); + Slack tns = 0.0; + CornerIterator corner_iter(this); + while (corner_iter.hasNext()) { + Corner *corner = corner_iter.next(); + PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + Slack tns1 = tns_[path_ap_index]; + if (tns1 < tns) + tns = tns1; + } + return tns; +} + +Slack +Search::totalNegativeSlack(const Corner *corner, + const MinMax *min_max) +{ + tnsPreamble(); + PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + return tns_[path_ap_index]; +} + +void +Search::tnsPreamble() { wnsTnsPreamble(); + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + tns_.resize(path_ap_count); + tns_slacks_.resize(path_ap_count); if (tns_exists_) updateInvalidTns(); else findTotalNegativeSlacks(); - return static_cast(tns_[min_max->index()]); } void @@ -3731,6 +3758,7 @@ Search::tnsInvalid(Vertex *vertex) void Search::updateInvalidTns() { + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); VertexSet::Iterator vertex_iter(invalid_tns_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); @@ -3738,7 +3766,7 @@ Search::updateInvalidTns() if (isEndpoint(vertex)) { debugPrint1(debug_, "tns", 2, "update tns %s\n", vertex->name(sdc_network_)); - Slack slacks[MinMax::index_count]; + SlackSeq slacks(path_ap_count); wnsSlacks(vertex, slacks); if (tns_exists_) @@ -3753,67 +3781,64 @@ Search::updateInvalidTns() void Search::findTotalNegativeSlacks() { - int min_index = MinMax::minIndex(); - int max_index = MinMax::maxIndex(); - tns_[min_index] = 0.0; - tns_[max_index] = 0.0; - tns_slacks_[min_index].clear(); - tns_slacks_[max_index].clear(); + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + for (PathAPIndex i = 0; i < path_ap_count; i++) { + tns_[i] = 0.0; + tns_slacks_[i].clear(); + } VertexSet::Iterator end_iter(endpoints()); while (end_iter.hasNext()) { Vertex *vertex = end_iter.next(); // No locking required. - Slack slacks[MinMax::index_count]; + SlackSeq slacks(path_ap_count); wnsSlacks(vertex, slacks); - tnsIncr(vertex, delayAsFloat(slacks[min_index]), min_index); - tnsIncr(vertex, delayAsFloat(slacks[max_index]), max_index); + for (PathAPIndex i = 0; i < path_ap_count; i++) + tnsIncr(vertex, slacks[i], i); } tns_exists_ = true; } void Search::updateTns(Vertex *vertex, - Slack *slacks) + SlackSeq &slacks) { - int min_index = MinMax::minIndex(); - int max_index = MinMax::maxIndex(); - tnsDecr(vertex, min_index); - tnsIncr(vertex, delayAsFloat(slacks[min_index]), min_index); - - tnsDecr(vertex, max_index); - tnsIncr(vertex, delayAsFloat(slacks[max_index]), max_index); + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + for (PathAPIndex i = 0; i < path_ap_count; i++) { + tnsDecr(vertex, i); + tnsIncr(vertex, slacks[i], i); + } } void Search::tnsIncr(Vertex *vertex, - float slack, - int min_max_index) + Slack slack, + PathAPIndex path_ap_index) { - if (fuzzyLess(slack, 0.0)) { + if (delayFuzzyLess(slack, 0.0)) { debugPrint2(debug_, "tns", 3, "tns+ %s %s\n", delayAsString(slack, units_), vertex->name(sdc_network_)); - tns_[min_max_index] += slack; - if (tns_slacks_[min_max_index].hasKey(vertex)) + tns_[path_ap_index] += slack; + if (tns_slacks_[path_ap_index].hasKey(vertex)) internalError("tns incr existing vertex"); - tns_slacks_[min_max_index][vertex] = slack; + tns_slacks_[path_ap_index][vertex] = slack; } } void Search::tnsDecr(Vertex *vertex, - int min_max_index) + PathAPIndex path_ap_index) { Slack slack; bool found; - tns_slacks_[min_max_index].findKey(vertex, slack, found); + tns_slacks_[path_ap_index].findKey(vertex, slack, found); if (found && delayFuzzyLess(slack, 0.0)) { debugPrint2(debug_, "tns", 3, "tns- %s %s\n", delayAsString(slack, units_), vertex->name(sdc_network_)); - tns_[min_max_index] -= delayAsFloat(slack); - tns_slacks_[min_max_index].eraseKey(vertex); + tns_[path_ap_index] -= delayAsFloat(slack); + tns_slacks_[path_ap_index].eraseKey(vertex); } } @@ -3823,27 +3848,44 @@ Search::tnsNotifyBefore(Vertex *vertex) { if (tns_exists_ && isEndpoint(vertex)) { - int min_index = MinMax::minIndex(); - int max_index = MinMax::maxIndex(); - tnsDecr(vertex, min_index); - tnsDecr(vertex, max_index); + int ap_count = corners_->pathAnalysisPtCount(); + for (int i = 0; i < ap_count; i++) { + tnsDecr(vertex, i); + } } } //////////////////////////////////////////////////////////////// -Slack -Search::worstSlack(const MinMax *min_max) +void +Search::worstSlack(const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worstSlackPreamble(); - return worst_slacks_->worstSlack(min_max); + worst_slacks_->worstSlack(min_max, worst_slack, worst_vertex); } -Vertex * -Search::worstSlackVertex(const MinMax *min_max) +void +Search::worstSlack(const Corner *corner, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { worstSlackPreamble(); - return worst_slacks_->worstSlackVertex(min_max); + worst_slacks_->worstSlack(corner, min_max, worst_slack, worst_vertex); +} + +void +Search::worstSlackPreamble() +{ + wnsTnsPreamble(); + if (worst_slacks_) + updateInvalidTns(); + else + worst_slacks_ = new WorstSlacks(this); } void @@ -3873,16 +3915,6 @@ Search::wnsTnsPreamble() seedRequireds(); } -void -Search::worstSlackPreamble() -{ - wnsTnsPreamble(); - if (worst_slacks_) - updateInvalidTns(); - else - worst_slacks_ = new WorstSlacks(this); -} - void Search::clearWorstSlack() { @@ -3898,17 +3930,17 @@ Search::clearWorstSlack() class FindEndSlackVisitor : public PathEndVisitor { public: - FindEndSlackVisitor(Slack *slacks, + FindEndSlackVisitor(SlackSeq &slacks, const StaState *sta); virtual PathEndVisitor *copy(); virtual void visit(PathEnd *path_end); protected: - Slack *slacks_; + SlackSeq &slacks_; const StaState *sta_; }; -FindEndSlackVisitor::FindEndSlackVisitor(Slack *slacks, +FindEndSlackVisitor::FindEndSlackVisitor(SlackSeq &slacks, const StaState *sta) : slacks_(slacks), sta_(sta) @@ -3927,21 +3959,22 @@ FindEndSlackVisitor::visit(PathEnd *path_end) { if (!path_end->isUnconstrained()) { PathRef &path = path_end->pathRef(); - int mm_index = path.minMax(sta_)->index(); + PathAPIndex path_ap_index = path.pathAnalysisPtIndex(sta_); Slack slack = path_end->slack(sta_); - if (delayFuzzyLess(slack, slacks_[mm_index])) - slacks_[mm_index] = slack; + if (delayFuzzyLess(slack, slacks_[path_ap_index])) + slacks_[path_ap_index] = slack; } } void Search::wnsSlacks(Vertex *vertex, // Return values. - Slack *slacks) + SlackSeq &slacks) { Slack slack_init = MinMax::min()->initValue(); - slacks[MinMax::minIndex()] = slack_init; - slacks[MinMax::maxIndex()] = slack_init; + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + for (PathAPIndex i = 0; i < path_ap_count; i++) + slacks[i] = slack_init; if (hasFanout(vertex, search_adj_, graph_)) { // If the vertex has fanout the path slacks include downstream // PathEnd slacks so find the endpoint slack directly. @@ -3952,23 +3985,23 @@ Search::wnsSlacks(Vertex *vertex, VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); - const MinMax *path_min_max = path->minMax(this); - int path_mm_index = path_min_max->index(); + PathAPIndex path_ap_index = path->pathAnalysisPtIndex(this); const Slack path_slack = path->slack(this); if (!path->tag(this)->isFilter() - && delayFuzzyLess(path_slack, slacks[path_mm_index])) - slacks[path_mm_index] = path_slack; + && delayFuzzyLess(path_slack, slacks[path_ap_index])) + slacks[path_ap_index] = path_slack; } } } Slack Search::wnsSlack(Vertex *vertex, - const MinMax *min_max) + PathAPIndex path_ap_index) { - Slack slacks[MinMax::index_count]; + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + SlackSeq slacks(path_ap_count); wnsSlacks(vertex, slacks); - return slacks[min_max->index()]; + return slacks[path_ap_index]; } //////////////////////////////////////////////////////////////// diff --git a/search/Search.hh b/search/Search.hh index 7a88ca6a..3164df39 100644 --- a/search/Search.hh +++ b/search/Search.hh @@ -59,6 +59,8 @@ typedef Set ClkInfoSet; typedef HashSet TagHashSet; typedef HashSet TagGroupSet; typedef Map VertexSlackMap; +typedef Vector VertexSlackMapSeq; +typedef Vector WorstSlacksSeq; class Search : public StaState { @@ -124,11 +126,22 @@ public: void findRequireds(Level level); bool requiredsSeeded() const { return requireds_seeded_; } bool requiredsExist() const { return requireds_exist_; } + // The sum of all negative endpoints slacks. + // Incrementally updated. Slack totalNegativeSlack(const MinMax *min_max); - // Worst endpoint slack. - Slack worstSlack(const MinMax *min_max); - // Worst endpoint slack vertex. - Vertex *worstSlackVertex(const MinMax *min_max); + Slack totalNegativeSlack(const Corner *corner, + const MinMax *min_max); + // Worst endpoint slack and vertex. + // Incrementally updated. + void worstSlack(const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Corner *corner, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); // Clock arrival respecting ideal clock insertion delay and latency. Arrival clkPathArrival(const Path *clk_path) const; Arrival clkPathArrival(const Path *clk_path, @@ -313,7 +326,7 @@ public: TagGroup *tagGroup(TagGroupIndex index) const; void reportArrivals(Vertex *vertex) const; Slack wnsSlack(Vertex *vertex, - const MinMax *min_max); + PathAPIndex path_ap_index); void levelChangedBefore(Vertex *vertex); void seedInputArrival(const Pin *pin, Vertex *vertex, @@ -471,24 +484,25 @@ protected: void deleteFilterTagGroups(); void deleteFilterClkInfos(); + void tnsPreamble(); void findTotalNegativeSlacks(); void updateInvalidTns(); void clearWorstSlack(); void wnsSlacks(Vertex *vertex, // Return values. - Slack *slacks); + SlackSeq &slacks); void wnsTnsPreamble(); void worstSlackPreamble(); void deleteWorstSlacks(); void updateWorstSlacks(Vertex *vertex, - Slack *slacks); + Slack slacks); void updateTns(Vertex *vertex, - Slack *slacks); + SlackSeq &slacks); void tnsIncr(Vertex *vertex, - float slack, - int min_max_index); + Slack slack, + PathAPIndex path_ap_index); void tnsDecr(Vertex *vertex, - int min_max_index); + PathAPIndex path_ap_index); void tnsNotifyBefore(Vertex *vertex); PathGroups *makePathGroups(int group_count, int endpoint_count, @@ -539,9 +553,12 @@ protected: bool tns_exists_; // Endpoint vertices with slacks that have changed since tns was found. VertexSet invalid_tns_; - double tns_[MinMax::index_count]; - VertexSlackMap tns_slacks_[MinMax::index_count]; + // Indexed by path_ap->index(). + SlackSeq tns_; + // Indexed by path_ap->index(). + VertexSlackMapSeq tns_slacks_; mutable Mutex tns_lock_; + // Indexed by path_ap->index(). WorstSlacks *worst_slacks_; // Use pointer to clk_info set so Tag.hh does not need to be included. ClkInfoSet *clk_info_set_; diff --git a/search/SearchClass.hh b/search/SearchClass.hh index a66fb98d..313fc9de 100644 --- a/search/SearchClass.hh +++ b/search/SearchClass.hh @@ -110,6 +110,7 @@ typedef Vector ArrivalSeq; typedef Map VertexPathCountMap; typedef UnorderedMap ArrivalMap; typedef Vector PathVertexSeq; +typedef Vector SlackSeq; typedef enum { report_path_full, diff --git a/search/Sim.cc b/search/Sim.cc index 47bcd52b..f7099765 100644 --- a/search/Sim.cc +++ b/search/Sim.cc @@ -1114,11 +1114,10 @@ isCondDisabled(Edge *edge, LibertyCell *cell = network->libertyCell(inst); LibertyPort *from_port = network->libertyPort(from_pin); LibertyPort *to_port = network->libertyPort(to_pin); - CellTimingArcSetIterator *cond_iter = - cell->timingArcSetIterator(from_port, to_port); + LibertyCellTimingArcSetIterator cond_iter(cell, from_port, to_port); is_disabled = false; - while (cond_iter->hasNext()) { - TimingArcSet *cond_set = cond_iter->next(); + while (cond_iter.hasNext()) { + TimingArcSet *cond_set = cond_iter.next(); FuncExpr *cond = cond_set->cond(); if (cond && sim->evalExpr(cond, inst) == logic_one) { disable_cond = cond; @@ -1126,7 +1125,6 @@ isCondDisabled(Edge *edge, break; } } - delete cond_iter; } } diff --git a/search/Sta.cc b/search/Sta.cc index 6ef6c813..7fd8cb50 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -515,6 +515,7 @@ Sta::~Sta() delete debug_; delete units_; delete report_; + delete power_; } void @@ -1624,15 +1625,12 @@ hasDisabledArcs(Edge *edge, Graph *graph) { TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); - if (!searchThru(edge, arc, graph)) { - delete arc_iter; + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + if (!searchThru(edge, arc, graph)) return true; - } } - delete arc_iter; return false; } @@ -2847,17 +2845,32 @@ Sta::totalNegativeSlack(const MinMax *min_max) } Slack -Sta::worstSlack(const MinMax *min_max) +Sta::totalNegativeSlack(const Corner *corner, + const MinMax *min_max) { searchPreamble(); - return search_->worstSlack(min_max); + return search_->totalNegativeSlack(corner, min_max); } -Vertex * -Sta::worstSlackVertex(const MinMax *min_max) +void +Sta::worstSlack(const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { searchPreamble(); - return search_->worstSlackVertex(min_max); + return search_->worstSlack(min_max, worst_slack, worst_vertex); +} + +void +Sta::worstSlack(const Corner *corner, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) +{ + searchPreamble(); + return search_->worstSlack(corner, min_max, worst_slack, worst_vertex); } //////////////////////////////////////////////////////////////// @@ -3623,16 +3636,15 @@ Sta::makeInstanceAfter(Instance *inst) { LibertyCell *lib_cell = network_->libertyCell(inst); if (lib_cell && lib_cell->hasInternalPorts()) { - LibertyCellPortBitIterator *port_iter = lib_cell->libertyPortBitIterator(); - while (port_iter->hasNext()) { - LibertyPort *lib_port = port_iter->next(); + LibertyCellPortBitIterator port_iter(lib_cell); + while (port_iter.hasNext()) { + LibertyPort *lib_port = port_iter.next(); if (lib_port->direction()->isInternal() && lib_cell->hasTimingArcs(lib_port)) { Pin *pin = network_->findPin(inst, lib_port); connectPinAfter(pin); } } - delete port_iter; } } @@ -4719,7 +4731,7 @@ Sta::reportSlewLimitVerbose(Pin *pin, Slew slew; float limit, slack; check_slew_limits_->checkSlews(pin, corner, min_max, - corner, tr, slew, limit, slack); + corner1, tr, slew, limit, slack); report_path_->reportSlewLimitVerbose(pin, corner1, tr, slew, limit, slack, min_max); } diff --git a/search/Sta.hh b/search/Sta.hh index 17e3cf42..fdb0b232 100644 --- a/search/Sta.hh +++ b/search/Sta.hh @@ -903,12 +903,19 @@ public: // The sum of all negative endpoints slacks. // Incrementally updated. Slack totalNegativeSlack(const MinMax *min_max); - // Worst endpoint slack. + Slack totalNegativeSlack(const Corner *corner, + const MinMax *min_max); + // Worst endpoint slack and vertex. // Incrementally updated. - Slack worstSlack(const MinMax *min_max); - // Worst endpoint slack vertex. - // Incrementally updated. - Vertex *worstSlackVertex(const MinMax *min_max); + void worstSlack(const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Corner *corner, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); VertexPathIterator *vertexPathIterator(Vertex *vertex, const TransRiseFall *tr, const PathAnalysisPt *path_ap); diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc index d3a03a80..856cffc3 100644 --- a/search/WorstSlack.cc +++ b/search/WorstSlack.cc @@ -19,155 +19,95 @@ #include "Debug.hh" #include "Report.hh" #include "Graph.hh" +#include "Corner.hh" #include "Search.hh" +#include "PathAnalysisPt.hh" #include "WorstSlack.hh" namespace sta { using std::min; -//////////////////////////////////////////////////////////////// - -class WnsSlackLess +WorstSlacks::WorstSlacks(StaState *sta) : + sta_(sta) { -public: - WnsSlackLess(const MinMax *min_max, - Search *search); - bool operator()(Vertex *vertex1, - Vertex *vertex2); - -private: - const MinMax *min_max_; - Search *search_; -}; - -WnsSlackLess::WnsSlackLess(const MinMax *min_max, - Search *search) : - min_max_(min_max), - search_(search) -{ -} - -bool -WnsSlackLess::operator()(Vertex *vertex1, - Vertex *vertex2) -{ - return delayFuzzyLess(search_->wnsSlack(vertex1, min_max_), - search_->wnsSlack(vertex2, min_max_)); -} - -//////////////////////////////////////////////////////////////// - -class WorstSlack -{ -public: - WorstSlack(const MinMax *min_max, - StaState *sta); - Vertex *worstVertex(); - Slack worstSlack(); - void clear(); - void updateWorstSlack(Vertex *vertex, - Slack *slacks); - void deleteVertexBefore(Vertex *vertex); - -protected: - void findWorstSlack(); - void initQueue(); - void findWorstInQueue(); - void setWorstSlack(Vertex *vertex, - Slack slack); - void sortQueue(); - void checkQueue(); - - Slack slack_init_; - // Vertex with the worst slack. - // When NULL the worst slack is unknown but in the queue. - Vertex *worst_vertex_; - Slack worst_slack_; - Slack slack_threshold_; - // Vertices with slack < threshold_ - VertexSet queue_; - // Queue is sorted and pruned to min_queue_size_ vertices when it - // reaches max_queue_size_. - int min_queue_size_; - int max_queue_size_; - Mutex lock_; - const MinMax *min_max_; - WnsSlackLess slack_less_; - StaState *sta_; -}; - -//////////////////////////////////////////////////////////////// - - -WorstSlacks::WorstSlacks(StaState *sta) -{ - worst_slacks_[MinMax::minIndex()] = new WorstSlack(MinMax::min(), sta); - worst_slacks_[MinMax::maxIndex()] = new WorstSlack(MinMax::max(), sta); -} - -WorstSlacks::~WorstSlacks() -{ - delete worst_slacks_[MinMax::minIndex()]; - delete worst_slacks_[MinMax::maxIndex()]; + worst_slacks_.resize(sta->corners()->pathAnalysisPtCount()); } void -WorstSlacks::clear() +WorstSlacks::worstSlack(const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { - worst_slacks_[MinMax::minIndex()]->clear(); - worst_slacks_[MinMax::maxIndex()]->clear(); + worst_slack = MinMax::min()->initValue(); + worst_vertex = NULL; + CornerIterator corner_iter(sta_); + while (corner_iter.hasNext()) { + Corner *corner = corner_iter.next(); + PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + Slack worst_slack1; + Vertex *worst_vertex1; + worst_slacks_[path_ap_index].worstSlack(path_ap_index, sta_, + worst_slack1, worst_vertex1); + if (delayFuzzyLess(worst_slack1, worst_slack)) { + worst_slack = worst_slack1; + worst_vertex = worst_vertex1; + } + } } -Slack -WorstSlacks::worstSlack(const MinMax *min_max) +void +WorstSlacks::worstSlack(const Corner *corner, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { - return worst_slacks_[min_max->index()]->worstSlack(); -} - -Vertex * -WorstSlacks::worstSlackVertex(const MinMax *min_max) -{ - return worst_slacks_[min_max->index()]->worstVertex(); + PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + worst_slacks_[path_ap_index].worstSlack(path_ap_index, sta_, + worst_slack, worst_vertex); } void WorstSlacks::updateWorstSlacks(Vertex *vertex, - Slack *slacks) + SlackSeq &slacks) { - worst_slacks_[MinMax::minIndex()]->updateWorstSlack(vertex, slacks); - worst_slacks_[MinMax::maxIndex()]->updateWorstSlack(vertex, slacks); + PathAPIndex path_ap_count = sta_->corners()->pathAnalysisPtCount(); + for (PathAPIndex i = 0; i < path_ap_count; i++) + worst_slacks_[i].updateWorstSlack(vertex, slacks, i, sta_); } void WorstSlacks::worstSlackNotifyBefore(Vertex *vertex) { - worst_slacks_[MinMax::minIndex()]->deleteVertexBefore(vertex); - worst_slacks_[MinMax::maxIndex()]->deleteVertexBefore(vertex); + WorstSlackSeq::Iterator worst_iter(worst_slacks_); + while (worst_iter.hasNext()) { + WorstSlack &worst_slack = worst_iter.next(); + worst_slack.deleteVertexBefore(vertex); + } } //////////////////////////////////////////////////////////////// -WorstSlack::WorstSlack(const MinMax *min_max, - StaState *sta) : +WorstSlack::WorstSlack() : slack_init_(MinMax::min()->initValue()), worst_vertex_(NULL), worst_slack_(slack_init_), slack_threshold_(slack_init_), min_queue_size_(10), - max_queue_size_(20), - min_max_(min_max), - slack_less_(min_max, sta->search()), - sta_(sta) + max_queue_size_(20) { } -void -WorstSlack::clear() +WorstSlack::WorstSlack(const WorstSlack &) : + slack_init_(MinMax::min()->initValue()), + worst_vertex_(NULL), + worst_slack_(slack_init_), + slack_threshold_(slack_init_), + min_queue_size_(10), + max_queue_size_(20) { - queue_.clear(); - worst_vertex_ = NULL; - worst_slack_ = slack_init_; } void @@ -182,36 +122,36 @@ WorstSlack::deleteVertexBefore(Vertex *vertex) lock_.unlock(); } -Vertex * -WorstSlack::worstVertex() +void +WorstSlack::worstSlack(PathAPIndex path_ap_index, + const StaState *sta, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) { - findWorstSlack(); - return worst_vertex_; -} - -Slack -WorstSlack::worstSlack() -{ - findWorstSlack(); - return worst_slack_; + findWorstSlack(path_ap_index, sta); + worst_slack = worst_slack_; + worst_vertex = worst_vertex_; } void -WorstSlack::findWorstSlack() +WorstSlack::findWorstSlack(PathAPIndex path_ap_index, + const StaState *sta) { if (worst_vertex_ == NULL) { if (queue_.empty()) - initQueue(); + initQueue(path_ap_index, sta); else - findWorstInQueue(); + findWorstInQueue(path_ap_index, sta); } } void -WorstSlack::initQueue() +WorstSlack::initQueue(PathAPIndex path_ap_index, + const StaState *sta) { - Search *search = sta_->search(); - const Debug *debug = sta_->debug(); + Search *search = sta->search(); + const Debug *debug = sta->debug(); debugPrint0(debug, "wns", 3, "init queue\n"); queue_.clear(); @@ -221,27 +161,28 @@ WorstSlack::initQueue() VertexSet::Iterator end_iter(search->endpoints()); while (end_iter.hasNext()) { Vertex *vertex = end_iter.next(); - Slack slack = search->wnsSlack(vertex, min_max_); + Slack slack = search->wnsSlack(vertex, path_ap_index); if (!delayFuzzyEqual(slack, slack_init_)) { if (delayFuzzyLess(slack, worst_slack_)) - setWorstSlack(vertex, slack); + setWorstSlack(vertex, slack, sta); if (delayFuzzyLessEqual(slack, slack_threshold_)) queue_.insert(vertex); int queue_size = queue_.size(); if (queue_size >= max_queue_size_) - sortQueue(); + sortQueue(path_ap_index, sta); } } debugPrint1(debug, "wns", 3, "threshold %s\n", - delayAsString(slack_threshold_, sta_->units())); + delayAsString(slack_threshold_, sta->units())); // checkQueue(); } void -WorstSlack::sortQueue() +WorstSlack::sortQueue(PathAPIndex path_ap_index, + const StaState *sta) { - Search *search = sta_->search(); - const Debug *debug = sta_->debug(); + Search *search = sta->search(); + const Debug *debug = sta->debug(); debugPrint0(debug, "wns", 3, "sort queue\n"); VertexSeq vertices; @@ -251,36 +192,38 @@ WorstSlack::sortQueue() Vertex *vertex = queue_iter.next(); vertices.push_back(vertex); } - sort(vertices, slack_less_); + WnsSlackLess slack_less(path_ap_index, sta); + sort(vertices, slack_less); int vertex_count = vertices.size(); int threshold_index = min(min_queue_size_, vertex_count - 1); Vertex *threshold_vertex = vertices[threshold_index]; - slack_threshold_ = search->wnsSlack(threshold_vertex, min_max_); + slack_threshold_ = search->wnsSlack(threshold_vertex, path_ap_index); debugPrint1(debug, "wns", 3, "threshold %s\n", - delayAsString(slack_threshold_, sta_->units())); + delayAsString(slack_threshold_, sta->units())); // Reinsert vertices with slack < threshold. queue_.clear(); VertexSeq::Iterator queue_iter2(vertices); while (queue_iter2.hasNext()) { Vertex *vertex = queue_iter2.next(); - Slack slack = search->wnsSlack(vertex, min_max_); + Slack slack = search->wnsSlack(vertex, path_ap_index); if (delayFuzzyGreater(slack, slack_threshold_)) break; queue_.insert(vertex); } max_queue_size_ = queue_.size() * 2; Vertex *worst_slack_vertex = vertices[0]; - Slack worst_slack_slack = search->wnsSlack(worst_slack_vertex, min_max_); - setWorstSlack(worst_slack_vertex, worst_slack_slack); + Slack worst_slack_slack = search->wnsSlack(worst_slack_vertex, path_ap_index); + setWorstSlack(worst_slack_vertex, worst_slack_slack, sta); } void -WorstSlack::findWorstInQueue() +WorstSlack::findWorstInQueue(PathAPIndex path_ap_index, + const StaState *sta) { - Search *search = sta_->search(); - const Debug *debug = sta_->debug(); + Search *search = sta->search(); + const Debug *debug = sta->debug(); debugPrint0(debug, "wns", 3, "find worst in queue\n"); worst_vertex_ = NULL; @@ -288,29 +231,31 @@ WorstSlack::findWorstInQueue() VertexSet::Iterator queue_iter(queue_); while (queue_iter.hasNext()) { Vertex *vertex = queue_iter.next(); - Slack slack = search->wnsSlack(vertex, min_max_); + Slack slack = search->wnsSlack(vertex, path_ap_index); if (slack < worst_slack_) - setWorstSlack(vertex, slack); + setWorstSlack(vertex, slack, sta); } } void -WorstSlack::checkQueue() +WorstSlack::checkQueue(PathAPIndex path_ap_index, + const StaState *sta) { - Search *search = sta_->search(); - Report *report = sta_->report(); - Units *units = sta_->units(); - const Network *network = sta_->network(); + Search *search = sta->search(); + Report *report = sta->report(); + Units *units = sta->units(); + const Network *network = sta->network(); VertexSeq ends; VertexSet::Iterator end_iter(search->endpoints()); while (end_iter.hasNext()) { Vertex *end = end_iter.next(); - if (delayFuzzyLessEqual(search->wnsSlack(end, min_max_), + if (delayFuzzyLessEqual(search->wnsSlack(end, path_ap_index), slack_threshold_)) ends.push_back(end); } - sort(ends, slack_less_); + WnsSlackLess slack_less(path_ap_index, sta); + sort(ends, slack_less); VertexSet end_set; VertexSeq::Iterator end_iter2(ends); @@ -318,11 +263,11 @@ WorstSlack::checkQueue() Vertex *end = end_iter2.next(); end_set.insert(end); if (!queue_.hasKey(end) - && delayFuzzyLessEqual(search->wnsSlack(end, min_max_), + && delayFuzzyLessEqual(search->wnsSlack(end, path_ap_index), slack_threshold_)) report->print("WorstSlack queue missing %s %s < %s\n", end->name(network), - delayAsString(search->wnsSlack(end, min_max_), units), + delayAsString(search->wnsSlack(end, path_ap_index), units), delayAsString(slack_threshold_, units)); } @@ -332,7 +277,7 @@ WorstSlack::checkQueue() if (!end_set.hasKey(end)) report->print("WorstSlack queue extra %s %s > %s\n", end->name(network), - delayAsString(search->wnsSlack(end, min_max_), + delayAsString(search->wnsSlack(end, path_ap_index), units), delayAsString(slack_threshold_, units)); } @@ -340,18 +285,20 @@ WorstSlack::checkQueue() void WorstSlack::updateWorstSlack(Vertex *vertex, - Slack *slacks) + SlackSeq &slacks, + PathAPIndex path_ap_index, + const StaState *sta) { - const Debug *debug = sta_->debug(); - const Network *network = sta_->network(); - Slack slack = slacks[min_max_->index()]; + const Debug *debug = sta->debug(); + const Network *network = sta->network(); + Slack slack = slacks[path_ap_index]; - // Locking is required because ArrivalVisitor called by multiple + // Locking is required because ArrivalVisitor is called by multiple // threads. lock_.lock(); if (worst_vertex_ && delayFuzzyLess(slack, worst_slack_)) - setWorstSlack(vertex, slack); + setWorstSlack(vertex, slack, sta); else if (vertex == worst_vertex_) // Mark worst slack as unknown (updated by findWorstSlack(). worst_vertex_ = NULL; @@ -360,13 +307,13 @@ WorstSlack::updateWorstSlack(Vertex *vertex, && delayFuzzyLessEqual(slack, slack_threshold_)) { debugPrint2(debug, "wns", 3, "insert %s %s\n", vertex->name(network), - delayAsString(slack, sta_->units())); + delayAsString(slack, sta->units())); queue_.insert(vertex); } else { debugPrint2(debug, "wns", 3, "delete %s %s\n", vertex->name(network), - delayAsString(slack, sta_->units())); + delayAsString(slack, sta->units())); queue_.eraseKey(vertex); } lock_.unlock(); @@ -375,13 +322,31 @@ WorstSlack::updateWorstSlack(Vertex *vertex, void WorstSlack::setWorstSlack(Vertex *vertex, - Slack slack) + Slack slack, + const StaState *sta) { - debugPrint2(sta_->debug(), "wns", 3, "%s %s\n", - vertex->name(sta_->network()), - delayAsString(slack, sta_->units())); + debugPrint2(sta->debug(), "wns", 3, "%s %s\n", + vertex->name(sta->network()), + delayAsString(slack, sta->units())); worst_vertex_ = vertex; worst_slack_ = slack; } +//////////////////////////////////////////////////////////////// + +WnsSlackLess::WnsSlackLess(PathAPIndex path_ap_index, + const StaState *sta) : + path_ap_index_(path_ap_index), + search_(sta->search()) +{ +} + +bool +WnsSlackLess::operator()(Vertex *vertex1, + Vertex *vertex2) +{ + return delayFuzzyLess(search_->wnsSlack(vertex1, path_ap_index_), + search_->wnsSlack(vertex2, path_ap_index_)); +} + } // namespace diff --git a/search/WorstSlack.hh b/search/WorstSlack.hh index a7bd4416..20a93e4b 100644 --- a/search/WorstSlack.hh +++ b/search/WorstSlack.hh @@ -20,25 +20,95 @@ namespace sta { #include "MinMax.hh" +#include "Vector.hh" #include "GraphClass.hh" +#include "SearchClass.hh" class StaState; class WorstSlack; +class WnsSlackLess; + +typedef Vector WorstSlackSeq; class WorstSlacks { public: WorstSlacks(StaState *sta); - ~WorstSlacks(); - void clear(); - Slack worstSlack(const MinMax *min_max); - Vertex *worstSlackVertex(const MinMax *min_max); + void worstSlack(const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void worstSlack(const Corner *corner, + const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); void updateWorstSlacks(Vertex *vertex, - Slack *slacks); + SlackSeq &slacks); void worstSlackNotifyBefore(Vertex *vertex); protected: - WorstSlack *worst_slacks_[MinMax::index_count]; + WorstSlackSeq worst_slacks_; + const StaState *sta_; +}; + +class WnsSlackLess +{ +public: + WnsSlackLess(PathAPIndex path_ap_index, + const StaState *sta); + bool operator()(Vertex *vertex1, + Vertex *vertex2); + +private: + PathAPIndex path_ap_index_; + Search *search_; +}; + +class WorstSlack +{ +public: + WorstSlack(); + WorstSlack(const WorstSlack &); + void worstSlack(PathAPIndex path_ap_index, + const StaState *sta, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void updateWorstSlack(Vertex *vertex, + SlackSeq &slacks, + PathAPIndex path_ap_index, + const StaState *sta); + void deleteVertexBefore(Vertex *vertex); + +protected: + void findWorstSlack(PathAPIndex path_ap_index, + const StaState *sta); + void initQueue(PathAPIndex path_ap_index, + const StaState *sta); + void findWorstInQueue(PathAPIndex path_ap_index, + const StaState *sta); + void setWorstSlack(Vertex *vertex, + Slack slack, + const StaState *sta); + void sortQueue(PathAPIndex path_ap_index, + const StaState *sta); + void checkQueue(PathAPIndex path_ap_index, + const StaState *sta); + + Slack slack_init_; + // Vertex with the worst slack. + // When NULL the worst slack is unknown but in the queue. + Vertex *worst_vertex_; + Slack worst_slack_; + Slack slack_threshold_; + // Vertices with slack < threshold_ + VertexSet queue_; + // Queue is sorted and pruned to min_queue_size_ vertices when it + // reaches max_queue_size_. + int min_queue_size_; + int max_queue_size_; + Mutex lock_; }; } // namespace diff --git a/tcl/Cmds.tcl b/tcl/Cmds.tcl index 8ee51ec6..916b051e 100644 --- a/tcl/Cmds.tcl +++ b/tcl/Cmds.tcl @@ -1228,6 +1228,9 @@ proc get_ports_or_pins { pattern } { ################################################################ +# If -corner keyword is missing: +# one corner, return default +# multiple corner, error proc parse_corner { keys_var } { upvar 1 $keys_var keys @@ -1246,6 +1249,23 @@ proc parse_corner { keys_var } { } } +# Assumes caller checks for existence of -corner keyword arg. +proc parse_corner_required { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-corner)] } { + set corner_name $keys(-corner) + set corner [find_corner $corner_name] + if { $corner == "NULL" } { + sta_error "$corner_name is not the name of process corner." + } else { + return $corner + } + } else { + sta_error "missing -corner arg." + } +} + proc parse_corner_or_default { keys_var } { upvar 1 $keys_var keys diff --git a/tcl/Search.tcl b/tcl/Search.tcl index d0a55909..8c84c454 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -289,25 +289,39 @@ proc report_tag_arrivals { pin } { ################################################################ -define_hidden_cmd_args "total_negative_slack" {[-min]|[-max]} +define_hidden_cmd_args "total_negative_slack" \ + {[-corner corner] [-min]|[-max]} proc total_negative_slack { args } { - parse_key_args "total_negative_slack" args keys {} flags {-min -max} + parse_key_args "total_negative_slack" args \ + keys {-corner} flags {-min -max} check_argc_eq0 "total_negative_slack" $args set min_max [parse_min_max_flags flags] - set tns [total_negative_slack_cmd $min_max] + if { [info exists keys(-corner)] } { + set corner [parse_corner_required keys] + set tns [total_negative_slack_corner_cmd $corner $min_max] + } else { + set tns [total_negative_slack_cmd $min_max] + } return [time_sta_ui $tns] } ################################################################ -define_hidden_cmd_args "worst_negative_slack" {[-min]|[-max]} +define_hidden_cmd_args "worst_negative_slack" \ + {[-corner corner] [-min]|[-max]} proc worst_negative_slack { args } { - parse_key_args "total_negative_slack" args keys {} flags {-min -max} + parse_key_args "total_negative_slack" args \ + keys {-corner} flags {-min -max} check_argc_eq0 "worst_negative_slack" $args set min_max [parse_min_max_flags flags] - set worst_slack [worst_slack $min_max] + if { [info exists keys(-corner)] } { + set corner [parse_corner_required keys] + set worst_slack [worst_slack_corner $corner $min_max] + } else { + set worst_slack [worst_slack $min_max] + } if { $worst_slack < 0.0 } { return [time_sta_ui $worst_slack] } else { diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index a1d7ead3..73f84d4c 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -681,9 +681,9 @@ edgeDelayProperty(Edge *edge, ArcDelay delay = 0.0; bool delay_exists = false; TimingArcSet *arc_set = edge->timingArcSet(); - TimingArcSetArcIterator *arc_iter = arc_set->timingArcIterator(); - while (arc_iter->hasNext()) { - TimingArc *arc = arc_iter->next(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); if (to_tr == tr) { CornerIterator corner_iter(sta); @@ -700,7 +700,6 @@ edgeDelayProperty(Edge *edge, } } } - delete arc_iter; return sta->units()->timeUnit()->asString(delayAsFloat(delay), 8); } @@ -1747,7 +1746,7 @@ using namespace sta; Tcl_SetObjResult(interp, obj); } -%typemap(out) CellTimingArcSetIterator* { +%typemap(out) LibertyCellTimingArcSetIterator* { Tcl_Obj *obj=SWIG_NewInstanceObj($1, $1_descriptor, false); Tcl_SetObjResult(interp, obj); } @@ -1994,11 +1993,11 @@ private: ~TimingArcSet(); }; -class CellTimingArcSetIterator +class LibertyCellTimingArcSetIterator { private: - CellTimingArcSetIterator(); - ~CellTimingArcSetIterator(); + LibertyCellTimingArcSetIterator(); + ~LibertyCellTimingArcSetIterator(); }; class TimingArcSetArcIterator @@ -4666,27 +4665,43 @@ find_delays() } Slack -total_negative_slack_cmd(MinMax *min_max) +total_negative_slack_cmd(const MinMax *min_max) { cmdLinkedNetwork(); Sta *sta = Sta::sta(); return sta->totalNegativeSlack(min_max); } +Slack +total_negative_slack_corner_cmd(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + return sta->totalNegativeSlack(corner, min_max); +} + Slack worst_slack(const MinMax *min_max) { cmdLinkedNetwork(); Sta *sta = Sta::sta(); - return sta->worstSlack(min_max); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(min_max, worst_slack, worst_vertex); + return worst_slack; } -Vertex * -worst_slack_vertex(const MinMax *min_max) +Slack +worst_slack_corner(const Corner *corner, + const MinMax *min_max) { cmdLinkedNetwork(); Sta *sta = Sta::sta(); - return sta->worstSlackVertex(min_max); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(corner, min_max, worst_slack, worst_vertex); + return worst_slack; } PathRef * @@ -5460,10 +5475,10 @@ find_liberty_ports_matching(const char *pattern, } LibertyCellPortIterator * -liberty_port_iterator() { return self->libertyPortIterator(); } +liberty_port_iterator() { return new LibertyCellPortIterator(self); } -CellTimingArcSetIterator * -timing_arc_set_iterator() { return self->timingArcSetIterator(); } +LibertyCellTimingArcSetIterator * +timing_arc_set_iterator() { return new LibertyCellTimingArcSetIterator(self); } } // LibertyCell methods @@ -5500,7 +5515,7 @@ Cell *cell() { return self->cell(); } const char *object_name() { return self->name(); } bool is_bus() { return self->isBus(); } LibertyPortMemberIterator * -member_iterator() { return self->libertyMemberIterator(); } +member_iterator() { return new LibertyPortMemberIterator(self); } const char * direction() { return self->direction()->name(); } @@ -5575,7 +5590,7 @@ object_name() } // TimingArcSet methods -%extend CellTimingArcSetIterator { +%extend LibertyCellTimingArcSetIterator { bool has_next() { return self->hasNext(); } TimingArcSet *next() { return self->next(); } void finish() { delete self; } @@ -5908,7 +5923,7 @@ arrivals_clk(const TransRiseFall *tr, return floats; } -StringSeq * +TmpStringSeq * arrivals_clk_delays(const TransRiseFall *tr, Clock *clk, const TransRiseFall *clk_tr, @@ -5948,7 +5963,7 @@ requireds_clk(const TransRiseFall *tr, return floats; } -StringSeq * +TmpStringSeq * requireds_clk_delays(const TransRiseFall *tr, Clock *clk, const TransRiseFall *clk_tr, @@ -6002,7 +6017,7 @@ slacks_clk(const TransRiseFall *tr, return floats; } -StringSeq * +TmpStringSeq * slacks_clk_delays(const TransRiseFall *tr, Clock *clk, const TransRiseFall *clk_tr, @@ -6054,7 +6069,7 @@ Pin *to_pin() { return self->to(Sta::sta()->graph())->pin(); } TimingRole *role() { return self->role(); } const char *sense() { return timingSenseString(self->sense()); } TimingArcSetArcIterator * -timing_arc_iterator() { return self->timingArcSet()->timingArcIterator(); } +timing_arc_iterator() { return new TimingArcSetArcIterator(self->timingArcSet()); } bool is_disabled_loop() { return Sta::sta()->isDisabledLoop(self); } bool is_disabled_constraint() { return Sta::sta()->isDisabledConstraint(self);} bool is_disabled_constant() { return Sta::sta()->isDisabledConstant(self); } @@ -6084,7 +6099,7 @@ arc_delays(TimingArc *arc) return floats; } -StringSeq * +TmpStringSeq * arc_delay_strings(TimingArc *arc, int digits) { diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 7049c45d..b1a306ac 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -560,9 +560,8 @@ VerilogReader::makeModuleInst(const char *module_name, Port *port = network_->findPort(cell, port_name); LibertyPort *lport = network_->libertyPort(port); if (lport->isBus()) { - LibertyPortMemberIterator *member_iter=lport->libertyMemberIterator(); - lport = member_iter->next(); - delete member_iter; + LibertyPortMemberIterator member_iter(lport); + lport = member_iter.next(); } int pin_index = lport->pinIndex(); const char *prev_net_name = net_names[pin_index]; @@ -1969,9 +1968,9 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, Instance *inst = network_->makeInstance(cell, lib_inst->instanceName(), parent); const char **net_names = lib_inst->netNames(); - LibertyCellPortBitIterator *port_iter = lib_cell->libertyPortBitIterator(); - while (port_iter->hasNext()) { - LibertyPort *port = port_iter->next(); + LibertyCellPortBitIterator port_iter(lib_cell); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); const char *net_name = net_names[port->pinIndex()]; // net_name may be the name of a single bit bus. if (net_name) { @@ -1997,7 +1996,6 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, && lib_cell->hasTimingArcs(port)) network_->makePin(inst, reinterpret_cast(port), NULL); } - delete port_iter; } ////////////////////////////////////////////////////////////////