# -*- explicit-buffer-name: "Configuration.py<cumulus/src/plugins/chip>" -*-
#
# This file is part of the Coriolis Software.
# Copyright (c) UPMC 2014-2015, All Rights Reserved
#
# +-----------------------------------------------------------------+
# |                   C O R I O L I S                               |
# |      C u m u l u s  -  P y t h o n   T o o l s                  |
# |                                                                 |
# |  Author      :                    Jean-Paul CHAPUT              |
# |  E-mail      :       Jean-Paul.Chaput@asim.lip6.fr              |
# | =============================================================== |
# |  Python      :       "./plugins/chip/Configuration.py"          |
# +-----------------------------------------------------------------+


import sys
import os.path
import Cfg
from   Hurricane import Breakpoint
from   Hurricane import DbU
from   Hurricane import Box
from   Hurricane import Transformation
from   Hurricane import Box
from   Hurricane import Path
from   Hurricane import Occurrence
from   Hurricane import Net
from   Hurricane import RoutingPad
from   Hurricane import Contact
from   Hurricane import Horizontal
from   Hurricane import Vertical
from   Hurricane import Plug
from   Hurricane import Instance
import CRL
from   CRL import RoutingLayerGauge
from   helpers   import trace
from   helpers   import ErrorMessage
from   helpers   import WarningMessage
from   plugins   import getParameter


def breakpoint ( editor, level, message ):
  if editor:
    editor.fit()
    editor.refresh()
    Breakpoint.stop( level, message )
  return


def getPlugByName ( instance, netName ):
  masterCell = instance.getMasterCell()
  masterNet  = masterCell.getNet( netName )
  if masterNet:
    return instance.getPlug( masterNet )
  return None


def getPlugByNet ( instance, net ):
  for plug in net.getPlugs():
    if plug.getInstance() == instance:
      return plug
  return None


def getRpBb ( instance, netName ):
  bb = Box()
  for net in instance.getMasterCell().getNets():
    if net.isExternal() and net.getName() == netName:
      for component in net.getExternalComponents():
        if isinstance(component,Vertical):
          bb = component.getBoundingBox()
  instance.getTransformation().applyOn( bb )
  return bb


def showNet ( cell, netName ):
  net = cell.getNet(netName)
  if not net:
    print ErrorMessage( 3, 'Cell %s doesn\'t have net %s' % (cell.getName(),netName) ) 
    return
  
  print 'Components of', netName
  for component in net.getComponents():
    print '| ', component, component.getBoundingBox() 
  return


def destroyNetComponents ( net ):
 # 1. We cannot iterate over a Hurricane Collection if we are deleting
 #    some of it's elements at the same time (could be improved as it
 #    is an intrusive map.
 # 2. Lazy programming: as we don't know the destruction order, some
 #    components can be deleted by (previous) others so we can endup
 #    on dangling Python proxy which send an exception that we catch.
 # 3. Plugs are not destroyed (they cannot as they are part of the
 #    Instance). They stay connected to the net.
  toDestroy = []
  for component in net.getComponents():
    if not isinstance(component,Plug):
      toDestroy.append( component )
  for component in toDestroy:
    try:
      component.destroy()
    except:
     pass
  return


# -------------------------------------------------------------------
# Class  :  "Configuration.GaugeConf".

