# OpenSTA, Static Timing Analyzer # Copyright (c) 2024, 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 . # Graph utilities namespace eval sta { define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]} proc report_edges { args } { parse_key_args "report_edges" args keys {-from -to} flags {} check_argc_eq0 "report_edges" $args if { [info exists keys(-from)] && [info exists keys(-to)] } { set from_pin [get_port_pin_error "from_pin" $keys(-from)] set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach from_vertex [$from_pin vertices] { foreach to_vertex [$to_pin vertices] { report_edges_between_ $from_vertex $to_vertex } } } elseif [info exists keys(-from)] { set from_pin [get_port_pin_error "from_pin" $keys(-from)] foreach from_vertex [$from_pin vertices] { report_edges_ $from_vertex out_edge_iterator \ vertex_port_name vertex_path_name } } elseif [info exists keys(-to)] { set to_pin [get_port_pin_error "to_pin" $keys(-to)] foreach to_vertex [$to_pin vertices] { report_edges_ $to_vertex in_edge_iterator \ vertex_path_name vertex_port_name } } } proc report_edges_between_ { from_vertex to_vertex } { set iter [$from_vertex out_edge_iterator] while {[$iter has_next]} { set edge [$iter next] if { [$edge to] == $to_vertex } { if { [$edge role] == "wire" } { report_edge_ $edge vertex_path_name vertex_path_name } else { report_edge_ $edge vertex_port_name vertex_port_name } } } $iter finish } proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { # First report edges internal to the device. set device_header 0 set iter [$vertex $iter_proc] while {[$iter has_next]} { set edge [$iter next] if { [$edge role] != "wire" } { if { !$device_header } { set pin [$vertex pin] if { ![$pin is_top_level_port] } { set inst [$pin instance] } set device_header 1 } report_edge_ $edge vertex_port_name vertex_port_name } } $iter finish # Then wires. set iter [$vertex $iter_proc] while {[$iter has_next]} { set edge [$iter next] if { [$edge role] == "wire" } { report_edge_ $edge $wire_from_name_proc $wire_to_name_proc } } $iter finish } proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { global sta_report_default_digits set latch_enable [$edge latch_d_to_q_en] if { $latch_enable != "" } { set latch_enable " enable $latch_enable" } report_line "[$vertex_from_name_proc [$edge from]] -> [$vertex_to_name_proc [$edge to]] [$edge role]$latch_enable" set disables [edge_disable_reason $edge] if { $disables != "" } { report_line " Disabled by $disables" } set cond [$edge cond] if { $cond != "" } { report_line " Condition: $cond" } set mode_name [$edge mode_name] if { $mode_name != "" } { report_line " Mode: $mode_name [$edge mode_value]" } foreach arc [$edge timing_arcs] { set delays [$edge arc_delay_strings $arc $sta_report_default_digits] set delays_fmt [format_delays $delays] set disable_reason "" if { [timing_arc_disabled $edge $arc] } { set disable_reason " disabled" } report_line " [$arc from_edge] -> [$arc to_edge] $delays_fmt$disable_reason" } } # Separate list elements with colons. proc format_times { values digits } { set result "" foreach value $values { if { $result != "" } { append result ":" } append result [format_time $value $digits] } return $result } # Separate delay list elements with colons. proc format_delays { values } { set result "" foreach value $values { if { $result != "" } { append result ":" } append result $value } return $result } proc edge_disable_reason { edge } { set disables "" if [$edge is_disabled_constraint] { append disables "constraint" } if [$edge is_disabled_constant] { if { $disables != "" } { append disables ", " } append disables "constant" } if [$edge is_disabled_cond_default] { if { $disables != "" } { append disables ", " } append disables "cond_default" } if [$edge is_disabled_loop] { if { $disables != "" } { append disables ", " } append disables "loop" } if [$edge is_disabled_bidirect_inst_path] { if { $disables != "" } { append disables ", " } append disables "bidirect instance path" } if [$edge is_disabled_bidirect_net_path] { if { $disables != "" } { append disables ", " } append disables "bidirect net path" } if { [$edge is_disabled_preset_clear] } { if { $disables != "" } { append disables ", " } append disables "timing_enable_preset_clear_arcs" } return $disables } ################################################################ define_cmd_args "report_constant" {pin|instance|net} proc report_constant { obj } { parse_inst_port_pin_net_arg $obj insts pins nets foreach pin $pins { report_pin_constant $pin } foreach inst $insts { set pin_iter [$inst pin_iterator] while {[$pin_iter has_next]} { set pin [$pin_iter next] report_pin_constant $pin } $pin_iter finish } foreach net $nets { set pin_iter [$net pin_iterator] while {[$pin_iter has_next]} { set pin [$pin_iter next] report_pin_constant $pin } $pin_iter finish } } proc report_pin_constant { pin } { set sim_value [pin_sim_logic_value $pin] set case_value [pin_case_logic_value $pin] if { $case_value != "X" } { set case " case=$case_value" } else { set case "" } set logic_value [pin_logic_value $pin] if { $logic_value != "X" } { set logic " logic=$logic_value" } else { set logic "" } report_line "[pin_property $pin lib_pin_name] $sim_value$case$logic" } ################################################################ proc_redirect report_disabled_edges { foreach edge [disabled_edges_sorted] { if { [$edge role] == "wire" } { set from_pin_name [get_full_name [[$edge from] pin]] set to_pin_name [get_full_name [[$edge to] pin]] report_line "$from_pin_name $to_pin_name [edge_disable_reason_verbose $edge]" } else { set from_pin [[$edge from] pin] set to_pin [[$edge to] pin] set inst_name [get_full_name [$from_pin instance]] set from_port_name [get_name [$from_pin port]] set to_port_name [get_name [$to_pin port]] set cond [$edge cond] if { $cond != "" } { set when " when: $cond" } else { set when "" } report_line "$inst_name $from_port_name $to_port_name$when [edge_disable_reason_verbose $edge]" } } } proc edge_disable_reason_verbose { edge } { set disables "" if { [$edge is_disabled_constraint] } { append disables "constraint" } if { [$edge is_disabled_constant] } { if { $disables != "" } { append disables ", " } append disables "constant" set sense [$edge sim_timing_sense] if { $sense == "positive_unate" || $sense == "negative_unate" } { append disables " $sense" } set const_pins [$edge disabled_constant_pins] foreach pin [sort_by_full_name $const_pins] { set port_name [pin_property $pin lib_pin_name] set value [pin_sim_logic_value $pin] append disables " $port_name=$value" } } if { [$edge is_disabled_cond_default] } { if { $disables != "" } { append disables ", " } append disables "cond_default" } if { [$edge is_disabled_loop] } { if { $disables != "" } { append disables ", " } append disables "loop" } if { [$edge is_disabled_preset_clear] } { if { $disables != "" } { append disables ", " } append disables "timing_enable_preset_clear_arcs" } return $disables } ################################################################ define_cmd_args "report_slews" {[-corner corner] pin} proc report_slews { args } { global sta_report_default_digits parse_key_args "report_slews" args keys {-corner} flags {} check_argc_eq1 "report_slews" $args set corner [parse_corner_or_all keys] set pin [get_port_pin_error "pin" [lindex $args 0]] set digits $sta_report_default_digits foreach vertex [$pin vertices] { if { $corner == "NULL" } { report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew rise min] $digits]:[format_time [$vertex slew rise max] $digits] [fall_short_name] [format_time [$vertex slew fall min] $digits]:[format_time [$vertex slew fall max] $digits]" } else { report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew_corner rise $corner min] $digits]:[format_time [$vertex slew_corner rise $corner max] $digits] [fall_short_name] [format_time [$vertex slew_corner fall $corner min] $digits]:[format_time [$vertex slew_corner fall $corner max] $digits]" } } } proc vertex_path_name { vertex } { set pin [$vertex pin] set pin_name [get_full_name $pin] return [vertex_name_ $vertex $pin $pin_name] } proc vertex_port_name { vertex } { set pin [$vertex pin] set pin_name [pin_property $pin lib_pin_name] return [vertex_name_ $vertex $pin $pin_name] } # Append driver/load for bidirect pin vertices. proc vertex_name_ { vertex pin pin_name } { if { [pin_direction $pin] == "bidirect" } { if [$vertex is_bidirect_driver] { return "$pin_name driver" } else { return "$pin_name load " } } else { return $pin_name } } # Return a list of hierarchical pins crossed by a graph edge. proc hier_pins_crossed_by_edge { edge } { set from_pins [hier_pins_above [[$edge from] pin]] set to_pins [hier_pins_above [[$edge to] pin]] foreach p $to_pins { report_line [$p path_name] } while { [llength $from_pins] > 0 \ && [llength $to_pins] > 0 \ && [lindex $from_pins 0] == [lindex $to_pins 0] } { set from_pins [lrange $from_pins 1 end] set to_pins [lrange $to_pins 1 end] } return [concat $from_pins $to_pins] } proc hier_pins_above { pin } { set pins_above {} set inst [$pin instance] set net [$pin net] set parent [$inst parent] while { $parent != "NULL" } { set found 0 set parent_pin_iter [$parent pin_iterator] while {[$parent_pin_iter has_next]} { set parent_pin [$parent_pin_iter next] if {[$parent_pin net] == $net} { set pins_above [concat [list $parent_pin] $pins_above] set found 1 break } } $parent_pin_iter finish if { !$found} { break } set parent [$parent parent] } return $pins_above } proc report_level { pin } { set pin1 [get_port_pin_error "pin" $pin] foreach vertex [$pin1 vertices] { if { $vertex != "NULL" } { report_line "[vertex_path_name $vertex] level = [$vertex level]" } } } # Show how many pins are at each level from an input. proc report_level_distribution {} { set max_level 0 set iter [vertex_iterator] while {[$iter has_next]} { set vertex [$iter next] set level [$vertex level] if { $level > $max_level } { set max_level $level } if [info exists count($level)] { incr count($level) } else { set count($level) 1 } } $iter finish report_line "level pin count" for { set level 0 } { $level < $max_level } { incr level } { report_line " $level $count($level)" } } # sta namespace end }