Tuesday, August 22, 2017

procs may be useful in STA primetime analysis:

 proc max {a b} {
  return [expr $a > $b ? $a : $b]
 }

 proc min {a b} {
  return [expr $a < $b ? $a : $b]
 }
}

#-------------------------------------------------------------#
#-- MY procs
#-------------------------------------------------------------#
alias all   "source ../custom/custom.csv_report/all_together.tcl"
alias ts    "timing_summary"
alias gon   "get_object_name "
alias fic   "foreach_in_collection "
alias soc   "sizeof_collection "
alias gat   "get_attribute "
alias lat   "list_attributes -application -nosplit -class"
alias rat   "report_attribute -application -nosplit -class"
alias gp    "get_pins "
alias gpo   "get_ports "
alias gc    "get_cells "
alias glc   "get_lib_cells "
alias glp   "get_lib_pins "
alias gn    "get_nets "
alias galc  "get_alternative_lib_cells "
alias gclk  "get_clocks "
alias ggclk "get_generated_clocks "
alias gtp   "get_timing_paths "
alias ac    "all_connected "
alias aclf  "all_connected -leaf "
alias rt    "report_timing "
alias rac   "report_analysis_coverage "
alias rdc   "report_delay_calculation "
alias ut    "update_timing -full"
alias rcmt  "report_constraint -all_violators -max_transition -nosplit"
alias gd    "get_drivers"
alias gl    "get_loads"
alias gpg   "get_path_groups"


#----------------------------------------------------------------------------------------------------------#
#-- END
#----------------------------------------------------------------------------------------------------------#





#----------------------------------------------------------------------------------------------------------#
#--  SETTING CUSTOM VARIABLES
#----------------------------------------------------------------------------------------------------------#
#-- THIS VARIABLE sdc_save_source_file_information prints the line where the exception is faulty
set_app_var  sdc_save_source_file_information  true

#----------------------------------------------------------------------------------------------------------#
#-- END
#----------------------------------------------------------------------------------------------------------#





#----------------------------------------------------------------------------------------------------------#
#--  LOCAL_PROCS
#----------------------------------------------------------------------------------------------------------#
proc gpl {var} {
    foreach_in_collection  pin [get_pins $var] {
        puts "[gon $pin]"
    }
}

proc gpol {var} {
    foreach_in_collection  port [get_ports $var] {
        puts "[gon $port]"
    }
}

proc gcl {var} {
    foreach_in_collection  cell [get_cells $var] {
        puts "[gon $cell]"
    }
}

proc glcl {var} {
    foreach_in_collection  lib_cell [get_lib_cells $var] {
        puts "[gon $lib_cell]"
    }
}

proc glpl {var} {
    foreach_in_collection  lib_pin [get_lib_pins $var] {
        puts "[gon $lib_pin]"
    }
}

proc gnl {var} {
    foreach_in_collection  net [get_nets $var] {
        puts "[gon $net]"
    }
}

proc galcl {var} {
    foreach_in_collection  alternative_lib_cell [get_alternative_lib_cells $var] {
        puts "[gon $alternative_lib_cell]"
    }
}

proc gclkl {var} {
    foreach_in_collection  clock [get_clocks $var] {
        puts "[gon $clock]"
    }
}

proc ggclkl {var} {
    foreach_in_collection  gen_clock [get_generated_clocks $var] {
        puts "[gon $gen_clock]"
    }
}

proc gtpl {var} {
    foreach_in_collection  timing_path [get_timing_paths $var] {
        puts "[gon $timing_path]"
    }
}

proc gpgl {var} {
    foreach_in_collection  path_group [get_path_groups $var] {
        puts "[gon $path_group]"
    }
}

proc acl {var} {
    foreach_in_collection  all_connection [all_connected [get_nets -of [get_pins $var]]] {
        puts "[gon $all_connection]"
    }
}

proc ll {var} {
    fic var1 $var {
        puts "[gon $var1]"
    }
}

###Prints all connected output/inout pins if pin is input port
###Prints all connected input/inout pins if pin is output port
proc qnp {pin} {
  if {[gat [gp $pin] direction]=="in"} {
    fic p1 [aclf [ac $pin]] {                                                                            
      if {[gat [gp $p1] direction]=="out" || [gat [gp $p1] direction]=="inout" } {                              
        puts "[gon $p1]"                                                    
      }
    }
  } elseif {[gat [gp $pin] direction]=="out"} {
    fic p1 [gp -of [gc -of [gp $pin]]] {                                                                            
      if {[gat [gp $p1] direction]=="in" || [gat [gp $p1] direction]=="inout" } {                              
        puts "[gon $p1]"                                                    
      }
    }
  }
}


proc qns {pin} {
  if {[gat [gp $pin] direction]=="out"} {
    fic p1 [aclf [ac $pin]] {                                                                            
      if {[gat [gp $p1] direction]=="in" || [gat [gp $p1] direction]=="inout" } {                              
        puts "[gon $p1]"                                                    
      }
    }
  } elseif {[gat [gp $pin] direction]=="in"} {
    fic p1 [gp -of [gc -of [gp $pin]]] {                                                                            
      if {[gat [gp $p1] direction]=="out" || [gat [gp $p1] direction]=="inout" } {                              
        puts "[gon $p1]"                                                    
      }
    }
  }
}


proc ref_name {cell} {
    puts "[gat [gc $cell] ref_name]"
}

proc lib_cell {cell} {
    puts "[gon [gat [gc $cell] lib_cell]]"
}

proc get_instance {lib_cell} {
    fic c1 [gc "*" -hier -filter "ref_name == $lib_cell"] {
        puts "[gon $c1]"
    }
}

proc clocks {pin} {
    puts "[gon [gat [gp $pin] clocks]]"
}

proc case_value {pin} {
    puts "[gat [gp $pin] case_value]"
}


proc constant_value {pin} {
    puts "[gat [gp $pin] constant_value]"
}

proc is_sequential {cell} {
    puts "[gat [gc $cell] is_sequential]"
}

proc is_combinational {cell} {
    puts "[gat [gc $cell] is_combinational]"
}

proc is_integrated_clock_gating_cell {cell} {
    puts "[gat [gc $cell] is_integrated_clock_gating_cell]"
}

#----------------------------------------------------------------------------------------------------------#
#-- END
#----------------------------------------------------------------------------------------------------------#




