PrimeTime Min Delay (Hold Violation) Fix

To using this script, we should using script PrimeTime Report to generate reports.

#!  /tools/cfr/bin/python
 
import os
import sys
import re
import time
from collections import defaultdict   
 
def get_all_hold_rpt():
  modes =  'func mbist bisr scan merged'.split(' ')
  corners = 'max maxlt min minht maxrc minrc'.split(' ')
  content = ""
  print "\nReading hold timing reports:" 
  for mode in modes:
    for corner in corners:
      ddir = "postlayout_%s_%s" % (mode, corner)
      file_name = "hfbi_%s_%s_hold.rpt" %  (mode, corner)
      file_name = "zgdh_timing_hold_reg2reg_startend.rpt"
      vio_file = os.path.join(ddir, file_name)
      if os.path.exists(vio_file):
        print "  Reading File: %s" % vio_file
        content += open(os.path.join(ddir, file_name)).read()
      #break
    #break
  return content 
 
def get_all_violators():
  modes =  'func mbist bisr scan merged'.split(' ')
  corners = 'max maxlt min minht maxrc minrc'.split(' ')
  content = ""
  for mode in modes:
    for corner in corners:
      ddir = "postlayout_%s_%s" % (mode, corner)
      file_name = "uld_eti_%s_%s_all_violators.rpt" %  (mode, corner)
      content += open(os.path.join(ddir, file_name)).read()
  return content 
 
def get_hold_vio_dict(all_violators):
  patt = """min_delay/hold \('reg2reg' group\)\s+Endpoint.*?\n\n"""
  all_hold_vio = re.findall(patt, all_violators, re.DOTALL)
 
  hold_endpoints_slack_dic = defaultdict(list)
  for line in "\n".join(all_hold_vio).split("\n"):
    line = line.strip()
    if line.find("(VIOLATED") >= 0:
      fields = re.split("\s+", line)
      endpoint = fields[0]
      hold_endpoints_slack_dic[endpoint].append(fields[1])
  return hold_endpoints_slack_dic
 
def get_slack_from_path(timing_path):
  patt="""slack \(VIOLATED\)\s+(-?[\d\.]+)"""
  slacks = re.findall(patt, timing_path, re.DOTALL)
  slack = None
  if slacks:
    slack = float(slacks[0])
  return slack
 
def remove_hier_net_pin(timing_rpt):
  new_rpt = ""
  lines = timing_rpt.split("\n")
  for line in lines:
    line_tmp = line.strip()
    if line_tmp.endswith("(net)"):
      # remove hier nets
      continue
    else:
      fields = line_tmp.split()
      if len(fields) == 7 and fields[2] == '0.00000' and fields[3] == '0.00000':
        # remove hier pins
        #print fields
        continue
    new_rpt += "%s\n" % line
  return new_rpt
 
 
def get_hold_fix_pin(timing_path):
  point_patt = """(\S+) \((\S+)\)\s+([\d\.]+)"""
 
  data_arrival_index = timing_path.find('data arrival time')
  data_path = timing_path[:data_arrival_index]
  all_points = re.findall(point_patt, data_path, re.DOTALL)
  all_points.reverse()
  hold_fix_pin = ""
  for index, point_type_fanout in enumerate(all_points):
    #print point_type_fanout
    point, type, fanout = point_type_fanout
    if type == 'net':
      if fanout.find('.') == -1:
        if int(fanout) > 1:
          hold_fix_pin = all_points[index-1][0]
          break
        elif int(fanout) == 1:
          hold_fix_pin = all_points[index-1][0]
  #print hold_fix_pin
  return hold_fix_pin
 
def get_hold_fix_pin_slacks(timing_path_rpt):
  patt = """Startpoint.*?-+\n\s+clock.*?slack \(VIOLATED\)\s+-[\d\.]+"""
  timing_paths = re.findall(patt, timing_path_rpt, re.DOTALL)
  count = 0;
  path_num = len(timing_paths)
  hold_fix_pin_slacks = defaultdict(list)
  print "Total %d timing paths to fix." % path_num
  for timing_path in timing_paths:
    hold_fix_pin = get_hold_fix_pin(timing_path)
    hold_slack   = get_slack_from_path(timing_path)
    #print '\n',hold_fix_pin, hold_slack
    hold_fix_pin_slacks[hold_fix_pin].append( hold_slack )
    count += 1
    if count % 500 == 0:
      print "Process timing path %d in %d, %s" % ( count, path_num, time.strftime("%a, %d %b %Y %H:%M:%S")  )
      #print "Process timing path %d " % ( count)
    #break
  return hold_fix_pin_slacks
 
