OpenSTA/tcl/Sta.tcl

308 lines
8.6 KiB
Tcl

# 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 <https://www.gnu.org/licenses/>.
namespace eval sta {
################################################################
#
# Non-SDC commands
#
################################################################
define_cmd_args "get_fanin" \
{-to sink_list [-flat] [-only_cells] [-startpoints_only]\
[-levels level_count] [-pin_levels pin_count]\
[-trace_arcs timing|enabled|all]}
proc get_fanin { args } {
parse_key_args "get_fanin" args \
keys {-to -levels -pin_levels -trace_arcs} \
flags {-flat -only_cells -startpoints_only}
if { [llength $args] != 0 } {
cmd_usage_error "get_fanin"
}
if { ![info exists keys(-to)] } {
cmd_usage_error "get_fanin"
}
parse_port_pin_net_arg $keys(-to) pins nets
foreach net $nets {
lappend pins [net_driver_pins $net]
}
set flat [info exists flags(-flat)]
set only_insts [info exists flags(-only_cells)]
set startpoints_only [info exists flags(-startpoints_only)]
set inst_levels 0
if { [info exists keys(-levels)] } {
set inst_levels $keys(-levels)
}
set pin_levels 0
if { [info exists keys(-pin_levels)] } {
set pin_levels $keys(-pin_levels)
}
set thru_disabled 0
set thru_constants 0
if { [info exists keys(-trace_arcs)] } {
set trace_arcs $keys(-trace_arcs)
if { $trace_arcs == "timing" } {
set thru_disabled 0
set thru_constants 0
} elseif { $trace_arcs == "enabled" } {
set thru_disabled 0
set thru_constants 0
} elseif { $trace_arcs == "all" } {
set thru_disabled 1
set thru_constants 1
} else {
cmd_usage_error "get_fanin"
}
}
if { $only_insts } {
return [find_fanin_insts $pins $flat $startpoints_only \
$inst_levels $pin_levels $thru_disabled $thru_constants]
} else {
return [find_fanin_pins $pins $flat $startpoints_only \
$inst_levels $pin_levels $thru_disabled $thru_constants]
}
}
################################################################
define_cmd_args "get_fanout" \
{-from source_list [-flat] [-only_cells] [-endpoints_only]\
[-levels level_count] [-pin_levels pin_count]\
[-trace_arcs timing|enabled|all]}
proc get_fanout { args } {
parse_key_args "get_fanout" args \
keys {-from -levels -pin_levels -trace_arcs} \
flags {-flat -only_cells -endpoints_only}
if { [llength $args] != 0 } {
cmd_usage_error "get_fanout"
}
parse_port_pin_net_arg $keys(-from) pins nets
foreach net $nets {
lappend pins [net_load_pins $net]
}
set flat [info exists flags(-flat)]
set only_insts [info exists flags(-only_cells)]
set endpoints_only [info exists flags(-endpoints_only)]
set inst_levels 0
if { [info exists keys(-levels)] } {
set inst_levels $keys(-levels)
}
set pin_levels 0
if { [info exists keys(-pin_levels)] } {
set pin_levels $keys(-pin_levels)
}
set thru_disabled 0
set thru_constants 0
if { [info exists keys(-trace_arcs)] } {
set trace_arcs $keys(-trace_arcs)
if { $trace_arcs == "timing" } {
set thru_disabled 0
set thru_constants 0
} elseif { $trace_arcs == "enabled" } {
set thru_disabled 0
set thru_constants 0
} elseif { $trace_arcs == "all" } {
set thru_disabled 1
set thru_constants 1
} else {
cmd_usage_error "get_fanout"
}
}
if { $only_insts } {
return [find_fanout_insts $pins $flat $endpoints_only \
$inst_levels $pin_levels $thru_disabled $thru_constants]
} else {
return [find_fanout_pins $pins $flat $endpoints_only \
$inst_levels $pin_levels $thru_disabled $thru_constants]
}
}
################################################################
define_cmd_args "get_timing_edges" \
{[-from from_pin] [-to to_pin] [-of_objects objects] [-filter expr]}
proc get_timing_edges { args } {
return [get_timing_edges_cmd "get_timing_edges" $args]
}
proc get_timing_edges_cmd { cmd cmd_args } {
parse_key_args $cmd cmd_args \
keys {-from -to -of_objects -filter} flags {}
check_argc_eq0 $cmd $cmd_args
set arcs {}
if { [info exists keys(-of_objects)] } {
if { [info exists keys(-from)] \
|| [info exists keys(-from)] } {
sta_error 540 "-from/-to arguments not supported with -of_objects."
}
set arcs [get_timing_arcs_objects $keys(-of_objects)]
} elseif { [info exists keys(-from)] \
&& [info exists keys(-to)] } {
set arcs [get_timing_arcs_from_to \
[get_port_pin_error "from" $keys(-from)] \
[get_port_pin_error "to" $keys(-to)]]
} elseif { [info exists keys(-from)] } {
set arcs [get_timing_arcs_from $keys(-from)]
} elseif { [info exists keys(-to)] } {
set arcs [get_timing_arcs_to $keys(-to)]
} else {
cmd_usage_error $cmd
}
if [info exists keys(-filter)] {
set arcs [filter_objs $keys(-filter) $arcs filter_timing_arcs "timing arc"]
}
return $arcs
}
proc get_timing_arcs_objects { object_arg } {
parse_libcell_inst_arg $object_arg libcells insts
if { $insts != {} } {
set edges {}
foreach inst $insts {
lappend edges [instance_edges $inst]
}
return $edges
} elseif { $libcells != {} } {
set arc_sets {}
foreach libcell $libcells {
lappend arc_sets [$libcell timing_arc_sets]
}
return $arc_sets
}
}
proc instance_edges { inst } {
set edges {}
set pin_iter [$inst pin_iterator]
while {[$pin_iter has_next]} {
set pin [$pin_iter next]
foreach vertex [$pin vertices] {
set edge_iter [$vertex out_edge_iterator]
while {[$edge_iter has_next]} {
set edge [$edge_iter next]
if { [$edge role] != "wire" } {
lappend edges $edge
}
}
$edge_iter finish
}
}
$pin_iter finish
return $edges
}
proc get_timing_arcs_from_to { from_pin_arg to_pin_arg } {
set edges {}
set from_pin [get_port_pin_error "from" $from_pin_arg]
set to_pin [get_port_pin_error "to" $to_pin_arg]
foreach from_vertex [$from_pin vertices] {
foreach to_vertex [$to_pin vertices] {
set edge_iter [$from_vertex out_edge_iterator]
while {[$edge_iter has_next]} {
set edge [$edge_iter next]
if { [$edge to] == $to_vertex } {
lappend edges $edge
}
}
$edge_iter finish
}
}
return $edges
}
proc get_timing_arcs_from { from_pin_arg } {
set from_pin [get_port_pin_error "from" $from_pin_arg]
set edges {}
foreach from_vertex [$from_pin vertices] {
set edge_iter [$from_vertex out_edge_iterator]
while {[$edge_iter has_next]} {
set edge [$edge_iter next]
lappend edges $edge
}
$edge_iter finish
}
return $edges
}
proc get_timing_arcs_to { to_pin_arg } {
set to_pin [get_port_pin_error "to" $to_pin_arg]
set edges {}
foreach to_vertex [$to_pin vertices] {
set edge_iter [$to_vertex in_edge_iterator]
while {[$edge_iter has_next]} {
set edge [$edge_iter next]
lappend edges $edge
}
$edge_iter finish
}
return $edges
}
################################################################
define_cmd_args "report_clock_properties" {[clocks]}
proc_redirect report_clock_properties {
check_argc_eq0or1 "report_clock_properties" $args
update_generated_clks
report_line "Clock Period Waveform"
report_line "----------------------------------------------------"
if { [llength $args] == 0 } {
foreach clk [all_clocks] {
report_clock1 $clk
}
} else {
foreach clk [get_clocks_warn "clock_name" [lindex $args 0]] {
report_clock1 $clk
}
}
}
proc report_clock1 { clk } {
global sta_report_default_digits
if { [$clk waveform_valid] } {
set digits $sta_report_default_digits
set waveform [$clk waveform]
if { $waveform == {} } {
set wave " "
} else {
set wave ""
foreach edge $waveform {
set wave "$wave[format "%10s" [format_time $edge $digits]]"
}
}
if {[$clk is_generated]} {
set generated " (generated)"
} else {
set generated ""
}
report_line "[format %-20s [get_name $clk]][format %10s [format_time [$clk period] $digits]] $wave$generated"
}
}
# sta namespace end.
}