#----------------------------------------------------------------------------------------------------------#
#-- report_cells_on_clock_data_network
#----------------------------------------------------------------------------------------------------------#
proc report_cells_on_clock_data_network { args } {
  set options(-network) both
  set options(-exclude_lib_cells) ""
  set options(-lib_cells) ""

  parse_proc_arguments -args $args options
  if { $options(-lib_cells)!="" && $options(-exclude_lib_cells)!="" } {
    echo " -lib_cells and -exclude_lib_cells are mutually exclusive options, please specify only one of them"
    return 0
   } elseif { $options(-lib_cells)=="" && $options(-exclude_lib_cells)==""  } {
    echo " either  -lib_cells and -exclude_lib_cells options are required, please specify one of them"
    return 0
   }

  if { $options(-lib_cells)!="" } {
    set lib_cells $options(-lib_cells)
    set operator1 "="
    set operator2 "||"
    set tag ""
  } else {
    set lib_cells $options(-exclude_lib_cells)
    set operator1 "!"
    set operator2 "&&"
    set tag (excluded)
  }
  set network  $options(-network)
  set count [llength $lib_cells]
  set filter_option ""
  foreach lib_cell $lib_cells {
  set count [expr $count - 1]
    if { [regexp \\* $lib_cell ] } {
      set filter_option "$filter_option ref_name${operator1}~${lib_cell}"
    } else {
      set filter_option "$filter_option ref_name${operator1}=${lib_cell}"
    }
    if { [expr $count > 0] } {
      set filter_option "$filter_option $operator2"
    }
  }
  #echo "$filter_option"
  set instances_of_lib_cells [get_cells -hier * -filter "$filter_option"]

  set clock_path_cells ""
  set data_path_cells ""

  foreach_in_collection cell $instances_of_lib_cells {

    if { [get_attribute -quiet [get_pins -of [get_cells $cell]] clocks ] != "" } {
      set clock_path_cells [add_to_collection  $clock_path_cells $cell]
    } else {
      set data_path_cells [add_to_collection  $data_path_cells $cell]
    }

 }

  if  { $network=="clock" || $network =="both"} {
    echo "##################################################################"
    echo "Cells on Clock Network of Lib Cells $tag: $lib_cells"
    echo "##################################################################"
    if { [sizeof $clock_path_cells] =="0" } { echo "None" }
    foreach_in_collection cellc $clock_path_cells {
      echo "[get_object_name $cellc] [get_attribute $cellc ref_name]"
    }
  }

  if  { $network=="data" || $network =="both" } {
    echo "##################################################################"
    echo "Cells on Data Network of Lib Cells $tag: $lib_cells"
    echo "##################################################################"
    if { [sizeof $data_path_cells] =="0" } { echo "None" }
    foreach_in_collection celld $data_path_cells {
      echo "[get_object_name $celld]\t[get_attribute $celld ref_name]"
    }
  }
  return 1
}


define_proc_attributes report_cells_on_clock_data_network \
   -info "report the given lib cells are on clock or data network " \
   -define_args \
   {
     { -network "report for clock, data or both" "" one_of_string {optional {values {clock data both}}} }
     { -lib_cells "list of lib_cells, wild cards supported" "" string optional }
     { -exclude_lib_cells "list of lib cells to be excluded, wild cards supported" "" string optional  }

   }
#----------------------------------------------------------------------------------------------------------#
#-- END
#----------------------------------------------------------------------------------------------------------#


#----------------------------------------------------------------------------------------------------------#
#-- report_slack_distribution
#----------------------------------------------------------------------------------------------------------#
   proc report_slack_distribution { targetClock {marginPoints "0 .5 1 1.5"  } {ListEndPoints 0}}  {
   #
   # 5/8/97 xxxxxxx
   # 6/13/05 update to tcl commands
   # report_slack_distribution
   #  positionals
   #    target clock name
   #    list of interesting margin points
   #    list end points
   #
   #  usage:
   #  report_slack_dist sysclk "0 0.1 0.5 1 2 3 4" 0
   #
   # reports the total negative slack, the worst violator at the given clock
   # and then calculates the number of violating paths at each of the margin
   # points subtracted from the clock
   #
   # Here is a sample report
   #
   #  Timing Information for clock clk with 128 total endpoints:
   #
   #     Worst Negative Slack:          -1.39365
   #
   #  margin   Num. of Violators  Total Neg. Slack   percent violators
   #  ------   -----------------  ----------------   -----------------
   #  0          16                   -17.9794            13
   #  .5         24                   -27.77              19
   #  1          24                   -39.7698            19
   #  1.5        28                   -53.1804            22
   #


   #set ListEndPoints 0
   #set marginPoints 0.0
   set WNS 100000.0
   set TE 0
   foreach margin $marginPoints {
      set TNS($margin) 0.0
      set NVE($margin) 0
      set PCT($margin) 0
      }
   set pathsPerEndpoint 1
   echo ""
   echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"
   echo "         Analyzing end point slack clocked by $targetClock"
   echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"

   # find all the endpoints clocked by targetClock
   set allRegisterPins [all_registers -clock $targetClock -data]
   set allOutputPorts [all_outputs -clock $targetClock]
   set allEndPoints [concat $allRegisterPins $allOutputPorts]

   foreach_in_collection endPoint $allEndPoints {
      # get and print the startpoint, arrival and slack
      set maxTimingSelection [get_timing_path -nworst $pathsPerEndpoint -to $endPoint -delay max]
      set firstFor 1
      foreach_in_collection path $maxTimingSelection {
         set TE [expr $TE + 1]
         set slack [get_attribute $path slack]
         set startpoint [get_attribute $path startpoint]
            if { $firstFor == 1 } {
               if { $slack < $WNS } {
                  set WNS $slack
               }
               if { $ListEndPoints == 1 } {
                   if { $slack < 0.0 } {
                       echo [format "Slack of %-5.2f from %-30s to %-30s" \
                       $slack [get_object_name $startpoint ] \
                       [get_object_name $endPoint ] ]
                   }
               }
               foreach margin $marginPoints {
                   if { $slack < $margin } {
                      set TNS($margin) [expr $TNS($margin) + ($slack-$margin)]
                      set NVE($margin) [expr $NVE($margin) + 1]
                   }
               }
            }
       }
    }
   echo ""
   echo "Timing Information for clock $targetClock with $TE total endpoints:"
   echo ""
   echo [format "   Worst Negative Slack:          %-20.2f"  $WNS]
   echo ""
   echo " margin   Num. of Violators  Total Neg. Slack   percent violators"
   echo " ------   -----------------  ----------------   -----------------"
   foreach margin $marginPoints {
      echo [format "%6.2f %13d       %14.2f          %5.1f " \
            $margin $NVE($margin) $TNS($margin) [expr 100-(100*($TE-$NVE($margin))/$TE) ]]
   }
}
#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#


