# -*- explicit-buffer-name: "configuration.py<cumulus/src/plugins/chip>" -*-
#
# This file is part of the Coriolis Software.
# Copyright (c) UPMC 2014-2018, 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@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 Layer
from   Hurricane import Occurrence
from   Hurricane import Net
from   Hurricane import RoutingPad
from   Hurricane import Horizontal
from   Hurricane import Vertical
from   Hurricane import Contact
from   Hurricane import Pin
from   Hurricane import Plug
from   Hurricane import Instance
import CRL
from   CRL          import RoutingLayerGauge
from   helpers      import trace
from   helpers.io   import ErrorMessage
from   helpers.io   import WarningMessage
from   helpers.io   import catch
from   plugins      import getParameter
import plugins.chip


plugins.chip.importConstants( globals() )


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  :  "IoPadConf".

class IoPadConf ( object ):
  # self._datas is a table of 6 elements, the five first coming from
  # the configuration itself. Direction are taken from the core point
  # of view.
  #
  # Meaning of the table element's:
  #
  #  +---------+-----------------------------------------------------------+
  #  |  Index  |  Type                                                     |
  #  +=========+===========================================================+
  #  |    0    |  Pad instance name                                        |
  #  +---------+-----------------------------------------------------------+
  #  |    1    |  Pad connected signal name.                               |
  #  |         |  The name of the external signal at chip level            |
  #  +---------+-----------------------------------------------------------+
  #  |    2    |  The name of the signal going *from* the pad to the core. |
  #  |         |  OUT direction in the core                                |
  #  +---------+-----------------------------------------------------------+
  #  |    3    |  The name of the signal going *to* the pad from the core. |
  #  |         |  IN direction in core                                     |
  #  +---------+-----------------------------------------------------------+
  #  |    4    |  The enable signal, coming from the core                  |
  #  +---------+-----------------------------------------------------------+
  #  |    5    |  The IoPad associated object. It is set to None initially |
  #  +---------+-----------------------------------------------------------+

  def __init__ ( self, datas ):
      if not isinstance(datas,list):
        raise ErrorMessage( 1, [ 'IoPadConf.__init__(): The "datas" parameter is not a list.'
                               , str(datas) ] )
      if len(datas) < 3 and len(datas) > 5:
        raise ErrorMessage( 1, [ 'IoPadConf.__init__(): The "datas" list must have between 3 to 5 elements.'
                               , str(datas) ] )

      self._datas = datas
      self._datas.append( None )
      return

  @property
  def padInstanceName ( self ): return self._datas[0]

  @property
  def padNetName ( self ): return self._datas[1]

  @property
  def fromCoreNet ( self ): return self._datas[2]

  @property
  def toCoreNet ( self ): return self._datas[3]

  @property
  def enableNet ( self ): return self._datas[-2]

  @property
  def nets ( self ): return self._datas[2:-1]

  @property
  def udata ( self ): return self._datas[-1]

  @udata.setter
  def udata ( self, data ): self._datas[-1] = data

  def isTristate ( self ): return len(self._datas) == 5
  def isBidir    ( self ): return len(self._datas) == 6

  def __repr__ ( self ):
      s = '<IoPadConf %s pad:%s from:%s' % (self.padInstanceName,self.padNetName,self.fromCoreNet)
      if self.isBidir():
        s += ' to:%s en:%s' % (self.toCoreNet,self.enableNet)
      s += '>'
      return s


