
import os
import re

import Cfg
from   Hurricane import *
from   helpers   import ErrorMessage
import CRL
#import Mauka

#import Knik
#import Kite
#import Zephyr
#import Lightning
#import Zephiros

#from Anemometer import *
#from Metisse    import *

#from Mistral    import *
#from Nimbus     import *

from math import *


missingCORIOLIS_TOP = "Missing environment variable CORIOLIS_TOP"

global POWER, OCCUPIED, FREE
POWER    = 2
OCCUPIED = 1
FREE     = 0

global PITCH, SLICE
PITCH = 5
SLICE = 50

global RING_WIDTH
RING_WIDTH = 12

global RING_INTERVAL
RING_INTERVAL =  3

global standard_instances_masque

global standard_instances_list
standard_instances_list = []

global pad_north, pad_south, pad_east, pad_west
pad_north = []
pad_south = []
pad_east  = []
pad_west  = []

global nb_alims_verticales
nb_alims_verticales = 0

global nb_vdd_pins, nb_vss_pins
nb_vdd_pins = 0
nb_vss_pins = 0

global ck_contact_list_to_create
ck_contact_list_to_create = []


def getNetFromPlug ( plug ):
  net = plug.getNet()
  if net: return net

  masterNet = plug.getMasterNet()
  if masterNet.getName() in [ 'vddi', 'vssi', 'vdde', 'vsse' ]:
    cell = plug.getCell()
    net  = cell.getNet(masterNet.getName())

  return net

##################
## PlaceCentric ##
##################
def pyPlaceCentric ( cell, instance ) :
  '''This function places an instance in the middle of the abutment box of a cell'''
  
  UpdateSession.open()
  
  if not instance :
    raise ErrorMessage(2,"PlaceCentric: the instance does not exist.")

  w = cell.getAbutmentBox().getWidth()
  h = cell.getAbutmentBox().getHeight()
  
  if ( w < instance.getAbutmentBox().getWidth() ) or ( h < instance.getAbutmentBox().getHeight() ) : 
    raise ErrorMessage(2,"PlaceCentric : the instance's size is greater than this model.")

  XCenter  = cell.getAbutmentBox().getXCenter()
  YCenter  = cell.getAbutmentBox().getYCenter()
    
  New_Xmin = XCenter - instance.getAbutmentBox().getWidth()  / 2
  New_Ymin = YCenter - instance.getAbutmentBox().getHeight() / 2

  # One must place the cell on a pitch grid
  New_Xmin = New_Xmin - ( New_Xmin % DbU_lambda(PITCH) )
  New_Ymin = New_Ymin - ( New_Ymin % DbU_lambda(PITCH) )
             
  Tx       = New_Xmin - instance.getAbutmentBox().getXMin() 
  Ty       = New_Ymin - instance.getAbutmentBox().getYMin() 
  
  instance.setTransformation ( Transformation ( Tx, Ty ).getTransformation ( instance.getTransformation() ) )  
  instance.setPlacementStatus ( Instance.PlacementStatus.PLACED )
    
  UpdateSession.close()
    
#######################
##     PlaceGlue     ##
#######################
def pyPlaceGlue ( cell ) :
  '''This function places the glue (unplaced leaf instances) in current cell.
     The placement will be made in the region given by the core argument:
       - if core is None, the placement region will be the cell abox.
       - if core is a cell sub-instance, the placement region will be the sub-instance abox.'''    

  mauka = Mauka.MaukaEngine.create ( cell )
  mauka.run ()
  temporarySave ()

       
#############
## RouteCk ##
#############
def pyRouteCk ( cell, netCk ) :
  '''This function route signel Ck to standard cells'''

  global pad_north, pad_south, pad_east, pad_west

  UpdateSession.open()
   
  # Error if Pads have not already been placed
  if pad_north == [] : raise ErrorMessage(2,"RouteCk : Pads in the north haven't been placed.")
  if pad_south == [] : raise ErrorMessage(2,"RouteCk : Pads in the south haven't been placed.")
  if pad_east  == [] : raise ErrorMessage(2,"RouteCk : Pads in the east haven't been placed.")
  if pad_west  == [] : raise ErrorMessage(2,"RouteCk : Pads in the west haven't been placed.")
 
  # pad_list contains all pads
  pad_list = []
  pad_list.extend ( pad_north ) 
  pad_list.extend ( pad_south ) 
  pad_list.extend ( pad_east  ) 
  pad_list.extend ( pad_west  )
    
  net_list = dict()

  for ins in pad_list :
    # For each pad that is a clock pad -> adding corresponding net in net_list if not already existing
    if isInternalClockPad ( ins ) or isExternalClockPad ( ins ):
      clockPlug = None
      for plug in ins.getPlugs():
        if plug.getNet() == netCk :
          clockPlug = plug
          break

      if clockPlug and not net_list.has_key ( str ( clockPlug.getNet().getName() ) ) : net_list[str ( clockPlug.getNet().getName() )] = ( clockPlug.getNet(), cell ) 
  
  map ( createGrid, net_list.values() )
     
  UpdateSession.close()

######################
## AlimVerticalRail ##
######################
def pyAlimVerticalRail ( cell, xcoord ) :
  '''x is in pitch, it is where the vertical alimentation call back are placed'''

  global PITCH, SLICE
  global standard_instances_list, nb_alims_verticales, nb_lignes, standard_instances_masque
  global nb_vdd_pins, nb_vss_pins
  
  UpdateSession.open()
  
  box = cell.getAbutmentBox()  
   
  # Error message if wrong abutment box 
  if ( box.getXMin() == box.getXMax() ) or ( box.getYMin() == box.getYMax() ) :
    message = "AlimVerticalRail : Can't place the rail vertical in the abutment box of model %s." %str(cell.getName()) \
        + "     The abutment box doesn't exist !" \
        + "     Maybe you should use DefAb function or ResizeAb function to define un box before the placement of Rail Vertical."
    raise ErrorMessage(2,message)

  # Check the value of x
  nb_col = cell.getAbutmentBox().getWidth() / DbU_lambda(PITCH)
  if ( xcoord >=  nb_col ) or ( xcoord < 0 ) : 
    print 'This is it'
    message = "AlimVerticalRail : Illegal argument x , x must be between %d and %d\n" % ( 0, nb_col )  
    raise ErrorMessage(2,message)

  # To get the informations about the placement
  reference = getStandardInstancesMasque ( cell )
  
  if not reference : 
    message = "AlimVerticalRail : No instance is placed in the model " + str(cell.getName()) + ".\n" \
        + "Please place some instances before the placement of Rail Vertical.\n"
    raise ErrorMessage(2,message)
  
  # Placement verification
  #verifyPlacement ( cell )  # NON !!
  # delete elements in standard_instances_list
  del standard_instances_list[0:]
  # get YMin of reference
  Ymin = reference[1]
  # get orientation code
  orientation = reference[2]

  nb_alims_verticales += 1
  
  # get the opposite of orientation ID=>MY MY=>ID
  if orientation in [Transformation.Orientation.ID, Transformation.Orientation.MX] :
    inv_orientation = Transformation.Orientation.MY
    orientation     = Transformation.Orientation.ID
  elif orientation in [Transformation.Orientation.MY, Transformation.Orientation.R2] :
    inv_orientation = Transformation.Orientation.ID     
    orientation     = Transformation.Orientation.MY
  else :
    raise ErrorMessage(2,"AlimVerticalRail : Strawberry.")
    
#  print "Placement of vertical rail" 
#  print "Reference  ", reference
  
  power_cell = CRL.AllianceFramework.get().getCell ( "powmid_x0", CRL.Catalog.State.Views )
  
  Height_power = power_cell.getAbutmentBox().getHeight()
  Width_power  = power_cell.getAbutmentBox().getWidth()
    
  nb_slices    = Height_power / DbU_lambda(SLICE)
  nb_pitchs    = Width_power  / DbU_lambda(PITCH)
 
  # skim through column of index x
  for i in range ( nb_lignes ) :
    espace_enough = True
    
    # check the space left
    for j in range ( nb_slices ) :
      for k in range ( nb_pitchs ) :  
        if standard_instances_masque[i + j][xcoord + k] != FREE  :  
          espace_enough = False 
          break 
          
    if espace_enough :
      # avoir le nombre de slices entre la reference
      distance = Ymin / DbU_lambda(SLICE) - i 
      
      if distance < 0 : distance = -distance 
      
      name_inst = "powmid_%d_%d" % ( nb_alims_verticales , i )
      Powmid = create_inst ( "powmid_x0", name_inst, cell )
      x = xcoord * DbU_lambda(PITCH)
      y = i      * DbU_lambda(SLICE)
      if distance % 2 : my_orientation = inv_orientation
      else            : my_orientation = orientation
      place ( Powmid, int(x), int(y), my_orientation )
  
  # place pins (connectors) in metal3 on top and bottom 
  metal3 = DataBase.getDB().getTechnology().getLayer( "METAL3" )

  powerNet = None
  for net in cell.getPowerNets():
    if powerNet:
      raise ErrorMessage(2,"AlimVerticalRail : more than 1 Power Net found !")
    powerNet = net
  if not powerNet:
    raise ErrorMessage(2,"AlimVerticalRail : no Power Net found !")

  groundNet = None
  for net in cell.getGroundNets():
    if groundNet:
      raise ErrorMessage(2,"AlimVerticalRail : more than 1 Ground Net found !")
    groundNet = net
  if not groundNet:
    raise ErrorMessage(2,"AlimVerticalRail : no Ground Net found !")

  pin_height = 10
  pin_width  = 10
  pin_x1     = xcoord * DbU_lambda(PITCH) + DbU_lambda(10)
  pin_x2     = xcoord * DbU_lambda(PITCH) + DbU_lambda(25)
  pin_y1     = ( nb_lignes * DbU_lambda(SLICE) ) - DbU_lambda(pin_height/4)
  pin_y2     = 0 + DbU_lambda(pin_height/4)
    
  # Top
  if standard_instances_masque[nb_lignes-1][xcoord] == FREE :
    pin_name       = str(powerNet.getName()) + "." + str(nb_vdd_pins)
    pin            = Pin.create ( powerNet, pin_name, 1, 2, metal3, pin_x1, pin_y1, DbU_lambda(pin_width), DbU_lambda(pin_height) )
    nb_vdd_pins   += 1 
  
    pin_name       = str(groundNet.getName()) + "." + str(nb_vss_pins)
    pin            = Pin.create ( groundNet, pin_name, 1, 2, metal3, pin_x2, pin_y1, DbU_lambda(pin_width), DbU_lambda(pin_height) )
    nb_vss_pins   += 1 
  
  # Bottom
  if standard_instances_masque[0][xcoord] == FREE :          
    pin_name       = str(powerNet.getName()) + "." + str(nb_vdd_pins)
    pin            = Pin.create ( powerNet, pin_name, 2 , 2, metal3, pin_x1, pin_y2, DbU_lambda(pin_width), DbU_lambda(pin_height) )
    nb_vdd_pins   += 1 
  
    pin_name       = str(groundNet.getName()) + "." + str(nb_vss_pins)
    pin            = Pin.create ( groundNet, pin_name, 2 , 2, metal3, pin_x2, pin_y2, DbU_lambda(pin_width), DbU_lambda(pin_height) )
    nb_vss_pins   += 1

  UpdateSession.close()    

########################
## AlimHorizontalRail ##
########################
def pyAlimHorizontalRail ( cell, ycoord ) :
  '''This function places the horizontal alimentation call back in Alu 4 
  1. the two pins in Alu 4 which name is VDD or VSS are placed
  2. the horizontal in Alu 4 is placed, and is locked on the two pins 
  y is in slice''' 

  global PITCH, SLICE, RING_WIDTH
  global standard_instances_list, nb_alims_verticales, nb_lignes, standard_instances_masque
  global nb_vdd_pins, nb_vss_pins  
  
  UpdateSession.open()
  
  box = cell.getAbutmentBox()
    
  # Error message if wrong abutment box
  if ( box.getXMin() == box.getXMax() ) or ( box.getYMin() == box.getYMax() ) :
    err = "\nAlimHorizontalRail :  Can't place the rail horizontal in the abutment box of model " + str(cell.getName()) \
        + "\n     The abutment box doesn't exist !" \
        + "\n     Maybe you should use DefAb function or ResizeAb function to define un box before the placement of Rail Horizontal.\n"
    raise ErrorMessage(2,err)

  # Check the value of y
  if ( ycoord >= nb_lignes ) or ( ycoord < 0 )  : 
    err = "\nAlimHorizontalRail : Illegal argument y, y must be between %d and %d\n" % ( 0, nb_lignes )
    raise ErrorMessage(2,err)

  # To get the informations about the placement
  reference = getStandardInstancesMasque ( cell )    

  if not reference : 
    err = "AlimHorizontalRail : No instance is placed in the model " + str(cell.getName()) + ".\n"
    raise ErrorMessage(2,err)
  
  # get layers     
  metal4 = DataBase.getDB().getTechnology().getLayer ( "METAL4" )
  via1 = DataBase.getDB().getTechnology().getLayer ( "VIA12" )
  via2 = DataBase.getDB().getTechnology().getLayer ( "VIA23" )
  via3 = DataBase.getDB().getTechnology().getLayer ( "VIA34" )
  
  # Know if it is vdd or vss 
  string = getVddVss ( cell, ycoord )  
  
  pin_width  = DbU_lambda(RING_WIDTH)
  pin_height = DbU_lambda(RING_WIDTH)
  pin_x1     = 0 + ( pin_height / 4 )
  pin_x2     = cell.getAbutmentBox().getWidth() - ( pin_height / 4 )
  pin_y      = ycoord * DbU_lambda(SLICE)
  
  # Create two pins
  if string == "vdd" :
    net = cell.getNet ( "vdd" )   
    
    pin_name      = str(net.getName()) + "." + str(nb_vdd_pins)
    pin1          = Pin.create ( net, pin_name, 4, 2, metal4, pin_x1, pin_y, pin_width, pin_height )
    nb_vdd_pins  += 1 
    
    pin_name      = str(net.getName()) + "." + str(nb_vdd_pins)
    pin2          = Pin.create ( net, pin_name, 3, 2, metal4, pin_x2, pin_y, pin_width, pin_height ) 
    nb_vdd_pins  += 1 
  
  else : 
    net = cell.getNet ( "vss" )   

    pin_name      = str(net.getName()) + "." + str(nb_vss_pins)
    pin1          = Pin.create ( net, pin_name, 4, 2, metal4, pin_x1, pin_y, pin_width, pin_height )
    nb_vss_pins  += 1 
    
    pin_name      = str(net.getName()) + "." + str(nb_vss_pins)
    pin2          = Pin.create ( net, pin_name, 3, 2, metal4, pin_x2, pin_y, pin_width, pin_height ) 
    nb_vss_pins  += 1 
  
  # Create horizontal rail 
  rail_horizontal = Horizontal.create ( pin1 , pin2 , metal4 , pin_y, pin_height, 0 , 0 )
   
  power_hur_cell  = CRL.AllianceFramework.get().getCell ( "powmid_x0", CRL.Catalog.State.Views )
  Width_power     = power_hur_cell.getAbutmentBox().getWidth()  
  nb_pitchs       = Width_power / DbU_lambda(PITCH)       # avoir l'interval du cellule power  
  
  # create contacts in via1 , via2 , et via3
  i = 0  
  while i < nb_colonnes :
   # if it is a power cell, make the via3, and pass the power cell     
   if standard_instances_masque[ycoord][i] == POWER :
     contact_side = DbU_lambda(RING_WIDTH) - DbU_lambda(1.0)
      
     if string == "vdd" : contact3 = Contact.create ( rail_horizontal, via3, i*DbU_lambda(PITCH) + DbU_lambda(10), 0, contact_side, DbU_lambda(2) ) 
     else               : contact3 = Contact.create ( rail_horizontal, via3, i*DbU_lambda(PITCH) + DbU_lambda(25), 0, contact_side, DbU_lambda(2) ) 
     i += nb_pitchs
  
   i+=1  
   
  UpdateSession.close()

