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