#-- #----------------------------------------------------------------------------------------------------------#
#-- #-- curdes_prompt
#-- #----------------------------------------------------------------------------------------------------------#
#--
#-- proc curdes_prompt {} {
#--   global sh_dev_null
#--   global synopsys_program_name
#--   set des ""
#--   redirect $sh_dev_null {set des [current_design]}
#--   if { $des == "" } {
#--     echo -n "${synopsys_program_name}> "
#--     return
#--   }
#--   set des [get_object_name $des]
#--   set inst ""
#--   redirect $sh_dev_null {set inst [current_instance .]}
#--   if { $inst == "" } {
#--     set sep ""
#--   } else {
#--     set sep "/"
#--   }
#--   #-- "SETTING PRIMETIME PROMPT"
#--   set prmpt "${des}${sep}${inst}-pt_shell-> "
#--   #-- set prmpt "PRIMETIME-> "
#--   echo -n ${prmpt}
#-- }
#-- set tcl_prompt1 curdes_prompt
#-- #-- unset tcl_prompt1 curdes_prompt
#-- #------------------------------------------------------------------------------------------------#
#-- #-- END
#-- #------------------------------------------------------------------------------------------------#




#------------------------------------------------------------------------------------------------#
#-- write_path_summary
#------------------------------------------------------------------------------------------------#
#-- USAGE :
#-- e.g.
#-- set paths [get_timing_paths -delay_type max -slack_lesser_than 1000 -max_paths 10]
#-- process_paths $paths                                                            
#-- write_path_summary -fields {index endpoint slack duration crpr num_segments num_unique_segments num_segment_crossings max_trans skew}
#-- where '-fields' can be
#--   index - index number of path in original path collection (0, 1, 2...)
#--   startpoint - name of path startpoint
#--   endpoint - name of path endpoint
#--   start_clk - name of startpoint launching clock
#--   end_clk - name of endpoint capturing clock
#--   launch_latency - launching clock latency
#--   capture_latency - capturing clock latency
#--   skew - skew between launch/capture clock (negative is tighter)
#--   crpr - clock reconvergence pessimism removal amount
#--   path_group - path group name
#--   slack - path slack
#--   duration - combinational path delay between startpoint and endpoint
#--   levels - levels of combinational logic
#--   hier_pins - number of hierarchy pins in path
#--   num_segments - number of segments in path
#--   num_unique_segments - number of unique segments in path
#--   num_segment_crossings - number of segment crossings in path
#--   average_cell_delay - average combinational cell delay (duration / levels)
#--   slowest_cell - name of slowest cell in path
#--   slowest_cell_delay - cell delay of slowest cell in path
#--   slowest_net - name of slowest net in path
#--   slowest_net_delay - net delay of slowest net in path
#--   slowest_net_R - resistance of slowest net in path
#--   slowest_net_C - capacitance of slowest net in path
#--   total_net_delay - summation of all net delays in path
#--   max_trans - slowest pin transition in path
#--   total_xtalk_data - summation of all crosstalk deltas in data path
#--   total_xtalk_clock - summation of all crosstalk deltas in clock path
#--   total_xtalk - summation of all crosstalk deltas in clock/data path
#--   xtalk_ratio - percentage ratio of 'total_xtalk_data' versus 'duration'

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 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}
 }

#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#


#------------------------------------------------------------------------------------------------#
#-- get_drivers  get_loads
#------------------------------------------------------------------------------------------------#
proc get_drivers {args} {
 parse_proc_arguments -args $args results

 # if it's not a collection, convert into one
 redirect /dev/null {set size [sizeof_collection $results(object_spec)]}
 if {$size == ""} {
  set objects {}
  foreach name $results(object_spec) {
   if {[set stuff [get_ports -quiet $name]] == ""} {
    if {[set stuff [get_cells -quiet $name]] == ""} {
     if {[set stuff [get_pins -quiet $name]] == ""} {
      if {[set stuff [get_nets -quiet $name]] == ""} {
       continue
      }
      }
    }
   }
   set objects [add_to_collection $objects $stuff]
  }
 } else {
  set objects $results(object_spec)
 }

 if {$objects == ""} {
  echo "Error: no objects given"
  return 0
 }

 set driver_results {}

 # process all cells
 if {[set cells [get_cells -quiet $objects]] != ""} {
  # add driver pins of these cells
  set driver_results [add_to_collection -unique $driver_results \
   [get_pins -quiet -of $cells -filter "pin_direction == out || pin_direction == inout"]]
 }

 # get any nets
 set nets [get_nets -quiet $objects]

 # get any pin-connected nets
 if {[set pins [get_pins -quiet $objects]] != ""} {
  set nets [add_to_collection -unique $nets \
   [get_nets -quiet -of $pins]]
 }

 # get any port-connected nets
 if {[set ports [get_ports -quiet $objects]] != ""} {
  set nets [add_to_collection -unique $nets \
   [get_nets -quiet -of $ports]]
 }

 # process all nets
 if {$nets != ""} {
  # add driver pins of these nets
  set driver_results [add_to_collection -unique $driver_results \
   [get_pins -quiet -leaf -of $nets -filter "pin_direction == out || pin_direction == inout"]]
  set driver_results [add_to_collection -unique $driver_results \
   [get_ports -quiet -of $nets -filter "port_direction == in || port_direction == inout"]]
 }

 # return results
 return $driver_results
}

define_proc_attributes get_drivers \
 -info "Return driver ports/pins of object" \
 -define_args {\
  {object_spec "Object(s) to report" "object_spec" string required}
 }