####################
## AlimConnectors ##
####################
def pyAlimConnectors ( cell ) :
  '''Creates the METAL1 connectors on the east & west sides of the core''' 

  global nb_lignes, nb_vdd_pins, nb_vss_pins

  UpdateSession.open()
  
  box = cell.getAbutmentBox()

  # Error message if wrong abutment box    
  if ( box.getXMin() == box.getXMax() ) or ( box.getYMin() == box.getYMax() ) :  
    err = "\nAlimConnectors : can't place connectors. The abutment box don't exist.\n" \
        + "The abutment box don't exist !\n" \
        + "Maybe you should use DefAb function or ResizeAb function to define un box before the placement of alim connectors.\n"
    raise ErrorMessage(2,err)
    
  # avoir les infos actuelles de placement 
  getStandardInstancesMasque ( cell ) 

  if not reference : 
    err = "\nAlimConnectors : no instance placed in the model " + str(cell.getName()) + "." \
        + "Please place some instances before the placement of alim connectors.\n"
    raise ErrorMessage(2,err)
    
  metal1 = DataBase.getDB().getTechnology().getLayer("METAL1")
  string = getVddVss ( cell, 0 )
     
  if re.search ( "vdd", string ) :
    inv_string     = "vss"
    nb_string_pins = nb_vdd_pins
    nb_inv_pins    = nb_vss_pins
  else :
    inv_string  = "vdd"
    nb_string_pins = nb_vss_pins
    nb_inv_pins    = nb_vdd_pins
  
  netPair = cell.getNet ( string )
  if not netPair:
    raise ErrorMessage(2,"AlimConnectors : can't get net %s."%string)
  
  netImpair = cell.getNet ( inv_string )
  if not netImpair:
    raise ErrorMessage(2,"AlimConnectors : can't get net %s.\n"%inv_string)
    
  for i in range ( nb_lignes + 1 ) :
    pin_width  = DbU_lambda(12)
    pin_height = DbU_lambda(12)
    pin_y      = i * DbU_lambda(SLICE)

    if i == 0 :
      pin_height = pin_height / 2
      pin_width  = pin_width / 2
      pin_y      = pin_y + ( pin_height / 2 )
    elif i == nb_lignes :
      pin_height = pin_height / 2
      pin_width  = pin_width / 2
      pin_y      = pin_y - ( pin_height / 2 )

    pin_x1     = 0 + ( pin_width / 2 )
    pin_x2     = cell.getAbutmentBox().getWidth() - ( pin_width / 2 )

    # Ligne impaire
    if i % 2 :                              
      pin_name       = str(netImpair.getName()) + "." + str(nb_inv_pins)
      pin1           = Pin.create ( netImpair, pin_name, 4, 2, metal1, pin_x1, pin_y, pin_width, pin_height ) 
      nb_inv_pins   += 1 

      pin_name       = str(netImpair.getName()) + "." + str(nb_inv_pins)
      pin2           = Pin.create ( netImpair, pin_name, 3, 2, metal1, pin_x2, pin_y, pin_width, pin_height ) 
      nb_inv_pins   += 1
      Horizontal.create ( pin1, pin2, metal1, pin1.getY(), pin_height )
    # Ligne paire   
    else :
      pin_name        = str(netPair.getName()) + "." + str(nb_string_pins)
      pin1            = Pin.create ( netPair, pin_name, 4, 2, metal1, pin_x1, pin_y, pin_width, pin_height ) 
      nb_string_pins += 1 

      pin_name        = str(netPair.getName()) + "." + str(nb_string_pins)
      pin2            = Pin.create ( netPair, pin_name, 3, 2, metal1, pin_x2, pin_y, pin_width, pin_height )
      nb_string_pins += 1
      Horizontal.create ( pin1, pin2, metal1, pin1.getY(), pin_height )
      
  if re.search ( "vdd", string ) :
    nb_vdd_pins = nb_string_pins
    nb_vss_pins = nb_inv_pins
  else :
    nb_vss_pins = nb_string_pins
    nb_vdd_pins = nb_inv_pins

  UpdateSession.close()

#######################
##    GlobalRoute    ##
#######################
#def pyGlobalRoute ( cell ):
#    """Perform global routing"""
#
#  # Initialize Nimbus Grid (50x50 lambdas).
#    Kite.setupNimbusGrid ( cell )    # ca a pas l'air de faire ce qu'il faut
#
#    # la il faudrait rajouter un petit appel a cell->FlattenNets() pour que ce soit correct !
#
#   # Global routing.
#    globalRouter = Knik.getKnik ( cell
#                                , 1      # congestion.
#                                , 2      # preCongestion.
#                                , False  # benchMode.
#                                , True   # useSegments.
#                                )
#    globalRouter.Route ()
#    globalRouter.Delete ()

#######################
##    DetailRoute    ##
#######################
#def pyDetailRoute ( cell ):
#    """Perform detailed routing"""
#
#    viewer = getCEditor ( cell )
#
#    TOOL_TOP = os.getenv ( "CORIOLIS_TOP" )
#    if not TOOL_TOP:
#        raise missingCORIOLIS_TOP
#    config = TOOL_TOP + "/share/etc/kite.conf.xml"
#
#    Kite.setDemoMode ( True )
#    Kite.ConfigurationCreate ( config )
#    detailedRouter = Kite.Create ( cell, viewer )
#    detailedRouter.setGlobalThreshold ( 9*DbU_lambda(50.0) )
#    detailedRouter.setRipupLimit      ( 10 )
#    detailedRouter.LoadGlobalRouting  ( Kite.LOAD_GR_BY_NET )
#    if viewer: viewer.Refresh ()
#
#    detailedRouter.LayerAssign ( Kite.LAYER_ASSIGN_BY_TRUNK )
#    if viewer: viewer.Refresh ()
#
#    detailedRouter.ComputeNetConstraints ()
#    if viewer: viewer.Refresh ()
#
#    detailedRouter.ComputeNetOptimals ()
#    if viewer: viewer.Refresh ()
#
#    detailedRouter.RunNegociate ()
#    if viewer: viewer.Refresh ()
#
#   # Save detailed routing.
#    detailedRouter.Delete ()
#    if viewer: viewer.Refresh ()
#
#    return

#def pyTimingStaticAnalysis ( cell ):
#    """Perfom timing static analysis"""
#
#    TOOL_TOP = os.getenv ( "CORIOLIS_TOP")
#    if not TOOL_TOP:
#        raise missingCORIOLIS_TOP
#    config = TOOL_TOP + "/share/cells/sxlib/sxlib_timing.lib"
#
#    LibertyParse ( config )
#
#    lightning = Lightning.getLightning ( cell )
#    timingStaticAnalysis = Zephyr.getZephyr ( cell )
#    #zephiros = Zephiros.getZephiros ( cell )
#
#    timingStaticAnalysis.Update()
#    #zephiros.PrintMaxDelay()
#    
#    return
    


##############
## PadNorth ##
##############
def pyPadNorth ( cell, core, args ) :
  '''This function places pads up north of the cell. It leaves an empty squarre of CARRE * CARRE, CARRE is defined in macro'''

  global pad_north

  # Sauvegarder la liste des plots
  pad_north = args

  UpdateSession.open()
  
  # avoir le largeur de l espace au nord pour placer les pads 
  north_espace_width    = cell.getAbutmentBox().getYMax() - core.getAbutmentBox().getYMax()
  north_espace_longueur = cell.getAbutmentBox().getWidth() - (2 * getPadHeight ( cell ) )     # FIXME D2 

  # avoir le nombre de pads 
  nb_pads = len ( args )
  
  # avoir le largeur total des pads 
  largeur = 0  
  for ins in args : 
    if ins.getPlacementStatus() :
      raise ErrorMessage(2,"PadNorth : the instance %s is already placed."%str(ins.getName()))

    if ins.getAbutmentBox().getHeight() >= north_espace_width :
      raise ErrorMessage(2,"PadNorth : not enough space for all pads.")
  
    largeur += ins.getAbutmentBox().getWidth() 
  
  if largeur > north_espace_longueur :
    raise ErrorMessage(2,"PadNorth : not enough space for all pads.")
  
  # calculer l interval entre les pads 
  interval = ( north_espace_longueur - largeur ) / ( nb_pads + 1 )
  
  # placer les pads  
  Xmin_cell   = cell.getAbutmentBox().getXMin()
  Ymin_cell   = cell.getAbutmentBox().getYMin()
  Height_cell = cell.getAbutmentBox().getHeight()
  
  x_init = Xmin_cell + getPadHeight ( cell ) + interval 
  
  ##### Parcours des pads #####
  for ins in args :
    pad_height = ins.getAbutmentBox().getHeight()
    pad_width  = ins.getAbutmentBox().getWidth()
  
    _y = Ymin_cell + Height_cell - pad_height # la position finale dans la cell
    _x = x_init 

    # ATTENTION LES PLOTS DOIVENT ETRE PLACES SUR UNE GRILLE DE 5 LAMBDAS !!!!!!!!!!!!!!
    _x = _x - ( _x % DbU_lambda(PITCH) )

    # ATTENTION ON NE PLACE PAS DES PLOTS D ALIM EN DEHORS DE LA COURONNE INTERNE
    if isInternalGroundPad ( ins ) or isInternalPowerPad ( ins ) \
    or isExternalGroundPad ( ins ) or isExternalPowerPad ( ins ) :
      cellAB     = cell.getAbutmentBox()
      coreAB     = core.getAbutmentBox()
      pad_height = getPadHeight ( cell )
      if _x <= ( ( cellAB.getXMin() + pad_height ) + coreAB.getXMin() ) / 2 \
      or _x >= ( ( cellAB.getXMax() - pad_height ) + coreAB.getXMax() ) / 2 :
        raise ErrorMessage(2,"PadNorth : pad %s must be closer to the center.\n"%str(ins.getName()))
 
    # x calcule pour le pad suivant
    x_init = _x + interval + pad_width
  
    # avoir x y
    box   = ins.getMasterCell().getAbutmentBox()
    _difx = box.getXMin()
    _dify = box.getYMin()
  
    ins.setTransformation ( Transformation ( _x - _difx
                                           , _y - _dify
                                           , Transformation.Orientation.ID
                                           )
                          )
  
    ins.setPlacementStatus ( Instance.PlacementStatus.FIXED )

  UpdateSession.close()
    
