Thursday, August 31, 2017

Why hold is zero cycle check and independent of frequency?

Let us assume,
FF1 - launch flop (rising edge)
FF2 - capture flop (rising edge)

Always setup and hold checks are w.r.t to capture flop.

Setup Timing check:
           Data (D1) is launched at rising edge1 will be captured at rising edge2 of capture flop.

Here, it should satisfy setup time of FF2 to capture the launched data successfully. Hence, setup timing check is always checked at next clock cycle.
                      Min Clock period (T) > Tcq1+ Tcomb+ Tsu2

Hold Timing Check:
           Data (D1) should be stable for minimum Thold time (Tcq1,min + Tcomb1,min > Thold) so that it doesn't overwrite the data (D2) that is going to be captured by FF2.

Where this check needs to applied:
i) Launched data at edge1 should be captured by capture clk @edge2.
ii) In other words, what ever data launched @edge1 should not be captured @edge1 of capture clock. So this needs to be tested in the same clock edges.That's why it is called "Zero cycle check". 

Mathematically the data should change only after (Tcq,min + Tcomb,min) only. Then only it will not overwrite the capture flop data.

This has to be checked at launch edge (i.e. edge1) of FF1. So, hold timing check is zero cycle check and it doesn't depend on time period of clock  (independent of frequency).

                             Tcq1,min + Tcomb1, min > Thold

The intention is for the data from the launch FF to be captured by the capture FF in the next clock cycle. If the data is captured in the same clock cycle, the intended data in the capture FF (from the prev clock cycle) is overwritten. The hold time check is to ensure that the intended data in the capture FF is not overwritten.

Hold timing check ensures that
Launch FF output value that is changing doesn't pass through to a capture FF and
overwrite its output before the FF has had a chance to capture its original value

So, data being latched should be held stable for a specified amount of time after the
active edge of the clock ==> This min amount of time is referred to as Hold time of FF.

The hold check is carried out on each active edge of the clock of the capture FF.

setup timing check -> data launched by FF1 needs to be captured @FF2
hold timing check  -> to make sure that correct data is not overwritten by subsequent data @FF2

After manufacturing,
    i) Still setup violation is there, operate chip at lower frequency
    ii) But if hold violation is there, design inoperable at any frequency (as it checks minimum delays). So make sure to resolve all hold timing violations.


Wednesday, August 23, 2017

list_attributes

list_attributes -application -class

Class names are listed below:
cell
clock
clock_group
clock_group_group
design
exception
exception_group
input_delay
lib
lib_cell
lib_pin
lib_timing_arc
output_delay
net
pin
port
rule
rule_violaion
ruleset
scenario
timing_arc


##Finding disabled timing arcs of specified cells:

pt_shell> set arcs [get_timing_arcs -of_objects U1 -filter "sense == positive_unate"]
_sel3

pt_shell>  foreach_in_collection arc $arcs {
                    echo [get_attribute $arc is_disabled]
                 }
true
false

##Listing the senses of Timing Arcs:
pt_shell>  set larcs [get_lib_timing_arcs  -from  class/FD1/CP]
_sel5

pt_shell>  foreach_in_collection larc $larcs {
                        echo  [get_attribute $larc sense]
                 }
hold_clk_rise
setup_clk_rise
rising_edge
rising_edge


Reporting of invalid Startpoints or Endpoints

set timing_report_always_use_valid_start_end_points true

When variable is set to true, each reporting command ignores invalid startpoints and invalid endpoints.

Setup and Hold time calculation for Multi Frequency clocks:

How PT find clock edges for same clock freq:
Image result for launch and capture clocks waveforms


For multi-freq clocks:
Which edges need to be considered for Setup and Hold times calculation:
1. Evaluate waveforms over smallest common base period.
2. For each capture edge, find the closest setup launch edge. Call these the primary pairs.

For Setup:
3. Out of the primary pairs, pick the most restrictive setup launch and capture edges.

For Hold:
4. For each primary pair, draw 2 hold relationships: Launch to (Capture-1); (L+1) to Capture. Form all of these hold relationships, pick the most restrictive.

check_timing

Flag ideal clocks in a design:
lappend timing_check_defaults ideal_clocks
check_timing

All subsequently created clocks are propagated by default.
set timing_all_clocks_propagated true
set_propagated_clock [all_clocks]

get_attribute [get_pins  PIN_NAME] clocks
get_attribute [get_pins  PIN_NAME] case_value/constant_value

list_attributes [get_cells CELL_NAME ] -application class 
report_attributes [get_cells  CELL_NAME] -application class

all_fanin -to -flat -trace_arcs all



Tuesday, August 22, 2017

Synchronous Data transfer vs Asynchronous Data Transfer:


  • Synchronous data transfer: sender and receiver use the same clock signal

    • supports high data transfer rate
    • needs clock signal between the sender and the receiver
    • requires master/slave configuration
  • Asynchronous data transfer: sender provides a synchronization signal to the receiver before starting the transfer of each message

    • does not need clock signal between the sender and the receiver
    • slower data transfer rate