class GaugeConf ( object ):

    HAccess         = 0x0001
    OffsetRight1    = 0x0002
    OffsetTop1      = 0x0004
    OffsetBottom1   = 0x0008
    DeepDepth       = 0x0010
    UseContactWidth = 0x0020
    ExpandWidth     = 0x0040

    def __init__ ( self ):
      self._cellGauge     = None
      self._routingGauge  = None
      self._topLayerDepth = 0
      self._plugToRp      = { }

      self._loadRoutingGauge()
      return

    def getSliceHeight ( self ): return self._cellGauge.getSliceHeight()
    def getSliceStep   ( self ): return self._cellGauge.getSliceStep()

    def _loadRoutingGauge ( self ):
      self._cellGauge    = CRL.AllianceFramework.get().getCellGauge()
      self._routingGauge = CRL.AllianceFramework.get().getRoutingGauge()

      topLayer = Cfg.getParamString('katabatic.topRoutingLayer').asString()

      self._topLayerDepth = 0
      for layerGauge in self._routingGauge.getLayerGauges():
        if layerGauge.getLayer().getName() == topLayer:
          self._topLayerDepth = layerGauge.getDepth()
          break
      if not self._topLayerDepth:
        print WarningMessage( 'Gauge top layer not defined, using top of gauge (%d).' \
                              % self._routingGauge.getDepth() )
        self._topLayerDepth = self._routingGauge.getDepth()

      self._horizontalDepth     = -1
      self._verticalDepth       = -1
      self._horizontalDeepDepth = -1
      self._verticalDeepDepth   = -1
      for depth in range(0,self._topLayerDepth+1):
        if self._routingGauge.getLayerGauge(depth).getType() == RoutingLayerGauge.PinOnly:
          continue
        if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal:
          if self._horizontalDeepDepth < 0:
            self._horizontalDeepDepth = depth
          self._horizontalDepth = depth
        if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Vertical:
          if self._verticalDeepDepth < 0:
            self._verticalDeepDepth = depth
          self._verticalDepth = depth
      return

    def _createContact ( self, net, x, y, flags ):
      if flags & GaugeConf.DeepDepth: depth = self._horizontalDeepDepth
      else:                           depth = self._horizontalDepth

      return Contact.create( net
                           , self._routingGauge.getContactLayer(depth)
                           , x, y
                           , self._routingGauge.getLayerGauge(depth).getViaWidth()
                           , self._routingGauge.getLayerGauge(depth).getViaWidth()
                           )

    def _createHorizontal ( self, source, target, y, flags ):
      if flags & GaugeConf.DeepDepth: depth = self._horizontalDeepDepth
      else:                           depth = self._horizontalDepth

      layer = self._routingGauge.getRoutingLayer(depth)

      if flags & GaugeConf.UseContactWidth: width  = source.getBoundingBox(layer.getBasicLayer()).getHeight()
      else:                                 width  = self._routingGauge.getLayerGauge(depth).getWireWidth()
      if flags & GaugeConf.ExpandWidth:     width += DbU.fromLambda( 1.0 )

      segment = Horizontal.create( source, target, layer, y, width )
      trace( 550, segment )
      return segment
  
    def _createVertical ( self, source, target, x, flags ):
      if flags & GaugeConf.DeepDepth: depth = self._verticalDeepDepth
      else:                           depth = self._verticalDepth

      layer = self._routingGauge.getRoutingLayer(depth)

      if flags & GaugeConf.UseContactWidth: width  = source.getBoundingBox(layer.getBasicLayer()).getWidth()
      else:                                 width  = self._routingGauge.getLayerGauge(depth).getWireWidth()
      if flags & GaugeConf.ExpandWidth:     width += DbU.fromLambda( 1.0 )

      segment = Vertical.create( source, target, layer, x, width )
      trace( 550, segment )
      return segment
  
    def _rpAccess ( self, rp, flags ):
      trace( 550, ',+', '\t_rpAccess() %s\n' % str(rp) )

      if flags & GaugeConf.DeepDepth:
        hdepth = self._horizontalDeepDepth
        vdepth = self._verticalDeepDepth
      else:
        hdepth = self._horizontalDepth
        vdepth = self._verticalDepth

      hpitch    = self._routingGauge.getLayerGauge(hdepth).getPitch()
      hoffset   = self._routingGauge.getLayerGauge(hdepth).getOffset()
      contact1  = Contact.create( rp, self._routingGauge.getContactLayer(0), 0, 0 )
      midSliceY = contact1.getY() - (contact1.getY() % self._cellGauge.getSliceHeight()) \
                                                     + self._cellGauge.getSliceHeight() / 2
      midTrackY = midSliceY - ((midSliceY - hoffset) % hpitch)
      dy        = midSliceY - contact1.getY()
  
      if flags & GaugeConf.OffsetBottom1: dy += hpitch
      if flags & GaugeConf.OffsetTop1:    dy -= hpitch
      contact1.setDy( dy )

      trace( 550, contact1 )
  
      if flags & GaugeConf.HAccess: stopDepth = hdepth
      else:                         stopDepth = vdepth
  
      for depth in range(1,stopDepth):
        xoffset = 0
        if flags & GaugeConf.OffsetRight1 and depth == 1:
          xoffset = self._routingGauge.getLayerGauge(depth+1).getPitch()
        contact2 = Contact.create( rp.getNet()
                                 , self._routingGauge.getContactLayer(depth)
                                 , contact1.getX() + xoffset
                                 , contact1.getY()
                                 , self._routingGauge.getLayerGauge(depth).getViaWidth()
                                 , self._routingGauge.getLayerGauge(depth).getViaWidth()
                                 )
        trace( 550, contact2 )
        if self._routingGauge.getLayerGauge(depth).getDirection() == RoutingLayerGauge.Horizontal:
          segment = Horizontal.create( contact1
                                     , contact2
                                     , self._routingGauge.getRoutingLayer(depth)
                                     , contact1.getY()
                                     , self._routingGauge.getLayerGauge(depth).getWireWidth()
                                     )
          trace( 550, segment )
        else:
          segment = Vertical.create( contact1
                                   , contact2
                                   , self._routingGauge.getRoutingLayer(depth)
                                   , contact1.getX()
                                   , self._routingGauge.getLayerGauge(depth).getWireWidth()
                                   )
          trace( 550, segment )
        contact1 = contact2
  
      trace( 550, '-' )
      return contact1
  
    def _rpByOccurrence ( self, occurrence, net ):
      plug = occurrence.getEntity()
      if self._plugToRp.has_key(plug):
        rp = self._plugToRp[plug]
      else:
        rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea )
        self._plugToRp[plug] = rp
  
      return rp
  
    def _rpAccessByOccurrence ( self, occurrence, net, flags ):
      plug = occurrence.getEntity()
      if self._plugToRp.has_key(plug):
        rp = self._plugToRp[plug]
      else:
        rp = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea )
        self._plugToRp[plug] = rp
  
      return self._rpAccess( self._rpByOccurrence(occurrence,net), flags )
  
    def _rpByPlug ( self, plug, net ):
      if self._plugToRp.has_key(plug):
        rp = self._plugToRp[plug]
      else:
        occurrence = Occurrence( plug, Path(net.getCell(),'') )
        rp         = RoutingPad.create( net, occurrence, RoutingPad.BiggestArea )
        self._plugToRp[plug] = rp
  
      return rp
  
    def _rpByPlugName ( self, instance, plugName, net ):
      return self._rpByPlug( getPlugByName(instance,plugName), net )
  
    def _rpAccessByPlug ( self, plug, net, flags ):
      return self._rpAccess( self._rpByPlug(plug,net), flags )
  
    def _rpAccessByPlugName ( self, instance, plugName, net, flags=0 ):
      return self._rpAccess( self._rpByPlugName(instance,plugName,net), flags )


