Merge remote-tracking branch 'opensta/master' into secure-sta-test-by-opus

Signed-off-by: Jaehyun Kim <jhkim@precisioninno.com>

# Conflicts:
#	test/get_filter.ok
This commit is contained in:
Jaehyun Kim 2026-03-31 15:29:44 +09:00
commit b42e064281
9 changed files with 740 additions and 150 deletions

View File

@ -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

View File

@ -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 <https://www.gnu.org/licenses/>.
//
// 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 <string>
#include <string_view>
#include <vector>
#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

508
sdc/FilterObjects.cc Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
//
// 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 <regex>
#include <stack>
#include <functional>
#include <memory>
#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<std::unique_ptr<Token>> postfix(bool bool_props_as_int);
private:
std::vector<std::unique_ptr<Token>> lex(bool bool_props_as_int);
std::vector<std::unique_ptr<Token>> shuntingYard(std::vector<std::unique_ptr<Token>> &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<std::unique_ptr<FilterExpr::Token>>
FilterExpr::postfix(bool bool_props_as_int)
{
auto infix = lex(bool_props_as_int);
return shuntingYard(infix);
}
std::vector<std::unique_ptr<FilterExpr::Token>>
FilterExpr::lex(bool bool_props_as_int)
{
std::vector<std::pair<std::regex, Token::Kind>> 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<std::unique_ptr<Token>> 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<PredicateToken>(property, op, arg));
}
else if (kind == Token::Kind::defined)
result.push_back(std::make_unique<Token>(token_match[1].str(), kind));
else if (kind == Token::Kind::undefined)
result.push_back(std::make_unique<Token>(token_match[1].str(), kind));
else if (kind != Token::Kind::skip)
result.push_back(std::make_unique<Token>(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<std::unique_ptr<FilterExpr::Token>>
FilterExpr::shuntingYard(std::vector<std::unique_ptr<Token>> &infix)
{
std::vector<std::unique_ptr<Token>> output;
std::stack<std::unique_ptr<Token>> 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 <typename T> std::set<T*>
filterObjects(const char *property,
const char *op,
const char *pattern,
std::set<T*> &all,
Sta *sta)
{
Properties &properties = sta->properties();
Network *network = sta->network();
auto filtered_objects = std::set<T*>();
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 <typename T> std::vector<T*>
filterObjects(std::string_view filter_expression,
std::vector<T*> *objects,
bool bool_props_as_int,
Sta *sta)
{
Report *report = sta->report();
Network *network = sta->network();
Properties &properties = sta->properties();
std::vector<T*> result;
if (objects) {
std::set<T*> all;
for (auto object: *objects)
all.insert(object);
FilterExpr filter(filter_expression, report);
auto postfix = filter.postfix(bool_props_as_int);
std::stack<std::set<T*>> 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<T*>();
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<T*>();
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<T*>();
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<T*>();
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<FilterExpr::PredicateToken *>(token.get());
auto result = filterObjects<T>(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<T*, int> 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<const Port>(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<const Instance>(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<const Pin>(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<const Net>(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<Clock>(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<LibertyCell>(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<LibertyPort>(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<LibertyLibrary>(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<Edge>(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<PathEnd>(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

170
sdc/Sdc.i
View File

@ -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 <typename T> std::vector<T*>
filter_objects(std::string_view property,
std::string_view op,
std::string_view pattern,
std::vector<T*> *objects)
{
std::vector<T*> 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<const Port>(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<const Instance>(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<const Pin>(property, op, pattern, pins);
}
ClockSeq
filter_clocks(std::string_view property,
std::string_view op,
std::string_view pattern,
ClockSeq *clocks)
{
return filter_objects<Clock>(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<LibertyCell>(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<LibertyPort>(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<LibertyLibrary>(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<const Net>(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<Edge>(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);
}
////////////////////////////////////////////////////////////////

View File

@ -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 <attr_name>==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
}

View File

@ -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
}

View File

@ -53,4 +53,10 @@ in2
[get_ports -filter direction==output *]
out
[get_cells -filter {name ~= *r1*} *]
Error 336: get_filter.tcl line 1, unknown filter operand.
Error: 2600 -filter parsing failed at '~= *r1*'.
clk1
clk2
clk3
clk1
clk2
clk3

View File

@ -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

View File

@ -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".