proc get_loads {args} {
 parse_proc_arguments -args $args results

 # if it's not a collection, convert into one
 redirect /dev/null {set size [sizeof_collection $results(object_spec)]}
 if {$size == ""} {
  set objects {}
  foreach name $results(object_spec) {
   if {[set stuff [get_ports -quiet $name]] == ""} {
    if {[set stuff [get_cells -quiet $name]] == ""} {
     if {[set stuff [get_pins -quiet $name]] == ""} {
      if {[set stuff [get_nets -quiet $name]] == ""} {
       continue
      }
      }
    }
   }
   set objects [add_to_collection $objects $stuff]
  }
 } else {
  set objects $results(object_spec)
 }

 if {$objects == ""} {
  echo "Error: no objects given"
  return 0
 }

 set load_results {}

 # process all cells
 if {[set cells [get_cells -quiet $objects]] != ""} {
  # add load pins of these cells
  set load_results [add_to_collection -unique $load_results \
   [get_pins -quiet -of $cells -filter "pin_direction == in || pin_direction == inout"]]
 }

 # get any nets
 set nets [get_nets -quiet $objects]

 # get any pin-connected nets
 if {[set pins [get_pins -quiet $objects]] != ""} {
  set nets [add_to_collection -unique $nets \
   [get_nets -quiet -of $pins]]
 }

 # get any port-connected nets
 if {[set ports [get_ports -quiet $objects]] != ""} {
  set nets [add_to_collection -unique $nets \
   [get_nets -quiet -of $ports]]
 }

 # process all nets
 if {$nets != ""} {
  # add load pins of these nets
  set load_results [add_to_collection -unique $load_results \
   [get_pins -quiet -leaf -of $nets -filter "pin_direction == in || pin_direction == inout"]]
  set load_results [add_to_collection -unique $load_results \
   [get_ports -quiet -of $nets -filter "port_direction == out || port_direction == inout"]]
 }

 # return results
 return $load_results
}

define_proc_attributes get_loads \
 -info "Return load ports/pins of object" \
 -define_args {\
  {object_spec "Object(s) to report" "object_spec" string required}
 }

#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#




#------------------------------------------------------------------------------------------------#
#-- get_failing_paths_high_slew
#------------------------------------------------------------------------------------------------#
#-- USAGE
#--   get_failing_paths_high_slew 1 max 0.01
#--   All Violations of nworst 1 and for 'max' paths with trans above 0.01
proc get_failing_paths_high_slew { Nworst_numb Path_type Tran_threshold } {

set Nworst_numb $Nworst_numb
set Path_type $Path_type
set Tran_threshold $Tran_threshold

foreach_in_collection path [get_timing_paths -delay_type $Path_type -nworst $Nworst_numb] {

  foreach_in_collection itr $path {
     set cnt 0
     set Slack  [get_attribute $itr slack]
     set StartPoint [get_object_name [get_attribute $itr startpoint] ]
     set EndPoint [get_object_name [get_attribute $itr endpoint] ]

     if { $Slack < 1000.0 } {
        if { $cnt == 0 } {
          echo " Timing path fails:  \t Start point: $StartPoint \t End Point $EndPoint  \t Slack: $Slack";
          set $cnt [ expr $cnt + 1]
          }
     ;# violating paths next  
      foreach_in_collection point [get_attribute $path points] {
         set Object [get_attribute $point object]
         set Obj_name   [get_object_name [get_attribute $point object] ]

         set Obj_r_f    [get_attribute $point rise_fall]
         set Slew_att   [format "actual_%s_transition_%s" $Obj_r_f $Path_type]
         set Slew_val   [ get_attribute $Object $Slew_att]
         set Cell       [ get_cells -quiet -of_objects $Object ]
     
         if { [get_attribute -quiet $Object is_port] == "false" } {
            set Lib_cell   [ get_lib_cells -of_objects $Cell ]
            set Ref_name   [ get_attribute $Lib_cell base_name ]
            } else {
            ;# it's a port
            set Ref_name   $Object
            }

         if { $Slew_val > $Tran_threshold } {
            echo " has a $Obj_r_f slew = $Slew_val on pin $Obj_name \t Reference = $Ref_name"
           }
        } ;# end of foreach point
     } else {
     continue
     ;# slack is > 0.0 no violation
     ;# go to next timing path
     }
   }; # end foreach $path
 } ;# end of foreach get_timing_path

 ;# variable cleanup
#-- unset Slack  Obj_name Obj_r_f Slew_att Slew_val Nworst_numb Path_type Tran_threshold

} ;# end of proc
#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#




#------------------------------------------------------------------------------------------------#
#-- report_critical_loads
#------------------------------------------------------------------------------------------------#
namespace eval rcl {
 proc pin_basename {object} {
  set basename [get_attribute $object lib_pin_name]
  set fullname [get_attribute $object full_name]
  return [string range $fullname 0 [expr [string last $basename $fullname]-2]]
 }

 proc net_basename {object} {
  set basename [get_attribute $object base_name]
  set fullname [get_attribute $object full_name]
  return [string range $fullname 0 [expr [string last $basename $fullname]-2]]
 }

 proc net_loads {net} {
  set loads {}
  foreach_in_collection object [all_connected $net] {
   set direction [get_attribute $object direction]
   if {[get_attribute $object object_class] == "pin" && [pin_basename $object] != [net_basename $net]} {
    # pin at deeper hierarchical level
    if {$direction != "out"} {
     set loads [add_to_collection $loads $object]
    }
   } else {
    # pin at same hierarchical level
    if {$direction != "in"} {
     set loads [add_to_collection $loads $object]
    }
   }
  }
  return $loads
 }

 proc net_drivers {net} {
  set drivers {}
  foreach_in_collection object [all_connected $net] {
   set direction [get_attribute $object direction]
   if {[get_attribute $object object_class] == "pin" && [pin_basename $object] != [net_basename $net]} {
    # pin at deeper hierarchical level
    if {$direction != "in"} {
     set drivers [add_to_collection $drivers $object]
    }
   } else {
    # pin at same hierarchical level
    if {$direction != "out"} {
     set drivers [add_to_collection $drivers $object]
    }
   }
  }
  return $drivers
 }

 proc max {a b} {return [expr {$a > $b ? $a : $b}]}
}