def gen_hold_fix_cmd (hold_fix_pin_slacks):
  #buf_030 = "g65lxp/BUFM3LXP"
  #buf_050 = "g65xpipo/HDEL100XP"
  #buf_120 = "g65xpipo/HDEL200XP"
  hold_fix_cmd = """
# lib file:
#   /lsi/soft/lsi/fs/5.0/lsi_fs_5.0/lib3p/liberty/g65/ccs/g65lxp_lsiB_105V_125C.lib
#   /lsi/soft/lsi/fs/5.0/lsi_fs_5.0/lib3p/liberty/g65/ccs/g65xpipo_lsiB_105V_125C.lib
set buf_030 {g65lxp/BUFM3LXP}     ;#  0.0475800,  0.0445400
set buf_070 {g65xpipo/HDEL100XP}  ;#  0.0731400,  0.0748000
set buf_170 {g65xpipo/HDEL200XP}  ;#  0.176280,   0.182230
 
####;initial list
set hold_fix_pin_0 {}; # ~ 0.01      
set hold_fix_pin_03 {}; # 0.01 ~ 0.03 
set hold_fix_pin_05 {}; # 0.03 ~ 0.05 
set hold_fix_pin_07 {}; # 0.05 ~ 0.07 
set hold_fix_pin_09 {}; # 0.07 ~ 0.09 
set hold_fix_pin_12 {}; # 0.09 ~ 0.12 
set hold_fix_pin_14 {}; # 0.12 ~ 0.14 
set hold_fix_pin_17 {}; # 0.14 ~ 0.18 
set hold_fix_pin_20 {}; # > 0.18      
set hold_fix_pin_9 {}; #
 
"""
 
  for pin, slacks in hold_fix_pin_slacks.items():
    slacks.sort()
    wslack = -slacks[0]
    if wslack < 0.01:
      #hold_fix_cmd += "# lappend hold_fix_pin_0 %s\n" % pin
      pass
    elif wslack < 0.03:
      hold_fix_cmd += "lappend hold_fix_pin_03 %s\n" % pin
    elif wslack < 0.05:
      hold_fix_cmd += "lappend hold_fix_pin_05 %s\n" % pin
    elif wslack < 0.07:
      hold_fix_cmd += "lappend hold_fix_pin_07 %s\n" % pin
    elif wslack < 0.09:
      hold_fix_cmd += "lappend hold_fix_pin_09 %s\n" % pin
    elif wslack < 0.12:
      hold_fix_cmd += "lappend hold_fix_pin_12 %s\n" % pin
    elif wslack < 0.14:
      hold_fix_cmd += "lappend hold_fix_pin_14 %s\n" % pin
    elif wslack < 0.17:
      hold_fix_cmd += "lappend hold_fix_pin_17 %s\n" % pin
    else: #
      hold_fix_cmd += "lappend hold_fix_pin_20 %s\n" % pin
    #print slacks[0], slacks[-1], len(slacks), pin
 
  hold_fix_cmd += "\n\n\n"
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_03 $buf_030\n"  
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_05 $buf_070\n"  
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_07 $buf_070\n"  
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_09 $buf_170\n"  
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_12 $buf_170\n"  
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_14 $buf_170\n"  
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_17 $buf_170\n"  
  hold_fix_cmd += "insert_buffer  $hold_fix_pin_20 $buf_170\n"  
 
  return hold_fix_cmd
 
 
if __name__ == "__main__":
 
  all_hold_rpt_file = "all_hold_timing_path.rpt"
  if len(sys.argv) == 2:
    all_hold_rpt = open(sys.argv[1]).read()
  elif len(sys.argv) == 1:
    all_hold_rpt = get_all_hold_rpt()
    print "\nSave all hold timing report to file: %s" % all_hold_rpt_file
    open(all_hold_rpt_file, 'w').write(all_hold_rpt)
 
  print "Remove hier nets & pins in timing report"
  all_hold_rpt = remove_hier_net_pin(all_hold_rpt)
 
  #print "Save refined timing report"
  #open(all_hold_rpt_file+'.refine', 'w').write(all_hold_rpt)
 
  print "Get hold fix pins and slacks in timing rpt"
  hold_fix_pin_slacks = get_hold_fix_pin_slacks(all_hold_rpt)
 
  print "Write hold slacks & fix pin data"
  contents = []
  for pin, slacks in hold_fix_pin_slacks.items():
    slacks.sort()
    line = "%.5f, %2d, %s" % (slacks[0], len(slacks), pin)
    contents.append(line)
  contents.sort()
  contents[0:0] = ["hold-slack, path-number, pin"]
  open('all_hold_fix_pins.rpt','w').write("\n".join(contents))
 
  fix_cmd = gen_hold_fix_cmd(hold_fix_pin_slacks)
  open('hold_fix.tcl','w').write(fix_cmd)