diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 6591be01..c03085f4 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1533,6 +1533,9 @@ public: // TCL variable sta_enable_collections bool enableCollections() const; void setEnableCollections(bool enable); + // TCL variable sta_case_insensitive_matching. + bool caseInsensitiveMatching() const; + void setCaseInsensitiveMatching(bool enable); //////////////////////////////////////////////////////////////// Properties &properties() { return properties_; } diff --git a/include/sta/Variables.hh b/include/sta/Variables.hh index 938a94ab..17887e47 100644 --- a/include/sta/Variables.hh +++ b/include/sta/Variables.hh @@ -101,6 +101,11 @@ public: // TCL variable sta_enable_collections bool enableCollections() const { return enable_collections_; } void setEnableCollections(bool enable) { enable_collections_ = enable; } + // TCL variable sta_case_insensitive_matching. + // Match port/instance/net/cell/clock names ignoring case in the + // get_* and find_*_matching commands. + bool caseInsensitiveMatching() const { return case_insensitive_matching_; } + void setCaseInsensitiveMatching(bool enable) { case_insensitive_matching_ = enable; } private: @@ -127,6 +132,7 @@ private: bool no_inv_power_calc_{false}; bool strip_escaped_bus_{false}; bool enable_collections_{false}; + bool case_insensitive_matching_{false}; }; } // namespace sta diff --git a/sdc/FilterObjects.cc b/sdc/FilterObjects.cc index 472d41b4..80440015 100644 --- a/sdc/FilterObjects.cc +++ b/sdc/FilterObjects.cc @@ -277,6 +277,8 @@ filterObjects(std::string_view property, bool pattern_match = (op == "=~"); bool not_match = (op == "!="); bool not_pattern_match = (op == "!~"); + // Honor the global sta_case_insensitive_matching variable. + bool nocase = sta->caseInsensitiveMatching(); for (T *object : all) { PropertyValue value = properties.getProperty(object, property); std::string prop = value.to_string(network); @@ -297,10 +299,11 @@ filterObjects(std::string_view property, } } } - if ((exact_match && prop == pattern) - || (not_match && prop != pattern) - || (pattern_match && patternMatch(pattern, prop)) - || (not_pattern_match && !patternMatch(pattern, prop))) + bool eq = nocase ? stringEqual(prop, pattern) : (prop == pattern); + if ((exact_match && eq) + || (not_match && !eq) + || (pattern_match && patternMatchNoCase(pattern, prop, nocase)) + || (not_pattern_match && !patternMatchNoCase(pattern, prop, nocase))) filtered_objects.insert(object); } return filtered_objects; diff --git a/sdc/Sdc.tcl b/sdc/Sdc.tcl index b938b7b1..ec66e2f0 100644 --- a/sdc/Sdc.tcl +++ b/sdc/Sdc.tcl @@ -719,6 +719,10 @@ proc get_libs { args } { } proc find_liberty_libraries_matching { pattern regexp nocase } { + # Honor the global sta_case_insensitive_matching variable. + if { [case_insensitive_matching] } { + set nocase 1 + } # Remove "lib.db:" reference from the library name. set ix [string last ".db:" $pattern] if { $ix != -1 } { @@ -735,7 +739,8 @@ proc find_liberty_libraries_matching { pattern regexp nocase } { while { [$lib_iter has_next] } { set lib [$lib_iter next] set lib_name [get_name $lib] - if { (!$regexp && [string match $pattern2 $lib_name]) \ + if { (!$regexp && !$nocase && [string match $pattern2 $lib_name]) \ + || (!$regexp && $nocase && [string match -nocase $pattern2 $lib_name]) \ || ($regexp && $nocase && [regexp -nocase $pattern2 $lib_name]) \ || ($regexp && !$nocase && [regexp $pattern2 $lib_name]) } { lappend matches $lib diff --git a/sdc/Variables.tcl b/sdc/Variables.tcl index e4c51e4c..f480dd4d 100644 --- a/sdc/Variables.tcl +++ b/sdc/Variables.tcl @@ -227,6 +227,14 @@ proc trace_enable_collections { name1 name2 op } { enable_collections set_enable_collections } +trace add variable ::sta_case_insensitive_matching {read write} \ + sta::trace_case_insensitive_matching + +proc trace_case_insensitive_matching { name1 name2 op } { + trace_boolean_var $op ::sta_case_insensitive_matching \ + case_insensitive_matching set_case_insensitive_matching +} + trace add variable ::sta_pocv_quantile {read write} \ sta::trace_pocv_quantile diff --git a/search/Search.i b/search/Search.i index 892c8337..3c842469 100644 --- a/search/Search.i +++ b/search/Search.i @@ -1260,6 +1260,18 @@ set_enable_collections(bool enable) Sta::sta()->setEnableCollections(enable); } +bool +case_insensitive_matching() +{ + return Sta::sta()->caseInsensitiveMatching(); +} + +void +set_case_insensitive_matching(bool enable) +{ + Sta::sta()->setCaseInsensitiveMatching(enable); +} + %} // inline //////////////////////////////////////////////////////////////// diff --git a/search/Sta.cc b/search/Sta.cc index 29fd1bad..5c5d1236 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2639,6 +2639,18 @@ Sta::setEnableCollections(bool enable) variables_->setEnableCollections(enable); } +bool +Sta::caseInsensitiveMatching() const +{ + return variables_->caseInsensitiveMatching(); +} + +void +Sta::setCaseInsensitiveMatching(bool enable) +{ + variables_->setCaseInsensitiveMatching(enable); +} + //////////////////////////////////////////////////////////////// // Init one scene named "default". diff --git a/test/case_insensitive_matching.ok b/test/case_insensitive_matching.ok new file mode 100644 index 00000000..3e340734 --- /dev/null +++ b/test/case_insensitive_matching.ok @@ -0,0 +1,31 @@ +######## sta_case_insensitive_matching = 0 (default) ######## +get_ports clk1 = {clk1} +get_ports CLK1 = {} +get_ports CLK* = {} +get_cells R1 = {} +get_pins R1/CK = {} +get_nets R1Q = {} +get_clocks CORE_CLK = {} +get_libs NANGATE* = {} + +######## sta_case_insensitive_matching = 1 ######## +get_ports CLK1 = {clk1} +get_ports CLK* = {clk1 clk2 clk3} +get_ports In2 = {in2} +get_cells R1 = {r1} +get_cells U* = {u1 u2} +get_pins R1/CK = {r1/CK} +get_pins U1/* = {u1/A u1/Z} +get_nets R1Q = {r1q} +get_clocks CORE_CLK = {core_clk} +get_clocks CORE_* = {core_clk} +get_lib_cells */dff_x1 = {NangateOpenCellLibrary/DFF_X1} +get_lib_pins */dff_x1/ck = {CK} +get_cells -filter =~U* = {u1 u2} +get_cells -filter ==R1 = {r1} +get_libs NANGATE* = {NangateOpenCellLibrary} + +######## sta_case_insensitive_matching = 0 (restored) ######## +get_ports CLK1 = {} +get_ports clk1 = {clk1} +get_cells -filter ==R1 = {} diff --git a/test/case_insensitive_matching.tcl b/test/case_insensitive_matching.tcl new file mode 100644 index 00000000..db94b874 --- /dev/null +++ b/test/case_insensitive_matching.tcl @@ -0,0 +1,61 @@ +# Test the sta_case_insensitive_matching variable. +# The design uses lower case object names (ports clk1/in1, instances r1/u1, +# nets r1q, ...) so upper case patterns only match when the variable is on. +read_liberty ../examples/nangate45_typ.lib.gz +read_verilog ../examples/example1.v +link_design top +create_clock -name core_clk -period 1 {clk1 clk2 clk3} + +# Print the sorted full names matched by a get_* command. +proc show { label cmd } { + set names {} + foreach_in_collection obj [eval $cmd] { + lappend names [get_full_name $obj] + } + puts "$label = {[lsort $names]}" +} + +# Print the sorted names of matched liberty libraries. +proc show_libs { label cmd } { + set names {} + foreach_in_collection lib [eval $cmd] { + lappend names [get_name $lib] + } + puts "$label = {[lsort $names]}" +} + +puts "######## sta_case_insensitive_matching = 0 (default) ########" +show "get_ports clk1 " {get_ports -quiet clk1} +show "get_ports CLK1 " {get_ports -quiet CLK1} +show "get_ports CLK* " {get_ports -quiet CLK*} +show "get_cells R1 " {get_cells -quiet R1} +show "get_pins R1/CK " {get_pins -quiet R1/CK} +show "get_nets R1Q " {get_nets -quiet R1Q} +show "get_clocks CORE_CLK " {get_clocks -quiet CORE_CLK} +show_libs "get_libs NANGATE* " {get_libs -quiet NANGATE*} + +puts "" +puts "######## sta_case_insensitive_matching = 1 ########" +set sta_case_insensitive_matching 1 +show "get_ports CLK1 " {get_ports -quiet CLK1} +show "get_ports CLK* " {get_ports -quiet CLK*} +show "get_ports In2 " {get_ports -quiet In2} +show "get_cells R1 " {get_cells -quiet R1} +show "get_cells U* " {get_cells -quiet U*} +show "get_pins R1/CK " {get_pins -quiet R1/CK} +show "get_pins U1/* " {get_pins -quiet U1/*} +show "get_nets R1Q " {get_nets -quiet R1Q} +show "get_clocks CORE_CLK " {get_clocks -quiet CORE_CLK} +show "get_clocks CORE_* " {get_clocks -quiet CORE_*} +show "get_lib_cells */dff_x1" {get_lib_cells -quiet */dff_x1} +show "get_lib_pins */dff_x1/ck" {get_lib_pins -quiet */dff_x1/ck} +show "get_cells -filter =~U*" {get_cells -quiet -filter "full_name=~U*"} +show "get_cells -filter ==R1" {get_cells -quiet -filter "full_name==R1"} +show_libs "get_libs NANGATE* " {get_libs -quiet NANGATE*} + +puts "" +puts "######## sta_case_insensitive_matching = 0 (restored) ########" +set sta_case_insensitive_matching 0 +show "get_ports CLK1 " {get_ports -quiet CLK1} +show "get_ports clk1 " {get_ports -quiet clk1} +show "get_cells -filter ==R1" {get_cells -quiet -filter "full_name==R1"} diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index 79371c16..7c6947d0 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -141,6 +141,7 @@ record_example_tests { } record_public_tests { + case_insensitive_matching collections delay_calc_no_inv disable_clock_gating_check diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc index c25c48e4..10d51eda 100644 --- a/util/PatternMatch.cc +++ b/util/PatternMatch.cc @@ -28,11 +28,26 @@ #include #include "Sta.hh" +#include "Variables.hh" namespace sta { static std::string stripEscapedBus(std::string_view str); +// TCL variable sta_case_insensitive_matching. +// When enabled, all glob/regexp name matching done by the get_*/find_*_matching +// commands (ports, instances, nets, cells, clocks, ...) ignores case. +// Read lazily so that toggling the variable takes effect immediately and so +// that PatternMatch objects constructed before Sta exists are safe. +static bool +caseInsensitiveMatching() +{ + const Sta *sta = Sta::sta(); + return sta + && sta->variables() + && sta->variables()->caseInsensitiveMatching(); +} + PatternMatch::PatternMatch(std::string_view pattern, bool is_regexp, bool nocase, @@ -72,7 +87,7 @@ void PatternMatch::compileRegexp() { int flags = TCL_REG_ADVANCED; - if (nocase_) + if (nocase_ || caseInsensitiveMatching()) flags |= TCL_REG_NOCASE; std::string anchored_pattern; anchored_pattern += '^'; @@ -96,6 +111,11 @@ regexpWildcards(std::string_view pattern) bool PatternMatch::hasWildcards() const { + // When case-insensitive matching is enabled the find_*_matching functions + // must iterate and match each candidate name rather than doing an exact + // (case-sensitive) hashed lookup, so report the pattern as having wildcards. + if (caseInsensitiveMatching()) + return true; if (is_regexp_) return regexpWildcards(pattern_); else @@ -106,13 +126,16 @@ bool PatternMatch::match(std::string_view str) const { if (regexp_) { + // regexp_ was compiled with TCL_REG_NOCASE when case-insensitive + // matching is enabled. std::string buf(str); const char *cstr = buf.c_str(); return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1; } - return patternMatch(pattern_, str) || + bool nocase = caseInsensitiveMatching(); + return patternMatchNoCase(pattern_, str, nocase) || (Sta::sta()->stripEscapedBus() && - patternMatch(pattern_, stripEscapedBus(str)));; + patternMatchNoCase(pattern_, stripEscapedBus(str), nocase)); } std::string @@ -158,9 +181,10 @@ PatternMatch::matchNoCase(std::string_view str) const const char *cstr = buf.c_str(); return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1; } - return patternMatchNoCase(pattern_, str, nocase_) || + bool nocase = nocase_ || caseInsensitiveMatching(); + return patternMatchNoCase(pattern_, str, nocase) || (Sta::sta()->stripEscapedBus() && - patternMatchNoCase(pattern_, stripEscapedBus(str), nocase_)); + patternMatchNoCase(pattern_, stripEscapedBus(str), nocase)); } ////////////////////////////////////////////////////////////////