# -------------------------------------------------------------------
# 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.ioPadGauge     = None
      self.routingGauge   = None
      self.topLayerDepth  = 0
      self._plugToRp      = { }
      self._rpToAccess    = { }

      self._loadRoutingGauge()
      return

    def getSliceHeight   ( self ): return self.cellGauge.getSliceHeight()
    def getSliceStep     ( self ): return self.cellGauge.getSliceStep()
    def getIoPadHeight   ( self ): return self.ioPadGauge.getSliceHeight()
    def getIoPadStep     ( self ): return self.ioPadGauge.getSliceStep()
    def getIoPadPitch    ( self ): return self.ioPadGauge.getPitch()
    def getIoPadGauge    ( self ): return self.ioPadGauge
    def getHRoutingGauge ( self ): return self.routingGauge.getLayerGauge( self.horizontalDepth )
    def getVRoutingGauge ( self ): return self.routingGauge.getLayerGauge( self.verticalDepth )
    def getPitch         ( self, layer ): return self.routingGauge.getPitch( layer )

    def _loadRoutingGauge ( self ):
      gaugeName = Cfg.getParamString('anabatic.routingGauge').asString()
      self.cellGauge    = CRL.AllianceFramework.get().getCellGauge( gaugeName )
      self.routingGauge = CRL.AllianceFramework.get().getRoutingGauge( gaugeName )

      if not self.routingGauge:
        raise ErrorMessage( 1, [ 'RoutingGauge._loadRoutingGauge(): No routing gauge named "%s".' % gaugeName
                               , 'Please check the "anabatic.routingGauge" configuration parameter." ' ])

      topLayer = Cfg.getParamString('anabatic.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() - 1

      self.horizontalDepth     = -1
      self.verticalDepth       = -1
      self.horizontalDeepDepth = -1
      self.verticalDeepDepth   = -1
      for depth in range(0,self.topLayerDepth+1):
        trace( 550, '\tdepth:%d\n' % depth )
        trace( 550, '\t%s\n'       % self.routingGauge.getLayerGauge(depth) )

        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
      trace( 550, '\thorizontalDepth    :%d\n' % self.horizontalDepth )
      trace( 550, '\tverticalDepth      :%d\n' % self.verticalDepth )
      trace( 550, '\thorizontalDeepDepth:%d\n' % self.horizontalDeepDepth )
      trace( 550, '\tverticalDeepDepth  :%d\n' % self.verticalDeepDepth )
      return

    def _loadIoPadGauge ( self, ioPadGaugeName ):
      self.ioPadGauge = CRL.AllianceFramework.get().getCellGauge( ioPadGaugeName )
      if not self.ioPadGauge:
        print WarningMessage( 'IO pad gauge "%s" not found.' % ioPadGaugeName )
      return

    def isHorizontal ( self, layer ):
      mask = layer.getMask()
      for lg in self.routingGauge.getLayerGauges():
        if lg.getLayer().getMask() == mask:
          if lg.getDirection() == RoutingLayerGauge.Horizontal: return True
          return False

      print ErrorMessage( 1, 'GaugeConf.isHorizontal(): Layer "%s" is not part of gauge "%s", cannot know preferred direction.' \
                              % (layer.getName(), self.routingGauge.getName()) )
      return False

    def isVertical ( self, layer ): return not self.isHorizontal( layer )

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

      if self.horizontalDepth > self.verticalDepth: depth -= 1

      trace( 550, '\t%s, horizontalDepth:%d, gaugeDepth:%d\n'
             % (self.routingGauge,self.horizontalDepth,self.routingGauge.getDepth()))
      return Contact.create( net
                           , self.routingGauge.getContactLayer(depth)
                           , x, y
                           , self.routingGauge.getLayerGauge(depth).getViaWidth()
                           , self.routingGauge.getLayerGauge(depth).getViaWidth()
                           )

    def _getNearestHorizontalTrack ( self, bb, y, flags ):
      if flags & GaugeConf.DeepDepth: depth = self.horizontalDeepDepth
      else:                           depth = self.horizontalDepth

      index = self.routingGauge.getLayerGauge(depth).getTrackIndex( bb.getYMin(), bb.getYMax(), y, RoutingLayerGauge.Nearest )
      return self.routingGauge.getLayerGauge(depth).getTrackPosition( bb.getYMin(), index )

    def _getNearestVerticalTrack ( self, bb, x, flags ):
      if flags & GaugeConf.DeepDepth: depth = self.verticalDeepDepth
      else:                           depth = self.verticalDepth

      index = self.routingGauge.getLayerGauge(depth).getTrackIndex( bb.getXMin(), bb.getXMax(), x, RoutingLayerGauge.Nearest )
      return self.routingGauge.getLayerGauge(depth).getTrackPosition( bb.getXMin(), index )

    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 self._rpToAccess.has_key(rp):
        trace( 550, '-' )
        return self._rpToAccess[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
      trace( 550, '\tstopDepth:%d\n' % stopDepth )

      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

      self._rpToAccess[rp] = contact1
  
      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 )

    def _setStackPosition ( self, topContact, x, y ):
      topContact.setX( x )
      topContact.setY( y )

      count = 0
      for component in topContact.getSlaveComponents():
        segment = component
        count  += 1
      if count > 1:
        raise ErrorMessage( 1, 'GaugeConf::_setStackPosition(): There must be exactly one segment connected to %s, not %d.' % (topContact,count) )

      if count == 1:
        if isinstance(segment,Horizontal):
          segment.setY( y )
          segment.getOppositeAnchor( topContact ).setY( y )
        elif isinstance(segment,Vertical):
          segment.setX( x )
          segment.getOppositeAnchor( topContact ).setX( x )
      return


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

class ChipConf ( object ):

    @staticmethod
    def _toSymbolic ( u, rounding ): 
      oneLambda = DbU.fromLambda( 1.0 )
      remainder = u % oneLambda
      if remainder:
        if rounding == Superior: u = u + (oneLambda - remainder)
        else:                    u = u -              remainder
      return u


    @staticmethod
    def toSymbolic ( v, rounding ): 
      if isinstance(v,long): return ChipConf._toSymbolic( v, rounding )
      if isinstance(v,Box):
        if rounding & Inwards:
          roundings = [ Superior
                      , Superior
                      , Inferior
                      , Inferior ]
        else:
          roundings = [ Inferior
                      , Inferior
                      , Superior
                      , Superior ]
        xMin = ChipConf._toSymbolic( v.getXMin(), roundings[0] )
        yMin = ChipConf._toSymbolic( v.getYMin(), roundings[1] )
        xMax = ChipConf._toSymbolic( v.getXMax(), roundings[2] )
        yMax = ChipConf._toSymbolic( v.getYMax(), roundings[3] )
        return Box( xMin, yMin, xMax, yMax )
      return v


    @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, chipSize[0], 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, coreSize[0], coreSize[1] )


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


    @staticmethod
    def _readChipName( chipConfigDict ):
      if chipConfigDict.has_key('chip.name'): return chipConfigDict['chip.name']
      return 'chip'


    def _loadIoPadGauge ( self, chipConfigDict ):
      if not chipConfigDict.has_key('pads.ioPadGauge'):
       #raise ErrorMessage( 1, 'The IO pad gauge configuration parameter "pads.ioPadGauge" is missing.' )
        return
      self.gaugeConf._loadIoPadGauge( chipConfigDict['pads.ioPadGauge'] )
      return


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

      af      = CRL.AllianceFramework.get()
      padList = []
      for i in range(len(padConfList)):
        position     = None
        instanceName = None
        if isinstance(padConfList[i],str):
          instanceName = padConfList[i]
        elif isinstance(padConfList[i],list):
          self.padsHavePosition = True
          if isinstance(padConfList[i][0],long) and isinstance(padConfList[i][1],str):
            position     = padConfList[i][0]
            instanceName = padConfList[i][1]

        if not instanceName:
          raise ErrorMessage( 1, 'The element [%d] of list %s is neither a string nor a list "[pos,name]" (skipped).'
                                 % (i,keyword) )
          continue

        padList.append( [ position, instanceName ] )
      
      return padList


    def _readPadInstances ( self, chipConfigDict ):
      if not chipConfigDict.has_key('pads.instances'): return [ ]

      padInstancesConf = chipConfigDict['pads.instances']
      if not isinstance(padInstancesConf,list):
          raise ErrorMessage( 1, 'The "%s" entry is not a list.' )
          return [ ]

      padInstances = [ ]
      for entry in padInstancesConf:
        padInstances.append( IoPadConf( entry ) )
      return padInstances


    def __init__ ( self, chipConfigDict, cell, viewer=None ):
      trace( 550, '\tONE LAMBDA = %s\n' % DbU.getValueString(DbU.fromLambda(1.0)) )

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

      self.validated        = True
      self.gaugeConf        = GaugeConf()
      self.cell             = cell
      self.viewer           = viewer
     # Block Corona parameters.
      self.railsNb          = getParameter('chip','chip.block.rails.count'   ).asInt()
      self.hRailWidth       = getParameter('chip','chip.block.rails.hWidth'  ).asInt()
      self.vRailWidth       = getParameter('chip','chip.block.rails.vWidth'  ).asInt()
      self.hRailSpace       = getParameter('chip','chip.block.rails.hSpacing').asInt()
      self.vRailSpace       = getParameter('chip','chip.block.rails.vSpacing').asInt()
     # Global Net names.    
      self.blockageName     = "blockagenet"
     # Global Nets.         
      self.coronaVdd        = None
      self.coronaVss        = None
      self.coronaCk         = None
      self.blockageNet      = None
      self.coronas          = []
      self.cores            = []
                          
      self._loadIoPadGauge( chipConfigDict )

      self.padsHavePosition = False
      self.padInstances     = self._readPadInstances( chipConfigDict )
      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.useClockTree     = ChipConf._readClockTree( chipConfigDict )
      self.chipName         = ChipConf._readChipName ( 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

      return


    def chipValidate ( self ):
      self.checkPads()
      self.checkCorona()
      self.computeChipSize()
     #self.checkChipSize()
      self.findPowerAndClockNets()
      return


    @property
    def icorona ( self ): return self.coronas[0]

    @property
    def corona  ( self ): return self.coronas[0].getMasterCell()

    @property
    def icore ( self ): return self.cores[0]

    @property
    def core  ( self ): return self.cores[0].getMasterCell()

    @property
    def chip  ( self ): return self.cell


    def getInstanceAb ( self, instance ):
      ab = instance.getMasterCell().getAbutmentBox()
      instance.getTransformation().applyOn( ab )

      if instance.getCell() == self.cell: return ab

      if instance.getCell() != self.corona:
        raise ErrorMessage( 1, 'ChipConf.getInstanceAb(): Instance "%s" neither belong to chip or corona.' % instance.getName() )
        return ab

      self.icorona.getTransformation().applyOn( ab )
      return ab


    def getCoronaNet ( self, chipNet ):
      for plug in chipNet.getPlugs():
        if plug.getInstance() == self.icorona:
          return plug.getMasterNet()
      return None


    def toRoutingGauge ( self, uMin, uMax, layer ):
      trace( 550, ',+', '\ttoRoutingGauge() [%s %s] %s\n' \
             % (DbU.getValueString(uMin), DbU.getValueString(uMax), layer) )

      ab   = self.corona.getAbutmentBox()
      lg   = None
      mask = layer.getMask()
      for layerGauge in self.gaugeConf.routingGauge.getLayerGauges():
        if layerGauge.getLayer().getMask() == mask:
          lg = layerGauge
          trace( 550, '\tUsing layer gauge %s\n' % str(lg) )
          break

      if uMax < uMin: uMin, uMax = uMax, uMin
      if lg:
        if lg.getDirection() == RoutingLayerGauge.Horizontal:
          abMin = ab.getYMin()
          abMax = ab.getYMax()
        else:
          abMin = ab.getXMin()
          abMax = ab.getXMax()

        if uMin <= abMin:
          shiftRight = abMin - uMin + lg.getPitch()
          uMin      += shiftRight
          uMax      += shiftRight
        if uMax >= abMax:
          shiftLeft = uMax - abMax + lg.getPitch()
          uMin     -= shiftLeft
          uMax     -= shiftLeft

        iTrackMin = lg.getTrackIndex( abMin, abMax, uMin, RoutingLayerGauge.Superior )
        iTrackMax = lg.getTrackIndex( abMin, abMax, uMax, RoutingLayerGauge.Inferior )
        if iTrackMax < iTrackMin: iTrackMax = iTrackMin
        
        uTrackMin = lg.getTrackPosition( abMin, iTrackMin )
        uTrackMax = lg.getTrackPosition( abMin, iTrackMax )

        axis  = (uTrackMax + uTrackMin) / 2
        width = (iTrackMax - iTrackMin) * lg.getPitch() + lg.getWireWidth()

        if self.gaugeConf.routingGauge.isSymbolic():
          oneLambda = DbU.fromLambda( 1.0 )
          if axis % oneLambda:
            axis  -= oneLambda / 2
            width -= oneLambda

        trace( 550, '\t[%i %i]\n' % (iTrackMin, iTrackMax) )
        trace( 550, '\taxis:  %sl %s\n' %  (DbU.toLambda(axis ), DbU.getValueString(axis )) )
        trace( 550, '\twidth: %sl %s\n' %  (DbU.toLambda(width), DbU.getValueString(width)) )
      else:
        axis  = (uMax + uMin) / 2
        width = (uMax - uMin)

      trace( 550, '-' )
      return axis, width


    def toCoronaPitchInChip ( self, uCore, layer ):
      trace( 550, ',+', '\tChipConf.toCoronaPitchInChip(): uCore:  %sl %s\n' % (DbU.toLambda(uCore), DbU.getValueString(uCore)) )

      coronaAb = self.getInstanceAb( self.icorona )
      lg       = None
      mask     = layer.getMask()
      for layerGauge in self.gaugeConf.routingGauge.getLayerGauges():
        if layerGauge.getLayer().getMask() == mask:
          lg = layerGauge
          break

      if not lg:
        trace( 550, '-' )
        return 0

      trace( 550, '\t%s\n' % str(lg) )
      if lg:
        if lg.getDirection() == RoutingLayerGauge.Horizontal:
          uCorona = uCore - coronaAb.getYMin()
        else:
          uCorona = uCore - coronaAb.getXMin()

      uCorona, width = self.toRoutingGauge( uCorona, uCorona, layer )

      trace( 550, '\ttoCoronaPitchInChip(): uCorona:  %sl %s\n' % (DbU.toLambda(uCorona), DbU.getValueString(uCorona)) )

      if lg:
        if lg.getDirection() == RoutingLayerGauge.Horizontal:
          uCore = uCorona + coronaAb.getYMin()
        else:
          uCore = uCorona + coronaAb.getXMin()

      trace( 550, '\ttoCoronaPitchInChip(): uCorona:  %sl %s\n' % (DbU.toLambda(uCorona), DbU.getValueString(uCorona)) )
      trace( 550, '\ttoCoronaPitchInChip(): uCore:  %sl %s\n'   % (DbU.toLambda(uCore  ), DbU.getValueString(uCore  )) )
      trace( 550, '-' )
      return uCore
      


    def coronaHorizontal ( self, chipNet, layer, chipY, width, chipXMin, chipXMax ):
      trace( 550, ',+', '\tChipConf.coronaHorizontal\n' )

      coronaAb  = self.getInstanceAb( self.icorona )
      coronaNet = self.getCoronaNet ( chipNet )
      if not coronaNet: return None

      coronaY = chipY - coronaAb.getYMin()
      dxMin   = ChipConf.toSymbolic( chipXMin - coronaAb.getXMin(), Superior )
      dxMax   = ChipConf.toSymbolic( chipXMax - coronaAb.getXMin(), Inferior )

      trace( 550, '\t| chipNet: %s %s\n' % (chipNet, layer) )
      trace( 550, '\t| Real\n' )
      trace( 550, '\t|   axis: %10s\n' % DbU.getValueString(coronaY) )
      trace( 550, '\t|   width:%10s\n' % DbU.getValueString(width) )
      trace( 550, '\t|   dxMin:%10s (%sl)\n' \
             % (DbU.getValueString(chipXMin - coronaAb.getXMin()), DbU.toLambda(chipXMin - coronaAb.getXMin()) ) )
      trace( 550, '\t|   dxMax:%10s\n' % DbU.getValueString(chipXMax - coronaAb.getXMin()) )

      coronaY, width = self.toRoutingGauge( coronaY - width/2, coronaY + width/2, layer )

      trace( 550, '\t| On Grid\n' )
      trace( 550, '\t|   axis: %10sl or %10s\n' % (DbU.toLambda(coronaY), DbU.getValueString(coronaY)) )
      trace( 550, '\t|   width:%10sl or %10s\n' % (DbU.toLambda(width)  , DbU.getValueString(width)) )
      trace( 550, '\t|   dxMin:%10sl\n' % DbU.toLambda(dxMin) )
      trace( 550, '\t|   dxMax:%10sl\n' % DbU.toLambda(dxMax) )

      h = Horizontal.create( coronaNet, layer, coronaY, width, dxMin, dxMax )

      trace( 550, '\t| %s\n' % str(h) )
      trace( 550, '-' )
      return h


    def coronaVertical ( self, chipNet, layer, chipX, width, chipYMin, chipYMax ):
      trace( 550, ',+', '\tChipConf.coronaVertical\n' )

      coronaAb  = self.getInstanceAb( self.icorona )
      coronaNet = self.getCoronaNet( chipNet )
      if not coronaNet: return None

      coronaX = chipX - coronaAb.getXMin()
      dyMin   = ChipConf.toSymbolic( chipYMin - coronaAb.getYMin(), Superior )
      dyMax   = ChipConf.toSymbolic( chipYMax - coronaAb.getYMin(), Inferior )

      trace( 550, '\t| chipNet: %s %s\n' % (chipNet, layer) )
      trace( 550, '\t| Real\n' )
      trace( 550, '\t|   axis: %s\n' % DbU.getValueString(coronaX) )
      trace( 550, '\t|   width:%s\n' % DbU.getValueString(width) )

      coronaX, width = self.toRoutingGauge( coronaX - width/2, coronaX + width/2, layer )

      trace( 550, '\t| On Grid\n' )
      trace( 550, '\t|   axis: %s or %s\n' % (DbU.toLambda(coronaX), DbU.getValueString(coronaX)) )
      trace( 550, '\t|   width:%s or %s\n' % (DbU.toLambda(width)  , DbU.getValueString(width)) )

      v = Vertical.create( coronaNet, layer, coronaX, width, dyMin, dyMax )

      trace( 550, '\t| %s\n' % str(v) )
      trace( 550, '-' )
      return v


    def coronaContact ( self, chipNet, layer, chipX, chipY, width, height, flags=0 ):
      trace( 550, ',+', '\tChipConf.coronaContact\n' )

      coronaAb  = self.getInstanceAb( self.icorona )
      coronaNet = self.getCoronaNet( chipNet )
      if not coronaNet: return None

      coronaX = chipX - coronaAb.getXMin()
      coronaY = chipY - coronaAb.getYMin()

      trace( 550, '\t| chipNet: %s %s\n' % (chipNet, layer) )
      trace( 550, '\t| Real\n' )
      trace( 550, '\t|   center: %12s %12s\n' % (DbU.getValueString(coronaX), DbU.getValueString(coronaY)) )
      trace( 550, '\t|   WxH:    %12s %12s\n' % (DbU.getValueString(width  ), DbU.getValueString(height )) )

      topLayer = layer.getTop()
      if self.gaugeConf.isHorizontal(topLayer):
        coronaX, width  = self.toRoutingGauge( coronaX - width /2, coronaX + width /2, layer.getBottom() )
        coronaY, height = self.toRoutingGauge( coronaY - height/2, coronaY + height/2, topLayer )
      else:
        coronaX, width  = self.toRoutingGauge( coronaX - width /2, coronaX + width /2, topLayer )
        coronaY, height = self.toRoutingGauge( coronaY - height/2, coronaY + height/2, layer.getBottom() )

      if not (flags & OnHorizontalPitch):
        trace( 550, '\tNot on horizontal routing pitch, Y on lambda only.\n' )
        coronaY = self.toSymbolic( chipY - coronaAb.getYMin(), Superior )
      if not (flags & OnVerticalPitch  ):
        trace( 550, '\tNot on vertical routing pitch, X on lambda only.\n' )
        coronaX = self.toSymbolic( chipX - coronaAb.getXMin(), Superior )

      trace( 550, '\t| On Grid\n' )
      trace( 550, '\t|   X axis: %12s or %12s\n' % (DbU.toLambda(coronaX)      , DbU.getValueString(coronaX)) )
      trace( 550, '\t|   Y axis: %12s or %12s\n' % (DbU.toLambda(coronaY)      , DbU.getValueString(coronaY)) )
      trace( 550, '\t|   center: %12s %12s\n'    % (DbU.getValueString(coronaX), DbU.getValueString(coronaY)) )
      trace( 550, '\t|   WxH:    %12s %12s\n'    % (DbU.getValueString(width  ), DbU.getValueString(height )) )

      c = Contact.create( coronaNet
                        , layer
                        , coronaX
                        , coronaY
                        , width
                        , height
                        )

      trace( 550, '\t| %s\n' % str(c) )
      trace( 550, '-' )
      return c


    def coronaContactArray ( self, chipNet, layer, chipX, chipY, array, flags ):
      trace( 550, ',+', '\tChipConf.coronaContactArray\n' )

      viaPitch  = layer.getMinimalSize() + layer.getMinimalSpacing()
      coronaAb  = self.getInstanceAb( self.icorona )
      coronaNet = self.getCoronaNet( chipNet )
      if not coronaNet: return None

      trace( 550, '\t| chipNet: %s %s\n' % (chipNet, layer) )

      coronaX = chipX - coronaAb.getXMin()
      coronaY = chipY - coronaAb.getYMin()

      topLayer = layer.getTop()
      if self.gaugeConf.isHorizontal(topLayer):
        coronaX, width  = self.toRoutingGauge( coronaX, coronaX, layer.getBottom() )
        coronaY, height = self.toRoutingGauge( coronaY, coronaY, topLayer )
      else:
        coronaX, width  = self.toRoutingGauge( coronaX, coronaX, topLayer )
        coronaY, height = self.toRoutingGauge( coronaY, coronaY, layer.getBottom() )

      if not (flags & OnHorizontalPitch):
        trace( 550, '\tNot on horizontal routing pitch, Y on lambda only.\n' )
        coronaY = self.toSymbolic( chipY - coronaAb.getYMin(), Superior )
      if not (flags & OnVerticalPitch  ):
        trace( 550, '\tNot on vertical routing pitch, X on lambda only.\n' )
        coronaX = self.toSymbolic( chipX - coronaAb.getXMin(), Superior )

      contacts    = []
      xContact    = coronaX - viaPitch * (array[0]-1)/2
      yContact    = coronaY - viaPitch * (array[1]-1)/2
      contactSize = layer.getMinimalSize()

      trace( 550, '\txContact:%sl yContact:%sl\n' % (DbU.toLambda(xContact),DbU.toLambda(yContact)) )

      for i in range(array[0]):
        for j in range(array[1]):
          c = Contact.create( coronaNet
                            , layer
                            , xContact + i*viaPitch
                            , yContact + j*viaPitch
                            , contactSize
                            , contactSize
                            )
          trace( 550, '\t+ %s\n' % str(c) )
          contacts.append( c )
    
      trace( 550, '-' )
      return contacts


    def coronaPin ( self, chipNet, count, direction, layer, chipX, chipY, width, height ):
      trace( 550, ',+', '\tChipConf.coronaPin\n' )

      coronaAb  = self.getInstanceAb( self.icorona )
      coronaNet = self.getCoronaNet( chipNet )
      if not coronaNet: return None

      coronaX = chipX - coronaAb.getXMin()
      coronaY = chipY - coronaAb.getYMin()

      trace( 550, '\t| chipNet: %s (%d) %s\n' % (chipNet, count, layer) )
      trace( 550, '\t| Real\n' )
      trace( 550, '\t|   center: %s %s\n' % (DbU.getValueString(coronaX), DbU.getValueString(coronaY)) )
      trace( 550, '\t|   WxH:    %s %s\n' % (DbU.getValueString(width  ), DbU.getValueString(height )) )

      topLayer = layer.getTop()
      if self.gaugeConf.isHorizontal(topLayer):
        coronaX, width  = self.toRoutingGauge( coronaX - width /2, coronaX + width /2, layer.getBottom() )
        coronaY, height = self.toRoutingGauge( coronaY - height/2, coronaY + height/2, topLayer )
      else:
        coronaX, width  = self.toRoutingGauge( coronaX - width /2, coronaX + width /2, topLayer )
        coronaY, height = self.toRoutingGauge( coronaY - height/2, coronaY + height/2, layer.getBottom() )

      if direction == Pin.Direction.NORTH or direction == Pin.Direction.SOUTH: 
        trace( 550, '\tEast/West not on horizontal routing pitch, Y on lambda only.\n' )
        coronaY = self.toSymbolic( chipY - coronaAb.getYMin(), Superior )
      if direction == Pin.Direction.EAST or direction == Pin.Direction.WEST: 
        trace( 550, '\tNorth/South not on vertical routing pitch, X on lambda only.\n' )
        coronaX = self.toSymbolic( chipX - coronaAb.getXMin(), Superior )

      trace( 550, '\t| On Grid\n' )
      trace( 550, '\t|   X axis: %s or %s\n' % (DbU.toLambda(coronaY)      , DbU.getValueString(coronaY)) )
      trace( 550, '\t|   Y axis: %s or %s\n' % (DbU.toLambda(coronaX)      , DbU.getValueString(coronaX)) )
      trace( 550, '\t|   center: %s %s\n'    % (DbU.getValueString(coronaX), DbU.getValueString(coronaY)) )
      trace( 550, '\t|   WxH:    %s %s\n'    % (DbU.getValueString(width  ), DbU.getValueString(height )) )

      c = Pin.create( coronaNet
                    , '%s.%d' % (coronaNet.getName(),count)
                    , direction
                    , Pin.PlacementStatus.FIXED
                    , layer
                    , coronaX
                    , coronaY
                    , width
                    , height
                    )

      trace( 550, '\t| %s\n' % str(c) )
      trace( 550, '-' )
      return c


    def checkPads ( self ):

      def contains ( padList, side, padInstance ):
        for i in range(len(padList)):
          if padList[i][1] == padInstance.getName():
            if (padInstance.getMasterCell().getAbutmentBox().getHeight() != self.gaugeConf.getIoPadHeight()):
              raise ErrorMessage( 1, 'The pad [%d] %s (%s) on %s side is not an instance of a pad cell.'
                                     % (i,padInstance.getName(),padInstance.getMasterCell().getName(),side) )
            padList[i][1] = padInstance
            return True
        return False

      def checkNotFounds ( padList, side ):
        for i in range(len(padList)):
          if not isinstance(padList[i][1],Instance):
            print ErrorMessage( 1, 'The pad [%d] (%s) of list %s do not exists in netlist (skipped).'
                                   % (i,padList[i][1],side) )
        return
    
        
      af       = CRL.AllianceFramework.get()
      cellPads = []
      for instance in self.cell.getInstances():
        if contains(self.southPads,'south',instance): continue
        if contains(self.northPads,'north',instance): continue
        if contains(self.eastPads ,'east' ,instance): continue
        if contains(self.westPads ,'west' ,instance): continue
        if (instance.getMasterCell().getAbutmentBox().getHeight() == self.gaugeConf.getIoPadHeight()):
          raise ErrorMessage( 1, 'Pad "%s" is not on any side (N/S/E/W).' % instance.getName() )
          self.validated = False
        else:
          self.coronas.append( instance )

      checkNotFounds( self.southPads, 'south' )    
      checkNotFounds( self.northPads, 'north' )    
      checkNotFounds( self.eastPads , 'east'  )    
      checkNotFounds( self.westPads , 'west'  )    

      if len(self.coronas) > 1:
        message = [ 'Chip "%s" have more than one corona:' % self.cell.getName() ]
        for i in range(len(self.coronas)):
          message.append( '%4d: %s' % (i,self.coronas[i].getName()) )
        raise ErrorMessage( 1, message )
        self.validated = False

      if len(self.coronas) < 1:
        raise ErrorMessage( 1, 'Chip "%s" doesn\'t seems to have a corona.' % self.cell.getName() )
        self.validated = False
      else:
        for instance in self.corona.getInstances():
          self.cores.append( instance )

        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()) )
          raise ErrorMessage( 1, message )
          self.validated = False

          if len(self.cores) < 1:
            raise ErrorMessage( 1, 'Chip "%s" doesn\'t seems to have a core.' % self.cell.getName() )
            self.validated = False

      return


    def findPowerAndClockNets ( self ):
      if self.icore:
        for plug in self.icore.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.corona.getNet( masterNet.getName() )
            if not net:
              raise ErrorMessage( 1, 'ChipConf.findPowerAndClockNets(): Missing global net "%s" at corona level.'
                                     % masterNet.getName() )
              self._validated = False
              continue

          if netType == Net.Type.GROUND:
            if self.coronaVss and self.coronaVss != net:
              raise ErrorMessage( 1, 'ChipConf.findPowerAndClockNets(): Multiple ground nets "%s" and "%s" at corona level.'
                                     % (self.coronaVss.getName(), net.getName()) )
              self._validated = False
              continue
            else:
              self.coronaVss = net

          if netType == Net.Type.POWER:
            if self.coronaVdd and self.coronaVdd != net:
              raise ErrorMessage( 1, 'ChipConf.findPowerAndClockNets(): Multiple power nets "%s" and "%s" at corona level.'
                                     % (self.coronaVdd.getName(), net.getName()) )
              self._validated = False
              continue
            else:
              self.coronaVdd = net

          if netType == Net.Type.CLOCK:
            if self.coronaCk and self.coronaCk != net:
              raise ErrorMessage( 1, 'ChipConf.findPowerAndClockNets(): Multiple clock nets "%s" and "%s" at corona level.'
                                     % (self.coronaCk.getName(), net.getName()) )
              self._validated = False
              continue
            else:
              self.coronaCk = net

      for net in self.corona.getNets():
        if net.getType() == Net.Type.BLOCKAGE:
          self.blockageNet  = net
          self.blockageName = net.getName()

      if not self.blockageNet:
        self.blockageNet = Net.create( self.corona, self.blockageName )
        self.blockageNet.setType( Net.Type.BLOCKAGE )

      return

    def checkChipSize ( self ):
     #if self._coreSize.isEmpty(): return
     #
     #minWidth  = self._coreSize.getWidth () + self._minCorona + 2*self._padHeight
     #minHeight = self._coreSize.getHeight() + self._minCorona + 2*self._padHeight
     #
     #if self._chipSize.getWidth() < minWidth:
     #  raise ErrorMessage( 1, 'Core is too wide to fit into the chip. Needs: %d, but has %d' \
     #                         % ( DbU.toLambda(minWidth), DbU.toLambda(self._chipSize.getWidth()) ) )
     #  self._validated = False
     #
     #if self._chipSize.getHeight() < minHeight:
     #  raise ErrorMessage( 1, 'Core is too wide to fit into the chip. Needs: %d, but has %d' \
     #                         % ( DbU.toLambda(minHeight), DbU.toLambda(self._chipSize.getHeight()) ) )
     #  self._validated = False
      return

    def checkCorona ( self ):
      trace( 550, ',+', 'Configuration.checkCorona()\n' )
      netPads = {}
      for plug in self.icorona.getPlugs():
        padNet    = plug.getNet()
        coronaNet = plug.getMasterNet()
        if not padNet and coronaNet.isGlobal():
          padNet = self.cell.getNet( coronaNet.getName() )

        if padNet:
          if not netPads.has_key(padNet):
            trace( 550, '\t%20s <-> %-20s\n' % (padNet.getName(),coronaNet.getName()) )
            netPads[ padNet ] = coronaNet
          else:
            raise ErrorMessage( 1, 'ChipConf.checkCorona(): Corona nets "%s" and "%s" connected to the same pad net "%s".' \
                                    % (coronaNet.getName(),netPads[padNet].getName(),padNet.getName()) )
            self._validated = False
          
      trace( 550, '-' )
      return


    def computeChipSize ( self ):

      def getSideLength ( pads ):
        sideLength = self.gaugeConf.getIoPadHeight() * 2 
        for pad in pads: sideLength += pad.getMasterCell().getAbutmentBox().getWidth()
        return sideLength

        
      if not self.chipSize.isEmpty(): return
      
      southPadsLength = getSideLength( self.southPads )
      northPadsLength = getSideLength( self.northPads )
      eastPadsLength  = getSideLength( self.eastPads  )
      westPadsLength  = getSideLength( self.westPads  )
      
      horizontalPads = max( len(self.southPads), len(self.northPads) )
      verticalPads   = max( len(self.eastPads ), len(self.westPads ) )
      self.chipSize = Box( 0
                         , 0
                         , max( southPadsLength, northPadsLength )
                         , max(  westPadsLength,  eastPadsLength )
                         )
      return


    def setupCorona ( self, gapX1, gapY1, gapX2, gapY2 ):
      ab = self.cell.getAbutmentBox()
      ab.inflate  ( -gapX1, -gapY1, -gapX2, -gapY2 )
      ab.inflate  ( - self.getIoPadHeight() )
      ab.translate( - self.getIoPadHeight(), - self.getIoPadHeight())
      ab = self.toSymbolic( ab, Inwards )

      self. corona.setAbutmentBox( Box( 0, 0, ab.getWidth(), ab.getHeight() ) )
      self.icorona.setTransformation(
        Transformation( self.toSymbolic( self.getIoPadHeight() + ab.getXMin(), Superior )
                      , self.toSymbolic( self.getIoPadHeight() + ab.getYMin(), Superior )
                      , Transformation.Orientation.ID ) )
      self.icorona.setPlacementStatus( Instance.PlacementStatus.FIXED )
      return


    def setupCore ( self, gapX1, gapY1, gapX2, gapY2 ):
      ab = self.getInstanceAb( self.icorona )
      if ab.isEmpty():
        raise ErrorMessage( 1, 'ChipConf.setupCore(): Attempt to setup core *before* corona.' )
        return

      ab.inflate( -gapX1, -gapY1, -gapX2, -gapY2 )
      ab = self.toSymbolic( ab, Inwards )

      trace( 550, '\tChipConf.setupCore(): Abutment box:%s\n' % str(ab) )
      
      self.core.setAbutmentBox( Box( 0, 0, ab.getWidth(), ab.getHeight() ) )
      self.icore.setTransformation(
        Transformation( ChipConf.toSymbolic(ab.getXMin(),Inferior) - self.icorona.getTransformation().getTx()
                      , ChipConf.toSymbolic(ab.getYMin(),Inferior) - self.icorona.getTransformation().getTy()
                      , Transformation.Orientation.ID ) )
      self.icore.setPlacementStatus( Instance.PlacementStatus.FIXED )
      return


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

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

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

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

    def getSliceHeight   ( self ): return self.gaugeConf.getSliceHeight()
    def getSliceStep     ( self ): return self.gaugeConf.getSliceStep()
    def getIoPadHeight   ( self ): return self.gaugeConf.getIoPadHeight()
    def getIoPadStep     ( self ): return self.gaugeConf.getIoPadStep()
    def getIoPadPitch    ( self ): return self.gaugeConf.getIoPadPitch()
    def getIoPadGauge    ( self ): return self.gaugeConf.getIoPadGauge()
    def getHRoutingGauge ( self ): return self.gaugeConf.getHRoutingGauge()
    def getVRoutingGauge ( self ): return self.gaugeConf.getVRoutingGauge()

    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 )

    def getNearestHorizontalTrack ( self, bb, y, flags ):
      return self.gaugeConf._getNearestHorizontalTrack ( bb, y, flags )

    def getNearestVerticalTrack ( self, bb, x, flags ):
      return self.gaugeConf._getNearestVerticalTrack( bb, x, flags )

    def setStackPosition ( self, topContact, x, y ):
      self.gaugeConf._setStackPosition( topContact, x, y )


def loadConfiguration ( cell, viewer=None ):
    sys.path.append( os.getcwd() )

    try:
      confFile = 'coriolis2/ioring.py'
      if not os.path.isfile(confFile):
        raise ErrorMessage( 1, 'ChipPlugin, configuration file "%s" is missing.' % confFile )
      else:
        if not os.path.isfile('coriolis2/__init__.py'):
          raise ErrorMessage( 1, 'ChipPlugin, configuration directory "./coriolis2/" is missing "__init__.py".' )
      
      from coriolis2.ioring import chip
    except Exception, e:
      catch( e )

    return ChipConf( chip, cell, viewer )