Notes:

There are many serial data transfer protocols. The protocols for serial data transfer can be grouped into two types: synchronous and asynchronous. For synchronous data transfer, both the sender and receiver access the data according to the same clock. Therefore, a special line for the clock signal is required. A master (or one of the senders) should provide the clock signal to all the receivers in the synchronous data transfer. 

For asynchronous data transfer, there is no common clock signal between the sender and receivers. Therefore, the sender and the receiver first need to agree on a data transfer speed. This speed usually does not change after the data transfer starts. Both the sender and receiver set up their own internal circuits to make sure that the data accessing is follows that agreement. However, just like some watches run faster than others, computer clocks also differ in accuracy. Although the difference is very small, it can accumulate fast and eventually cause errors in data transfer. This problem is solved by adding synchronization bits at the front, middle or end of the data. Since the synchronization is done periodically, the receiver can correct the clock accumulation error. The synchronization information may be added to every byte of data or to every frame of data. Sending these extra synchronization bits may account for up to 50% data transfer overhead and hence slows down the actual data transfer rate. 

Synchronous vs Asynchronous clocks:

2 clocks are synchronous w.r.t. each other if they share
i) common source
ii) fixed phase relationship

Unless we specify otherwise, PT assumes that 2 clocks are synchronous if there is any path with data
launched by one clock and captured by the other clock.

The clock waveforms are synchronized at time zero, as defined by the create_clock command. For example, consider below 3 commnads.

pt_shell > create_clock -period 4 -name CK1 -waveform {0 2}
pt_shell > create_clock -period 4 -name CK1 -waveform {1 3}
pt_shell > create_clock -period 6 -name CK1 -waveform {2 3}

PT creates the clocks as specified in the commands, with the waveforms synchronized. PT adjusts timing relationship further for any specified or calculated latency or uncertainity.

1. In a design that uses these 3 clocks, there might be paths launched by one clock and captured by         another clock.
2. When such paths exist, to test all possible timing relationships b/w different clock edges, PT internally expands the clocks to the LCM of all synchronous clock periods, thus creating longer period clocks with multiple rising and falling edges.

3. The  LCM of these periods called base period is 12.

4. To analyze the paths that cross the clock domains, PT internally expands the clocks by repeating them over the base period.

For multi-freq clocks:
Which edges need to be considered for Setup and Hold times calculation:
1. Evaluate waveforms over smallest common base period.
2. For each capture edge, find the closest setup launch edge. Call these the primary pairs.

For Setup:
3. Out of the primary pairs, pick the most restrictive setup launch and capture edges.

For Hold:
4. For each primary pair, draw 2 hold relationships: Launch to (Capture-1); (L+1) to Capture. Form all of these hold relationships, pick the most restrictive.

Asynchronous clocks:
Two clocks are asynchronous if they don't communicate with each other in the design.

When 2 clocks are defined as asynchronous, PT doesn't check the timing paths launched by one clock and captured by the other clock, which is like declaring a false path b/w the 2 clocks.

In addition, if you are doing xtalk analysis, PT-SI assigns infinite arrival windows to the nets in aggressor-victim relationships b/w the 2 clocks domains.


###############
To specify in PrimeTime:

#clk1 and clk2 are asynchronous:
set_false_path -from [get_clocks clk1] -to [get_clocks clk2]
set_false_path -from [get_clocks clk2] -to [get_clocks clk1]

# A preferable method, which is portable to PTSI
set_clock_groups -asynchronous -group clk1 -group clk2

##Single Analysis with multiple clocks:
#  Specify exclusive clock groups:
set_clock_groups -logically_exclusive -group clk1 -group clk2
set_clock_groups -logically_exclusive -group clk3 -group clk4

# selecting clk groups with SEL signal and specifying logically exclusive clock groups
set_clock_groups -name SEL -logically_exclusive \
          -group "clk1 clk3" \
          -group "clk2 clk4"

# Defining CLK1 has a range of possible duty cycles -> 40/60 to 60/40
create_clock -name CLK1a -period 10 -w {0 4} [get_ports CLK1]
create_clock -name CLK1b -period 10 -w {0 6} [get_ports CLK1] -add

set_clock_groups -exclusive -group CLK1a -group  CLK1b
set_clock_groups -exclusive -group "CLK1a CLK1b CLK3" -group "clk2 clk4"



Synchronous counter vs Asynchronous counter

Asynchronous/Ripple Counter
  • Flip flops are connected in such a way that the o/p of first flip-flop drives the clock of next flip-flop.
  • Flip-flops are not clocked simultaneously.
  • Circuit is simple for more number of states.
  • Speed is slow as clock is propagated through number of stages.
  • Asynchronous Counter are also known as “Ripple Counter” because of the way the clock pulses or ripples, its way through the flip-flop.
Synchronous Counter
  • There is no connection between o/p of first flip-flop and clock of next flip-flop.
  • Flip-flops are clocked simultaneously.
  • Circuit becomes complicated as number of states increases.
  • Speed is high as clock is given at a same time.
Source :

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