proc report_critical_loads {args} {
 set results(-delay) "max"
 parse_proc_arguments -args $args results

 if {$::timing_save_pin_arrival_and_slack != "true"} {
  echo "Error: the variable 'timing_save_pin_arrival_and_slack' must be set to 'true' for this procedure"
  return 0
 }

 if {[set nets [get_nets -seg -quiet $results(object)]] == ""} {
  if {[set object [get_pins -quiet $results(object)]] == ""} {
   if {[set object [get_ports -quiet $results(object)]] == ""} {
    echo "Error: couldn't find specified object"
    return 0
   } else {
    set nets [get_nets -seg -of $object]
   }
  } else {
   set nets [get_nets -seg -of $object]
  }
 }

 set driver_load([list 0 1]) "load"
 set driver_load([list 1 0]) "driver"
 set driver_load([list 1 1]) "driver/load"

 set min_max "max"
 set max_pre 0
 set max_post 0

 echo "Number of net segments: [sizeof_collection $nets]"
 echo ""

 foreach_in_collection net $nets {
  set driver_object_slack_list {}
  set load_object_slack_list {}

  echo "Net segment '[set net_name [get_object_name $net]]':"

  # determine drivers/loads
  set drivers [rcl::net_drivers $net]
  set loads [rcl::net_loads $net]

  # determine max length of all loads and driver slacks
  set max_name_length 0
  foreach_in_collection object [add_to_collection -unique $drivers $loads] {
   set rise_slack [get_attribute $object ${min_max}_rise_slack]
   set fall_slack [get_attribute $object ${min_max}_fall_slack]

   # handle unconstrained load pins
   if {$rise_slack == {INFINITY} && $fall_slack == {INFINITY}} {
    set slack {INFINITY}
   } else {
    set slack [expr {"$rise_slack" < "$fall_slack" ? "$rise_slack" : "$fall_slack"}]
   }
   regexp {([-0-9]*\.?)(.*)} $slack dummy pre post
   set max_pre [rcl::max $max_pre [string length $pre]]
   set max_post [rcl::max $max_post [string length $post]]
   set max_name_length [rcl::max $max_name_length [string length [get_object_name $object]]]
   set is_driver [expr {[remove_from_collection $object $drivers] == ""}]
   set is_load [expr {[remove_from_collection $object $loads] == ""}]
   if {$is_driver} {
    lappend driver_object_slack_list [list $object $slack $is_driver $is_load]
   } else {
    lappend load_object_slack_list [list $object $slack $is_driver $is_load]
   }
  }
  set max_slack_length [expr $max_pre + $max_post]
  echo [format "%-${max_slack_length}s %s" "slack" "name"]
  echo [string repeat "-" [expr {$max_slack_length + 1 + $max_name_length}]]
  set driver_object_slack_list [lsort -real -index 1 -increasing $driver_object_slack_list]
  set load_object_slack_list [lsort -real -index 1 -increasing $load_object_slack_list]

  foreach object [concat $driver_object_slack_list $load_object_slack_list] {
   set obj [lindex $object 0]
   set driver_or_load $driver_load([list [lindex $object 2] [lindex $object 3]])
   set object_class [get_attribute $obj object_class]
   if {$object_class == "pin"} {
    set driver_or_load "[expr {[get_attribute [get_cells -of $obj] is_hierarchical] == "true" ? "hierarchical" : "leaf"}] $driver_or_load"
   }
   echo [format "%${max_slack_length}.${max_post}f %s (%s %s)" [lindex $object 1] [get_object_name $obj] $driver_or_load $object_class]
  }
  echo ""
  echo "insert_buffer \[get_pins {"
  foreach load_pin $load_object_slack_list {
   echo " [get_object_name [lindex $load_pin 0]]"
  }
  echo "}\] lib/cell"
  echo ""
  echo ""
 }
}

define_proc_attributes report_critical_loads \
 -info "Show loads on a net sorted by slack" \
 -define_args {\
  {-delay_type "Type of slack to analyze" type one_of_string {optional value_help {values {min max}}}}
  {object "Net, pin, or port to analyze" "objects" string required}
 }

#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#


#------------------------------------------------------------------------------------------------#
#-- check_data_pulse_width [all_outputs]
#------------------------------------------------------------------------------------------------#
proc check_data_pulse_width {args} {
 global sh_dev_null

 set results(-verbose) 0
 set results(data_pins) {}
 set results(-width) 1.0
 parse_proc_arguments -args $args results

 if {! $results(-width) > 0.0} {
  echo "Error: pulse width must be a positive value"
  return 0
 }

 if {$results(data_pins) == ""} {
  set results(data_pins) [all_registers -data_pins]
 }

 set pins [get_pins -quiet $results(data_pins)]

 # support passing in a collection of ports (outputs or inouts) for pulsewidth calculation
 set pins [add_to_collection $pins [get_ports -quiet $results(data_pins)]]

 set rise_paths [get_timing_paths -delay max_rise -to $pins -max_paths 99999]
 set fall_paths [get_timing_paths -delay max_fall -to $pins -max_paths 99999]

 # first, do rising setup arrivals, falling hold arrivals
 foreach_in_collection this_path $rise_paths {
  set this_pin [get_attribute $this_path endpoint]
  set max_this_pin_clock_close_edge_value [get_attribute -quiet $this_path endpoint_clock_close_edge_value]
  set arrival_windows [lindex [get_attribute $this_pin arrival_window] 0]
  if {$max_this_pin_clock_close_edge_value == ""} {continue}

  # determine latest rising arrival, earliest falling arrival
  set latest_rise_arrival {}
  set earliest_fall_arrival {}
  foreach window $arrival_windows {
   # {CLK1} pos_edge {min_r_f 0.30098 0.328784} {max_r_f 0.30098 0.328784}
   set max_rise_arrival [lindex [lindex $window 3] 1]
   set min_fall_arrival [lindex [lindex $window 2] 2]
   if {$latest_rise_arrival == "" || $max_rise_arrival > $latest_rise_arrival} {
    set latest_rise_arrival $max_rise_arrival
   }
   if {$earliest_fall_arrival == "" || $min_fall_arrival < $earliest_fall_arrival} {
    set earliest_fall_arrival $min_fall_arrival
   }
  }

  set setup_window [expr $max_this_pin_clock_close_edge_value - $latest_rise_arrival]
  set hold_window $earliest_fall_arrival
  set pulse_width [expr $setup_window + $hold_window]

  if {$results(-verbose) || $pulse_width < $results(-width)} {
   echo "Rising setup window: $setup_window ($max_this_pin_clock_close_edge_value - $latest_rise_arrival)"
   echo "Falling hold window: $earliest_fall_arrival"
   echo "Active high pulse width for [get_object_name $this_pin]: $pulse_width ($setup_window   $hold_window)\n"
  }
 }

 # second, do falling setup arrivals, rising hold arrivals
 foreach_in_collection this_path $fall_paths {
  set this_pin [get_attribute $this_path endpoint]
  set max_this_pin_clock_close_edge_value [get_attribute -quiet $this_path endpoint_clock_close_edge_value]
  set arrival_windows [lindex [get_attribute $this_pin arrival_window] 0]
  if {$max_this_pin_clock_close_edge_value == ""} {continue}

  # determine latest falling arrival, earliest rising arrival
  set latest_fall_arrival {}
  set earliest_rise_arrival {}
  foreach window $arrival_windows {
   # {CLK1} pos_edge {min_r_f 0.30098 0.328784} {max_r_f 0.30098 0.328784}
   set max_fall_arrival [lindex [lindex $window 3] 2]
   set min_rise_arrival [lindex [lindex $window 2] 1]
   if {$latest_fall_arrival == "" || $max_fall_arrival > $latest_fall_arrival} {
    set latest_fall_arrival $max_fall_arrival
   }
   if {$earliest_rise_arrival == "" || $min_rise_arrival < $earliest_rise_arrival} {
    set earliest_rise_arrival $min_rise_arrival
   }
  }

  set setup_window [expr $max_this_pin_clock_close_edge_value - $latest_fall_arrival]
  set hold_window $earliest_rise_arrival
  set pulse_width [expr $setup_window + $hold_window]

  if {$results(-verbose) || $pulse_width < $results(-width)} {
   echo "Falling setup window: $setup_window ($max_this_pin_clock_close_edge_value - $latest_fall_arrival)"
   echo "Rising hold window: $earliest_rise_arrival"
   echo "Active low pulse width for [get_object_name $this_pin]: $pulse_width ($setup_window   $hold_window)\n"
  }
 }


 echo "Analysis of [sizeof_collection $pins] pin(s) complete."
}

