Vous êtes sur la page 1sur 12

# write_path_summary.

tcl
# writes customizable summary table for a collection of paths
#
# v1.0 chrispy 04/02/2004
# initial release
# v1.1 chrispy 05/12/2004
# added startpoint/endpoint clock latency, clock skew, CRPR
# (thanks to John S. for article feedback!)
# v1.2 chrispy 06/15/2004
# changed net/cell delay code to work in 2003.03
# (thanks John Schritz @ Tektronix for feedback on this!)
# v1.3 chrispy 08/31/2004
# fixed append_to_collection bug (again, thanks to John Schritz @
Tektronix!)
# v1.4 chrispy 03/26/2006
# fixed handling of unconstrained paths
# v1.5 chrispy 09/01/2006
# fixed slowest_cell reporting (thanks Pradeep @ OpenSilicon!)
# v1.6 chrispy 11/17/2010
# fix harmless warning when a path has no cells (ie, feedthrough)
# fix harmless warning when a path has no startpoint or endpoint
clock
# v1.7 chrispy 01/31/2012
# rename total_xtalk as total_xtalk_data
# add total_xtalk_clock, total_xtalk (clock+data)
namespace eval path_summary {
set finfo(index) {int {index number of path in original path
collection (0, 1, 2...)} {{index} {#}}}
set finfo(startpoint) {string {name of path startpoint} {{startpoint}
{name}}}
set finfo(endpoint) {string {name of path endpoint} {{endpoint}
{name}}}
set finfo(start_clk) {string {name of startpoint launching clock}
{{startpoint} {clock}}}
set finfo(end_clk) {string {name of endpoint capturing clock}
{{endpoint} {clock}}}
set finfo(launch_latency) {real {launching clock latency} {{launch}
{latency}}}
set finfo(capture_latency) {real {capturing clock latency} {{capture}
{latency}}}
set finfo(skew) {real {skew between launch/capture clock (negative is
tighter)} {{clock} {skew}}}
set finfo(crpr) {real {clock reconvergence pessimism removal amount}
{{CRPR} {amount}}}
set finfo(path_group) {string {path group name} {{path} {group}}}
set finfo(slack) {real {path slack} {{path} {slack}}}
set finfo(duration) {real {combinational path delay between
startpoint and endpoint} {{path} {duration}}}
set finfo(levels) {real {levels of combinational logic} {{levels} {of
logic}}}

set finfo(hier_pins) {int {number of hierarchy pins in path} {{#


hier} {pins}}}
set finfo(num_segments) {int {number of segments in path} {{#}
{segments}}}
set finfo(num_unique_segments) {int {number of unique segments in
path} {{# unique} {segments}}}
set finfo(num_segment_crossings) {int {number of segment crossings in
path} {{# segment} {crossings}}}
set finfo(average_cell_delay) {real {average combinational cell delay
(duration / levels)} {{average} {cell delay}}}
set finfo(slowest_cell) {string {name of slowest cell in path}
{{slowest} {cell}}}
set finfo(slowest_cell_delay) {real {cell delay of slowest cell in
path} {{slowest} {cell delay}}}
set finfo(slowest_net) {string {name of slowest net in path}
{{slowest} {net}}}
set finfo(slowest_net_delay) {real {net delay of slowest net in path}
{{slowest} {net delay}}}
set finfo(slowest_net_R) {real {resistance of slowest net in path}
{{slowest} {net R}}}
set finfo(slowest_net_C) {real {capacitance of slowest net in path}
{{slowest} {net C}}}
set finfo(total_net_delay) {real {summation of all net delays in
path} {{total} {net delay}}}
set finfo(max_trans) {real {slowest pin transition in path} {{max}
{transition}}}
set finfo(total_xtalk_data) {real {summation of all crosstalk deltas
in data path} {{data} {xtalk}}}
set finfo(total_xtalk_clock) {real {summation of all crosstalk deltas
in clock path} {{clock} {xtalk}}}
set finfo(total_xtalk) {real {summation of all crosstalk deltas in
clock/data path} {{total} {xtalk}}}
set finfo(xtalk_ratio) {real {percentage ratio of 'total_xtalk_data'
versus 'duration'} {{xtalk} {ratio}}}
set known_fields {index startpoint endpoint start_clk end_clk
launch_latency capture_latency skew crpr path_group slack duration
levels hier_pins num_segments num_unique_segments
num_segment_crossings average_cell_delay slowest_cell
slowest_cell_delay slowest_net slowest_net_delay slowest_net_R
slowest_net_C total_net_delay max_trans total_xtalk_data
total_xtalk_clock total_xtalk xtalk_ratio}
proc max {a b} {
return [expr $a > $b ? $a : $b]
}
proc min {a b} {
return [expr $a < $b ? $a : $b]
}

proc process_paths {args} {

set results(-ungrouped) {}
parse_proc_arguments -args $args results
if {[set paths [filter_collection $results(paths) {object_class ==
timing_path}]] == ""} {
echo "Error: no timing paths provided"
return 0
}
set ungrouped_cells {}
if {[set cells [get_cells -quiet $results(-ungrouped) -filter
"is_hierarchical == true"]] != ""} {
echo "Assuming the following instances have been ungrouped and
flattened for segment processing:"
foreach_in_collection cell $cells {
echo " [get_object_name $cell]"
}
echo ""
# now build a list of all ungrouped hierarchical cells
while {$cells != ""} {
set cell [index_collection $cells 0]
set hier_cells [get_cells -quiet "[get_object_name $cell]/*"
-filter "is_hierarchical == true"]
set cells [remove_from_collection $cells $cell]
set cells [append_to_collection -unique cells $hier_cells]
set ungrouped_cells [append_to_collection -unique ungrouped_cells
$cell]
}
}
# come up with a list of index numbers where we want to print
progress
if {[set num_paths [sizeof $paths]] >= 25} {
set index_notice_point 0
set index_notice_messages {"\n(0%.."}
set index_notice_points {}
for {set i 10} {$i <= 90} {incr i 10} {
lappend index_notice_points [expr {int($i * ($num_paths - 1) /
100)}]
lappend index_notice_messages "${i}%.."
}
lappend index_notice_points [expr {$num_paths - 1}]
lappend index_notice_messages "100%)\n"
} else {
set index_notice_point 25
}
# store path data in this namespace
set path_summary::data_list {}
# we start at an index number of 0

set index 0
foreach_in_collection path $paths {
# print progress message if needed
if {$index == $index_notice_point} {
echo -n "[lindex $index_notice_messages 0]"
set index_notice_point [lindex $index_notice_points 0]
set index_notice_messages [lrange $index_notice_messages 1 [expr
[llength $index_notice_messages]-1]]
set index_notice_points [lrange $index_notice_points 1 [expr
[llength $index_notice_points]-1]]
}
set hier_pins 0
set combo_cell_pins 0
set last_cell_port {}
set slowest_cell {}
set slowest_cell_delay "-INFINITY"
set slowest_net_delay "-INFINITY"
set total_net_delay 0
set max_trans 0
set total_xtalk_data 0.0
set total_xtalk_clock 0.0
set hier_cell_paths {}
set last_cell_or_port {}
set change_in_hier 1
set last_cell_or_port {}
set cell_delay {}
set input_pin_arrival {}
foreach_in_collection point [set points [get_attribute $path
points]] {
set object [get_attribute $point object]
set port [get_ports -quiet $object]
set pin [get_pins -quiet $object]
set cell [get_cells -quiet -of $pin]
set is_hier [get_attribute -quiet $cell is_hierarchical]
set annotated_delta_transition [get_attribute -quiet $point
annotated_delta_transition]
if {$is_hier == "true"} {
# if the pin is hierarchical, increment (these are always in
pairs)
incr hier_pins
if {[remove_from_collection $cell $ungrouped_cells] != ""} {
set change_in_hier 1
}
continue
}
# if we are looking at a new cell just after a change in hierarchy,
# add this to our list
if {$change_in_hier} {

if {$cell != ""} {
# add cell path to list
set basename [get_attribute $cell base_name]
set fullname [get_attribute $cell full_name]
lappend hier_cell_paths [string range $fullname 0 [expr [string
last $basename $fullname]-2]]
} else {
# port, which is base level
lappend hier_cell_paths {}
}
}
# we've handled any change in hierarchy
set change_in_hier 0
# use the fact that a true expression evaluates to 1, count
combinational pins
incr combo_cell_pins [expr {[get_attribute -quiet $cell
is_sequential] == "false"}]
if {[set annotated_delay_delta [get_attribute -quiet $point
annotated_delay_delta]] != ""} {
set total_xtalk_data [expr $total_xtalk_data +
$annotated_delay_delta]
}
set max_trans [path_summary::max $max_trans [get_attribute $point
transition]]
# at this point, we have either a leaf pin or a port
# net delay - delay from previous point to current point with
annotated_delay_delta
# cell delay - delay from previous point with annotated_delay_delta
to current point
set this_arrival [get_attribute $point arrival]
set this_cell_or_port [add_to_collection $port $cell]
if {[compare_collection $this_cell_or_port $last_cell_or_port]} {
if {$last_cell_or_port != ""} {
if {[set net_delay [expr $this_arrival-$last_arrival]] >
$slowest_net_delay} {
set slowest_net_delay $net_delay
set slowest_net [get_nets -quiet -segments
-top_net_of_hierarchical_group [all_connected $object]]
}
set total_net_delay [expr $total_net_delay + $net_delay]
}
if {$input_pin_arrival != ""} {
set cell_delay [expr {$last_arrival - $input_pin_arrival}]
if {$cell_delay > $slowest_cell_delay} {
set slowest_cell_delay $cell_delay

set slowest_cell $last_cell_or_port

}
if {$cell != ""} {
set input_pin_arrival $this_arrival
}
set last_cell_or_port $this_cell_or_port

}
set last_arrival $this_arrival

# get first data arrival time, but skip any clock-as-data pins
set i 0
while {1} {
set startpoint_arrival [get_attribute [set point [index_collection
$points $i]] arrival]
if {[get_attribute -quiet [get_attribute $point object]
is_clock_pin] != "true"} {
break
}
incr i
}
# get clock crosstalk
# 1. pins may appear twice at gclock boundaries, but the delta only
appears once
# and is not double-counted
# 2. capture clock deltas are subtracted to account for inverted
sign
foreach_in_collection point [get_attribute -quiet [get_attribute
-quiet $path launch_clock_paths] points] {
if {[set annotated_delay_delta [get_attribute -quiet $point
annotated_delay_delta]] != ""} {
set total_xtalk_clock [expr $total_xtalk_clock +
$annotated_delay_delta]
}
}
foreach_in_collection point [get_attribute -quiet [get_attribute
-quiet $path capture_clock_paths] points] {
if {[set annotated_delay_delta [get_attribute -quiet $point
annotated_delay_delta]] != ""} {
set total_xtalk_clock [expr $total_xtalk_clock $annotated_delay_delta]
}
}
set data(startpoint) [get_object_name [get_attribute $path
startpoint]]
set data(endpoint) [get_object_name [get_attribute $path endpoint]]
set data(start_clk) [get_attribute -quiet [get_attribute -quiet
$path startpoint_clock] full_name]

set data(end_clk) [get_attribute -quiet [get_attribute -quiet $path


endpoint_clock] full_name]
if {[set data(launch_latency) [get_attribute -quiet $path
startpoint_clock_latency]] == {}} {set data(launch_latency) 0.0}
if {[set data(capture_latency) [get_attribute -quiet $path
endpoint_clock_latency]] == {}} {set data(capture_latency) 0.0}
set data(skew) [expr {($data(capture_latency)$data(launch_latency))*([get_attribute $path path_type]=="max" ? 1 :
-1)}]
if {[set data(crpr) [get_attribute -quiet $path
common_path_pessimism]] == ""} {set data(crpr) 0}
set data(path_group) [get_object_name [get_attribute -quiet $path
path_group]]
set data(duration) [format "%.8f" [expr {[get_attribute $path
arrival]-$data(launch_latency)-$startpoint_arrival}]]
set data(slack) [get_attribute $path slack]
set data(hier_pins) [expr $hier_pins / 2]
set data(num_segments) [llength $hier_cell_paths]
set data(num_segment_crossings) [expr $data(num_segments) - 1]
set data(num_unique_segments) [llength [lsort -unique
$hier_cell_paths]]
set data(levels) [expr {$combo_cell_pins / 2.0}]
set data(average_cell_delay) [expr {$data(levels) == 0 ? 0.0 :
[format "%.7f" [expr {($data(duration) / $data(levels))}]]}]
set data(slowest_cell) [get_attribute -quiet $slowest_cell
full_name]
set data(slowest_cell_delay) $slowest_cell_delay
set data(total_net_delay) $total_net_delay
set data(slowest_net) [get_object_name $slowest_net]
set data(slowest_net_delay) $slowest_net_delay
set data(slowest_net_R) [get_attribute $slowest_net
net_resistance_max]
set data(slowest_net_C) [get_attribute $slowest_net
total_capacitance_max]
set data(index) $index
set data(max_trans) $max_trans
set data(total_xtalk_data) $total_xtalk_data
set data(total_xtalk_clock) $total_xtalk_clock
set data(total_xtalk) [expr {$total_xtalk_data +
$total_xtalk_clock}]
set data(xtalk_ratio) [expr {$data(duration) == 0.0 ? 0 : (100.0 *
$total_xtalk_data / $data(duration))}]
incr index
set list_entry {}
foreach field $path_summary::known_fields {
lappend list_entry $data($field)
}
lappend path_summary::data_list $list_entry

}
echo "Path information stored."
echo ""

}
define_proc_attributes process_paths \
-info "Extract information from paths for write_path_summary" \
-define_args {\
{paths "Timing paths from get_timing_paths" "timing_paths" string
required}
{-ungrouped "Assume these instances have been ungrouped" ungrouped
list optional}
}

proc write_path_summary {args} {


# if user asks for help, remind him of what info is available
if {[lsearch -exact $args {-longhelp}] != -1} {
echo "Available data fields:"
foreach field $path_summary::known_fields {
echo " $field - [lindex $path_summary::finfo($field) 1]"
}
echo ""
return
}
# process arguments
set results(-fields) {startpoint endpoint levels slack}
set results(-csv) 0
set results(-descending) 0
parse_proc_arguments -args $args results
set num_fields [llength $results(-fields)]
# did the user ask for any fields we don't understand?
set leftovers [lminus $results(-fields) $path_summary::known_fields]
if {$leftovers != ""} {
echo "Error: unknown fields $leftovers"
echo " (Possible values: $path_summary::known_fields)"
return 0
}
# get sort type and direction, if specified
if {[info exists results(-sort)]} {
if {[set sort_field [lsearch -exact $path_summary::known_fields
$results(-sort)]] == -1} {
echo "Error: unknown sort field $results(-sort)"
echo " (Possible values: $path_summary::known_fields)"
return 0
}
set sort_type [lindex $path_summary::finfo($results(-sort)) 0]
set sort_dir [expr {$results(-descending) ? "-decreasing" : "increasing"}]
}

# obtain saved data from namespace, apply -sort and -max_paths


set data_list $path_summary::data_list
if {[info exists sort_field]} {
set data_list [lsort $sort_dir -$sort_type -index $sort_field
$data_list]
}
set data_list_length [llength $data_list]
if {[info exists results(-max_paths)] && $data_list_length >
$results(-max_paths)} {
set data_list [lrange $data_list 0 [expr $results(-max_paths)-1]]
}
# generate a list of field index numbers relating to our known fields
set field_indices {}
foreach field $results(-fields) {
lappend field_indices [lsearch $path_summary::known_fields $field]
}
# generate report
if {$results(-csv)} {
# join multi-line headers together
set headers {}
foreach index $field_indices {
lappend headers [join [lindex $path_summary::finfo([lindex
$path_summary::known_fields $index]) 2] { }]
}
# print headers
echo [join $headers {,}]
# print data
foreach item $data_list {
set print_list {}
foreach index $field_indices {
lappend print_list [lindex $item $index]
}
echo [join $print_list {,}]
}
} else {
# determine maximum column widths
echo ""
echo "Legend:"
foreach index $field_indices {
set this_field [lindex $path_summary::known_fields $index]
set this_finfo $path_summary::finfo($this_field)
set this_max_length 0
# check widths of each line of header
foreach header [lindex $this_finfo 2] {

set this_max_length [path_summary::max $this_max_length [string


length $header]]
}
# check widths of data
switch [lindex $this_finfo 0] {
real {
set max_pre 0
set max_post 0
foreach item $data_list {
if {[set this_item [lindex $item $index]] == {INFINITY} ||
$this_item == {-INFINITY}} {
set max_pre 3
set max_post 0
} else {
regexp {([-0-9]*\.?)(.*)} [expr $this_item] dummy pre post
set max_pre [path_summary::max $max_pre [string length $pre]]
set max_post [path_summary::max $max_post [string length
$post]]
}
}
if {[info exists results(-significant_digits)]} {
set max_post $results(-significant_digits)
} else {
set max_post [path_summary::min $max_post 7]
}
set this_max_length [path_summary::max $this_max_length [expr
$max_pre + $max_post]]
}
default {
foreach item $data_list {
set this_max_length [path_summary::max $this_max_length [string
length [lindex $item $index]]]
}
}
}
set max_length($index) $this_max_length
switch [lindex $this_finfo 0] {
int {
set formatting($index) "%${this_max_length}d"
}
real {
set formatting($index) "%${this_max_length}.${max_post}f"
}
string {
set formatting($index) "%-${this_max_length}s"
}

}
echo "$this_field - [lindex $this_finfo 1]"

# now print header


echo ""
for {set i 0} {$i <= 1} {incr i} {
set print_list {}
foreach index $field_indices {
set this_field [lindex $path_summary::known_fields $index]
set this_finfo $path_summary::finfo($this_field)
lappend print_list [format "%-$max_length($index)s" [lindex
[lindex $this_finfo 2] $i]]
}
echo [join $print_list { }]
}
set print_list {}
foreach index $field_indices {
lappend print_list [string repeat {-} $max_length($index)]
}
echo [join $print_list {+}]
# print all data
foreach item $data_list {
set print_list {}
foreach index $field_indices {
lappend print_list [format $formatting($index) [lindex $item
$index]]
}
echo [join $print_list { }]
}
echo ""
}
}
define_proc_attributes write_path_summary \
-info "Generate a summary report for given timing paths" \
-define_args {\
{-longhelp "Show description of available data fields" "" boolean
optional}
{-max_paths "Limit report to this many paths" "num_paths" int
optional}
{-fields "Information fields of interest" "fields" list optional}
{-sort "Sort by this field" "field" string optional}
{-descending "Sort in descending order" "" boolean optional}
{-csv "Generate CSV report for spreadsheet" "" boolean optional}
{-significant_digits "Number of digits to display" digits int
optional}
}

Vous aimerez peut-être aussi