##############
## PadSouth ##
##############
def pyPadSouth ( cell, core, args ) :
  '''This function places pads down south of the cell. It leaves an empty squarre of CARRE * CARRE, CARRE is defined in macro'''

  global pad_south

  # Sauvegarder la liste des plots
  pad_south = args

  UpdateSession.open()
    
  # avoir le largeur du espace disponible du nord pour placer les pads 
  south_espace_width    = core.getAbutmentBox().getYMin() - cell.getAbutmentBox().getYMin()
  south_espace_longueur = cell.getAbutmentBox().getWidth() - ( 2 * getPadHeight ( cell ) )

  # avoir le nombre de pads 
  nb_pads = len ( args )
  
  # avoir le largeur total des pads 
  largeur = 0  
  for ins in args : 
    if ins.getPlacementStatus() :
      raise ErrorMessage(2,"PadSouth : the instance %s is already placed."%str(ins.getName()))
  
    if ins.getAbutmentBox().getHeight() >= south_espace_width :
      raise ErrorMessage(2,"PadSouth : not enough space for all pads.")
  
    largeur += ins.getAbutmentBox().getWidth() 
  
  if largeur > south_espace_longueur :
    raise ErrorMessage(2,"PadSouth : not enough space for all pads.")
  
  # calculer l'interval entre les pads 
  interval = ( south_espace_longueur - largeur ) / ( nb_pads + 1 ) 
  
  # placer les pads  
  Xmin_cell = cell.getAbutmentBox().getXMin()
  Ymin_cell = cell.getAbutmentBox().getYMin()
    
  x_init = Xmin_cell + getPadHeight ( cell ) + interval 
  
  ##### Parcours des pads #####
  for ins in args : 
    pad_height = ins.getAbutmentBox().getHeight()
    pad_width  = ins.getAbutmentBox().getWidth()
  
    _y = Ymin_cell # la position finale dans la cell 
    _x = x_init 
  
    # ATTENTION LES PLOTS DOIVENT ETRE PLACES SUR UNE GRILLE DE 5 LAMBDAS !!!!!!!!!!!!!!
    _x = _x - ( _x % DbU_lambda(PITCH) )

    # ATTENTION ON NE PLACE PAS DES PLOTS D ALIM EN DEHORS DE LA COURONNE INTERNE
    if isInternalGroundPad ( ins ) or isInternalPowerPad ( ins ) \
    or isExternalGroundPad ( ins ) or isExternalPowerPad ( ins ) :
      cellAB     = cell.getAbutmentBox()
      coreAB     = core.getAbutmentBox()
      pad_height = getPadHeight ( cell )
      if _x <= ( ( cellAB.getXMin() + pad_height ) + coreAB.getXMin() ) / 2 \
      or _x >= ( ( cellAB.getXMax() - pad_height ) + coreAB.getXMax() ) / 2 :
        raise ErrorMessage(2,"PadSouth : pad %s must be closer to the center."%str(ins.getName()))
 
    # x calcule pour le pad suivant
    x_init = _x + interval + pad_width
  
    # avoir x y apres orientation
    box   = ins.getMasterCell().getAbutmentBox()
    _difx = Transformation ( 0, 0, Transformation.Orientation.MY ).getBox ( box ).getXMin()
    _dify = Transformation ( 0, 0, Transformation.Orientation.MY ).getBox ( box ).getYMin() 
  
    ins.setTransformation ( Transformation ( _x - _difx
                                           , _y - _dify
                                           , Transformation.Orientation.MY
                                           )
                          )
  
    ins.setPlacementStatus ( Instance.PlacementStatus.FIXED )  
    
  UpdateSession.close()

#############
## PadEast ##
#############
def pyPadEast ( cell, core, args ) :
  '''This function places pads at the east of the cell. It leaves an empty squarre of CARRE * CARRE, CARRE is defined in macro''' 

  global pad_east

  # Sauvegarder la liste des plots
  pad_east = args

  UpdateSession.open()
      
  # avoir le largeur du espace disponible du nord pour placer les pads 
  east_espace_width    = cell.getAbutmentBox().getXMax() - core.getAbutmentBox().getXMax()
  east_espace_longueur = cell.getAbutmentBox().getHeight() - ( 2 * getPadHeight ( cell ) ) 
  
  # avoir le nombre de pads 
  nb_pads = len ( args )
  
  # avoir le largeur total des pads 
  largeur = 0  
  for ins in args :
    if ins.getPlacementStatus() :
      raise ErrorMessage(2,"PadEast : the instance %s is already placed."%str(insGetName()))
  
    if ins.getAbutmentBox().getHeight() >= east_espace_width :
      raise ErrorMessage(2,"PadEast : not enough space for pads.")
  
    largeur += ins.getAbutmentBox().getWidth() 
  
  if largeur > east_espace_longueur :
    raise ErrorMessage(2,"PadEast : not enough space for all pads.")
  
  # calculer l'interval entre les pads 
  interval = ( east_espace_longueur - largeur ) / ( nb_pads + 1 ) 
  
  # placer les pads  
  Xmin_cell = cell.getAbutmentBox().getXMin()
  Ymin_cell = cell.getAbutmentBox().getYMin()
  Xmax_cell = cell.getAbutmentBox().getXMax()
  
  y_init = Ymin_cell + getPadHeight ( cell ) + interval 
  
  ##### Parcours des pads #####
  for ins in args : 
    pad_height = ins.getAbutmentBox().getHeight()
    pad_width  = ins.getAbutmentBox().getWidth()
  
    Xmin_pad   = ins.getMasterCell().getAbutmentBox().getXMin()
    Ymin_pad   = ins.getMasterCell().getAbutmentBox().getYMin()
  
    _x = Xmax_cell - pad_height   # la position finale dans la cell 
    _y = y_init 
  
    # ATTENTION LES PLOTS DOIVENT ETRE PLACES SUR UNE GRILLE DE 5 LAMBDAS !!!!!!!!!!!!!!
    _y = _y - ( _y % DbU_lambda(PITCH) )

    # ATTENTION ON NE PLACE PAS DES PLOTS D ALIM EN DEHORS DE LA COURONNE INTERNE
    if isInternalGroundPad ( ins ) or isInternalPowerPad ( ins ) \
    or isExternalGroundPad ( ins ) or isExternalPowerPad ( ins ) :
      cellAB     = cell.getAbutmentBox()
      coreAB     = core.getAbutmentBox()
      pad_height = getPadHeight ( cell )
      if _y <= ( ( cellAB.getYMin() + pad_height ) + coreAB.getYMin() ) / 2 \
      or _y >= ( ( cellAB.getYMax() - pad_height ) + coreAB.getYMax() ) / 2 :
        raise ErrorMessage(2,"PadEast : pad %s must be closer to the center.\n"%str(ins.getName()))
 
    # y calcule pour le pad suivant
    y_init = _y + interval + pad_width
  
    # avoir le x y apres orientation
    box   = ins.getMasterCell().getAbutmentBox()
    _difx = Transformation ( 0, 0, 7 ).getBox ( box ).getXMin()     
    _dify = Transformation ( 0, 0, 7 ).getBox ( box ).getYMin() 
  
    ins.setTransformation ( Transformation ( _x - _difx
                                           , _y - _dify
                                           , Transformation.Orientation.YR
                                           )
                          )
  
    ins.setPlacementStatus ( Instance.PlacementStatus.FIXED )  
    
  UpdateSession.close()

#############
## PadWest ##
#############
def pyPadWest ( cell, core, args ) :
  '''This function places pads at the west of the cell. It leaves an empty squarre of CARRE * CARRE, CARRE is defined in macro''' 

  global pad_west

  # Sauvegarder la liste des plots
  pad_west = args

  UpdateSession.open()

  # avoir le largeur du espace disponible du nord pour placer les pads 
  west_espace_width    = core.getAbutmentBox().getXMin() - cell.getAbutmentBox().getXMin()  
  west_espace_longueur = cell.getAbutmentBox().getHeight() - ( 2 * getPadHeight ( cell ) )
  
  # avoir le nombre de pads 
  nb_pads = len ( args )
  
  # avoir le largeur total des pads 
  largeur = 0  
  for ins in args : 
    if ins.getPlacementStatus() :
      raise ErrorMessage(2,"PadWest : the instance %s is already placed."%str(ins.getName()))
      
    if ins.getAbutmentBox().getHeight() >= west_espace_width :
      raise ErrorMessage(2,"PadWest : not enough space for pads.")
  
    largeur += ins.getAbutmentBox().getWidth() 
  
  if largeur > west_espace_longueur :
    raise ErrorMessage(2,"PadWest : not enough space for pads.")
  
  # calculer l'interval entre les pads 
  interval = int ( ( west_espace_longueur - largeur ) / ( nb_pads + 1 ) ) 
  
  # placer les pads  
  Xmin_cell  = cell.getAbutmentBox().getXMin()  
  Ymin_cell  = cell.getAbutmentBox().getYMin()
  
  y_init = Ymin_cell + getPadHeight ( cell ) + interval
  
  ##### Parcours des pads #####
  for ins in args :
    pad_height = ins.getAbutmentBox().getHeight()  
    pad_width  = ins.getAbutmentBox().getWidth()                
  
    _x = Xmin_cell # la position finale dans la cell 
    _y = y_init 
  
    # ATTENTION LES PLOTS DOIVENT ETRE PLACES SUR UNE GRILLE DE 5 LAMBDAS !!!!!!!!!!!!!!
    _y = _y - ( _y % DbU_lambda(PITCH) )

    # ATTENTION ON NE PLACE PAS DES PLOTS D ALIM EN DEHORS DE LA COURONNE INTERNE
    if isInternalGroundPad ( ins ) or isInternalPowerPad ( ins ) \
    or isExternalGroundPad ( ins ) or isExternalPowerPad ( ins ) :
      cellAB     = cell.getAbutmentBox()
      coreAB     = core.getAbutmentBox()
      pad_height = getPadHeight ( cell )

      if _y <= ( ( cellAB.getYMin() + pad_height ) + coreAB.getYMin() ) / 2 \
      or _y >= ( ( cellAB.getYMax() - pad_height ) + coreAB.getYMax() ) / 2 :
        raise ErrorMessage(2,"PadWest : pad %s must be closer to the center.\n"%str(ins.getName()))
        
    # y calcule pour le pad suivant
    y_init = _y + interval + pad_width   
  
    # avoir x y apres orientation
    box   = ins.getMasterCell().getAbutmentBox()
    _difx = Transformation ( 0, 0, 1 ).getBox ( box ).getXMin() 
    _dify = Transformation ( 0, 0, 1 ).getBox ( box ).getYMin() 
  
    ins.setTransformation ( Transformation ( _x - _difx
                                           , _y - _dify
                                           , Transformation.Orientation.R1
                                           )
                          )
  
    ins.setPlacementStatus ( Instance.PlacementStatus.FIXED )  
    
  UpdateSession.close()

