| # 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 <https://www.gnu.org/licenses/>. |
| |
| # Network editing commands. |
| |
| namespace eval sta { |
| |
| proc connect_pin { net pin } { |
| set insts_port [parse_connect_pin $pin] |
| if { $insts_port == 0 } { |
| return 0 |
| } |
| set net [get_net_warn "net" $net] |
| if { $net == "NULL" } { |
| return 0 |
| } |
| lassign $insts_port inst port |
| connect_pin_cmd $inst $port $net |
| return 1 |
| } |
| |
| proc parse_connect_pin { arg } { |
| set path_regexp [path_regexp] |
| set insts_port {} |
| if { [is_object $arg] } { |
| set object_type [object_type $arg] |
| if { $object_type == "Pin" } { |
| set pin $arg |
| set inst [$pin instance] |
| set port [$pin port] |
| } elseif { $object_type == "Port" } { |
| # Explicit port arg - convert to pin. |
| set pin [find_pin [get_name $arg]] |
| set inst [$pin instance] |
| set port [$pin port] |
| } else { |
| sta_error 586 "unsupported object type $object_type." |
| } |
| } else { |
| if {[regexp $path_regexp $arg ignore path_name port_name]} { |
| set inst [find_instance $path_name] |
| if { $inst == "NULL" } { |
| return 0 |
| } |
| } else { |
| set inst [top_instance] |
| set port_name $arg |
| } |
| set cell [$inst cell] |
| set port [$cell find_port $port_name] |
| if { $port == "NULL" } { |
| return 0 |
| } |
| set pin [$inst find_pin $port_name] |
| } |
| |
| # Make sure the pin is not currently connected to a net. |
| if { $pin != "NULL" \ |
| && ![$pin is_hierarchical] \ |
| && [$pin net] != "NULL" } { |
| return 0 |
| } |
| return [list $inst $port] |
| } |
| |
| proc connect_pins { net pins } { |
| sta_warn 372 "connect_pins is deprecated. Use connect_pin." |
| # Visit the pins to make sure command will succeed. |
| set insts_ports [parse_connect_pins $pins] |
| if { $insts_ports == 0 } { |
| return 0 |
| } |
| set net [get_net_warn "net" $net] |
| if { $net == "NULL" } { |
| return 0 |
| } |
| foreach {inst port} $insts_ports { |
| connect_pin_cmd $inst $port $net |
| } |
| return 1 |
| } |
| |
| proc parse_connect_pins { arg } { |
| set path_regexp [path_regexp] |
| set inst_ports {} |
| # Copy backslashes that will be removed by foreach. |
| set arg [string map {\\ \\\\} $arg] |
| foreach obj $arg { |
| set inst_port [parse_connect_pin $obj] |
| if { $inst_port == 0 } { |
| return 0 |
| } |
| set inst_ports [concat $inst_ports $inst_port] |
| } |
| return $inst_ports |
| } |
| |
| ################################################################ |
| |
| proc delete_instance { instance } { |
| if { [is_object $instance] } { |
| set object_type [object_type $instance] |
| if { $object_type == "Instance" } { |
| set inst $obj |
| } 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 |
| } |
| } |
| |
| ################################################################ |
| |
| proc disconnect_pin { net pin } { |
| set net [get_net_warn "net" $net] |
| if { $net == "NULL" } { |
| return 0 |
| } |
| if { $pin == "-all" } { |
| set iter [$net connected_pin_iterator] |
| while {[$iter has_next]} { |
| set pin [$iter next] |
| disconnect_pin_cmd $pin |
| } |
| $iter finish |
| return 1 |
| } else { |
| set pin1 [get_port_pin_warn "pin" $pin] |
| if { $pin1 == "NULL" } { |
| return 0 |
| } else { |
| disconnect_pin_cmd $pin1 |
| return 1 |
| } |
| } |
| } |
| |
| 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] |
| } |
| } else { |
| set inst_name $inst_path |
| set parent [top_instance] |
| } |
| return [make_instance_cmd $inst_name $lib_cell $parent] |
| } else { |
| return 0 |
| } |
| } |
| |
| ################################################################ |
| |
| 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] |
| } |
| |
| ################################################################ |
| |
| proc replace_cell { instance lib_cell } { |
| set cell [get_lib_cell_warn "lib_cell" $lib_cell] |
| if { $cell != "NULL" } { |
| set inst [get_instance_error "instance" $instance] |
| set inst_cell [$inst liberty_cell] |
| if { $inst_cell == "NULL" \ |
| || ![equiv_cell_ports $inst_cell $cell] } { |
| return 0 |
| } |
| replace_cell_cmd $inst $cell |
| return 1 |
| } else { |
| return 0 |
| } |
| } |
| |
| proc replace_to_scan_cell { instance } { |
| set inst [get_instance_error "instance" $instance] |
| set inst_cell [get_full_name [$inst liberty_cell]] |
| #Target scan cell __d to __sd |
| #example sky130_fd_sc_hd__dfrtp_2 to sky130_fd_sc_hd__sdfrtp_2 |
| set inst_scell [regsub -all "__d" $inst_cell "__sd"] |
| puts "Info: Scan Swapping => Instance:$instance From:$inst_cell to:$inst_scell"; |
| if { $inst_cell == "NULL"} { |
| return 0 |
| } |
| set scell [get_lib_cell_warn "lib_cell" $inst_scell] |
| replace_cell_cmd $inst $scell |
| return 1 |
| } |
| |
| proc path_regexp {} { |
| global hierarchy_separator |
| set id_regexp "\[^${hierarchy_separator}\]+" |
| set prefix_regexp "${id_regexp}(?:${hierarchy_separator}${id_regexp})*" |
| return "^(${prefix_regexp})${hierarchy_separator}(${id_regexp})$" |
| } |
| |
| # sta namespace end. |
| } |