diff --git a/CMakeLists.txt b/CMakeLists.txt index baa4e582..52ef2661 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -586,16 +586,22 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sta DESTINATION include) ################################################################ -add_custom_target(sta_tags etags -o TAGS +add_custom_target(sta_tags + COMMAND etags -o TAGS ${STA_SOURCE} */*.hh include/sta/*.hh ${CMAKE_CURRENT_BINARY_DIR}/include/sta/*.hh ${SWIG_FILES} + ${SWIG_FILES} + + # Append tcl tags. + # Note that the regexp used for tcl files is broken in etags so provide one that works. + COMMAND etags -a -o TAGS --language=none + --regex='/^[ \\t]*proc[ \\t]+[a-zA-Z0-9_]+/' ${STA_TCL_FILES} - ${SWIG_TCL_FILES} WORKING_DIRECTORY ${STA_HOME} - ) +) add_custom_command( TARGET OpenSTA diff --git a/README.md b/README.md index 44f5b53e..2da00a84 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,16 @@ Set these variables before using cmake to cirumvent the Xcode versions. Homebrew does not support tclreadline, but the macports system does (see https://www.macports.org). +## Install using a package manager + +### Guix + +OpenSTA is available in the [default repositories](https://hpc.guix.info/package/opensta): + +``` + guix install opensta +``` + ## Bug Reports Use the Issues tab on the github repository to report bugs. diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 14466228..531e1eeb 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -297,7 +297,7 @@ GraphDelayCalc::seedInvalidDelays() void GraphDelayCalc::seedRootSlews() { - for (Vertex *vertex : *levelize_->roots()) + for (Vertex *vertex : levelize_->roots()) seedRootSlew(vertex, arc_delay_calc_); } @@ -1597,28 +1597,39 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, void GraphDelayCalc::minPeriod(const Pin *pin, + const Corner *corner, // Return values. float &min_period, bool &exists) { exists = false; const MinMax *min_max = MinMax::max(); - for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) { - // Sdf annotation. - float min_period1 = 0.0; - bool exists1 = false; - graph_->periodCheckAnnotation(pin, dcalc_ap->index(), - min_period, exists); - if (exists1 - && (!exists || min_period1 < min_period)) { - min_period = min_period1; + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + // Sdf annotation. + float min_period1 = 0.0; + bool exists1 = false; + graph_->periodCheckAnnotation(pin, dcalc_ap->index(), + min_period, exists); + if (exists1 + && (!exists || min_period1 < min_period)) { + min_period = min_period1; + exists = true; + } + if (!exists) { + // Liberty timing group timing_type minimum_period. + Vertex *vertex = graph_->pinLoadVertex(pin); + Edge *edge; + TimingArc *arc; + graph_->minPeriodArc(vertex, RiseFall::rise(), edge, arc); + if (edge) { exists = true; + min_period = delayAsFloat(graph_->arcDelay(edge, arc, dcalc_ap->index())); } } if (!exists) { + // Liberty port min_period attribute. LibertyPort *port = network_->libertyPort(pin); if (port) { - // Liberty library. Instance *inst = network_->instance(pin); OperatingConditions *op_cond = sdc_->operatingConditions(min_max); const Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr; diff --git a/graph/Graph.cc b/graph/Graph.cc index b3bb0260..fc134d29 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -871,6 +871,30 @@ Graph::minPulseWidthArc(Vertex *vertex, arc = nullptr; } +void +Graph::minPeriodArc(Vertex *vertex, + const RiseFall *rf, + // Return values. + Edge *&edge, + TimingArc *&arc) +{ + VertexOutEdgeIterator edge_iter(vertex, this); + while (edge_iter.hasNext()) { + edge = edge_iter.next(); + TimingArcSet *arc_set = edge->timingArcSet(); + if (arc_set->role() == TimingRole::period()) { + for (TimingArc *arc1 : arc_set->arcs()) { + if (arc1->fromEdge()->asRiseFall() == rf) { + arc = arc1; + return; + } + } + } + } + edge = nullptr; + arc = nullptr; +} + //////////////////////////////////////////////////////////////// void diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 34e3e122..1e173361 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -171,6 +171,11 @@ public: // Return values. Edge *&edge, TimingArc *&arc); + void minPeriodArc(Vertex *vertex, + const RiseFall *rf, + // Return values. + Edge *&edge, + TimingArc *&arc); // Sdf period check annotation. void periodCheckAnnotation(const Pin *pin, DcalcAPIndex ap_index, diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 216ebe03..b38e6654 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -114,8 +114,10 @@ public: ArcDelayCalc *arc_delay_calc); // Precedence: // SDF annotation - // Liberty library + // Liberty port timing group timing_type minimum_period. + // Liberty port min_period attribute. void minPeriod(const Pin *pin, + const Corner *corner, // Return values. float &min_period, bool &exists); diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index f642f0f3..7fe410bb 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -26,6 +26,7 @@ #include #include +#include #include "MinMax.hh" #include "RiseFallMinMax.hh" @@ -873,6 +874,16 @@ protected: void addScaledPort(OperatingConditions *op_cond, LibertyPort *scaled_port); RiseFallMinMax clkTreeDelays1() const; + void setMemberFlag(bool value, + const std::function &setter); + void setMemberFloat(float value, + const std::function &setter); + void setMemberMinMaxFloat(float value, + const MinMax *min_max, + const std::function &setter); + LibertyCell *liberty_cell_; BusDcl *bus_dcl_; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 5a05c424..c87093fb 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -994,6 +994,14 @@ LibertyCell::findLibertyPortsMatching(PatternMatch *pattern) const LibertyPort *port = port_iter.next(); if (pattern->match(port->name())) matches.push_back(port); + if (port->hasMembers()) { + LibertyPortMemberIterator port_iter2(port); + while (port_iter2.hasNext()) { + LibertyPort *port2 = port_iter2.next(); + if (pattern->match(port2->name())) + matches.push_back(port2); + } + } } return matches; } @@ -2347,7 +2355,7 @@ void LibertyPort::setFunction(FuncExpr *func) { function_ = func; - if (is_bus_ || is_bundle_) { + if (hasMembers()) { LibertyPortMemberIterator member_iter(this); int bit_offset = 0; while (member_iter.hasNext()) { @@ -2388,6 +2396,7 @@ LibertyPort::setSlewLimit(float slew, const MinMax *min_max) { slew_limit_.setValue(min_max, slew); + setMemberMinMaxFloat(slew, min_max, &LibertyPort::setSlewLimit); } void @@ -2404,6 +2413,7 @@ LibertyPort::setCapacitanceLimit(float cap, const MinMax *min_max) { cap_limit_.setValue(min_max, cap); + setMemberMinMaxFloat(cap, min_max, &LibertyPort::setCapacitanceLimit); } void @@ -2420,6 +2430,7 @@ LibertyPort::setFanoutLoad(float fanout_load) { fanout_load_ = fanout_load; fanout_load_exists_ = true; + setMemberFloat(fanout_load, &LibertyPort::setFanoutLoad); } void @@ -2470,6 +2481,13 @@ LibertyPort::setMinPeriod(float min_period) { min_period_ = min_period; min_period_exists_ = true; + if (hasMembers()) { + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + port_bit->setMinPeriod(min_period); + } + } } void @@ -2489,6 +2507,13 @@ LibertyPort::setMinPulseWidth(const RiseFall *hi_low, int hi_low_index = hi_low->index(); min_pulse_width_[hi_low_index] = min_width; min_pulse_width_exists_ |= (1 << hi_low_index); + if (hasMembers()) { + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + port_bit->setMinPulseWidth(hi_low, min_width); + } + } } bool @@ -2538,24 +2563,28 @@ void LibertyPort::setIsClock(bool is_clk) { is_clk_ = is_clk; + setMemberFlag(is_clk, &LibertyPort::setIsClock); } void LibertyPort::setIsRegClk(bool is_clk) { is_reg_clk_ = is_clk; + setMemberFlag(is_clk, &LibertyPort::setIsRegClk); } void LibertyPort::setIsRegOutput(bool is_reg_out) { is_reg_output_ = is_reg_out; + setMemberFlag(is_reg_out, &LibertyPort::setIsRegOutput); } void LibertyPort::setIsLatchData(bool is_latch_data) { is_latch_data_ = is_latch_data; + setMemberFlag(is_latch_data, &LibertyPort::setIsLatchData); } void @@ -2592,24 +2621,28 @@ void LibertyPort::setIsolationCellData(bool isolation_cell_data) { isolation_cell_data_ = isolation_cell_data; + setMemberFlag(isolation_cell_data, &LibertyPort::setIsolationCellData); } void LibertyPort::setIsolationCellEnable(bool isolation_cell_enable) { isolation_cell_enable_ = isolation_cell_enable; + setMemberFlag(isolation_cell_enable, &LibertyPort::setIsolationCellEnable); } void LibertyPort::setLevelShifterData(bool level_shifter_data) { level_shifter_data_ = level_shifter_data; + setMemberFlag(level_shifter_data, &LibertyPort::setLevelShifterData); } void LibertyPort::setIsSwitch(bool is_switch) { is_switch_ = is_switch; + setMemberFlag(is_switch, &LibertyPort::setIsSwitch); } void @@ -2821,6 +2854,50 @@ LibertyPort::setClkTreeDelay(const TableModel *model, clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()] = model; } +void +LibertyPort::setMemberFlag(bool value, + const std::function &setter) +{ + if (hasMembers()) { + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + setter(port_bit, value); + } + } +} + +void +LibertyPort::setMemberFloat(float value, + const std::function &setter) +{ + if (hasMembers()) { + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + setter(port_bit, value); + } + } +} + +void +LibertyPort::setMemberMinMaxFloat(float value, + const MinMax *min_max, + const std::function &setter) +{ + if (hasMembers()) { + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + setter(port_bit, value, min_max); + } + } +} + //////////////////////////////////////////////////////////////// LibertyPortSeq diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index 0103d3f5..d4ac84a1 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -65,6 +65,8 @@ proc_redirect report_lib_cell { } proc report_lib_cell_ { cell corner } { + global sta_report_default_digits + set lib [$cell liberty_library] report_line "Cell [get_name $cell]" report_line "Library [get_name $lib]" diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc index e5184345..46bde91a 100644 --- a/liberty/LibertyBuilder.cc +++ b/liberty/LibertyBuilder.cc @@ -289,6 +289,8 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell, return makeMinPulseWidthArcs(cell, from_port, to_port, related_out, TimingRole::width(), attrs); case TimingType::minimum_period: + return makeMinPulseWidthArcs(cell, from_port, to_port, related_out, + TimingRole::period(), attrs); case TimingType::nochange_high_high: case TimingType::nochange_high_low: case TimingType::nochange_low_high: diff --git a/liberty/LibertyLex.ll b/liberty/LibertyLex.ll index 1bac5e9f..ccf2aea5 100644 --- a/liberty/LibertyLex.ll +++ b/liberty/LibertyLex.ll @@ -78,7 +78,7 @@ TOKEN ({ALPHA}|{DIGIT}|_)({ALPHA}|{DIGIT}|[._\-])*(:({ALPHA}|{DIGIT}|_)+)? /* bus_naming_style : %s[%d] ; */ BUS_STYLE "%s"{BUS_LEFT}"%d"{BUS_RIGHT} PUNCTUATION [,\:;|(){}+*&!'=] -TOKEN_END {PUNCTUATION}|[ \t\r\n] +TOKEN_END {PUNCTUATION}|[ \t\r\n]|\\{EOL} EOL \r?\n %% diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index bcf8712b..c921ffd8 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -4251,6 +4251,7 @@ LibertyReader::endTiming(LibertyGroup *group) TimingType timing_type = timing_->attrs()->timingType(); if (timing_->relatedPortNames() == nullptr && !(timing_type == TimingType::min_pulse_width + || timing_type == TimingType::minimum_period || timing_type == TimingType::min_clock_tree_path || timing_type == TimingType::max_clock_tree_path)) libWarn(1243, group, "timing group missing related_pin/related_bus_pin."); diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 0d71d4ec..f36a5470 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -820,6 +820,20 @@ ConcreteParasitics::deleteParasitics(const Pin *drvr_pin, } } +void +ConcreteParasitics::deleteParasitics(const Pin *drvr_pin) +{ + ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; + if (parasitics) { + int ap_count = corners_->parasiticAnalysisPtCount(); + int ap_rf_count = ap_count * RiseFall::index_count; + for (int i = 0; i < ap_rf_count; i++) { + delete parasitics[i]; + parasitics[i] = nullptr; + } + } +} + void ConcreteParasitics::deleteParasitics(const Net *net, const ParasiticAnalysisPt *ap) @@ -1244,7 +1258,7 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net, delete parasitic; if (net) { for (const Pin *drvr_pin : *network_->drivers(net)) - deleteParasitics(drvr_pin, ap); + deleteParasitics(drvr_pin); } } parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_); diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 0c0b91af..ae0f9bc9 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -53,6 +53,7 @@ public: const ParasiticAnalysisPt *ap) override; void deleteParasitics(const Pin *drvr_pin, const ParasiticAnalysisPt *ap) override; + void deleteParasitics(const Pin *drvr_pin); bool isReducedParasiticNetwork(const Parasitic *parasitic) const override; void setIsReducedParasiticNetwork(Parasitic *parasitic, diff --git a/parasitics/Parasitics.tcl b/parasitics/Parasitics.tcl index f0c2aa5f..15e8e81d 100644 --- a/parasitics/Parasitics.tcl +++ b/parasitics/Parasitics.tcl @@ -38,30 +38,11 @@ define_cmd_args "read_spef" \ proc_redirect read_spef { parse_key_args "read_spef" args \ - keys {-path -coupling_reduction_factor -reduce_to -corner} \ - flags {-min -max -increment -pin_cap_included -keep_capacitive_coupling \ - -reduce -delete_after_reduce -quiet -save} + keys {-path -coupling_reduction_factor -corner -name} \ + flags {-min -max -increment -pin_cap_included -keep_capacitive_coupling -reduce} check_argc_eq1 "read_spef" $args set reduce [info exists flags(-reduce)] - if { [info exists flags(-quiet)] } { - # deprecated 2024-02-08 - sta_warn 272 "read_spef -quiet is deprecated." - } - if { [info exists keys(-reduce_to)] } { - # deprecated 2024-02-08 - sta_warn 273 "read_spef -reduce_to is deprecated. Use -reduce instead." - set reduce 1 - } - if { [info exists flags(-delete_after_reduce)] } { - # deprecated 2024-02-08 - sta_warn 274 "read_spef -delete_after_reduce is deprecated." - } - if { [info exists flags(-save)] } { - # deprecated 2024-02-08 - sta_warn 275 "read_spef -save is deprecated." - } - set instance [top_instance] if [info exists keys(-path)] { set path $keys(-path) diff --git a/power/Power.cc b/power/Power.cc index 25d1cb2d..dd2210c1 100644 --- a/power/Power.cc +++ b/power/Power.cc @@ -104,6 +104,17 @@ Power::Power(StaState *sta) : { } +void +Power::clear() +{ + global_activity_.init(); + input_activity_.init(); + user_activity_map_.clear(); + seq_activity_map_.clear(); + activity_map_.clear(); + activities_valid_ = false; +} + void Power::setGlobalActivity(float density, float duty) @@ -737,7 +748,7 @@ Power::ensureActivities() void Power::seedActivities(BfsFwdIterator &bfs) { - for (Vertex *vertex : *levelize_->roots()) { + for (Vertex *vertex : levelize_->roots()) { const Pin *pin = vertex->pin(); // Clock activities are baked in. if (!sdc_->isLeafPinClock(pin) diff --git a/power/Power.hh b/power/Power.hh index f4fde251..18bc3ab9 100644 --- a/power/Power.hh +++ b/power/Power.hh @@ -75,6 +75,7 @@ class Power : public StaState { public: Power(StaState *sta); + void clear(); void power(const Corner *corner, // Return values. PowerResult &total, diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index b5616f38..7c846cd6 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -2909,6 +2909,9 @@ proc set_load { args } { parse_port_net_args [lindex $args 1] ports nets if { $ports != {} } { + if { $subtract_pin_load } { + sta_warn 486 "-subtract_pin_load not allowed for port objects." + } # -pin_load is the default. if { $pin_load || (!$pin_load && !$wire_load) } { foreach port $ports { @@ -2916,7 +2919,7 @@ proc set_load { args } { } } elseif { $wire_load } { foreach port $ports { - set_port_ext_wire_cap $port $subtract_pin_load $rf $corner $min_max $cap + set_port_ext_wire_cap $port 0 $rf $corner $min_max $cap } } } diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc index 043290e4..81b64396 100644 --- a/sdf/SdfWriter.cc +++ b/sdf/SdfWriter.cc @@ -546,7 +546,7 @@ SdfWriter::writeTimingChecks(const Instance *inst, } float min_period; bool exists; - graph_delay_calc_->minPeriod(pin, min_period, exists); + graph_delay_calc_->minPeriod(pin, corner_, min_period, exists); if (exists) { ensureTimingCheckheaders(check_header, inst, inst_header); writePeriodCheck(pin, min_period); diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc index 7e3a14c9..06358382 100644 --- a/search/CheckMinPeriods.cc +++ b/search/CheckMinPeriods.cc @@ -39,12 +39,21 @@ namespace sta { class MinPeriodCheckVisitor { public: - MinPeriodCheckVisitor() {} + MinPeriodCheckVisitor(const Corner *corner); virtual ~MinPeriodCheckVisitor() {} virtual void visit(MinPeriodCheck &check, StaState *sta) = 0; + const Corner *corner() { return corner_; } + +protected: + const Corner *corner_; }; +MinPeriodCheckVisitor::MinPeriodCheckVisitor(const Corner *corner) : + corner_(corner) +{ +} + CheckMinPeriods::CheckMinPeriods(StaState *sta) : sta_(sta) { @@ -64,7 +73,8 @@ CheckMinPeriods::clear() class MinPeriodViolatorsVisitor : public MinPeriodCheckVisitor { public: - MinPeriodViolatorsVisitor(MinPeriodCheckSeq &checks); + MinPeriodViolatorsVisitor(const Corner *corner, + MinPeriodCheckSeq &checks); virtual void visit(MinPeriodCheck &check, StaState *sta); @@ -72,7 +82,9 @@ private: MinPeriodCheckSeq &checks_; }; -MinPeriodViolatorsVisitor::MinPeriodViolatorsVisitor(MinPeriodCheckSeq &checks): +MinPeriodViolatorsVisitor::MinPeriodViolatorsVisitor(const Corner *corner, + MinPeriodCheckSeq &checks): + MinPeriodCheckVisitor(corner), checks_(checks) { } @@ -86,10 +98,10 @@ MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check, } MinPeriodCheckSeq & -CheckMinPeriods::violations() +CheckMinPeriods::violations(const Corner *corner) { clear(); - MinPeriodViolatorsVisitor visitor(checks_); + MinPeriodViolatorsVisitor visitor(corner, checks_); visitMinPeriodChecks(&visitor); sort(checks_, MinPeriodSlackLess(sta_)); return checks_; @@ -113,16 +125,17 @@ CheckMinPeriods::visitMinPeriodChecks(Vertex *vertex, { Search *search = sta_->search(); GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc(); + const Corner *corner = visitor->corner(); Pin *pin = vertex->pin(); float min_period; bool exists; - graph_dcalc->minPeriod(pin, min_period, exists); + graph_dcalc->minPeriod(pin, corner, min_period, exists); if (exists) { const ClockSet clks = search->clocks(vertex); ClockSet::ConstIterator clk_iter(clks); while (clk_iter.hasNext()) { Clock *clk = clk_iter.next(); - MinPeriodCheck check(pin, clk); + MinPeriodCheck check(pin, clk, corner); visitor->visit(check, sta_); } } @@ -133,16 +146,17 @@ CheckMinPeriods::visitMinPeriodChecks(Vertex *vertex, class MinPeriodSlackVisitor : public MinPeriodCheckVisitor { public: - MinPeriodSlackVisitor(); - virtual void visit(MinPeriodCheck &check, - StaState *sta); + MinPeriodSlackVisitor(const Corner *corner); + void visit(MinPeriodCheck &check, + StaState *sta) override; MinPeriodCheck *minSlackCheck(); private: MinPeriodCheck *min_slack_check_; }; -MinPeriodSlackVisitor::MinPeriodSlackVisitor() : +MinPeriodSlackVisitor::MinPeriodSlackVisitor(const Corner *corner) : + MinPeriodCheckVisitor(corner), min_slack_check_(nullptr) { } @@ -167,10 +181,10 @@ MinPeriodSlackVisitor::minSlackCheck() } MinPeriodCheck * -CheckMinPeriods::minSlackCheck() +CheckMinPeriods::minSlackCheck(const Corner *corner) { clear(); - MinPeriodSlackVisitor visitor; + MinPeriodSlackVisitor visitor(corner); visitMinPeriodChecks(&visitor); MinPeriodCheck *check = visitor.minSlackCheck(); // Save check for cleanup. @@ -181,16 +195,18 @@ CheckMinPeriods::minSlackCheck() //////////////////////////////////////////////////////////////// MinPeriodCheck::MinPeriodCheck(Pin *pin, - Clock *clk) : + Clock *clk, + const Corner *corner) : pin_(pin), - clk_(clk) + clk_(clk), + corner_(corner) { } MinPeriodCheck * MinPeriodCheck::copy() { - return new MinPeriodCheck(pin_, clk_); + return new MinPeriodCheck(pin_, clk_, corner_); } float @@ -205,7 +221,7 @@ MinPeriodCheck::minPeriod(const StaState *sta) const GraphDelayCalc *graph_dcalc = sta->graphDelayCalc(); float min_period; bool exists; - graph_dcalc->minPeriod(pin_, min_period, exists); + graph_dcalc->minPeriod(pin_, corner_, min_period, exists); return min_period; } diff --git a/search/CheckMinPeriods.hh b/search/CheckMinPeriods.hh index 871cec50..bcba3218 100644 --- a/search/CheckMinPeriods.hh +++ b/search/CheckMinPeriods.hh @@ -38,12 +38,12 @@ class MinPeriodCheckVisitor; class CheckMinPeriods { public: - explicit CheckMinPeriods(StaState *sta); + CheckMinPeriods(StaState *sta); ~CheckMinPeriods(); void clear(); - MinPeriodCheckSeq &violations(); + MinPeriodCheckSeq &violations(const Corner *corner); // Min period check with the least slack. - MinPeriodCheck *minSlackCheck(); + MinPeriodCheck *minSlackCheck(const Corner *corner); protected: void visitMinPeriodChecks(MinPeriodCheckVisitor *visitor); @@ -57,7 +57,9 @@ protected: class MinPeriodCheck { public: - MinPeriodCheck(Pin *pin, Clock *clk); + MinPeriodCheck(Pin *pin, + Clock *clk, + const Corner *corner); MinPeriodCheck *copy(); Pin *pin() const { return pin_; } Clock *clk() const { return clk_; } @@ -68,17 +70,18 @@ public: private: Pin *pin_; Clock *clk_; + const Corner *corner_; }; class MinPeriodSlackLess { public: - explicit MinPeriodSlackLess(StaState *sta); + MinPeriodSlackLess(StaState *sta); bool operator()(const MinPeriodCheck *check1, const MinPeriodCheck *check2) const; private: - const StaState *sta_; + const StaState *sta_; }; } // namespace diff --git a/search/CheckMinPulseWidths.hh b/search/CheckMinPulseWidths.hh index 8c6abeac..d250cc20 100644 --- a/search/CheckMinPulseWidths.hh +++ b/search/CheckMinPulseWidths.hh @@ -38,7 +38,7 @@ class MinPulseWidthCheckVisitor; class CheckMinPulseWidths { public: - explicit CheckMinPulseWidths(StaState *sta); + CheckMinPulseWidths(StaState *sta); ~CheckMinPulseWidths(); void clear(); // Min pulse width checks for pins. @@ -96,7 +96,7 @@ protected: class MinPulseWidthSlackLess { public: - explicit MinPulseWidthSlackLess(const StaState *sta); + MinPulseWidthSlackLess(const StaState *sta); bool operator()(const MinPulseWidthCheck *check1, const MinPulseWidthCheck *check2) const; diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index 84ca3feb..4ff5ee24 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -230,14 +230,14 @@ void CheckTiming::checkUnconstrainedEndpoints() { PinSet unconstrained_ends(network_); - checkUnconstraintedOutputs(unconstrained_ends); + checkUnconstrainedOutputs(unconstrained_ends); checkUnconstrainedSetups(unconstrained_ends); pushPinErrors("Warning: There %is %d unconstrained endpoint%s.", unconstrained_ends); } void -CheckTiming::checkUnconstraintedOutputs(PinSet &unconstrained_ends) +CheckTiming::checkUnconstrainedOutputs(PinSet &unconstrained_ends) { Instance *top_inst = network_->topInstance(); InstancePinIterator *pin_iter = network_->pinIterator(top_inst); diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh index 205ad2da..1bbc6dd1 100644 --- a/search/CheckTiming.hh +++ b/search/CheckTiming.hh @@ -59,7 +59,7 @@ protected: void checkUnconstrainedEndpoints(); bool hasClkedArrival(Vertex *vertex); void checkNoOutputDelay(PinSet &ends); - void checkUnconstraintedOutputs(PinSet &unconstrained_ends); + void checkUnconstrainedOutputs(PinSet &unconstrained_ends); void checkUnconstrainedSetups(PinSet &unconstrained_ends); void checkLoops(); bool hasClkedDepature(Pin *pin); diff --git a/search/Corner.cc b/search/Corner.cc index 193e173b..19a7e5df 100644 --- a/search/Corner.cc +++ b/search/Corner.cc @@ -197,7 +197,8 @@ Corners::makeDcalcAnalysisPts(Corner *corner) switch (sdc_->analysisType()) { case AnalysisType::single: corner->setDcalcAnalysisPtcount(1); - makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min()); + max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min()); + max_ap->setCheckClkSlewIndex(max_ap->index()); break; case AnalysisType::bc_wc: corner->setDcalcAnalysisPtcount(2); diff --git a/search/Levelize.cc b/search/Levelize.cc index 79f54066..7adef739 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -51,16 +51,15 @@ Levelize::Levelize(StaState *sta) : levels_valid_(false), max_level_(0), level_space_(10), - roots_(new VertexSet(graph_)), - relevelize_from_(new VertexSet(graph_)), + max_incremental_level_(100), + roots_(graph_), + relevelize_from_(graph_), observer_(nullptr) { } Levelize::~Levelize() { - delete roots_; - delete relevelize_from_; delete observer_; loops_.deleteContents(); } @@ -83,8 +82,8 @@ Levelize::clear() { levelized_ = false; levels_valid_ = false; - roots_->clear(); - relevelize_from_->clear(); + roots_.clear(); + relevelize_from_.clear(); clearLoopEdges(); loops_.deleteContentsClear(); loop_edges_.clear(); @@ -106,7 +105,8 @@ void Levelize::ensureLevelized() { if (!levels_valid_) { - if (levelized_) + if (levelized_ + && relevelize_from_.size() < max_incremental_level_) relevelize(); else levelize(); @@ -151,6 +151,7 @@ Levelize::levelize() vertex->setVisited(false); vertex->setOnPath(false); } + relevelize_from_.clear(); levelized_ = true; levels_valid_ = true; stats.report("Levelize"); @@ -159,7 +160,7 @@ Levelize::levelize() void Levelize::findRoots() { - roots_->clear(); + roots_.clear(); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); @@ -167,17 +168,17 @@ Levelize::findRoots() debugPrint(debug_, "levelize", 2, "root %s%s", vertex->to_string(this).c_str(), hasFanout(vertex) ? " fanout" : ""); - roots_->insert(vertex); + roots_.insert(vertex); } } if (debug_->check("levelize", 1)) { size_t fanout_roots = 0; - for (Vertex *root : *roots_) { + for (Vertex *root : roots_) { if (hasFanout(root)) fanout_roots++; } debugPrint(debug_, "levelize", 1, "Found %zu roots %zu with fanout", - roots_->size(), + roots_.size(), fanout_roots); } } @@ -251,7 +252,7 @@ VertexSeq Levelize::sortedRootsWithFanout() { VertexSeq roots; - for (Vertex *root : *roots_) { + for (Vertex *root : roots_) { if (hasFanout(root)) roots.push_back(root); } @@ -322,7 +323,7 @@ Levelize::findCycleBackEdges() stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_)); EdgeSet back_edges = findBackEdges(path, stack); for (Edge *back_edge : back_edges) - roots_->insert(back_edge->from(graph_)); + roots_.insert(back_edge->from(graph_)); back_edge_count += back_edges.size(); } } @@ -378,7 +379,7 @@ Levelize::findTopologicalOrder() } std::deque queue; - for (Vertex *root : *roots_) + for (Vertex *root : roots_) queue.push_back(root); VertexSeq topo_order; @@ -501,10 +502,11 @@ Levelize::reportPath(EdgeSeq &path) const void Levelize::assignLevels(VertexSeq &topo_sorted) { - for (Vertex *root : *roots_) + for (Vertex *root : roots_) setLevel(root, 0); for (Vertex *vertex : topo_sorted) { - if (vertex->level() != -1) { + if (vertex->level() != -1 + && search_pred_.searchFrom(vertex)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -557,7 +559,7 @@ Levelize::setLevel(Vertex *vertex, vertex->setLevel(level); max_level_ = max(level, max_level_); if (level >= Graph::vertex_level_max) - criticalError(616, "maximum logic level exceeded"); + report_->critical(616, "maximum logic level exceeded"); } void @@ -580,9 +582,9 @@ Levelize::invalidFrom(Vertex *vertex) while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *from_vertex = edge->from(graph_); - relevelize_from_->insert(from_vertex); + relevelize_from_.insert(from_vertex); } - relevelize_from_->insert(vertex); + relevelize_from_.insert(vertex); levels_valid_ = false; } } @@ -591,8 +593,8 @@ void Levelize::deleteVertexBefore(Vertex *vertex) { if (levelized_) { - roots_->erase(vertex); - relevelize_from_->erase(vertex); + roots_.erase(vertex); + relevelize_from_.erase(vertex); } } @@ -600,9 +602,9 @@ void Levelize::relevelizeFrom(Vertex *vertex) { if (levelized_) { - debugPrint(debug_, "levelize", 1, "invalid relevelize from %s", + debugPrint(debug_, "levelize", 1, "relevelize from %s", vertex->to_string(this).c_str()); - relevelize_from_->insert(vertex); + relevelize_from_.insert(vertex); levels_valid_ = false; } } @@ -633,23 +635,22 @@ Levelize::deleteEdgeBefore(Edge *edge) void Levelize::relevelize() { - for (Vertex *vertex : *relevelize_from_) { + for (Vertex *vertex : relevelize_from_) { debugPrint(debug_, "levelize", 1, "relevelize from %s", vertex->to_string(this).c_str()); if (search_pred_.searchFrom(vertex)) { - if (isRoot(vertex)) { - setLevelIncr(vertex, 0); - roots_->insert(vertex); - } - VertexSet visited(graph_); + if (isRoot(vertex)) + roots_.insert(vertex); VertexSet path_vertices(graph_); EdgeSeq path; - visit(vertex, nullptr, vertex->level(), 1, visited, path_vertices, path); + visit(vertex, nullptr, vertex->level(), 1, path_vertices, path); } } ensureLatchLevels(); levels_valid_ = true; - relevelize_from_->clear(); + relevelize_from_.clear(); + + checkLevels(); } void @@ -657,14 +658,11 @@ Levelize::visit(Vertex *vertex, Edge *from, Level level, Level level_space, - VertexSet &visited, VertexSet &path_vertices, EdgeSeq &path) { Pin *from_pin = vertex->pin(); setLevelIncr(vertex, level); - level += level_space; - visited.insert(vertex); path_vertices.insert(vertex); if (from) path.push_back(from); @@ -679,9 +677,9 @@ Levelize::visit(Vertex *vertex, if (path_vertices.find(to_vertex) != path_vertices.end()) // Back edges form feedback loops. recordLoop(edge, path); - else if (visited.find(to_vertex) == visited.end() - && to_vertex->level() < level) - visit(to_vertex, edge, level, level_space, visited, path_vertices, path); + else if (to_vertex->level() <= level) + visit(to_vertex, edge, level+level_space, level_space, + path_vertices, path); } if (edge->role() == TimingRole::latchDtoQ()) latch_d_to_q_edges_.insert(edge); @@ -691,9 +689,9 @@ Levelize::visit(Vertex *vertex, && !vertex->isBidirectDriver()) { Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); if (search_pred_.searchTo(to_vertex) - && (visited.find(to_vertex) == visited.end() - || to_vertex->level() < level)) - visit(to_vertex, nullptr, level, level_space, visited, path_vertices, path); + && (to_vertex->level() <= level)) + visit(to_vertex, nullptr, level+level_space, level_space, + path_vertices, path); } } path_vertices.erase(vertex); @@ -724,6 +722,34 @@ Levelize::setLevelIncr(Vertex *vertex, criticalError(617, "maximum logic level exceeded"); } +void +Levelize::checkLevels() +{ + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (search_pred_.searchTo(vertex)) { + Level level = vertex->level(); + VertexInEdgeIterator edge_iter1(vertex, graph_); + while (edge_iter1.hasNext()) { + Edge *edge = edge_iter1.next(); + Vertex *from_vertex = edge->from(graph_); + Level from_level = from_vertex->level(); + if (search_pred_.searchFrom(from_vertex) + && search_pred_.searchThru(edge) + && from_level >= level + // Loops with no entry edges are all level zero. + && !(from_level == 0 && level == 0)) + report_->warn(617, "level check failed %s %d -> %s %d", + from_vertex->name(network_), + from_vertex->level(), + vertex->name(network_), + level); + } + } + } +} + //////////////////////////////////////////////////////////////// GraphLoop::GraphLoop(EdgeSeq *edges) : diff --git a/search/Levelize.hh b/search/Levelize.hh index 28a34603..68eece66 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -28,7 +28,7 @@ #include "NetworkClass.hh" #include "SdcClass.hh" -#include "GraphClass.hh" +#include "Graph.hh" #include "SearchPred.hh" #include "StaState.hh" @@ -58,7 +58,7 @@ public: void deleteEdgeBefore(Edge *edge); int maxLevel() const { return max_level_; } // Vertices with no fanin edges. - VertexSet *roots() { return roots_; } + VertexSet &roots() { return roots_; } bool isRoot(Vertex *vertex); bool hasFanout(Vertex *vertex); // Reset to virgin state. @@ -69,6 +69,7 @@ public: GraphLoopSeq &loops() { return loops_; } // Set the observer for level changes. void setObserver(LevelizeObserver *observer); + void checkLevels(); protected: void levelize(); @@ -91,7 +92,6 @@ protected: Edge *from, Level level, Level level_space, - VertexSet &visited, VertexSet &path_vertices, EdgeSeq &path); void setLevel(Vertex *vertex, @@ -107,8 +107,9 @@ protected: bool levels_valid_; Level max_level_; Level level_space_; - VertexSet *roots_; - VertexSet *relevelize_from_; + size_t max_incremental_level_; + VertexSet roots_; + VertexSet relevelize_from_; GraphLoopSeq loops_; EdgeSet loop_edges_; EdgeSet disabled_loop_edges_; diff --git a/search/Property.cc b/search/Property.cc index 3e5f740c..67aa3aac 100644 --- a/search/Property.cc +++ b/search/Property.cc @@ -872,6 +872,8 @@ Properties::getProperty(const LibertyPort *port, float cap = port->capacitance(RiseFall::rise(), MinMax::max()); return capacitancePropertyValue(cap); } + else if (property == "is_clock") + return PropertyValue(port->isClock()); else if (property == "is_register_clock") return PropertyValue(port->isRegClk()); @@ -989,6 +991,10 @@ Properties::getProperty(const Pin *pin, return PropertyValue(network->isHierarchical(pin)); else if (property == "is_port") return PropertyValue(network->isTopLevelPort(pin)); + else if (property == "is_clock") { + const LibertyPort *port = network->libertyPort(pin); + return PropertyValue(port->isClock()); + } else if (property == "is_register_clock") { const LibertyPort *port = network->libertyPort(pin); return PropertyValue(port && port->isRegClk()); diff --git a/search/Search.cc b/search/Search.cc index ffc28ccf..8677dab6 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1671,7 +1671,7 @@ Search::makeUnclkedPaths(Vertex *vertex, void Search::findRootVertices(VertexSet &vertices) { - for (Vertex *vertex : *levelize_->roots()) { + for (Vertex *vertex : levelize_->roots()) { const Pin *pin = vertex->pin(); if (!sdc_->isLeafPinClock(pin) && !sdc_->hasInputDelay(pin) diff --git a/search/Sta.cc b/search/Sta.cc index d86434af..b7c28bbc 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -586,6 +586,7 @@ Sta::clear() parasitics_->clear(); graph_delay_calc_->clear(); sim_->clear(); + power_->clear(); if (check_min_pulse_widths_) check_min_pulse_widths_->clear(); if (check_min_periods_) @@ -4407,7 +4408,7 @@ Sta::connectDrvrPinAfter(Vertex *vertex) graph_delay_calc_->delayInvalid(vertex); search_->requiredInvalid(vertex); search_->endpointInvalid(vertex); - levelize_->invalidFrom(vertex); + levelize_->relevelizeFrom(vertex); clk_network_->connectPinAfter(pin); } @@ -4422,11 +4423,11 @@ Sta::connectLoadPinAfter(Vertex *vertex) 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); - levelize_->invalidFrom(vertex); search_->arrivalInvalid(vertex); search_->endpointInvalid(vertex); clk_network_->connectPinAfter(pin); @@ -4584,6 +4585,7 @@ Sta::deletePinBefore(const Pin *pin) } levelize_->deleteEdgeBefore(edge); } + // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } @@ -4606,6 +4608,7 @@ Sta::deletePinBefore(const Pin *pin) } levelize_->deleteEdgeBefore(edge); } + // Deletes edges to/from vertex also. graph_->deleteVertex(vertex); } } @@ -5554,14 +5557,16 @@ MinPeriodCheckSeq & Sta::minPeriodViolations() { minPeriodPreamble(); - return check_min_periods_->violations(); + const Corner *corner = cmdCorner(); + return check_min_periods_->violations(corner); } MinPeriodCheck * Sta::minPeriodSlack() { minPeriodPreamble(); - return check_min_periods_->minSlackCheck(); + const Corner *corner = cmdCorner(); + return check_min_periods_->minSlackCheck(corner); } void diff --git a/spice/WriteSpice.cc b/spice/WriteSpice.cc index 4b61ebdd..6fddecf3 100644 --- a/spice/WriteSpice.cc +++ b/spice/WriteSpice.cc @@ -25,6 +25,7 @@ #include "spice/WriteSpice.hh" #include // swap +#include #include "Debug.hh" #include "Units.hh" @@ -127,18 +128,6 @@ WriteSpice::initPowerGnd() gnd_voltage_ = 0.0; } -// Use c++17 fs::path(filename).stem() -static string -filenameStem(const char *filename) -{ - string filename1 = filename; - const size_t last_slash_idx = filename1.find_last_of("\\/"); - if (last_slash_idx != std::string::npos) - return filename1.substr(last_slash_idx + 1); - else - return filename1; -} - void WriteSpice::writeHeader(string &title, float max_time, @@ -146,9 +135,9 @@ WriteSpice::writeHeader(string &title, { streamPrint(spice_stream_, "* %s\n", title.c_str()); streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_); - string subckt_filename_stem = filenameStem(subckt_filename_); - streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_stem.c_str()); - + std::filesystem::path subckt_filename + = std::filesystem::path(subckt_filename_).filename(); + streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename.c_str()); streamPrint(spice_stream_, ".tran %.3g %.3g\n", time_step, max_time); // Suppress printing model parameters. if (ckt_sim_ == CircuitSim::hspice) diff --git a/test/liberty_backslash_eol.lib b/test/liberty_backslash_eol.lib new file mode 100644 index 00000000..d79dbe30 --- /dev/null +++ b/test/liberty_backslash_eol.lib @@ -0,0 +1,51 @@ +library (liberty_backslash_eol) { + delay_model : "table_lookup"; + simulation : false; + capacitive_load_unit\ + (1,pF); + leakage_power_unit : "1pW"; + current_unit : "1A"; + pulling_resistance_unit : "1kohm"; + time_unit : "1ns"; + voltage_unit : "1v"; + library_features : "report_delay_calculation"; + input_threshold_pct_rise : 50; + input_threshold_pct_fall : 50; + output_threshold_pct_rise : 50; + output_threshold_pct_fall : 50; + slew_lower_threshold_pct_rise : 30; + slew_lower_threshold_pct_fall : 30; + slew_upper_threshold_pct_rise : 70; + slew_upper_threshold_pct_fall : 70; + slew_derate_from_library : 1.0; + nom_process : 1.0; + nom_temperature : 85.0; + nom_voltage : 0.75; + + cell (my_inv) { + pin (A) { + capacitance : 1; + direction : "input"; + } + pin (Y) { + function : "!A"; + direction : "output"; + timing () { + related_pin : "A"; + timing_sense : "negative_unate"; + cell_rise (scalar) { + values ("1"); + } + cell_fall (scalar) { + values ("1"); + } + rise_transition (scalar) { + values ("1"); + } + fall_transition (scalar) { + values ("1"); + } + } + } + } +} diff --git a/test/liberty_backslash_eol.ok b/test/liberty_backslash_eol.ok new file mode 100644 index 00000000..e69de29b diff --git a/test/liberty_backslash_eol.tcl b/test/liberty_backslash_eol.tcl new file mode 100644 index 00000000..01df1c62 --- /dev/null +++ b/test/liberty_backslash_eol.tcl @@ -0,0 +1,2 @@ +# backslash eol should be ignored in liberty file +read_liberty liberty_backslash_eol.lib diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index 2d0b5b0f..2d71a1ac 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -145,6 +145,7 @@ record_sta_tests { get_objrefs liberty_arcs_one2one_1 liberty_arcs_one2one_2 + liberty_backslash_eol liberty_ccsn liberty_latch3 path_group_names