###############
## PowerRing ##
###############
def pyPowerRing ( cell, core, n ) :
  '''This function places power rings around the core and around the plots
  n is the number of rings (n vdd, n+1 vss) 
  , Distance between rings is defined by macro DbU_lambda ( RING_INTERVAL )
  Width of rings is defined by macro DbU_lambda ( RING_WIDTH )'''

  global pad_north, pad_south, pad_east, pad_west
  global RING_INTERVAL, RING_WIDTH

  db = DataBase.getDB()
  
  topRoutingLayerName = Cfg.getParamString('katabatic.topRoutingLayer', 'METAL4').asString()
  topRoutingLayer     = db.getTechnology().getLayer( topRoutingLayerName )
  allowedDepth        = CRL.AllianceFramework.get().getRoutingGauge().getLayerDepth( topRoutingLayer )

  print 'topRoutingLayer: <%s> depth:%d' % (topRoutingLayer.getName(), allowedDepth)
  
  UpdateSession.open()
  
  if pad_north == [] : raise ErrorMessage(2,"PowerRing : Pads in the north haven't been placed")
  if pad_south == [] : raise ErrorMessage(2,"PowerRing : Pads in the south haven't been placed")
  if pad_east  == [] : raise ErrorMessage(2,"PowerRing : Pads in the east haven't been placed")
  if pad_west  == [] : raise ErrorMessage(2,"PowerRing : Pads in the west haven't been placed")
  
  #############################
  #  PARAMETRE DU PLACEMENT  ##
  #############################
  
  # le largeur du fil de routage 
  routage_segment_width = DbU_lambda(12)
  
  # la distance entre les deux centres des couronnes  
  decalage = DbU_lambda(RING_WIDTH) + DbU_lambda(RING_INTERVAL)     
  
  # Recuperer les informations sur le core
  core_width  = core.getAbutmentBox().getWidth()
  core_height = core.getAbutmentBox().getHeight()
  core_Xmin   = core.getAbutmentBox().getXMin()
  core_Ymin   = core.getAbutmentBox().getYMin()
  core_Xmax   = core.getAbutmentBox().getXMax()
  core_Ymax   = core.getAbutmentBox().getYMax()
  mips_core   = core.getMasterCell()

  # Recuperer la hauteur des plots 
  pad_height  = getPadHeight ( cell )
  
  # Recuperer les layers ( Vias , Alus )  
  metal4 = db.getTechnology().getLayer ( "METAL4"  )
  metal3 = db.getTechnology().getLayer ( "METAL3"  )
  metal2 = db.getTechnology().getLayer ( "METAL2"  )
  metal1 = db.getTechnology().getLayer ( "METAL1"  )
  via1 = db.getTechnology().getLayer ( "VIA12" )
  via2 = db.getTechnology().getLayer ( "VIA23" )
  via3 = db.getTechnology().getLayer ( "VIA34" )

  if (allowedDepth < 3):
    hCoronaLayer = metal2
    vCoronaLayer = metal3
    cCoronaLayer = via2
  else:
    hCoronaLayer = metal4
    vCoronaLayer = metal3
    cCoronaLayer = via3
               
  # Recuperer les nets ( qui connectent les connectors du plot : vdde , vsse , vddi , vssi , ck  )
  instance = cell.getInstance( pad_north[0].getName() )
  model    = instance.getMasterCell()
  vddp     = instance.getPlug ( model.getNet("vdde") ).getNet()
  vssp     = instance.getPlug ( model.getNet("vsse") ).getNet()
  vdd      = instance.getPlug ( model.getNet("vddi") ).getNet()
  vss      = instance.getPlug ( model.getNet("vssi") ).getNet()
  ck       = instance.getPlug ( model.getNet("ck"  ) ).getNet()
 # If nets are globals, get them from the netlist.
  if not vddp: vddp = cell.getNet('vdde')
  if not vssp: vssp = cell.getNet('vsse')
  if not vdd:  vdd  = cell.getNet('vddi')
  if not vss:  vss  = cell.getNet('vssi')
  if not ck:   ck   = cell.getNet('cki')
                 
  # Prendre la hauteur, la longueur, Xmin et Ymin de ce modele 
  cell_height = cell.getAbutmentBox().getHeight()
  cell_width  = cell.getAbutmentBox().getWidth()
  cell_Xmin   = cell.getAbutmentBox().getXMin()
  cell_Ymin   = cell.getAbutmentBox().getYMin()
  cell_Xmax   = cell.getAbutmentBox().getXMax()
  cell_Ymax   = cell.getAbutmentBox().getYMax()
  
  # Verifier s'il y a assez de places pour les couronnes
  if ( cell_height - core_height ) < ( cell_width - core_width ) : smaller = cell_height - core_height
  else                                                           : smaller = cell_width  - core_width
  limit = DbU_lambda ( int ( DbU_getLambda ( smaller ) / 2 ) ) - pad_height
  
  if ( (2*n + 1) * DbU_lambda(RING_INTERVAL) + (2*n) * DbU_lambda(RING_WIDTH) ) >= limit :   # 2*n + 1 spaces + 2*n width
    raise ErrorMessage(2,"PowerRing : too many rings, not enough space")
  
  # la distance entre les couronnes et le core         
  marge = DbU_lambda ( int ( DbU_getLambda ( limit - (2*n + 1 ) * DbU_lambda(RING_INTERVAL) - (2*n) * DbU_lambda(RING_WIDTH) ) ) / 2 )
  
  east_power  = [] 
  west_power  = []
  north_power = []
  south_power = []
  
  searchVddVss ( cell, east_power, west_power, north_power, south_power )
  #affichePad ( cell )
  
  # Creation des listes pad_north_pin_y_vss et pad_north_pin_vdd
  pad_north_pin_x_vss = []
  pad_north_pin_x_vdd = []
  for pad_inst in pad_north :
    if isInternalGroundPad ( pad_inst ) or isInternalPowerPad ( pad_inst ) \
    or isExternalGroundPad ( pad_inst ) or isExternalPowerPad ( pad_inst ):
      transformation = pad_inst.getTransformation()
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_north_pin_x_vss.append ( [transformation.getX ( element.getX(), element.getY() ), element.getBoundingBox().getHalfWidth()] )

      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_north_pin_x_vdd.append ( [transformation.getX ( element.getX(), element.getY() ), element.getBoundingBox().getHalfWidth()] )


  # Creation des listes pad_south_pin_y_vss et pad_south_pin_vdd
  pad_south_pin_x_vss = []
  pad_south_pin_x_vdd = []
  for pad_inst in pad_south :
    if isInternalGroundPad ( pad_inst ) or isInternalPowerPad ( pad_inst ) \
    or isExternalGroundPad ( pad_inst ) or isExternalPowerPad ( pad_inst ):
      transformation = pad_inst.getTransformation()
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_south_pin_x_vss.append ( [transformation.getX ( element.getX(), element.getY() ), element.getBoundingBox().getHalfWidth()] )

      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_south_pin_x_vdd.append ( [transformation.getX ( element.getX(), element.getY() ), element.getBoundingBox().getHalfWidth()] )


  # Creation des listes pad_east_pin_y_vss et pad_east_pin_vdd
  pad_east_pin_y_vss = []
  pad_east_pin_y_vdd = []
  for pad_inst in pad_east :
    if isInternalGroundPad ( pad_inst ) or isInternalPowerPad ( pad_inst ) \
    or isExternalGroundPad ( pad_inst ) or isExternalPowerPad ( pad_inst ):
      transformation = pad_inst.getTransformation()
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_east_pin_y_vss.append ( [transformation.getY ( element.getX(), element.getY() ), element.getBoundingBox().getHalfHeight()] )

      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_east_pin_y_vdd.append ( [transformation.getY ( element.getX(), element.getY() ), element.getBoundingBox().getHalfHeight()] )
      
  # Creation des listes pad_west_pin_y_vss et pad_west_pin_vdd
  pad_west_pin_y_vss = []
  pad_west_pin_y_vdd = []
  for pad_inst in pad_west :
    if isInternalGroundPad ( pad_inst ) or isInternalPowerPad ( pad_inst ) \
    or isExternalGroundPad ( pad_inst ) or isExternalPowerPad ( pad_inst ):
      transformation = pad_inst.getTransformation()
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_west_pin_y_vss.append ( [transformation.getY ( element.getX(), element.getY() ), element.getBoundingBox().getHalfHeight()] )

      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        if re.search ( "METAL2", str ( element.getLayer() ) ) \
        and ( ( element.getY() - element.getBoundingBox().getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          pad_west_pin_y_vdd.append ( [transformation.getY ( element.getX(), element.getY() ), element.getBoundingBox().getHalfHeight()] )
      
  #####################################
  ##  PLACER LES COURONNES INTERNES  ##
  #####################################
  
  # les listes pour sauvegarder les segments des couronnes des quatres coins 
  vertical_west_vss    = [] 
  vertical_east_vss    = []
  horizontal_south_vss = []
  horizontal_north_vss = []
  
  vertical_west_vdd    = []
  vertical_east_vdd    = []
  horizontal_south_vdd = []
  horizontal_north_vdd = []
  
  init_Xmin = core_Xmin - marge - DbU_lambda(RING_WIDTH) / 2
  init_Xmin = init_Xmin - ( init_Xmin % DbU_lambda(PITCH) )
  init_Ymin = core_Ymin - marge - DbU_lambda(RING_WIDTH) / 2
  init_Ymin = init_Ymin - ( init_Ymin % DbU_lambda(PITCH) )
  init_Xmax = core_Xmax + marge + DbU_lambda(RING_WIDTH) / 2 
  init_Xmax = init_Xmax - ( init_Xmax % DbU_lambda(PITCH) )
  init_Ymax = core_Ymax + marge + DbU_lambda(RING_WIDTH) / 2 
  init_Ymax = init_Ymax - ( init_Ymax % DbU_lambda(PITCH) )

  contact_side = DbU_lambda(RING_WIDTH-1)
  
  
  for i in range ( 0, 2*n+1, 2 ) : 
    contact1 = Contact.create ( vss, cCoronaLayer, init_Xmin - decalage*i, init_Ymin - decalage*i, contact_side, contact_side )
    contact2 = Contact.create ( vss, cCoronaLayer, init_Xmin - decalage*i, init_Ymax + decalage*i, contact_side, contact_side )
    contact3 = Contact.create ( vss, cCoronaLayer, init_Xmax + decalage*i, init_Ymax + decalage*i, contact_side, contact_side )
    contact4 = Contact.create ( vss, cCoronaLayer, init_Xmax + decalage*i, init_Ymin - decalage*i, contact_side, contact_side )

    vertical_west_vss.append    ( Vertical.create   ( contact1, contact2, vCoronaLayer, init_Xmin - decalage*i, DbU_lambda(RING_WIDTH) ) )          
    vertical_east_vss.append    ( Vertical.create   ( contact3, contact4, vCoronaLayer, init_Xmax + decalage*i, DbU_lambda(RING_WIDTH) ) )          
    horizontal_south_vss.append ( Horizontal.create ( contact1, contact4, hCoronaLayer, init_Ymin - decalage*i, DbU_lambda(RING_WIDTH) ) )          
    horizontal_north_vss.append ( Horizontal.create ( contact2, contact3, hCoronaLayer, init_Ymax + decalage*i, DbU_lambda(RING_WIDTH) ) )
     
    if i != 2*n :   
     contact1 = Contact.create ( vdd, cCoronaLayer, init_Xmin - decalage* ( i + 1 ), init_Ymin - decalage*( i + 1 ) , contact_side, contact_side )
     contact2 = Contact.create ( vdd, cCoronaLayer, init_Xmin - decalage* ( i + 1 ), init_Ymax + decalage*( i + 1 ) , contact_side, contact_side )
     contact3 = Contact.create ( vdd, cCoronaLayer, init_Xmax + decalage* ( i + 1 ), init_Ymax + decalage*( i + 1 ) , contact_side, contact_side )
     contact4 = Contact.create ( vdd, cCoronaLayer, init_Xmax + decalage* ( i + 1 ), init_Ymin - decalage*( i + 1 ) , contact_side, contact_side )        
     vertical_west_vdd.append    ( Vertical.create   ( contact1, contact2, vCoronaLayer, init_Xmin - decalage* ( i + 1 ), DbU_lambda(RING_WIDTH) ) )
     vertical_east_vdd.append    ( Vertical.create   ( contact3, contact4, vCoronaLayer, init_Xmax + decalage* ( i + 1 ), DbU_lambda(RING_WIDTH) ) )
     horizontal_south_vdd.append ( Horizontal.create ( contact1, contact4, hCoronaLayer, init_Ymin - decalage* ( i + 1 ), DbU_lambda(RING_WIDTH) ) )  
     horizontal_north_vdd.append ( Horizontal.create ( contact2, contact3, hCoronaLayer, init_Ymax + decalage* ( i + 1 ), DbU_lambda(RING_WIDTH) ) )
  
  # MACRO pour les directions d'access des pins  
  UNDEFINED = 0
  NORTH     = 1
  SOUTH     = 2
  EAST      = 3
  WEST      = 4

  ##############################################################
  ##     Connecter les pins de vss aux couronnes de vss       ##
  ##############################################################
  core_transformation = core.getTransformation()        
                                 
  # parcourir les pins de vss
  for element in mips_core.getNet("vss").getPins():
    element_layer_name = str ( element.getLayer().getName() ) # string, nom du layer
    # avoir le x, y transforme
    _x = core_transformation.getX ( element.getX(), element.getY() )
    _y = core_transformation.getY ( element.getX(), element.getY() )

    # Creer un contact a la place du pin 
    if   (allowedDepth > 2) and \
         re.search ( "METAL4", element_layer_name ) : old_contact = Contact.create ( vss, metal4, _x, _y , element.getHeight(), element.getHeight() ) 
    elif re.search ( "METAL1", element_layer_name ) : old_contact = Contact.create ( vss, metal1, _x, _y , element.getHeight(), element.getHeight() )
    elif re.search ( "METAL3", element_layer_name ) : old_contact = Contact.create ( vss, metal3, _x, _y , element.getHeight(), element.getHeight() )
    else : 
      raise ErrorMessage(2,"wrong layer of pin in the west of core : %s.\n" % element_layer_name)
  
    # Connection du cote de l'ouest   
    if element.getAccessDirection() == WEST : 
      # Parcourir tous les couronnes vss
      halfHeight = element.getHeight() / 2
      lastVerti  = n
      for [yPin,pinHalfHeight] in pad_west_pin_y_vss + pad_west_pin_y_vdd :
        if ( _y + halfHeight + DbU_lambda(3) >= yPin - pinHalfHeight and _y + halfHeight + DbU_lambda(3) <= yPin + pinHalfHeight ) \
        or ( _y - halfHeight - DbU_lambda(3) <= yPin + pinHalfHeight and _y - halfHeight - DbU_lambda(3) >= yPin - pinHalfHeight ):
          lastVerti = n/2 + (n+1)%2
          
      for i in range ( lastVerti + 1 ) : 
       contact_x =  init_Xmin - ( decalage*2 )*i # x du contact a creer
       contact_side = element.getHeight() - DbU_lambda(1.0) 

       if (allowedDepth > 2) and re.search( "METAL4", element_layer_name ) :
         contact     = Contact.create ( vss, via3 , contact_x , _y , contact_side , contact_side )
         horizontal  = Horizontal.create ( contact, old_contact , metal4 , _y , element.getHeight() )
         old_contact = contact
       else : 
         contact_via1 = Contact.create ( vss, via1 , contact_x , _y , contact_side , contact_side )
         contact_via2 = Contact.create ( vss, via2 , contact_x , _y , contact_side , contact_side )
         horizontal   = Horizontal.create ( contact_via1 , old_contact , metal1 , _y , element.getHeight() )       
         old_contact  = contact_via1
         
    # Connection du cote de l'est   
    if element.getAccessDirection() == EAST : 
      # Parcourir tous les couronnes vss
      halfHeight = element.getHeight() / 2
      lastVerti  = n
      for [yPin,pinHalfHeight] in pad_east_pin_y_vss + pad_east_pin_y_vdd :
        if ( _y + halfHeight + DbU_lambda(3) >= yPin - pinHalfHeight and _y + halfHeight + DbU_lambda(3) <= yPin + pinHalfHeight ) \
        or ( _y - halfHeight - DbU_lambda(3) <= yPin + pinHalfHeight and _y - halfHeight - DbU_lambda(3) >= yPin - pinHalfHeight ):
          lastVerti = n/2 + (n+1)%2

      for i in range ( lastVerti + 1 ) : 
        contact_x    =  init_Xmax + ( decalage*2 )*i # x du contact a creer         
        contact_side = element.getHeight() - DbU_lambda(1.0)
        
        if (allowedDepth > 2) and re.search( "METAL4", element_layer_name ) :
          contact     = Contact.create ( vss, via3 , contact_x , _y , contact_side, contact_side )
          horizontal  = Horizontal.create ( contact, old_contact , metal4 , _y , element.getHeight() )
          old_contact = contact
        else : 
          contact_via1 = Contact.create ( vss, via1 , contact_x , _y , contact_side , contact_side )
          contact_via2 = Contact.create ( vss, via2 , contact_x , _y , contact_side , contact_side )
          horizontal   = Horizontal.create ( contact_via1 , old_contact , metal1 , _y , element.getHeight() )
          old_contact  = contact_via1
  
    # Connection du cote du nord    
    if element.getAccessDirection() == NORTH  : 
      # Parcourir tous les couronnes vss
      halfWidth = element.getHeight() / 2
      lastVerti  = n
      for [xPin,pinHalfWidth] in pad_north_pin_x_vss + pad_north_pin_x_vdd :
        if ( _x + halfWidth + DbU_lambda(3) >= xPin - pinHalfWidth and _x + halfWidth + DbU_lambda(3) <= xPin + pinHalfWidth ) \
        or ( _x - halfWidth - DbU_lambda(3) <= xPin + pinHalfWidth and _x - halfWidth - DbU_lambda(3) >= xPin - pinHalfWidth ):
          lastVerti = n/2 + (n+1)%2

      for i in range ( lastVerti + 1 ) : 
        contact_y =  init_Ymax  +  ( decalage*2 )*i # y du contact a creer         
  
        if re.search ( "METAL3", element_layer_name ) :         
          contact     = Contact.create ( vss, via3, _x, contact_y, element.getHeight(), element.getHeight() )
          vertical    = Vertical.create ( contact, old_contact, metal3, _x, DbU_lambda(RING_WIDTH) )
          old_contact = contact 
        else :
          raise ErrorMessage(2,"wrong layer of pin in the west of core : %s.\n"% element_layer_name)
       
    # Connection du cote du sud    
    if element.getAccessDirection() == SOUTH : 
      # Parcourir tous les couronnes vss
      halfWidth = element.getHeight() / 2
      lastVerti  = n
      for [xPin,pinHalfWidth] in pad_south_pin_x_vss + pad_south_pin_x_vdd :
        if ( _x + halfWidth + DbU_lambda(3) >= xPin - pinHalfWidth and _x + halfWidth + DbU_lambda(3) <= xPin + pinHalfWidth ) \
        or ( _x - halfWidth - DbU_lambda(3) <= xPin + pinHalfWidth and _x - halfWidth - DbU_lambda(3) >= xPin - pinHalfWidth ):
          lastVerti = n/2 + (n+1)%2

      for i in range ( lastVerti+1 ) : 
        contact_y =  init_Ymin  -  ( decalage*2 )*i # x du contact a creer
  
        if re.search ( "METAL3", element_layer_name ) :         
          contact     = Contact.create ( vss, via3, _x, contact_y, element.getHeight(), element.getHeight() )
          vertical    = Vertical.create ( contact, old_contact, metal3, _x, DbU_lambda(RING_WIDTH) )
          old_contact = contact
        else :
          raise ErrorMessage(2,"wrong layer of pin in the west of core : %s."%element_layer_name)

    # End of while 
    
  #######################################################
  ### Connecter les pins de vdd aux couronnes de vdd  ###
  #######################################################
  core_transformation = core.getTransformation()        
  
  # parcourir les pins de vdd
  for element in mips_core.getNet("vdd").getPins():
    element_layer_name = str(element.getLayer().getName()) # string , nom du layer 
    # avoir x, y transforme
    _x = core_transformation.getX ( element.getX(), element.getY() )
    _y = core_transformation.getY ( element.getX(), element.getY() )
  
    # Creer un contact a la place du pin 
    if   (allowedDepth > 2) and \
         re.search ( "METAL4", element_layer_name ) : old_contact = Contact.create ( vdd, metal4, _x, _y , element.getHeight(), element.getHeight() )
    elif re.search ( "METAL1", element_layer_name ) : old_contact = Contact.create ( vdd, metal1, _x, _y , element.getHeight(), element.getHeight() )
    elif re.search ( "METAL3", element_layer_name ) : old_contact = Contact.create ( vdd, metal3, _x, _y , element.getHeight(), element.getHeight() )  
    else : 
      raise ErrorMessage(2,"wrong layer of pin in the west of core : %s."%element_layer_name)
  
    # Connection du cote de l'ouest   
    if element.getAccessDirection() == WEST : 
      # Parcourir tous les couronnes vdd
      halfHeight = element.getHeight() / 2
      lastVerti  = n
      for [yPin,pinHalfHeight] in pad_west_pin_y_vss + pad_west_pin_y_vdd :
        if ( _y + halfHeight + DbU_lambda(3) >= yPin - pinHalfHeight and _y + halfHeight + DbU_lambda(3) <= yPin + pinHalfHeight ) \
        or ( _y - halfHeight - DbU_lambda(3) <= yPin + pinHalfHeight and _y - halfHeight - DbU_lambda(3) >= yPin - pinHalfHeight ):
          lastVerti = n/2 + (n+1)%2
      
      for i in range ( lastVerti ) : 
        contact_x    =  init_Xmin - decalage - ( decalage*2 )*i # x du contact a creer         
        contact_side = element.getHeight() - DbU_lambda(1.0)
  
        if (allowedDepth > 2) and re.search( "METAL4", element_layer_name ) :         
          contact     = Contact.create ( vdd, via3, contact_x, _y, contact_side, contact_side )
          horizontal  = Horizontal.create ( contact, old_contact, metal4, _y, element.getHeight() )
          old_contact = contact 
        else : 
          contact_via1 = Contact.create ( vdd, via1, contact_x, _y, contact_side, contact_side )
          contact_via2 = Contact.create ( vdd, via2, contact_x, _y, contact_side, contact_side )
          horizontal   = Horizontal.create ( contact_via1, old_contact, metal1, _y, element.getHeight() )       
          old_contact  = contact_via1
  
    # Connection du cote de l'est   
    if element.getAccessDirection() == EAST : 
      # Parcourir tous les couronnes vdd
      halfHeight = element.getHeight() / 2
      lastVerti  = n
      for [yPin,pinHalfHeight] in pad_east_pin_y_vss + pad_east_pin_y_vdd :
        if ( _y + halfHeight + DbU_lambda(3) >= yPin - pinHalfHeight and _y + halfHeight + DbU_lambda(3) <= yPin + pinHalfHeight ) \
        or ( _y - halfHeight - DbU_lambda(3) <= yPin + pinHalfHeight and _y - halfHeight - DbU_lambda(3) >= yPin - pinHalfHeight ):
          lastVerti = n/2 + (n+1)%2
      
      for i in range ( lastVerti ) : 
       contact_x    =  init_Xmax + ( decalage*2 )*i + decalage # x du contact a creer         
       contact_side = element.getHeight() - DbU_lambda(1.0)
  
       if (allowedDepth > 2) and re.search( "METAL4", element_layer_name ) :
         contact     = Contact.create ( vdd, via3 , contact_x , _y , contact_side , contact_side )
         horizontal  = Horizontal.create ( contact, old_contact , metal4 , _y , element.getHeight() )
         old_contact = contact 
       else : 
         contact_via1 = Contact.create ( vdd, via1, contact_x, _y, contact_side, contact_side )
         contact_via2 = Contact.create ( vdd, via2, contact_x, _y, contact_side, contact_side )
         horizontal   = Horizontal.create ( contact_via1, old_contact, metal1, _y, element.getHeight() )       
         old_contact  = contact_via1  
  
    # Connection du cote du nord    
    if element.getAccessDirection() == NORTH  : 
      # Parcourir tous les couronnes vdd
      halfWidth = element.getHeight() / 2
      lastVerti  = n
      for [xPin,pinHalfWidth] in pad_north_pin_x_vss + pad_north_pin_x_vdd :
        if ( _x + halfWidth + DbU_lambda(3) >= xPin - pinHalfWidth and _x + halfWidth + DbU_lambda(3) <= xPin + pinHalfWidth ) \
        or ( _x - halfWidth - DbU_lambda(3) <= xPin + pinHalfWidth and _x - halfWidth - DbU_lambda(3) >= xPin - pinHalfWidth ):
          lastVerti = n/2 + (n+1)%2

      for i in range ( lastVerti ) : 
        contact_y =  init_Ymax + decalage + ( decalage*2 )*i # x du contact a creer         
  
        if re.search ( "METAL3", element_layer_name ) :         
          contact     = Contact.create ( vdd, via3, _x, contact_y, element.getHeight(), element.getHeight() )
          vertical    = Vertical.create ( contact, old_contact, metal3, _x, DbU_lambda(RING_WIDTH) )
          old_contact = contact 
        else :
          raise ErrorMessage(2,"wrong layer of pin in the west of core : %s."%element_layer_name)

    # Connection du cote du sud    
    if element.getAccessDirection() == SOUTH  : 
      # Parcourir tous les couronnes vdd
      halfWidth = element.getHeight() / 2
      lastVerti  = n
      for [xPin,pinHalfWidth] in pad_south_pin_x_vss + pad_south_pin_x_vdd :
        if ( _x + halfWidth + DbU_lambda(3) >= xPin - pinHalfWidth and _x + halfWidth + DbU_lambda(3) <= xPin + pinHalfWidth ) \
        or ( _x - halfWidth - DbU_lambda(3) <= xPin + pinHalfWidth and _x - halfWidth - DbU_lambda(3) >= xPin - pinHalfWidth ):
          lastVerti = n/2 + (n+1)%2
         
      for i in range ( lastVerti ) : 
        contact_y =  init_Ymin - decalage - ( decalage*2 )*i # x du contact a creer         
  
        if re.search ( "METAL3", element_layer_name ) :         
          contact     = Contact.create ( vdd, via3, _x, contact_y, element.getHeight(), element.getHeight() )
          vertical    = Vertical.create ( contact, old_contact, metal3, _x, DbU_lambda(RING_WIDTH) )
          old_contact = contact     
        else :
          raise ErrorMessage(2,"wrong layer of pin in the west of core : %s."%element_layer_name)
  
    # End of while 
  
  # Attention au decalage de 1/2 de CMETAL 
  #LAMBDA = DbU_lambda ( 1 ) / 2
  LAMBDA = DbU_lambda ( 1 )
  
  ###############################################################
  #  Connection entre couronnes internes et couronnes externes  #
  ###############################################################
  for pad_inst in pad_east :
    if isInternalPowerPad ( pad_inst )  or isExternalPowerPad ( pad_inst )  \
    or isInternalGroundPad ( pad_inst ) or isExternalGroundPad ( pad_inst ) :

      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)
    
        if re.search ( "METAL3", str ( layer ) ) \
        and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )
          
          Contact.create ( vss, via2, X, Y, height, height )
          contactPad = Contact.create ( vss, via1, X, Y, height, height )
          Horizontal.create ( contactPad, vertical_east_vss[-1], metal1, Y, DbU_lambda ( RING_WIDTH ) )
          Contact.create    ( vss, via1, vertical_east_vss[-1].getX(), Y, height, height )
          Contact.create    ( vss, via2, vertical_east_vss[-1].getX(), Y, height, height )
      
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)
    
        if re.search ( "METAL3", str ( layer ) ) \
        and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )

         #Contact ( vdd, via2, X, Y, height, height )
          contactPad = Contact.create ( vdd, via1, X, Y, height, height )
          Horizontal.create ( contactPad, vertical_east_vdd[-1], metal1, Y, DbU_lambda ( RING_WIDTH ) )
          Contact.create    ( vdd, via1, vertical_east_vdd[-1].getX(), Y, height, height )
          Contact.create    ( vdd, via2, vertical_east_vdd[-1].getX(), Y, height, height )
      
  for pad_inst in pad_west :
    if isInternalPowerPad ( pad_inst )  or isExternalPowerPad ( pad_inst )  \
    or isInternalGroundPad ( pad_inst ) or isExternalGroundPad ( pad_inst ) :
        
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)
    
        if re.search ( "METAL3", str ( layer ) ) \
        and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )

         #Contact ( vss, via2, X, Y, height, height )
          contactPad = Contact.create ( vss, via1, X, Y, height, height )
          Horizontal.create ( contactPad, vertical_west_vss[-1], metal1, Y, DbU_lambda ( RING_WIDTH ) )
          Contact.create    ( vss, via1, vertical_west_vss[-1].getX(), Y, height, height )
          Contact.create    ( vss, via2, vertical_west_vss[-1].getX(), Y, height, height )
      
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)

        if re.search ( "METAL3", str ( layer ) ) \
        and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )

         #Contact ( vdd, via2, X, Y, height, height )
          contactPad = Contact.create ( vdd, via1, X, Y, height, height )
          Horizontal.create ( contactPad, vertical_west_vdd[-1], metal1, Y, DbU_lambda ( RING_WIDTH ) )
          Contact.create    ( vdd, via1, vertical_west_vdd[-1].getX(), Y, height, height )
          Contact.create    ( vdd, via2, vertical_west_vdd[-1].getX(), Y, height, height )
      
  for pad_inst in pad_north : 
    if isInternalPowerPad ( pad_inst )  or isExternalPowerPad ( pad_inst )  \
    or isInternalGroundPad ( pad_inst ) or isExternalGroundPad ( pad_inst ) :
        
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)
    
        if re.search ( "METAL3", str ( layer ) ) \
        and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :    
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )

          Contact.create    ( vss, via1, X, Y, height, height )
         #Contact.create    ( vss, via2, X, Y, height, height )
    
          contactPad = Contact.create ( vss, metal1, X, Y, height, height )
          Vertical.create   ( contactPad, horizontal_north_vss[-1], metal1, X , DbU_lambda ( RING_WIDTH ) )
          Contact.create    ( vss, via1, X, horizontal_north_vss[-1].getY(), height, height )
          if allowedDepth > 2:
            Contact.create  ( vss, via2, X, horizontal_north_vss[-1].getY(), height, height )
            Contact.create  ( vss, via3, X, horizontal_north_vss[-1].getY(), height, height )
      
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)
    
        if re.search ( "METAL3", str ( layer ) ) \
        and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )
          
          Contact.create    ( vdd, via1, X, Y, height, height )
         #Contact.create    ( vdd, via2, X, Y, height, height )
          
          contactPad = Contact.create ( vdd, metal1, X, Y, height, height )
          Vertical.create   ( contactPad, horizontal_north_vdd[-1], metal1, X, DbU_lambda ( RING_WIDTH ) )
          Contact.create    ( vdd, via1, X, horizontal_north_vdd[-1].getY(), height, height )
          if allowedDepth > 2:
            Contact.create  ( vdd, via2, X, horizontal_north_vdd[-1].getY(), height, height )
            Contact.create  ( vdd, via3, X, horizontal_north_vdd[-1].getY(), height, height )
  
  for pad_inst in pad_south : 
    if isInternalPowerPad ( pad_inst )  or isExternalPowerPad ( pad_inst )  \
    or isInternalGroundPad ( pad_inst ) or isExternalGroundPad ( pad_inst ) :
        
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vssi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)
    
        if re.search ( "METAL3", str ( layer ) ) \
           and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )

          Contact.create    ( vss, via1, X, Y, height, height )
         #Contact.create    ( vss, via2, X, Y, height, height )
          
          contactPad = Contact.create ( vss, metal1, X, Y, height, height )
          Vertical.create ( contactPad, horizontal_south_vss[-1], metal1, X , DbU_lambda ( RING_WIDTH ) )
          Contact.create    ( vss, via1, X, horizontal_south_vss[-1].getY(), height, height )
          if allowedDepth > 2:
            Contact.create  ( vss, via2, X, horizontal_south_vss[-1].getY(), height, height )
            Contact.create  ( vss, via3, X, horizontal_south_vss[-1].getY(), height, height )
      
      for element in NetExternalComponents.get ( pad_inst.getMasterCell().getNet("vddi") ):
        layer   = element.getLayer()
        height  = element.getBoundingBox().getHeight() - DbU_lambda(1.0)
    
        if re.search ( "METAL3", str ( layer ) ) \
        and ( ( element.getY() - ( height / 2 ) ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :
          X = pad_inst.getTransformation().getX ( element.getX(), element.getY() ) 
          Y = pad_inst.getTransformation().getY ( element.getX(), element.getY() )

          Contact.create    ( vdd, via1, X, Y, height, height )
         #Contact.create    ( vdd, via2, X, Y, height, height )
          
          contactPad = Contact.create ( vdd, metal1, X, Y, height, height )
          Vertical.create ( contactPad, horizontal_south_vdd[-1], metal1, X , DbU_lambda ( RING_WIDTH ) )
          Contact.create  ( vdd, via1, X, horizontal_south_vdd[-1].getY(), height, height )
          if allowedDepth > 2:
            Contact.create  ( vdd, via2, X, horizontal_south_vdd[-1].getY(), height, height )
            Contact.create  ( vdd, via3, X, horizontal_south_vdd[-1].getY(), height, height )
  

  #####################################                      ###########################
  ##  PLACER LES COURONNES EXTERNES  ##                      # 1                     2 #
  #####################################                      #                         #
                                                             #                         #
                                                             #                         #
  points_0 = []                                              #           CHIP          #
  points_1 = []                                              #                         #
  points_2 = []                                              #                         #
  points_3 = []                                              #                         #
                                                             #                         #
                                                             # 0                     3 #
                                                             ###########################
  
  instance    = cell.getInstance ( pad_north[0].getName() )
  trans       = instance.getTransformation()
    
  for net in instance.getMasterCell().getNets():
    if net.isSupply() or net.isClock() :
      for component in NetExternalComponents.get(net):
        plug = instance.getPlug ( net )
        NET  = getNetFromPlug( plug )
        if ( not NET ) : raise ErrorMessage(2,"Error plug : %s must be connected" % str(plug.getName()))
  
        layer     = getNonCLayer ( component.getLayer() )

        # On applique la transformation au component, et on recupere les nouvelles coordonnees
        Y = cell_Ymax + cell_Ymin - trans.getY ( component.getX(), component.getY() )
  
        # On recupere la hauteur du component pour router sur la meme hauteur
        RING_HEIGHT = component.getBoundingBox().getHeight()
        
        # On calcule les differents points aux angles pour chaque net
        X_point = cell_Xmin + Y
        Y_point = cell_Ymin + Y
        contact = Contact.create ( NET, layer, X_point, Y_point, RING_HEIGHT, RING_HEIGHT )
        points_0.append ( dict ( [ ['X',X_point], ['Y',Y_point], ['N',NET], ['L',layer], ['R',RING_HEIGHT], ['C',contact] ] ) )
  
        X_point = cell_Xmin + Y
        Y_point = cell_Ymin + ( cell_Ymax - Y )
        contact = Contact.create ( NET, layer, X_point, Y_point, RING_HEIGHT, RING_HEIGHT )
        points_1.append ( dict ( [ ['X',X_point], ['Y',Y_point], ['N',NET], ['L',layer], ['R',RING_HEIGHT], ['C',contact] ] ) )
        
        X_point = cell_Xmin + ( cell_Xmax - Y )
        Y_point = cell_Ymin + ( cell_Ymax - Y )
        contact = Contact.create ( NET, layer, X_point, Y_point, RING_HEIGHT, RING_HEIGHT )
        points_2.append ( dict ( [ ['X',X_point], ['Y',Y_point], ['N',NET], ['L',layer], ['R',RING_HEIGHT], ['C',contact] ] ) )
      
        X_point = cell_Xmin + ( cell_Xmax - Y )
        Y_point = cell_Ymin + Y
        contact = Contact.create ( NET, layer, X_point, Y_point, RING_HEIGHT, RING_HEIGHT )
        points_3.append ( dict ( [ ['X',X_point], ['Y',Y_point], ['N',NET], ['L',layer], ['R',RING_HEIGHT], ['C',contact] ] ) )
      # end of while
  # end of while

  #print "\n\n\n\npoints_0 : ", points_0 , "\n\npoints_1 : " ,points_1 , "\n\npoints_2 : " ,points_2 , "\n\npoints_3 : " , points_3 , "\n\n\n\n" 

  # Placer au cote du nord 
  for ins_pad in pad_north :
    Y_pad   = DbU_lambda ( int ( DbU_getLambda ( ins_pad.getAbutmentBox().getYCenter() ) ) )
    X_pad   = DbU_lambda ( int ( DbU_getLambda ( ins_pad.getAbutmentBox().getXCenter() ) ) )
  
    if ins_pad == pad_north[0] :
      for point in points_1 :
        contact = Contact.create ( point['N'], point['L'], X_pad, point['Y'], point['R'], point['R'] )
        Horizontal.create ( point['C'], contact, point['L'], point['Y'], point['R'] ) 
    else :
      for point in points_1 :
        Horizontal.create ( point['N'], point['L'], point['Y'], point['R'], old_X_pad, X_pad ) 
      
    if ins_pad == pad_north[-1] :
      for point in points_2 :
        contact = Contact.create ( point['N'], point['L'], X_pad, point['Y'], point['R'], point['R'] )
        Horizontal.create ( contact, point['C'], point['L'], point['Y'], point['R'] ) 
  
    old_X_pad = X_pad

  # Placer au cote de l'est 
  for ins_pad in pad_east :
    Y_pad   = ins_pad.getAbutmentBox().getYCenter()
    X_pad   = ins_pad.getAbutmentBox().getXCenter()
  
    if ins_pad == pad_east[0] :
      for point in points_3 :
        contact = Contact.create ( point['N'], point['L'], point['X'], Y_pad, point['R'], point['R'] )
        Vertical.create ( point['C'], contact, point['L'], point['X'], point['R'] )  
    else :
      for point in points_3 :
        Vertical.create ( point['N'], point['L'], point['X'], point['R'], old_Y_pad, Y_pad )  
  
    if ins_pad == pad_east[-1] :
      for point in points_2 :
        contact = Contact.create ( point['N'], point['L'], point['X'], Y_pad, point['R'], point['R'] )
        Vertical.create ( contact, point['C'], point['L'], point['X'], point['R'] )  
       
    old_Y_pad = Y_pad

  # Placer au cote du south 
  for ins_pad in pad_south :
    Y_pad   = ins_pad.getAbutmentBox().getYCenter()
    X_pad   = ins_pad.getAbutmentBox().getXCenter()
  
    if ins_pad == pad_south[0] :
      for point in points_0 :
        contact = Contact.create ( point['N'], point['L'], X_pad, point['Y'], point['R'], point['R'] )
        Horizontal.create ( point['C'], contact, point['L'], point['Y'], point['R'] ) 
    else :
      for point in points_0 :
        Horizontal.create ( point['N'], point['L'], point['Y'], point['R'], old_X_pad, X_pad ) 
      
    if ins_pad == pad_south[-1] :
      for point in points_3 :
        contact = Contact.create ( point['N'], point['L'], X_pad, point['Y'], point['R'], point['R'] )
        Horizontal.create ( contact, point['C'], point['L'], point['Y'], point['R'] ) 
  
    old_X_pad = X_pad
    
  # Placer au cote de l'ouest 
  for ins_pad in pad_west :
    Y_pad   = ins_pad.getAbutmentBox().getYCenter()
    X_pad   = ins_pad.getAbutmentBox().getXCenter()
  
    if ins_pad == pad_west[0] :
      for point in points_0 :
        contact = Contact.create ( point['N'], point['L'], point['X'], Y_pad, point['R'], point['R'] )
        Vertical.create ( point['C'], contact, point['L'], point['X'], point['R'] )  
    else :
      for point in points_0 :
        Vertical.create ( point['N'], point['L'], point['X'], point['R'], old_Y_pad, Y_pad )  
  
    if ins_pad == pad_west[-1] :
      for point in points_1 :
        contact = Contact.create ( point['N'], point['L'], point['X'], Y_pad, point['R'], point['R'] )
        Vertical.create ( contact, point['C'], point['L'], point['X'], point['R'] )  
       
    old_Y_pad = Y_pad

  UpdateSession.close()

#######################################
def create_inst ( model, name, cell ) :

  # Error : if the cell the instance has to be instanciated in does not exist
  if not cell :
    raise ErrorMessage(2,"Cannot create instance %s : the cell does not exist."%name)
    
  # Load model in the database
  modelmastercell = CRL.AllianceFramework.get().getCell ( model, CRL.Catalog.State.Views )
    
  # Error : if the model is not found in the libraries
  if not modelmastercell :
    raise ErrorMessage(2,"Cannot create instance %s : model %s does not exist in the database."%(name,model))
    
  inst = Instance.create ( cell, name, modelmastercell )
  
  # Connection
  plugGround = inst.getPlug ( iter(modelmastercell.getGroundNets()).next() )
  plugGround.setNet ( iter(cell.getGroundNets()).next() )
  plugPower = inst.getPlug ( iter(modelmastercell.getPowerNets()).next() )
  plugPower.setNet ( iter(cell.getPowerNets()).next() )

  return inst

##########################
def place ( inst, x, y, orientation ) :
    
  # Error : if the hurricane instance does not exist
  if not inst :
    raise ErrorMessage(2,"Layout : The instance of %s has not been created."%str(inst.getName()))
 
  # Error : if the instance is already placed
  if inst.getPlacementStatus() == Instance.PlacementStatus.FIXED :
    raise ErrorMessage(2,"Placement : the instance %s is already placed."%str(inst.getName()))
  
  UpdateSession.open()
    
  ## A COMPLETER POUR FAIRE DES PLACE AVEC TOUTES LES SYMETRIES ##
  if orientation == Transformation.Orientation.MY :
    y += inst.getAbutmentBox().getHeight()
    
  inst.setTransformation  ( Transformation ( x, y, orientation ) )
  inst.setPlacementStatus ( Instance.PlacementStatus.FIXED ) 

  UpdateSession.close()

#########################################
def getStandardInstancesMasque ( cell ) :
  '''This function creates the mask of the standard cells'''

  global FREE, standard_instances_masque, nb_lignes, nb_colonnes, reference
            
  # The two dimensions of the mask 
  nb_lignes   = cell.getAbutmentBox().getHeight() / DbU_lambda(SLICE) 
  nb_colonnes = cell.getAbutmentBox().getWidth()  / DbU_lambda(PITCH) 

  # Creation of a mask named standard_instances_masque 
  standard_instances_masque = []
  standard_instances_masque = [[FREE for j in range(nb_colonnes)] for i in range(nb_lignes)]
  
  # Cover of all the instances of this model and fill in of the mask 
  reference = getAllStandardInstances ( cell )

  return reference

######################################
def getAllStandardInstances ( cell ) : 
  '''This function is used to get all the standard instances of a cell and to flatten all the standard cells''' 
  
  global reference
  
  reference = []
  
  for element in cell.getInstances():
    # FOR EACH PLACED or FIXED instance of the cell, we call getStandardInstances method
    if element.getPlacementStatus() != Instance.PlacementStatus.UNPLACED :
      reference = getStandardInstances ( cell, element, element.getTransformation(), cell.getName() )

  return reference

##########################################################
def getStandardInstances ( cell, inst, transformation, name_masterinst ) :
  '''This function covers all the instances of the model , 
  it is also used to get all the transformations of the placed instances 
  it fills the standard_instances masque and the standard_instances_list ''' 
  
  global POWER, OCCUPIED
  global standard_instances_masque, reference

  # Si l'instance est PLACED ou FIXED
  if inst.getPlacementStatus() != Instance.PlacementStatus.UNPLACED :
    name = str ( inst.getMasterCell().getLibrary().getName() )
    
    if inst.isLeaf() :
      # new abutmentbox
      newbox = transformation.getBox ( inst.getMasterCell().getAbutmentBox() )
      
      Height = newbox.getHeight()
      Width  = newbox.getWidth()
      Xmin   = newbox.getXMin()
      Ymin   = newbox.getYMin()
  
      orientation = transformation.getOrientation()
      
      if reference == [] : reference[ 0:3 ] = [ Xmin, Ymin, orientation ]
      
      # on ajoute l'inst a la liste standard_instances_list
      standard_instances_list.append ( ( Ymin, orientation, inst, str(name_masterinst) ) ) # add a tuple 
               
      nb_slices = Height / DbU_lambda(SLICE)
      nb_pitchs = Width  / DbU_lambda(PITCH)
      
      # on remplit le masque
      for i in range ( nb_slices ) :
        for j in range ( nb_pitchs ) :
          # on marque les cellules en POWER ou OCCUPIED      FIXME : pourquoi ?
          if re.search ( "powmid_x0", str ( inst.getMasterCell().getName() ) ) : 
            standard_instances_masque [ Ymin / DbU_lambda(SLICE) + i ][ Xmin / DbU_lambda(PITCH) + j ] = POWER
          else :
            standard_instances_masque [ Ymin / DbU_lambda(SLICE) + i ][ Xmin / DbU_lambda(PITCH) + j ] = OCCUPIED 
  
    # Si la masterCell n'est pas terminale, on appelle la fonction recursivement sur ses instances
    else  :
      for element in inst.getMasterCell().getInstances():
        getStandardInstances ( cell, element, transformation.getTransformation(element.getTransformation()), inst.getName() ) 

  return reference

##########
def verifyPlacement ( cell ) :
  '''This function is used to check the placement of the standard cells''' 

  global standard_instances_list
  
  # YMin of reference 
  YMin = standard_instances_list[0][0]   
   
  # orientation code of reference 
  orientation = standard_instances_list[0][1]    

  # cover all the other  
  for i in range ( 1, len ( standard_instances_list ) ) :
     element             = standard_instances_list[i]           
     element_YMin        = element[0]
     element_orientation = element[1] 

     distance            = element_YMin - YMin

     # Error :  if the orientation is different from 0, 2, 4 ,6 
     #if not element_orientation in [0,2,4,6] :
     if element_orientation in [1,3,5,7] :
       err  = "Placement of cells : please check your file of layout with DRUC."
#       err += "Error Detail :" + "\n"
#       err += "    instance " + str(element[2].getName()) + " of model " + str(element[2].getMasterCell().getName()) + "\n"
#       err += "    in cell "  + str(element[2].getCell().getName()) + "\n"
#       err += "    incoherent with :" + "\n"
#       err += "    instance " + str(standard_instances_list[0][2].getName()) + "\n"
#       err += "    of model " + str(standard_instances_list[0][2].getMasterCell().getName()) + "\n"
#       err += "    in cell "  + str(standard_instances_list[0][2].getCell().getName()) + "\n"
#       err += "    reference ymin is " + str(YMin) + " orientation " + str(orientation) + "\n"
#       err += "    element ymin is", element[0], "orientation", element_orientation + "\n"
       raise ErrorMessage(2,err)
  
     if distance < 0 : distance = -distance

     nb_case = distance / DbU_lambda ( 50 )

     # odd number 
     if nb_case % 2 :
       if ( element_orientation - orientation ) in [4,-4] :  
         err  = "Placement of cells : please check your file of layout with DRUC\n"
#         err += "Error Detail :" + "\n"
#         err += "    instance " + str(element[2].getName()) + " of model " + str(element[2].getMasterCell().getName()) + "\n"
#         err += "    in cell "  + str(element[2].getCell().getName()) + "\n"
#         err += "    incoherent with :" + "\n"
#         err += "    instance " + str(standard_instances_list[0][2].getName()) + "\n"
#         err += "    of model " + str(standard_instances_list[0][2].getMasterCell().getName()) + "\n"
#         err += "    in cell "  + str(standard_instances_list[0][2].getCell().getName()) + "\n"
#         err += "    reference ymin is " + str(YMin) + " orientation " + orientation + "\n"
#         err += "    element ymin is " + element[0] + " orientation " + element_orientation + "\n"
         raise ErrorMessage(2,err)
           
     # even number
     else :
       if ( element_orientation - orientation ) in [2,-2,6,-6] : 
         err  = "Placement of cells : please check your file of layout with DRUC\n"
#         err += "Error Detail :"
#         err += "    instance " + str(element[2].getName()) + " of model " + str(element[2].getMasterCell().getName()) + "\n"
#         err += "    in cell "  + str(element[2].getCell().getName()) + "\n"
#         err += "    incoherent with :" + "\n"
#         err += "    instance " + str(standard_instances_list[0][2].getName()) + "\n"
#         err += "    of model " + str(standard_instances_list[0][2].getMasterCell().getName()) + "\n"
#         err += "    in cell "  + str(standard_instances_list[0][2].getCell().getName()) + "\n"
#         err += "    reference ymin is " + str(YMin) + " orientation " + orientation + "\n"
#         err += "    element ymin is " + element[0] + " orientation " + element_orientation + "\n"
         raise ErrorMessage(2,err)
         
###################################
def temporarySave ( cell = None ) :

  UpdateSession.open()

  framework = CRL.AllianceFramework.get()
  
  if cell == None :
    for cell in framework.getLibrary(0).getCells():
      if str ( cell.getName() ) != "__Scratch__" :
        framework.saveCell ( cell, CRL.Catalog.State.Physical )
  else :
    framework.saveCell ( cell, CRL.Catalog.State.Physical )
  
  UpdateSession.close()

###########################
def getVddVss ( cell, y ) :
  '''This function returns a string 'vdd' or 'vss' according to the variance between y and the reference cell. y is in slices''' 

  global reference
  
  if reference == [] : raise ErrorMessage(2,"getVddVss : Reference is not token.") 
  
  # in order to know if it is vdd or vss
  distance = y - int ( DbU_getLambda ( reference[1] ) ) / 50
  
  if distance < 0 : distance = -distance

  orientation = reference[2]    

  if orientation in [0,4] :
    if distance % 2 : return "vdd"
    else            : return "vss"
  elif orientation in [2,6] :
    if distance % 2 : return "vss"  
    else            : return "vdd"
  else :
    raise ErrorMessage(2,"get_vdd_vss : Illegal orientation of reference %s.\n"%orientation)

###########################
def getPadHeight ( cell ) :
  '''This function returns the pad height if there is at least one pad'''

  padFound = 0
  
  for instance in cell.getInstances():
    if isPad ( instance ):
      pad_height = instance.getMasterCell().getAbutmentBox().getHeight()
      padFound   = 1
      break
  
  if padFound : return pad_height
  else        : raise ErrorMessage(2,"getPadHeight : No pad found.")

###################
def isPad ( ins ) :
  '''This function returns 1 if the instance is a pad'''

  if re.search ( "p.*_px", str ( ins.getMasterCell().getName() ) ) \
  or re.search ( "p.*_sp", str ( ins.getMasterCell().getName() ) ):
    return True
  else:
    return False

################################
def isInternalPowerPad ( ins ) :
  '''This function returns 1 if pad is an internal power pad'''
  if re.search ( "pvddi", str ( ins.getMasterCell().getName() ) ) :
    return 1
  else:
    return 0

################################
def isExternalPowerPad ( ins ) :
  '''This function returns 1 if pad is an external power pad'''
  if re.search ( "pvdde", str ( ins.getMasterCell().getName() ) ) :
    return 1
  else:
    return 0

#################################
def isInternalGroundPad ( ins ) :
  '''This function returns 1 if pad is an internal ground pad'''
  if re.search ( "pvssi", str ( ins.getMasterCell().getName() ) ) :
    return 1
  else:
    return 0

#################################
def isExternalGroundPad ( ins ) :
  '''This function returns 1 if pad is an external ground pad'''
  if re.search ( "pvsse", str ( ins.getMasterCell().getName() ) ) :
    return 1
  else:
    return 0

################################
def isInternalClockPad ( ins ) :
  '''This function returns 1 if pad is an internal clock pad'''
  if re.search ( "pvssick_", str ( ins.getMasterCell().getName() ) ) \
  or re.search ( "pvddick_", str ( ins.getMasterCell().getName() ) ) :
    return 1
  else:
    return 0

################################
def isExternalClockPad ( ins ) :
  '''This function returns 1 if pad is an external clock pad'''
  if re.search ( "pvsseck_", str ( ins.getMasterCell().getName() ) ) \
  or re.search ( "pvddeck_", str ( ins.getMasterCell().getName() ) ) :
    return 1
  else:
    return 0

#########################
def affichePad ( cell ) :
  global pad_north, pad_south, pad_east, pad_west
  
  print "Pads in the north are :"
  for pad in pad_north : print cell.getInstance ( pad.getName() ).getMasterCell().getName() 

  print "Pads in the south are :"
  for pad in pad_south : print cell.getInstance ( pad.getName() ).getMasterCell().getName() 

  print "Pads in the east are :"
  for pad in pad_east  : print cell.getInstance ( pad.getName() ).getMasterCell().getName() 

  print "Pads in the west are :"
  for pad in pad_west  : print cell.getInstance ( pad.getName() ).getMasterCell().getName()

############
def searchVddVss ( cell, *args ) :
  '''This function searches plots providing vdd and vss, and returns four lists'''

  global pad_north, pad_south, pad_east, pad_west
  
  # MACRO pour les directions d'access des pins  
  UNDEFINED = 0
  NORTH     = 1
  SOUTH     = 2
  EAST      = 3
  WEST      = 4

  pad_list = [pad_east, pad_west, pad_north, pad_south]   

  for i in range ( len ( args ) ) :

    for ins in pad_list[i] : 
      model          = ins.getMasterCell()
      transformation = ins.getTransformation()

      if isInternalPowerPad ( ins )  or isExternalPowerPad ( ins )  \
      or isInternalGroundPad ( ins ) or isExternalGroundPad ( ins ) :
        # on connecte les pins vddi
        for element in model.getNet("vddi").getPins():
          layer = element.getLayer()
    
          X = transformation.getX ( element.getX(), element.getY() )
          Y = transformation.getY ( element.getX(), element.getY() )
    
          args[i].append ( dict ( [['N', "vdd"], ['L',layer], ['X',X], ['Y',Y], ['C',0]] ) )

        # on connecte les pins vssi
        for element in model.getNet("vssi").getPins():
          layer = element.getLayer()
    
          X = transformation.getX ( element.getX(), element.getY() ) 
          Y = transformation.getY ( element.getX(), element.getY() )
    
          args[i].append ( dict ( [['N', "vss"], ['L',layer], ['X',X], ['Y', Y],['C',0]] ) ) 

########################
def createGrid ( my_tuple ) :

  global ck_contact_list
  global _Xmin, _Ymin, _Xmax, _Ymax
    
  net, cell = my_tuple
    
  def FindNearestPosition ( pos, poslist ) :
    bestDistance = pos - poslist[0]
    bestPos      =  poslist[0]
    
    for x in poslist[1:] :
      distance = pos - x
      if distance <= 0 :
        distance *= -1
        if bestDistance > distance : return x
      else :
        if bestDistance > distance :
          bestPos      = x
          bestDistance = pos - bestPos
    return bestPos
    
  def CreateZ ( contact1, contact2 ) :
    centerX   = (contact1.getX() + contact2.getX() ) / 2
    centerX   = centerX - (centerX % DbU_lambda(5))
    zContact1 = Contact.create ( net, via5, centerX, contact1.getY(), DbU_lambda(11), DbU_lambda(11) )
    zContact2 = Contact.create ( net, via5, centerX, contact2.getY(), DbU_lambda(11), DbU_lambda(11) )
    
    Horizontal.create ( contact1,  zContact1, metal6, contact1.getY(),  DbU_lambda(12) )
    Vertical.create   ( zContact1, zContact2, metal5, zContact1.getX(), DbU_lambda(12) )
    Horizontal.create ( zContact2, contact2,  metal6, zContact2.getY(), DbU_lambda(12) )
    
  def CreateN ( contact1, contact2 ) :
    centerY   = ( contact1.getY() + contact2.getY() ) / 2
    centerY   = centerY - ( centerY % DbU_lambda(5) )
    nContact1 = Contact.create ( net, via5, contact1.getX(), centerY, DbU_lambda(11), DbU_lambda(11) )
    nContact2 = Contact.create ( net, via5, contact2.getX(), centerY, DbU_lambda(11), DbU_lambda(11) )
    
    Vertical.create   ( contact1,  nContact1, metal5, contact1.getX(),  DbU_lambda(12) )
    Horizontal.create ( nContact1, nContact2, metal6, nContact1.getY(), DbU_lambda(12) )
    Vertical.create   ( nContact2, contact2,  metal5, nContact2.getX(), DbU_lambda(12) )
    
  def FindPositionForContact ( position, contactlist1, contactlist2 ) :
    def PositionIsInTargetRange ( position, target ) :
      return position >= ( target - DbU_lambda(10) ) and position <= ( target + DbU_lambda(10) )
    
    if contactlist1 == [] and contactlist2 == [] : return position
    
    if contactlist1 != [] and contactlist2 != [] :
      nearest1 = FindNearestPosition ( position, contactlist1 )
      nearest2 = FindNearestPosition ( position, contactlist2 )
    
      if ( not PositionIsInTargetRange(position, nearest1) ) and ( not PositionIsInTargetRange(position, nearest2) ) : return position
                                                                                                                       
      if PositionIsInTargetRange ( position, nearest1 ) :
        if nearest2 > nearest1 : return position + DbU_lambda(12)
        else                   : return position - DbU_lambda(12)
    
      if PositionIsInTargetRange ( position, nearest2 ) :
        if nearest1 > nearest2 : return position + DbU_lambda(12)
        else                   : return position - DbU_lambda(12)
    
    if contactlist1 != [] : contactlist = contactlist1
    else                  : contactlist = contactlist2
    
    nearest = FindNearestPosition ( position, contactlist )
    
    if PositionIsInTargetRange ( position, nearest ) :
      if position > nearest : return position + DbU_lambda(12)
      else                  : return position - DbU_lambda(12)
    else :
      return position
      
  #_Xmin = None
  #_Ymin = None
  #_Xmax = None
  #_Ymax = None    
  coreBox = cell.getInstance('core').getAbutmentBox()
  #print coreBox
  _Xmin = coreBox.getXMin()
  _Ymin = coreBox.getYMin()
  _Xmax = coreBox.getXMax()
  _Ymax = coreBox.getYMax()
  ck_contact_list = []  

  getNetInstances ( cell, net, Transformation ( 0, 0, Transformation.Orientation.ID ) )
   
  db   = DataBase.getDB()
  via1 = db.getTechnology().getLayer ( "VIA12" )
  via2 = db.getTechnology().getLayer ( "VIA23" )
  via3 = db.getTechnology().getLayer ( "VIA34" )
  via4 = db.getTechnology().getLayer ( "VIA45" )
  via5 = db.getTechnology().getLayer ( "VIA56" )
  metal5 = db.getTechnology().getLayer ( "METAL5"  )
  metal6 = db.getTechnology().getLayer ( "METAL6"  )
  
  gridBoundingBox = Box()

  for contact in ck_contact_list : gridBoundingBox.merge ( contact[0], contact[1] )
    
  #the Bounding Box is inflated in function of the VIA_5 rule
  gridBoundingBox.inflate ( DbU_lambda(15) )
  
  #Create the Bounding Box grid
  NEContact    = Contact.create ( net, via5, gridBoundingBox.getXMin(), gridBoundingBox.getYMax() , DbU_lambda(11), DbU_lambda(11) )
  NWContact    = Contact.create ( net, via5, gridBoundingBox.getXMax(), gridBoundingBox.getYMax() , DbU_lambda(11), DbU_lambda(11) )
  SEContact    = Contact.create ( net, via5, gridBoundingBox.getXMin(), gridBoundingBox.getYMin() , DbU_lambda(11), DbU_lambda(11) )
  SWContact    = Contact.create ( net, via5, gridBoundingBox.getXMax(), gridBoundingBox.getYMin() , DbU_lambda(11), DbU_lambda(11) )
  northSegment = Segment ( NEContact, NWContact, metal6, DbU_lambda(12) )
  southSegment = Segment ( SEContact, SWContact, metal6, DbU_lambda(12) )
  eastSegment  = Segment ( NEContact, SEContact, metal5, DbU_lambda(12) )
  westSegment  = Segment ( NWContact, SWContact, metal5, DbU_lambda(12) )

  northContacts = []
  southContacts = []
  eastContacts  = []
  westContacts  = []
  
  #connect the pins to the grid
  plugList = []
  #Fill a list with the plugs ... we are going to modify the underlying collection ;)
  for plug in net.getPlugs():
    plugList.append ( plug )
  
  for plug in plugList:
    instance = plug.getInstance()
    if isPad ( instance ) : 
      # Connect this plug to the grid ...
      masterNet      = plug.getMasterNet()
      transformation = instance.getTransformation()
    
      for comp in NetExternalComponents.get(masterNet):
        x    = transformation.getX ( comp.getX(), comp.getY() ) 
        y    = transformation.getY ( comp.getX(), comp.getY() ) 
        #layer   = element.getLayer ()
        #if re.search ( "METAL2", str ( layer ) ) \
        #  and ( ( element.getY() - element.getHalfHeight() ) < pad_inst.getMasterCell().getAbutmentBox().getYMin() ) :

        if x >= gridBoundingBox.getXMin() and x <= gridBoundingBox.getXMax() :
          layer = metal5
          if y < gridBoundingBox.getYMin() :
            if x == gridBoundingBox.getXMin() :
              gridContact = NWContact
            elif x == gridBoundingBox.getXMax() :
              gridContact = NEContact
            else :
              gridContact = Contact.create(southSegment, via5, x, 0, DbU_lambda(11), DbU_lambda(11))
              southContacts.append(x)
              
          elif y > gridBoundingBox.getYMax() :
            if x == gridBoundingBox.getXMin() :
              gridContact = SWContact
            elif x == gridBoundingBox.getXMax() :
              gridBoundingBox = SEContact
            else :
              gridContact = Contact.create(northSegment, via5, x, 0, DbU_lambda(11), DbU_lambda(11))
              northContacts.append(x)
          else : 
            raise ErrorMessage(2,"RouteCK : bad pad placement.") 
            
        elif y >= gridBoundingBox.getYMin() and y <= gridBoundingBox.getYMax() :
          layer = metal6
          if x < gridBoundingBox.getXMin() :
            if y == gridBoundingBox.getYMin() :
              gridContact = SWContact
            elif y == gridBoundingBox.getYMax() :
              gridContact = NWContact
            else :
              gridContact = Contact.create(eastSegment, via5, 0, y, DbU_lambda(11), DbU_lambda(11))
              eastContacts.append(y)
          elif x > gridBoundingBox.getXMax() :
            if y == gridBoundingBox.getYMin() :
              gridContact = SEContact
            elif y == gridBoundingBox.getYMax() :
              gridContact = SWContact
            else :
              gridContact = Contact.create(westSegment, via5, 0, y, DbU_lambda(11), DbU_lambda(11))
              westContacts.append(y)
          else : 
            raise ErrorMessage(2,"RouteCK : bad pad placement.") 
            
        else :
          message = [ "pyRouteCk: The pads <%s> must be in direct regard of the clock grid" % str(instance.getName())
                    , "Clock grid is %s" % str(gridBoundingBox)
                    ]
          raise ErrorMessage(2,message)

        compContact = Contact.create ( net, via5, x, y, DbU_lambda(11), DbU_lambda(11) )
    
        Segment ( compContact, gridContact, layer , DbU_lambda(12) )
    
        break # just one component ...
        

  # create the internal grid 200x200
  nbVTracks     = gridBoundingBox.getWidth()  / DbU_lambda(200) 
  widthVTracks  = gridBoundingBox.getWidth()  / nbVTracks
  nbHTracks     = gridBoundingBox.getHeight() / DbU_lambda(200)
  heightHTracks = gridBoundingBox.getHeight() / nbHTracks
  via12Side     = DbU.fromLambda(1.0)
  via23Side     = DbU.fromLambda(1.0)
  via34Side     = DbU.fromLambda(1.0)
  via45Side     = DbU.fromLambda(1.0)
  via56Side     = DbU.fromLambda(1.0)

  xList = []
  yList = []
  for i in range ( 1, nbVTracks ) :
    x = gridBoundingBox.getXMin() + i * widthVTracks 
    x = x - (x % DbU_lambda(5))
    x = FindPositionForContact(x, northContacts, southContacts)
    
    contact1 = Contact.create ( southSegment, via5, x, 0, DbU_lambda(11), DbU_lambda(11))
    contact2 = Contact.create ( northSegment, via5, x, 0, DbU_lambda(11), DbU_lambda(11))
    
    Segment ( contact1, contact2, metal5, DbU_lambda(12) )
    
    xList.append ( x )
    
  for i in range ( 1, nbHTracks ) :
    y = gridBoundingBox.getYMin() + i * heightHTracks 
    y = y - ( y % DbU_lambda(5) )
    y = FindPositionForContact ( y, eastContacts, westContacts )
    
    contact1 = Contact.create ( westSegment, via5, 0, y, DbU_lambda(11), DbU_lambda(11) )
    contact2 = Contact.create ( eastSegment, via5, 0, y, DbU_lambda(11), DbU_lambda(11) )
    
    horizontal = Segment ( contact1, contact2, metal6, DbU_lambda(12) )
    
    yList.append ( y )

    for x in xList : Contact.create ( horizontal, via5, x, 0, DbU_lambda(11), DbU_lambda(11) )
          
  # Connection to the grid
  # Cette liste contient les contacts qui sont deja crees 
  ck_contact_list_created = []

  # Now connect all the internal contacts to the grid
  for contact in ck_contact_list :
    xContact = contact[0]
    yContact = contact[1]
    plugContact = Contact.create ( net, via1 , xContact, yContact, via12Side, via12Side ) 
    #find the closest x,y on grid
    xList.insert ( 0, gridBoundingBox.getXMin() )
    yList.insert ( 0, gridBoundingBox.getYMin() )
    xList.append ( gridBoundingBox.getXMax() )
    yList.append ( gridBoundingBox.getYMax() )

    xTarget = FindNearestPosition ( xContact, xList )
    yTarget = FindNearestPosition ( yContact, yList )

    xDistance = abs ( xTarget - xContact )
    yDistance = abs ( yTarget - yContact )

    Contact.create(net, via2, xContact, yContact, via23Side, via23Side ) 
    Contact.create(net, via3, xContact, yContact, via34Side, via34Side ) 
    Contact.create(net, via4, xContact, yContact, via45Side, via45Side )

    if xDistance != 0 or yDistance != 0 :
      if ( xDistance <= yDistance + DbU_lambda(10) ):  # test pour faire un horizontal
        if xDistance != 0 :
          if abs(xDistance) <= DbU_lambda(3) :
            gridContact = Contact.create ( net, metal5, xTarget, yContact, via56Side, via56Side ) 
            layer = metal5
          else :
            Contact.create ( net, via5, xContact, yContact, via56Side, via56Side )
            gridContact = Contact.create ( net, via5, xTarget, yContact, via56Side, via56Side ) 
            layer = metal6
          Horizontal.create( gridContact, plugContact, layer, gridContact.getY(), DbU_lambda(2) )
        else :
          gridContact = Contact.create ( net, via5, xTarget, yContact, via56Side, via56Side ) 
      else:
        if yDistance != 0 :
          if abs(yDistance) <= DbU_lambda(3) :
            layer = metal6
            gridContact = Contact.create ( net, metal6, xContact, yTarget, via56Side, via56Side ) 
            Contact.create ( net, via5, xContact, yContact, via56Side, via56Side ) 
          else :
            gridContact = Contact.create ( net, via5, xContact, yTarget, via56Side, via56Side ) 
            layer = metal5
          Vertical.create ( gridContact, plugContact, layer, gridContact.getX(), DbU_lambda(2) )
        else :
          gridContact = Contact.create ( net, via5, xContact, yTarget, via56Side, via56Side ) 

  del _Xmin
  del _Ymin 
  del _Xmax 
  del _Ymax   
  del ck_contact_list

############################################
def getNetInstances ( cell, net, transformation) :
  '''This function is used to cover instances with standard model, which are connected to "net"
  transformation is the transformation of the instance which model contains this net''' 

  global ck_contact_list_to_create, ck_contact_list
  global _Xmin, _Ymin, _Xmax, _Ymax
  
  for plug in net.getPlugs():   # parcourir les plugs de ce net 
    ins  = plug.getInstance()
    
    # Si c est une instance de type leaf 
    if ins.isLeaf() :
      if ins.getPlacementStatus() == Instance.PlacementStatus.UNPLACED :
        raise ErrorMessage(2,"getNetInstances : instance %s is unplaced" % str(ins.getName()))
      else :
        if not isPad ( ins ) :
          # get transformation final de cette instance 
          ins_transformation = transformation.getTransformation ( ins.getTransformation() )

          nbSeg = 0
          for segment in plug.getMasterNet().getSegments():
            layer = segment.getLayer() 
            if NetExternalComponents.isExternal(segment) \
               and (  re.search ( "METAL1", layer.getName() ) \
                   or re.search ( "METAL2", layer.getName() ) ):
              # avoir x ,y de contact a placer
              _x = ins_transformation.getX ( segment.getSourceX(), segment.getSourceY() )
              _y = ins_transformation.getY ( segment.getSourceX(), segment.getSourceY() )
               
              #print ins, ":", segment, ",", nbSeg, ",", _x, ",", _y
              nbSeg += 1
              ck_contact_list_to_create.append (  (_x, _y) ) 
              
              ck_contact_list.append ( (_x, _y) )           
 
              newbox = ins_transformation.getBox ( ins.getMasterCell().getAbutmentBox() )              # get new box 
              Height = newbox.getHeight()
              Width  = newbox.getWidth()
              Xmin   = newbox.getXMin()
              Ymin   = newbox.getYMin()
              Xmax   = newbox.getXMax()
              Ymax   = newbox.getYMax()
              #print " placer contact in ", _x, " ",  _y  , " in the net ", plug.getMasterNet().getName() ,
              #print " of instance ", plug.getInstance().getName() , " in ", Xmin , " ", Ymin , 
              #print " of model ", plug.getInstance().getMasterCell().getName(), "\n"

              # Positionner la grille
              if ( Xmin < _Xmin ) or ( _Xmin == None ) : _Xmin = Xmin
              if ( Ymin < _Ymin ) or ( _Ymin == None ) : _Ymin = Ymin
              if ( Xmax > _Xmax ) or ( _Xmax == None ) : _Xmax = Xmax
              if ( Ymax > _Ymax ) or ( _Ymax == None ) : _Ymax = Ymax
              break

          if not nbSeg :
            raise ErrorMessage(2,"getNetInstances : net %s in model %s does not have a segment\n" % ( str ( plug.getMasterNet().getName()), str(ins.getMasterCell().getName()) ) )

          if ( not ck_contact_list ) : print "Error in function getNetInstances : no segment found"
              
    else :
      if ins.getPlacementStatus() == Instance.PlacementStatus.UNPLACED :
        raise ErrorMessage(2,"getNetInstances : instance %s is unplaced" % str(ins.getName()))
      else :        
        getNetInstances ( cell, plug.getMasterNet(), transformation.getTransformation ( ins.getTransformation () ))
    
############################
def getNonCLayer ( layer ) :
  '''This function returns the nonC layer corresponding to the one given as argument'''
  
  metal1 = DataBase.getDB ().getTechnology ().getLayer ( "METAL1" )
  metal2 = DataBase.getDB ().getTechnology ().getLayer ( "METAL2" )
  metal3 = DataBase.getDB ().getTechnology ().getLayer ( "METAL3" )
  metal4 = DataBase.getDB ().getTechnology ().getLayer ( "METAL4" )
  metal5 = DataBase.getDB ().getTechnology ().getLayer ( "METAL5" )
  metal6 = DataBase.getDB ().getTechnology ().getLayer ( "METAL6" )

  if re.search ( "CMETAL1", str ( layer.getName() ) ) : return metal1
  if re.search ( "CMETAL2", str ( layer.getName() ) ) : return metal2
  if re.search ( "CMETAL3", str ( layer.getName() ) ) : return metal3
  if re.search ( "CMETAL4", str ( layer.getName() ) ) : return metal4
  if re.search ( "CMETAL5", str ( layer.getName() ) ) : return metal5
  if re.search ( "CMETAL6", str ( layer.getName() ) ) : return metal6
  
  return layer    

######################################################
def Segment ( component1, component2, layer, width ) :
  '''This function creates a segment linking component1 and component2'''
  
  if   component1.getX() == component2.getX() : return Vertical.create   ( component1, component2, layer, component1.getX(), width )
  elif component1.getY() == component2.getY() : return Horizontal.create ( component1, component2, layer, component1.getY(), width )
  else:
    raise ErrorMessage(2,"Segment : the components must be horizontaly or verticaly aligned.")
