diff --git a/CMakeLists.txt b/CMakeLists.txt index c42445b1..305de4bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,7 @@ set(STA_SOURCE sdc/DeratingFactors.cc sdc/DisabledPorts.cc sdc/ExceptionPath.cc + sdc/FilterObjects.cc sdc/InputDrive.cc sdc/PinPair.cc sdc/PortDelay.cc diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 577bce5c..6828aa67 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -44,7 +44,6 @@ #include "TimingArc.hh" #include "TableModel.hh" #include "Liberty.hh" -#include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" #include "ArcDelayCalc.hh" @@ -78,11 +77,11 @@ exp2(double x); class DmpError : public Exception { public: - DmpError(const char *what); - virtual const char *what() const noexcept { return what_; } + DmpError(std::string_view what); + virtual const char *what() const noexcept { return what_.c_str(); } private: - const char *what_; + std::string what_; }; static double @@ -139,9 +138,9 @@ public: double c2, double rpi, double c1); - virtual void gateDelaySlew( // Return values. - double &delay, - double &slew) = 0; + virtual void gateDelaySlew(// Return values. + double &delay, + double &slew) = 0; virtual void loadDelaySlew(const Pin *load_pin, double elmore, // Return values. @@ -685,9 +684,9 @@ public: double c2, double rpi, double c1) override; - void gateDelaySlew( // Return values. - double &delay, - double &slew) override; + void gateDelaySlew(// Return values. + double &delay, + double &slew) override; void loadDelaySlew(const Pin *, double elmore, // Return values. @@ -726,15 +725,15 @@ DmpCap::init(const LibertyLibrary *drvr_library, double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP cap"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, - c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); ceff_ = c1 + c2; } void -DmpCap::gateDelaySlew( // Return values. - double &delay, - double &slew) +DmpCap::gateDelaySlew(// Return values. + double &delay, + double &slew) { debugPrint(debug_, "dmp_ceff", 3, " ceff = {}", units_->capacitanceUnit()->asString(ceff_)); @@ -801,9 +800,9 @@ public: double c2, double rpi, double c1) override; - void gateDelaySlew( // Return values. - double &delay, - double &slew) override; + void gateDelaySlew(// Return values. + double &delay, + double &slew) override; void evalDmpEqns() override; double voCrossingUpperBound() override; @@ -868,8 +867,8 @@ DmpPi::init(const LibertyLibrary *drvr_library, double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP Pi"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, - c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); // Find poles/zeros. z1_ = 1.0 / (rpi_ * c1_); @@ -893,9 +892,9 @@ DmpPi::init(const LibertyLibrary *drvr_library, } void -DmpPi::gateDelaySlew( // Return values. - double &delay, - double &slew) +DmpPi::gateDelaySlew(// Return values. + double &delay, + double &slew) { driver_valid_ = false; try { @@ -1127,9 +1126,9 @@ public: double c2, double rpi, double c1) override; - void gateDelaySlew( // Return values. - double &delay, - double &slew) override; + void gateDelaySlew(// Return values. + double &delay, + double &slew) override; private: void V0(double t, @@ -1176,8 +1175,8 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library, double c1) { debugPrint(debug_, "dmp_ceff", 3, "Using DMP C2=0"); - DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, - c1); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); ceff_ = c1; z1_ = 1.0 / (rpi_ * c1_); @@ -1275,8 +1274,10 @@ newtonRaphson(const int max_iter, all_under_x_tol = false; x[i] += p[i]; } - if (all_under_x_tol) + if (all_under_x_tol) { + eval(); return; + } } throw DmpError("Newton-Raphson max iterations exceeded"); } @@ -1557,8 +1558,8 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, } else dmp_alg_ = dmp_cap_; - dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi, - c1); + dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, + c2, rpi, c1); debugPrint(debug_, "dmp_ceff", 3, " DMP in_slew = {} c2 = {} rpi = {} c1 = {} Rd = {} ({} alg)", units_->timeUnit()->asString(in_slew), @@ -1667,10 +1668,10 @@ DmpCeffDelayCalc::copyState(const StaState *sta) dmp_zero_c2_->copyState(sta); } -DmpError::DmpError(const char *what) : +DmpError::DmpError(std::string_view what) : what_(what) { - // printf("DmpError %s\n", what); + //sta::print(stdout, "DmpError {}\n", what); } // This saves about 2.5% in overall run time on designs with SPEF. diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index bcfb9cc0..f8984474 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -426,7 +426,8 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex, if (from_port == nullptr) from_port = driveCellDefaultFromPort(drvr_cell, to_port); findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf, - from_port, from_slews, to_port, scene, min_max); + from_port, from_slews, to_port, scene, min_max, + arc_delay_calc); } else seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, scene, min_max, @@ -601,7 +602,8 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, float *from_slews, const LibertyPort *to_port, const Scene *scene, - const MinMax *min_max) + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc) { debugPrint(debug_, "delay_calc", 2, " driver cell {} {}", drvr_cell->name(), @@ -610,11 +612,11 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, for (TimingArc *arc : arc_set->arcs()) { if (arc->toEdge()->asRiseFall() == rf) { float from_slew = from_slews[arc->fromEdge()->index()]; - findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, scene, min_max); + findInputArcDelay(drvr_pin, drvr_vertex, arc, from_slew, scene, min_max, + arc_delay_calc); } } } - arc_delay_calc_->finishDrvrPin(); } // Driving cell delay is the load dependent delay, which is the gate @@ -626,7 +628,8 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, const TimingArc *arc, float from_slew, const Scene *scene, - const MinMax *min_max) + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc) { debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({})", arc->from()->name(), @@ -640,20 +643,20 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, const Parasitic *parasitic; float load_cap; - parasiticLoad(drvr_pin, drvr_rf, scene, min_max, nullptr, arc_delay_calc_, + parasiticLoad(drvr_pin, drvr_rf, scene, min_max, nullptr, arc_delay_calc, load_cap, parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult intrinsic_result = - arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, - load_pin_index_map, scene, min_max); + arc_delay_calc->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr, + load_pin_index_map, scene, min_max); const ArcDelay &intrinsic_delay = intrinsic_result.gateDelay(); - ArcDcalcResult gate_result = arc_delay_calc_->gateDelay(drvr_pin, arc, - Slew(from_slew), load_cap, - parasitic, - load_pin_index_map, - scene, min_max); + ArcDcalcResult gate_result = arc_delay_calc->gateDelay(drvr_pin, arc, + Slew(from_slew), load_cap, + parasitic, + load_pin_index_map, + scene, min_max); const ArcDelay &gate_delay = gate_result.gateDelay(); const Slew &gate_slew = gate_result.drvrSlew(); @@ -666,7 +669,7 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); annotateLoadDelays(drvr_vertex, drvr_rf, gate_result, load_pin_index_map, load_delay, false, scene, min_max); - arc_delay_calc_->finishDrvrPin(); + arc_delay_calc->finishDrvrPin(); } } @@ -1598,41 +1601,41 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, for (Scene *scene : scenes_) { for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { - const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, + if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { + const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, scene, min_max); const Slew to_slew = graph_->slew(to_vertex, to_rf, ap_index); - debugPrint(debug_, "delay_calc", 3, + debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({}) scene:{}/{}", - arc_set->from()->name(), - arc->fromEdge()->to_string(), - arc_set->to()->name(), - arc->toEdge()->to_string(), - arc_set->role()->to_string(), + arc_set->from()->name(), + arc->fromEdge()->to_string(), + arc_set->to()->name(), + arc->toEdge()->to_string(), + arc_set->role()->to_string(), scene->name(), min_max->to_string()); - debugPrint(debug_, "delay_calc", 3, - " from_slew = {} to_slew = {}", - delayAsString(from_slew, this), - delayAsString(to_slew, this)); - float related_out_cap = 0.0; - if (related_out_pin) + debugPrint(debug_, "delay_calc", 3, + " from_slew = {} to_slew = {}", + delayAsString(from_slew, this), + delayAsString(to_slew, this)); + float related_out_cap = 0.0; + if (related_out_pin) related_out_cap = loadCap(related_out_pin, to_rf,scene,min_max, arc_delay_calc); - ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, - to_slew, related_out_cap, + ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, + to_slew, related_out_cap, scene, min_max); - debugPrint(debug_, "delay_calc", 3, - " check_delay = {}", - delayAsString(check_delay, this)); - graph_->setArcDelay(edge, arc, ap_index, check_delay); - delay_changed = true; - arc_delay_calc_->finishDrvrPin(); - } + debugPrint(debug_, "delay_calc", 3, + " check_delay = {}", + delayAsString(check_delay, this)); + graph_->setArcDelay(edge, arc, ap_index, check_delay); + delay_changed = true; + arc_delay_calc_->finishDrvrPin(); + } + } } } } - } if (delay_changed && observer_) observer_->checkDelayChangedTo(to_vertex); diff --git a/include/sta/FilterObjects.hh b/include/sta/FilterObjects.hh new file mode 100644 index 00000000..bbabd4b0 --- /dev/null +++ b/include/sta/FilterObjects.hh @@ -0,0 +1,106 @@ +// 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. + +#pragma once + +#include +#include +#include + +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "StringUtil.hh" + +namespace sta { + +class Sta; +class Report; + +PortSeq +filterPorts(std::string_view filter_expression, + PortSeq *objects, + bool bool_props_as_int, + Sta *sta); + +InstanceSeq +filterInstances(std::string_view filter_expression, + InstanceSeq *objects, + bool bool_props_as_int, + Sta *sta); + +PinSeq +filterPins(std::string_view filter_expression, + PinSeq *objects, + bool bool_props_as_int, + Sta *sta); + +NetSeq +filterNets(std::string_view filter_expression, + NetSeq *objects, + bool bool_props_as_int, + Sta *sta); + +ClockSeq +filterClocks(std::string_view filter_expression, + ClockSeq *objects, + bool bool_props_as_int, + Sta *sta); + +LibertyCellSeq +filterLibCells(std::string_view filter_expression, + LibertyCellSeq *objects, + bool bool_props_as_int, + Sta *sta); + +LibertyPortSeq +filterLibPins(std::string_view filter_expression, + LibertyPortSeq *objects, + bool bool_props_as_int, + Sta *sta); + +LibertyLibrarySeq +filterLibertyLibraries(std::string_view filter_expression, + LibertyLibrarySeq *objects, + bool bool_props_as_int, + Sta *sta); + +EdgeSeq +filterTimingArcs(std::string_view filter_expression, + EdgeSeq *objects, + bool bool_props_as_int, + Sta *sta); + +PathEndSeq +filterPathEnds(std::string_view filter_expression, + PathEndSeq *objects, + bool bool_props_as_int, + Sta *sta); + +// For FilterExpr unit tests. +StringSeq +filterExprToPostfix(std::string_view expr, + bool bool_props_as_int, + Report *report); + +} // namespace diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 60c12d43..85d198c6 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -174,7 +174,8 @@ protected: float *from_slews, const LibertyPort *to_port, const Scene *scene, - const MinMax *min_max); + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc); LibertyPort *driveCellDefaultFromPort(const LibertyCell *cell, const LibertyPort *to_port); int findPortIndex(const LibertyCell *cell, @@ -184,7 +185,8 @@ protected: const TimingArc *arc, float from_slew, const Scene *scene, - const MinMax *min_max); + const MinMax *min_max, + ArcDelayCalc *arc_delay_calc); void findDriverDelays(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index 0b8186f9..1a21bbd2 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -41,6 +41,7 @@ public: static PortDirection *internal() { return internal_; } static PortDirection *ground() { return ground_; } static PortDirection *power() { return power_; } + static PortDirection *bias() { return bias_; } static PortDirection *unknown() { return unknown_; } static PortDirection *find(const char *dir_name); std::string_view name() const { return name_; } @@ -57,7 +58,8 @@ public: bool isAnyTristate() const; bool isGround() const { return this == ground_; } bool isPower() const { return this == power_; } - // Ground or power. + bool isBias() const { return this == bias_; } + // Ground, power, or bias. bool isPowerGround() const; bool isInternal() const { return this == internal_; } bool isUnknown() const { return this == unknown_; } @@ -76,6 +78,7 @@ private: static PortDirection *internal_; static PortDirection *ground_; static PortDirection *power_; + static PortDirection *bias_; static PortDirection *unknown_; }; diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index b91c10e6..8489f6f8 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -1217,6 +1217,12 @@ LibertyReader::makePgPinPort(LibertyCell *cell, case PwrGndType::internal_power: dir = PortDirection::power(); break; + case PwrGndType::nwell: + case PwrGndType::pwell: + case PwrGndType::deepnwell: + case PwrGndType::deeppwell: + dir = PortDirection::bias(); + break; case PwrGndType::none: error(1291, pg_pin_group, "unknown pg_type."); break; diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index a27490fb..d38774e8 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -569,7 +569,8 @@ LibertyWriter::asString(const PortDirection *dir) return "internal"; else if (dir == PortDirection::bidirect()) return "inout"; - else if (dir == PortDirection::ground() || dir == PortDirection::power()) + else if (dir == PortDirection::ground() || dir == PortDirection::power() + || dir == PortDirection::bias()) return "input"; return "unknown"; } diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 926385b2..676d29af 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -108,19 +108,18 @@ GateTableModel::gateDelay(const Pvt *pvt, float &gate_delay, float &drvr_slew) const { - if (delay_models_) + if (delay_models_ && delay_models_->model()) gate_delay = findValue(pvt, delay_models_->model(), in_slew, load_cap, 0.0); else gate_delay = 0.0; - if (slew_models_) + if (slew_models_ && slew_models_->model()) { drvr_slew = findValue(pvt, slew_models_->model(), in_slew, load_cap, 0.0); + // Clip negative slews to zero. + if (drvr_slew < 0.0) + drvr_slew = 0.0; + } else drvr_slew = 0.0; - // TODO: Check for a better solution than clip negative delays and slews to zero. - //if (gate_delay < 0.0) - // gate_delay = 0.0; - if (drvr_slew < 0.0) - drvr_slew = 0.0; } void diff --git a/network/PortDirection.cc b/network/PortDirection.cc index 5a6aaae5..2ade330d 100644 --- a/network/PortDirection.cc +++ b/network/PortDirection.cc @@ -35,6 +35,7 @@ PortDirection *PortDirection::bidirect_; PortDirection *PortDirection::internal_; PortDirection *PortDirection::ground_; PortDirection *PortDirection::power_; +PortDirection *PortDirection::bias_; PortDirection *PortDirection::unknown_; void @@ -47,7 +48,8 @@ PortDirection::init() internal_ = new PortDirection("internal", 4); ground_ = new PortDirection("ground", 5); power_ = new PortDirection("power", 6); - unknown_ = new PortDirection("unknown", 7); + bias_ = new PortDirection("bias", 7); + unknown_ = new PortDirection("unknown", 8); } void @@ -67,6 +69,8 @@ PortDirection::destroy() ground_ = nullptr; delete power_; power_ = nullptr; + delete bias_; + bias_ = nullptr; delete unknown_; unknown_ = nullptr; } @@ -95,6 +99,8 @@ PortDirection::find(const char *dir_name) return ground_; else if (stringEqual(dir_name, "power")) return power_; + else if (stringEqual(dir_name, "bias")) + return bias_; else return nullptr; } @@ -124,8 +130,7 @@ PortDirection::isAnyTristate() const bool PortDirection::isPowerGround() const { - return this == ground_ - || this == power_; + return this == ground_ || this == power_ || this == bias_; } } // namespace diff --git a/network/test/cpp/TestNetwork.cc b/network/test/cpp/TestNetwork.cc index f95223f1..06e4b178 100644 --- a/network/test/cpp/TestNetwork.cc +++ b/network/test/cpp/TestNetwork.cc @@ -161,11 +161,20 @@ TEST_F(PortDirectionTest, PowerSingleton) { EXPECT_TRUE(dir->isPower()); } +TEST_F(PortDirectionTest, BiasSingleton) +{ + PortDirection *dir = PortDirection::bias(); + EXPECT_NE(dir, nullptr); + EXPECT_EQ(dir->name(), "bias"); + EXPECT_EQ(dir->index(), 7); + EXPECT_TRUE(dir->isBias()); +} + TEST_F(PortDirectionTest, UnknownSingleton) { PortDirection *dir = PortDirection::unknown(); EXPECT_NE(dir, nullptr); EXPECT_EQ(dir->name(), "unknown"); - EXPECT_EQ(dir->index(), 7); + EXPECT_EQ(dir->index(), 8); EXPECT_TRUE(dir->isUnknown()); } @@ -177,6 +186,7 @@ TEST_F(PortDirectionTest, FindByName) { EXPECT_EQ(PortDirection::find("internal"), PortDirection::internal()); EXPECT_EQ(PortDirection::find("ground"), PortDirection::ground()); EXPECT_EQ(PortDirection::find("power"), PortDirection::power()); + EXPECT_EQ(PortDirection::find("bias"), PortDirection::bias()); EXPECT_EQ(PortDirection::find("nonexistent"), nullptr); } @@ -188,6 +198,7 @@ TEST_F(PortDirectionTest, IsAnyInput) { EXPECT_FALSE(PortDirection::internal()->isAnyInput()); EXPECT_FALSE(PortDirection::ground()->isAnyInput()); EXPECT_FALSE(PortDirection::power()->isAnyInput()); + EXPECT_FALSE(PortDirection::bias()->isAnyInput()); EXPECT_FALSE(PortDirection::unknown()->isAnyInput()); } @@ -199,6 +210,7 @@ TEST_F(PortDirectionTest, IsAnyOutput) { EXPECT_FALSE(PortDirection::internal()->isAnyOutput()); EXPECT_FALSE(PortDirection::ground()->isAnyOutput()); EXPECT_FALSE(PortDirection::power()->isAnyOutput()); + EXPECT_FALSE(PortDirection::bias()->isAnyOutput()); EXPECT_FALSE(PortDirection::unknown()->isAnyOutput()); } @@ -210,12 +222,14 @@ TEST_F(PortDirectionTest, IsAnyTristate) { EXPECT_FALSE(PortDirection::internal()->isAnyTristate()); EXPECT_FALSE(PortDirection::ground()->isAnyTristate()); EXPECT_FALSE(PortDirection::power()->isAnyTristate()); + EXPECT_FALSE(PortDirection::bias()->isAnyTristate()); EXPECT_FALSE(PortDirection::unknown()->isAnyTristate()); } TEST_F(PortDirectionTest, IsPowerGround) { EXPECT_TRUE(PortDirection::power()->isPowerGround()); EXPECT_TRUE(PortDirection::ground()->isPowerGround()); + EXPECT_TRUE(PortDirection::bias()->isPowerGround()); EXPECT_FALSE(PortDirection::input()->isPowerGround()); EXPECT_FALSE(PortDirection::output()->isPowerGround()); EXPECT_FALSE(PortDirection::tristate()->isPowerGround()); @@ -851,6 +865,7 @@ TEST(PortDirectionExtraTest, AllDirections) { EXPECT_NE(PortDirection::internal(), nullptr); EXPECT_NE(PortDirection::ground(), nullptr); EXPECT_NE(PortDirection::power(), nullptr); + EXPECT_NE(PortDirection::bias(), nullptr); EXPECT_NE(PortDirection::unknown(), nullptr); } @@ -873,6 +888,7 @@ TEST(PortDirectionExtraTest, DirectionProperties) { EXPECT_TRUE(PortDirection::ground()->isPowerGround()); EXPECT_TRUE(PortDirection::power()->isPowerGround()); + EXPECT_TRUE(PortDirection::bias()->isPowerGround()); } TEST(PortDirectionExtraTest, DirectionNames) { @@ -886,6 +902,7 @@ TEST(PortDirectionExtraTest, DirectionNames) { EXPECT_EQ(PortDirection::internal()->name(), "internal"); EXPECT_EQ(PortDirection::ground()->name(), "ground"); EXPECT_EQ(PortDirection::power()->name(), "power"); + EXPECT_EQ(PortDirection::bias()->name(), "bias"); EXPECT_EQ(PortDirection::unknown()->name(), "unknown"); } @@ -900,6 +917,7 @@ TEST(PortDirectionExtraTest, FindAllByName) { EXPECT_EQ(PortDirection::find("internal"), PortDirection::internal()); EXPECT_EQ(PortDirection::find("ground"), PortDirection::ground()); EXPECT_EQ(PortDirection::find("power"), PortDirection::power()); + EXPECT_EQ(PortDirection::find("bias"), PortDirection::bias()); // "unknown" is not findable by name, returns nullptr EXPECT_EQ(PortDirection::find("nonexistent"), nullptr); } diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc new file mode 100644 index 00000000..2e18473e --- /dev/null +++ b/sdc/FilterObjects.cc @@ -0,0 +1,508 @@ +// 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 "FilterObjects.hh" + +#include +#include +#include +#include + +#include "Property.hh" +#include "PatternMatch.hh" +#include "Sta.hh" + +namespace sta { + +class FilterExpr +{ +public: + struct Token + { + enum class Kind { + skip = 0, + predicate, + op_lparen, + op_rparen, + op_or, + op_and, + op_inv, + defined, + undefined + }; + + Token(std::string text, + Kind kind); + + std::string text; + Kind kind; + }; + + struct PredicateToken : public Token + { + PredicateToken(std::string property, + std::string op, + std::string arg); + + std::string property; + std::string op; + std::string arg; + }; + + FilterExpr(std::string_view expression, + Report *report); + + std::vector> postfix(bool bool_props_as_int); +private: + std::vector> lex(bool bool_props_as_int); + std::vector> shuntingYard(std::vector> &infix); + + std::string raw_; + Report *report_; +}; + +FilterExpr::Token::Token(std::string text, + Token::Kind kind) : + text (text), + kind(kind) +{ +} + +FilterExpr::PredicateToken::PredicateToken(std::string property, + std::string op, + std::string arg) : + Token(property + " " + op + " " + arg, + Token::Kind::predicate), + property(property), op(op), arg(arg) +{ +} + +FilterExpr::FilterExpr(std::string_view expression, + Report *report) : + raw_(expression), + report_(report) +{ +} + +std::vector> +FilterExpr::postfix(bool bool_props_as_int) +{ + auto infix = lex(bool_props_as_int); + return shuntingYard(infix); +} + +std::vector> +FilterExpr::lex(bool bool_props_as_int) +{ + std::vector> token_regexes = { + {std::regex("^\\s+"), Token::Kind::skip}, + {std::regex("^defined\\(([a-zA-Z_]+)\\)"), Token::Kind::defined}, + {std::regex("^undefined\\(([a-zA-Z_]+)\\)"), Token::Kind::undefined}, + {std::regex("^@?([a-zA-Z_]+) *((==|!=|=~|!~) *([0-9a-zA-Z_\\/$\\[\\]*?]+))?"), Token::Kind::predicate}, + {std::regex("^(&&)"), Token::Kind::op_and}, + {std::regex("^(\\|\\|)"), Token::Kind::op_or}, + {std::regex("^(!)"), Token::Kind::op_inv}, + {std::regex("^(\\()"), Token::Kind::op_lparen}, + {std::regex("^(\\))"), Token::Kind::op_rparen}, + }; + + std::vector> result; + const char* ptr = &raw_[0]; + bool match = false; + while (*ptr != '\0') { + match = false; + for (auto& [regex, kind]: token_regexes) { + std::cmatch token_match; + if (std::regex_search(ptr, token_match, regex)) { + if (kind == Token::Kind::predicate) { + std::string property = token_match[1].str(); + + // The default operation on a predicate if an op and arg are + // omitted is 'arg == 1' / 'arg == true'. + std::string op = "=="; + std::string arg = (bool_props_as_int ? "1" : "true"); + + if (token_match[2].length() != 0) { + op = token_match[3].str(); + arg = token_match[4].str(); + } + result.push_back(std::make_unique(property, op, arg)); + } + else if (kind == Token::Kind::defined) + result.push_back(std::make_unique(token_match[1].str(), kind)); + else if (kind == Token::Kind::undefined) + result.push_back(std::make_unique(token_match[1].str(), kind)); + else if (kind != Token::Kind::skip) + result.push_back(std::make_unique(std::string(ptr,token_match.length()), + kind)); + ptr += token_match.length(); + match = true; + break; + }; + } + if (!match) + report_->error(2600, "-filter parsing failed at '{}'.", ptr); + } + return result; +} + +std::vector> +FilterExpr::shuntingYard(std::vector> &infix) +{ + std::vector> output; + std::stack> operator_stack; + + for (auto &token : infix) { + switch (token->kind) { + case Token::Kind::predicate: + output.push_back(std::move(token)); + break; + case Token::Kind::op_or: + [[fallthrough]]; + case Token::Kind::op_and: + // The operators' enum values are ascending by precedence: + // inv > and > or + while (operator_stack.size() + && operator_stack.top()->kind > token->kind) { + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + } + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_inv: + // Unary with highest precedence, no need for the while loop. + operator_stack.push(std::move(token)); + break; + case Token::Kind::defined: + operator_stack.push(std::move(token)); + break; + case Token::Kind::undefined: + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_lparen: + operator_stack.push(std::move(token)); + break; + case Token::Kind::op_rparen: + if (operator_stack.empty()) + report_->error(2601, "-filter extraneous )."); + while (operator_stack.size() + && operator_stack.top()->kind != Token::Kind::op_lparen) { + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + if (operator_stack.empty()) + report_->error(2602, "-filter extraneous )."); + } + // Guaranteed to be lparen at this point. + operator_stack.pop(); + break; + default: + // Unhandled/skip. + break; + } + } + + while (operator_stack.size()) { + if (operator_stack.top()->kind == Token::Kind::op_lparen) + report_->error(2603, "-filter unmatched (."); + output.push_back(std::move(operator_stack.top())); + operator_stack.pop(); + } + + return output; +} + +//////////////////////////////////////////////////////////////// + +template std::set +filterObjects(const char *property, + const char *op, + const char *pattern, + std::set &all, + Sta *sta) +{ + Properties &properties = sta->properties(); + Network *network = sta->network(); + auto filtered_objects = std::set(); + bool exact_match = stringEq(op, "=="); + bool pattern_match = stringEq(op, "=~"); + bool not_match = stringEq(op, "!="); + bool not_pattern_match = stringEq(op, "!~"); + for (T *object : all) { + PropertyValue value = properties.getProperty(object, property); + std::string prop_str = value.to_string(network); + const char *prop = prop_str.c_str(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (not_match && !stringEq(prop, pattern)) + || (pattern_match && patternMatch(pattern, prop)) + || (not_pattern_match && !patternMatch(pattern, prop)))) + filtered_objects.insert(object); + } + return filtered_objects; +} + +template std::vector +filterObjects(std::string_view filter_expression, + std::vector *objects, + bool bool_props_as_int, + Sta *sta) +{ + Report *report = sta->report(); + Network *network = sta->network(); + Properties &properties = sta->properties(); + std::vector result; + if (objects) { + std::set all; + for (auto object: *objects) + all.insert(object); + + FilterExpr filter(filter_expression, report); + auto postfix = filter.postfix(bool_props_as_int); + std::stack> eval_stack; + for (auto &token : postfix) { + if (token->kind == FilterExpr::Token::Kind::op_or) { + if (eval_stack.size() < 2) + report->error(2604, "-filter logical OR requires at least two operands."); + auto arg0 = eval_stack.top(); + eval_stack.pop(); + auto arg1 = eval_stack.top(); + eval_stack.pop(); + auto union_result = std::set(); + std::set_union(arg0.cbegin(), arg0.cend(), arg1.cbegin(), arg1.cend(), + std::inserter(union_result, union_result.begin())); + eval_stack.push(union_result); + } + else if (token->kind == FilterExpr::Token::Kind::op_and) { + if (eval_stack.size() < 2) { + report->error(2605, "-filter logical AND requires two operands."); + } + auto arg0 = eval_stack.top(); + eval_stack.pop(); + auto arg1 = eval_stack.top(); + eval_stack.pop(); + auto intersection_result = std::set(); + std::set_intersection(arg0.cbegin(), arg0.cend(), + arg1.cbegin(), arg1.cend(), + std::inserter(intersection_result, + intersection_result.begin())); + eval_stack.push(intersection_result); + } + else if (token->kind == FilterExpr::Token::Kind::op_inv) { + if (eval_stack.size() < 1) { + report->error(2606, "-filter NOT missing operand."); + } + auto arg0 = eval_stack.top(); + eval_stack.pop(); + + auto difference_result = std::set(); + std::set_difference(all.cbegin(), all.cend(), + arg0.cbegin(), arg0.cend(), + std::inserter(difference_result, + difference_result.begin())); + eval_stack.push(difference_result); + } + else if (token->kind == FilterExpr::Token::Kind::defined + || token->kind == FilterExpr::Token::Kind::undefined) { + bool should_be_defined = + (token->kind == FilterExpr::Token::Kind::defined); + auto result = std::set(); + for (auto object : all) { + PropertyValue value = properties.getProperty(object, token->text); + bool is_defined = false; + switch (value.type()) { + case PropertyValue::Type::float_: + is_defined = value.floatValue() != 0; + break; + case PropertyValue::Type::bool_: + is_defined = value.boolValue(); + break; + case PropertyValue::Type::string: + case PropertyValue::Type::liberty_library: + case PropertyValue::Type::liberty_cell: + case PropertyValue::Type::liberty_port: + case PropertyValue::Type::library: + case PropertyValue::Type::cell: + case PropertyValue::Type::port: + case PropertyValue::Type::instance: + case PropertyValue::Type::pin: + case PropertyValue::Type::net: + case PropertyValue::Type::clk: + is_defined = value.to_string(network) != ""; + break; + case PropertyValue::Type::none: + is_defined = false; + break; + case PropertyValue::Type::pins: + is_defined = value.pins()->size() > 0; + break; + case PropertyValue::Type::clks: + is_defined = value.clocks()->size() > 0; + break; + case PropertyValue::Type::paths: + is_defined = value.paths()->size() > 0; + break; + case PropertyValue::Type::pwr_activity: + is_defined = value.pwrActivity().isSet(); + break; + } + if (is_defined == should_be_defined) { + result.insert(object); + } + } + eval_stack.push(result); + } + else if (token->kind == FilterExpr::Token::Kind::predicate) { + auto *predicate_token = + static_cast(token.get()); + auto result = filterObjects(predicate_token->property.c_str(), + predicate_token->op.c_str(), + predicate_token->arg.c_str(), + all, sta); + eval_stack.push(result); + } + } + if (eval_stack.size() == 0) + report->error(2607, "-filter expression is empty."); + if (eval_stack.size() > 1) + // huh? + report->error(2608, "-filter expression evaluated to multiple sets."); + auto result_set = eval_stack.top(); + result.resize(result_set.size()); + std::copy(result_set.begin(), result_set.end(), result.begin()); + std::map objects_i; + for (size_t i = 0; i < objects->size(); ++i) + objects_i[objects->at(i)] = i; + std::sort(result.begin(), result.end(), + [&](T* a, T* b) { + return objects_i[a] < objects_i[b]; + }); + delete objects; + } + return result; +} + +PortSeq +filterPorts(std::string_view filter_expression, + PortSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +InstanceSeq +filterInstances(std::string_view filter_expression, + InstanceSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +PinSeq +filterPins(std::string_view filter_expression, + PinSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +NetSeq +filterNets(std::string_view filter_expression, + NetSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +ClockSeq +filterClocks(std::string_view filter_expression, + ClockSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +LibertyCellSeq +filterLibCells(std::string_view filter_expression, + LibertyCellSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +LibertyPortSeq +filterLibPins(std::string_view filter_expression, + LibertyPortSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +LibertyLibrarySeq +filterLibertyLibraries(std::string_view filter_expression, + LibertyLibrarySeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +EdgeSeq +filterTimingArcs(std::string_view filter_expression, + EdgeSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +PathEndSeq +filterPathEnds(std::string_view filter_expression, + PathEndSeq *objects, + bool bool_props_as_int, + Sta *sta) +{ + return filterObjects(filter_expression, objects, bool_props_as_int, sta); +} + +StringSeq +filterExprToPostfix(std::string_view expr, + bool bool_props_as_int, + Report *report) +{ + FilterExpr filter(expr, report); + auto postfix = filter.postfix(bool_props_as_int); + StringSeq result; + for (auto &token : postfix) + result.push_back(token->text); + return result; +} + +} // namespace sta diff --git a/sdc/Sdc.i b/sdc/Sdc.i index 411d8f49..eccd234e 100644 --- a/sdc/Sdc.i +++ b/sdc/Sdc.i @@ -34,6 +34,7 @@ #include "Clock.hh" #include "PortDelay.hh" #include "Property.hh" +#include "FilterObjects.hh" #include "Sta.hh" using namespace sta; @@ -1490,114 +1491,103 @@ find_register_output_pins(ClockSet *clks, //////////////////////////////////////////////////////////////// -template std::vector -filter_objects(std::string_view property, - std::string_view op, - std::string_view pattern, - std::vector *objects) -{ - std::vector filtered_objects; - if (objects) { - Sta *sta = Sta::sta(); - Properties &properties = sta->properties(); - bool exact_match = op == "=="; - bool pattern_match = op == "=~"; - bool not_match = op == "!="; - bool not_pattern_match = op == "!~"; - for (T *object : *objects) { - PropertyValue value(properties.getProperty(object, property)); - std::string prop_value = value.to_string(sta->network()); - if (!prop_value.empty() - && ((exact_match && prop_value == pattern) - || (not_match && prop_value != pattern) - || (pattern_match && patternMatch(pattern, prop_value)) - || (not_pattern_match && !patternMatch(pattern, prop_value)))) - filtered_objects.push_back(object); - } - delete objects; - } - return filtered_objects; -} - PortSeq -filter_ports(std::string_view property, - std::string_view op, - std::string_view pattern, - PortSeq *ports) +filter_ports(const char *filter_expression, + PortSeq *ports, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, ports); + sta::Sta *sta = Sta::sta(); + return filterPorts(filter_expression, ports, bool_props_as_int, sta); } InstanceSeq -filter_insts(std::string_view property, - std::string_view op, - std::string_view pattern, - InstanceSeq *insts) +filter_insts(const char *filter_expression, + InstanceSeq *insts, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, insts); + sta::Sta *sta = Sta::sta(); + return filterInstances(filter_expression, insts, bool_props_as_int, sta); } PinSeq -filter_pins(std::string_view property, - std::string_view op, - std::string_view pattern, - PinSeq *pins) +filter_pins(const char *filter_expression, + PinSeq *pins, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, pins); -} - -ClockSeq -filter_clocks(std::string_view property, - std::string_view op, - std::string_view pattern, - ClockSeq *clocks) -{ - return filter_objects(property, op, pattern, clocks); -} - -LibertyCellSeq -filter_lib_cells(std::string_view property, - std::string_view op, - std::string_view pattern, - LibertyCellSeq *cells) -{ - return filter_objects(property, op, pattern, cells); -} - -LibertyPortSeq -filter_lib_pins(std::string_view property, - std::string_view op, - std::string_view pattern, - LibertyPortSeq *pins) -{ - return filter_objects(property, op, pattern, pins); -} - -LibertyLibrarySeq -filter_liberty_libraries(std::string_view property, - std::string_view op, - std::string_view pattern, - LibertyLibrarySeq *libs) -{ - return filter_objects(property, op, pattern, libs); + sta::Sta *sta = Sta::sta(); + return filterPins(filter_expression, pins, bool_props_as_int, sta); } NetSeq -filter_nets(std::string_view property, - std::string_view op, - std::string_view pattern, - NetSeq *nets) +filter_nets(const char *filter_expression, + NetSeq *nets, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, nets); + sta::Sta *sta = Sta::sta(); + return filterNets(filter_expression, nets, bool_props_as_int, sta); +} + +ClockSeq +filter_clocks(const char *filter_expression, + ClockSeq *clocks, + bool bool_props_as_int) +{ + sta::Sta *sta = Sta::sta(); + return filterClocks(filter_expression, clocks, bool_props_as_int, sta); +} + +LibertyCellSeq +filter_lib_cells(const char *filter_expression, + LibertyCellSeq *cells, + bool bool_props_as_int) +{ + sta::Sta *sta = Sta::sta(); + return filterLibCells(filter_expression, cells, bool_props_as_int, sta); +} + +LibertyPortSeq +filter_lib_pins(const char *filter_expression, + LibertyPortSeq *pins, + bool bool_props_as_int) +{ + sta::Sta *sta = Sta::sta(); + return filterLibPins(filter_expression, pins, bool_props_as_int, sta); +} + +LibertyLibrarySeq +filter_liberty_libraries(const char *filter_expression, + LibertyLibrarySeq *libs, + bool bool_props_as_int) +{ + sta::Sta *sta = Sta::sta(); + return filterLibertyLibraries(filter_expression, libs, bool_props_as_int, sta); } EdgeSeq -filter_timing_arcs(std::string_view property, - std::string_view op, - std::string_view pattern, - EdgeSeq *edges) +filter_timing_arcs(const char *filter_expression, + EdgeSeq *edges, + bool bool_props_as_int) { - return filter_objects(property, op, pattern, edges); + sta::Sta *sta = Sta::sta(); + return filterTimingArcs(filter_expression, edges, bool_props_as_int, sta); +} + +PathEndSeq +filter_path_ends(const char *filter_expression, + PathEndSeq *path_ends, + bool bool_props_as_int) +{ + sta::Sta *sta = Sta::sta(); + return filterPathEnds(filter_expression, path_ends, bool_props_as_int, sta); +} + +// For FilterExpr unit tests. +StringSeq +filter_expr_to_postfix(const char* expr, + bool bool_props_as_int) +{ + Report *report = Sta::sta()->report(); + return filterExprToPostfix(expr, bool_props_as_int, report); } //////////////////////////////////////////////////////////////// diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index 26196d2f..dd5374cd 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -315,42 +315,6 @@ proc current_design { {design ""} } { ################################################################ -# Generic get_* filter. -proc filter_objs { filter objects filter_function object_type } { - # Regexp for attr op arg (e.g., full_name =~ *blk*) - set filter_regexp_op {@?([a-zA-Z_]+) *(==|!=|=~|!~) *([0-9a-zA-Z_\*]+)} - # Regexp for bool attr (e.g., is_hierarchical) - anchored for standalone use - set filter_regexp_bool {^@?([a-zA-Z_]+)$} - # Regexp for wildcard attr (e.g., full_name *blk*) - set filter_regexp_wild_op {@?([a-zA-Z_]+) *(.+) *([0-9a-zA-Z_\*]+)} - # Regexp for term in compound expression (no anchors) - set filter_regexp_term {@?([a-zA-Z_]+)( *(==|!=|=~|!~) *([0-9a-zA-Z_\*]+))?} - set filter_or_regexp "($filter_regexp_term) *\\|\\| *($filter_regexp_term)" - set filter_and_regexp "($filter_regexp_term) *&& *($filter_regexp_term)" - set filtered_objects {} - # Ignore sub-exprs in filter_regexp for expr2 match var. - if { [regexp $filter_or_regexp $filter ignore expr1 ignore ignore ignore ignore expr2] } { - set filtered_objects1 [filter_objs $expr1 $objects $filter_function $object_type] - set filtered_objects2 [filter_objs $expr2 $objects $filter_function $object_type] - set filtered_objects [concat $filtered_objects1 $filtered_objects2] - } elseif { [regexp $filter_and_regexp $filter ignore expr1 ignore ignore ignore ignore expr2] } { - set filtered_objects [filter_objs $expr1 $objects $filter_function $object_type] - set filtered_objects [filter_objs $expr2 $filtered_objects $filter_function $object_type] - } elseif { [regexp $filter_regexp_op $filter ignore attr_name op arg] } { - set filtered_objects [$filter_function $attr_name $op $arg $objects] - } elseif { [regexp $filter_regexp_bool $filter ignore attr_name] } { - # Bool property: use ==1 by default. - set filtered_objects [$filter_function $attr_name "==" "1" $objects] - } elseif { [regexp $filter_regexp_wild_op $filter ignore attr_name op arg] } { - sta_error 336 "unknown filter operand." - } else { - sta_error 350 "unsupported $object_type -filter expression." - } - return $filtered_objects -} - -################################################################ - define_cmd_args "get_cells" \ {[-hierarchical] [-hsc separator] [-filter expr]\ [-regexp] [-nocase] [-quiet] [-of_objects objects] [patterns]} @@ -429,7 +393,7 @@ proc get_cells { args } { } } if [info exists keys(-filter)] { - set insts [filter_objs $keys(-filter) $insts filter_insts "instance"] + set insts [filter_insts $keys(-filter) $insts 1] } return $insts } @@ -472,7 +436,7 @@ proc get_clocks { args } { } } if [info exists keys(-filter)] { - set clocks [filter_objs $keys(-filter) $clocks filter_clocks "clock"] + set clocks [filter_clocks $keys(-filter) $clocks 1] } return $clocks } @@ -553,7 +517,7 @@ proc get_lib_cells { args } { } } if [info exists keys(-filter)] { - set cells [filter_objs $keys(-filter) $cells filter_lib_cells "liberty cell"] + set cells [filter_lib_cells $keys(-filter) $cells 1] } return $cells } @@ -657,7 +621,7 @@ proc get_lib_pins { args } { } } if [info exists keys(-filter)] { - set ports [filter_objs $keys(-filter) $ports filter_lib_pins "liberty port"] + set ports [filter_lib_pins $keys(-filter) $ports 1] } return $ports } @@ -707,7 +671,7 @@ proc get_libs { args } { } } if [info exists keys(-filter)] { - set libs [filter_objs $keys(-filter) $libs filter_liberty_libraries "liberty library"] + set libs [filter_liberty_libraries $keys(-filter) $libs 1] } return $libs } @@ -808,7 +772,7 @@ proc get_nets { args } { } } if [info exists keys(-filter)] { - set nets [filter_objs $keys(-filter) $nets filter_nets "net"] + set nets [filter_nets $keys(-filter) $nets 1] } return $nets } @@ -899,7 +863,7 @@ proc get_pins { args } { } } if [info exists keys(-filter)] { - set pins [filter_objs $keys(-filter) $pins filter_pins "pin"] + set pins [filter_pins $keys(-filter) $pins 1] } return $pins } @@ -955,7 +919,7 @@ proc get_ports { args } { } } if [info exists keys(-filter)] { - set ports [filter_objs $keys(-filter) $ports filter_ports "port"] + set ports [filter_ports $keys(-filter) $ports 1] } return $ports } diff --git a/search/Bfs.cc b/search/Bfs.cc index 8324d54e..4cc7f769 100644 --- a/search/Bfs.cc +++ b/search/Bfs.cc @@ -195,7 +195,7 @@ BfsIterator::visitParallel(Level to_level, for (size_t k = 0; k < thread_count; k++) { // Last thread gets the left overs. size_t to = (k == thread_count - 1) ? vertex_count : from + chunk_size; - dispatch_queue_->dispatch([=, this](int) { + dispatch_queue_->dispatch([=, this](size_t) { for (size_t i = from; i < to; i++) { Vertex *vertex = level_vertices[i]; if (vertex) { diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc index 97626ca5..89eab1ea 100644 --- a/search/ClkSkew.cc +++ b/search/ClkSkew.cc @@ -196,7 +196,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks, std::vector partial_skews(thread_count_); for (Vertex *src_vertex : graph_->regClkVertices()) { if (hasClkPaths(src_vertex)) { - dispatch_queue_->dispatch([this, src_vertex, &partial_skews](int i) { + dispatch_queue_->dispatch([this, src_vertex, &partial_skews](size_t i) { findClkSkewFrom(src_vertex, partial_skews[i]); }); } diff --git a/search/PathGroup.cc b/search/PathGroup.cc index 1d3e6cd0..e2e20a80 100644 --- a/search/PathGroup.cc +++ b/search/PathGroup.cc @@ -1025,7 +1025,7 @@ PathGroups::makeGroupPathEnds(VertexSet &endpoints, MakeEndpointPathEnds(visitor, Scene::sceneSet(scenes), min_max, this)); for (const auto endpoint : endpoints) { - dispatch_queue_->dispatch( [endpoint, &visitors](int i) + dispatch_queue_->dispatch( [endpoint, &visitors](size_t i) { visitors[i].visit(endpoint); } ); } dispatch_queue_->finishTasks(); diff --git a/search/Search.cc b/search/Search.cc index f1a9658c..246d5dd6 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -1102,9 +1102,7 @@ Search::findArrivalsSeed() //////////////////////////////////////////////////////////////// ArrivalVisitor::ArrivalVisitor(const StaState *sta) : - PathVisitor(nullptr, - false, - sta) + PathVisitor(nullptr, false, sta) { init0(); init(true, false, nullptr); @@ -1114,9 +1112,7 @@ ArrivalVisitor::ArrivalVisitor(const StaState *sta) : ArrivalVisitor::ArrivalVisitor(bool always_to_endpoints, SearchPred *pred, const StaState *sta) : - PathVisitor(pred, - true, - sta) + PathVisitor(pred, true, sta) { init0(); init(always_to_endpoints, false, pred); @@ -1997,11 +1993,13 @@ PathVisitor::visitFaninPaths(Vertex *to_vertex) VertexInEdgeIterator edge_iter(to_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - const Pin *from_pin = from_vertex->pin(); - const Pin *to_pin = to_vertex->pin(); - if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) - break; + if (!edge->role()->isTimingCheck()) { + Vertex *from_vertex = edge->from(graph_); + const Pin *from_pin = from_vertex->pin(); + const Pin *to_pin = to_vertex->pin(); + if (!visitEdge(from_pin, from_vertex, edge, to_pin, to_vertex)) + break; + } } } diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index c1ceaf14..f6daed10 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -300,7 +300,7 @@ proc get_timing_edges_cmd { cmd cmd_args } { cmd_usage_error $cmd } if [info exists keys(-filter)] { - set arcs [filter_objs $keys(-filter) $arcs filter_timing_arcs "timing arc"] + set arcs [filter_timing_arcs $keys(-filter) $arcs 1] } return $arcs } diff --git a/test/get_filter.ok b/test/get_filter.ok index b66978cd..8bd8c18a 100644 --- a/test/get_filter.ok +++ b/test/get_filter.ok @@ -53,4 +53,10 @@ in2 [get_ports -filter direction==output *] out [get_cells -filter {name ~= *r1*} *] -Error 336: get_filter.tcl line 48, unknown filter operand. +Error: 2600 -filter parsing failed at '~= *r1*'. +clk1 +clk2 +clk3 +clk1 +clk2 +clk3 diff --git a/test/get_filter.tcl b/test/get_filter.tcl index 7263811e..d6009a89 100644 --- a/test/get_filter.tcl +++ b/test/get_filter.tcl @@ -5,9 +5,10 @@ link_design top create_clock -name clk -period 500 {clk1 clk2 clk3} create_clock -name vclk -period 1000 -# Test filters for each SDC command +# Test filters for each SDC get_* command. puts {[get_cells -filter liberty_cell==BUFx2_ASAP7_75t_R *]} report_object_full_names [get_cells -filter liberty_cell==BUFx2_ASAP7_75t_R *] + puts {[get_clocks -filter is_virtual==0 *]} report_object_full_names [get_clocks -filter is_virtual==0 *] puts {[get_clocks -filter is_virtual==1 *]} @@ -22,22 +23,28 @@ puts {[get_clocks -filter is_virtual||is_generated *]} report_object_full_names [get_clocks -filter is_virtual||is_generated *] puts {[get_clocks -filter is_virtual==0||is_generated *]} report_object_full_names [get_clocks -filter is_virtual==0||is_generated *] + puts {[get_lib_cells -filter is_buffer==1 *]} report_object_full_names [get_lib_cells -filter is_buffer==1 *] puts {[get_lib_cells -filter is_inverter==0 *]} report_object_full_names [get_lib_cells -filter is_inverter==0 *] + puts {[get_lib_pins -filter direction==input BUFx2_ASAP7_75t_R/*]} report_object_full_names [get_lib_pins -filter direction==input BUFx2_ASAP7_75t_R/*] puts {[get_lib_pins -filter direction==output BUFx2_ASAP7_75t_R/*]} report_object_full_names [get_lib_pins -filter direction==output BUFx2_ASAP7_75t_R/*] + puts {[get_libs -filter name==asap7_small *]} report_object_full_names [get_libs -filter name==asap7_small *] + puts {[get_nets -filter name=~*q *]} report_object_full_names [get_nets -filter name=~*q *] + puts {[get_pins -filter direction==input *]} report_object_full_names [get_pins -filter direction==input *] puts {[get_pins -filter direction==output *]} report_object_full_names [get_pins -filter direction==output *] + puts {[get_ports -filter direction==input *]} report_object_full_names [get_ports -filter direction==input *] puts {[get_ports -filter direction==output *]} @@ -47,3 +54,10 @@ report_object_full_names [get_ports -filter direction==output *] puts {[get_cells -filter {name ~= *r1*} *]} catch {get_cells -filter {name ~= *r1*} *} result puts $result + +# AND pattern match expr +report_object_names [get_ports -filter "direction == input && name =~ clk*" *] +# parens around sub-exprs +report_object_names [get_ports -filter "(direction == input) && (name =~ clk*)" *] + +sta::filter_expr_to_postfix "direction == input && name =~ clk* && is_clock" 1 diff --git a/test/regression.tcl b/test/regression.tcl index dcf0057b..8afefbf4 100755 --- a/test/regression.tcl +++ b/test/regression.tcl @@ -177,7 +177,6 @@ proc run_tests {} { } } write_failure_file - write_diff_file } proc run_test { test } { @@ -268,6 +267,8 @@ proc run_tests_parallel {} { vwait reg_parallel_job_done } } + # update results/failures and results/diffs + write_failure_file } } @@ -434,23 +435,23 @@ proc test_failed { test reason } { } proc write_failure_file {} { - global failure_file failed_tests + global failure_file failed_tests failed_tests_summery + global diff_file diff_options - set ch [open $failure_file "w"] + set fail_ch [open $failure_file "a"] foreach test $failed_tests { - puts $ch $test - } - close $ch -} + if { ![info exists failed_tests_summery($test)] } { + puts $fail_ch $test -proc write_diff_file {} { - global diff_file diff_options failed_tests + # Append diff to results/diffs + set log_file [test_log_file $test] + set ok_file [test_ok_file $test] + catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] - foreach test $failed_tests { - set log_file [test_log_file $test] - set ok_file [test_ok_file $test] - catch [concat exec diff $diff_options $ok_file $log_file >> $diff_file] + set failed_tests_summery($test) 1 + } } + close $fail_ch } # Error messages can be found in "valgrind/memcheck/mc_errcontext.c". diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index 8edaf699..114051c8 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -253,6 +253,8 @@ VerilogWriter::verilogPortDir(PortDirection *dir) return "inout"; else if (dir == PortDirection::ground()) return "inout"; + else if (dir == PortDirection::bias()) + return "inout"; else if (dir == PortDirection::internal() || dir == PortDirection::unknown()) return "inout"; diff --git a/verilog/test/CMakeLists.txt b/verilog/test/CMakeLists.txt index e6a9235f..bedd7fbc 100644 --- a/verilog/test/CMakeLists.txt +++ b/verilog/test/CMakeLists.txt @@ -1,5 +1,6 @@ sta_module_tests("verilog" TESTS + bias_pins bus ) diff --git a/verilog/test/verilog_bias_pins.ok b/verilog/test/verilog_bias_pins.ok new file mode 100644 index 00000000..1fe88107 --- /dev/null +++ b/verilog/test/verilog_bias_pins.ok @@ -0,0 +1,2 @@ +--- write_verilog bias pins --- +No differences found. diff --git a/verilog/test/verilog_bias_pins.tcl b/verilog/test/verilog_bias_pins.tcl new file mode 100644 index 00000000..2375fa1c --- /dev/null +++ b/verilog/test/verilog_bias_pins.tcl @@ -0,0 +1,25 @@ +# Test write_verilog should omit bias pins the same way it omits power/ground. + +source ../../test/helpers.tcl + +puts "--- write_verilog bias pins ---" +read_liberty ../../test/sky130hd/sky130_fd_sc_hd__ss_n40C_1v40.lib +read_verilog verilog_bias_pins.v +link_design top + +set outfile [make_result_file verilog_bias_pins_out.v] +write_verilog $outfile + +set outfile_pwr [make_result_file verilog_bias_pins_pwr.v] +write_verilog -include_pwr_gnd $outfile_pwr + +set combined_out [make_result_file verilog_bias_pins_combined.v] +set out_stream [open $combined_out w] +foreach file [list $outfile $outfile_pwr] { + set in_stream [open $file r] + puts -nonewline $out_stream [read $in_stream] + close $in_stream +} +close $out_stream + +diff_files verilog_bias_pins_out.vok $combined_out diff --git a/verilog/test/verilog_bias_pins.v b/verilog/test/verilog_bias_pins.v new file mode 100644 index 00000000..1abd47db --- /dev/null +++ b/verilog/test/verilog_bias_pins.v @@ -0,0 +1,20 @@ +module top ( + output y, + input a +); + + supply1 VPWR; + supply0 VGND; + supply1 VPB; + supply0 VNB; + + sky130_fd_sc_hd__buf_1 u1 ( + .X(y), + .A(a), + .VPWR(VPWR), + .VGND(VGND), + .VPB(VPB), + .VNB(VNB) + ); + +endmodule diff --git a/verilog/test/verilog_bias_pins_out.vok b/verilog/test/verilog_bias_pins_out.vok new file mode 100644 index 00000000..41b72482 --- /dev/null +++ b/verilog/test/verilog_bias_pins_out.vok @@ -0,0 +1,26 @@ +module top (y, + a); + output y; + input a; + + + sky130_fd_sc_hd__buf_1 u1 (.A(a), + .X(y)); +endmodule +module top (y, + a); + output y; + input a; + + wire VGND; + wire VNB; + wire VPB; + wire VPWR; + + sky130_fd_sc_hd__buf_1 u1 (.VGND(VGND), + .VNB(VNB), + .VPB(VPB), + .VPWR(VPWR), + .A(a), + .X(y)); +endmodule