# -------------------------------------------------------------------
# Class  :  "Configuration.GaugeConfWrapper".

class GaugeConfWrapper ( object ):

    def __init__ ( self, conf ):
     #print id(conf), type(conf)
     #if not isinstance(conf,GaugeConf):
     #  raise ErrorMessage( 1, 'Attempt to create a GaugeConfWrapper() from non-GaugeConf object.' )
      self._gaugeConf = conf
      return

    @property
    def gaugeConf ( self ): return self._gaugeConf

    @property
    def routingGauge        ( self ): return self._gaugeConf._routingGauge
    @property               
    def topLayerDepth       ( self ): return self._gaugeConf._topLayerDepth
    @property               
    def horizontalDepth     ( self ): return self._gaugeConf._horizontalDepth
    @property               
    def verticalDepth       ( self ): return self._gaugeConf._verticalDepth
    @property
    def horizontalDeepDepth ( self ): return self._gaugeConf._horizontalDeepDepth
    @property
    def verticalDeepDepth   ( self ): return self._gaugeConf._verticalDeepDepth

    def loadRoutingGauge ( self ): self._gaugeConf._loadRoutingGauge()

    def rpByOccurrence ( self, occurrence, net ):
      return self._gaugeConf._rpByOccurrence ( occurrence, net )

    def rpByPlugName ( self, instance, plugName, net ):
      return self._gaugeConf._rpByPlugName ( instance, plugName, net )

    def rpAccess ( self, rp, flags=0 ):
      return self._gaugeConf._rpAccess( rp, flags )

    def rpAccessByOccurrence ( self, occurrence, net, flags=0 ):
      return self.gaugeConf._rpAccessByOccurrence ( occurrence, net, flags )

    def rpAccessByPlugName ( self, instance, plugName, net, flags=0 ):
      return self._gaugeConf._rpAccessByPlugName( instance, plugName, net, flags )

    def createContact ( self, net, x, y, flags=0 ):
      return self._gaugeConf._createContact( net, x, y, flags )

    def createHorizontal ( self, source, target, y, flags=0 ):
      return self._gaugeConf._createHorizontal( source, target, y, flags )

    def createVertical ( self, source, target, x, flags=0 ):
      return self._gaugeConf._createVertical( source, target, x, flags )


