# This file is part of the Coriolis Software. # Copyright (c) SU 2014-2020, 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/pads.py" | # +-----------------------------------------------------------------+ from __future__ import print_function import sys import re from operator import itemgetter import Cfg from Hurricane import DbU from Hurricane import Point from Hurricane import Transformation from Hurricane import Interval from Hurricane import Box from Hurricane import Path from Hurricane import Occurrence from Hurricane import UpdateSession from Hurricane import Layer from Hurricane import BasicLayer from Hurricane import Net from Hurricane import Pin from Hurricane import Contact from Hurricane import Segment from Hurricane import Horizontal from Hurricane import Vertical from Hurricane import RoutingPad from Hurricane import Instance import CRL from CRL import RoutingLayerGauge import helpers from helpers import trace from helpers.io import ErrorMessage from helpers.io import WarningMessage from helpers.overlay import UpdateSession import plugins.alpha.chip plugins.alpha.chip.importConstants( globals() ) # -------------------------------------------------------------------- # Class : "pads.Corner" class Corner ( object ): def __init__ ( self, corona, cornerType ): self.type = cornerType self.corona = corona self.padCorner = None @property def conf ( self ): return self.corona.conf def _getPoints ( self, axis ): if self.type == SouthWest: xCorner = self.conf.chipAb.getXMin() + axis yCorner = self.conf.chipAb.getYMin() + axis xBb = self.conf.chipAb.getXMin() + self.conf.ioPadHeight yBb = self.conf.chipAb.getYMin() + self.conf.ioPadHeight elif self.type == SouthEast: xCorner = self.conf.chipAb.getXMax() - axis yCorner = self.conf.chipAb.getYMin() + axis xBb = self.conf.chipAb.getXMax() - self.conf.ioPadHeight yBb = self.conf.chipAb.getYMin() + self.conf.ioPadHeight elif self.type == NorthEast: xCorner = self.conf.chipAb.getXMax() - axis yCorner = self.conf.chipAb.getYMax() - axis xBb = self.conf.chipAb.getXMax() - self.conf.ioPadHeight yBb = self.conf.chipAb.getYMax() - self.conf.ioPadHeight elif self.type == NorthWest: xCorner = self.conf.chipAb.getXMin() + axis yCorner = self.conf.chipAb.getYMax() - axis xBb = self.conf.chipAb.getXMin() + self.conf.ioPadHeight yBb = self.conf.chipAb.getYMax() - self.conf.ioPadHeight return xCorner, yCorner, xBb, yBb def _createCorner ( self ): for rail in self.corona.padRails: net = rail[0] layer = rail[1] axis = rail[2] width = rail[3] xCorner, yCorner, xBb, yBb = self._getPoints( axis ) Contact .create( net, layer, xCorner, yCorner, width, width ) Horizontal.create( net, layer, yCorner, width, xCorner, xBb ) Vertical .create( net, layer, xCorner, width, yCorner, yBb ) def _getTransformation ( self ): if self.type == SouthWest: name = 'padcorner_sw' x = self.conf.chipAb.getXMin() y = self.conf.chipAb.getYMin() if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.ID else: orientation = Transformation.Orientation.R1 x += self.padCorner.getAbutmentBox().getWidth() elif self.type == SouthEast: name = 'padcorner_se' x = self.conf.chipAb.getXMax() y = self.conf.chipAb.getYMin() if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.R1 else: orientation = Transformation.Orientation.R2 x += self.padCorner.getAbutmentBox().getWidth() y += self.padCorner.getAbutmentBox().getHeight() elif self.type == NorthEast: name = 'padcorner_ne' x = self.conf.chipAb.getXMax() y = self.conf.chipAb.getYMax() if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.R2 else: orientation = Transformation.Orientation.R3 x -= self.padCorner.getAbutmentBox().getwidth() y -= self.padCorner.getAbutmentBox().getHeight() elif self.type == NorthWest: name = 'padcorner_nw' x = self.conf.chipAb.getXMin() y = self.conf.chipAb.getYMax() if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.R3 else: orientation = Transformation.Orientation.ID y -= self.padCorner.getAbutmentBox().getHeight() return name, Transformation( self.corona.toGrid(x), self.corona.toGrid(y), orientation ) def _instanciateCorner ( self ): name, transformation = self._getTransformation() corner = Instance.create( self.conf.cell , name, self.corona.padCorner , transformation , Instance.PlacementStatus.FIXED ) def doLayout ( self ): if self.corona.padCorner: self._instanciateCorner() else: self._createCorner() # -------------------------------------------------------------------- # Class : "pads.Side" class Side ( object ): def __init__ ( self, corona, sideType ): self.type = sideType self.corona = corona self.pins = [] self.u = self.conf.ioPadHeight self.spacerCount = 0 self.gap = 0 self.coreWires = [] if self.type == North: self.pads = self.corona.northPads self.sideName = 'north' self.sideLength = self.conf.chipAb.getWidth() elif self.type == South: self.pads = self.corona.southPads self.sideName = 'south' self.sideLength = self.conf.chipAb.getWidth() elif self.type == East: self.pads = self.corona.eastPads self.sideName = 'east' self.sideLength = self.conf.chipAb.getHeight() elif self.type == West: self.pads = self.corona.westPads self.sideName = 'west' self.sideLength = self.conf.chipAb.getHeight() else: raise ErrorMessage( 1, 'Pads.Side.__init__(): Invalid value for sideType ({})'.format(sideType)) self.spacerNames = 'padspacer_' + self.sideName + '_%d' @property def conf ( self ): return self.corona.conf def toGrid ( self, u ): return self.corona.toGrid( u ) def getAxis ( self, i ): if self.type == North: return self.conf.chipAb.getYMax() - self.conf.ioPadHeight + self.corona.powerRails[i][2] elif self.type == South: return self.conf.chipAb.getYMin() + self.conf.ioPadHeight - self.corona.powerRails[i][2] elif self.type == East: return self.conf.chipAb.getXMax() - self.conf.ioPadHeight + self.corona.powerRails[i][2] elif self.type == West: return self.conf.chipAb.getXMin() + self.conf.ioPadHeight - self.corona.powerRails[i][2] else: raise ErrorMessage( 1, 'Pads.Side.__init__(): Invalid value for sideType ({})'.format(sideType)) return 0 def hasPad ( self, padInstance ): for pad in self.pads: if pad[1] == padInstance: return True return False def addCoreWire ( self, coreWire ): self.coreWires.append( coreWire ) return def updateGap ( self, gapWidth ): self.gap = max( self.gap, gapWidth ) return def _check ( self, checkSize, checkName ): sideName = 'unknown' chipSize = 0 if self.type == North or self.type == South: chipSize = self.conf.chipAb.getWidth() sideName = 'wide' elif self.type == East or self.type == West: chipSize = self.conf.chipAb.getHeight() sideName = 'tall' if checkSize > chipSize: sliceHeight = self.conf.sliceHeight if checkSize % sliceHeight != 0: checkSize += sliceHeight - (checkSize % sliceHeight) raise ErrorMessage( 1, [ 'Chip is not {} enought to accomodate the {},' \ .format(sideName,checkName) , 'needs {}, but only has {}.' \ .format( DbU.getValueString(checkSize), DbU.getValueString(chipSize) ) ] ) return False return True def check ( self ): self.validated = True if self.type == North: self.conf.validated = self._check \ ( self.conf.coronaAb.getWidth() + 2*self.conf.ioPadHeight, 'core' ) checkName = 'north pads' elif self.type == East: self.conf.validated = self._check \ ( self.conf.coronaAb.getHeight() + 2*self.conf.ioPadHeight, 'core' ) checkName = 'east pads' elif self.type == South: checkName = 'south pads' elif self.type == West: checkName = 'west pads' self.conf.validated = self._check( len(self.pads) * self.conf.ioPadStep + 2*self.conf.ioPadHeight , checkName ) and self.conf.validated return self.conf.validated def _fillPadSpacing ( self, gapWidth ): def _getWidth ( spacer ): return spacer.getAbutmentBox().getWidth() def _nextSpacer ( iPadSpacer ): while iPadSpacer < len(self.corona.padSpacers) \ and gapWidth < _getWidth(self.corona.padSpacers[iPadSpacer]): iPadSpacer += 1 return iPadSpacer if not self.corona.padSpacers: self.u += gapWidth return iPadSpacer = 0 iPadSpacer = _nextSpacer( iPadSpacer ) while iPadSpacer < len(self.corona.padSpacers) and gapWidth > 0: gapWidth -= _getWidth( self.corona.padSpacers[iPadSpacer] ) spacer = Instance.create( self.conf.cell , self.spacerNames % self.spacerCount , self.corona.padSpacers[iPadSpacer]) self.spacerCount += 1 self._placePad( spacer ) if gapWidth < _getWidth(self.corona.padSpacers[iPadSpacer]): iPadSpacer = _nextSpacer( iPadSpacer ) if gapWidth != 0: raise ErrorMessage( 1, 'PadsCorona.Side._placePads(): Pad fillers cannot close the gap between pads on {} side, {} remains.' \ .format(self.sideName,DbU.getValueString(gapWidth)) ) self.u += gapWidth def _placePad ( self, padInstance ): if self.type == North: x = self.conf.chipAb.getXMin() + self.u y = self.conf.chipAb.getYMax() if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.MY else: orientation = Transformation.Orientation.ID y -= self.conf.ioPadHeight elif self.type == South: x = self.conf.chipAb.getXMin() + self.u y = self.conf.chipAb.getYMin() if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.ID else: orientation = Transformation.Orientation.MY y += self.conf.ioPadHeight elif self.type == West: x = self.conf.chipAb.getXMin() y = self.conf.chipAb.getYMin() + self.u if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.R3 y += padInstance.getMasterCell().getAbutmentBox().getWidth() else: orientation = Transformation.Orientation.R1 x += padInstance.getMasterCell().getAbutmentBox().getHeight() elif self.type == East: x = self.conf.chipAb.getXMax() y = self.conf.chipAb.getYMin() + self.u if self.corona.padOrient == Transformation.Orientation.ID: orientation = Transformation.Orientation.R1 else: orientation = Transformation.Orientation.R3 x -= padInstance.getMasterCell().getAbutmentBox().getHeight() y += padInstance.getMasterCell().getAbutmentBox().getWidth() padInstance.setTransformation ( Transformation( self.toGrid(x), self.toGrid(y), orientation ) ) padInstance.setPlacementStatus( Instance.PlacementStatus.FIXED ) self.u += padInstance.getMasterCell().getAbutmentBox().getWidth() p = None if self.conf.ioPadGauge.getName() == 'pxlib': p = re.compile( r'p(?Pv[sd]{2}[ei])ck_px' ) if self.conf.ioPadGauge.getName().startswith('phlib'): p = re.compile( r'p(?Pv[sd]{2})ck2_sp' ) if p: m = p.match( padInstance.getMasterCell().getName() ) padName = 'pad' if m: padName = m.group( 'power' ) padNet = padInstance.getMasterCell().getNet( padName ) #print 'padName:', padName, 'padNet:', padNet if padNet: plug = padInstance.getPlug( padNet ) chipNet = plug.getNet() if not chipNet and padNet.isGlobal(): chipNet = padInstance.getCell().getNet( padNet.getName() ) if chipNet: rp = RoutingPad.create( chipNet, Occurrence(plug), RoutingPad.BiggestArea ) return def _placePads ( self ): padLength = 0 for pad in self.pads: padLength += pad[1].getMasterCell().getAbutmentBox().getWidth() padSpacing = (self.sideLength - 2*self.conf.ioPadHeight - padLength) / (len(self.pads) + 1) if self.conf.padsHavePosition: position = self.u for pad in self.pads: trace( 550, '\tPlace pad {} @{}\n'.format(pad[1],DbU.getValueString(pad[0])) ) if pad[0] < position: pad[0] = position trace( 550, '\tShift position @{}\n'.format(DbU.getValueString(pad[0])) ) pad[0] = self.toGrid( pad[0] ) position = pad[0] + pad[1].getMasterCell().getAbutmentBox().getWidth() else: position = self.u for i in range(len(self.pads)): position += padSpacing self.pads[i][0] = self.toGrid( position ) position += self.pads[i][1].getMasterCell().getAbutmentBox().getWidth() for pad in self.pads: self._fillPadSpacing( pad[0] - self.u ) self._placePad( pad[1] ) self._fillPadSpacing( self.sideLength - self.conf.ioPadHeight - self.u ) def _getUMin ( self, box ): if self.type == North or self.type == South: return box.getXMin() return box.getYMin() def _getUMax ( self, box ): if self.type == North or self.type == South: return box.getXMax() return box.getYMax() def _createSegment ( self, rail, uMin, uMax ): net, layer, axis, width = rail if self.conf.ioPadGauge.getName() == 'pxlib': if net.isClock(): uMin -= DbU.fromLambda(5.0) uMax += DbU.fromLambda(5.0) else: uMin -= width/2 uMax += width/2 if self.type == North or self.type == South: if self.type == North: axis = self.conf.chipAb.getYMax() - axis Horizontal.create( net, layer, axis, width, uMin, uMax ) else: if self.type == East: axis = self.conf.chipAb.getXMax() - axis Vertical.create( net, layer, axis, width, uMin, uMax ) def _routePads ( self ): if self.type == South or self.type == North: startCorner = self.conf.chipAb.getXMin() stopCorner = self.conf.chipAb.getXMax() elif self.type == West or self.type == East: startCorner = self.conf.chipAb.getYMin() stopCorner = self.conf.chipAb.getYMax() else: return startCorner += self.conf.ioPadHeight stopCorner -= self.conf.ioPadHeight if len(self.pads) == 0: return padAb = self.conf.getInstanceAb( self.pads[0][1] ) for irail in range(len(self.corona.padRails)): self._createSegment( self.corona.padRails[ irail ] , startCorner , self._getUMin(padAb) ) padAb = self.conf.getInstanceAb( self.pads[-1][1] ) for irail in range(len(self.corona.padRails)): self._createSegment( self.corona.padRails[ irail ] , self._getUMax(padAb) , stopCorner ) for i in range(len(self.pads)-1): padAb1 = self.conf.getInstanceAb( self.pads[i ][1] ) padAb2 = self.conf.getInstanceAb( self.pads[i+1][1] ) for rail in self.corona.padRails: self._createSegment( rail, self._getUMax(padAb1), self._getUMin(padAb2) ) def doLayout ( self ): self._placePads() if not self.corona.padSpacers: self._routePads() def drawCoreWires ( self ): trace( 550, ',+', '\tSide.drawCoreWire()\n' ) if self.type == West or self.type == East: trace( 550, '\tSort East/West\n' ) self.coreWires.sort( key=lambda wire: wire.bbSegment.getCenter().getY() ) if self.type == North or self.type == South: trace( 550, '\tSort North/South\n' ) self.coreWires.sort( key=lambda wire: wire.bbSegment.getCenter().getX() ) for wire in self.coreWires: wire.updateInCorona() size = len(self.coreWires) offset = 0 for i in range(size): if self.coreWires[i].inCoronaRange: break offset += 1 for i in range(offset): self.coreWires[i].setOffset( i+1, CoreWire.AtBegin ) offset = 0 for i in range(1,size+1): if self.coreWires[-i].inCoronaRange: break offset += 1 for i in range(offset): self.coreWires[ -(offset+1) ].setOffset( i+1, CoreWire.AtEnd ) for wire in self.coreWires: if self.type == West or self.type == East: trace( 550, '\t| %s %s %d %s inRange:%s\n' % ( wire.chipNet.getName() , DbU.getValueString(wire.bbSegment.getCenter().getY()) , wire.count , wire.padSegment.getLayer() , wire.inCoronaRange ) ) if self.type == North or self.type == South: trace( 550, '\t| %s %s %d %s inRange:%s\n' % ( wire.chipNet.getName() , DbU.getValueString(wire.bbSegment.getCenter().getX()) , wire.count , wire.padSegment.getLayer() , wire.inCoronaRange ) ) wire.drawWire() trace( 550, '-' ) # -------------------------------------------------------------------- # Class : "pads.CoreWire" class CoreWire ( object ): # Should be read from the symbolic technology rules. viaPitch = DbU.fromLambda( 4.0 ) NoOffset = 0x0000 AtBegin = 0x0001 AtEnd = 0x0002 def __init__ ( self, corona, chipNet, padSegment, bbSegment, side, preferredDir, count ): self.corona = corona self.chipNet = chipNet self.padSegment = padSegment self.bbSegment = bbSegment self.offset = 0 self.offsetType = CoreWire.NoOffset self.side = side self.preferredDir = preferredDir self.inCoronaRange = True self.arraySize = None self.count = count @property def conf ( self ): return self.corona.conf def updateInCorona ( self ): coronaAb = self.conf.getInstanceAb( self.conf.icorona ) if self.side == South or self.side == North: xCoronaPin = self.bbSegment.getCenter().getX() if xCoronaPin <= coronaAb.getXMin(): self.inCoronaRange = False elif xCoronaPin >= coronaAb.getXMax(): self.inCoronaRange = False if self.side == East or self.side == West: yCoronaPin = self.bbSegment.getCenter().getY() if yCoronaPin <= coronaAb.getYMin(): self.inCoronaRange = False elif yCoronaPin >= coronaAb.getYMax(): self.inCoronaRange = False def setOffset ( self, offset, offsetType ): self.offset = offset self.offsetType = offsetType def _computeCoreLayers ( self ): rg = self.conf.routingGauge mask = self.padSegment.getLayer().getMask() trace( 550, ',+', '\tCoreWire._computeCoreLayers()\n' ) trace( 550, '\tbbSegment: {}\n'.format(self.bbSegment) ) self.symSegmentLayer = None for layerGauge in rg.getLayerGauges(): if layerGauge.getDepth() > self.conf.topLayerDepth: break if layerGauge.getLayer().getMask() == mask: self.symSegmentLayer = layerGauge.getLayer() if self.preferredDir: self.symContactLayer = self.symSegmentLayer if self.side & (West|East): self.symContactSize = ( layerGauge.getWireWidth(), self.bbSegment.getHeight() ) else: self.symContactSize = ( self.bbSegment.getWidth(), layerGauge.getWireWidth() ) else: depth = layerGauge.getDepth() if layerGauge.getDepth() + 1 > self.conf.topLayerDepth: self.symSegmentLayer = rg.getLayerGauge( depth-1 ).getLayer() depth -= 1 else: self.symSegmentLayer = rg.getLayerGauge( depth+1 ).getLayer() self.symContactLayer = rg.getContactLayer( depth ) if self.side & (West|East): self.symContactSize = ( self.bbSegment.getHeight(), self.bbSegment.getHeight() ) else: self.symContactSize = ( self.bbSegment.getWidth(), self.bbSegment.getWidth() ) contactMinSize = 2*self.symContactLayer.getEnclosure( self.symSegmentLayer.getBasicLayer() , Layer.EnclosureH|Layer.EnclosureV ) \ + self.symContactLayer.getMinimalSize() arrayWidth = self.symContactSize[0] arrayCount = (arrayWidth - contactMinSize) / CoreWire.viaPitch trace( 550, '\tCoreWire.viaPitch: {}l\n'.format(DbU.toLambda(CoreWire.viaPitch)) ) trace( 550, '\tcontactMinSize: {}l, arrayWidth: {}l, arrayCount: {}\n' \ .format(DbU.toLambda(contactMinSize),DbU.toLambda(arrayWidth),arrayCount) ) if arrayCount < 0: arrayCount = 0 if arrayCount < 3: if self.side & (North|South): self.arraySize = ( arrayCount+1, 2 ) else: self.arraySize = ( 2, arrayCount+1 ) trace( 550, '\tarraySize = ({},{})\n'.format(self.arraySize[0], self.arraySize[1]) ) trace( 550, ',-' ) return raise ErrorMessage( 1, 'CoreWire._computeCoreLayers(): Layer of IO pad segment "%s" is not in routing gauge.' \ .format(self.chipNet.getName()) ) trace( 550, ',-' ) def drawWire ( self ): trace( 550, ',+', '\tCoreWire.drawWire(): chip:"{}"\n'.format(self.chipNet.getName()) ) coronaAb = self.conf.getInstanceAb( self.conf.icorona ) self._computeCoreLayers() padLayer = self.padSegment.getLayer() if not isinstance(padLayer,BasicLayer): padLayer = padLayer.getBasicLayer() if self.side == West or self.side == East: flags = OnHorizontalPitch hPitch = self.conf.getPitch( self.symSegmentLayer ) vPitch = self.conf.getPitch( self.padSegment.getLayer() ) yCore = self.conf.toCoronaPitchInChip( self.bbSegment.getCenter().getY(), self.symSegmentLayer ) if self.offset: if self.offsetType == CoreWire.AtBegin: yCore += 2*hPitch*self.offset else: yCore -= 2*hPitch*self.offset if self.side == West: accessDirection = Pin.Direction.WEST xPadMin = self.bbSegment.getXMin() xContact = self.corona.coreSymBb.getXMin() - self.offset * 2*vPitch xPadMax = xContact xCore = coronaAb.getXMin() if not self.preferredDir: xPadMax += self.bbSegment.getHeight()/2 else: accessDirection = Pin.Direction.EAST xPadMax = self.bbSegment.getXMax() xContact = self.corona.coreSymBb.getXMax() + self.offset * 2*vPitch xPadMin = xContact xCore = coronaAb.getXMax() if not self.preferredDir: xPadMin -= self.bbSegment.getHeight()/2 hReal = Horizontal.create( self.chipNet , self.padSegment.getLayer() , self.bbSegment.getCenter().getY() , self.bbSegment.getHeight() , xPadMin , xPadMax ) trace( 550, '\tself.arraySize: %s\n' % str(self.arraySize) ) if self.arraySize: contacts = self.conf.coronaContactArray( self.chipNet , self.symContactLayer , xContact , yCore , self.arraySize , flags ) vStrapBb = Box() for contact in contacts: vStrapBb.merge( contact.getBoundingBox() ) else: contact = self.conf.coronaContact( self.chipNet , self.symContactLayer , xContact , yCore , self.symContactSize[0] , self.symContactSize[1] , flags ) vStrapBb = contact.getBoundingBox( padLayer ) hRealBb = hReal.getBoundingBox( padLayer ) self.conf.icorona.getTransformation().applyOn( vStrapBb ) vStrapBb.merge( vStrapBb.getXMin(), hRealBb.getYMin() ) vStrapBb.merge( vStrapBb.getXMin(), hRealBb.getYMax() ) if self.padSegment.getLayer().isSymbolic(): vStrapBb.inflate( 0, -self.padSegment.getLayer().getExtentionCap() , 0, -self.padSegment.getLayer().getExtentionCap() ) Vertical.create( self.chipNet , self.padSegment.getLayer() , vStrapBb.getCenter().getX() , vStrapBb.getWidth() , vStrapBb.getYMin() , vStrapBb.getYMax() ) if self.arraySize: if self.side == West: xContact = min( xContact, vStrapBb.getXMin() ) else: xContact = max( xContact, vStrapBb.getXMax() ) self.conf.coronaHorizontal( self.chipNet , self.symSegmentLayer , yCore , self.bbSegment.getHeight() , xContact , xCore ) trace( 550, '\tCORONA PIN: {} {}\n'.format(self.chipNet, self.count) ) pin = self.conf.coronaPin( self.chipNet , self.count , accessDirection , self.symSegmentLayer , xCore , yCore , DbU.fromLambda( 1.0 ) , self.bbSegment.getHeight() ) else: flags = OnVerticalPitch hPitch = self.conf.getPitch( self.padSegment.getLayer() ) vPitch = self.conf.getPitch( self.symSegmentLayer ) trace( 550, '\t%s translated of %s\n' % (self.symSegmentLayer, DbU.getValueString( 2*vPitch*self.offset )) ) xCore = self.conf.toCoronaPitchInChip( self.bbSegment.getCenter().getX(), self.symSegmentLayer ) if self.offset: if self.offsetType == CoreWire.AtBegin: xCore += 2*vPitch*self.offset else: xCore -= 2*vPitch*self.offset if self.side == South: accessDirection = Pin.Direction.SOUTH yPadMin = self.bbSegment.getYMin() yPadMax = self.corona.coreSymBb.getYMin() - self.offset * 2*hPitch yContact = yPadMax yCore = coronaAb.getYMin() if not self.preferredDir: yPadMax += self.bbSegment.getWidth()/2 else: accessDirection = Pin.Direction.NORTH yPadMax = self.bbSegment.getYMax() yPadMin = self.corona.coreSymBb.getYMax() + self.offset * 2*hPitch yContact = yPadMin yCore = coronaAb.getYMax() if not self.preferredDir: yPadMin -= self.bbSegment.getWidth()/2 vReal = Vertical.create( self.chipNet , self.padSegment.getLayer() , self.bbSegment.getCenter().getX() , self.bbSegment.getWidth() , yPadMin , yPadMax ) trace( 550, '\tself.arraySize: %s\n' % str(self.arraySize) ) if self.arraySize: contacts = self.conf.coronaContactArray( self.chipNet , self.symContactLayer , xCore , yContact , self.arraySize , flags ) hStrapBb = Box() for contact in contacts: hStrapBb.merge( contact.getBoundingBox() ) else: contact = self.conf.coronaContact( self.chipNet , self.symContactLayer , self.bbSegment.getCenter().getX() , yContact , self.symContactSize[0] , self.symContactSize[1] , flags ) hStrapBb = contact.getBoundingBox( padLayer ) vRealBb = vReal.getBoundingBox() self.conf.icorona.getTransformation().applyOn( hStrapBb ) hStrapBb.merge( vRealBb.getXMin(), hStrapBb.getYMin() ) hStrapBb.merge( vRealBb.getXMax(), hStrapBb.getYMin() ) if self.padSegment.getLayer().isSymbolic(): hStrapBb.inflate( -self.padSegment.getLayer().getExtentionCap(), 0 , -self.padSegment.getLayer().getExtentionCap(), 0 ) Horizontal.create( self.chipNet , self.padSegment.getLayer() , hStrapBb.getCenter().getY() , hStrapBb.getHeight() , hStrapBb.getXMin() , hStrapBb.getXMax() ) if self.arraySize: if self.side == South: yContact = min( yContact, hStrapBb.getYMin() ) else: yContact = max( yContact, hStrapBb.getYMax() ) trace( 550, '\txCore: %sl %s\n' % (DbU.toLambda(xCore), DbU.getValueString(xCore)) ) self.conf.coronaVertical( self.chipNet , self.symSegmentLayer , xCore , self.bbSegment.getWidth() , yContact , yCore ) pin = self.conf.coronaPin( self.chipNet , self.count , accessDirection , self.symSegmentLayer , xCore , yCore , self.bbSegment.getWidth() , DbU.fromLambda( 1.0 ) ) if self.side & North: self.corona.northSide.pins.append( pin ) if self.side & South: self.corona.southSide.pins.append( pin ) if self.side & East : self.corona.eastSide .pins.append( pin ) if self.side & West : self.corona.westSide .pins.append( pin ) trace( 550, '-' ) # -------------------------------------------------------------------- # Class : "pads.Corona" class Corona ( object ): def __init__ ( self, conf ): def _cmpPad ( pad1, pad2): width1 = pad1.getAbutmentBox().getWidth() width2 = pad2.getAbutmentBox().getWidth() if width1 == width2: return 0 if width1 > width2: return -1 return 1 def _dupPads ( padsConf ): duplicateds = [] for ioPadConf in padsConf: for padInstance in ioPadConf.pads: position = 0 if ioPadConf.position is None else ioPadConf.position duplicateds.append( [ position, padInstance ] ) return duplicateds self.conf = conf self.conf.validated = False self.northPads = _dupPads( self.conf.chipConf.northPads ) self.southPads = _dupPads( self.conf.chipConf.southPads ) self.eastPads = _dupPads( self.conf.chipConf.eastPads ) self.westPads = _dupPads( self.conf.chipConf.westPads ) self.northSide = Side( self, North ) self.southSide = Side( self, South ) self.eastSide = Side( self, East ) self.westSide = Side( self, West ) self.corners = { SouthWest : Corner( self, SouthWest ) , SouthEast : Corner( self, SouthEast ) , NorthWest : Corner( self, NorthWest ) , NorthEast : Corner( self, NorthEast ) } self.padLib = None self.padOrient = Transformation.Orientation.ID self.padSpacers = [] self.padCorner = [] self.padRails = [] # [ , [net, layer, axis, width] ] if Cfg.hasParameter('chip.padCoreSide'): if Cfg.getParamString('chip.padCoreSide').asString().lower() == 'south': self.padOrient = Transformation.Orientation.MY self._allPadsAnalysis() if Cfg.hasParameter('chip.padSpacers'): for spacerName in Cfg.getParamString('chip.padSpacers').asString().split(','): spacerCell = self.padLib.getCell( spacerName ) if spacerCell: self.padSpacers.append( spacerCell ) else: raise ErrorMessage( 1, 'Corona.__init__(): Missing spacer cell "{}"'.format(spacerName) ) self.padSpacers.sort( _cmpPad ) if Cfg.hasParameter('chip.padCorner'): self.padCorner = self.padLib.getCell( Cfg.getParamString('chip.padCorner').asString() ) def toGrid ( self, u ): return u - (u % self.conf.ioPadPitch) def validate ( self ): self._allPadsAnalysis() self.conf.validated = self.northSide.check() self.conf.validated = self.southSide.check() and self.conf.validated self.conf.validated = self.eastSide.check() and self.conf.validated self.conf.validated = self.westSide.check() and self.conf.validated return self.conf.validated def hasNorthPad ( self, padInstance ): return self.northSide.hasPad(padInstance) def hasSouthPad ( self, padInstance ): return self.southSide.hasPad(padInstance) def hasEastPad ( self, padInstance ): return self.eastSide.hasPad(padInstance) def hasWestPad ( self, padInstance ): return self.westSide.hasPad(padInstance) def hasPad ( self, padInstance ): if self.hasNorthPad(padInstance): return True if self.hasSouthPad(padInstance): return True if self.hasEastPad (padInstance): return True if self.hasWestPad (padInstance): return True return False def _allPadsAnalysis ( self ): for pad in self.southPads: self._padAnalysis( pad[1] ) if self.padRails: return for pad in self.northPads: self._padAnalysis( pad[1] ) if self.padRails: return for pad in self.eastPads: self._padAnalysis( pad[1] ) if self.padRails: return for pad in self.westPads: self._padAnalysis( pad[1] ) if self.padRails: return def _padAnalysis ( self, padInstance ): if self.padRails: return padCell = padInstance.getMasterCell() ab = padCell.getAbutmentBox() if not self.padLib: self.padLib = padCell.getLibrary() for plug in padInstance.getPlugs(): for component in plug.getMasterNet().getComponents(): if isinstance(component,Horizontal): hspan = Interval( component.getBoundingBox().getXMin() , component.getBoundingBox().getXMax() ) # Specific hack for Alliance pxlib pad library. if self.conf.ioPadGauge.getName() == 'pxlib': if plug.getMasterNet().isClock(): hspan.inflate( DbU.fromLambda(5.0) ) else: hspan.inflate( component.getWidth() / 2 ) if hspan.contains( ab.getXMin() ) or hspan.contains( ab.getXMax() ): duplicate = False if self.padOrient == Transformation.Orientation.ID: axis = component.getY() else: axis = self.conf.ioPadHeight - component.getY() for rail in self.padRails: if rail[0] == plug.getNet() \ and rail[1] == component.getLayer() \ and rail[2] == axis: duplicate = True break if not duplicate: net = plug.getNet() if not net: if plug.getMasterNet().isGlobal(): net = self.conf.cell.getNet( plug.getMasterNet().getName() ) if not net: raise ErrorMessage( 1, 'PadsCorona._padAnalysis(): Ring net "%s" is not connected and there is no global net (in pad \"%s").' \ % plug.getMasterNet().getName(), padCell.getName() ) else: raise ErrorMessage( 1, 'PadsCorona._padAnalysis(): Ring net "%s" is neither connected nor global (in pad \"%s").' \ % plug.getMasterNet().getName(), padCell.getName() ) if net: self.padRails.append( ( net , component.getLayer() , axis , component.getWidth() ) ) def _createCoreWire ( self, chipIntNet, padNet, padInstance, count ): side = None if self.hasSouthPad(padInstance): side = self.southSide elif self.hasNorthPad(padInstance): side = self.northSide elif self.hasEastPad (padInstance): side = self.eastSide elif self.hasWestPad (padInstance): side = self.westSide if not side: return count innerBb = self.conf.chip.getAbutmentBox().inflate( -self.conf.ioPadHeight ) rg = self.conf.routingGauge hsegments = {} vsegments = {} trace( 550, '\tinnerBb: {}\n'.format(innerBb) ) for component in padNet.getExternalComponents(): if isinstance(component,Segment) or isinstance(component,Contact): bb = component.getBoundingBox() padInstance.getTransformation().applyOn( bb ) trace( 550, '\t| External:{} bb:{}\n'.format(component,bb) ) if bb.intersect(innerBb): trace( 550, '\t| Accepted.\n' ) lg = rg.getLayerGauge( component.getLayer() ) depth = lg.getDepth() if depth > self.conf.topLayerDepth: continue if lg.getDirection() == RoutingLayerGauge.Vertical: if not vsegments.has_key(depth): vsegments[ depth ] = [] vsegments[ depth ].append( (component,bb) ) else: if not hsegments.has_key(depth): hsegments[ depth ] = [] hsegments[ depth ].append( (component,bb) ) gapWidth = 0 segments = None inPreferredDir = False if side.type == North or side.type == South: if len(vsegments): inPreferredDir = True segments = vsegments[ min(vsegments.keys()) ] elif len(hsegments): segments = hsegments[ min(hsegments.keys()) ] else: if len(hsegments): inPreferredDir = True segments = hsegments[ min(hsegments.keys()) ] elif len(vsegments): segments = vsegments[ min(vsegments.keys()) ] coreWires = [] if segments: for segment, bb in segments: if not inPreferredDir: if side.type == North or side.type == South: trace( 550, '\tNorth/South "{}" but RDir H, gapWidth: {}\n' \ .format(chipIntNet.getName(),DbU.getValueString(bb.getWidth()) ) ) side.updateGap( bb.getWidth() ) else: trace( 550, '\tEast/West "{}" but RDir V, gapWidth: {}\n' \ .format(chipIntNet.getName(),DbU.getValueString(bb.getHeight())) ) side.updateGap( bb.getHeight() ) side.addCoreWire( CoreWire( self, chipIntNet, segment, bb, side.type, inPreferredDir, count ) ) count += 1 else: if not (chipIntNet.isGlobal() or chipIntNet.isSupply() or chipIntNet.isClock()): raise ErrorMessage( 1, [ 'PadsCorona._createCoreWire(): In I/O pad "{}" ({},{}),' \ .format( padInstance.getMasterCell().getName() , padInstance.getName(), padInstance.getTransformation() ) , 'connector "{}" has no suitable segment for net "{}".' \ .format( padNet, chipIntNet.getName() ) ] ) return count def _placeInnerCorona ( self ): trace( 550, ',+', '\tCorona._placeInnerCorona()\n' ) rg = self.conf.routingGauge coronaSouthGap = 0 for layerGauge in rg.getLayerGauges(): self.southSide.gap = max( self.southSide.gap, layerGauge.getPitch() * 6 ) self.northSide.gap = self.southSide.gap self.eastSide.gap = self.southSide.gap self.westSide.gap = self.southSide.gap for coronaPlug in self.conf.icorona.getPlugs(): chipIntNet = coronaPlug.getNet() if not chipIntNet: trace( 550, '-' ) raise ErrorMessage( 1, 'PadsCorona._placeInnerCorona(): Corona net "{}" is not connected to a pad.' \ .format(coronaPlug.getMasterNet().getName()) ) continue padConnected = 0 doneInstances = [] trace( 550, '\tConnexions on chipIntNet: {}\n'.format(chipIntNet) ) for chipPlug in chipIntNet.getPlugs(): trace( 550, '\t| chipPlug: {}\n'.format(chipPlug) ) doneInstances.append( chipPlug.getInstance() ) padNet = chipPlug.getMasterNet() padConnected = self._createCoreWire( chipIntNet, padNet, doneInstances[-1], padConnected ) if chipIntNet.isGlobal(): for instance in self.conf.cell.getInstances(): if instance in doneInstances: continue doneInstances.append( instance ) padNet = instance.getMasterCell().getNet( chipIntNet.getName() ) if not padNet: continue padConnected = self._createCoreWire( chipIntNet, padNet, doneInstances[-1], padConnected ) if padConnected == 0: trace( 550, '-' ) raise ErrorMessage( 1, 'PadsCorona._placeInnerCorona(): Chip net "{}" is not connected to a pad.' \ .format(chipIntNet.getName()) ) self.conf.setupCorona( self.westSide.gap, self.southSide.gap, self.eastSide.gap, self.northSide.gap ) self.coreSymBb = self.conf.getInstanceAb( self.conf.icorona ) self.coreSymBb.inflate( self.conf.toSymbolic( self.westSide.gap /2, Superior ) , self.conf.toSymbolic( self.southSide.gap/2, Superior ) , self.conf.toSymbolic( self.eastSide.gap /2, Inferior ) , self.conf.toSymbolic( self.northSide.gap/2, Inferior ) ) self.southSide.drawCoreWires() self.northSide.drawCoreWires() self.eastSide .drawCoreWires() self.westSide .drawCoreWires() if len(self.southSide.coreWires) \ and len(self.westSide .coreWires) \ and not self.southSide.coreWires[0].inCoronaRange \ and not self.westSide .coreWires[0].inCoronaRange: print( ErrorMessage( 1, [ 'Corona._placeInnerCorona(): Both pads on south-east corner are outside corona range.' , 'This will generate a short-circuit between S:"{}" and W:"{}".' \ .format( self.southSide.coreWires[0].chipNet.getName() , self.westSide .coreWires[0].chipNet.getName()) ] )) if len(self.southSide.coreWires) \ and len(self.eastSide .coreWires) \ and not self.southSide.coreWires[-1].inCoronaRange \ and not self.eastSide .coreWires[ 0].inCoronaRange: print( ErrorMessage( 1, [ 'Corona._placeInnerCorona(): Both pads on south-west corner are outside corona range.' , 'This will generate a short-circuit between S:"{}" and E:"{}.' \ .format( self.southSide.coreWires[-1].chipNet.getName() , self.eastSide .coreWires[ 0].chipNet.getName()) ] )) if len(self.northSide.coreWires) \ and len(self.westSide .coreWires) \ and not self.northSide.coreWires[ 0].inCoronaRange \ and not self.westSide .coreWires[-1].inCoronaRange: print( ErrorMessage( 1, [ 'Corona._placeInnerCorona(): Both pads on north-east corner are outside corona range.' , 'This will generate a short-circuit between N:"%s" and W:"%s".' % \ ( self.northSide.coreWires[ 0].chipNet.getName() , self.westSide .coreWires[-1].chipNet.getName()) ] )) if len(self.northSide.coreWires) \ and len(self.eastSide .coreWires) \ and not self.northSide.coreWires[-1].inCoronaRange \ and not self.eastSide .coreWires[-1].inCoronaRange: print( ErrorMessage( 1, [ 'Corona._placeInnerCorona(): Both pads on north-west corner are outside corona range.' , 'This will generate a short-circuit between N:"%s" and E:"%s.' % \ ( self.northSide.coreWires[-1].chipNet.getName() , self.eastSide .coreWires[-1].chipNet.getName()) ] )) trace( 550, '-' ) def doLayout ( self ): if not self.conf.validated: return with UpdateSession(): for corner in self.corners.values(): corner.doLayout() self.northSide.doLayout() self.southSide.doLayout() self.eastSide.doLayout() self.westSide.doLayout() self._placeInnerCorona() self.conf.chip.setRouted( True )