// OpenSTA, Static Timing Analyzer // Copyright (c) 2025, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. // // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // This notice may not be removed or altered from any source distribution. #include "Sta.hh" #include "Machine.hh" #include "DispatchQueue.hh" #include "ReportTcl.hh" #include "Debug.hh" #include "Stats.hh" #include "Fuzzy.hh" #include "Units.hh" #include "TimingArc.hh" #include "FuncExpr.hh" #include "EquivCells.hh" #include "Liberty.hh" #include "liberty/LibertyReader.hh" #include "LibertyWriter.hh" #include "SdcNetwork.hh" #include "MakeConcreteNetwork.hh" #include "PortDirection.hh" #include "VerilogReader.hh" #include "Graph.hh" #include "GraphCmp.hh" #include "Sdc.hh" #include "Variables.hh" #include "WriteSdc.hh" #include "ExceptionPath.hh" #include "MakeConcreteParasitics.hh" #include "Parasitics.hh" #include "parasitics/SpefReader.hh" #include "parasitics/ReportParasiticAnnotation.hh" #include "DelayCalc.hh" #include "ArcDelayCalc.hh" #include "GraphDelayCalc.hh" #include "sdf/SdfWriter.hh" #include "Levelize.hh" #include "Sim.hh" #include "ClkInfo.hh" #include "TagGroup.hh" #include "PathAnalysisPt.hh" #include "Corner.hh" #include "Search.hh" #include "Latches.hh" #include "PathGroup.hh" #include "CheckTiming.hh" #include "CheckSlewLimits.hh" #include "CheckFanoutLimits.hh" #include "CheckCapacitanceLimits.hh" #include "CheckMinPulseWidths.hh" #include "CheckMinPeriods.hh" #include "CheckMaxSkews.hh" #include "ClkSkew.hh" #include "ClkLatency.hh" #include "FindRegister.hh" #include "ReportPath.hh" #include "VisitPathGroupVertices.hh" #include "Genclks.hh" #include "ClkNetwork.hh" #include "power/Power.hh" #include "VisitPathEnds.hh" #include "PathExpanded.hh" #include "MakeTimingModel.hh" #include "spice/WritePathSpice.hh" namespace sta { using std::string; using std::min; using std::max; static const ClockEdge *clk_edge_wildcard = reinterpret_cast(1); static bool libertyPortCapsEqual(const LibertyPort *port1, const LibertyPort *port2); static bool hasDisabledArcs(Edge *edge, Graph *graph); static InstanceSet pinInstances(PinSet &pins, const Network *network); //////////////////////////////////////////////////////////////// // // Observers are used to propagate updates from a component // to other components. // //////////////////////////////////////////////////////////////// // When an incremental change is made the delay calculation // changes downstream. This invalidates the required times // for all vertices upstream of the changes. class StaDelayCalcObserver : public DelayCalcObserver { public: explicit StaDelayCalcObserver(Search *search); virtual void delayChangedFrom(Vertex *vertex); virtual void delayChangedTo(Vertex *vertex); virtual void checkDelayChangedTo(Vertex *vertex); private: Search *search_; }; StaDelayCalcObserver::StaDelayCalcObserver(Search *search) : DelayCalcObserver(), search_(search) { } void StaDelayCalcObserver::delayChangedFrom(Vertex *vertex) { search_->requiredInvalid(vertex); } void StaDelayCalcObserver::delayChangedTo(Vertex *vertex) { search_->arrivalInvalid(vertex); } void StaDelayCalcObserver::checkDelayChangedTo(Vertex *vertex) { search_->requiredInvalid(vertex); } //////////////////////////////////////////////////////////////// class StaSimObserver : public SimObserver { public: StaSimObserver(GraphDelayCalc *graph_delay_calc, Levelize *levelize, Search *search); virtual void valueChangeAfter(Vertex *vertex); virtual void faninEdgesChangeAfter(Vertex *vertex); virtual void fanoutEdgesChangeAfter(Vertex *vertex); private: GraphDelayCalc *graph_delay_calc_; Levelize *levelize_; Search *search_; }; StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc, Levelize *levelize, Search *search) : SimObserver(), graph_delay_calc_(graph_delay_calc), levelize_(levelize), search_(search) { } // When pins with constant values are incrementally connected to a net // the downstream delays and arrivals will not be updated (removed) // because the search predicate does not search through constants. // This observer makes sure the delays and arrivals are invalidated. void StaSimObserver::valueChangeAfter(Vertex *vertex) { graph_delay_calc_->delayInvalid(vertex); search_->arrivalInvalid(vertex); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); levelize_->invalidFrom(vertex); } void StaSimObserver::faninEdgesChangeAfter(Vertex *vertex) { graph_delay_calc_->delayInvalid(vertex); search_->arrivalInvalid(vertex); search_->endpointInvalid(vertex); } void StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex) { search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); } //////////////////////////////////////////////////////////////// class StaLevelizeObserver : public LevelizeObserver { public: StaLevelizeObserver(Search *search); void levelsChangedBefore() override; void levelChangedBefore(Vertex *vertex) override; private: Search *search_; }; StaLevelizeObserver::StaLevelizeObserver(Search *search) : search_(search) { } void StaLevelizeObserver::levelsChangedBefore() { search_->levelsChangedBefore(); } void StaLevelizeObserver::levelChangedBefore(Vertex *vertex) { search_->levelChangedBefore(vertex); } //////////////////////////////////////////////////////////////// void initSta() { initElapsedTime(); PortDirection::init(); initLiberty(); initDelayConstants(); registerDelayCalcs(); initPathSenseThru(); } void deleteAllMemory() { Sta *sta = Sta::sta(); if (sta) { delete sta; Sta::setSta(nullptr); } deleteDelayCalcs(); PortDirection::destroy(); deleteLiberty(); } //////////////////////////////////////////////////////////////// // Singleton used by TCL commands. Sta *Sta::sta_; Sta::Sta() : StaState(), current_instance_(nullptr), cmd_corner_(nullptr), verilog_reader_(nullptr), check_timing_(nullptr), check_slew_limits_(nullptr), check_fanout_limits_(nullptr), check_capacitance_limits_(nullptr), check_min_pulse_widths_(nullptr), check_min_periods_(nullptr), check_max_skews_(nullptr), clk_skews_(nullptr), report_path_(nullptr), power_(nullptr), update_genclks_(false), equiv_cells_(nullptr), graph_sdc_annotated_(false), // Default to same parasitics for all corners. parasitics_per_corner_(false), properties_(this) { } void Sta::makeComponents() { makeVariables(); makeReport(); makeDebug(); makeUnits(); makeNetwork(); makeSdc(); makeLevelize(); makeParasitics(); makeCorners(); makeArcDelayCalc(); makeGraphDelayCalc(); makeSim(); makeSearch(); makeLatches(); makeClkNetwork(); makeSdcNetwork(); makeReportPath(); makePower(); setCmdNamespace1(CmdNamespace::sdc); setThreadCount1(defaultThreadCount()); updateComponentsState(); makeObservers(); // This must follow updateComponentsState. makeParasiticAnalysisPts(); } void Sta::makeObservers() { graph_delay_calc_->setObserver(new StaDelayCalcObserver(search_)); sim_->setObserver(new StaSimObserver(graph_delay_calc_, levelize_, search_)); levelize_->setObserver(new StaLevelizeObserver(search_)); } int Sta::defaultThreadCount() const { return 1; } void Sta::setThreadCount(int thread_count) { setThreadCount1(thread_count); updateComponentsState(); } void Sta::setThreadCount1(int thread_count) { thread_count_ = thread_count; if (dispatch_queue_) dispatch_queue_->setThreadCount(thread_count); else if (thread_count > 1) dispatch_queue_ = new DispatchQueue(thread_count); } void Sta::updateComponentsState() { network_->copyState(this); cmd_network_->copyState(this); sdc_network_->copyState(this); if (graph_) graph_->copyState(this); sdc_->copyState(this); corners_->copyState(this); levelize_->copyState(this); parasitics_->copyState(this); arc_delay_calc_->copyState(this); sim_->copyState(this); search_->copyState(this); latches_->copyState(this); graph_delay_calc_->copyState(this); report_path_->copyState(this); if (check_timing_) check_timing_->copyState(this); clk_network_->copyState(this); if (clk_skews_) clk_skews_->copyState(this); if (power_) power_->copyState(this); } void Sta::makeReport() { report_ = new ReportTcl(); } void Sta::makeDebug() { debug_ = new Debug(report_); } void Sta::makeUnits() { units_ = new Units(); } void Sta::makeNetwork() { network_ = makeConcreteNetwork(); } void Sta::makeSdc() { sdc_ = new Sdc(this); } void Sta::makeLevelize() { levelize_ = new Levelize(this); } void Sta::makeParasitics() { parasitics_ = makeConcreteParasitics(this); } void Sta::makeArcDelayCalc() { arc_delay_calc_ = makeDelayCalc("dmp_ceff_elmore", this); } void Sta::makeGraphDelayCalc() { graph_delay_calc_ = new GraphDelayCalc(this); } void Sta::makeSim() { sim_ = new Sim(this); } void Sta::makeSearch() { search_ = new Search(this); } void Sta::makeLatches() { latches_ = new Latches(this); } void Sta::makeSdcNetwork() { sdc_network_ = sta::makeSdcNetwork(network_); } void Sta::makeCheckTiming() { check_timing_ = new CheckTiming(this); } void Sta::makeCheckSlewLimits() { check_slew_limits_ = new CheckSlewLimits(this); } void Sta::makeCheckFanoutLimits() { check_fanout_limits_ = new CheckFanoutLimits(this); } void Sta::makeCheckCapacitanceLimits() { check_capacitance_limits_ = new CheckCapacitanceLimits(this); } void Sta::makeCheckMinPulseWidths() { check_min_pulse_widths_ = new CheckMinPulseWidths(this); } void Sta::makeCheckMinPeriods() { check_min_periods_ = new CheckMinPeriods(this); } void Sta::makeCheckMaxSkews() { check_max_skews_ = new CheckMaxSkews(this); } void Sta::makeReportPath() { report_path_ = new ReportPath(this); } void Sta::makeClkNetwork() { clk_network_ = new ClkNetwork(this); } void Sta::makePower() { power_ = new Power(this); } void Sta::makeVariables() { variables_ = new Variables(); } void Sta::setSta(Sta *sta) { sta_ = sta; } Sta * Sta::sta() { return sta_; } Sta::~Sta() { delete variables_; // Verilog modules refer to the network in the sta so it has // to deleted before the network. delete verilog_reader_; // Delete "top down" to minimize chance of referencing deleted memory. delete check_slew_limits_; delete check_fanout_limits_; delete check_capacitance_limits_; delete check_min_pulse_widths_; delete check_min_periods_; delete check_max_skews_; delete clk_skews_; delete check_timing_; delete report_path_; // Sdc references search filter, so delete search first. delete search_; delete latches_; delete parasitics_; delete arc_delay_calc_; delete graph_delay_calc_; delete sim_; delete levelize_; delete sdc_; delete corners_; delete graph_; delete sdc_network_; delete network_; delete debug_; delete units_; delete report_; delete clk_network_; delete power_; delete equiv_cells_; delete dispatch_queue_; } void Sta::clear() { clkPinsInvalid(); // Constraints reference search filter, so clear search first. search_->clear(); sdc_->clear(); graph_sdc_annotated_ = false; // corners are NOT cleared because they are used to index liberty files. levelize_->clear(); if (parasitics_) parasitics_->clear(); graph_delay_calc_->clear(); sim_->clear(); power_->clear(); if (check_min_pulse_widths_) check_min_pulse_widths_->clear(); if (check_min_periods_) check_min_periods_->clear(); delete graph_; graph_ = nullptr; current_instance_ = nullptr; // Notify components that graph is toast. updateComponentsState(); } void Sta::networkChanged() { // Everything else from clear(). search_->clear(); levelize_->clear(); if (parasitics_) parasitics_->clear(); graph_delay_calc_->clear(); sim_->clear(); if (check_min_pulse_widths_) check_min_pulse_widths_->clear(); if (check_min_periods_) check_min_periods_->clear(); delete graph_; graph_ = nullptr; graph_sdc_annotated_ = false; current_instance_ = nullptr; updateComponentsState(); } void Sta::setTclInterp(Tcl_Interp *interp) { tcl_interp_ = interp; report_->setTclInterp(interp); } Tcl_Interp * Sta::tclInterp() { return tcl_interp_; } CmdNamespace Sta::cmdNamespace() { return cmd_namespace_; } void Sta::setCmdNamespace(CmdNamespace namespc) { setCmdNamespace1(namespc); updateComponentsState(); } void Sta::setCmdNamespace1(CmdNamespace namespc) { cmd_namespace_ = namespc; switch (cmd_namespace_) { case CmdNamespace::sta: cmd_network_ = network_; break; case CmdNamespace::sdc: cmd_network_ = sdc_network_; break; } } Instance * Sta::currentInstance() const { if (current_instance_ == nullptr) return network_->topInstance(); else return current_instance_; } void Sta::setCurrentInstance(Instance *inst) { current_instance_ = inst; } //////////////////////////////////////////////////////////////// LibertyLibrary * Sta::readLiberty(const char *filename, Corner *corner, const MinMaxAll *min_max, bool infer_latches) { Stats stats(debug_, report_); LibertyLibrary *library = readLibertyFile(filename, corner, min_max, infer_latches); if (library // The default library is the first library read. // This corresponds to a link_path of '*'. && network_->defaultLibertyLibrary() == nullptr) { network_->setDefaultLibertyLibrary(library); // Set units from default (first) library. *units_ = *library->units(); } stats.report("Read liberty"); return library; } LibertyLibrary * Sta::readLibertyFile(const char *filename, Corner *corner, const MinMaxAll *min_max, bool infer_latches) { LibertyLibrary *liberty = sta::readLibertyFile(filename, infer_latches, network_); if (liberty) { // Don't map liberty cells if they are redefined by reading another // library with the same cell names. if (min_max == MinMaxAll::all()) { readLibertyAfter(liberty, corner, MinMax::min()); readLibertyAfter(liberty, corner, MinMax::max()); } else readLibertyAfter(liberty, corner, min_max->asMinMax()); network_->readLibertyAfter(liberty); } return liberty; } LibertyLibrary * Sta::readLibertyFile(const char *filename, bool infer_latches) { return sta::readLibertyFile(filename, infer_latches, network_); } void Sta::readLibertyAfter(LibertyLibrary *liberty, Corner *corner, const MinMax *min_max) { corner->addLiberty(liberty, min_max); LibertyLibrary::makeCornerMap(liberty, corner->libertyIndex(min_max), network_, report_); } bool Sta::readVerilog(const char *filename) { NetworkReader *network = networkReader(); if (network) { if (verilog_reader_ == nullptr) verilog_reader_ = new VerilogReader(network); readNetlistBefore(); return verilog_reader_->read(filename); } else return false; } void Sta::readNetlistBefore() { clear(); NetworkReader *network_reader = networkReader(); if (network_reader) network_reader->readNetlistBefore(); } bool Sta::linkDesign(const char *top_cell_name, bool make_black_boxes) { clear(); Stats stats(debug_, report_); bool status = network_->linkNetwork(top_cell_name, make_black_boxes, report_); stats.report("Link"); return status; } //////////////////////////////////////////////////////////////// void Sta::setDebugLevel(const char *what, int level) { debug_->setLevel(what, level); } //////////////////////////////////////////////////////////////// void Sta::setAnalysisType(AnalysisType analysis_type) { if (analysis_type != sdc_->analysisType()) { sdc_->setAnalysisType(analysis_type); delaysInvalid(); search_->deletePathGroups(); corners_->analysisTypeChanged(); if (graph_) graph_->setDelayCount(corners_->dcalcAnalysisPtCount()); } } OperatingConditions * Sta::operatingConditions(const MinMax *min_max) const { return sdc_->operatingConditions(min_max); } void Sta::setOperatingConditions(OperatingConditions *op_cond, const MinMaxAll *min_max) { sdc_->setOperatingConditions(op_cond, min_max); corners_->operatingConditionsChanged(); delaysInvalid(); } const Pvt * Sta::pvt(Instance *inst, const MinMax *min_max) { return sdc_->pvt(inst, min_max); } void Sta::setPvt(Instance *inst, const MinMaxAll *min_max, float process, float voltage, float temperature) { Pvt pvt(process, voltage, temperature); setPvt(inst, min_max, pvt); } void Sta::setPvt(const Instance *inst, const MinMaxAll *min_max, const Pvt &pvt) { sdc_->setPvt(inst, min_max, pvt); delaysInvalidFrom(inst); } void Sta::setVoltage(const MinMax *min_max, float voltage) { sdc_->setVoltage(min_max, voltage); } void Sta::setVoltage(const Net *net, const MinMax *min_max, float voltage) { sdc_->setVoltage(net, min_max, voltage); } void Sta::setTimingDerate(TimingDerateType type, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { sdc_->setTimingDerate(type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); } void Sta::setTimingDerate(const Net *net, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { sdc_->setTimingDerate(net, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); } void Sta::setTimingDerate(const Instance *inst, TimingDerateCellType type, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { sdc_->setTimingDerate(inst, type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); } void Sta::setTimingDerate(const LibertyCell *cell, TimingDerateCellType type, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { sdc_->setTimingDerate(cell, type, clk_data, rf, early_late, derate); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); } void Sta::unsetTimingDerate() { sdc_->unsetTimingDerate(); // Delay calculation results are still valid. // The search derates delays while finding arrival times. search_->arrivalsInvalid(); } void Sta::setInputSlew(const Port *port, const RiseFallBoth *rf, const MinMaxAll *min_max, float slew) { sdc_->setInputSlew(port, rf, min_max, slew); delaysInvalidFrom(port); } void Sta::setDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const Port *port, const LibertyPort *from_port, float *from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max) { sdc_->setDriveCell(library, cell, port, from_port, from_slews, to_port, rf, min_max); delaysInvalidFrom(port); } void Sta::setDriveResistance(const Port *port, const RiseFallBoth *rf, const MinMaxAll *min_max, float res) { sdc_->setDriveResistance(port, rf, min_max, res); delaysInvalidFrom(port); } void Sta::setLatchBorrowLimit(const Pin *pin, float limit) { sdc_->setLatchBorrowLimit(pin, limit); search_->requiredInvalid(pin); } void Sta::setLatchBorrowLimit(const Instance *inst, float limit) { sdc_->setLatchBorrowLimit(inst, limit); search_->requiredInvalid(inst); } void Sta::setLatchBorrowLimit(const Clock *clk, float limit) { sdc_->setLatchBorrowLimit(clk, limit); search_->arrivalsInvalid(); } void Sta::setMinPulseWidth(const RiseFallBoth *rf, float min_width) { sdc_->setMinPulseWidth(rf, min_width); } void Sta::setMinPulseWidth(const Pin *pin, const RiseFallBoth *rf, float min_width) { sdc_->setMinPulseWidth(pin, rf, min_width); } void Sta::setMinPulseWidth(const Instance *inst, const RiseFallBoth *rf, float min_width) { sdc_->setMinPulseWidth(inst, rf, min_width); } void Sta::setMinPulseWidth(const Clock *clk, const RiseFallBoth *rf, float min_width) { sdc_->setMinPulseWidth(clk, rf, min_width); } void Sta::setWireloadMode(WireloadMode mode) { sdc_->setWireloadMode(mode); delaysInvalid(); } void Sta::setWireload(Wireload *wireload, const MinMaxAll *min_max) { sdc_->setWireload(wireload, min_max); delaysInvalid(); } void Sta::setWireloadSelection(WireloadSelection *selection, const MinMaxAll *min_max) { sdc_->setWireloadSelection(selection, min_max); delaysInvalid(); } void Sta::setSlewLimit(Clock *clk, const RiseFallBoth *rf, const PathClkOrData clk_data, const MinMax *min_max, float slew) { sdc_->setSlewLimit(clk, rf, clk_data, min_max, slew); } void Sta::setSlewLimit(Port *port, const MinMax *min_max, float slew) { sdc_->setSlewLimit(port, min_max, slew); } void Sta::setSlewLimit(Cell *cell, const MinMax *min_max, float slew) { sdc_->setSlewLimit(cell, min_max, slew); } void Sta::setCapacitanceLimit(Cell *cell, const MinMax *min_max, float cap) { sdc_->setCapacitanceLimit(cell, min_max, cap); } void Sta::setCapacitanceLimit(Port *port, const MinMax *min_max, float cap) { sdc_->setCapacitanceLimit(port, min_max, cap); } void Sta::setCapacitanceLimit(Pin *pin, const MinMax *min_max, float cap) { sdc_->setCapacitanceLimit(pin, min_max, cap); } void Sta::setFanoutLimit(Cell *cell, const MinMax *min_max, float fanout) { sdc_->setFanoutLimit(cell, min_max, fanout); } void Sta::setFanoutLimit(Port *port, const MinMax *min_max, float fanout) { sdc_->setFanoutLimit(port, min_max, fanout); } void Sta::setMaxArea(float area) { sdc_->setMaxArea(area); } void Sta::makeClock(const char *name, PinSet *pins, bool add_to_pins, float period, FloatSeq *waveform, char *comment) { sdc_->makeClock(name, pins, add_to_pins, period, waveform, comment); update_genclks_ = true; search_->arrivalsInvalid(); } void Sta::makeGeneratedClock(const char *name, PinSet *pins, bool add_to_pins, Pin *src_pin, Clock *master_clk, int divide_by, int multiply_by, float duty_cycle, bool invert, bool combinational, IntSeq *edges, FloatSeq *edge_shifts, char *comment) { sdc_->makeGeneratedClock(name, pins, add_to_pins, src_pin, master_clk, divide_by, multiply_by, duty_cycle, invert, combinational, edges, edge_shifts, comment); update_genclks_ = true; search_->arrivalsInvalid(); } void Sta::removeClock(Clock *clk) { sdc_->removeClock(clk); search_->arrivalsInvalid(); } bool Sta::isClockSrc(const Pin *pin) const { return sdc_->isClock(pin); } void Sta::setPropagatedClock(Clock *clk) { sdc_->setPropagatedClock(clk); delaysInvalid(); clkPinsInvalid(); } void Sta::removePropagatedClock(Clock *clk) { sdc_->removePropagatedClock(clk); delaysInvalid(); clkPinsInvalid(); } void Sta::setPropagatedClock(Pin *pin) { sdc_->setPropagatedClock(pin); delaysInvalid(); clkPinsInvalid(); } void Sta::removePropagatedClock(Pin *pin) { sdc_->removePropagatedClock(pin); delaysInvalid(); clkPinsInvalid(); } void Sta::setClockSlew(Clock *clk, const RiseFallBoth *rf, const MinMaxAll *min_max, float slew) { sdc_->setClockSlew(clk, rf, min_max, slew); clockSlewChanged(clk); } void Sta::removeClockSlew(Clock *clk) { sdc_->removeClockSlew(clk); clockSlewChanged(clk); } void Sta::clockSlewChanged(Clock *clk) { for (const Pin *pin : clk->pins()) graph_delay_calc_->delayInvalid(pin); search_->arrivalsInvalid(); } void Sta::setClockLatency(Clock *clk, Pin *pin, const RiseFallBoth *rf, const MinMaxAll *min_max, float delay) { sdcChangedGraph(); sdc_->setClockLatency(clk, pin, rf, min_max, delay); search_->arrivalsInvalid(); } void Sta::sdcChangedGraph() { if (graph_sdc_annotated_) sdc_->removeGraphAnnotations(); graph_sdc_annotated_ = false; } void Sta::ensureGraphSdcAnnotated() { if (!graph_sdc_annotated_) { sdc_->annotateGraph(); graph_sdc_annotated_ = true; } } void Sta::removeClockLatency(const Clock *clk, const Pin *pin) { sdcChangedGraph(); sdc_->removeClockLatency(clk, pin); search_->arrivalsInvalid(); } void Sta::setClockInsertion(const Clock *clk, const Pin *pin, const RiseFallBoth *rf, const MinMaxAll *min_max, const EarlyLateAll *early_late, float delay) { sdc_->setClockInsertion(clk, pin, rf, min_max, early_late, delay); search_->arrivalsInvalid(); } void Sta::removeClockInsertion(const Clock *clk, const Pin *pin) { sdc_->removeClockInsertion(clk, pin); search_->arrivalsInvalid(); } void Sta::setClockUncertainty(Clock *clk, const SetupHoldAll *setup_hold, float uncertainty) { clk->setUncertainty(setup_hold, uncertainty); search_->arrivalsInvalid(); } void Sta::removeClockUncertainty(Clock *clk, const SetupHoldAll *setup_hold) { clk->removeUncertainty(setup_hold); search_->arrivalsInvalid(); } void Sta::setClockUncertainty(Pin *pin, const SetupHoldAll *setup_hold, float uncertainty) { sdc_->setClockUncertainty(pin, setup_hold, uncertainty); search_->arrivalsInvalid(); } void Sta::removeClockUncertainty(Pin *pin, const SetupHoldAll *setup_hold) { sdc_->removeClockUncertainty(pin, setup_hold); search_->arrivalsInvalid(); } void Sta::setClockUncertainty(Clock *from_clk, const RiseFallBoth *from_rf, Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold, float uncertainty) { sdc_->setClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold, uncertainty); search_->arrivalsInvalid(); } void Sta::removeClockUncertainty(Clock *from_clk, const RiseFallBoth *from_rf, Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold) { sdc_->removeClockUncertainty(from_clk, from_rf, to_clk, to_rf, setup_hold); search_->arrivalsInvalid(); } ClockGroups * Sta::makeClockGroups(const char *name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, const char *comment) { ClockGroups *groups = sdc_->makeClockGroups(name, logically_exclusive, physically_exclusive, asynchronous, allow_paths, comment); search_->requiredsInvalid(); return groups; } void Sta::removeClockGroupsLogicallyExclusive(const char *name) { sdc_->removeClockGroupsLogicallyExclusive(name); search_->requiredsInvalid(); } void Sta::removeClockGroupsPhysicallyExclusive(const char *name) { sdc_->removeClockGroupsPhysicallyExclusive(name); search_->requiredsInvalid(); } void Sta::removeClockGroupsAsynchronous(const char *name) { sdc_->removeClockGroupsAsynchronous(name); search_->requiredsInvalid(); } void Sta::makeClockGroup(ClockGroups *clk_groups, ClockSet *clks) { sdc_->makeClockGroup(clk_groups, clks); } void Sta::setClockSense(PinSet *pins, ClockSet *clks, ClockSense sense) { sdc_->setClockSense(pins, clks, sense); search_->arrivalsInvalid(); } //////////////////////////////////////////////////////////////// void Sta::setClockGatingCheck(const RiseFallBoth *rf, const SetupHold *setup_hold, float margin) { sdc_->setClockGatingCheck(rf, setup_hold, margin); search_->arrivalsInvalid(); } void Sta::setClockGatingCheck(Clock *clk, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin) { sdc_->setClockGatingCheck(clk, rf, setup_hold, margin); search_->arrivalsInvalid(); } void Sta::setClockGatingCheck(Instance *inst, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin, LogicValue active_value) { sdc_->setClockGatingCheck(inst, rf, setup_hold, margin,active_value); search_->arrivalsInvalid(); } void Sta::setClockGatingCheck(Pin *pin, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin, LogicValue active_value) { sdc_->setClockGatingCheck(pin, rf, setup_hold, margin,active_value); search_->arrivalsInvalid(); } void Sta::setDataCheck(Pin *from, const RiseFallBoth *from_rf, Pin *to, const RiseFallBoth *to_rf, Clock *clk, const SetupHoldAll *setup_hold, float margin) { sdcChangedGraph(); sdc_->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold,margin); search_->requiredInvalid(to); } void Sta::removeDataCheck(Pin *from, const RiseFallBoth *from_rf, Pin *to, const RiseFallBoth *to_rf, Clock *clk, const SetupHoldAll *setup_hold) { sdc_->removeDataCheck(from, from_rf, to, to_rf, clk, setup_hold); search_->requiredInvalid(to); } //////////////////////////////////////////////////////////////// void Sta::disable(Pin *pin) { sdcChangedGraph(); sdc_->disable(pin); // Levelization respects disabled edges. levelize_->invalid(); graph_delay_calc_->delayInvalid(pin); search_->arrivalsInvalid(); } void Sta::removeDisable(Pin *pin) { sdcChangedGraph(); sdc_->removeDisable(pin); disableAfter(); // Levelization respects disabled edges. levelize_->invalid(); graph_delay_calc_->delayInvalid(pin); search_->arrivalsInvalid(); } void Sta::disable(Instance *inst, LibertyPort *from, LibertyPort *to) { sdcChangedGraph(); sdc_->disable(inst, from, to); if (from) { Pin *from_pin = network_->findPin(inst, from); graph_delay_calc_->delayInvalid(from_pin); } if (to) { Pin *to_pin = network_->findPin(inst, to); graph_delay_calc_->delayInvalid(to_pin); } if (from == nullptr && to == nullptr) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); graph_delay_calc_->delayInvalid(pin); } delete pin_iter; } // Levelization respects disabled edges. levelize_->invalid(); search_->arrivalsInvalid(); } void Sta::removeDisable(Instance *inst, LibertyPort *from, LibertyPort *to) { sdcChangedGraph(); sdc_->removeDisable(inst, from, to); if (from) { Pin *from_pin = network_->findPin(inst, from); graph_delay_calc_->delayInvalid(from_pin); } if (to) { Pin *to_pin = network_->findPin(inst, to); graph_delay_calc_->delayInvalid(to_pin); } if (from == nullptr && to == nullptr) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); graph_delay_calc_->delayInvalid(pin); } delete pin_iter; } // Levelization respects disabled edges. levelize_->invalid(); search_->arrivalsInvalid(); } void Sta::disable(LibertyCell *cell, LibertyPort *from, LibertyPort *to) { sdc_->disable(cell, from, to); disableAfter(); } void Sta::removeDisable(LibertyCell *cell, LibertyPort *from, LibertyPort *to) { sdc_->removeDisable(cell, from, to); disableAfter(); } void Sta::disable(LibertyPort *port) { sdcChangedGraph(); sdc_->disable(port); disableAfter(); } void Sta::removeDisable(LibertyPort *port) { sdcChangedGraph(); sdc_->removeDisable(port); disableAfter(); } void Sta::disable(Port *port) { sdc_->disable(port); disableAfter(); } void Sta::removeDisable(Port *port) { sdc_->removeDisable(port); disableAfter(); } void Sta::disable(Edge *edge) { sdc_->disable(edge); disableAfter(); } void Sta::removeDisable(Edge *edge) { sdc_->removeDisable(edge); disableAfter(); } void Sta::disable(TimingArcSet *arc_set) { sdc_->disable(arc_set); disableAfter(); } void Sta::removeDisable(TimingArcSet *arc_set) { sdc_->removeDisable(arc_set); disableAfter(); } void Sta::disableAfter() { // Levelization respects disabled edges. levelize_->invalid(); delaysInvalid(); } //////////////////////////////////////////////////////////////// EdgeSeq Sta::disabledEdges() { ensureLevelized(); EdgeSeq disabled_edges; VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (isDisabledConstant(edge) || isDisabledCondDefault(edge) || isDisabledConstraint(edge) || edge->isDisabledLoop() || isDisabledPresetClr(edge)) disabled_edges.push_back(edge); } } return disabled_edges; } EdgeSeq Sta::disabledEdgesSorted() { EdgeSeq disabled_edges = disabledEdges(); sortEdges(&disabled_edges, network_, graph_); return disabled_edges; } bool Sta::isDisabledConstraint(Edge *edge) { Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); const Instance *inst = network_->instance(from_pin); TimingArcSet *arc_set = edge->timingArcSet(); return sdc_->isDisabled(from_pin) || sdc_->isDisabled(to_pin) || sdc_->isDisabled(inst, from_pin, to_pin, edge->role()) || sdc_->isDisabled(edge) || sdc_->isDisabled(arc_set); } bool Sta::isDisabledConstant(Edge *edge) { sim_->ensureConstantsPropagated(); const TimingRole *role = edge->role(); Vertex *from_vertex = edge->from(graph_); Pin *from_pin = from_vertex->pin(); Vertex *to_vertex = edge->to(graph_); Pin *to_pin = to_vertex->pin(); const Instance *inst = network_->instance(from_pin); return sim_->logicZeroOne(from_vertex) || sim_->logicZeroOne(to_vertex) || (!role->isWire() && (isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_) || isModeDisabled(edge, inst, network_, sim_) || hasDisabledArcs(edge, graph_))); } static bool hasDisabledArcs(Edge *edge, Graph *graph) { TimingArcSet *arc_set = edge->timingArcSet(); for (TimingArc *arc : arc_set->arcs()) { if (!searchThru(edge, arc, graph)) return true; } return false; } bool Sta::isDisabledLoop(Edge *edge) const { return levelize_->isDisabledLoop(edge); } PinSet Sta::disabledConstantPins(Edge *edge) { sim_->ensureConstantsPropagated(); PinSet pins(network_); Vertex *from_vertex = edge->from(graph_); Pin *from_pin = from_vertex->pin(); Vertex *to_vertex = edge->to(graph_); const Pin *to_pin = to_vertex->pin(); if (sim_->logicZeroOne(from_vertex)) pins.insert(from_pin); if (edge->role()->isWire()) { if (sim_->logicZeroOne(to_vertex)) pins.insert(to_pin); } else { const Instance *inst = network_->instance(to_pin); bool is_disabled; FuncExpr *disable_cond; isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_, is_disabled, disable_cond); if (is_disabled) exprConstantPins(disable_cond, inst, pins); isModeDisabled(edge, inst, network_, sim_, is_disabled, disable_cond); if (is_disabled) exprConstantPins(disable_cond, inst, pins); if (hasDisabledArcs(edge, graph_)) { LibertyPort *to_port = network_->libertyPort(to_pin); if (to_port) { FuncExpr *func = to_port->function(); if (func && sim_->functionSense(inst, from_pin, to_pin) != edge->sense()) exprConstantPins(func, inst, pins); } } } return pins; } TimingSense Sta::simTimingSense(Edge *edge) { Pin *from_pin = edge->from(graph_)->pin(); Pin *to_pin = edge->to(graph_)->pin(); Instance *inst = network_->instance(from_pin); return sim_->functionSense(inst, from_pin, to_pin); } void Sta::exprConstantPins(FuncExpr *expr, const Instance *inst, PinSet &pins) { FuncExprPortIterator port_iter(expr); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); Pin *pin = network_->findPin(inst, port); if (pin) { LogicValue value = sim_->logicValue(pin); if (value != LogicValue::unknown) pins.insert(pin); } } } bool Sta::isDisabledBidirectInstPath(Edge *edge) const { return !variables_->bidirectInstPathsEnabled() && edge->isBidirectInstPath(); } bool Sta::isDisabledBidirectNetPath(Edge *edge) const { return !variables_->bidirectNetPathsEnabled() && edge->isBidirectNetPath(); } bool Sta::isDisabledPresetClr(Edge *edge) const { return !variables_->presetClrArcsEnabled() && edge->role() == TimingRole::regSetClr(); } void Sta::disableClockGatingCheck(Instance *inst) { sdc_->disableClockGatingCheck(inst); search_->endpointsInvalid(); } void Sta::disableClockGatingCheck(Pin *pin) { sdc_->disableClockGatingCheck(pin); search_->endpointsInvalid(); } void Sta::removeDisableClockGatingCheck(Instance *inst) { sdc_->removeDisableClockGatingCheck(inst); search_->endpointsInvalid(); } void Sta::removeDisableClockGatingCheck(Pin *pin) { sdc_->removeDisableClockGatingCheck(pin); search_->endpointsInvalid(); } void Sta::setLogicValue(Pin *pin, LogicValue value) { sdc_->setLogicValue(pin, value); // Levelization respects constant disabled edges. levelize_->invalid(); sim_->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin // fails. This could be more incremental if the graph delay // calculator searched thru disabled edges but ignored their // results. delaysInvalid(); } void Sta::setCaseAnalysis(Pin *pin, LogicValue value) { sdc_->setCaseAnalysis(pin, value); // Levelization respects constant disabled edges. levelize_->invalid(); sim_->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin // fails. This could be handled incrementally by invalidating delays // on the output of gates one level downstream. delaysInvalid(); } void Sta::removeCaseAnalysis(Pin *pin) { sdc_->removeCaseAnalysis(pin); // Levelization respects constant disabled edges. levelize_->invalid(); sim_->constantsInvalid(); // Constants disable edges which isolate downstream vertices of the // graph from the delay calculator's BFS search. This means that // simply invaldating the delays downstream from the constant pin // fails. This could be handled incrementally by invalidating delays // on the output of gates one level downstream. delaysInvalid(); } void Sta::setInputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const Pin *ref_pin, bool source_latency_included, bool network_latency_included, const MinMaxAll *min_max, bool add, float delay) { sdc_->setInputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included, network_latency_included, min_max, add, delay); search_->arrivalInvalid(pin); } void Sta::removeInputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const MinMaxAll *min_max) { sdc_->removeInputDelay(pin, rf, clk, clk_rf, min_max); search_->arrivalInvalid(pin); } void Sta::setOutputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const Pin *ref_pin, bool source_latency_included, bool network_latency_included, const MinMaxAll *min_max, bool add, float delay) { sdc_->setOutputDelay(pin, rf, clk, clk_rf, ref_pin, source_latency_included,network_latency_included, min_max, add, delay); sdcChangedGraph(); search_->requiredInvalid(pin); } void Sta::removeOutputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const MinMaxAll *min_max) { sdc_->removeOutputDelay(pin, rf, clk, clk_rf, min_max); sdcChangedGraph(); search_->arrivalInvalid(pin); } void Sta::makeFalsePath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, const char *comment) { sdc_->makeFalsePath(from, thrus, to, min_max, comment); search_->arrivalsInvalid(); } void Sta::makeMulticyclePath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max, bool use_end_clk, int path_multiplier, const char *comment) { sdc_->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, path_multiplier, comment); search_->arrivalsInvalid(); } void Sta::makePathDelay(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMax *min_max, bool ignore_clk_latency, bool break_path, float delay, const char *comment) { sdc_->makePathDelay(from, thrus, to, min_max, ignore_clk_latency, break_path, delay, comment); search_->endpointsInvalid(); search_->arrivalsInvalid(); } void Sta::resetPath(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const MinMaxAll *min_max) { sdc_->resetPath(from, thrus, to, min_max); search_->arrivalsInvalid(); } void Sta::makeGroupPath(const char *name, bool is_default, ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, const char *comment) { sdc_->makeGroupPath(name, is_default, from, thrus, to, comment); search_->arrivalsInvalid(); } bool Sta::isGroupPathName(const char *group_name) { return isPathGroupName(group_name); } bool Sta::isPathGroupName(const char *group_name) const { return sdc_->findClock(group_name) || sdc_->isGroupPathName(group_name) || stringEq(group_name, PathGroups::asyncPathGroupName()) || stringEq(group_name, PathGroups::pathDelayGroupName()) || stringEq(group_name, PathGroups::gatedClkGroupName()) || stringEq(group_name, PathGroups::unconstrainedGroupName()); } StdStringSeq Sta::pathGroupNames() const { StdStringSeq names; for (const Clock *clk : *sdc_->clocks()) names.push_back(clk->name()); for (auto const &[name, group] : sdc_->groupPaths()) names.push_back(name); names.push_back(PathGroups::asyncPathGroupName()); names.push_back(PathGroups::pathDelayGroupName()); names.push_back(PathGroups::gatedClkGroupName()); names.push_back(PathGroups::unconstrainedGroupName()); return names; } ExceptionFrom * Sta::makeExceptionFrom(PinSet *from_pins, ClockSet *from_clks, InstanceSet *from_insts, const RiseFallBoth *from_rf) { return sdc_->makeExceptionFrom(from_pins, from_clks, from_insts, from_rf); } void Sta::checkExceptionFromPins(ExceptionFrom *from, const char *file, int line) const { if (from) { PinSet::ConstIterator pin_iter(from->pins()); while (pin_iter.hasNext()) { const Pin *pin = pin_iter.next(); if (!sdc_->isExceptionStartpoint(pin)) { if (line) report_->fileWarn(1554, file, line, "'%s' is not a valid start point.", cmd_network_->pathName(pin)); else report_->warn(1550, "'%s' is not a valid start point.", cmd_network_->pathName(pin)); } } } } void Sta::deleteExceptionFrom(ExceptionFrom *from) { delete from; } ExceptionThru * Sta::makeExceptionThru(PinSet *pins, NetSet *nets, InstanceSet *insts, const RiseFallBoth *rf) { return sdc_->makeExceptionThru(pins, nets, insts, rf); } void Sta::deleteExceptionThru(ExceptionThru *thru) { delete thru; } ExceptionTo * Sta::makeExceptionTo(PinSet *to_pins, ClockSet *to_clks, InstanceSet *to_insts, const RiseFallBoth *rf, const RiseFallBoth *end_rf) { return sdc_->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf); } void Sta::deleteExceptionTo(ExceptionTo *to) { delete to; } void Sta::checkExceptionToPins(ExceptionTo *to, const char *file, int line) const { if (to) { PinSet::Iterator pin_iter(to->pins()); while (pin_iter.hasNext()) { const Pin *pin = pin_iter.next(); if (!sdc_->isExceptionEndpoint(pin)) { if (line) report_->fileWarn(1551, file, line, "'%s' is not a valid endpoint.", cmd_network_->pathName(pin)); else report_->warn(1552, "'%s' is not a valid endpoint.", cmd_network_->pathName(pin)); } } } } void Sta::removeConstraints() { levelize_->invalid(); graph_delay_calc_->clear(); search_->clear(); sim_->constantsInvalid(); if (graph_) sdc_->removeGraphAnnotations(); sdc_->clear(); clk_network_->clear(); } void Sta::constraintsChanged() { levelize_->invalid(); delaysInvalid(); sim_->constantsInvalid(); } void Sta::writeSdc(const char *filename, bool leaf, bool native, int digits, bool gzip, bool no_timestamp) { ensureLibLinked(); sta::writeSdc(network_->topInstance(), filename, "write_sdc", leaf, native, digits, gzip, no_timestamp, sdc_); } //////////////////////////////////////////////////////////////// CheckErrorSeq & Sta::checkTiming(bool no_input_delay, bool no_output_delay, bool reg_multiple_clks, bool reg_no_clks, bool unconstrained_endpoints, bool loops, bool generated_clks) { searchPreamble(); if (unconstrained_endpoints) // Only need non-clock arrivals for unconstrained_endpoints. search_->findAllArrivals(); else search_->findClkArrivals(); if (check_timing_ == nullptr) makeCheckTiming(); return check_timing_->check(no_input_delay, no_output_delay, reg_multiple_clks, reg_no_clks, unconstrained_endpoints, loops, generated_clks); } //////////////////////////////////////////////////////////////// bool Sta::crprEnabled() const { return variables_->crprEnabled(); } void Sta::setCrprEnabled(bool enabled) { // Pessimism is only relevant for on_chip_variation analysis. if (sdc_->analysisType() == AnalysisType::ocv && enabled != variables_->crprEnabled()) search_->arrivalsInvalid(); variables_->setCrprEnabled(enabled); } CrprMode Sta::crprMode() const { return variables_->crprMode(); } void Sta::setCrprMode(CrprMode mode) { // Pessimism is only relevant for on_chip_variation analysis. if (sdc_->analysisType() == AnalysisType::ocv && variables_->crprEnabled() && variables_->crprMode() != mode) search_->arrivalsInvalid(); variables_->setCrprMode(mode); } bool Sta::pocvEnabled() const { return variables_->pocvEnabled(); } void Sta::setPocvEnabled(bool enabled) { if (enabled != variables_->pocvEnabled()) delaysInvalid(); variables_->setPocvEnabled(enabled); } void Sta::setSigmaFactor(float factor) { if (!fuzzyEqual(factor, sigma_factor_)) { sigma_factor_ = factor; search_->arrivalsInvalid(); updateComponentsState(); } } bool Sta::propagateGatedClockEnable() const { return variables_->propagateGatedClockEnable(); } void Sta::setPropagateGatedClockEnable(bool enable) { if (variables_->propagateGatedClockEnable() != enable) search_->arrivalsInvalid(); variables_->setPropagateGatedClockEnable(enable); } bool Sta::presetClrArcsEnabled() const { return variables_->presetClrArcsEnabled(); } void Sta::setPresetClrArcsEnabled(bool enable) { if (variables_->presetClrArcsEnabled() != enable) { levelize_->invalid(); delaysInvalid(); } variables_->setPresetClrArcsEnabled(enable); } bool Sta::condDefaultArcsEnabled() const { return variables_->condDefaultArcsEnabled(); } void Sta::setCondDefaultArcsEnabled(bool enabled) { if (variables_->condDefaultArcsEnabled() != enabled) { delaysInvalid(); variables_->setCondDefaultArcsEnabled(enabled); } } bool Sta::bidirectInstPathsEnabled() const { return variables_->bidirectInstPathsEnabled(); } void Sta::setBidirectInstPathsEnabled(bool enabled) { if (variables_->bidirectInstPathsEnabled() != enabled) { levelize_->invalid(); delaysInvalid(); variables_->setBidirectInstPathsEnabled(enabled); } } bool Sta::bidirectNetPathsEnabled() const { return variables_->bidirectNetPathsEnabled(); } void Sta::setBidirectNetPathsEnabled(bool enabled) { if (variables_->bidirectNetPathsEnabled() != enabled) { delaysInvalid(); variables_->setBidirectNetPathsEnabled(enabled); } } bool Sta::recoveryRemovalChecksEnabled() const { return variables_->recoveryRemovalChecksEnabled(); } void Sta::setRecoveryRemovalChecksEnabled(bool enabled) { if (variables_->recoveryRemovalChecksEnabled() != enabled) { search_->arrivalsInvalid(); variables_->setRecoveryRemovalChecksEnabled(enabled); } } bool Sta::gatedClkChecksEnabled() const { return variables_->gatedClkChecksEnabled(); } void Sta::setGatedClkChecksEnabled(bool enabled) { if (variables_->gatedClkChecksEnabled() != enabled) { search_->arrivalsInvalid(); variables_->setGatedClkChecksEnabled(enabled); } } bool Sta::dynamicLoopBreaking() const { return variables_->dynamicLoopBreaking(); } void Sta::setDynamicLoopBreaking(bool enable) { if (variables_->dynamicLoopBreaking() != enable) { if (levelize_->levelized()) { if (enable) sdc_->makeLoopExceptions(); else sdc_->deleteLoopExceptions(); } search_->arrivalsInvalid(); variables_->setDynamicLoopBreaking(enable); } } bool Sta::useDefaultArrivalClock() const { return variables_->useDefaultArrivalClock(); } void Sta::setUseDefaultArrivalClock(bool enable) { if (variables_->useDefaultArrivalClock() != enable) { variables_->setUseDefaultArrivalClock(enable); search_->arrivalsInvalid(); } } bool Sta::propagateAllClocks() const { return variables_->propagateAllClocks(); } void Sta::setPropagateAllClocks(bool prop) { variables_->setPropagateAllClocks(prop); } bool Sta::clkThruTristateEnabled() const { return variables_->clkThruTristateEnabled(); } void Sta::setClkThruTristateEnabled(bool enable) { if (enable != variables_->clkThruTristateEnabled()) { search_->arrivalsInvalid(); variables_->setClkThruTristateEnabled(enable); } } //////////////////////////////////////////////////////////////// Corner * Sta::findCorner(const char *corner_name) { return corners_->findCorner(corner_name); } bool Sta::multiCorner() { return corners_->multiCorner(); } // Init one corner named "default". void Sta::makeCorners() { corners_ = new Corners(this); StringSet corner_names; corner_names.insert("default"); corners_->makeCorners(&corner_names); cmd_corner_ = corners_->findCorner(0); sdc_->makeCornersAfter(corners_); } void Sta::makeCorners(StringSet *corner_names) { if (corner_names->size() > corner_count_max) report_->error(1553, "maximum corner count exceeded"); sdc_->makeCornersBefore(); parasitics_->deleteParasitics(); corners_->makeCorners(corner_names); makeParasiticAnalysisPts(); cmd_corner_ = corners_->findCorner(0); updateComponentsState(); sdc_->makeCornersAfter(corners_); } Corner * Sta::cmdCorner() const { return cmd_corner_; } void Sta::setCmdCorner(Corner *corner) { cmd_corner_ = corner; } //////////////////////////////////////////////////////////////// // from/thrus/to are owned and deleted by Search. // Returned sequence is owned by the caller. // PathEnds are owned by Search PathGroups and deleted on next call. PathEndSeq Sta::findPathEnds(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to, bool unconstrained, const Corner *corner, const MinMaxAll *min_max, int group_path_count, int endpoint_path_count, bool unique_pins, bool unique_edges, float slack_min, float slack_max, bool sort_by_slack, PathGroupNameSet *group_names, bool setup, bool hold, bool recovery, bool removal, bool clk_gating_setup, bool clk_gating_hold) { searchPreamble(); return search_->findPathEnds(from, thrus, to, unconstrained, corner, min_max, group_path_count, endpoint_path_count, unique_pins, unique_edges, slack_min, slack_max, sort_by_slack, group_names, setup, hold, recovery, removal, clk_gating_setup, clk_gating_hold); } //////////////////////////////////////////////////////////////// // Overall flow: // make graph // propagate constants // levelize // delay calculation // update generated clocks // find arrivals void Sta::searchPreamble() { findDelays(); updateGeneratedClks(); sdc_->searchPreamble(); // Delete results from last findPathEnds because they point to filtered arrivals. search_->deletePathGroups(); search_->deleteFilteredArrivals(); } void Sta::setReportPathFormat(ReportPathFormat format) { report_path_->setPathFormat(format); } void Sta::setReportPathFieldOrder(StringSeq *field_names) { report_path_->setReportFieldOrder(field_names); } void Sta::setReportPathFields(bool report_input_pin, bool report_hier_pins, bool report_net, bool report_cap, bool report_slew, bool report_fanout, bool report_src_attr) { report_path_->setReportFields(report_input_pin, report_hier_pins, report_net, report_cap, report_slew, report_fanout, report_src_attr); } ReportField * Sta::findReportPathField(const char *name) { return report_path_->findField(name); } void Sta::setReportPathDigits(int digits) { report_path_->setDigits(digits); } void Sta::setReportPathNoSplit(bool no_split) { report_path_->setNoSplit(no_split); } void Sta::setReportPathSigmas(bool report_sigmas) { report_path_->setReportSigmas(report_sigmas); } void Sta::reportPathEndHeader() { report_path_->reportPathEndHeader(); } void Sta::reportPathEndFooter() { report_path_->reportPathEndFooter(); } void Sta::reportPathEnd(PathEnd *end) { report_path_->reportPathEnd(end); } void Sta::reportPathEnd(PathEnd *end, PathEnd *prev_end, bool last) { report_path_->reportPathEnd(end, prev_end, last); } void Sta::reportPathEnds(PathEndSeq *ends) { report_path_->reportPathEnds(ends); } void Sta::reportPath(const Path *path) { report_path_->reportPath(path); } void Sta::updateTiming(bool full) { searchPreamble(); if (full) search_->arrivalsInvalid(); search_->findAllArrivals(); } //////////////////////////////////////////////////////////////// void Sta::reportClkSkew(ConstClockSeq &clks, const Corner *corner, const SetupHold *setup_hold, bool include_internal_latency, int digits) { clkSkewPreamble(); clk_skews_->reportClkSkew(clks, corner, setup_hold, include_internal_latency, digits); } float Sta::findWorstClkSkew(const SetupHold *setup_hold, bool include_internal_latency) { clkSkewPreamble(); return clk_skews_->findWorstClkSkew(cmd_corner_, setup_hold, include_internal_latency); } void Sta::clkSkewPreamble() { ensureClkArrivals(); if (clk_skews_ == nullptr) clk_skews_ = new ClkSkews(this); } //////////////////////////////////////////////////////////////// void Sta::reportClkLatency(ConstClockSeq &clks, const Corner *corner, bool include_internal_latency, int digits) { ensureClkArrivals(); ClkLatency clk_latency(this); clk_latency.reportClkLatency(clks, corner, include_internal_latency, digits); } ClkDelays Sta::findClkDelays(const Clock *clk, bool include_internal_latency) { ensureClkArrivals(); ClkLatency clk_latency(this); return clk_latency.findClkDelays(clk, nullptr, include_internal_latency); } //////////////////////////////////////////////////////////////// void Sta::delaysInvalid() const { graph_delay_calc_->delaysInvalid(); search_->arrivalsInvalid(); } void Sta::arrivalsInvalid() { search_->arrivalsInvalid(); } void Sta::ensureClkArrivals() { searchPreamble(); search_->findClkArrivals(); } //////////////////////////////////////////////////////////////// PinSet Sta::startpointPins() { ensureGraph(); PinSet pins(network_); VertexPinCollector visitor(pins); search_->visitStartpoints(&visitor); return pins; } VertexSet * Sta::endpoints() { ensureGraph(); return search_->endpoints(); } PinSet Sta::endpointPins() { ensureGraph(); PinSet pins(network_); for (Vertex *vertex : *search_->endpoints()) pins.insert(vertex->pin()); return pins; } int Sta::endpointViolationCount(const MinMax *min_max) { int violations = 0; for (Vertex *end : *search_->endpoints()) { if (delayLess(vertexSlack(end, min_max), 0.0, this)) violations++; } return violations; } PinSet Sta::findGroupPathPins(const char *group_path_name) { if (!(search_->havePathGroups() && search_->arrivalsValid())) { PathEndSeq path_ends = findPathEnds(// from, thrus, to, unconstrained nullptr, nullptr, nullptr, false, // corner, min_max, nullptr, MinMaxAll::max(), // group_path_count, endpoint_path_count 1, 1, // unique_pins, unique_edges true, true, -INF, INF, // slack_min, slack_max, false, // sort_by_slack nullptr, // group_names // setup, hold, recovery, removal, true, true, true, true, // clk_gating_setup, clk_gating_hold true, true); } PathGroup *path_group = search_->findPathGroup(group_path_name, MinMax::max()); PinSet pins(network_); VertexPinCollector visitor(pins); visitPathGroupVertices(path_group, &visitor, this); return pins; } //////////////////////////////////////////////////////////////// void Sta::findRequireds() { searchPreamble(); search_->findAllArrivals(); search_->findRequireds(); } //////////////////////////////////////////////////////////////// VertexPathIterator * Sta::vertexPathIterator(Vertex *vertex, const RiseFall *rf, const PathAnalysisPt *path_ap) { return new VertexPathIterator(vertex, rf, path_ap, this); } VertexPathIterator * Sta::vertexPathIterator(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { return new VertexPathIterator(vertex, rf, min_max, this); } Path * Sta::vertexWorstArrivalPath(Vertex *vertex, const MinMax *min_max) { return vertexWorstArrivalPath(vertex, nullptr, min_max); } Path * Sta::vertexWorstArrivalPath(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { Path *worst_path = nullptr; Arrival worst_arrival = min_max->initValue(); VertexPathIterator path_iter(vertex, rf, min_max, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); Arrival arrival = path->arrival(); if (!path->tag(this)->isGenClkSrcPath() && delayGreater(arrival, worst_arrival, min_max, this)) { worst_arrival = arrival; worst_path = path; } } return worst_path; } Path * Sta::vertexWorstRequiredPath(Vertex *vertex, const MinMax *min_max) { return vertexWorstRequiredPath(vertex, nullptr, min_max); } Path * Sta::vertexWorstRequiredPath(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { Path *worst_path = nullptr; const MinMax *req_min_max = min_max->opposite(); Arrival worst_req = req_min_max->initValue(); VertexPathIterator path_iter(vertex, rf, min_max, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const Required path_req = path->required(); if (!path->tag(this)->isGenClkSrcPath() && delayGreater(path_req, worst_req, req_min_max, this)) { worst_req = path_req; worst_path = path; } } return worst_path; } Path * Sta::vertexWorstSlackPath(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { Path *worst_path = nullptr; Slack min_slack = MinMax::min()->initValue(); VertexPathIterator path_iter(vertex, rf, min_max, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack slack = path->slack(this); if (!path->tag(this)->isGenClkSrcPath() && delayLess(slack, min_slack, this)) { min_slack = slack; worst_path = path; } } return worst_path; } Path * Sta::vertexWorstSlackPath(Vertex *vertex, const MinMax *min_max) { return vertexWorstSlackPath(vertex, nullptr, min_max); } Arrival Sta::pinArrival(const Pin *pin, const RiseFall *rf, const MinMax *min_max) { Vertex *vertex, *bidirect_vertex; graph_->pinVertices(pin, vertex, bidirect_vertex); Arrival arrival; if (vertex) arrival = vertexArrival(vertex, rf, clk_edge_wildcard, nullptr, min_max); if (bidirect_vertex) { Arrival arrival1 = vertexArrival(bidirect_vertex, rf, clk_edge_wildcard, nullptr, min_max); if (delayLess(arrival1, arrival, this)) arrival = arrival1; } return arrival; } Arrival Sta::vertexArrival(Vertex *vertex, const MinMax *min_max) { return vertexArrival(vertex, nullptr, clk_edge_wildcard, nullptr, min_max); } Arrival Sta::vertexArrival(Vertex *vertex, const RiseFall *rf, const PathAnalysisPt *path_ap) { return vertexArrival(vertex, rf, clk_edge_wildcard, path_ap, nullptr); } Arrival Sta::vertexArrival(Vertex *vertex, const RiseFall *rf, const ClockEdge *clk_edge, const PathAnalysisPt *path_ap, const MinMax *min_max) { searchPreamble(); search_->findArrivals(vertex->level()); if (min_max == nullptr) min_max = path_ap->pathMinMax(); Arrival arrival = min_max->initValue(); VertexPathIterator path_iter(vertex, rf, path_ap, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); const Arrival &path_arrival = path->arrival(); const ClkInfo *clk_info = path->clkInfo(search_); if ((clk_edge == clk_edge_wildcard || clk_info->clkEdge() == clk_edge) && !clk_info->isGenClkSrcPath() && delayGreater(path->arrival(), arrival, min_max, this)) arrival = path_arrival; } return arrival; } Required Sta::vertexRequired(Vertex *vertex, const MinMax *min_max) { return vertexRequired(vertex, nullptr, clk_edge_wildcard, nullptr, min_max); } Required Sta::vertexRequired(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { return vertexRequired(vertex, rf, clk_edge_wildcard, nullptr, min_max); } Required Sta::vertexRequired(Vertex *vertex, const RiseFall *rf, const PathAnalysisPt *path_ap) { return vertexRequired(vertex, rf, clk_edge_wildcard, path_ap, nullptr); } Required Sta::vertexRequired(Vertex *vertex, const RiseFall *rf, const ClockEdge *clk_edge, const PathAnalysisPt *path_ap) { return vertexRequired(vertex, rf, clk_edge, path_ap, nullptr); } Required Sta::vertexRequired(Vertex *vertex, const RiseFall *rf, const ClockEdge *clk_edge, const PathAnalysisPt *path_ap, const MinMax *min_max) { findRequired(vertex); const MinMax *req_min_max = min_max ? min_max->opposite() : path_ap->pathMinMax()->opposite(); Required required = req_min_max->initValue(); VertexPathIterator path_iter(vertex, rf, path_ap, min_max, this); while (path_iter.hasNext()) { const Path *path = path_iter.next(); const Required path_required = path->required(); if ((clk_edge == clk_edge_wildcard || path->clkEdge(search_) == clk_edge) && delayGreater(path_required, required, req_min_max, this)) required = path_required; } return required; } Slack Sta::netSlack(const Net *net, const MinMax *min_max) { ensureGraph(); Slack slack = MinMax::min()->initValue(); NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); Slack pin_slack = vertexSlack(vertex, min_max); if (delayLess(pin_slack, slack, this)) slack = pin_slack; } } delete pin_iter; return slack; } Slack Sta::pinSlack(const Pin *pin, const MinMax *min_max) { ensureGraph(); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); Slack slack = MinMax::min()->initValue(); if (vertex) slack = vertexSlack(vertex, min_max); if (bidirect_drvr_vertex) { Slack slack1 = vertexSlack(bidirect_drvr_vertex, min_max); if (delayLess(slack1, slack, this)) slack = slack1; } return slack; } Slack Sta::pinSlack(const Pin *pin, const RiseFall *rf, const MinMax *min_max) { ensureGraph(); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); Slack slack = MinMax::min()->initValue(); if (vertex) slack = vertexSlack(vertex, rf, min_max); if (bidirect_drvr_vertex) { Slack slack1 = vertexSlack(bidirect_drvr_vertex, rf, min_max); if (delayLess(slack1, slack, this)) slack = slack1; } return slack; } //////////////////////////////////////////////////////////////// class EndpointPathEndVisitor : public PathEndVisitor { public: EndpointPathEndVisitor(const std::string &path_group_name, const MinMax *min_max, const StaState *sta); PathEndVisitor *copy() const; void visit(PathEnd *path_end); Slack slack() const { return slack_; } private: const std::string &path_group_name_; const MinMax *min_max_; Slack slack_; const StaState *sta_; }; EndpointPathEndVisitor::EndpointPathEndVisitor(const std::string &path_group_name, const MinMax *min_max, const StaState *sta) : path_group_name_(path_group_name), min_max_(min_max), slack_(MinMax::min()->initValue()), sta_(sta) { } PathEndVisitor * EndpointPathEndVisitor::copy() const { return new EndpointPathEndVisitor(path_group_name_, min_max_, sta_); } void EndpointPathEndVisitor::visit(PathEnd *path_end) { if (path_end->minMax(sta_) == min_max_) { StdStringSeq group_names = PathGroups::pathGroupNames(path_end, sta_); for (std::string &group_name : group_names) { if (group_name == path_group_name_) { Slack end_slack = path_end->slack(sta_); if (delayLess(end_slack, slack_, sta_)) slack_ = end_slack; } } } } Slack Sta::endpointSlack(const Pin *pin, const std::string &path_group_name, const MinMax *min_max) { ensureGraph(); Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { findRequired(vertex); VisitPathEnds visit_ends(this); EndpointPathEndVisitor path_end_visitor(path_group_name, min_max, this); visit_ends.visitPathEnds(vertex, &path_end_visitor); return path_end_visitor.slack(); } else return INF; } //////////////////////////////////////////////////////////////// Slack Sta::vertexSlack(Vertex *vertex, const MinMax *min_max) { findRequired(vertex); Slack slack = MinMax::min()->initValue(); VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); if (path->minMax(this) == min_max) { Slack path_slack = path->slack(this); if (delayLess(path_slack, slack, this)) slack = path_slack; } } return slack; } Slack Sta::vertexSlack(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { findRequired(vertex); Slack slack = MinMax::min()->initValue(); VertexPathIterator path_iter(vertex, rf, min_max, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack path_slack = path->slack(this); if (delayLess(path_slack, slack, this)) slack = path_slack; } return slack; } Slack Sta::vertexSlack(Vertex *vertex, const RiseFall *rf, const PathAnalysisPt *path_ap) { findRequired(vertex); return vertexSlack1(vertex, rf, clk_edge_wildcard, path_ap); } Slack Sta::vertexSlack(Vertex *vertex, const RiseFall *rf, const ClockEdge *clk_edge, const PathAnalysisPt *path_ap) { findRequired(vertex); return vertexSlack1(vertex, rf, clk_edge, path_ap); } Slack Sta::vertexSlack1(Vertex *vertex, const RiseFall *rf, const ClockEdge *clk_edge, const PathAnalysisPt *path_ap) { const MinMax *min = MinMax::min(); Slack slack = min->initValue(); VertexPathIterator path_iter(vertex, rf, path_ap, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack path_slack = path->slack(this); if ((clk_edge == clk_edge_wildcard || path->clkEdge(search_) == clk_edge) && delayLess(path_slack, slack, this)) slack = path_slack; } return slack; } void Sta::vertexSlacks(Vertex *vertex, Slack (&slacks)[RiseFall::index_count][MinMax::index_count]) { findRequired(vertex); for (int rf_index : RiseFall::rangeIndex()) { for (const MinMax *min_max : MinMax::range()) { slacks[rf_index][min_max->index()] = MinMax::min()->initValue(); } } VertexPathIterator path_iter(vertex, this); while (path_iter.hasNext()) { Path *path = path_iter.next(); Slack path_slack = path->slack(this); int rf_index = path->rfIndex(this); int mm_index = path->minMax(this)->index(); if (delayLess(path_slack, slacks[rf_index][mm_index], this)) slacks[rf_index][mm_index] = path_slack; } } //////////////////////////////////////////////////////////////// class MinPeriodEndVisitor : public PathEndVisitor { public: MinPeriodEndVisitor(const Clock *clk, bool include_port_paths, StaState *sta); MinPeriodEndVisitor(const MinPeriodEndVisitor &) = default; virtual PathEndVisitor *copy() const; virtual void visit(PathEnd *path_end); float minPeriod() const { return min_period_; } private: bool pathIsFromInputPort(PathEnd *path_end); const Clock *clk_; bool include_port_paths_; StaState *sta_; float min_period_; }; MinPeriodEndVisitor::MinPeriodEndVisitor(const Clock *clk, bool include_port_paths, StaState *sta) : clk_(clk), include_port_paths_(include_port_paths), sta_(sta), min_period_(0) { } PathEndVisitor * MinPeriodEndVisitor::copy() const { return new MinPeriodEndVisitor(*this); } void MinPeriodEndVisitor::visit(PathEnd *path_end) { Network *network = sta_->network(); Path *path = path_end->path(); const ClockEdge *src_edge = path_end->sourceClkEdge(sta_); const ClockEdge *tgt_edge = path_end->targetClkEdge(sta_); PathEnd::Type end_type = path_end->type(); if ((end_type == PathEnd::Type::check || end_type == PathEnd::Type::output_delay) && path->minMax(sta_) == MinMax::max() && src_edge->clock() == clk_ && tgt_edge->clock() == clk_ // Only consider rise/rise and fall/fall paths. && src_edge->transition() == tgt_edge->transition() && path_end->multiCyclePath() == nullptr && (include_port_paths_ || !(network->isTopLevelPort(path->pin(sta_)) || pathIsFromInputPort(path_end)))) { Slack slack = path_end->slack(sta_); float period = clk_->period() - delayAsFloat(slack); min_period_ = max(min_period_, period); } } bool MinPeriodEndVisitor::pathIsFromInputPort(PathEnd *path_end) { PathExpanded expanded(path_end->path(), sta_); const Path *start = expanded.startPath(); Graph *graph = sta_->graph(); const Pin *first_pin = start->pin(graph); Network *network = sta_->network(); return network->isTopLevelPort(first_pin); } float Sta::findClkMinPeriod(const Clock *clk, bool include_port_paths) { searchPreamble(); search_->findArrivals(); VisitPathEnds visit_ends(this); MinPeriodEndVisitor min_period_visitor(clk, include_port_paths, this); for (Vertex *vertex : *search_->endpoints()) { findRequired(vertex); visit_ends.visitPathEnds(vertex, &min_period_visitor); } return min_period_visitor.minPeriod(); } //////////////////////////////////////////////////////////////// void Sta::findRequired(Vertex *vertex) { searchPreamble(); search_->findAllArrivals(); if (search_->isEndpoint(vertex) // Need to include downstream required times if there is fanout. && !hasFanout(vertex, search_->searchAdj(), graph_)) search_->seedRequired(vertex); else search_->findRequireds(vertex->level()); } Slack Sta::totalNegativeSlack(const MinMax *min_max) { searchPreamble(); return search_->totalNegativeSlack(min_max); } Slack Sta::totalNegativeSlack(const Corner *corner, const MinMax *min_max) { searchPreamble(); return search_->totalNegativeSlack(corner, min_max); } Slack Sta::worstSlack(const MinMax *min_max) { searchPreamble(); Slack worst_slack; Vertex *worst_vertex; search_->worstSlack(min_max, worst_slack, worst_vertex); return worst_slack; } void Sta::worstSlack(const MinMax *min_max, // Return values. Slack &worst_slack, Vertex *&worst_vertex) { searchPreamble(); 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); } //////////////////////////////////////////////////////////////// string Sta::reportDelayCalc(Edge *edge, TimingArc *arc, const Corner *corner, const MinMax *min_max, int digits) { findDelays(); return graph_delay_calc_->reportDelayCalc(edge, arc, corner, min_max, digits); } void Sta::setArcDelayCalc(const char *delay_calc_name) { delete arc_delay_calc_; arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_); // Update pointers to arc_delay_calc. updateComponentsState(); delaysInvalid(); } void Sta::findDelays(Vertex *to_vertex) { delayCalcPreamble(); graph_delay_calc_->findDelays(to_vertex->level()); } void Sta::findDelays() { delayCalcPreamble(); graph_delay_calc_->findDelays(levelize_->maxLevel()); } void Sta::findDelays(Level level) { delayCalcPreamble(); graph_delay_calc_->findDelays(level); } void Sta::delayCalcPreamble() { ensureLibLinked(); ensureClkNetwork(); } void Sta::setIncrementalDelayTolerance(float tol) { graph_delay_calc_->setIncrementalDelayTolerance(tol); } ArcDelay Sta::arcDelay(Edge *edge, TimingArc *arc, const DcalcAnalysisPt *dcalc_ap) { findDelays(edge->to(graph_)); return graph_->arcDelay(edge, arc, dcalc_ap->index()); } bool Sta::arcDelayAnnotated(Edge *edge, TimingArc *arc, DcalcAnalysisPt *dcalc_ap) { return graph_->arcDelayAnnotated(edge, arc, dcalc_ap->index()); } void Sta::setArcDelayAnnotated(Edge *edge, TimingArc *arc, DcalcAnalysisPt *dcalc_ap, bool annotated) { graph_->setArcDelayAnnotated(edge, arc, dcalc_ap->index(), annotated); Vertex *to = edge->to(graph_); search_->arrivalInvalid(to); search_->requiredInvalid(edge->from(graph_)); if (!annotated) graph_delay_calc_->delayInvalid(to); } Slew Sta::vertexSlew(Vertex *vertex, const RiseFall *rf, const Corner *corner, const MinMax *min_max) { findDelays(vertex); const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); return graph_->slew(vertex, rf, dcalc_ap->index()); } Slew Sta::vertexSlew(Vertex *vertex, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) { findDelays(vertex); return graph_->slew(vertex, rf, dcalc_ap->index()); } Slew Sta::vertexSlew(Vertex *vertex, const RiseFall *rf, const MinMax *min_max) { findDelays(vertex); Slew mm_slew = min_max->initValue(); for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); if (delayGreater(slew, mm_slew, min_max, this)) mm_slew = slew; } return mm_slew; } Slew Sta::vertexSlew(Vertex *vertex, const MinMax *min_max) { findDelays(vertex); Slew mm_slew = min_max->initValue(); for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { for (const RiseFall *rf : RiseFall::range()) { Slew slew = graph_->slew(vertex, rf, dcalc_ap->index()); if (delayGreater(slew, mm_slew, min_max, this)) mm_slew = slew; } } return mm_slew; } //////////////////////////////////////////////////////////////// // Make sure the network has been read and linked. // Throwing an error means the caller doesn't have to check the result. Network * Sta::ensureLinked() { if (network_ == nullptr || !network_->isLinked()) report_->error(1570, "No network has been linked."); // Return cmd/sdc network. return cmd_network_; } Network * Sta::ensureLibLinked() { if (network_ == nullptr || !network_->isLinked()) report_->error(1571, "No network has been linked."); // OpenROAD db is inherently linked but may not have associated // liberty files so check for them here. if (network_->defaultLibertyLibrary() == nullptr) report_->error(2141, "No liberty libraries found."); // Return cmd/sdc network. return cmd_network_; } Graph * Sta::ensureGraph() { ensureLibLinked(); if (graph_ == nullptr && network_) { makeGraph(); // Update pointers to graph. updateComponentsState(); } return graph_; } void Sta::makeGraph() { graph_ = new Graph(this, 2, corners_->dcalcAnalysisPtCount()); graph_->makeGraph(); } void Sta::ensureLevelized() { ensureGraph(); ensureGraphSdcAnnotated(); // Need constant propagation before levelization to know edges that // are disabled by constants. sim_->ensureConstantsPropagated(); levelize_->ensureLevelized(); } void Sta::updateGeneratedClks() { if (update_genclks_) { ensureLevelized(); bool gen_clk_changed = true; while (gen_clk_changed) { gen_clk_changed = false; for (Clock *clk : sdc_->clks()) { if (clk->isGenerated() && !clk->waveformValid()) { search_->genclks()->ensureMaster(clk); Clock *master_clk = clk->masterClk(); if (master_clk && master_clk->waveformValid()) { clk->generate(master_clk); gen_clk_changed = true; } } } } } update_genclks_ = false; } Level Sta::vertexLevel(Vertex *vertex) { ensureLevelized(); return vertex->level(); } GraphLoopSeq & Sta::graphLoops() { ensureLevelized(); return levelize_->loops(); } PathAnalysisPt * Sta::pathAnalysisPt(Path *path) { return path->pathAnalysisPt(this); } DcalcAnalysisPt * Sta::pathDcalcAnalysisPt(Path *path) { return pathAnalysisPt(path)->dcalcAnalysisPt(); } Vertex * Sta::maxPathCountVertex() const { Vertex *max_vertex = nullptr; int max_count = 0; VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); int count = vertexPathCount(vertex); if (count > max_count) { max_count = count; max_vertex = vertex; } } return max_vertex; } int Sta::vertexPathCount(Vertex *vertex) const { TagGroup *tag_group = search_->tagGroup(vertex); if (tag_group) return tag_group->pathCount(); else return 0; } int Sta::pathCount() const { int count = 0; VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); count += vertexPathCount(vertex); } return count; } TagIndex Sta::tagCount() const { return search_->tagCount(); } TagGroupIndex Sta::tagGroupCount() const { return search_->tagGroupCount(); } int Sta::clkInfoCount() const { return search_->clkInfoCount(); } void Sta::setArcDelay(Edge *edge, TimingArc *arc, const Corner *corner, const MinMaxAll *min_max, ArcDelay delay) { ensureGraph(); for (const MinMax *mm : min_max->range()) { const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); DcalcAPIndex ap_index = dcalc_ap->index(); graph_->setArcDelay(edge, arc, ap_index, delay); // Don't let delay calculation clobber the value. graph_->setArcDelayAnnotated(edge, arc, ap_index, true); } if (edge->role()->isTimingCheck()) search_->requiredInvalid(edge->to(graph_)); else { search_->arrivalInvalid(edge->to(graph_)); search_->requiredInvalid(edge->from(graph_)); } } void Sta::setAnnotatedSlew(Vertex *vertex, const Corner *corner, const MinMaxAll *min_max, const RiseFallBoth *rf, float slew) { ensureGraph(); for (const MinMax *mm : min_max->range()) { const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); DcalcAPIndex ap_index = dcalc_ap->index(); for (const RiseFall *rf1 : rf->range()) { graph_->setSlew(vertex, rf1, ap_index, slew); // Don't let delay calculation clobber the value. vertex->setSlewAnnotated(true, rf1, ap_index); } } graph_delay_calc_->delayInvalid(vertex); } void Sta::writeSdf(const char *filename, const Corner *corner, char divider, bool include_typ, int digits, bool gzip, bool no_timestamp, bool no_version) { findDelays(); sta::writeSdf(filename, corner, divider, include_typ, digits, gzip, no_timestamp, no_version, this); } void Sta::removeDelaySlewAnnotations() { if (graph_) { graph_->removeDelaySlewAnnotations(); delaysInvalid(); } } LogicValue Sta::simLogicValue(const Pin *pin) { ensureGraph(); sim_->ensureConstantsPropagated(); return sim_->logicValue(pin); } void Sta::findLogicConstants() { ensureGraph(); sim_->findLogicConstants(); } void Sta::clearLogicConstants() { sim_->clear(); } void Sta::setPortExtPinCap(const Port *port, const RiseFallBoth *rf, const Corner *corner, const MinMaxAll *min_max, float cap) { for (const RiseFall *rf1 : rf->range()) { for (const MinMax *mm : min_max->range()) { if (corner == nullptr) { for (const Corner *corner : *corners_) sdc_->setPortExtPinCap(port, rf1, corner, mm, cap); } else sdc_->setPortExtPinCap(port, rf1, corner, mm, cap); } } delaysInvalidFromFanin(port); } void Sta::portExtCaps(const Port *port, const Corner *corner, const MinMax *min_max, float &pin_cap, float &wire_cap, int &fanout) { bool pin_exists = false; bool wire_exists = false; bool fanout_exists = false; pin_cap = min_max->initValue(); wire_cap = min_max->initValue(); fanout = min_max->initValueInt(); for (const RiseFall *rf : RiseFall::range()) { float pin_cap1, wire_cap1; int fanout1; bool pin_exists1, wire_exists1, fanout_exists1; sdc_->portExtCap(port, rf, corner, min_max, pin_cap1, pin_exists1, wire_cap1, wire_exists1, fanout1, fanout_exists1); if (pin_exists1) { pin_cap = min_max->minMax(pin_cap, pin_cap1); pin_exists = true; } if (wire_exists1) { wire_cap = min_max->minMax(wire_cap, wire_cap1); wire_exists = true; } if (fanout_exists1) { fanout = min_max->minMax(fanout, fanout1); fanout_exists = true; } } if (!pin_exists) pin_cap = 0.0; if (!wire_exists) wire_cap = 0.0; if (!fanout_exists) fanout = 0; } void Sta::setPortExtWireCap(const Port *port, bool subtract_pin_cap, const RiseFallBoth *rf, const Corner *corner, const MinMaxAll *min_max, float cap) { for (const RiseFall *rf1 : rf->range()) { for (const MinMax *mm : min_max->range()) { if (corner == nullptr) { for (const Corner *corner : *corners_) sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap); } else sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap); } } delaysInvalidFromFanin(port); } void Sta::removeNetLoadCaps() const { sdc_->removeNetLoadCaps(); delaysInvalid(); } void Sta::setPortExtFanout(const Port *port, int fanout, const Corner *corner, const MinMaxAll *min_max) { for (const MinMax *mm : min_max->range()) { if (corner == nullptr) { for (const Corner *corner : *corners_) sdc_->setPortExtFanout(port, corner, mm, fanout); } else sdc_->setPortExtFanout(port, corner, mm, fanout); } delaysInvalidFromFanin(port); } void Sta::setNetWireCap(const Net *net, bool subtract_pin_cap, const Corner *corner, const MinMaxAll *min_max, float cap) { for (const MinMax *mm : min_max->range()) { if (corner == nullptr) { for (const Corner *corner : *corners_) sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap); } else sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap); } delaysInvalidFromFanin(net); } void Sta::connectedCap(const Pin *drvr_pin, const RiseFall *rf, const Corner *corner, const MinMax *min_max, float &pin_cap, float &wire_cap) const { const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); graph_delay_calc_->loadCap(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap); } void Sta::connectedCap(const Net *net, Corner *corner, const MinMax *min_max, float &pin_cap, float &wire_cap) const { const Pin *drvr_pin = findNetParasiticDrvrPin(net); if (drvr_pin) { pin_cap = min_max->initValue(); wire_cap = min_max->initValue(); for (const Corner *corner : makeCornerSeq(corner)) { for (const RiseFall *rf : RiseFall::range()) { float pin_cap1, wire_cap1; connectedCap(drvr_pin, rf, corner, min_max, pin_cap1, wire_cap1); pin_cap = min_max->minMax(pin_cap, pin_cap1); wire_cap = min_max->minMax(wire_cap, wire_cap1); } } } else { pin_cap = 0.0; wire_cap = 0.0; } } CornerSeq Sta::makeCornerSeq(Corner *corner) const { CornerSeq corners; if (corner) corners.push_back(corner); else corners = corners_->corners(); return corners; } float Sta::capacitance(const LibertyPort *port, Corner *corner, const MinMax *min_max) { OperatingConditions *op_cond = operatingConditions(min_max); float cap = min_max->initValue(); for (const Corner *corner : makeCornerSeq(corner)) { const LibertyPort *corner_port = port->cornerPort(corner, min_max); for (const RiseFall *rf : RiseFall::range()) cap = min_max->minMax(cap, corner_port->capacitance(rf, min_max, op_cond, op_cond)); } return cap; } // Look for a driver to find a parasitic if the net has one. // Settle for a load pin if there are no drivers. const Pin * Sta::findNetParasiticDrvrPin(const Net *net) const { const Pin *load_pin = nullptr; NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (network_->isDriver(pin)) { delete pin_iter; return pin; } if (network_->isLoad(pin)) load_pin = pin; } delete pin_iter; return load_pin; } void Sta::setResistance(const Net *net, const MinMaxAll *min_max, float res) { sdc_->setResistance(net, min_max, res); } //////////////////////////////////////////////////////////////// bool Sta::readSpef(const char *filename, Instance *instance, const Corner *corner, const MinMaxAll *min_max, bool pin_cap_included, bool keep_coupling_caps, float coupling_cap_factor, bool reduce) { ensureLibLinked(); setParasiticAnalysisPts(corner != nullptr); const MinMax *ap_min_max = (min_max == MinMaxAll::all()) ? MinMax::max() : min_max->asMinMax(); const Corner *ap_corner = corner ? corner : corners_->corners()[0]; ParasiticAnalysisPt *ap = ap_corner->findParasiticAnalysisPt(ap_min_max); bool success = readSpefFile(filename, instance, ap, pin_cap_included, keep_coupling_caps, coupling_cap_factor, reduce, corner, min_max, this); delaysInvalid(); return success; } void Sta::setParasiticAnalysisPts(bool per_corner) { if (per_corner != parasitics_per_corner_) { deleteParasitics(); parasitics_per_corner_ = per_corner; makeParasiticAnalysisPts(); } } void Sta::makeParasiticAnalysisPts() { corners_->makeParasiticAnalysisPts(parasitics_per_corner_); } void Sta::reportParasiticAnnotation(bool report_unannotated, const Corner *corner) { ensureLibLinked(); ensureGraph(); sta::reportParasiticAnnotation(report_unannotated, corner, this); } void Sta::findPiElmore(Pin *drvr_pin, const RiseFall *rf, const MinMax *min_max, float &c2, float &rpi, float &c1, bool &exists) const { Corner *corner = cmd_corner_; const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); if (pi_elmore) { parasitics_->piModel(pi_elmore, c2, rpi, c1); exists = true; } else exists = false; } void Sta::makePiElmore(Pin *drvr_pin, const RiseFall *rf, const MinMaxAll *min_max, float c2, float rpi, float c1) { const Corner *corner = cmd_corner_; for (const MinMax *mm : min_max->range()) { ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); parasitics_->makePiElmore(drvr_pin, rf, ap, c2, rpi, c1); } delaysInvalidFrom(drvr_pin); } void Sta::findElmore(Pin *drvr_pin, Pin *load_pin, const RiseFall *rf, const MinMax *min_max, float &elmore, bool &exists) const { Corner *corner = cmd_corner_; const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max); Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); if (pi_elmore) parasitics_->findElmore(pi_elmore, load_pin, elmore, exists); else exists = false; } void Sta::setElmore(Pin *drvr_pin, Pin *load_pin, const RiseFall *rf, const MinMaxAll *min_max, float elmore) { const Corner *corner = cmd_corner_; for (const MinMax *mm : min_max->range()) { const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap); if (pi_elmore) parasitics_->setElmore(pi_elmore, load_pin, elmore); } delaysInvalidFrom(drvr_pin); } void Sta::deleteParasitics() { parasitics_->deleteParasitics(); delaysInvalid(); } Parasitic * Sta::makeParasiticNetwork(const Net *net, bool includes_pin_caps, const ParasiticAnalysisPt *ap) { Parasitic *parasitic = parasitics_->makeParasiticNetwork(net, includes_pin_caps, ap); delaysInvalidFromFanin(const_cast(net)); return parasitic; } //////////////////////////////////////////////////////////////// // // Network edit commands. // // This implementation calls Sta before/after methods to // update the Sta components. // A different implementation may let the network edits // call the before/after methods implicitly so these functions // should not (Verific). // //////////////////////////////////////////////////////////////// NetworkEdit * Sta::networkCmdEdit() { return dynamic_cast(cmd_network_); } Instance * Sta::makeInstance(const char *name, LibertyCell *cell, Instance *parent) { NetworkEdit *network = networkCmdEdit(); Instance *inst = network->makeInstance(cell, name, parent); network->makePins(inst); makeInstanceAfter(inst); return inst; } void Sta::deleteInstance(Instance *inst) { NetworkEdit *network = networkCmdEdit(); deleteInstanceBefore(inst); network->deleteInstance(inst); } void Sta::replaceCell(Instance *inst, LibertyCell *to_lib_cell) { Cell *to_cell = network_->cell(to_lib_cell); replaceCell(inst, to_cell, to_lib_cell); } void Sta::replaceCell(Instance *inst, Cell *to_cell) { LibertyCell *to_lib_cell = network_->libertyCell(to_cell); replaceCell(inst, to_cell, to_lib_cell); } void Sta::replaceCell(Instance *inst, Cell *to_cell, LibertyCell *to_lib_cell) { NetworkEdit *network = networkCmdEdit(); LibertyCell *from_lib_cell = network->libertyCell(inst); if (sta::equivCellsArcs(from_lib_cell, to_lib_cell)) { // Replace celll optimized for less disruption to graph // when ports and timing arcs are equivalent. replaceEquivCellBefore(inst, to_lib_cell); network->replaceCell(inst, to_cell); replaceEquivCellAfter(inst); } else { replaceCellBefore(inst, to_lib_cell); network->replaceCell(inst, to_cell); replaceCellAfter(inst); } } Net * Sta::makeNet(const char *name, Instance *parent) { NetworkEdit *network = networkCmdEdit(); Net *net = network->makeNet(name, parent); // Sta notification unnecessary. return net; } void Sta::deleteNet(Net *net) { NetworkEdit *network = networkCmdEdit(); deleteNetBefore(net); network->deleteNet(net); } void Sta::connectPin(Instance *inst, Port *port, Net *net) { NetworkEdit *network = networkCmdEdit(); Pin *pin = network->connect(inst, port, net); connectPinAfter(pin); } void Sta::connectPin(Instance *inst, LibertyPort *port, Net *net) { NetworkEdit *network = networkCmdEdit(); Pin *pin = network->connect(inst, port, net); connectPinAfter(pin); } void Sta::disconnectPin(Pin *pin) { NetworkEdit *network = networkCmdEdit(); disconnectPinBefore(pin); network->disconnectPin(pin); } void Sta::makePortPin(const char *port_name, PortDirection *dir) { ensureLinked(); NetworkReader *network = dynamic_cast(network_); Instance *top_inst = network->topInstance(); Cell *top_cell = network->cell(top_inst); Port *port = network->makePort(top_cell, port_name); network->setDirection(port, dir); Pin *pin = network->makePin(top_inst, port, nullptr); makePortPinAfter(pin); } //////////////////////////////////////////////////////////////// // // Network edit before/after methods. // //////////////////////////////////////////////////////////////// void Sta::makeInstanceAfter(const Instance *inst) { debugPrint(debug_, "network_edit", 1, "make instance %s", sdc_network_->pathName(inst)); if (graph_) { LibertyCell *lib_cell = network_->libertyCell(inst); if (lib_cell) { // Make vertices and internal instance edges. LibertyCellPortBitIterator port_iter(lib_cell); while (port_iter.hasNext()) { LibertyPort *lib_port = port_iter.next(); Pin *pin = network_->findPin(inst, lib_port); if (pin) { Vertex *vertex, *bidir_drvr_vertex; graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); } } graph_->makeInstanceEdges(inst); } } } void Sta::makePortPinAfter(Pin *pin) { if (graph_) { Vertex *vertex, *bidir_drvr_vertex; graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); } } void Sta::replaceEquivCellBefore(const Instance *inst, const LibertyCell *to_cell) { if (graph_) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); LibertyPort *port = network_->libertyPort(pin); if (port) { if (port->direction()->isAnyInput()) { Vertex *vertex = graph_->pinLoadVertex(pin); replaceCellPinInvalidate(port, vertex, to_cell); // Replace the timing arc sets in the graph edges. VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); if (network_->instance(to_vertex->pin()) == inst) { TimingArcSet *from_set = edge->timingArcSet(); // Find corresponding timing arc set. TimingArcSet *to_set = to_cell->findTimingArcSet(from_set); if (to_set) edge->setTimingArcSet(to_set); else report_->critical(1555, "corresponding timing arc set not found in equiv cells"); } } } else { // Force delay calculation on output pins. Vertex *vertex = graph_->pinDrvrVertex(pin); graph_delay_calc_->delayInvalid(vertex); } } } delete pin_iter; } } void Sta::replaceEquivCellAfter(const Instance *inst) { if (graph_) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); if (network_->direction(pin)->isAnyInput()) parasitics_->loadPinCapacitanceChanged(pin); } delete pin_iter; } } void Sta::replaceCellPinInvalidate(const LibertyPort *from_port, Vertex *vertex, const LibertyCell *to_cell) { LibertyPort *to_port = to_cell->findLibertyPort(from_port->name()); if (to_port == nullptr || (!libertyPortCapsEqual(to_port, from_port) // If this is an ideal clock pin, do not invalidate // arrivals and delay calc on the clock pin driver. && !(to_port->isClock() && idealClockMode()))) // Input port capacitance changed, so invalidate delay // calculation from input driver. delaysInvalidFromFanin(vertex); else delaysInvalidFrom(vertex); } bool Sta::idealClockMode() { for (Clock *clk : sdc_->clks()) { if (clk->isPropagated()) return false; } return true; } static bool libertyPortCapsEqual(const LibertyPort *port1, const LibertyPort *port2) { return port1->capacitance(RiseFall::rise(), MinMax::min()) == port2->capacitance(RiseFall::rise(), MinMax::min()) && port1->capacitance(RiseFall::rise(), MinMax::max()) == port2->capacitance(RiseFall::rise(), MinMax::max()) && port1->capacitance(RiseFall::fall(), MinMax::min()) == port2->capacitance(RiseFall::fall(), MinMax::min()) && port1->capacitance(RiseFall::fall(), MinMax::max()) == port2->capacitance(RiseFall::fall(), MinMax::max()); } void Sta::replaceCellBefore(const Instance *inst, const LibertyCell *to_cell) { if (graph_) { // Delete all graph edges between instance pins. InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); LibertyPort *port = network_->libertyPort(pin); if (port->direction()->isAnyInput()) { Vertex *vertex = graph_->pinLoadVertex(pin); replaceCellPinInvalidate(port, vertex, to_cell); VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); if (network_->instance(to_vertex->pin()) == inst) deleteEdge(edge); } } } delete pin_iter; } } void Sta::replaceCellAfter(const Instance *inst) { if (graph_) { graph_->makeInstanceEdges(inst); InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); sim_->pinSetFuncAfter(pin); if (network_->direction(pin)->isAnyInput()) parasitics_->loadPinCapacitanceChanged(pin); } delete pin_iter; } } void Sta::connectPinAfter(const Pin *pin) { debugPrint(debug_, "network_edit", 1, "connect %s to %s", sdc_network_->pathName(pin), sdc_network_->pathName(network_->net(pin))); if (graph_) { if (network_->isHierarchical(pin)) { graph_->makeWireEdgesThruPin(pin); EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->isWire()) { connectDrvrPinAfter(edge->from(graph_)); connectLoadPinAfter(edge->to(graph_)); } } } else { Vertex *vertex, *bidir_drvr_vertex; graph_->pinVertices(pin, vertex, bidir_drvr_vertex); if (vertex) { search_->arrivalInvalid(vertex); search_->requiredInvalid(vertex); if (bidir_drvr_vertex) { search_->arrivalInvalid(bidir_drvr_vertex); search_->requiredInvalid(bidir_drvr_vertex); } // Make interconnect edges from/to pin. if (network_->isDriver(pin)) { graph_->makeWireEdgesFromPin(pin); connectDrvrPinAfter(bidir_drvr_vertex ? bidir_drvr_vertex : vertex); } // Note that a bidirect is both a driver and a load so this // is NOT an else clause for the above "if". if (network_->isLoad(pin)) { graph_->makeWireEdgesToPin(pin); connectLoadPinAfter(vertex); } } } } sdc_->connectPinAfter(pin); sim_->connectPinAfter(pin); } void Sta::connectDrvrPinAfter(Vertex *vertex) { // Invalidate arrival at fanout vertices. VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); search_->arrivalInvalid(to_vertex); search_->endpointInvalid(to_vertex); sdc_->clkHpinDisablesChanged(to_vertex->pin()); } Pin *pin = vertex->pin(); sdc_->clkHpinDisablesChanged(pin); graph_delay_calc_->delayInvalid(vertex); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); levelize_->relevelizeFrom(vertex); clk_network_->connectPinAfter(pin); } void Sta::connectLoadPinAfter(Vertex *vertex) { // Invalidate delays and required at fanin vertices. VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); graph_delay_calc_->delayInvalid(from_vertex); search_->requiredInvalid(from_vertex); sdc_->clkHpinDisablesChanged(from_vertex->pin()); levelize_->relevelizeFrom(from_vertex); } Pin *pin = vertex->pin(); sdc_->clkHpinDisablesChanged(pin); graph_delay_calc_->delayInvalid(vertex); search_->arrivalInvalid(vertex); search_->endpointInvalid(vertex); clk_network_->connectPinAfter(pin); } void Sta::disconnectPinBefore(const Pin *pin) { debugPrint(debug_, "network_edit", 1, "disconnect %s from %s", sdc_network_->pathName(pin), sdc_network_->pathName(network_->net(pin))); parasitics_->disconnectPinBefore(pin, network_); sdc_->disconnectPinBefore(pin); sim_->disconnectPinBefore(pin); if (graph_) { if (network_->isDriver(pin)) { Vertex *vertex = graph_->pinDrvrVertex(pin); // Delete wire edges from pin. if (vertex) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->isWire()) deleteEdge(edge); } clk_network_->disconnectPinBefore(pin); } } if (network_->isLoad(pin)) { // Delete wire edges to pin. Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->isWire()) deleteEdge(edge); } clk_network_->disconnectPinBefore(pin); } } if (network_->isHierarchical(pin)) { // Delete wire edges thru pin. EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->isWire()) { deleteEdge(edge); clk_network_->disconnectPinBefore(edge->from(graph_)->pin()); } } } } } void Sta::deleteEdge(Edge *edge) { debugPrint(debug_, "network_edit", 2, "delete edge %s -> %s", edge->from(graph_)->name(sdc_network_), edge->to(graph_)->name(sdc_network_)); Vertex *to = edge->to(graph_); search_->deleteEdgeBefore(edge); graph_delay_calc_->delayInvalid(to); levelize_->relevelizeFrom(to); levelize_->deleteEdgeBefore(edge); sdc_->clkHpinDisablesChanged(edge->from(graph_)->pin()); graph_->deleteEdge(edge); } void Sta::deleteNetBefore(const Net *net) { debugPrint(debug_, "network_edit", 1, "delete net %s", sdc_network_->pathName(net)); if (graph_) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (!network_->isHierarchical(pin)) { disconnectPinBefore(pin); // Delete wire edges on net pins. Vertex *vertex = graph_->pinDrvrVertex(pin); if (vertex) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->isWire()) deleteEdge(edge); } } } } delete pin_iter; } sdc_->deleteNetBefore(net); } void Sta::deleteInstanceBefore(const Instance *inst) { debugPrint(debug_, "network_edit", 1, "delete instance %s", sdc_network_->pathName(inst)); if (network_->isLeaf(inst)) { deleteInstancePinsBefore(inst); deleteLeafInstanceBefore(inst); } else { // Delete hierarchical instance children. InstanceChildIterator *child_iter = network_->childIterator(inst); while (child_iter->hasNext()) { Instance *child = child_iter->next(); deleteInstanceBefore(child); } delete child_iter; } } void Sta::deleteLeafInstanceBefore(const Instance *inst) { sim_->deleteInstanceBefore(inst); sdc_->deleteInstanceBefore(inst); } void Sta::deleteInstancePinsBefore(const Instance *inst) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); deletePinBefore(pin); } delete pin_iter; } void Sta::deletePinBefore(const Pin *pin) { if (graph_) { debugPrint(debug_, "network_edit", 1, "delete pin %s", sdc_network_->pathName(pin)); if (network_->isLoad(pin)) { Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { levelize_->deleteVertexBefore(vertex); graph_delay_calc_->deleteVertexBefore(vertex); search_->deleteVertexBefore(vertex); VertexInEdgeIterator in_edge_iter(vertex, graph_); while (in_edge_iter.hasNext()) { Edge *edge = in_edge_iter.next(); if (edge->role()->isWire()) { Vertex *from = edge->from(graph_); // Only notify to_vertex (from_vertex will be deleted). search_->requiredInvalid(from); } levelize_->deleteEdgeBefore(edge); } // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } if (network_->isDriver(pin)) { Vertex *vertex = graph_->pinDrvrVertex(pin); if (vertex) { levelize_->deleteVertexBefore(vertex); graph_delay_calc_->deleteVertexBefore(vertex); search_->deleteVertexBefore(vertex); VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (edge->role()->isWire()) { // Only notify to vertex (from will be deleted). Vertex *to = edge->to(graph_); search_->arrivalInvalid(to); graph_delay_calc_->delayInvalid(to); levelize_->relevelizeFrom(to); } levelize_->deleteEdgeBefore(edge); } // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } if (network_->direction(pin) == PortDirection::internal()) { // Internal pins are not loads or drivers. Vertex *vertex = graph_->pinLoadVertex(pin); if (vertex) { levelize_->deleteVertexBefore(vertex); graph_delay_calc_->deleteVertexBefore(vertex); search_->deleteVertexBefore(vertex); graph_->deleteVertex(vertex); } } } sim_->deletePinBefore(pin); clk_network_->deletePinBefore(pin); } void Sta::delaysInvalidFrom(const Port *port) { if (graph_) { Instance *top_inst = network_->topInstance(); Pin *pin = network_->findPin(top_inst, port); delaysInvalidFrom(pin); } } void Sta::delaysInvalidFrom(const Instance *inst) { if (graph_) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); delaysInvalidFrom(pin); } delete pin_iter; } } void Sta::delaysInvalidFrom(const Pin *pin) { if (graph_) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); delaysInvalidFrom(vertex); if (bidirect_drvr_vertex) delaysInvalidFrom(bidirect_drvr_vertex); } } void Sta::delaysInvalidFrom(Vertex *vertex) { search_->arrivalInvalid(vertex); search_->requiredInvalid(vertex); graph_delay_calc_->delayInvalid(vertex); } void Sta::delaysInvalidFromFanin(const Port *port) { if (graph_) { Instance *top_inst = network_->topInstance(); Pin *pin = network_->findPin(top_inst, port); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); delaysInvalidFromFanin(vertex); if (bidirect_drvr_vertex) delaysInvalidFromFanin(bidirect_drvr_vertex); } } void Sta::delaysInvalidFromFanin(const Pin *pin) { if (graph_) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) delaysInvalidFromFanin(vertex); if (bidirect_drvr_vertex) delaysInvalidFromFanin(bidirect_drvr_vertex); } } void Sta::delaysInvalidFromFanin(const Net *net) { if (graph_) { NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); if (!network_->isHierarchical(pin)) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) delaysInvalidFrom(vertex); if (bidirect_drvr_vertex) delaysInvalidFrom(bidirect_drvr_vertex); } } delete pin_iter; } } void Sta::delaysInvalidFromFanin(Vertex *vertex) { VertexInEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); delaysInvalidFrom(from_vertex); search_->requiredInvalid(from_vertex); } } //////////////////////////////////////////////////////////////// ClockSet Sta::clocks(const Pin *pin) { ensureClkArrivals(); return search_->clocks(pin); } ClockSet Sta::clockDomains(const Pin *pin) { searchPreamble(); search_->findAllArrivals(); return search_->clockDomains(pin); } //////////////////////////////////////////////////////////////// InstanceSet Sta::findRegisterInstances(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { findRegisterPreamble(); return findRegInstances(clks, clk_rf, edge_triggered, latches, this); } PinSet Sta::findRegisterDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { findRegisterPreamble(); return findRegDataPins(clks, clk_rf, edge_triggered, latches, this); } PinSet Sta::findRegisterClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { findRegisterPreamble(); return findRegClkPins(clks, clk_rf, edge_triggered, latches, this); } PinSet Sta::findRegisterAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { findRegisterPreamble(); return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, this); } PinSet Sta::findRegisterOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { findRegisterPreamble(); return findRegOutputPins(clks, clk_rf, edge_triggered, latches, this); } void Sta::findRegisterPreamble() { ensureLibLinked(); ensureGraph(); ensureGraphSdcAnnotated(); sim_->ensureConstantsPropagated(); } //////////////////////////////////////////////////////////////// class FanInOutSrchPred : public SearchPred { public: FanInOutSrchPred(bool thru_disabled, bool thru_constants, const StaState *sta); virtual bool searchFrom(const Vertex *from_vertex); virtual bool searchThru(Edge *edge); virtual bool searchTo(const Vertex *to_vertex); protected: bool crossesHierarchy(Edge *edge); virtual bool searchThruRole(Edge *edge); bool thru_disabled_; bool thru_constants_; const StaState *sta_; }; FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled, bool thru_constants, const StaState *sta) : SearchPred(), thru_disabled_(thru_disabled), thru_constants_(thru_constants), sta_(sta) { } bool FanInOutSrchPred::searchFrom(const Vertex *from_vertex) { return (thru_disabled_ || !from_vertex->isDisabledConstraint()) && (thru_constants_ || !from_vertex->isConstant()); } bool FanInOutSrchPred::searchThru(Edge *edge) { return searchThruRole(edge) && (thru_disabled_ || !(edge->isDisabledConstraint() || edge->isDisabledCond() || sta_->isDisabledCondDefault(edge))) && (thru_constants_ || edge->simTimingSense() != TimingSense::none); } bool FanInOutSrchPred::searchThruRole(Edge *edge) { const TimingRole *role = edge->role(); return role == TimingRole::wire() || role == TimingRole::combinational() || role == TimingRole::tristateEnable() || role == TimingRole::tristateDisable(); } bool FanInOutSrchPred::crossesHierarchy(Edge *edge) { Network *network = sta_->network(); Graph *graph = sta_->graph(); Vertex *from = edge->from(graph); Vertex *to = edge->to(graph); Instance *from_inst = network->instance(from->pin()); Instance *to_inst = network->instance(to->pin()); return network->parent(from_inst) != network->parent(to_inst); } bool FanInOutSrchPred::searchTo(const Vertex *to_vertex) { return (thru_disabled_ || !to_vertex->isDisabledConstraint()) && (thru_constants_ || !to_vertex->isConstant()); } class FaninSrchPred : public FanInOutSrchPred { public: FaninSrchPred(bool thru_disabled, bool thru_constants, const StaState *sta); protected: virtual bool searchThruRole(Edge *edge); }; FaninSrchPred::FaninSrchPred(bool thru_disabled, bool thru_constants, const StaState *sta) : FanInOutSrchPred(thru_disabled, thru_constants, sta) { } bool FaninSrchPred::searchThruRole(Edge *edge) { const TimingRole *role = edge->role(); return role == TimingRole::wire() || role == TimingRole::combinational() || role == TimingRole::tristateEnable() || role == TimingRole::tristateDisable() || role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ(); } PinSet Sta::findFaninPins(PinSeq *to, bool flat, bool startpoints_only, int inst_levels, int pin_levels, bool thru_disabled, bool thru_constants) { ensureGraph(); ensureLevelized(); PinSet fanin(network_); FaninSrchPred pred(thru_disabled, thru_constants, this); for (const Pin *pin : *to) { if (network_->isHierarchical(pin)) { EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); findFaninPins(edge->from(graph_), flat, startpoints_only, inst_levels, pin_levels, fanin, pred); } } else { Vertex *vertex = graph_->pinLoadVertex(pin); findFaninPins(vertex, flat, startpoints_only, inst_levels, pin_levels, fanin, pred); } } return fanin; } void Sta::findFaninPins(Vertex *vertex, bool flat, bool startpoints_only, int inst_levels, int pin_levels, PinSet &fanin, SearchPred &pred) { VertexSet visited(graph_); findFaninPins(vertex, flat, inst_levels, pin_levels, visited, &pred, 0, 0); VertexSet::Iterator visited_iter(visited); while (visited_iter.hasNext()) { Vertex *visited_vertex = visited_iter.next(); Pin *visited_pin = visited_vertex->pin(); if (!startpoints_only || network_->isRegClkPin(visited_pin) || !hasFanin(visited_vertex, &pred, graph_)) fanin.insert(visited_pin); } } void Sta::findFaninPins(Vertex *to, bool flat, int inst_levels, int pin_levels, VertexSet &visited, SearchPred *pred, int inst_level, int pin_level) { debugPrint(debug_, "fanin", 1, "%s", to->to_string(this).c_str()); if (!visited.hasKey(to)) { visited.insert(to); Pin *to_pin = to->pin(); bool is_reg_clk_pin = network_->isRegClkPin(to_pin); if (!is_reg_clk_pin && (inst_levels <= 0 || inst_level < inst_levels) && (pin_levels <= 0 || pin_level < pin_levels) && pred->searchTo(to)) { VertexInEdgeIterator edge_iter(to, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); if (pred->searchThru(edge) && (flat || !crossesHierarchy(edge)) && pred->searchFrom(from_vertex)) { findFaninPins(from_vertex, flat, inst_levels, pin_levels, visited, pred, edge->role()->isWire() ? inst_level : inst_level+1, pin_level+1); } } } } } InstanceSet Sta::findFaninInstances(PinSeq *to, bool flat, bool startpoints_only, int inst_levels, int pin_levels, bool thru_disabled, bool thru_constants) { PinSet pins = findFaninPins(to, flat, startpoints_only, inst_levels, pin_levels, thru_disabled, thru_constants); return pinInstances(pins, network_); } PinSet Sta::findFanoutPins(PinSeq *from, bool flat, bool endpoints_only, int inst_levels, int pin_levels, bool thru_disabled, bool thru_constants) { ensureGraph(); ensureLevelized(); PinSet fanout(network_); FanInOutSrchPred pred(thru_disabled, thru_constants, this); PinSeq::Iterator from_iter(from); while (from_iter.hasNext()) { const Pin *pin = from_iter.next(); if (network_->isHierarchical(pin)) { EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); findFanoutPins(edge->to(graph_), flat, endpoints_only, inst_levels, pin_levels, fanout, pred); } } else { Vertex *vertex = graph_->pinDrvrVertex(pin); findFanoutPins(vertex, flat, endpoints_only, inst_levels, pin_levels, fanout, pred); } } return fanout; } void Sta::findFanoutPins(Vertex *vertex, bool flat, bool endpoints_only, int inst_levels, int pin_levels, PinSet &fanout, SearchPred &pred) { VertexSet visited(graph_); findFanoutPins(vertex, flat, inst_levels, pin_levels, visited, &pred, 0, 0); VertexSet::Iterator visited_iter(visited); while (visited_iter.hasNext()) { Vertex *visited_vertex = visited_iter.next(); Pin *visited_pin = visited_vertex->pin(); if (!endpoints_only || search_->isEndpoint(visited_vertex, &pred)) fanout.insert(visited_pin); } } // DFS to support level limits. void Sta::findFanoutPins(Vertex *from, bool flat, int inst_levels, int pin_levels, VertexSet &visited, SearchPred *pred, int inst_level, int pin_level) { debugPrint(debug_, "fanout", 1, "%s", from->to_string(this).c_str()); if (!visited.hasKey(from)) { visited.insert(from); if (!search_->isEndpoint(from, pred) && (inst_levels <= 0 || inst_level < inst_levels) && (pin_levels <= 0 || pin_level < pin_levels) && pred->searchFrom(from)) { VertexOutEdgeIterator edge_iter(from, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); if (pred->searchThru(edge) && (flat || !crossesHierarchy(edge)) && pred->searchTo(to_vertex)) { findFanoutPins(to_vertex, flat, inst_levels, pin_levels, visited, pred, edge->role()->isWire() ? inst_level : inst_level+1, pin_level+1); } } } } } InstanceSet Sta::findFanoutInstances(PinSeq *from, bool flat, bool endpoints_only, int inst_levels, int pin_levels, bool thru_disabled, bool thru_constants) { PinSet pins = findFanoutPins(from, flat, endpoints_only, inst_levels, pin_levels, thru_disabled, thru_constants); return pinInstances(pins, network_); } static InstanceSet pinInstances(PinSet &pins, const Network *network) { InstanceSet insts(network); for (const Pin *pin : pins) insts.insert(network->instance(pin)); return insts; } bool Sta::crossesHierarchy(Edge *edge) const { Vertex *from = edge->from(graph_); Vertex *to = edge->to(graph_); const Pin *from_pin = from->pin(); Instance *from_inst = network_->instance(from_pin); Instance *to_inst = network_->instance(to->pin()); Instance *from_parent, *to_parent; // Treat input/output port pins as "inside". if (network_->isTopInstance(from_inst)) from_parent = from_inst; else from_parent = network_->parent(from_inst); if (network_->isTopInstance(to_inst)) to_parent = to_inst; else to_parent = network_->parent(to_inst); return from_parent != to_parent; } //////////////////////////////////////////////////////////////// static Slew instMaxSlew(const Instance *inst, Sta *sta) { Network *network = sta->network(); Graph *graph = sta->graph(); Slew max_slew = 0.0; InstancePinIterator *pin_iter = network->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); if (network->isDriver(pin)) { Vertex *vertex = graph->pinDrvrVertex(pin); Slew slew = sta->vertexSlew(vertex, MinMax::max()); if (delayGreater(slew, max_slew, sta)) max_slew = slew; } } delete pin_iter; return max_slew; } InstanceSeq Sta::slowDrivers(int count) { findDelays(); InstanceSeq insts = network_->leafInstances(); sort(insts, [=] (const Instance *inst1, const Instance *inst2) { return delayGreater(instMaxSlew(inst1, this), instMaxSlew(inst2, this), this); }); insts.resize(count); return insts; } //////////////////////////////////////////////////////////////// void Sta::checkSlewLimitPreamble() { if (sdc_->haveClkSlewLimits()) // Arrivals are needed to know pin clock domains. updateTiming(false); else findDelays(); if (check_slew_limits_ == nullptr) makeCheckSlewLimits(); ensureClkNetwork(); } PinSeq Sta::checkSlewLimits(Net *net, bool violators, const Corner *corner, const MinMax *min_max) { checkSlewLimitPreamble(); return check_slew_limits_->checkSlewLimits(net, violators, corner, min_max); } void Sta::reportSlewLimitShortHeader() { report_path_->reportLimitShortHeader(report_path_->fieldSlew()); } void Sta::reportSlewLimitShort(Pin *pin, const Corner *corner, const MinMax *min_max) { const Corner *corner1; const RiseFall *rf1; Slew slew1; float limit1, slack1; check_slew_limits_->checkSlew(pin, corner, min_max, true, corner1, rf1, slew1, limit1, slack1); report_path_->reportLimitShort(report_path_->fieldSlew(), pin, delayAsFloat(slew1), limit1, slack1); } void Sta::reportSlewLimitVerbose(Pin *pin, const Corner *corner, const MinMax *min_max) { const Corner *corner1; const RiseFall *rf1; Slew slew1; float limit1, slack1; check_slew_limits_->checkSlew(pin, corner, min_max, true, corner1, rf1, slew1, limit1, slack1); report_path_->reportLimitVerbose(report_path_->fieldSlew(), pin, rf1, delayAsFloat(slew1), limit1, slack1, corner1, min_max); } void Sta::checkSlew(const Pin *pin, const Corner *corner, const MinMax *min_max, bool check_clks, // Return values. const Corner *&corner1, const RiseFall *&rf, Slew &slew, float &limit, float &slack) { check_slew_limits_->checkSlew(pin, corner, min_max, check_clks, corner1, rf, slew, limit, slack); } void Sta::maxSlewCheck(// Return values. const Pin *&pin, Slew &slew, float &slack, float &limit) { checkSlewLimitPreamble(); PinSeq pins = check_slew_limits_->checkSlewLimits(nullptr, false, nullptr, MinMax::max()); pin = nullptr; slew = 0.0; slack = INF; limit = INF; if (!pins.empty()) { pin = pins[0]; const Corner *corner; const RiseFall *rf; check_slew_limits_->checkSlew(pin, nullptr, MinMax::max(), true, corner, rf, slew, limit, slack); } } void Sta::findSlewLimit(const LibertyPort *port, const Corner *corner, const MinMax *min_max, // Return values. float &limit, bool &exists) { if (check_slew_limits_ == nullptr) makeCheckSlewLimits(); check_slew_limits_->findLimit(port, corner, min_max, limit, exists); } ////////////////////////////////////////////////////////////////' void Sta::checkFanoutLimitPreamble() { if (check_fanout_limits_ == nullptr) makeCheckFanoutLimits(); ensureClkNetwork(); } PinSeq Sta::checkFanoutLimits(Net *net, bool violators, const MinMax *min_max) { checkFanoutLimitPreamble(); return check_fanout_limits_->checkFanoutLimits(net, violators, min_max); } void Sta::reportFanoutLimitShortHeader() { report_path_->reportLimitShortHeader(report_path_->fieldFanout()); } void Sta::reportFanoutLimitShort(Pin *pin, const MinMax *min_max) { float fanout, limit, slack; check_fanout_limits_->checkFanout(pin, min_max, fanout, limit, slack); report_path_->reportLimitShort(report_path_->fieldFanout(), pin, fanout, limit, slack); } void Sta::reportFanoutLimitVerbose(Pin *pin, const MinMax *min_max) { float fanout, limit, slack; check_fanout_limits_->checkFanout(pin, min_max, fanout, limit, slack); report_path_->reportLimitVerbose(report_path_->fieldFanout(), pin, nullptr, fanout, limit, slack, nullptr, min_max); } void Sta::checkFanout(const Pin *pin, const MinMax *min_max, // Return values. float &fanout, float &limit, float &slack) { check_fanout_limits_->checkFanout(pin, min_max, fanout, limit, slack); } void Sta::maxFanoutCheck(// Return values. const Pin *&pin, float &fanout, float &slack, float &limit) { checkFanoutLimitPreamble(); PinSeq pins = check_fanout_limits_->checkFanoutLimits(nullptr, false, MinMax::max()); pin = nullptr; fanout = 0; slack = INF; limit = INF; if (!pins.empty()) { pin = pins[0]; check_fanout_limits_->checkFanout(pin, MinMax::max(), fanout, limit, slack); } } ////////////////////////////////////////////////////////////////' void Sta::checkCapacitanceLimitPreamble() { if (check_capacitance_limits_ == nullptr) makeCheckCapacitanceLimits(); ensureClkNetwork(); } PinSeq Sta::checkCapacitanceLimits(Net *net, bool violators, const Corner *corner, const MinMax *min_max) { checkCapacitanceLimitPreamble(); return check_capacitance_limits_->checkCapacitanceLimits(net, violators, corner, min_max); } void Sta::reportCapacitanceLimitShortHeader() { report_path_->reportLimitShortHeader(report_path_->fieldCapacitance()); } void Sta::reportCapacitanceLimitShort(Pin *pin, const Corner *corner, const MinMax *min_max) { const Corner *corner1; const RiseFall *rf; float capacitance, limit, slack; check_capacitance_limits_->checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); report_path_->reportLimitShort(report_path_->fieldCapacitance(), pin, capacitance, limit, slack); } void Sta::reportCapacitanceLimitVerbose(Pin *pin, const Corner *corner, const MinMax *min_max) { const Corner *corner1; const RiseFall *rf1; float capacitance1, limit1, slack1; check_capacitance_limits_->checkCapacitance(pin, corner, min_max, corner1, rf1, capacitance1, limit1, slack1); report_path_->reportLimitVerbose(report_path_->fieldCapacitance(), pin, rf1, capacitance1, limit1, slack1, corner1, min_max); } void Sta::checkCapacitance(const Pin *pin, const Corner *corner, const MinMax *min_max, // Return values. const Corner *&corner1, const RiseFall *&rf, float &capacitance, float &limit, float &slack) { check_capacitance_limits_->checkCapacitance(pin, corner, min_max, corner1, rf, capacitance, limit, slack); } void Sta::maxCapacitanceCheck(// Return values. const Pin *&pin, float &capacitance, float &slack, float &limit) { checkCapacitanceLimitPreamble(); PinSeq pins = check_capacitance_limits_->checkCapacitanceLimits(nullptr, false, nullptr, MinMax::max()); pin = nullptr; capacitance = 0.0; slack = INF; limit = INF; if (!pins.empty()) { pin = pins[0]; const Corner *corner; const RiseFall *rf; check_capacitance_limits_->checkCapacitance(pin, nullptr, MinMax::max(), corner, rf, capacitance, limit, slack); } } //////////////////////////////////////////////////////////////// void Sta::minPulseWidthPreamble() { ensureClkArrivals(); if (check_min_pulse_widths_ == nullptr) makeCheckMinPulseWidths(); } MinPulseWidthCheckSeq & Sta::minPulseWidthChecks(PinSeq *pins, const Corner *corner) { minPulseWidthPreamble(); return check_min_pulse_widths_->check(pins, corner); } MinPulseWidthCheckSeq & Sta::minPulseWidthChecks(const Corner *corner) { minPulseWidthPreamble(); return check_min_pulse_widths_->check(corner); } MinPulseWidthCheckSeq & Sta::minPulseWidthViolations(const Corner *corner) { minPulseWidthPreamble(); return check_min_pulse_widths_->violations(corner); } MinPulseWidthCheck * Sta::minPulseWidthSlack(const Corner *corner) { minPulseWidthPreamble(); return check_min_pulse_widths_->minSlackCheck(corner); } void Sta::reportMpwChecks(MinPulseWidthCheckSeq *checks, bool verbose) { report_path_->reportMpwChecks(checks, verbose); } void Sta::reportMpwCheck(MinPulseWidthCheck *check, bool verbose) { report_path_->reportMpwCheck(check, verbose); } //////////////////////////////////////////////////////////////// MinPeriodCheckSeq & Sta::minPeriodViolations() { minPeriodPreamble(); const Corner *corner = cmdCorner(); return check_min_periods_->violations(corner); } MinPeriodCheck * Sta::minPeriodSlack() { minPeriodPreamble(); const Corner *corner = cmdCorner(); return check_min_periods_->minSlackCheck(corner); } void Sta::minPeriodPreamble() { // Need clk arrivals to know what clks arrive at the clk tree endpoints. ensureClkArrivals(); if (check_min_periods_ == nullptr) makeCheckMinPeriods(); } void Sta::reportChecks(MinPeriodCheckSeq *checks, bool verbose) { report_path_->reportChecks(checks, verbose); } void Sta::reportCheck(MinPeriodCheck *check, bool verbose) { report_path_->reportCheck(check, verbose); } //////////////////////////////////////////////////////////////// MaxSkewCheckSeq & Sta::maxSkewViolations() { maxSkewPreamble(); return check_max_skews_->violations(); } MaxSkewCheck * Sta::maxSkewSlack() { maxSkewPreamble(); return check_max_skews_->minSlackCheck(); } void Sta::maxSkewPreamble() { ensureClkArrivals(); if (check_max_skews_ == nullptr) makeCheckMaxSkews(); } void Sta::reportChecks(MaxSkewCheckSeq *checks, bool verbose) { report_path_->reportChecks(checks, verbose); } void Sta::reportCheck(MaxSkewCheck *check, bool verbose) { report_path_->reportCheck(check, verbose); } //////////////////////////////////////////////////////////////// void Sta::makeEquivCells(LibertyLibrarySeq *equiv_libs, LibertyLibrarySeq *map_libs) { delete equiv_cells_; equiv_cells_ = new EquivCells(equiv_libs, map_libs); } LibertyCellSeq * Sta::equivCells(LibertyCell *cell) { if (equiv_cells_) return equiv_cells_->equivs(cell); else return nullptr; } //////////////////////////////////////////////////////////////// void Sta::writeTimingModel(const char *lib_name, const char *cell_name, const char *filename, const Corner *corner) { ensureLibLinked(); ensureGraph(); LibertyLibrary *library = makeTimingModel(lib_name, cell_name, filename, corner, this); writeLiberty(library, filename, this); } //////////////////////////////////////////////////////////////// void Sta::powerPreamble() { ensureLibLinked(); // Use arrivals to find clocking info. searchPreamble(); search_->findAllArrivals(); ensureClkNetwork(); } void Sta::power(const Corner *corner, // Return values. PowerResult &total, PowerResult &sequential, PowerResult &combinational, PowerResult &clock, PowerResult ¯o, PowerResult &pad) { powerPreamble(); power_->power(corner, total, sequential, combinational, clock, macro, pad); } PowerResult Sta::power(const Instance *inst, const Corner *corner) { powerPreamble(); return power_->power(inst, corner); } PwrActivity Sta::activity(const Pin *pin) { powerPreamble(); return power_->pinActivity(pin); } //////////////////////////////////////////////////////////////// void Sta::writePathSpice(Path *path, const char *spice_filename, const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, const char *power_name, const char *gnd_name, CircuitSim ckt_sim) { ensureLibLinked(); sta::writePathSpice(path, spice_filename, subckt_filename, lib_subckt_filename, model_filename, power_name, gnd_name, ckt_sim, this); } //////////////////////////////////////////////////////////////// void Sta::ensureClkNetwork() { ensureLevelized(); clk_network_->ensureClkNetwork(); } bool Sta::isClock(const Pin *pin) const { return clk_network_->isClock(pin); } bool Sta::isClock(const Net *net) const { return clk_network_->isClock(net); } bool Sta::isIdealClock(const Pin *pin) const { return clk_network_->isIdealClock(pin); } bool Sta::isPropagatedClock(const Pin *pin) const { return clk_network_->isPropagatedClock(pin); } const PinSet * Sta::pins(const Clock *clk) { return clk_network_->pins(clk); } void Sta::clkPinsInvalid() { clk_network_->clkPinsInvalid(); } } // namespace