# OpenSTA, Static Timing Analyzer # Copyright (c) 2025, Parallax Software, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # The origin of this software must not be misrepresented; you must not # claim that you wrote the original software. # # Altered source versions must be plainly marked as such, and must not be # misrepresented as being the original software. # # This notice may not be removed or altered from any source distribution. # Network reporting commands. namespace eval sta { define_cmd_args "report_instance" \ {[-connections] [-verbose] instance_path [> filename] [>> filename]} proc_redirect report_instance { parse_key_args "report_instance" args keys {} flags {-connections -verbose} check_argc_eq1 "report_instance" $args if { [info exists flags(-connections)] } { # deprecated 2024-01-17 sta_warn 233 "report_instance -connections is deprecated." } if { [info exists flags(-verbose)] } { # deprecated 2024-01-17 sta_warn 234 "report_instance -verbose is deprecated." } set instance_path [lindex $args 0] if { [is_object $instance_path] } { set instance $instance_path } else { set instance [find_instance $instance_path] } if { $instance != "NULL" } { report_instance1 $instance } else { sta_error 230 "instance $instance_path not found." } } proc report_instance1 { instance } { if { $instance == [top_instance] } { set inst_name "top" } else { set inst_name [get_full_name $instance] } report_line "Instance $inst_name" set cell [instance_property $instance "cell"] report_line " Cell: [get_name $cell]" report_line " Library: [get_name [$cell library]]" report_line " Path cells: [instance_cell_path $instance]" report_instance_pins $instance report_instance_children_ $instance } proc report_instance_pins { instance } { report_instance_pins1 $instance " Input pins:" 0 {"input" "bidirect"} report_instance_pins1 $instance " Output pins:" 0 {"output" "bidirect" "tristate"} report_instance_pins1 $instance " Other pins:" 1 {"internal" "power" "ground" "unknown"} } proc report_instance_pins1 {instance header header_optional dirs} { set header_shown 0 if { !$header_optional } { report_line $header set header_shown 1 } set iter [$instance pin_iterator] while {[$iter has_next]} { set pin [$iter next] set dir [pin_direction $pin] if { [lsearch $dirs $dir] != -1 } { if { !$header_shown } { report_line $header set header_shown 1 } report_instance_pin $pin } } $iter finish } proc report_instance_pin { pin } { set net [$pin net] if { $net == "NULL" } { set net_name "(unconnected)" } else { set net_name [get_full_name [$net highest_connected_net]] } report_line " [$pin port_name] [pin_direction $pin] $net_name" } # Concatenate the cell names of the instance parents. proc instance_cell_path { instance } { set cell_path "[get_name [instance_property $instance "cell"]]" set parent [$instance parent] set top_instance [top_instance] while { $parent != "NULL" && $parent != $top_instance } { set cell_path "[get_name [$parent cell]]/$cell_path" set parent [$parent parent] } return $cell_path } proc report_instance_children_ { instance } { set children [instance_sorted_children $instance] if { $children != {} } { report_line " Children:" foreach child $children { report_line " [get_name $child] ([instance_property $child ref_name])" } } } proc instance_sorted_children { instance } { set children {} set iter [$instance child_iterator] while {[$iter has_next]} { lappend children [$iter next] } $iter finish return [sort_by_full_name $children] } ################################################################ define_cmd_args "report_net" {[-corner corner] [-digits digits]\ net_path [> filename] [>> filename]} # -hpins to show hierarchical pins proc_redirect report_net { global sta_report_default_digits parse_key_args "report_net" args keys {-corner -digits} \ flags {-connections -verbose -hier_pins} check_argc_eq1 "report_net" $args if { [info exists flags(-connections)] } { # deprecated 2024-01-17 sta_warn 235 "report_net -connections is deprecated." } if { [info exists flags(-verbose)] } { # deprecated 2024-01-17 sta_warn 236 "report_net -verbose is deprecated." } if { [info exists flags(-hier_pins)] } { # deprecated 2024-01-17 sta_warn 237 "report_net -hier_pins is deprecated." } set corner [parse_corner_or_all keys] set digits $sta_report_default_digits if { [info exists keys(-digits)] } { set digits $keys(-digits) } set net_path [lindex $args 0] set net [find_net $net_path] if { $net != "NULL" } { report_net1 $net $corner $digits } else { set pin [find_pin $net_path] if { $pin != "NULL" } { set net [$pin net] if { $net != "NULL" } { report_net1 $net $corner $digits } else { sta_error 231 "net $net_path not found." } } else { sta_error 232 "net $net_path not found." } } } proc report_net1 { net corner digits } { report_line "Net [get_full_name $net]" set pins [net_connected_pins_sorted $net] report_net_caps $net $pins $corner $digits report_net_pins $pins "Driver pins" "is_driver" $corner $digits report_net_pins $pins "Load pins" "is_load" $corner $digits report_net_pins $pins "Hierarchical pins" "is_hierarchical" $corner $digits report_net_other_pins $pins $corner $digits } proc net_connected_pins_sorted { net } { set pins {} set iter [$net connected_pin_iterator] while {[$iter has_next]} { set pin [$iter next] lappend pins $pin } $iter finish set pins [sort_by_full_name $pins] return $pins } proc report_net_caps { net pins corner digits } { report_net_cap $net "Pin" "pin_capacitance" $corner $digits report_net_cap $net "Wire" "wire_capacitance" $corner $digits report_net_cap $net "Total" "capacitance" $corner $digits set pin_count 0 set driver_count 0 set load_count 0 foreach pin $pins { if {![$pin is_hierarchical]} { incr pin_count } if [$pin is_driver] { incr driver_count } if [$pin is_load] { incr load_count } } report_line " Number of drivers: $driver_count" report_line " Number of loads: $load_count" report_line " Number of pins: $pin_count" report_line "" } proc report_net_cap { net caption cap_msg corner digits } { set cap_min [$net $cap_msg $corner "min"] set cap_max [$net $cap_msg $corner "max"] report_line " $caption capacitance: [capacitance_range_str $cap_min $cap_max $digits]" } proc report_net_pins { pins header pin_pred corner digits } { set found 0 foreach pin $pins { if {[$pin $pin_pred]} { if { !$found } { report_line $header set found 1 } report_net_pin $pin $corner $digits } } if { $found } { report_line "" } } proc report_net_other_pins { pins corner digits } { set header 0 foreach pin $pins { if { !([$pin is_driver] || [$pin is_load] || [$pin is_hierarchical]) } { if { !$header } { report_line "" report_line "Other pins" set header 1 } report_net_pin $pin $corner $digits } } } proc report_net_pin { pin corner digits } { if [$pin is_leaf] { set cell_name [get_name [[$pin instance] cell]] set cap "" set liberty_port [$pin liberty_port] if { $liberty_port != "NULL" } { set cap [port_capacitance_str $liberty_port $corner $digits] } report_line " [get_full_name $pin] [pin_direction $pin] ($cell_name)$cap[pin_location_str $pin]" } elseif [$pin is_top_level_port] { set wire_cap "" set pin_cap "" set corner [sta::cmd_corner] set port [$pin port] set cap_min [port_ext_wire_cap $port $corner "min"] set cap_max [port_ext_wire_cap $port $corner "max"] if { $cap_min > 0 || $cap_max > 0 } { set wire_cap " wire [capacitance_range_str $cap_min $cap_max $digits]" } set cap_min [port_ext_pin_cap $port $corner "min"] set cap_max [port_ext_pin_cap $port $corner "max"] if { $cap_min > 0 || $cap_max > 0} { set pin_cap " pin [capacitance_range_str $cap_min $cap_max $digits]" } report_line " [get_full_name $pin] [pin_direction $pin] port$wire_cap$pin_cap[pin_location_str $pin]" } elseif [$pin is_hierarchical] { report_line " [get_full_name $pin] [pin_direction $pin]" } } # Used by report_net proc pin_location_str { pin } { set loc [pin_location $pin] if { $loc != "" } { lassign $loc x y return " ([format_distance $x 0], [format_distance $y 0])" } else { return "" } } ################################################################ proc report_pin_ { pin corner digits } { set liberty_port [$pin liberty_port] if { $liberty_port != "NULL" } { set cap [port_capacitance_str $liberty_port $corner $digits] } else { set cap "" } if { [$pin is_top_level_port] } { set term [$pin term] if { $term == "NULL" } { set net "NULL" } else { set net [$term net] } } else { set net [$pin net] } if { $net == "NULL" } { set net_name "(unconnected)" } else { set net_name [get_full_name [$net highest_connected_net]] } report_line "Pin [get_full_name $pin] [pin_direction_desc $pin]$cap $net_name" } proc pin_direction_desc { pin } { if [$pin is_hierarchical] { return "hierarchical [pin_direction $pin]" } elseif [$pin is_top_level_port] { return "[pin_direction $pin] port" } else { return [pin_direction $pin] } } # Do not preceed this field by a space in the caller. proc port_capacitance_str { liberty_port corner digits } { set cap_min [$liberty_port capacitance $corner "min"] set cap_max [$liberty_port capacitance $corner "max"] if { $cap_min > 0 || $cap_max > 0 } { return " [capacitance_range_str $cap_min $cap_max $digits]" } else { return "" } } proc capacitance_range_str { cap_min cap_max digits } { if { $cap_min == $cap_max } { return "[format_capacitance $cap_max $digits]" } else { return "[format_capacitance $cap_min $digits]-[format_capacitance $cap_max $digits]" } } proc capacitances_str { cap_r_min cap_r_max cap_f_min cap_f_max digits } { if { $cap_r_min == $cap_r_max \ && $cap_f_min == $cap_f_max \ && $cap_r_max == $cap_f_max } { # All 4 values are the same. set cap $cap_r_min return "[format_capacitance $cap $digits]" } elseif { $cap_r_min == $cap_r_max \ && $cap_f_min == $cap_f_max } { # Mins equal maxes. return "r [format_capacitance $cap_r_min $digits] f [format_capacitance $cap_f_min $digits]" } else { return "r [format_capacitance $cap_r_min $digits]:[format_capacitance $cap_r_max $digits] f [format_capacitance $cap_f_min $digits]:[format_capacitance $cap_f_max $digits]" } } # sta namespace end }