# -------------------------------------------------------------------
# Class  :  "Configuration.ChipConf".

class ChipConf ( object ):

    @staticmethod
    def _readChipSize( chipConfigDict ):
      if not chipConfigDict.has_key('chip.size'): return Box()
      chipSize = chipConfigDict['chip.size']
      if not isinstance(chipSize,tuple):
        print ErrorMessage( 1, 'The Chip size parameter is *not* a tuple.' )
        return Box()
      if len(chipSize) != 2:
        print ErrorMessage( 1, 'The Chip size parameter is *not* a tuple of exactly two items.' )
        return Box()
      return Box( 0, 0, DbU.fromLambda(chipSize[0]), DbU.fromLambda(chipSize[1]) )


    @staticmethod
    def _readCoreSize( chipConfigDict ):
      if not chipConfigDict.has_key('core.size'):
        print ErrorMessage( 1, 'The Core size parameter is missing.' )
        return Box()
      coreSize = chipConfigDict['core.size']
      if not isinstance(coreSize,tuple):
        print ErrorMessage( 1, 'The Core size parameter is *not* a tuple.' )
        return Box()
      if len(coreSize) != 2:
        print ErrorMessage( 1, 'The Core size parameter is *not* a tuple of exactly two items.' )
        return Box()
      return Box( 0, 0, DbU.fromLambda(coreSize[0]), DbU.fromLambda(coreSize[1]) )


    @staticmethod
    def _readClockTree( chipConfigDict ):
      useClockTree = False
      if chipConfigDict.has_key('chip.clockTree'):
        if chipConfigDict['chip.clockTree']:
          useClockTree = True
      return useClockTree


    def _readPads ( self, chipConfigDict, keyword ):
      if not chipConfigDict.has_key(keyword): return []
      padNameList = chipConfigDict[keyword]
      if not isinstance(padNameList,list):
          print ErrorMessage( 1, 'The "%s" entry is not a list.' )
          return []

      af      = CRL.AllianceFramework.get()
      padList = []
      for i in range(len(padNameList)):
        if not isinstance(padNameList[i],str):
          print ErrorMessage( 1, 'The element [%d] of list %s is *not* a string (skipped).'
                                  % (i,keyword) )
          continue

        instance = self._cell.getInstance( padNameList[i] )
        if not instance:
          print ErrorMessage( 1, 'The pad [%d] (%s) of list %s do not exists in netlist (skipped).'
                                  % (i,padNameList[i],keyword) )
          continue

        if (not af.isPad(instance.getMasterCell().getName())):
          print ErrorMessage( 1, 'The pad [%d] (%s) of list %s is not an instance of a pad cell (skipped).'
                                  % (i,padNameList[i],keyword) )
          continue

        padList.append( instance )

        if not self._clockPad and instance.getMasterCell().getName() == self._pckName:
          self._clockPad = instance

        if not self._powerPad and instance.getMasterCell().getName() == self._pvddickName:
          self._powerPad = instance
      
      return padList


    def _guessGlobalNet ( self, name, net ):
      if name == self._vddeName:     self._vdde = net
      if name == self._vddiName:     self._vddi = net
      if name == self._vsseName:     self._vsse = net
      if name == self._vssiName:     self._vssi = net
      if name == self._ckiName:      self._cki  = net
      if name == self._ckoName:      self._cko  = net
      if name == self._ckName:       self._ck   = net
      if name == self._blockageName: self._blockageNet = net
      return


    def __init__ ( self, chipConfigDict, cell ):
      if not isinstance(chipConfigDict,dict):
          raise ErrorMessage( 1, 'The "chip" variable is not a dictionnary.' )

      self._validated     = True
      self._cell          = cell
     # Block Corona parameters.
      self._railsNb       = getParameter('chip','chip.block.rails.count').asInt()
      self._hRailWidth    = DbU.fromLambda( getParameter('chip','chip.block.rails.hWidth'  ).asInt() )
      self._vRailWidth    = DbU.fromLambda( getParameter('chip','chip.block.rails.vWidth'  ).asInt() )
      self._hRailSpace    = DbU.fromLambda( getParameter('chip','chip.block.rails.hSpacing').asInt() )
      self._vRailSpace    = DbU.fromLambda( getParameter('chip','chip.block.rails.vSpacing').asInt() )
     # Global Pad names.
      self._pckName       = getParameter('chip', 'chip.pad.pck'    ).asString()
      self._pvddickName   = getParameter('chip', 'chip.pad.pvddick').asString()
      self._pvssickName   = getParameter('chip', 'chip.pad.pvssick').asString()
      self._pvddeckName   = getParameter('chip', 'chip.pad.pvddeck').asString()
      self._pvsseckName   = getParameter('chip', 'chip.pad.pvsseck').asString()
     # Global Net names.
      self._vddeName      = "vdde"
      self._vddiName      = "vddi"
      self._vsseName      = "vsse"
      self._vssiName      = "vssi"
      self._ckiName       = "ck"
      self._ckoName       = "cko"
      self._ckName        = "pad"
      self._blockageName  = "blockagenet"
     # Global Nets.  
      self._vdde          = None
      self._vddi          = None
      self._vsse          = None
      self._vssi          = None
      self._cki           = None
      self._cko           = None
      self._ck            = None
      self._blockageNet   = None
                          
      self._clockPad      = None
      self._powerPad      = None
      self._cores         = []
      self._southPads     = self._readPads( chipConfigDict, 'pads.south' )
      self._northPads     = self._readPads( chipConfigDict, 'pads.north' )
      self._eastPads      = self._readPads( chipConfigDict, 'pads.east'  )
      self._westPads      = self._readPads( chipConfigDict, 'pads.west'  )
      self._coreSize      = ChipConf._readCoreSize( chipConfigDict )
      self._chipSize      = ChipConf._readChipSize( chipConfigDict )
      self._padWidth      = 0
      self._padHeight     = 0
      self._useClockTree  = ChipConf._readClockTree( chipConfigDict )

      minHCorona          = self._railsNb*(self._hRailWidth + self._hRailSpace) + self._hRailSpace
      minVCorona          = self._railsNb*(self._vRailWidth + self._vRailSpace) + self._vRailSpace
      if minHCorona > minVCorona: self._minCorona = minHCorona*2
      else:                       self._minCorona = minVCorona*2

      self.checkPads()
      self.computeChipSize()
      self.findPowerAndClockNets()
      return


    def checkPads ( self ):
      af       = CRL.AllianceFramework.get()
      cellPads = []
      for instance in self._cell.getInstances():
        if (af.isPad(instance.getMasterCell().getName())):
          cellPads.append( instance )
        else:
          self._cores.append( instance )
            
      for pad in cellPads:
        if pad in self._southPads: continue
        if pad in self._northPads: continue
        if pad in self._eastPads:  continue
        if pad in self._westPads:  continue
        print ErrorMessage( 1, 'Pad "%s" is not on any side (N/S/E/W).' % pad.getName() )
        self._validated = False

      if len(self._cores) < 1:
        print ErrorMessage( 1, 'Chip "%s" doesn\'t seems to have a core.' % self._cell.getName() )
        self._validated = False

      if len(self._cores) > 1:
        message = [ 'Chip "%s" have more than one core:' % self._cell.getName() ]
        for i in range(len(self._cores)):
          message.append( '%4d: %s' % (i,self._cores[i].getName()) )
        print ErrorMessage( 1, message )
        self._validated = False

      return


    def findPowerAndClockNets ( self ):
      if self._powerPad:
        for plug in self._powerPad.getPlugs():
          masterNet = plug.getMasterNet()
          netType   = masterNet.getType()
          if     netType != Net.Type.POWER \
             and netType != Net.Type.GROUND \
             and netType != Net.Type.CLOCK:
            continue

          net = plug.getNet()
          if not net:
            net = self._cell.getNet( masterNet.getName() )
            if not net:
              print ErrorMessage( 1, 'Missing global net <%s> at chip level.' % masterNet.getName() )
              self._validated = False
              continue
          self._guessGlobalNet( masterNet.getName(), net )

      if self._clockPad:
        for plug in self._clockPad.getPlugs():
          masterNet = plug.getMasterNet()
          netType   = masterNet.getType()
          net       = plug.getNet()

          if not net:
            net = self._cell.getNet( masterNet.getName() )
            if not net:
              print ErrorMessage( 1, 'Missing global net <%s> at chip level.' % masterNet.getName() )
              self._validated = False
              continue

          if masterNet.getName() == self._ckName:
            self._guessGlobalNet( masterNet.getName(), net )

      for netData in ( (self._vddeName,self._vdde,'pad ring external power')
                     , (self._vddiName,self._vddi,'pad ring internal power')
                     , (self._vsseName,self._vsse,'pad ring external ground')
                     , (self._vssiName,self._vssi,'pad ring external ground')
                     , (self._ckiName ,self._cki ,'pad ring inner clock')
                     , (self._ckoName ,self._cko ,'core clock')
                     , (self._ckName  ,self._ck  ,'external chip clock')
                     ):
        if not netData[1]:
          print ErrorMessage( 1, 'Missing global net <%s> (%s) at chip level.' % (netData[0],netData[2]) )
          self._validated = False

      self._blockageNet = self._cell.getNet(self._blockageName)
      if not self._blockageNet:
        self._blockageNet = Net.create( self._cell, self._blockageName )
      return


    def computeChipSize ( self ):
      if not self._clockPad:
        print ErrorMessage( 1, 'There must be at least one pad of model "%s" to be used as reference.' \
                               % self._pckName )
        self._validated = False
        return False

      self._padHeight = self._clockPad.getMasterCell().getAbutmentBox().getHeight()
      self._padWidth  = self._clockPad.getMasterCell().getAbutmentBox().getWidth()

      if not self._chipSize.isEmpty(): return
      
      horizontalPads = max( len(self._southPads), len(self._northPads) )
      verticalPads   = max( len(self._eastPads ), len(self._westPads ) )
      self._chipSize  = Box( 0
                           , 0
                           , self._padWidth * horizontalPads + 2*self._padHeight
                           , self._padWidth * verticalPads   + 2*self._padHeight
                           )
      return

    def getSpecialNetRoot ( self, net ):
      if net.getName() == self._vddeName:  return self._vdde
      if net.getName() == self._vsseName:  return self._vsse
      if net.getType() == Net.Type.POWER:  return self._vddi
      if net.getType() == Net.Type.GROUND: return self._vssi
      return None