define_proc_attributes check_data_pulse_width \
  -info "checks data pulse width between setup/hold edge arrivals" \
  -define_args \
  {
    {-verbose "show details of calculation" "" boolean optional}
    {-width "minimum pulse width between setup/hold edge arrivals" min_width string optional}
    {data_pins "data pins to check" "data_pins" string optional}
  }

#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#




#------------------------------------------------------------------------------------------------#
#-- si_net_delta_delay
#------------------------------------------------------------------------------------------------#
#-- For finding the crosstalk delta delay associated with a net.
#-- It takes a net and returns the delta_delay for that net.
proc find_delta { net {min_max ""} {rise_fall ""} } {
  set net_arcs [get_timing_arcs -of_objects $net]
  set worst_delta 0.0
  set abs_delta 0.0
  if {$min_max == ""} {
    set min_max "min max"
  }
  if {$rise_fall == ""} {
    set rise_fall "rise fall"
  }

  set worst_mm_rf "$min_max\_$rise_fall"
  foreach mm $min_max {
    foreach rf $rise_fall {
      foreach_in_collection net_arc $net_arcs {
        set delta [get_attribute $net_arc \
                       "annotated_delay_delta_$mm\_$rf"]
        set abs_delta [expr abs($delta)]
        if {$abs_delta > $worst_delta } {
          set worst_mm_rf "$mm\_$rf"
          set worst_delta $abs_delta
        }
      }
    }
  }

  return $worst_delta
}


#-- This procedure takes a collection of nets and crosstalk delay threshold as inputs.
#-- It will then find all of the nets in the collection that equal or surpass the specified threshold.
#-- The procedure then returns an array of these nets, the delta delay of the net,
#-- the driving pin of the net, and the reference name (library cell) of the driving cell.
#-- Timing units of the Threshold are in library units.  Threshold should be >= 0.
#-- It is required to source the find_delta.tcl procedure.
#--
#-- USAGE :
#--   #Find all clock nets of clk1 that have more than 50 ps of crosstalk delay
#--   set NETS [get_clock_network_objects clk1 -include -type net]
#--   set THRESH 0.050
#--   si_net_delta_delay $NETS $THRESH si_net_delay_array_clk1
#--   # To access the net names use the following command
#--   array names si_net_delay_array_clk1
proc si_net_delta_delay { NETS THRESH ARRAY} {
  upvar $ARRAY NET_DELTA_ARRAY

  foreach_in_coll NET [get_nets $NETS] {
    set DELTA [find_delta $NET]
      if {$DELTA >= $THRESH } {
      set DRIVER [filter_coll [all_connected $NET] direction==out]
      if {[sizeof_collection $DRIVER] !=0} {
      set NET_DELTA_ARRAY([get_object_name $NET]) "$DELTA [get_object_name $DRIVER] [get_attr [get_cell -of_obj $DRIVER] ref_name]"
      echo $NET_DELTA_ARRAY([get_object_name $NET])
     } else {
     echo "No driver cell for " [get_att $NET  full_name ]
     }
    }
  }
}
#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#


