2018-09-28 17:54:21 +02:00
|
|
|
# OpenSTA, Static Timing Analyzer
|
2019-01-01 21:26:11 +01:00
|
|
|
# Copyright (c) 2019, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
#
|
|
|
|
|
# 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/>.
|
|
|
|
|
|
|
|
|
|
namespace eval sta {
|
|
|
|
|
|
|
|
|
|
################################################################
|
|
|
|
|
#
|
|
|
|
|
# Search debugging/probing commands
|
|
|
|
|
#
|
|
|
|
|
################################################################
|
|
|
|
|
|
|
|
|
|
define_cmd_args "report_arrival" {pin}
|
|
|
|
|
|
|
|
|
|
proc report_arrival { pin } {
|
2018-11-26 18:15:52 +01:00
|
|
|
report_delays_wrt_clks $pin "arrivals_clk_delays"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc report_delays_wrt_clks { pin_arg what } {
|
|
|
|
|
set pin [get_port_pin_error "pin" $pin_arg]
|
|
|
|
|
foreach vertex [$pin vertices] {
|
|
|
|
|
if { $vertex != "NULL" } {
|
|
|
|
|
report_delays_wrt_clk $vertex $what "NULL" "rise"
|
|
|
|
|
report_delays_wrt_clk $vertex $what [default_arrival_clock] "rise"
|
|
|
|
|
set clk_iter [clock_iterator]
|
|
|
|
|
while {[$clk_iter has_next]} {
|
|
|
|
|
set clk [$clk_iter next]
|
|
|
|
|
report_delays_wrt_clk $vertex $what $clk "rise"
|
|
|
|
|
report_delays_wrt_clk $vertex $what $clk "fall"
|
|
|
|
|
}
|
|
|
|
|
$clk_iter finish
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc report_delays_wrt_clk { vertex what clk clk_tr } {
|
|
|
|
|
global sta_report_default_digits
|
|
|
|
|
|
|
|
|
|
set rise [$vertex $what rise $clk $clk_tr $sta_report_default_digits]
|
|
|
|
|
set fall [$vertex $what fall $clk $clk_tr $sta_report_default_digits]
|
|
|
|
|
# Filter INF/-INF arrivals.
|
|
|
|
|
if { !([delays_are_inf $rise] && [delays_are_inf $fall]) } {
|
|
|
|
|
set rise_fmt [format_delays $rise]
|
|
|
|
|
set fall_fmt [format_delays $fall]
|
|
|
|
|
if {$clk != "NULL"} {
|
2019-01-17 00:37:31 +01:00
|
|
|
set clk_str " ([get_name $clk] [rise_fall_short_name $clk_tr])"
|
2018-11-26 18:15:52 +01:00
|
|
|
} else {
|
|
|
|
|
set clk_str ""
|
|
|
|
|
}
|
|
|
|
|
puts "$clk_str r $rise_fmt f $fall_fmt"
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc report_wrt_clks { pin_arg what } {
|
|
|
|
|
set pin [get_port_pin_error "pin" $pin_arg]
|
|
|
|
|
foreach vertex [$pin vertices] {
|
|
|
|
|
if { $vertex != "NULL" } {
|
|
|
|
|
report_wrt_clk $vertex $what "NULL" "rise"
|
|
|
|
|
report_wrt_clk $vertex $what [default_arrival_clock] "rise"
|
|
|
|
|
set clk_iter [clock_iterator]
|
|
|
|
|
while {[$clk_iter has_next]} {
|
|
|
|
|
set clk [$clk_iter next]
|
|
|
|
|
report_wrt_clk $vertex $what $clk "rise"
|
|
|
|
|
report_wrt_clk $vertex $what $clk "fall"
|
|
|
|
|
}
|
|
|
|
|
$clk_iter finish
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc report_wrt_clk { vertex what clk clk_tr } {
|
|
|
|
|
global sta_report_default_digits
|
|
|
|
|
|
|
|
|
|
set rise [$vertex $what rise $clk $clk_tr]
|
|
|
|
|
set fall [$vertex $what fall $clk $clk_tr]
|
|
|
|
|
# Filter INF/-INF arrivals.
|
|
|
|
|
if { !([times_are_inf $rise] && [times_are_inf $fall]) } {
|
|
|
|
|
set rise_fmt [format_times $rise $sta_report_default_digits]
|
|
|
|
|
set fall_fmt [format_times $fall $sta_report_default_digits]
|
|
|
|
|
if {$clk != "NULL"} {
|
2019-01-17 00:37:31 +01:00
|
|
|
set clk_str " ([get_name $clk] [rise_fall_short_name $clk_tr])"
|
2018-09-28 17:54:21 +02:00
|
|
|
} else {
|
|
|
|
|
set clk_str ""
|
|
|
|
|
}
|
|
|
|
|
puts "$clk_str r $rise_fmt f $fall_fmt"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc rise_fall_short_name { tr } {
|
|
|
|
|
if { $tr eq "rise" } {
|
|
|
|
|
return [rise_short_name]
|
|
|
|
|
} elseif { $tr eq "fall" } {
|
|
|
|
|
return [fall_short_name]
|
|
|
|
|
} else {
|
|
|
|
|
error "unknown transition name $tr"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc times_are_inf { times } {
|
|
|
|
|
foreach time $times {
|
|
|
|
|
if { $time < 1e+10 && $time > -1e+10 } {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
proc delays_are_inf { delays } {
|
|
|
|
|
foreach delay $delays {
|
|
|
|
|
if { !([string match "INF*" $delay] \
|
|
|
|
|
|| [string match "-INF*" $delay]) } {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
################################################################
|
|
|
|
|
|
|
|
|
|
# Note that -all and -tags are intentionally "hidden".
|
|
|
|
|
define_cmd_args "report_path" \
|
|
|
|
|
{[-min|-max]\
|
|
|
|
|
[-format full|full_clock|full_clock_expanded|short|end|summary]\
|
|
|
|
|
[-fields [capacitance|transition_time|input_pin|net]\
|
|
|
|
|
[-digits digits] [-no_line_splits]\
|
|
|
|
|
[> filename] [>> filename]\
|
|
|
|
|
pin ^|r|rise|v|f|fall}
|
|
|
|
|
|
|
|
|
|
proc_redirect report_path {
|
|
|
|
|
parse_key_args "report_path" args keys {} \
|
|
|
|
|
flags {-max -min -all -tags} 0
|
|
|
|
|
|
|
|
|
|
if { [info exists flags(-min)] && [info exists flags(-max)] } {
|
|
|
|
|
sta_error "-min and -max cannot both be specified."
|
|
|
|
|
} elseif [info exists flags(-min)] {
|
|
|
|
|
set min_max "min"
|
|
|
|
|
} elseif [info exists flags(-max)] {
|
|
|
|
|
set min_max "max"
|
|
|
|
|
} else {
|
|
|
|
|
# Default to max path.
|
|
|
|
|
set min_max "max"
|
|
|
|
|
}
|
|
|
|
|
set report_tags [info exists flags(-tags)]
|
|
|
|
|
set report_all [info exists flags(-all)]
|
|
|
|
|
|
|
|
|
|
parse_report_path_options "report_path" args "full" 1
|
|
|
|
|
check_argc_eq2 "report_path" $args
|
|
|
|
|
|
|
|
|
|
set pin_arg [lindex $args 0]
|
|
|
|
|
set tr [parse_rise_fall_arg [lindex $args 1]]
|
|
|
|
|
|
|
|
|
|
set pin [get_port_pin_error "pin" $pin_arg]
|
|
|
|
|
if { [$pin is_hierarchical] } {
|
|
|
|
|
sta_error "pin '$pin_arg' is hierarchical."
|
|
|
|
|
} else {
|
|
|
|
|
foreach vertex [$pin vertices] {
|
|
|
|
|
if { $vertex != "NULL" } {
|
|
|
|
|
if { $report_all } {
|
|
|
|
|
set first 1
|
|
|
|
|
set path_iter [$vertex path_iterator $tr $min_max]
|
|
|
|
|
while {[$path_iter has_next]} {
|
|
|
|
|
set path [$path_iter next]
|
|
|
|
|
if { $first } {
|
|
|
|
|
puts "Tag group: [$vertex tag_group_index]"
|
|
|
|
|
} else {
|
|
|
|
|
puts ""
|
|
|
|
|
}
|
|
|
|
|
if { $report_tags } {
|
|
|
|
|
puts "Tag: [$path tag]"
|
|
|
|
|
}
|
|
|
|
|
report_path_cmd $path
|
|
|
|
|
delete_path_ref $path
|
|
|
|
|
set first 0
|
|
|
|
|
}
|
|
|
|
|
$path_iter finish
|
|
|
|
|
} else {
|
|
|
|
|
set worst_path [vertex_worst_arrival_path_tr $vertex $tr $min_max]
|
|
|
|
|
if { $worst_path != "NULL" } {
|
|
|
|
|
if { $report_tags } {
|
|
|
|
|
puts "Tag: [$worst_path tag]"
|
|
|
|
|
}
|
|
|
|
|
report_path_cmd $worst_path
|
|
|
|
|
delete_path_ref $worst_path
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc parse_rise_fall_arg { arg } {
|
|
|
|
|
if { $arg eq "r" || $arg eq "^" || $arg eq "rise" } {
|
|
|
|
|
return "rise"
|
|
|
|
|
} elseif { $arg eq "f" || $arg eq "v" || $arg eq "fall" } {
|
|
|
|
|
retur "fall"
|
|
|
|
|
} else {
|
|
|
|
|
error "unknown rise/fall transition name."
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc parse_report_path_options { cmd args_var default_format
|
|
|
|
|
unknown_key_is_error } {
|
|
|
|
|
variable path_options
|
|
|
|
|
global sta_report_default_digits
|
|
|
|
|
|
|
|
|
|
upvar 1 $args_var args
|
|
|
|
|
if [info exists path_options] {
|
|
|
|
|
unset path_options
|
|
|
|
|
}
|
|
|
|
|
parse_key_args $cmd args path_options {-format -digits -fields} \
|
|
|
|
|
path_options {-no_line_splits} $unknown_key_is_error
|
|
|
|
|
|
|
|
|
|
set format $default_format
|
|
|
|
|
if [info exists path_options(-format)] {
|
|
|
|
|
set format $path_options(-format)
|
|
|
|
|
set formats {full full_clock full_clock_expanded short \
|
|
|
|
|
end slack_only summary}
|
|
|
|
|
if { [lsearch $formats $format] == -1 } {
|
|
|
|
|
sta_error "-format $format not recognized."
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
set path_options(-format) $default_format
|
|
|
|
|
}
|
|
|
|
|
set_report_path_format $format
|
|
|
|
|
|
|
|
|
|
set digits $sta_report_default_digits
|
|
|
|
|
if [info exists path_options(-digits)] {
|
|
|
|
|
set digits $path_options(-digits)
|
|
|
|
|
check_positive_integer "-digits" $digits
|
|
|
|
|
}
|
|
|
|
|
set path_options(num_fmt) "%.${digits}f"
|
|
|
|
|
set_report_path_digits $digits
|
|
|
|
|
|
|
|
|
|
if { [info exists path_options(-fields)] } {
|
|
|
|
|
set fields $path_options(-fields)
|
|
|
|
|
set report_input_pin [expr [lsearch $fields "input*"] != -1]
|
|
|
|
|
set report_cap [expr [lsearch $fields "cap*"] != -1]
|
|
|
|
|
set report_net [expr [lsearch $fields "net*"] != -1]
|
|
|
|
|
set report_slew [expr [lsearch $fields "trans*"] != -1]
|
|
|
|
|
} else {
|
|
|
|
|
set report_input_pin 0
|
|
|
|
|
set report_cap 0
|
|
|
|
|
set report_net 0
|
|
|
|
|
set report_slew 0
|
|
|
|
|
}
|
|
|
|
|
set_report_path_fields $report_input_pin $report_net \
|
|
|
|
|
$report_cap $report_slew
|
|
|
|
|
|
|
|
|
|
set_report_path_no_split [info exists path_options(-no_line_splits)]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
################################################################
|
|
|
|
|
|
|
|
|
|
define_cmd_args "report_required" {pin}
|
|
|
|
|
|
|
|
|
|
proc report_required { pin } {
|
2018-11-26 18:15:52 +01:00
|
|
|
report_delays_wrt_clks $pin "requireds_clk_delays"
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
################################################################
|
|
|
|
|
|
|
|
|
|
define_cmd_args "report_slack" {pin}
|
|
|
|
|
|
|
|
|
|
proc report_slack { pin } {
|
2018-11-26 18:15:52 +01:00
|
|
|
report_delays_wrt_clks $pin "slacks_clk_delays"
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
################################################################
|
|
|
|
|
|
|
|
|
|
# Internal debugging command.
|
|
|
|
|
proc report_tag_arrivals { pin } {
|
|
|
|
|
set pin [get_port_pin_error "pin" $pin]
|
|
|
|
|
foreach vertex [$pin vertices] {
|
|
|
|
|
report_tag_arrivals_cmd $vertex
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
################################################################
|
|
|
|
|
|
2018-12-05 23:18:41 +01:00
|
|
|
define_hidden_cmd_args "total_negative_slack" \
|
|
|
|
|
{[-corner corner] [-min]|[-max]}
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
proc total_negative_slack { args } {
|
2018-12-05 23:18:41 +01:00
|
|
|
parse_key_args "total_negative_slack" args \
|
|
|
|
|
keys {-corner} flags {-min -max}
|
2018-09-28 17:54:21 +02:00
|
|
|
check_argc_eq0 "total_negative_slack" $args
|
|
|
|
|
set min_max [parse_min_max_flags flags]
|
2018-12-05 23:18:41 +01:00
|
|
|
if { [info exists keys(-corner)] } {
|
|
|
|
|
set corner [parse_corner_required keys]
|
|
|
|
|
set tns [total_negative_slack_corner_cmd $corner $min_max]
|
|
|
|
|
} else {
|
|
|
|
|
set tns [total_negative_slack_cmd $min_max]
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
return [time_sta_ui $tns]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
################################################################
|
|
|
|
|
|
2018-12-05 23:18:41 +01:00
|
|
|
define_hidden_cmd_args "worst_negative_slack" \
|
|
|
|
|
{[-corner corner] [-min]|[-max]}
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
proc worst_negative_slack { args } {
|
2018-12-05 23:18:41 +01:00
|
|
|
parse_key_args "total_negative_slack" args \
|
|
|
|
|
keys {-corner} flags {-min -max}
|
2018-09-28 17:54:21 +02:00
|
|
|
check_argc_eq0 "worst_negative_slack" $args
|
|
|
|
|
set min_max [parse_min_max_flags flags]
|
2018-12-05 23:18:41 +01:00
|
|
|
if { [info exists keys(-corner)] } {
|
|
|
|
|
set corner [parse_corner_required keys]
|
|
|
|
|
set worst_slack [worst_slack_corner $corner $min_max]
|
|
|
|
|
} else {
|
|
|
|
|
set worst_slack [worst_slack $min_max]
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
if { $worst_slack < 0.0 } {
|
|
|
|
|
return [time_sta_ui $worst_slack]
|
|
|
|
|
} else {
|
|
|
|
|
return 0.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
################################################################
|
|
|
|
|
#
|
|
|
|
|
# Helper functions
|
|
|
|
|
#
|
|
|
|
|
################################################################
|
|
|
|
|
|
|
|
|
|
proc parse_path_group_arg { group_names } {
|
|
|
|
|
set names {}
|
|
|
|
|
foreach name $group_names {
|
|
|
|
|
if { [is_path_group_name $name] } {
|
|
|
|
|
lappend names $name
|
|
|
|
|
} else {
|
|
|
|
|
sta_warn "unknown path group '$name'."
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $names
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-09 19:04:16 +01:00
|
|
|
proc report_slew_limits { corner min_max all_violators verbose nosplit } {
|
2018-09-28 17:54:21 +02:00
|
|
|
if { $all_violators } {
|
2018-11-09 19:04:16 +01:00
|
|
|
set violators [pin_slew_limit_violations $corner $min_max]
|
2018-09-28 17:54:21 +02:00
|
|
|
if { $violators != {} } {
|
|
|
|
|
puts "${min_max}_transition"
|
|
|
|
|
puts ""
|
|
|
|
|
if { $verbose } {
|
|
|
|
|
foreach pin $violators {
|
2018-11-09 19:04:16 +01:00
|
|
|
report_slew_limit_verbose $pin $corner $min_max
|
2018-09-28 17:54:21 +02:00
|
|
|
puts ""
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
report_slew_limit_short_header
|
|
|
|
|
foreach pin $violators {
|
2018-11-09 19:04:16 +01:00
|
|
|
report_slew_limit_short $pin $corner $min_max
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
puts ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-11-09 19:04:16 +01:00
|
|
|
set pin [pin_min_slew_limit_slack $corner $min_max]
|
2018-09-28 17:54:21 +02:00
|
|
|
if { $pin != "NULL" } {
|
|
|
|
|
puts "${min_max}_transition"
|
|
|
|
|
puts ""
|
|
|
|
|
if { $verbose } {
|
2018-11-09 19:04:16 +01:00
|
|
|
report_slew_limit_verbose $pin $corner $min_max
|
2018-09-28 17:54:21 +02:00
|
|
|
puts ""
|
|
|
|
|
} else {
|
|
|
|
|
report_slew_limit_short_header
|
2018-11-09 19:04:16 +01:00
|
|
|
report_slew_limit_short $pin $corner $min_max
|
2018-09-28 17:54:21 +02:00
|
|
|
puts ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-21 07:41:54 +01:00
|
|
|
proc report_path_ends { path_ends } {
|
|
|
|
|
report_path_end_header
|
|
|
|
|
set prev_end "NULL"
|
|
|
|
|
foreach path_end $path_ends {
|
|
|
|
|
report_path_end2 $path_end $prev_end
|
|
|
|
|
set prev_end $path_end
|
|
|
|
|
}
|
|
|
|
|
report_path_end_footer
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
# sta namespace end.
|
|
|
|
|
}
|