# -------------------------------------------------------------------
# Class  :  "Configuration.ChipConfWrapper".

class ChipConfWrapper ( GaugeConfWrapper ):

    def __init__ ( self, gaugeConf, chipConf ):
      GaugeConfWrapper.__init__( self, gaugeConf )

     #if not isinstance(chipConf,ChipConf):
     #  raise ErrorMessage( 1, 'Attempt to create a ChipConfWrapper() from non-ChipConf object.' )
      self._chipConf = chipConf
      return

    def isValid ( self ): return self._chipConf._validated

    @property
    def chipConf ( self ): return self._chipConf

    def getSliceHeight ( self ): return self._gaugeConf.getSliceHeight()
    def getSliceStep   ( self ): return self._gaugeConf.getSliceStep()

    @property
    def cell ( self ): return self._chipConf._cell

    # Global Pad names.
    @property
    def pvddeckName ( self ): return self._chipConf._pvddeckName
    @property
    def pvsseckName ( self ): return self._chipConf._pvsseckName
    @property
    def pvddickName ( self ): return self._chipConf._pvddickName
    @property
    def pvssickName ( self ): return self._chipConf._pvssickName
    @property
    def pckName     ( self ): return self._chipConf._pckName

    # Global Net names.
    @property
    def vddeName ( self ): return self._chipConf._vddeName
    @property
    def vsseName ( self ): return self._chipConf._vsseName
    @property
    def vddiName ( self ): return self._chipConf._vddiName
    @property
    def vssiName ( self ): return self._chipConf._vssiName
    @property
    def ckiName  ( self ): return self._chipConf._ckiName
    @property
    def ckoName  ( self ): return self._chipConf._ckoName
    @property
    def ckName   ( self ): return self._chipConf._ckName

    # Global Nets.
    @property
    def vdde        ( self ): return self._chipConf._vdde
    @property       
    def vsse        ( self ): return self._chipConf._vsse
    @property       
    def vddi        ( self ): return self._chipConf._vddi
    @property       
    def vssi        ( self ): return self._chipConf._vssi
    @property       
    def cki         ( self ): return self._chipConf._cki
    @property       
    def cko         ( self ): return self._chipConf._cko
    @property       
    def ck          ( self ): return self._chipConf._ck
    @property
    def blockageNet ( self ): return self._chipConf._blockageNet

    # Various.
    @property
    def clockPad     ( self ): return self._chipConf._clockPad
    @property        
    def powerPad     ( self ): return self._chipConf._powerPad
    @property        
    def cores        ( self ): return self._chipConf._cores
    @property        
    def southPads    ( self ): return self._chipConf._southPads
    @property        
    def northPads    ( self ): return self._chipConf._northPads
    @property        
    def eastPads     ( self ): return self._chipConf._eastPads
    @property        
    def westPads     ( self ): return self._chipConf._westPads
    @property        
    def coreSize     ( self ): return self._chipConf._coreSize
    @coreSize.setter
    def coreSize     ( self, ab ): self._chipConf._coreSize = ab
    @property        
    def chipSize     ( self ): return self._chipConf._chipSize
    @property        
    def minCorona    ( self ): return self._chipConf._minCorona
    @property        
    def padWidth     ( self ): return self._chipConf._padWidth
    @property        
    def padHeight    ( self ): return self._chipConf._padHeight
    @property
    def useClockTree ( self ): return self._chipConf._useClockTree


def loadConfiguration ( cell ):
    sys.path.append( os.getcwd() )

    confFile = cell.getName()+'_chip'
    if not os.path.isfile(confFile+'.py'):
      raise ErrorMessage( 1, 'ChipPlugin configuration file <%s.py> is missing.' % confFile )
    
    confModule = __import__( confFile, globals(), locals(), confFile )

    if not confModule.__dict__.has_key('chip'):
      raise WarningMessage( 'Module <%s> do not provides the chip variable, skipped.' \
                            % confFile )

    return ChipConfWrapper( GaugeConf()
                          , ChipConf ( confModule.__dict__['chip'], cell ) )