#------------------------------------------------------------------------------------------------#
#-- plot_lcd
#------------------------------------------------------------------------------------------------#
#--  plot cell delay versus load, area, always_on and dont_use
#-- USAGE  :
#--  plot_lcd  hs_invd4  I  ZN
#--  libcell_delay_plot -help
proc libcell_delay_plot {args} {
 global link_path

 set results(-transition) 0
 set results(-delay_type) {max}
 set results(-load_indices) {0 1}
 set results(-num_points) 200
 parse_proc_arguments -args $args results
 set lib_cell $results(cell)
 set from_pin $results(-from_pin)
 set to_pin $results(-to_pin)

 if {![string is double [set min_slew [lindex $results(-load_indices) 0]]] ||
     ![string is double [set max_slew [lindex $results(-load_indices) 1]]]} {
  echo "Error: non-float value given in slew range list"
  return 0
 }
 set min_slew [expr double($min_slew)]
 set max_slew [expr double($max_slew)]

 # create indices
 set indices [list [set lastindex [lindex $results(-load_indices) 0]]]
 foreach index [lrange $results(-load_indices) 1 [expr [llength $results(-load_indices)]-1]] {
  for {set i 1} {$i < $results(-num_points)} {incr i} {
   lappend indices [expr $lastindex + (($index - $lastindex) * double($i) / ($results(-num_points)))]
  }
  lappend indices [set lastindex $index]
 }

 foreach lib $link_path {
  if {$lib == {*}} {continue}
  set libpath [which $lib]
  if {$libpath != ""} {
   if {[get_libs -quiet "*" -filter "source_file_name == $libpath"] == ""} {
    read_db $libpath
   }
  }
 }

 if {[set cell [get_lib_cells -quiet $lib_cell]] == ""} {
  foreach_in_collection lib [get_libs -quiet "*"] {
   if {[set cell [get_lib_cells -quiet [get_object_name $lib]/$lib_cell]] != ""} {
    break;
   }
  }
 }

 set cellname [get_attribute $cell base_name]
 set fullcellname [get_attribute $cell full_name]

 if {[get_lib_pins -of $cell -filter "base_name == $from_pin"] == ""} {
  echo "Error: couldn't find $from_pin on $cellname"
  return 0
 }
 if {[get_lib_pins -of $cell -filter "base_name == $to_pin"] == ""} {
  echo "Error: couldn't find $to_pin on $cellname"
  return 0
 }

 set outfile [expr {[info exists results(-output)] ? "$results(-output)" : "${cellname}.txt"}]

 set pins(in) {}
 set pins(out) {}
 set pins(inout) {}
 foreach_in_collection pin [get_lib_pins -of $cell] {
  lappend pins([get_attribute $pin pin_direction]) [get_attribute $pin base_name]
 }
 set allpins [concat $pins(in) $pins(out) $pins(inout)];

 redirect ~/_${cellname}.v {
  echo "module ${cellname}_inst ([join $allpins {,}]);"
  if {$pins(in) != ""} {echo "input [join $pins(in) {,}];"}
  if {$pins(out) != ""} {echo "output [join $pins(out) {,}];"}
  if {$pins(inout) != ""} {echo "inout [join $pins(inout) {,}];"}
  echo "$cellname U ([join $allpins {,}]);"
  echo "endmodule"
 }
 redirect /dev/null {remove_design -all}
 redirect /dev/null {set success [read_verilog ~/_${cellname}.v]}
 if {!$success} {
  echo "Error: read of temporary netlist _${cellname} failed"
  return 0
 }
 file delete _${cellname}.v
 redirect /dev/null {set success [link]}
 if {!$success} {
  echo "Error: link of temporary netlist _${cellname} failed"
  return 0
 }
 set_input_transition $results(-transition) [all_inputs]

 if {[info exists results(-oc_condition)]} {
  set_operating_condition -analysis_type bc_wc $results(-oc_condition)
 } else {
  set results(-oc_condition) ""
 }

 redirect $outfile {
  echo "#         library cell: $fullcellname $from_pin -> $to_pin"
  echo "# operating conditions: $results(-oc_condition)"
  echo "# input pin transition: $results(-transition)"
  foreach load $indices {
   set_load -subtract_pin_load $load [get_nets "*"]
   redirect /dev/null {update_timing}
   set arcs [get_timing_arcs -from U/$from_pin -to U/$to_pin]
   set dmaxr [get_attribute [index_collection [sort_collection $arcs delay_max_rise] 0] delay_max_rise]
   set dmaxf [get_attribute [index_collection [sort_collection $arcs delay_max_rise] 0] delay_max_fall]
   set dminr [get_attribute [index_collection [sort_collection -descending $arcs delay_max_rise] 0] delay_min_rise]
   set dminf [get_attribute [index_collection [sort_collection -descending $arcs delay_max_rise] 0] delay_min_fall]
   set area  [get_attribute $fullcellname area]
   echo $load $dmaxr $dmaxf $dminr $dminf $area
  }
 }

 # now get time and load units
 set tmpfilename "/tmp/[pid]_libinfo.txt"
 redirect $tmpfilename {report_lib -nosplit [get_libs -of $cell]}
 set file [open $tmpfilename]
 set report_lib [read $file]
 close $file
 file delete $tmpfilename
 regexp -linestop -- {Time Unit *: (.*)} $report_lib dummy time_unit
 regexp -linestop -- {Capacitance Unit *: (.*)} $report_lib dummy cap_unit

 # generate datafile
 redirect ${outfile}.gnuplot {
  echo "set data style linespoints"
  echo "set key bottom right"
  echo "set yrange \[0:\]"
  echo "set title  '$fullcellname $from_pin -> $to_pin ($results(-transition) slew on $from_pin)'"
  echo "set label  \"       CELL     $cellname\" right at graph 0.95, graph 0.28 font 'Calibri,12' textcolor lt 8"
  echo "set label  \"AREA (um^2)      [get_attribute [get_lib_cells $fullcellname] area]\" right at graph 0.95, graph 0.24 font 'Courier,10' textcolor lt 3"
  echo "set label  \"ALWAYS_ON         [get_attribute [get_lib_cells $fullcellname] always_on]\" right at graph 0.95, graph 0.21 font 'Courier,10' textcolor lt 3"
  echo "set label  \" DONT_USE         [get_attribute [get_lib_cells $fullcellname] dont_use]\" right at graph 0.95, graph 0.18 font 'Courier,10' textcolor lt 3"
  echo "set label  \" DISABLE_TIMING         [get_attribute [get_lib_cells $fullcellname] disable_timing]\" right at graph 0.95, graph 0.15 font 'Courier,10' textcolor lt 3"
  echo "set xlabel 'output load ($cap_unit)'"
  echo "set ylabel 'cell delay ($time_unit)'"
  echo "set grid"
  switch $results(-delay_type) {
   max {
    echo "plot   '[pwd]/$outfile' using 1:2 title 'delay_max_rise'"
    echo "replot '[pwd]/$outfile' using 1:3 title 'delay_max_fall'"
   }
   min {
    echo "plot   '[pwd]/$outfile' using 1:4 title 'delay_min_rise'"
    echo "replot '[pwd]/$outfile' using 1:5 title 'delay_min_fall'"
   }
  }
 }
 echo "        library cell: $fullcellname $from_pin -> $to_pin"
 echo "operating conditions: $results(-oc_condition)"
 echo "input pin transition: $results(-transition)"
 echo ""
 echo "ON UNIX SHELL, PLOT IT BY RUNNING :"
 echo "\n"
 echo "  plot [pwd]/${outfile}.gnuplot"
 echo "\n"
 echo "\n"
}

 define_proc_attributes libcell_delay_plot \
  -info "plot cell delay versus load" \
  -define_args {\
   {-from_pin "from pin on cell" from_pin_name string required}
   {-to_pin "from pin on cell" to_pin_name string required}
   {-transition "input pin transition (default 0)" input_transition float optional}
   {-delay_type "plot worst-case or best-case curves" type one_of_string {optional value_help {values {min max}}}}
   {-oc_condition "operating condition" condition string optional}
   {-load_indices "list of load indices {slew0 slew1 ...}" "indices" string optional}
   {-num_points "number of points to interpolate per index" "num" int optional}
   {-output "gnuplot output data file" "filename" string optional}
   {cell "library cell, CELL or LIB/CELL" "cell" string required}
  }

proc plot_lcd {lib_cell from_pin to_pin} {
  libcell_delay_plot ${lib_cell} -from_pin ${from_pin} -to_pin ${to_pin}
  file delete ~/_${lib_cell}.v
}
#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#


#------------------------------------------------------------------------------------------------#
#-- machine_stats
#------------------------------------------------------------------------------------------------#
#-- USAGE :
#-- pt_shell> machine_stats -start
#-- Top job started recording top.log every 60 seconds with pid 58347, issue machine_stats -stop to kill it.
#-- pt_shell> update_timing -full ; #or any command in any tool.
#-- pt_shell> machine_stats -stop
#-- Killing earlier top job with pid 58347.

