diff --git a/CMakeLists.txt b/CMakeLists.txt index dc61b90f..182c22d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,17 +221,20 @@ set(STA_SOURCE set(STA_TCL_FILES tcl/Init.tcl tcl/Util.tcl + tcl/CmdArgs.tcl + tcl/CmdUtil.tcl tcl/Graph.tcl tcl/Liberty.tcl tcl/Link.tcl tcl/Network.tcl tcl/NetworkEdit.tcl + tcl/Property.tcl tcl/Sdc.tcl tcl/Search.tcl - tcl/Cmds.tcl - tcl/Variables.tcl tcl/Sta.tcl tcl/Splash.tcl + tcl/Variables.tcl + tcl/WritePathSpice.tcl dcalc/DelayCalc.tcl parasitics/Parasitics.tcl power/Power.tcl diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl index 92e06b89..28b98e7c 100644 --- a/dcalc/DelayCalc.tcl +++ b/dcalc/DelayCalc.tcl @@ -120,5 +120,248 @@ proc set_delay_calculator { alg } { } } +define_cmd_args "set_pocv_sigma_factor" { factor } + +################################################################ + +define_cmd_args "set_assigned_delay" \ + {-cell|-net [-rise] [-fall] [-corner corner] [-min] [-max]\ + [-from from_pins] [-to to_pins] delay} + +# Change the delay for timing arcs between from_pins and to_pins matching +# on cell (instance) or net. +proc set_assigned_delay { args } { + set_assigned_delay_cmd "set_assigned_delay" $args +} + +proc set_assigned_delay_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {-corner -from -to} \ + flags {-cell -net -rise -fall -max -min} + check_argc_eq1 $cmd $cmd_args + set corner [parse_corner keys] + set min_max [parse_min_max_all_check_flags flags] + set to_rf [parse_rise_fall_flags flags] + + if [info exists keys(-from)] { + set from_pins [get_port_pins_error "from_pins" $keys(-from)] + } else { + sta_error 442 "$cmd missing -from argument." + } + if [info exists keys(-to)] { + set to_pins [get_port_pins_error "to_pins" $keys(-to)] + } else { + sta_error 443 "$cmd missing -to argument." + } + + set delay [lindex $cmd_args 0] + if {![string is double $delay]} { + sta_error 444 "$cmd delay is not a float." + } + set delay [time_ui_sta $delay] + + if {[info exists flags(-cell)] && [info exists flags(-net)]} { + sta_error 445 "set_annotated_delay -cell and -net options are mutually excluive." + } elseif {[info exists flags(-cell)]} { + if { $from_pins != {} } { + set inst [[lindex $from_pins 0] instance] + foreach pin $from_pins { + if {[$pin instance] != $inst} { + sta_error 446 "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]." + } + } + foreach pin $to_pins { + if {[$pin instance] != $inst} { + sta_error 447 "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" + } + } + } + } elseif {![info exists flags(-net)]} { + sta_error 448 "$cmd -cell or -net required." + } + foreach from_pin $from_pins { + set from_vertices [$from_pin vertices] + set_assigned_delay1 [lindex $from_vertices 0] \ + $to_pins $to_rf $corner $min_max $delay + if { [llength $from_vertices] == 2 } { + set_assigned_delay1 [lindex $from_vertices 1] \ + $to_pins $to_rf $corner $min_max $delay + } + } +} + +proc set_assigned_delay1 { from_vertex to_pins to_rf corner min_max delay } { + foreach to_pin $to_pins { + set to_vertices [$to_pin vertices] + set_assigned_delay2 $from_vertex [lindex $to_vertices 0] \ + $to_rf $corner $min_max $delay + if { [llength $to_vertices] == 2 } { + # Bidirect driver. + set_assigned_delay2 $from_vertex [lindex $to_vertices 1] \ + $to_rf $corner $min_max $delay + } + } +} + +proc set_assigned_delay2 {from_vertex to_vertex to_rf corner min_max delay} { + set edge_iter [$from_vertex out_edge_iterator] + while {[$edge_iter has_next]} { + set edge [$edge_iter next] + if { [$edge to] == $to_vertex \ + && ![timing_role_is_check [$edge role]] } { + foreach arc [$edge timing_arcs] { + if { $to_rf == "rise_fall" \ + || $to_rf eq [$arc to_edge_name] } { + set_arc_delay $edge $arc $corner $min_max $delay + } + } + } + } + $edge_iter finish +} + +################################################################ + +define_cmd_args "set_assigned_check" \ + {-setup|-hold|-recovery|-removal [-rise] [-fall]\ + [-corner corner] [-min] [-max]\ + [-from from_pins] [-to to_pins] [-clock rise|fall]\ + [-cond sdf_cond] check_value} + +proc set_assigned_check { args } { + set_assigned_check_cmd "set_assigned_check" $args +} + +proc set_assigned_check_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-from -to -corner -clock -cond} \ + flags {-setup -hold -recovery -removal -rise -fall -max -min} + check_argc_eq1 $cmd $cmd_args + + if { [info exists keys(-from)] } { + set from_pins [get_port_pins_error "from_pins" $keys(-from)] + } else { + sta_error 449 "$cmd missing -from argument." + } + set from_rf "rise_fall" + if { [info exists keys(-clock)] } { + set clk_arg $keys(-clock) + if { $clk_arg eq "rise" \ + || $clk_arg eq "fall" } { + set from_rf $clk_arg + } else { + sta_error 450 "$cmd -clock must be rise or fall." + } + } + + if { [info exists keys(-to)] } { + set to_pins [get_port_pins_error "to_pins" $keys(-to)] + } else { + sta_error 451 "$cmd missing -to argument." + } + set to_rf [parse_rise_fall_flags flags] + set corner [parse_corner keys] + set min_max [parse_min_max_all_check_flags flags] + + if { [info exists flags(-setup)] } { + set role "setup" + } elseif { [info exists flags(-hold)] } { + set role "hold" + } elseif { [info exists flags(-recovery)] } { + set role "recovery" + } elseif { [info exists flags(-removal)] } { + set role "removal" + } else { + sta_error 452 "$cmd missing -setup|-hold|-recovery|-removal check type.." + } + set cond "" + if { [info exists key(-cond)] } { + set cond $key(-cond) + } + set check_value [lindex $cmd_args 0] + if { ![string is double $check_value] } { + sta_error 453 "$cmd check_value is not a float." + } + set check_value [time_ui_sta $check_value] + + foreach from_pin $from_pins { + set from_vertices [$from_pin vertices] + set_assigned_check1 [lindex $from_vertices 0] $from_rf \ + $to_pins $to_rf $role $corner $min_max $cond $check_value + if { [llength $from_vertices] == 2 } { + set_assigned_check1 [lindex $from_vertices 1] $from_rf \ + $to_pins $to_rf $role $corner $min_max $cond $check_value + } + } +} + +proc set_assigned_check1 { from_vertex from_rf to_pins to_rf \ + role corner min_max cond check_value } { + foreach to_pin $to_pins { + set to_vertices [$to_pin vertices] + set_assigned_check2 $from_vertex $from_rf [lindex $to_vertices 0] \ + $to_rf $role $corner $min_max $cond $check_value + if { [llength $to_vertices] == 2 } { + # Bidirect driver. + set_assigned_check2 $from_vertex $from_rf \ + [lindex $to_vertices 1] $to_rf $role $corner $min_max \ + $cond $check_value + } + } +} + +proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \ + role corner min_max cond check_value } { + set edge_iter [$from_vertex out_edge_iterator] + while {[$edge_iter has_next]} { + set edge [$edge_iter next] + if { [$edge to] == $to_vertex } { + foreach arc [$edge timing_arcs] { + if { ($from_rf eq "rise_fall" \ + || $from_rf eq [$arc from_edge_name]) \ + && ($to_rf eq "rise_fall" \ + || $to_rf eq [$arc to_edge_name]) \ + && [$arc role] eq $role \ + && ($cond eq "" || [$arc sdf_cond] eq $cond) } { + set_arc_delay $edge $arc $corner $min_max $check_value + } + } + } + } + $edge_iter finish +} + +################################################################a + +define_cmd_args "set_assigned_transition" \ + {[-rise] [-fall] [-corner corner] [-min] [-max] slew pins} + +# Change the slew on a list of ports. +proc set_assigned_transition { args } { + parse_key_args "set_assigned_transition" args keys {-corner} \ + flags {-rise -fall -max -min} + + set corner [parse_corner keys] + set min_max [parse_min_max_all_check_flags flags] + set tr [parse_rise_fall_flags flags] + check_argc_eq2 "set_assigned_transition" $args + + set slew [lindex $args 0] + if {![string is double $slew]} { + sta_error 428 "set_assigned_transition transition is not a float." + } + set slew [time_ui_sta $slew] + set pins [get_port_pins_error "pins" [lindex $args 1]] + foreach pin $pins { + set vertices [$pin vertices] + set vertex [lindex $vertices 0] + set_annotated_slew $vertex $corner $min_max $tr $slew + if { [llength $vertices] == 2 } { + # Bidirect driver. + set vertex [lindex $vertices 1] + set_annotated_slew $vertex $min_max $tr $slew + } + } +} + # sta namespace end } diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt index 3709eae3..fae86a3f 100644 --- a/doc/ApiChanges.txt +++ b/doc/ApiChanges.txt @@ -16,7 +16,7 @@ This file summarizes STA API changes for each release. -Release 2.4.0 2023/01/?? +Release 2.4.0 2023/01/19 ------------------------- The Network API and associated containers now 'const' the network class objects. diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index c68c054c..fea750c3 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -3,6 +3,9 @@ OpenSTA Timing Analyzer Release Notes This file summarizes user visible changes for each release. +Release 2.4.0 2023/01/19 +------------------------- + The report_parasitics_annotation command reports SPEF annotation completeness. report_parasitics_annotation [-report_unannotated] @@ -12,6 +15,8 @@ pin activities for power analysis. read_power_activities -vcd filename +The report_cell command has been removed; use report_instance. + Release 2.3.3 2022/09/24 ------------------------- diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 78ea3b17..b6a82082 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/tcl/Cmds.tcl b/tcl/CmdArgs.tcl similarity index 100% rename from tcl/Cmds.tcl rename to tcl/CmdArgs.tcl diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl new file mode 100644 index 00000000..ec5122bd --- /dev/null +++ b/tcl/CmdUtil.tcl @@ -0,0 +1,307 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2022, 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 . + +namespace eval sta { + +################################################################ +# +# Utility commands +# +################################################################ + +define_cmd_args "help" {[pattern]} + +proc_redirect help { + variable cmd_args + + set arg_count [llength $args] + if { $arg_count == 0 } { + set pattern "*" + } elseif { $arg_count == 1 } { + set pattern [lindex $args 0] + } else { + cmd_usage_error "help" + } + set matches [array names cmd_args $pattern] + if { $matches != {} } { + foreach cmd [lsort $matches] { + show_cmd_args $cmd + } + } else { + sta_warn 300 "no commands match '$pattern'." + } +} + +proc show_cmd_args { cmd } { + variable cmd_args + + set max_col 80 + set indent 2 + set indent_str " " + set line $cmd + set col [string length $cmd] + set arglist $cmd_args($cmd) + # Break the arglist up into max_col length lines. + while {1} { + if {[regexp {(^[\n ]*)([a-zA-Z0-9_\\\|\-]+|\[[^\[]+\])(.*)} \ + $arglist ignore space arg rest]} { + set arg_length [string length $arg] + if { $col + $arg_length < $max_col } { + set line "$line $arg" + set col [expr $col + $arg_length + 1] + } else { + report_line $line + set line "$indent_str $arg" + set col [expr $indent + $arg_length + 1] + } + set arglist $rest + } else { + report_line $line + break + } + } +} + +# This is used in lieu of command completion to make sdc commands +# like get_ports be abbreviated get_port. +proc define_cmd_alias { alias cmd } { + eval "proc $alias { args } { eval [concat $cmd \$args] }" + namespace export $alias +} + +proc cmd_usage_error { cmd } { + variable cmd_args + + if [info exists cmd_args($cmd)] { + sta_error 404 "Usage: $cmd $cmd_args($cmd)" + } else { + sta_error 405 "Usage: $cmd argument error" + } +} + +################################################################ + +define_cmd_args "with_output_to_variable" { var { cmds }} + +# with_output_to_variable variable { command args... } +proc with_output_to_variable { var_name args } { + upvar 1 $var_name var + + set body [lindex $args 0] + sta::redirect_string_begin; + catch $body ret + set var [sta::redirect_string_end] + return $ret +} + +################################################################ + +define_cmd_args "report_units" {} + +proc report_units { args } { + check_argc_eq0 "report_units" $args + foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power" "distance"} { + report_line " $unit 1[unit_scale_abreviation $unit][unit_suffix $unit]" + } +} + +################################################################ + +define_cmd_args "set_cmd_units" \ + {[-capacitance cap_unit] [-resistance res_unit] [-time time_unit]\ + [-voltage voltage_unit] [-current current_unit] [-power power_unit]\ + [-distance distance_unit]} + +proc set_cmd_units { args } { + parse_key_args "set_cmd_units" args \ + keys {-capacitance -resistance -time -voltage -current -power \ + -distance -digits -suffix} \ + flags {} + + check_argc_eq0 "set_cmd_units" $args + set_unit_values "capacitance" -capacitance "f" keys + set_unit_values "time" -time "s" keys + set_unit_values "voltage" -voltage "v" keys + set_unit_values "current" -current "A" keys + set_unit_values "resistance" -resistance "ohm" keys + set_unit_values "distance" -distance "m" keys +} + +proc set_unit_values { unit key unit_name key_var } { + upvar 1 $key_var keys + if { [info exists keys($key)] } { + set value $keys($key) + if { [string equal -nocase $value $unit_name] } { + set_cmd_unit_scale $unit 1.0 + } else { + set prefix [string index $value 0] + set suffix [string range $value 1 end] + # unit includes "1" prefix + if { [string is digit $prefix] } { + set prefix [string index $value 1] + set suffix [string range $value 2 end] + } + if { [string equal -nocase $suffix $unit_name] } { + set scale [unit_prefix_scale $unit $prefix] + set_cmd_unit_scale $unit $scale + } else { + sta_error 515 "unknown $unit unit '$suffix'." + } + } + if [info exists keys(-digits)] { + set_cmd_unit_digits $unit $keys(-digits) + } + if [info exists keys(-suffix)] { + set_cmd_unit_suffix $unit $keys(-suffix) + } + } +} + +################################################################ + +define_cmd_args "delete_from_list" {list objs} + +proc delete_from_list { list objects } { + delete_objects_from_list_cmd $list $objects +} + +proc delete_objects_from_list_cmd { list objects } { + set list0 [lindex $list 0] + set list_is_object [is_object $list0] + set list_type [object_type $list0] + foreach obj $objects { + # If the list is a collection of tcl objects (returned by get_*), + # convert the obj to be removed from a name to an object of the same + # type. + if {$list_is_object && ![is_object $obj]} { + if {$list_type == "Clock"} { + set obj [find_clock $obj] + } elseif {$list_type == "Port"} { + set top_instance [top_instance] + set top_cell [$top_instance cell] + set obj [$top_cell find_port $obj] + } elseif {$list_type == "Pin"} { + set obj [find_pin $obj] + } elseif {$list_type == "Instance"} { + set obj [find_instance $obj] + } elseif {$list_type == "Net"} { + set obj [find_net $obj] + } elseif {$list_type == "LibertyLibrary"} { + set obj [find_liberty $obj] + } elseif {$list_type == "LibertyCell"} { + set obj [find_liberty_cell $obj] + } elseif {$list_type == "LibertyPort"} { + set obj [get_lib_pins $obj] + } else { + sta_error 439 "unsupported object type $list_type." + } + } + set index [lsearch $list $obj] + if { $index != -1 } { + set list [lreplace $list $index $index] + } + } + return $list +} + +################################################################ + +proc set_cmd_namespace { namespc } { + if { $namespc == "sdc" || $namespc == "sta" } { + set_cmd_namespace_cmd $namespc + } else { + sta_error 589 "unknown namespace $namespc." + } +} + +################################################################ + +define_cmd_args "report_object_full_names" {objects} + +proc report_object_full_names { objects } { + foreach obj [sort_by_full_name $objects] { + report_line [get_full_name $obj] + } +} + +define_cmd_args "report_object_names" {objects} + +proc report_object_names { objects } { + foreach obj [sort_by_name $objects] { + report_line [get_name $obj] + } +} + +################################################################ + +define_cmd_args "get_name" {object} +define_cmd_args "get_full_name" {object} + +################################################################ + +proc get_name { object } { + return [get_object_property $object "name"] +} + +proc get_full_name { object } { + return [get_object_property $object "full_name"] +} + +proc sort_by_name { objects } { + return [lsort -command name_cmp $objects] +} + +proc name_cmp { obj1 obj2 } { + return [string compare [get_name $obj1] [get_name $obj2]] +} + +proc sort_by_full_name { objects } { + return [lsort -command full_name_cmp $objects] +} + +proc full_name_cmp { obj1 obj2 } { + return [string compare [get_full_name $obj1] [get_full_name $obj2]] +} + +proc get_object_type { obj } { + set object_type [object_type $obj] + if { $object_type == "Clock" } { + return "clock" + } elseif { $object_type == "LibertyCell" } { + return "lib_cell" + } elseif { $object_type == "LibertyPort" } { + return "lib_pin" + } elseif { $object_type == "Cell" } { + return "cell" + } elseif { $object_type == "Instance" } { + return "instance" + } elseif { $object_type == "Port" } { + return "port" + } elseif { $object_type == "Pin" } { + return "pin" + } elseif { $object_type == "Net" } { + return "net" + } elseif { $object_type == "Edge" } { + return "timing_arc" + } elseif { $object_type == "TimingArcSet" } { + return "timing_arc" + } else { + return "?" + } +} + +# sta namespace end. +} diff --git a/tcl/Exception.i b/tcl/Exception.i index 0854be02..d8b9ca5b 100644 --- a/tcl/Exception.i +++ b/tcl/Exception.i @@ -1,5 +1,3 @@ -%{ - // OpenSTA, Static Timing Analyzer // Copyright (c) 2022, Parallax Software, Inc. // @@ -16,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -%} - %exception { try { $function } catch (std::bad_alloc &) { diff --git a/tcl/Network.tcl b/tcl/Network.tcl index b5f4e0cd..98f515e2 100644 --- a/tcl/Network.tcl +++ b/tcl/Network.tcl @@ -18,26 +18,6 @@ namespace eval sta { -proc set_cmd_namespace { namespc } { - if { $namespc == "sdc" || $namespc == "sta" } { - set_cmd_namespace_cmd $namespc - } else { - sta_error 589 "unknown namespace $namespc." - } -} - -################################################################ - -define_cmd_args "report_cell" \ - {[-connections] [-verbose] instance_path [> filename] [>> filename]} - -# Alias for report_instance. -proc_redirect report_cell { - eval report_instance $args -} - -################################################################ - define_cmd_args "report_instance" \ {[-connections] [-verbose] instance_path [> filename] [>> filename]} @@ -224,28 +204,6 @@ proc report_cell_ { cell } { $iter finish } -define_cmd_args "report_pin" {[-corner corner] [-digits digits] pin\ - [> filename] [>> filename]} - -proc_redirect report_pin { - global sta_report_default_digits - - parse_key_args "report_pin" args keys {-corner -digits} \ - flags {-connections -verbose -hier_pins} - set corner [parse_corner_or_all keys] - set digits $sta_report_default_digits - if { [info exists keys(-digits)] } { - set digits $keys(-digits) - } - check_argc_eq1 "report_pin" $args - set pin_path [lindex $args 0] - set pin [get_pin_warn "pin" $pin_path] - - if { $pin != "NULL" } { - report_pin_ $pin $corner $digits - } -} - ################################################################ define_cmd_args "report_net" \ @@ -497,54 +455,5 @@ proc capacitances_str { cap_r_min cap_r_max cap_f_min cap_f_max digits } { } } -################################################################ -# -# Debugging functions -# -################################################################ - -proc_redirect report_network { - report_hierarchy [top_instance] -} - -proc report_hierarchy { inst } { - report_instance1 $inst 1 1 - foreach child [instance_sorted_children $inst] { - report_hierarchy $child - } -} - -proc port_direction_any_input { dir } { - return [expr { $dir == "input" || $dir == "bidirect" } ] -} - -proc port_direction_any_output { dir } { - return [expr { $dir == "output" \ - || $dir == "bidirect" \ - || $dir == "tristate" } ] -} - -# collect instance pins into list -proc instance_pins { instance } { - set pins {} - set iter [$instance pin_iterator] - while {[$iter has_next]} { - lappend pins [$iter next] - } - $iter finish - return $pins -} - -# collect ports into a list -proc cell_ports { cell } { - set ports {} - set iter [$cell port_iterator] - while {[$iter has_next]} { - lappend ports [$iter next] - } - $iter finish - return $ports -} - # sta namespace end } diff --git a/tcl/NetworkEdit.tcl b/tcl/NetworkEdit.tcl index 714dd483..4757134d 100644 --- a/tcl/NetworkEdit.tcl +++ b/tcl/NetworkEdit.tcl @@ -18,6 +18,55 @@ namespace eval sta { +define_cmd_args "make_instance" {inst_path lib_cell} + +proc make_instance { inst_path lib_cell } { + set lib_cell [get_lib_cell_warn "lib_cell" $lib_cell] + if { $lib_cell != "NULL" } { + set path_regexp [path_regexp] + if {[regexp $path_regexp $inst_path ignore path_name inst_name]} { + set parent [find_instance $path_name] + if { $parent == "NULL" } { + # Parent instance not found. This could be a typo, but since + # SDC does not escape hierarchy dividers it can also be + # an escaped name. + set inst_name $inst_path + set parent [top_instance] + } + } else { + set inst_name $inst_path + set parent [top_instance] + } + return [make_instance_cmd $inst_name $lib_cell $parent] + } else { + return 0 + } +} + +################################################################ + +define_cmd_args "make_net" {} + +proc make_net { net_path } { + # Copy backslashes that will be removed by foreach. + set net_path [string map {\\ \\\\} $net_path] + set path_regexp [path_regexp] + if {[regexp $path_regexp $net_path ignore path_name net_name]} { + set parent [find_instance $path_name] + if { $parent == "NULL" } { + return 0 + } + } else { + set parent [top_instance] + set net_name $net_path + } + return [make_net_cmd $net_name $parent] +} + +################################################################ + +define_cmd_args "connect_pin" {net pin} + proc connect_pin { net pin } { set insts_port [parse_connect_pin $pin] if { $insts_port == 0 } { @@ -110,39 +159,7 @@ proc parse_connect_pins { arg } { ################################################################ -proc delete_instance { instance } { - if { [is_object $instance] } { - set object_type [object_type $instance] - if { $object_type == "Instance" } { - set inst $instance - } else { - sta_error 587 "unsupported object type $object_type." - } - } else { - set inst [find_instance $instance] - } - if { $inst != "NULL" } { - delete_instance_cmd $inst - } -} - -################################################################ - -proc delete_net { net } { - if { [is_object $net] } { - set object_type [object_type $net] - if { $object_type != "Net" } { - sta_error 588 "unsupported object type $object_type." - } - } else { - set net [find_net $net] - } - if { $net != "NULL" } { - delete_net_cmd $net - } -} - -################################################################ +define_cmd_args "disconnect_pin" {net -all|pin} proc disconnect_pin { net pin } { set net [get_net_warn "net" $net] @@ -168,58 +185,48 @@ proc disconnect_pin { net pin } { } } -proc disconnect_pins { net pins } { - sta_warn 603 "disconnect_pins is deprecated. Use disconnect_pin." - foreach pin $pins { - disconnect_pin $net $pins - } -} - ################################################################ -proc make_instance { inst_path lib_cell } { - set lib_cell [get_lib_cell_warn "lib_cell" $lib_cell] - if { $lib_cell != "NULL" } { - set path_regexp [path_regexp] - if {[regexp $path_regexp $inst_path ignore path_name inst_name]} { - set parent [find_instance $path_name] - if { $parent == "NULL" } { - # Parent instance not found. This could be a typo, but since - # SDC does not escape hierarchy dividers it can also be - # an escaped name. - set inst_name $inst_path - set parent [top_instance] - } +define_cmd_args "delete_instance" {inst} + +proc delete_instance { instance } { + if { [is_object $instance] } { + set object_type [object_type $instance] + if { $object_type == "Instance" } { + set inst $instance } else { - set inst_name $inst_path - set parent [top_instance] + sta_error 587 "unsupported object type $object_type." } - return [make_instance_cmd $inst_name $lib_cell $parent] } else { - return 0 + set inst [find_instance $instance] + } + if { $inst != "NULL" } { + delete_instance_cmd $inst } } ################################################################ -proc make_net { net_path } { - # Copy backslashes that will be removed by foreach. - set net_path [string map {\\ \\\\} $net_path] - set path_regexp [path_regexp] - if {[regexp $path_regexp $net_path ignore path_name net_name]} { - set parent [find_instance $path_name] - if { $parent == "NULL" } { - return 0 +define_cmd_args "delete_net" {net} + +proc delete_net { net } { + if { [is_object $net] } { + set object_type [object_type $net] + if { $object_type != "Net" } { + sta_error 588 "unsupported object type $object_type." } } else { - set parent [top_instance] - set net_name $net_path + set net [find_net $net] + } + if { $net != "NULL" } { + delete_net_cmd $net } - return [make_net_cmd $net_name $parent] } ################################################################ +define_cmd_args "replace_cell" {instance lib_cell} + proc replace_cell { instance lib_cell } { set cell [get_lib_cell_warn "lib_cell" $lib_cell] if { $cell != "NULL" } { @@ -236,6 +243,8 @@ proc replace_cell { instance lib_cell } { } } +################################################################ + proc path_regexp {} { global hierarchy_separator set id_regexp "\[^${hierarchy_separator}\]+" diff --git a/tcl/Property.tcl b/tcl/Property.tcl new file mode 100644 index 00000000..e3fdb20d --- /dev/null +++ b/tcl/Property.tcl @@ -0,0 +1,116 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2022, 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 . + +namespace eval sta { + +define_cmd_args "get_property" \ + {[-object_type cell|pin|net|port|clock|timing_arc] object property} + +proc get_property { args } { + return [get_property_cmd "get_property" "-object_type" $args] +} + +proc get_property_cmd { cmd type_key cmd_args } { + parse_key_args $cmd cmd_args keys $type_key flags {-quiet} + set quiet [info exists flags(-quiet)] + check_argc_eq2 $cmd $cmd_args + set object [lindex $cmd_args 0] + if { $object == "" } { + sta_error 491 "$cmd object is null." + } elseif { ![is_object $object] } { + if [info exists keys($type_key)] { + set object_type $keys($type_key) + } else { + sta_error 492 "$cmd $type_key must be specified with object name argument." + } + set object [get_property_object_type $object_type $object $quiet] + } + set prop [lindex $cmd_args 1] + return [get_object_property $object $prop] +} + +proc get_object_property { object prop } { + if { [is_object $object] } { + set object_type [object_type $object] + if { $object_type == "Instance" } { + return [instance_property $object $prop] + } elseif { $object_type == "Pin" } { + return [pin_property $object $prop] + } elseif { $object_type == "Net" } { + return [net_property $object $prop] + } elseif { $object_type == "Clock" } { + return [clock_property $object $prop] + } elseif { $object_type == "Port" } { + return [port_property $object $prop] + } elseif { $object_type == "LibertyPort" } { + return [liberty_port_property $object $prop] + } elseif { $object_type == "LibertyCell" } { + return [liberty_cell_property $object $prop] + } elseif { $object_type == "Cell" } { + return [cell_property $object $prop] + } elseif { $object_type == "Library" } { + return [library_property $object $prop] + } elseif { $object_type == "LibertyLibrary" } { + return [liberty_library_property $object $prop] + } elseif { $object_type == "Edge" } { + return [edge_property $object $prop] + } elseif { $object_type == "PathEnd" } { + return [path_end_property $object $prop] + } elseif { $object_type == "PathRef" } { + return [path_ref_property $object $prop] + } elseif { $object_type == "TimingArcSet" } { + return [timing_arc_set_property $object $prop] + } else { + sta_error 606 "get_property unsupported object type $object_type." + } + } else { + sta_error 493 "get_property $object is not an object." + } +} + +proc get_property_object_type { object_type object_name quiet } { + set object "NULL" + if { $object_type == "instance" \ + || $object_type == "cell"} { + set object [get_cells -quiet $object_name] + } elseif { $object_type == "pin" } { + set object [get_pins -quiet $object_name] + } elseif { $object_type == "net" } { + set object [get_nets -quiet $object_name] + } elseif { $object_type == "port" } { + set object [get_ports -quiet $object_name] + } elseif { $object_type == "clock" } { + set object [get_clocks -quiet $object_name] + } elseif { $object_type == "liberty_cell" \ + || $object_type == "lib_cell"} { + set object [get_lib_cells -quiet $object_name] + } elseif { $object_type == "liberty_port" \ + || $object_type == "lib_pin" } { + set object [get_lib_pins -quiet $object_name] + } elseif { $object_type == "library" \ + || $object_type == "lib"} { + set object [get_libs -quiet $object_name] + } else { + sta_error 494 "$object_type not supported." + } + if { $object == "NULL" && !$quiet } { + sta_error 495 "$object_type '$object_name' not found." + } + return [lindex $object 0] +} + +# sta namespace end. +} diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl index af7a37d8..cc6e8fc3 100644 --- a/tcl/Sdc.tcl +++ b/tcl/Sdc.tcl @@ -289,58 +289,6 @@ proc check_unit_scale { unit scale } { } } -################################################################ - -define_cmd_args "set_cmd_units" \ - {[-capacitance cap_unit] [-resistance res_unit] [-time time_unit]\ - [-voltage voltage_unit] [-current current_unit] [-power power_unit]\ - [-distance distance_unit]} - -proc set_cmd_units { args } { - parse_key_args "set_cmd_units" args \ - keys {-capacitance -resistance -time -voltage -current -power \ - -distance -digits -suffix} \ - flags {} - - check_argc_eq0 "set_cmd_units" $args - set_unit_values "capacitance" -capacitance "f" keys - set_unit_values "time" -time "s" keys - set_unit_values "voltage" -voltage "v" keys - set_unit_values "current" -current "A" keys - set_unit_values "resistance" -resistance "ohm" keys - set_unit_values "distance" -distance "m" keys -} - -proc set_unit_values { unit key unit_name key_var } { - upvar 1 $key_var keys - if { [info exists keys($key)] } { - set value $keys($key) - if { [string equal -nocase $value $unit_name] } { - set_cmd_unit_scale $unit 1.0 - } else { - set prefix [string index $value 0] - set suffix [string range $value 1 end] - # unit includes "1" prefix - if { [string is digit $prefix] } { - set prefix [string index $value 1] - set suffix [string range $value 2 end] - } - if { [string equal -nocase $suffix $unit_name] } { - set scale [unit_prefix_scale $unit $prefix] - set_cmd_unit_scale $unit $scale - } else { - sta_error 515 "unknown $unit unit '$suffix'." - } - } - if [info exists keys(-digits)] { - set_cmd_unit_digits $unit $keys(-digits) - } - if [info exists keys(-suffix)] { - set_cmd_unit_suffix $unit $keys(-suffix) - } - } -} - ################################################################ # # Object Access Commands @@ -1134,6 +1082,24 @@ proc create_clock { args } { ################################################################ +define_cmd_args "delete_clock" {[-all] clocks} + +proc delete_clock { args } { + parse_key_args "delete_clock" args keys {} flags {-all} + if { [info exists flags(-all)] } { + check_argc_eq0 "delete_clock" $args + set clks [all_clocks] + } else { + check_argc_eq1 "delete_clock" $args + set clks [get_clocks_warn "clocks" [lindex $args 0]] + } + foreach clk $clks { + remove_clock_cmd $clk + } +} + +################################################################ + define_cmd_args "create_generated_clock" \ {[-name clock_name] -source master_pin [-master_clock clock]\ [-divide_by divisor | -multiply_by multiplier]\ @@ -1270,6 +1236,30 @@ proc create_generated_clock { args } { ################################################################ +define_cmd_args "delete_generated_clock" {[-all] clocks} + +proc delete_generated_clock { args } { + remove_gclk_cmd "delete_generated_clock" $args +} + +proc remove_gclk_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {} flags {-all} + if { [info exists flags(-all)] } { + check_argc_eq0 $cmd $cmd_args + set clks [all_clocks] + } else { + check_argc_eq1 $cmd $cmd_args + set clks [get_clocks_warn "clocks" [lindex $cmd_args 0]] + } + foreach clk $clks { + if { [$clk is_generated] } { + remove_clock_cmd $clk + } + } +} + +################################################################ + define_cmd_args "group_path" \ {-name group_name [-weight weight] [-critical_range range]\ [-default] [-comment comment]\ @@ -1459,6 +1449,66 @@ proc set_clock_groups { args } { ################################################################ +define_cmd_args "unset_clock_groups" \ + {[-logically_exclusive] [-physically_exclusive]\ + [-asynchronous] [-name names] [-all]} + +proc unset_clock_groups { args } { + unset_clk_groups_cmd "unset_clock_groups" $args +} + +proc unset_clk_groups_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-name} \ + flags {-logically_exclusive -physically_exclusive -asynchronous -all} + + set all [info exists flags(-all)] + set names {} + if {[info exists keys(-name)]} { + set names $keys(-name) + } + + if { $all && $names != {} } { + sta_error 454 "the -all and -name options are mutually exclusive." + } + if { !$all && $names == {} } { + sta_error 455 "either -all or -name options must be specified." + } + + set logically_exclusive [info exists flags(-logically_exclusive)] + set physically_exclusive [info exists flags(-physically_exclusive)] + set asynchronous [info exists flags(-asynchronous)] + + if { ($logically_exclusive+$physically_exclusive+$asynchronous) == 0 } { + sta_error 456 "one of -logically_exclusive, -physically_exclusive or -asynchronous is required." + } + if { ($logically_exclusive+$physically_exclusive+$asynchronous) > 1 } { + sta_error 457 "the keywords -logically_exclusive, -physically_exclusive and -asynchronous are mutually exclusive." + } + + if { $all } { + if { $logically_exclusive } { + unset_clock_groups_logically_exclusive "NULL" + } elseif { $physically_exclusive } { + unset_clock_groups_physically_exclusive "NULL" + } elseif { $asynchronous } { + unset_clock_groups_asynchronous "NULL" + } + } else { + foreach name $names { + if { $logically_exclusive } { + unset_clock_groups_logically_exclusive $name + } elseif { $physically_exclusive } { + unset_clock_groups_physically_exclusive $name + } elseif { $asynchronous } { + unset_clock_groups_asynchronous $name + } + } + } +} + +################################################################ + define_cmd_args "set_clock_latency" \ {[-source] [-clock clock] [-rise] [-fall] [-min] [-max]\ [-early] [-late] delay objects} @@ -1518,6 +1568,50 @@ proc set_clock_latency { args } { ################################################################ +define_cmd_args "unset_clock_latency" {[-source] [-clock clock] objects} + +proc unset_clock_latency { args } { + unset_clk_latency_cmd "unset_clock_latency" $args +} + +proc unset_clk_latency_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {-clock} flags {-source} + check_argc_eq1 $cmd $cmd_args + set objects [lindex $cmd_args 0] + parse_clk_port_pin_arg $objects clks pins + set pin_clk "NULL" + if { [info exists keys(-clock)] } { + set pin_clk [get_clock_warn "clock" $keys(-clock)] + if { $clks != {} } { + sta_warn 303 "-clock ignored for clock objects." + } + } + + if {[info exists flags(-source)]} { + # Source latency. + foreach clk $clks { + unset_clock_insertion_cmd $clk "NULL" + } + foreach pin $pins { + # Source only allowed on clocks and clock pins. + if { ![is_clock_pin $pin] } { + sta_error 458 "-source '[$pin path_name]' is not a clock pin." + } + unset_clock_insertion_cmd $pin_clk $pin + } + } else { + # Latency. + foreach clk $clks { + unset_clock_latency_cmd $clk "NULL" + } + foreach pin $pins { + unset_clock_latency_cmd $pin_clk $pin + } + } +} + +################################################################ + define_cmd_args "set_sense" \ {[-type clock|data] [-positive] [-negative] [-pulse pulse_type]\ [-stop_propagation] [-clocks clocks] pins} @@ -1611,6 +1705,18 @@ proc set_clock_transition { args } { ################################################################ +define_cmd_args "unset_clock_transition" {clocks} + +proc unset_clock_transition { args } { + check_argc_eq1 "unset_clock_transition" $args + set clks [get_clocks_warn "clocks" [lindex $args 0]] + foreach clk $clks { + unset_clock_slew_cmd $clk + } +} + +################################################################ + # -rise/-fall are obsolete. define_cmd_args "set_clock_uncertainty" \ {[-from|-rise_from|-fall_from from_clock]\ @@ -1707,6 +1813,92 @@ proc set_clock_uncertainty { args } { ################################################################ +define_cmd_args "unset_clock_uncertainty" \ + {[-from|-rise_from|-fall_from from_clock]\ + [-to|-rise_to|-fall_to to_clock] [-rise] [-fall]\ + [-setup] [-hold] [objects]} + +proc unset_clock_uncertainty { args } { + unset_clk_uncertainty_cmd "unset_clock_uncertainty" $args +} + +proc unset_clk_uncertainty_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ + flags {-rise -fall -setup -hold} + + set min_max "min_max" + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set min_max "max" + } + if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set min_max "min" + } + + if { [info exists keys(-from)] } { + set from_key "-from" + set from_rf "rise_fall" + } elseif { [info exists keys(-rise_from)] } { + set from_key "-rise_from" + set from_rf "rise" + } elseif { [info exists keys(-fall_from)] } { + set from_key "-fall_from" + set from_rf "fall" + } else { + set from_key "none" + } + + if { [info exists keys(-to)] } { + set to_key "-to" + set to_rf "rise_fall" + } elseif { [info exists keys(-rise_to)] } { + set to_key "-rise_to" + set to_rf "rise" + } elseif { [info exists keys(-fall_to)] } { + set to_key "-fall_to" + set to_rf "fall" + } else { + set to_key "none" + } + + if { $from_key != "none" && $to_key == "none" \ + || $from_key == "none" && $to_key != "none" } { + sta_error 459 "-from/-to must be used together." + } elseif { $from_key != "none" && $to_key != "none" } { + # Inter-clock uncertainty. + check_argc_eq0 "unset_clock_uncertainty" $cmd_args + + # -from/-to can be lists. + set from_clks [get_clocks_warn "from_clocks" $keys($from_key)] + set to_clks [get_clocks_warn "to_clocks" $keys($to_key)] + + foreach from_clk $from_clks { + foreach to_clk $to_clks { + unset_inter_clock_uncertainty $from_clk $from_rf \ + $to_clk $to_rf $min_max + } + } + } else { + # Single clock uncertainty. + check_argc_eq1 $cmd $cmd_args + if { [info exists keys(-rise)] \ + || [info exists keys(-fall)] } { + sta_error 460 "-rise, -fall options not allowed for single clock uncertainty." + } + set objects [lindex $cmd_args 0] + parse_clk_port_pin_arg $objects clks pins + + foreach clk $clks { + unset_clock_uncertainty_clk $clk $min_max + } + foreach pin $pins { + unset_clock_uncertainty_pin $pin $min_max + } + } +} + +################################################################ + define_cmd_args "set_data_check" \ {[-from from_pin] [-rise_from from_pin] [-fall_from from_pin]\ [-to to_pin] [-rise_to to_pin] [-fall_to to_pin]\ @@ -1764,6 +1956,66 @@ proc set_data_check { args } { ################################################################ +define_cmd_args "unset_data_check" \ + {[-from from_pin] [-rise_from from_pin] [-fall_from from_pin]\ + [-to to_pin] [-rise_to to_pin] [-fall_to to_pin]\ + [-setup | -hold] [-clock clock]} + +proc unset_data_check { args } { + unset_data_checks_cmd "unset_data_check" $args +} + +proc unset_data_checks_cmd { cmd cmd_args } { + parse_key_args cmd cmd_args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to -clock} \ + flags {-setup -hold} + check_argc_eq0 $cmd $cmd_args + + set from_rf "rise_fall" + set to_rf "rise_fall" + set clk "NULL" + set setup_hold "max" + if [info exists keys(-from)] { + set from [get_port_pin_error "from_pin" $keys(-from)] + } elseif [info exists keys(-rise_from)] { + set from [get_port_pin_error "from_pin" $keys(-rise_from)] + set from_rf "rise" + } elseif [info exists keys(-fall_from)] { + set from [get_port_pin_error "from_pin" $keys(-fall_from)] + set from_rf "fall" + } else { + sta_error 461 "missing -from, -rise_from or -fall_from argument." + } + + if [info exists keys(-to)] { + set to [get_port_pin_error "to_pin" $keys(-to)] + } elseif [info exists keys(-rise_to)] { + set to [get_port_pin_error "to_pin" $keys(-rise_to)] + set to_rf "rise" + } elseif [info exists keys(-fall_to)] { + set to [get_port_pin_error "to_pin" $keys(-fall_to)] + set to_rf "fall" + } else { + sta_error 462 "missing -to, -rise_to or -fall_to argument." + } + + if [info exists keys(-clock)] { + set clk [get_clock_warn "clock" $keys(-clock)] + } + + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set setup_hold "setup" + } elseif { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set setup_hold "hold" + } else { + set setup_hold "setup_hold" + } + + unset_data_check_cmd $from $from_rf $to $to_rf $clk $setup_hold +} + +################################################################ + define_cmd_args "set_disable_timing" \ {[-from from_port] [-to to_port] objects} @@ -1899,6 +2151,105 @@ proc parse_disable_cell_ports { cell port_name } { ################################################################ +define_cmd_args "unset_disable_timing" \ + {[-from from_port] [-to to_port] objects} + +proc unset_disable_timing { args } { + unset_disable_cmd "unset_disable_timing" $args +} + +proc unset_disable_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {-from -to} flags {} + check_argc_eq1 $cmd $cmd_args + + set from "" + if { [info exists keys(-from)] } { + set from $keys(-from) + } + set to "" + if { [info exists keys(-to)] } { + set to $keys(-to) + } + parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg $cmd_args \ + libcells libports insts ports pins edges timing_arc_sets + + if { ([info exists keys(-from)] || [info exists keys(-to)]) \ + && ($libports != {} || $pins != {} || $ports != {}) } { + sta_warn 304 "-from/-to keywords ignored for lib_pin, port and pin arguments." + } + + foreach libcell $libcells { + unset_disable_timing_cell $libcell $from $to + } + foreach libport $libports { + unset_disable_lib_port $libport + } + foreach inst $insts { + unset_disable_timing_instance $inst $from $to + } + foreach pin $pins { + unset_disable_pin $pin + } + foreach port $ports { + unset_disable_port $port + } + foreach edge $edges { + unset_disable_edge $edge + } + foreach timing_arc_set $timing_arc_sets { + unset_disable_timing_arc_set $timing_arc_set + } +} + +proc unset_disable_timing_cell { cell from to } { + set from_ports [parse_disable_cell_ports $cell $from] + set to_ports [parse_disable_cell_ports $cell $to] + if { $from_ports == "NULL" && $to_ports == "NULL" } { + unset_disable_cell $cell "NULL" "NULL" + } elseif { $from_ports == "NULL" } { + foreach to_port $to_ports { + unset_disable_cell $cell "NULL" $to_port + } + } elseif { $to_ports == "NULL" } { + foreach from_port $from_ports { + unset_disable_cell $cell $from_port "NULL" + } + } else { + foreach from_port $from_ports { + foreach to_port $to_ports { + unset_disable_cell $cell $from_port $to_port + } + } + } +} + +proc unset_disable_timing_instance { inst from to } { + set from_ports [parse_disable_inst_ports $inst $from] + set to_ports [parse_disable_inst_ports $inst $to] + if { ![$inst is_leaf] } { + sta_error 463 "-from/-to hierarchical instance not supported." + } + if { $from_ports == "NULL" && $to_ports == "NULL" } { + unset_disable_instance $inst "NULL" "NULL" + } elseif { $from_ports == "NULL" } { + foreach to_port $to_ports { + unset_disable_instance $inst "NULL" $to_port + } + } elseif { $to_ports == "NULL" } { + foreach from_port $from_ports { + unset_disable_instance $inst $from_port "NULL" + } + } else { + foreach from_port $from_ports { + foreach to_port $to_ports { + unset_disable_instance $inst $from_port $to_port + } + } + } +} + +################################################################ + define_cmd_args "set_false_path" \ {[-setup] [-hold] [-rise] [-fall] [-reset_path] [-comment comment]\ [-from from_list] [-rise_from from_list] [-fall_from from_list]\ @@ -2048,6 +2399,17 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } { ################################################################ +define_cmd_args "unset_input_delay" \ + {[-rise] [-fall] [-max] [-min]\ + [-clock clock] [-clock_fall]\ + port_pin_list} + +proc unset_input_delay { args } { + unset_port_delay "unset_input_delay" "unset_input_delay_cmd" $args +} + +################################################################ + define_cmd_args "set_max_delay" \ {[-rise] [-fall] [-ignore_clock_latency] [-reset_path] [-comment comment]\ [-from from_list] [-rise_from from_list] [-fall_from from_list]\ @@ -2237,6 +2599,56 @@ proc set_multicycle_path { args } { ################################################################ +define_cmd_args "unset_path_exceptions" \ + {[-setup] [-hold] [-rise] [-fall] [-from from_list]\ + [-rise_from from_list] [-fall_from from_list]\ + [-through through_list] [-rise_through through_list]\ + [-fall_through through_list] [-to to_list] [-rise_to to_list]\ + [-fall_to to_list]} + +proc unset_path_exceptions { args } { + unset_path_exceptions_cmd "unset_path_exceptions" $args +} + +proc unset_path_exceptions_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ + flags {-setup -hold -rise -fall} 0 + + set min_max "min_max" + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set min_max "max" + } + if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set min_max "min" + } + + set arg_error 0 + set from [parse_from_arg keys arg_error] + set thrus [parse_thrus_arg cmd_args arg_error] + set to [parse_to_arg keys flags arg_error] + if { $arg_error } { + delete_from_thrus_to $from $thrus $to + sta_error 464 "$cmd command failed." + return 0 + } + + check_for_key_args $cmd cmd_args + if { $cmd_args != {} } { + delete_from_thrus_to $from $thrus $to + sta_error 465 "positional arguments not supported." + } + if { ($from == "NULL" && $thrus == "" && $to == "NULL") } { + delete_from_thrus_to $from $thrus $to + sta_error 466 "-from, -through or -to required." + } + + reset_path_cmd $from $thrus $to $min_max + delete_from_thrus_to $from $thrus $to +} + +################################################################ + define_cmd_args "set_output_delay" \ {[-rise] [-fall] [-max] [-min]\ [-clock clock] [-clock_fall]\ @@ -2251,6 +2663,44 @@ proc set_output_delay { args } { ################################################################ +define_cmd_args "unset_output_delay" \ + {[-rise] [-fall] [-max] [-min]\ + [-clock clock] [-clock_fall]\ + port_pin_list} + +proc unset_output_delay { args } { + unset_port_delay "unset_output_delay" "unset_output_delay_cmd" $args +} + +proc unset_port_delay { cmd swig_cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-clock -reference_pin} \ + flags {-rise -fall -max -min -clock_fall } + check_argc_eq1 $cmd $cmd_args + + set pins [get_port_pins_error "pins" [lindex $cmd_args 0]] + + set clk "NULL" + if [info exists keys(-clock)] { + set clk [get_clock_warn "clock" $keys(-clock)] + } + + if [info exists flags(-clock_fall)] { + set clk_rf "fall" + } else { + set clk_rf "rise" + } + + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_flags flags] + + foreach pin $pins { + $swig_cmd $pin $tr $clk $clk_rf $min_max + } +} + +################################################################ + define_cmd_args "set_propagated_clock" {objects} proc set_propagated_clock { objects } { @@ -2267,6 +2717,20 @@ proc set_propagated_clock { objects } { } } +################################################################ + +define_cmd_args "unset_propagated_clock" {objects} + +proc unset_propagated_clock { objects } { + parse_clk_port_pin_arg $objects clks pins + foreach clk $clks { + unset_propagated_clock_cmd $clk + } + foreach pin $pins { + unset_propagated_clock_pin_cmd $pin + } +} + ################################################################ # # Environment Commands @@ -2295,6 +2759,17 @@ proc set_case_analysis { value pins } { ################################################################ +define_cmd_args "unset_case_analysis" {pins} + +proc unset_case_analysis { pins } { + set pins1 [get_port_pins_error "pins" $pins] + foreach pin $pins1 { + unset_case_analysis_cmd $pin + } +} + +################################################################ + define_cmd_args "set_drive" {[-rise] [-fall] [-min] [-max] \ resistance ports} @@ -2424,6 +2899,12 @@ proc set_driving_cell { args } { } } +proc port_direction_any_output { dir } { + return [expr { $dir == "output" \ + || $dir == "bidirect" \ + || $dir == "tristate" } ] +} + ################################################################ define_cmd_args "set_fanout_load" {fanout ports} @@ -2789,6 +3270,17 @@ proc set_timing_derate { args } { } } +################################################################ + +define_cmd_args "unset_timing_derate" {} + +proc unset_timing_derate { args } { + check_argc_eq0 "unset_timing_derate" $args + unset_timing_derate_cmd +} + +################################################################ + proc parse_from_arg { keys_var arg_error_var } { upvar 1 $keys_var keys diff --git a/tcl/Search.tcl b/tcl/Search.tcl index 59fa0db8..06a06540 100644 --- a/tcl/Search.tcl +++ b/tcl/Search.tcl @@ -1109,6 +1109,41 @@ proc report_clock_min_period { args } { } } +################################################################ + +define_cmd_args "set_disable_inferred_clock_gating" { objects } + +proc set_disable_inferred_clock_gating { objects } { + set_disable_inferred_clock_gating_cmd $objects +} + +proc set_disable_inferred_clock_gating_cmd { objects } { + parse_inst_port_pin_arg $objects insts pins + foreach inst $insts { + disable_clock_gating_check_inst $inst + } + foreach pin $pins { + disable_clock_gating_check_pin $pin + } +} + +################################################################ + +define_cmd_args "unset_disable_inferred_clock_gating" { objects } + +proc unset_disable_inferred_clock_gating { objects } { + unset_disable_inferred_clock_gating_cmd $objects +} + +proc unset_disable_inferred_clock_gating_cmd { objects } { + parse_inst_port_pin_arg $objects insts pins + foreach inst $insts { + unset_disable_clock_gating_check_inst $inst + } + foreach pin $pins { + unset_disable_clock_gating_check_pin $pin + } +} ################################################################ diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl index 9a72b117..15b3a69d 100644 --- a/tcl/Sta.tcl +++ b/tcl/Sta.tcl @@ -17,932 +17,11 @@ namespace eval sta { ################################################################ -# +# # Non-SDC commands # ################################################################ -define_cmd_args "delete_clock" {[-all] clocks} - -proc delete_clock { args } { - parse_key_args "delete_clock" args keys {} flags {-all} - if { [info exists flags(-all)] } { - check_argc_eq0 "delete_clock" $args - set clks [all_clocks] - } else { - check_argc_eq1 "delete_clock" $args - set clks [get_clocks_warn "clocks" [lindex $args 0]] - } - foreach clk $clks { - remove_clock_cmd $clk - } -} - -################################################################ - -define_cmd_args "delete_generated_clock" {[-all] clocks} - -proc delete_generated_clock { args } { - remove_gclk_cmd "delete_generated_clock" $args -} - -################################################################ - -define_cmd_args "set_disable_inferred_clock_gating" { objects } - -proc set_disable_inferred_clock_gating { objects } { - set_disable_inferred_clock_gating_cmd $objects -} - -proc set_disable_inferred_clock_gating_cmd { objects } { - parse_inst_port_pin_arg $objects insts pins - foreach inst $insts { - disable_clock_gating_check_inst $inst - } - foreach pin $pins { - disable_clock_gating_check_pin $pin - } -} - -################################################################ - -define_cmd_args "unset_case_analysis" {pins} - -proc unset_case_analysis { pins } { - set pins1 [get_port_pins_error "pins" $pins] - foreach pin $pins1 { - unset_case_analysis_cmd $pin - } -} - -################################################################ - -define_cmd_args "unset_clock_groups" \ - {[-logically_exclusive] [-physically_exclusive]\ - [-asynchronous] [-name names] [-all]} - -proc unset_clock_groups { args } { - unset_clk_groups_cmd "unset_clock_groups" $args -} - -proc unset_clk_groups_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args \ - keys {-name} \ - flags {-logically_exclusive -physically_exclusive -asynchronous -all} - - set all [info exists flags(-all)] - set names {} - if {[info exists keys(-name)]} { - set names $keys(-name) - } - - if { $all && $names != {} } { - sta_error 454 "the -all and -name options are mutually exclusive." - } - if { !$all && $names == {} } { - sta_error 455 "either -all or -name options must be specified." - } - - set logically_exclusive [info exists flags(-logically_exclusive)] - set physically_exclusive [info exists flags(-physically_exclusive)] - set asynchronous [info exists flags(-asynchronous)] - - if { ($logically_exclusive+$physically_exclusive+$asynchronous) == 0 } { - sta_error 456 "one of -logically_exclusive, -physically_exclusive or -asynchronous is required." - } - if { ($logically_exclusive+$physically_exclusive+$asynchronous) > 1 } { - sta_error 457 "the keywords -logically_exclusive, -physically_exclusive and -asynchronous are mutually exclusive." - } - - if { $all } { - if { $logically_exclusive } { - unset_clock_groups_logically_exclusive "NULL" - } elseif { $physically_exclusive } { - unset_clock_groups_physically_exclusive "NULL" - } elseif { $asynchronous } { - unset_clock_groups_asynchronous "NULL" - } - } else { - foreach name $names { - if { $logically_exclusive } { - unset_clock_groups_logically_exclusive $name - } elseif { $physically_exclusive } { - unset_clock_groups_physically_exclusive $name - } elseif { $asynchronous } { - unset_clock_groups_asynchronous $name - } - } - } -} - -################################################################ - -define_cmd_args "unset_clock_latency" {[-source] [-clock clock] objects} - -proc unset_clock_latency { args } { - unset_clk_latency_cmd "unset_clock_latency" $args -} - -proc unset_clk_latency_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args keys {-clock} flags {-source} - check_argc_eq1 $cmd $cmd_args - set objects [lindex $cmd_args 0] - parse_clk_port_pin_arg $objects clks pins - set pin_clk "NULL" - if { [info exists keys(-clock)] } { - set pin_clk [get_clock_warn "clock" $keys(-clock)] - if { $clks != {} } { - sta_warn 303 "-clock ignored for clock objects." - } - } - - if {[info exists flags(-source)]} { - # Source latency. - foreach clk $clks { - unset_clock_insertion_cmd $clk "NULL" - } - foreach pin $pins { - # Source only allowed on clocks and clock pins. - if { ![is_clock_pin $pin] } { - sta_error 458 "-source '[$pin path_name]' is not a clock pin." - } - unset_clock_insertion_cmd $pin_clk $pin - } - } else { - # Latency. - foreach clk $clks { - unset_clock_latency_cmd $clk "NULL" - } - foreach pin $pins { - unset_clock_latency_cmd $pin_clk $pin - } - } -} - -################################################################ - -define_cmd_args "unset_clock_transition" {clocks} - -proc unset_clock_transition { args } { - check_argc_eq1 "unset_clock_transition" $args - set clks [get_clocks_warn "clocks" [lindex $args 0]] - foreach clk $clks { - unset_clock_slew_cmd $clk - } -} - -################################################################ - -define_cmd_args "unset_clock_uncertainty" \ - {[-from|-rise_from|-fall_from from_clock]\ - [-to|-rise_to|-fall_to to_clock] [-rise] [-fall]\ - [-setup] [-hold] [objects]} - -proc unset_clock_uncertainty { args } { - unset_clk_uncertainty_cmd "unset_clock_uncertainty" $args -} - -proc unset_clk_uncertainty_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args \ - keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ - flags {-rise -fall -setup -hold} - - set min_max "min_max" - if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { - set min_max "max" - } - if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { - set min_max "min" - } - - if { [info exists keys(-from)] } { - set from_key "-from" - set from_rf "rise_fall" - } elseif { [info exists keys(-rise_from)] } { - set from_key "-rise_from" - set from_rf "rise" - } elseif { [info exists keys(-fall_from)] } { - set from_key "-fall_from" - set from_rf "fall" - } else { - set from_key "none" - } - - if { [info exists keys(-to)] } { - set to_key "-to" - set to_rf "rise_fall" - } elseif { [info exists keys(-rise_to)] } { - set to_key "-rise_to" - set to_rf "rise" - } elseif { [info exists keys(-fall_to)] } { - set to_key "-fall_to" - set to_rf "fall" - } else { - set to_key "none" - } - - if { $from_key != "none" && $to_key == "none" \ - || $from_key == "none" && $to_key != "none" } { - sta_error 459 "-from/-to must be used together." - } elseif { $from_key != "none" && $to_key != "none" } { - # Inter-clock uncertainty. - check_argc_eq0 "unset_clock_uncertainty" $cmd_args - - # -from/-to can be lists. - set from_clks [get_clocks_warn "from_clocks" $keys($from_key)] - set to_clks [get_clocks_warn "to_clocks" $keys($to_key)] - - foreach from_clk $from_clks { - foreach to_clk $to_clks { - unset_inter_clock_uncertainty $from_clk $from_rf \ - $to_clk $to_rf $min_max - } - } - } else { - # Single clock uncertainty. - check_argc_eq1 $cmd $cmd_args - if { [info exists keys(-rise)] \ - || [info exists keys(-fall)] } { - sta_error 460 "-rise, -fall options not allowed for single clock uncertainty." - } - set objects [lindex $cmd_args 0] - parse_clk_port_pin_arg $objects clks pins - - foreach clk $clks { - unset_clock_uncertainty_clk $clk $min_max - } - foreach pin $pins { - unset_clock_uncertainty_pin $pin $min_max - } - } -} - -################################################################ - -define_cmd_args "unset_data_check" \ - {[-from from_pin] [-rise_from from_pin] [-fall_from from_pin]\ - [-to to_pin] [-rise_to to_pin] [-fall_to to_pin]\ - [-setup | -hold] [-clock clock]} - -proc unset_data_check { args } { - unset_data_checks_cmd "unset_data_check" $args -} - -proc unset_data_checks_cmd { cmd cmd_args } { - parse_key_args cmd cmd_args \ - keys {-from -rise_from -fall_from -to -rise_to -fall_to -clock} \ - flags {-setup -hold} - check_argc_eq0 $cmd $cmd_args - - set from_rf "rise_fall" - set to_rf "rise_fall" - set clk "NULL" - set setup_hold "max" - if [info exists keys(-from)] { - set from [get_port_pin_error "from_pin" $keys(-from)] - } elseif [info exists keys(-rise_from)] { - set from [get_port_pin_error "from_pin" $keys(-rise_from)] - set from_rf "rise" - } elseif [info exists keys(-fall_from)] { - set from [get_port_pin_error "from_pin" $keys(-fall_from)] - set from_rf "fall" - } else { - sta_error 461 "missing -from, -rise_from or -fall_from argument." - } - - if [info exists keys(-to)] { - set to [get_port_pin_error "to_pin" $keys(-to)] - } elseif [info exists keys(-rise_to)] { - set to [get_port_pin_error "to_pin" $keys(-rise_to)] - set to_rf "rise" - } elseif [info exists keys(-fall_to)] { - set to [get_port_pin_error "to_pin" $keys(-fall_to)] - set to_rf "fall" - } else { - sta_error 462 "missing -to, -rise_to or -fall_to argument." - } - - if [info exists keys(-clock)] { - set clk [get_clock_warn "clock" $keys(-clock)] - } - - if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { - set setup_hold "setup" - } elseif { [info exists flags(-hold)] && ![info exists flags(-setup)] } { - set setup_hold "hold" - } else { - set setup_hold "setup_hold" - } - - unset_data_check_cmd $from $from_rf $to $to_rf $clk $setup_hold -} - -################################################################ - -define_cmd_args "unset_disable_inferred_clock_gating" { objects } - -proc unset_disable_inferred_clock_gating { objects } { - unset_disable_inferred_clock_gating_cmd $objects -} - -proc unset_disable_inferred_clock_gating_cmd { objects } { - parse_inst_port_pin_arg $objects insts pins - foreach inst $insts { - unset_disable_clock_gating_check_inst $inst - } - foreach pin $pins { - unset_disable_clock_gating_check_pin $pin - } -} - -################################################################ - -define_cmd_args "unset_disable_timing" \ - {[-from from_port] [-to to_port] objects} - -proc unset_disable_timing { args } { - unset_disable_cmd "unset_disable_timing" $args -} - -proc unset_disable_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args keys {-from -to} flags {} - check_argc_eq1 $cmd $cmd_args - - set from "" - if { [info exists keys(-from)] } { - set from $keys(-from) - } - set to "" - if { [info exists keys(-to)] } { - set to $keys(-to) - } - parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg $cmd_args \ - libcells libports insts ports pins edges timing_arc_sets - - if { ([info exists keys(-from)] || [info exists keys(-to)]) \ - && ($libports != {} || $pins != {} || $ports != {}) } { - sta_warn 304 "-from/-to keywords ignored for lib_pin, port and pin arguments." - } - - foreach libcell $libcells { - unset_disable_timing_cell $libcell $from $to - } - foreach libport $libports { - unset_disable_lib_port $libport - } - foreach inst $insts { - unset_disable_timing_instance $inst $from $to - } - foreach pin $pins { - unset_disable_pin $pin - } - foreach port $ports { - unset_disable_port $port - } - foreach edge $edges { - unset_disable_edge $edge - } - foreach timing_arc_set $timing_arc_sets { - unset_disable_timing_arc_set $timing_arc_set - } -} - -proc unset_disable_timing_cell { cell from to } { - set from_ports [parse_disable_cell_ports $cell $from] - set to_ports [parse_disable_cell_ports $cell $to] - if { $from_ports == "NULL" && $to_ports == "NULL" } { - unset_disable_cell $cell "NULL" "NULL" - } elseif { $from_ports == "NULL" } { - foreach to_port $to_ports { - unset_disable_cell $cell "NULL" $to_port - } - } elseif { $to_ports == "NULL" } { - foreach from_port $from_ports { - unset_disable_cell $cell $from_port "NULL" - } - } else { - foreach from_port $from_ports { - foreach to_port $to_ports { - unset_disable_cell $cell $from_port $to_port - } - } - } -} - -proc unset_disable_timing_instance { inst from to } { - set from_ports [parse_disable_inst_ports $inst $from] - set to_ports [parse_disable_inst_ports $inst $to] - if { ![$inst is_leaf] } { - sta_error 463 "-from/-to hierarchical instance not supported." - } - if { $from_ports == "NULL" && $to_ports == "NULL" } { - unset_disable_instance $inst "NULL" "NULL" - } elseif { $from_ports == "NULL" } { - foreach to_port $to_ports { - unset_disable_instance $inst "NULL" $to_port - } - } elseif { $to_ports == "NULL" } { - foreach from_port $from_ports { - unset_disable_instance $inst $from_port "NULL" - } - } else { - foreach from_port $from_ports { - foreach to_port $to_ports { - unset_disable_instance $inst $from_port $to_port - } - } - } -} - -################################################################ - -define_cmd_args "unset_generated_clock" {[-all] clocks} - -proc unset_generated_clock { args } { - unset_gclk_cmd "unset_generated_clock" $args -} - -proc remove_gclk_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args keys {} flags {-all} - if { [info exists flags(-all)] } { - check_argc_eq0 $cmd $cmd_args - set clks [all_clocks] - } else { - check_argc_eq1 $cmd $cmd_args - set clks [get_clocks_warn "clocks" [lindex $cmd_args 0]] - } - foreach clk $clks { - if { [$clk is_generated] } { - remove_clock_cmd $clk - } - } -} - -################################################################ - -define_cmd_args "unset_input_delay" \ - {[-rise] [-fall] [-max] [-min]\ - [-clock clock] [-clock_fall]\ - port_pin_list} - -proc unset_input_delay { args } { - unset_port_delay "unset_input_delay" "unset_input_delay_cmd" $args -} - -################################################################ - -define_cmd_args "unset_output_delay" \ - {[-rise] [-fall] [-max] [-min]\ - [-clock clock] [-clock_fall]\ - port_pin_list} - -proc unset_output_delay { args } { - unset_port_delay "unset_output_delay" "unset_output_delay_cmd" $args -} - -proc unset_port_delay { cmd swig_cmd cmd_args } { - parse_key_args $cmd cmd_args \ - keys {-clock -reference_pin} \ - flags {-rise -fall -max -min -clock_fall } - check_argc_eq1 $cmd $cmd_args - - set pins [get_port_pins_error "pins" [lindex $cmd_args 0]] - - set clk "NULL" - if [info exists keys(-clock)] { - set clk [get_clock_warn "clock" $keys(-clock)] - } - - if [info exists flags(-clock_fall)] { - set clk_rf "fall" - } else { - set clk_rf "rise" - } - - set tr [parse_rise_fall_flags flags] - set min_max [parse_min_max_all_flags flags] - - foreach pin $pins { - $swig_cmd $pin $tr $clk $clk_rf $min_max - } -} - -################################################################ - -define_cmd_args "unset_path_exceptions" \ - {[-setup] [-hold] [-rise] [-fall] [-from from_list]\ - [-rise_from from_list] [-fall_from from_list]\ - [-through through_list] [-rise_through through_list]\ - [-fall_through through_list] [-to to_list] [-rise_to to_list]\ - [-fall_to to_list]} - -proc unset_path_exceptions { args } { - unset_path_exceptions_cmd "unset_path_exceptions" $args -} - -proc unset_path_exceptions_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args \ - keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ - flags {-setup -hold -rise -fall} 0 - - set min_max "min_max" - if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { - set min_max "max" - } - if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { - set min_max "min" - } - - set arg_error 0 - set from [parse_from_arg keys arg_error] - set thrus [parse_thrus_arg cmd_args arg_error] - set to [parse_to_arg keys flags arg_error] - if { $arg_error } { - delete_from_thrus_to $from $thrus $to - sta_error 464 "$cmd command failed." - return 0 - } - - check_for_key_args $cmd cmd_args - if { $cmd_args != {} } { - delete_from_thrus_to $from $thrus $to - sta_error 465 "positional arguments not supported." - } - if { ($from == "NULL" && $thrus == "" && $to == "NULL") } { - delete_from_thrus_to $from $thrus $to - sta_error 466 "-from, -through or -to required." - } - - reset_path_cmd $from $thrus $to $min_max - delete_from_thrus_to $from $thrus $to -} - -################################################################ - -define_cmd_args "unset_propagated_clock" {objects} - -proc unset_propagated_clock { objects } { - parse_clk_port_pin_arg $objects clks pins - foreach clk $clks { - unset_propagated_clock_cmd $clk - } - foreach pin $pins { - unset_propagated_clock_pin_cmd $pin - } -} - -################################################################ - -define_cmd_args "unset_timing_derate" {} - -proc unset_timing_derate { args } { - check_argc_eq0 "unset_timing_derate" $args - unset_timing_derate_cmd -} - -################################################################ -# -# Network editing commands -# -################################################################ - -define_cmd_args "connect_pin" {net pin} -# deprecated 2.0.16 05/02/2019 -define_cmd_args "connect_pins" {net pins} - -define_cmd_args "delete_instance" {inst} - -define_cmd_args "delete_net" {net} - -define_cmd_args "disconnect_pin" {net -all|pin} -# deprecated 2.0.16 05/02/2019 -define_cmd_args "disconnect_pins" {net -all|pins} - -define_cmd_args "make_instance" {inst_path lib_cell} - -define_cmd_args "make_net" {} - -define_cmd_args "replace_cell" {instance lib_cell} - -define_cmd_args "insert_buffer" {buffer_name buffer_cell net load_pins\ - buffer_out_net_name} - -################################################################ -# -# Delay calculation commands -# -################################################################ - -define_cmd_args "set_assigned_delay" \ - {-cell|-net [-rise] [-fall] [-corner corner] [-min] [-max]\ - [-from from_pins] [-to to_pins] delay} - -# Change the delay for timing arcs between from_pins and to_pins matching -# on cell (instance) or net. -proc set_assigned_delay { args } { - set_assigned_delay_cmd "set_assigned_delay" $args -} - -proc set_assigned_delay_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args keys {-corner -from -to} \ - flags {-cell -net -rise -fall -max -min} - check_argc_eq1 $cmd $cmd_args - set corner [parse_corner keys] - set min_max [parse_min_max_all_check_flags flags] - set to_rf [parse_rise_fall_flags flags] - - if [info exists keys(-from)] { - set from_pins [get_port_pins_error "from_pins" $keys(-from)] - } else { - sta_error 442 "$cmd missing -from argument." - } - if [info exists keys(-to)] { - set to_pins [get_port_pins_error "to_pins" $keys(-to)] - } else { - sta_error 443 "$cmd missing -to argument." - } - - set delay [lindex $cmd_args 0] - if {![string is double $delay]} { - sta_error 444 "$cmd delay is not a float." - } - set delay [time_ui_sta $delay] - - if {[info exists flags(-cell)] && [info exists flags(-net)]} { - sta_error 445 "set_annotated_delay -cell and -net options are mutually excluive." - } elseif {[info exists flags(-cell)]} { - if { $from_pins != {} } { - set inst [[lindex $from_pins 0] instance] - foreach pin $from_pins { - if {[$pin instance] != $inst} { - sta_error 446 "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]." - } - } - foreach pin $to_pins { - if {[$pin instance] != $inst} { - sta_error 447 "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" - } - } - } - } elseif {![info exists flags(-net)]} { - sta_error 448 "$cmd -cell or -net required." - } - foreach from_pin $from_pins { - set from_vertices [$from_pin vertices] - set_assigned_delay1 [lindex $from_vertices 0] \ - $to_pins $to_rf $corner $min_max $delay - if { [llength $from_vertices] == 2 } { - set_assigned_delay1 [lindex $from_vertices 1] \ - $to_pins $to_rf $corner $min_max $delay - } - } -} - -proc set_assigned_delay1 { from_vertex to_pins to_rf corner min_max delay } { - foreach to_pin $to_pins { - set to_vertices [$to_pin vertices] - set_assigned_delay2 $from_vertex [lindex $to_vertices 0] \ - $to_rf $corner $min_max $delay - if { [llength $to_vertices] == 2 } { - # Bidirect driver. - set_assigned_delay2 $from_vertex [lindex $to_vertices 1] \ - $to_rf $corner $min_max $delay - } - } -} - -proc set_assigned_delay2 {from_vertex to_vertex to_rf corner min_max delay} { - set edge_iter [$from_vertex out_edge_iterator] - while {[$edge_iter has_next]} { - set edge [$edge_iter next] - if { [$edge to] == $to_vertex \ - && ![timing_role_is_check [$edge role]] } { - foreach arc [$edge timing_arcs] { - if { $to_rf == "rise_fall" \ - || $to_rf eq [$arc to_edge_name] } { - set_arc_delay $edge $arc $corner $min_max $delay - } - } - } - } - $edge_iter finish -} - -################################################################ - -define_cmd_args "set_assigned_check" \ - {-setup|-hold|-recovery|-removal [-rise] [-fall]\ - [-corner corner] [-min] [-max]\ - [-from from_pins] [-to to_pins] [-clock rise|fall]\ - [-cond sdf_cond] check_value} - -proc set_assigned_check { args } { - set_assigned_check_cmd "set_assigned_check" $args -} - -proc set_assigned_check_cmd { cmd cmd_args } { - parse_key_args $cmd cmd_args \ - keys {-from -to -corner -clock -cond} \ - flags {-setup -hold -recovery -removal -rise -fall -max -min} - check_argc_eq1 $cmd $cmd_args - - if { [info exists keys(-from)] } { - set from_pins [get_port_pins_error "from_pins" $keys(-from)] - } else { - sta_error 449 "$cmd missing -from argument." - } - set from_rf "rise_fall" - if { [info exists keys(-clock)] } { - set clk_arg $keys(-clock) - if { $clk_arg eq "rise" \ - || $clk_arg eq "fall" } { - set from_rf $clk_arg - } else { - sta_error 450 "$cmd -clock must be rise or fall." - } - } - - if { [info exists keys(-to)] } { - set to_pins [get_port_pins_error "to_pins" $keys(-to)] - } else { - sta_error 451 "$cmd missing -to argument." - } - set to_rf [parse_rise_fall_flags flags] - set corner [parse_corner keys] - set min_max [parse_min_max_all_check_flags flags] - - if { [info exists flags(-setup)] } { - set role "setup" - } elseif { [info exists flags(-hold)] } { - set role "hold" - } elseif { [info exists flags(-recovery)] } { - set role "recovery" - } elseif { [info exists flags(-removal)] } { - set role "removal" - } else { - sta_error 452 "$cmd missing -setup|-hold|-recovery|-removal check type.." - } - set cond "" - if { [info exists key(-cond)] } { - set cond $key(-cond) - } - set check_value [lindex $cmd_args 0] - if { ![string is double $check_value] } { - sta_error 453 "$cmd check_value is not a float." - } - set check_value [time_ui_sta $check_value] - - foreach from_pin $from_pins { - set from_vertices [$from_pin vertices] - set_assigned_check1 [lindex $from_vertices 0] $from_rf \ - $to_pins $to_rf $role $corner $min_max $cond $check_value - if { [llength $from_vertices] == 2 } { - set_assigned_check1 [lindex $from_vertices 1] $from_rf \ - $to_pins $to_rf $role $corner $min_max $cond $check_value - } - } -} - -proc set_assigned_check1 { from_vertex from_rf to_pins to_rf \ - role corner min_max cond check_value } { - foreach to_pin $to_pins { - set to_vertices [$to_pin vertices] - set_assigned_check2 $from_vertex $from_rf [lindex $to_vertices 0] \ - $to_rf $role $corner $min_max $cond $check_value - if { [llength $to_vertices] == 2 } { - # Bidirect driver. - set_assigned_check2 $from_vertex $from_rf \ - [lindex $to_vertices 1] $to_rf $role $corner $min_max \ - $cond $check_value - } - } -} - -proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \ - role corner min_max cond check_value } { - set edge_iter [$from_vertex out_edge_iterator] - while {[$edge_iter has_next]} { - set edge [$edge_iter next] - if { [$edge to] == $to_vertex } { - foreach arc [$edge timing_arcs] { - if { ($from_rf eq "rise_fall" \ - || $from_rf eq [$arc from_edge_name]) \ - && ($to_rf eq "rise_fall" \ - || $to_rf eq [$arc to_edge_name]) \ - && [$arc role] eq $role \ - && ($cond eq "" || [$arc sdf_cond] eq $cond) } { - set_arc_delay $edge $arc $corner $min_max $check_value - } - } - } - } - $edge_iter finish -} - -################################################################a - -define_cmd_args "set_assigned_transition" \ - {[-rise] [-fall] [-corner corner] [-min] [-max] slew pins} - -# Change the slew on a list of ports. -proc set_assigned_transition { args } { - parse_key_args "set_assigned_transition" args keys {-corner} \ - flags {-rise -fall -max -min} - - set corner [parse_corner keys] - set min_max [parse_min_max_all_check_flags flags] - set tr [parse_rise_fall_flags flags] - check_argc_eq2 "set_assigned_transition" $args - - set slew [lindex $args 0] - if {![string is double $slew]} { - sta_error 428 "set_assigned_transition transition is not a float." - } - set slew [time_ui_sta $slew] - set pins [get_port_pins_error "pins" [lindex $args 1]] - foreach pin $pins { - set vertices [$pin vertices] - set vertex [lindex $vertices 0] - set_annotated_slew $vertex $corner $min_max $tr $slew - if { [llength $vertices] == 2 } { - # Bidirect driver. - set vertex [lindex $vertices 1] - set_annotated_slew $vertex $min_max $tr $slew - } - } -} - -################################################################a - -# compatibility -define_cmd_args "read_parasitics" \ - {[-min]\ - [-max]\ - [-elmore]\ - [-path path]\ - [-increment]\ - [-pin_cap_included]\ - [-keep_capacitive_coupling]\ - [-coupling_reduction_factor factor]\ - [-reduce_to pi_elmore|pi_pole_residue2]\ - [-delete_after_reduce]\ - [-quiet]\ - [-save]\ - filename} - -################################################################ -# -# Utility commands -# -################################################################ - -define_cmd_args "delete_from_list" {list objs} - -proc delete_from_list { list objects } { - delete_objects_from_list_cmd $list $objects -} - -proc delete_objects_from_list_cmd { list objects } { - set list0 [lindex $list 0] - set list_is_object [is_object $list0] - set list_type [object_type $list0] - foreach obj $objects { - # If the list is a collection of tcl objects (returned by get_*), - # convert the obj to be removed from a name to an object of the same - # type. - if {$list_is_object && ![is_object $obj]} { - if {$list_type == "Clock"} { - set obj [find_clock $obj] - } elseif {$list_type == "Port"} { - set top_instance [top_instance] - set top_cell [$top_instance cell] - set obj [$top_cell find_port $obj] - } elseif {$list_type == "Pin"} { - set obj [find_pin $obj] - } elseif {$list_type == "Instance"} { - set obj [find_instance $obj] - } elseif {$list_type == "Net"} { - set obj [find_net $obj] - } elseif {$list_type == "LibertyLibrary"} { - set obj [find_liberty $obj] - } elseif {$list_type == "LibertyCell"} { - set obj [find_liberty_cell $obj] - } elseif {$list_type == "LibertyPort"} { - set obj [get_lib_pins $obj] - } else { - sta_error 439 "unsupported object type $list_type." - } - } - set index [lsearch $list $obj] - if { $index != -1 } { - set list [lreplace $list $index $index] - } - } - return $list -} - -################################################################ - define_cmd_args "get_fanin" \ {-to sink_list [-flat] [-only_cells] [-startpoints_only]\ [-levels level_count] [-pin_levels pin_count]\ @@ -1060,162 +139,6 @@ proc get_fanout { args } { ################################################################ -define_cmd_args "get_name" {objects} -define_cmd_args "get_full_name" {objects} - -################################################################ - -define_cmd_args "get_property" \ - {[-object_type cell|pin|net|port|clock|timing_arc] object property} - -proc get_property { args } { - return [get_property_cmd "get_property" "-object_type" $args] -} - -################################################################ - -proc get_property_cmd { cmd type_key cmd_args } { - parse_key_args $cmd cmd_args keys $type_key flags {-quiet} - set quiet [info exists flags(-quiet)] - check_argc_eq2 $cmd $cmd_args - set object [lindex $cmd_args 0] - if { $object == "" } { - sta_error 491 "$cmd object is null." - } elseif { ![is_object $object] } { - if [info exists keys($type_key)] { - set object_type $keys($type_key) - } else { - sta_error 492 "$cmd $type_key must be specified with object name argument." - } - set object [get_property_object_type $object_type $object $quiet] - } - set prop [lindex $cmd_args 1] - return [get_object_property $object $prop] -} - -proc get_object_property { object prop } { - if { [is_object $object] } { - set object_type [object_type $object] - if { $object_type == "Instance" } { - return [instance_property $object $prop] - } elseif { $object_type == "Pin" } { - return [pin_property $object $prop] - } elseif { $object_type == "Net" } { - return [net_property $object $prop] - } elseif { $object_type == "Clock" } { - return [clock_property $object $prop] - } elseif { $object_type == "Port" } { - return [port_property $object $prop] - } elseif { $object_type == "LibertyPort" } { - return [liberty_port_property $object $prop] - } elseif { $object_type == "LibertyCell" } { - return [liberty_cell_property $object $prop] - } elseif { $object_type == "Cell" } { - return [cell_property $object $prop] - } elseif { $object_type == "Library" } { - return [library_property $object $prop] - } elseif { $object_type == "LibertyLibrary" } { - return [liberty_library_property $object $prop] - } elseif { $object_type == "Edge" } { - return [edge_property $object $prop] - } elseif { $object_type == "PathEnd" } { - return [path_end_property $object $prop] - } elseif { $object_type == "PathRef" } { - return [path_ref_property $object $prop] - } elseif { $object_type == "TimingArcSet" } { - return [timing_arc_set_property $object $prop] - } else { - sta_error 606 "get_property unsupported object type $object_type." - } - } else { - sta_error 493 "get_property $object is not an object." - } -} - -proc get_property_object_type { object_type object_name quiet } { - set object "NULL" - if { $object_type == "instance" \ - || $object_type == "cell"} { - set object [get_cells -quiet $object_name] - } elseif { $object_type == "pin" } { - set object [get_pins -quiet $object_name] - } elseif { $object_type == "net" } { - set object [get_nets -quiet $object_name] - } elseif { $object_type == "port" } { - set object [get_ports -quiet $object_name] - } elseif { $object_type == "clock" } { - set object [get_clocks -quiet $object_name] - } elseif { $object_type == "liberty_cell" \ - || $object_type == "lib_cell"} { - set object [get_lib_cells -quiet $object_name] - } elseif { $object_type == "liberty_port" \ - || $object_type == "lib_pin" } { - set object [get_lib_pins -quiet $object_name] - } elseif { $object_type == "library" \ - || $object_type == "lib"} { - set object [get_libs -quiet $object_name] - } else { - sta_error 494 "$object_type not supported." - } - if { $object == "NULL" && !$quiet } { - sta_error 495 "$object_type '$object_name' not found." - } - return [lindex $object 0] -} - -proc get_object_type { obj } { - set object_type [object_type $obj] - if { $object_type == "Clock" } { - return "clock" - } elseif { $object_type == "LibertyCell" } { - return "lib_cell" - } elseif { $object_type == "LibertyPort" } { - return "lib_pin" - } elseif { $object_type == "Cell" } { - return "cell" - } elseif { $object_type == "Instance" } { - return "instance" - } elseif { $object_type == "Port" } { - return "port" - } elseif { $object_type == "Pin" } { - return "pin" - } elseif { $object_type == "Net" } { - return "net" - } elseif { $object_type == "Edge" } { - return "timing_arc" - } elseif { $object_type == "TimingArcSet" } { - return "timing_arc" - } else { - return "?" - } -} - -proc get_name { object } { - return [get_object_property $object "name"] -} - -proc get_full_name { object } { - return [get_object_property $object "full_name"] -} - -proc sort_by_name { objects } { - return [lsort -command name_cmp $objects] -} - -proc name_cmp { obj1 obj2 } { - return [string compare [get_name $obj1] [get_name $obj2]] -} - -proc sort_by_full_name { objects } { - return [lsort -command full_name_cmp $objects] -} - -proc full_name_cmp { obj1 obj2 } { - return [string compare [get_full_name $obj1] [get_full_name $obj2]] -} - -################################################################ - define_cmd_args "get_timing_edges" \ {[-from from_pin] [-to to_pin] [-of_objects objects] [-filter expr]} @@ -1408,132 +331,5 @@ proc report_clock1 { clk } { } } -################################################################ - -define_cmd_args "report_object_full_names" {objects} - -proc report_object_full_names { objects } { - foreach obj [sort_by_full_name $objects] { - report_line [get_full_name $obj] - } -} - -define_cmd_args "report_object_names" {objects} - -proc report_object_names { objects } { - foreach obj [sort_by_name $objects] { - report_line [get_name $obj] - } -} - -################################################################ - -define_cmd_args "report_units" {} - -proc report_units { args } { - check_argc_eq0 "report_units" $args - foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power" "distance"} { - report_line " $unit 1[unit_scale_abreviation $unit][unit_suffix $unit]" - } -} - -################################################################ - -define_cmd_args "with_output_to_variable" { var { cmds }} - -# with_output_to_variable variable { command args... } -proc with_output_to_variable { var_name args } { - upvar 1 $var_name var - - set body [lindex $args 0] - sta::redirect_string_begin; - catch $body ret - set var [sta::redirect_string_end] - return $ret -} - -define_cmd_args "set_pocv_sigma_factor" { factor } - -################################################################ - -define_cmd_args "write_path_spice" { -path_args path_args\ - -spice_directory spice_directory\ - -lib_subckt_file lib_subckts_file\ - -model_file model_file\ - -power power\ - -ground ground} - -proc write_path_spice { args } { - parse_key_args "write_path_spice" args \ - keys {-spice_directory -lib_subckt_file -model_file \ - -power -ground -path_args} \ - flags {} - - if { [info exists keys(-spice_directory)] } { - set spice_dir [file nativename $keys(-spice_directory)] - if { ![file exists $spice_dir] } { - sta_error 496 "Directory $spice_dir not found." - } - if { ![file isdirectory $spice_dir] } { - sta_error 497 "$spice_dir is not a directory." - } - if { ![file writable $spice_dir] } { - sta_error 498 "Cannot write in $spice_dir." - } - } else { - sta_error 499 "No -spice_directory specified." - } - - if { [info exists keys(-lib_subckt_file)] } { - set lib_subckt_file [file nativename $keys(-lib_subckt_file)] - if { ![file readable $lib_subckt_file] } { - sta_error 500 "-lib_subckt_file $lib_subckt_file is not readable." - } - } else { - sta_error 501 "No -lib_subckt_file specified." - } - - if { [info exists keys(-model_file)] } { - set model_file [file nativename $keys(-model_file)] - if { ![file readable $model_file] } { - sta_error 502 "-model_file $model_file is not readable." - } - } else { - sta_error 503 "No -model_file specified." - } - - if { [info exists keys(-power)] } { - set power $keys(-power) - } else { - sta_error 504 "No -power specified." - } - - if { [info exists keys(-ground)] } { - set ground $keys(-ground) - } else { - sta_error 505 "No -ground specified." - } - - if { ![info exists keys(-path_args)] } { - sta_error 506 "No -path_args specified." - } - set path_args $keys(-path_args) - set path_ends [eval [concat find_timing_paths $path_args]] - if { $path_ends == {} } { - sta_error 507 "No paths found for -path_args $path_args." - } else { - set path_index 1 - foreach path_end $path_ends { - set path [$path_end path] - set path_name "path_$path_index" - set spice_file [file join $spice_dir "$path_name.sp"] - set subckt_file [file join $spice_dir "$path_name.subckt"] - write_path_spice_cmd $path $spice_file $subckt_file \ - $lib_subckt_file $model_file $power $ground - incr path_index - } - } -} - # sta namespace end. } diff --git a/tcl/Util.tcl b/tcl/Util.tcl index 0e0e0887..c4b4c016 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -177,78 +177,6 @@ proc define_hidden_cmd_args { cmd arglist } { namespace export $cmd } -# This is used in lieu of command completion to make sdc commands -# like get_ports be abbreviated get_port. -proc define_cmd_alias { alias cmd } { - eval "proc $alias { args } { eval [concat $cmd \$args] }" - namespace export $alias -} - -proc cmd_usage_error { cmd } { - variable cmd_args - - if [info exists cmd_args($cmd)] { - sta_error 404 "Usage: $cmd $cmd_args($cmd)" - } else { - sta_error 405 "Usage: $cmd argument error" - } -} - -################################################################ - -define_cmd_args "help" {[pattern]} - -proc_redirect help { - variable cmd_args - - set arg_count [llength $args] - if { $arg_count == 0 } { - set pattern "*" - } elseif { $arg_count == 1 } { - set pattern [lindex $args 0] - } else { - cmd_usage_error "help" - } - set matches [array names cmd_args $pattern] - if { $matches != {} } { - foreach cmd [lsort $matches] { - show_cmd_args $cmd - } - } else { - sta_warn 300 "no commands match '$pattern'." - } -} - -proc show_cmd_args { cmd } { - variable cmd_args - - set max_col 80 - set indent 2 - set indent_str " " - set line $cmd - set col [string length $cmd] - set arglist $cmd_args($cmd) - # Break the arglist up into max_col length lines. - while {1} { - if {[regexp {(^[\n ]*)([a-zA-Z0-9_\\\|\-]+|\[[^\[]+\])(.*)} \ - $arglist ignore space arg rest]} { - set arg_length [string length $arg] - if { $col + $arg_length < $max_col } { - set line "$line $arg" - set col [expr $col + $arg_length + 1] - } else { - report_line $line - set line "$indent_str $arg" - set col [expr $indent + $arg_length + 1] - } - set arglist $rest - } else { - report_line $line - break - } - } -} - ################################################################ proc sta_warn { msg_id msg } { diff --git a/tcl/WritePathSpice.tcl b/tcl/WritePathSpice.tcl new file mode 100644 index 00000000..f8d33cb7 --- /dev/null +++ b/tcl/WritePathSpice.tcl @@ -0,0 +1,99 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2022, 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 . + +namespace eval sta { + +define_cmd_args "write_path_spice" { -path_args path_args\ + -spice_directory spice_directory\ + -lib_subckt_file lib_subckts_file\ + -model_file model_file\ + -power power\ + -ground ground} + +proc write_path_spice { args } { + parse_key_args "write_path_spice" args \ + keys {-spice_directory -lib_subckt_file -model_file \ + -power -ground -path_args} \ + flags {} + + if { [info exists keys(-spice_directory)] } { + set spice_dir [file nativename $keys(-spice_directory)] + if { ![file exists $spice_dir] } { + sta_error 496 "Directory $spice_dir not found." + } + if { ![file isdirectory $spice_dir] } { + sta_error 497 "$spice_dir is not a directory." + } + if { ![file writable $spice_dir] } { + sta_error 498 "Cannot write in $spice_dir." + } + } else { + sta_error 499 "No -spice_directory specified." + } + + if { [info exists keys(-lib_subckt_file)] } { + set lib_subckt_file [file nativename $keys(-lib_subckt_file)] + if { ![file readable $lib_subckt_file] } { + sta_error 500 "-lib_subckt_file $lib_subckt_file is not readable." + } + } else { + sta_error 501 "No -lib_subckt_file specified." + } + + if { [info exists keys(-model_file)] } { + set model_file [file nativename $keys(-model_file)] + if { ![file readable $model_file] } { + sta_error 502 "-model_file $model_file is not readable." + } + } else { + sta_error 503 "No -model_file specified." + } + + if { [info exists keys(-power)] } { + set power $keys(-power) + } else { + sta_error 504 "No -power specified." + } + + if { [info exists keys(-ground)] } { + set ground $keys(-ground) + } else { + sta_error 505 "No -ground specified." + } + + if { ![info exists keys(-path_args)] } { + sta_error 506 "No -path_args specified." + } + set path_args $keys(-path_args) + set path_ends [eval [concat find_timing_paths $path_args]] + if { $path_ends == {} } { + sta_error 507 "No paths found for -path_args $path_args." + } else { + set path_index 1 + foreach path_end $path_ends { + set path [$path_end path] + set path_name "path_$path_index" + set spice_file [file join $spice_dir "$path_name.sp"] + set subckt_file [file join $spice_dir "$path_name.subckt"] + write_path_spice_cmd $path $spice_file $subckt_file \ + $lib_subckt_file $model_file $power $ground + incr path_index + } + } +} + +# sta namespace end. +}