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/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/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/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 7e432c73..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 1, 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".