proc machine_stats {args} {

  suppress_message CMD-041
  parse_proc_arguments -args ${args} opt
  if {[info exists opt(-stop)]} {
    if {![info exists ::top_pid]} { return "ERROR!! no prior top job started with machine_stats -start found to stop" }
    echo "Killing earlier top job with pid $::top_pid"
    puts "\n"
    echo "Machine Usage details printed in "
    echo "\t[pwd]/top.log"
    puts "\n"
    sh kill -9 $::top_pid
    unset -nocomplain ::top_pid
    return

  } elseif {[info exists opt(-start)]} {

    if {[info exists opt(-delay)]} { set delay $opt(-delay) } else { set delay 60 }
    if {[info exists opt(-top_log)]} { set log $opt(-top_log) } else { set log "top.log" }

    if {[info exists ::top_pid]} {
      return "ERROR!! top job with pid $::top_pid already in progress, machine_stats -stop it first"
    }
    sh rm -rf $log
    set user [exec whoami]
    set ::top_pid [exec top -b -u $user -d $delay > $log &]
    echo "top job started recording top.log every $delay seconds with pid $::top_pid, issue machine_stats -stop to kill it"
    unsuppress_message CMD-041

  } else {
    return "ERROR!! -start or -stop are required, Exiting"
  }

}

define_proc_attributes machine_stats \
    -info "USER PROC: Use machine_stats -start and machine_stats -stop in scripts to start and stop batch top jobs and record results into top.log file" \
    -define_args {
        {-start "Optional - starts and runs a batch top job until machine_stats -stop is issued to kill it" "" boolean optional}
        {-stop "Optional - stops an earlier batch top job started with machine_stats -start" "" boolean optional}
        {-delay "Optional - refreshes top every given seconds, default is 60sec" "" int optional}
        {-top_log "Optional - output top log file, default top.log" "" string optional}
    }

echo "\tmachine_stats"
#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#


#------------------------------------------------------------------------------------------------#
#-- all_commands
#------------------------------------------------------------------------------------------------#
#-- USAGE :
#-- pt_shell> all_commands * -verbose
proc all_commands {args} {

   parse_proc_arguments -args $args results

   echo "#--   ALL COMMANDS  --#"
   help *$results(pattern)*

   if {[info exists results(-verbose)]} {
      echo "********* -help *************"
      apropos *$results(pattern)*
   }
}; # end proc

define_proc_attributes all_commands -info "lists all commands and their set explanation" \
-define_args {
   {pattern "Pattern to search for" pattern string required}
   {-verbose "Search -help as well" "" boolean optional}}
#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------#
#-- all_variables
#------------------------------------------------------------------------------------------------#
#-- USAGE :
#-- pt_shell> all_variables * -verbose
proc all_variables {args} {

   parse_proc_arguments -args $args results

   echo "#--   ALL VARIABLES  --#"
   uplevel "printvar *$results(pattern)*"

   #-- if {[info exists results(-verbose)]} {
   #--    echo "********* -help *************"
   #--    apropos *$results(pattern)*
   #-- }
}; # end proc

define_proc_attributes all_variables -info "lists all variables and their current values" \
-define_args {
   {pattern "Pattern to search for" pattern string required}
   {-verbose "Search -help as well" "" boolean optional}}
#------------------------------------------------------------------------------------------------#
#-- END
#------------------------------------------------------------------------------------------------#




#------------------------------------------------------------------------------------------------#
#-- get_blocking_arcs
#------------------------------------------------------------------------------------------------#
#-- USAGE :
#-- pt_shell> get_blocking_arcs -from_pin  /CP -to_pin /D
proc intersect {a b} {return [remove_from_collection $a [remove_from_collection $a $b]]}

proc get_blocking_arcs {args} {
 set results(-include_net_arcs) 0
 parse_proc_arguments -args $args results

 if {[set p1 [get_pins $results(-from_pin)]] eq {}} {return 0}
 if {[set p2 [get_pins $results(-to_pin)]] eq {}} {return 0}

 # get intersection pins
 set fanout [all_fanout -flat -from $p1 -trace_arcs all]
 set fanin [all_fanin -flat -to $p2 -trace_arcs all]
 if {[set intersection [intersect $fanin $fanout]] eq {}} {
  echo "Error: no topological path between the pins."
  return 0
 }

 # populate intersection pins into an array for efficient testing
 foreach_in_collection pin $intersection {
  set intersection_pin([get_object_name $pin]) 1
 }

 # determine the timing arc pin pairs between the pins
 foreach_in_collection arc [expr {$results(-include_net_arcs) ? [get_timing_arcs -from $intersection] : [get_timing_arcs -from $intersection -filter {defined(sense) && sense != ""}]}] {
  set from_pin_name [get_object_name [get_attribute $arc from_pin]]
  set to_pin_name [get_object_name [get_attribute $arc to_pin]]
  if {[info exists intersection_pin($to_pin_name)]} {
   set pin_pairs([list $from_pin_name $to_pin_name]) 1
  }
 }

 # now check each pin pair to see if traversal is blocked here
 foreach pair [array names pin_pairs] {
  foreach {from_pin_name to_pin_name} $pair {}
  if {[intersect $p1 [all_fanin -flat -to $from_pin_name]] eq {} && [intersect $p2 [all_fanout -flat -from $to_pin_name]] eq {}} {
   lappend unreachable $pair
  }
  if {[intersect $p2 [all_fanout -flat -from $to_pin_name]] ne {} && [intersect $p2 [all_fanout -flat -from $from_pin_name]] eq {}} {
   lappend fanout_blocked $pair
  }
  if {[intersect $p1 [all_fanin -flat -to $from_pin_name]] ne {} && [intersect $p1 [all_fanin -flat -to $to_pin_name]] eq {}} {
   lappend fanin_blocked $pair
  }
 }

 # print results
 if {[info exists fanin_blocked]} {
  echo "\n"
  echo "FANIN FROM  '[get_object_name $p1]' BLOCKED AT TIMING ARCS :"
  foreach pair $fanin_blocked {
   echo "   [lindex $pair 0]  ->  [lindex $pair 1]"
  }
  echo "\n"
 }
 if {[info exists unreachable]} {
  echo "UNREACHABLE TIMING ARCS :"
  foreach pair $unreachable {
   echo "   [lindex $pair 0]  ->  [lindex $pair 1]"
  }
  echo "\n"
 }
 if {[info exists fanout_blocked]} {
  echo "FANOUT TO  '[get_object_name $p2]' BLOCKED AT TIMING ARCS :"
  foreach pair $fanout_blocked {
   echo "   [lindex $pair 0]  ->  [lindex $pair 1]"
  }
 }
 echo "\n"
}

No comments:

Post a Comment