1319 lines
66 KiB
Python
1319 lines
66 KiB
Python
|
|
# 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
|
|
from Hurricane import DbU, Point, Transformation, Interval, Box, \
|
|
Path, Occurrence, UpdateSession, Layer, \
|
|
BasicLayer, Net, Pin, Contact, Segment, \
|
|
Horizontal, Vertical, Diagonal, RoutingPad, \
|
|
Instance, DataBase
|
|
import CRL
|
|
from CRL import RoutingGauge, RoutingLayerGauge
|
|
import helpers
|
|
from helpers import trace, l, u, n, onFGrid
|
|
from helpers.io import ErrorMessage, WarningMessage
|
|
from helpers.overlay import UpdateSession
|
|
import plugins.alpha.chip
|
|
from plugins.alpha.block.bigvia import BigVia
|
|
|
|
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 ):
|
|
"""
|
|
Compute the various coordinates to draw the corner for a wire
|
|
positioned at ``axis`` distance from the *border* of the chip.
|
|
xExtend and yExtend are used only in case of 45 degree corners,
|
|
the 0.5 ratio is an approximate of ``sqrt(2) - 1``.
|
|
"""
|
|
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
|
|
xExtend = - onFGrid( long( 0.5 * float(self.conf.ioPadHeight - axis) ) )
|
|
yExtend = xExtend
|
|
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
|
|
xExtend = onFGrid( long( 0.5 * float(self.conf.ioPadHeight - axis) ) )
|
|
yExtend = - xExtend
|
|
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
|
|
xExtend = onFGrid( long( 0.5 * float(self.conf.ioPadHeight - axis) ) )
|
|
yExtend = xExtend
|
|
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
|
|
xExtend = - onFGrid( long( 0.5 * float(self.conf.ioPadHeight - axis) ) )
|
|
yExtend = - xExtend
|
|
return xCorner, yCorner, xBb, yBb, xExtend, yExtend
|
|
|
|
def _createCorner ( self ):
|
|
self.corona.conf.cfg.chip.use45corners = None
|
|
for rail in self.corona.padRails:
|
|
net = rail[0]
|
|
layer = rail[1]
|
|
axis = rail[2]
|
|
width = rail[3]
|
|
xCorner, yCorner, xBb, yBb, xExtend, yExtend = self._getPoints( axis )
|
|
if self.corona.conf.cfg.chip.use45corners:
|
|
Diagonal.create( net, layer, Point(xBb+xExtend,yCorner), Point(xCorner,yBb+yExtend), width )
|
|
Horizontal.create( net, layer, yCorner, width, xBb+xExtend, xBb )
|
|
Vertical .create( net, layer, xCorner, width, yBb+yExtend, yBb )
|
|
else:
|
|
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( x, 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'
|
|
padLength = 2*self.conf.ioPadHeight
|
|
for pad in self.pads:
|
|
padLength += pad[1].getMasterCell().getAbutmentBox().getWidth()
|
|
self.conf.validated = self._check( padLength, 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
|
|
x = self.toGrid( x )
|
|
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
|
|
x = self.toGrid( x )
|
|
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()
|
|
y = self.toGrid( y )
|
|
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()
|
|
y = self.toGrid( y )
|
|
padInstance.setTransformation ( Transformation( x, 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(?P<power>v[sd]{2}[ei])ck_px' )
|
|
if self.conf.ioPadGauge.getName().startswith('phlib'):
|
|
p = re.compile( r'p(?P<power>v[sd]{2})ck2_sp' )
|
|
if self.conf.ioPadGauge.getName() == 'niolib':
|
|
p = re.compile( r'(?P<power>(io)?v[sd]{2})' )
|
|
if p:
|
|
m = p.match( padInstance.getMasterCell().getName() )
|
|
padName = 'pad'
|
|
if m: padName = m.group( 'power' )
|
|
padNet = padInstance.getMasterCell().getNet( padName )
|
|
trace( 550, '\tpadName:{} padNet:{}\n'.format(padName,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:
|
|
spacing = 0
|
|
minSpacing = self.corona.minPadSpacing
|
|
position = self.u
|
|
for i in range(len(self.pads)):
|
|
spacing += padSpacing
|
|
nextPosition = self.toGrid( position + spacing )
|
|
if nextPosition - position >= minSpacing:
|
|
position += spacing
|
|
spacing = 0
|
|
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.
|
|
|
|
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
|
|
self.viaPitch = DbU.fromLambda( 4.0 )
|
|
self.gapWidth = 0
|
|
self._computeCoreLayers()
|
|
|
|
@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() )
|
|
self.viaPitch = self.conf.getViaPitch( self.symContactLayer )
|
|
basicLayer = self.symSegmentLayer
|
|
if basicLayer.isSymbolic():
|
|
basicLayer = basicLayer.getBasicLayer()
|
|
contactMinSize = 2*self.symContactLayer.getEnclosure( basicLayer
|
|
, Layer.EnclosureH|Layer.EnclosureV ) \
|
|
+ self.symContactLayer.getMinimalSize()
|
|
arrayWidth = self.symContactSize[0]
|
|
arrayCount = (arrayWidth - contactMinSize) / self.viaPitch
|
|
trace( 550, '\tcontactMinSize: {}, arrayWidth: {}, arrayCount: {}\n' \
|
|
.format(DbU.getValueString(contactMinSize),DbU.getValueString(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]) )
|
|
self.gapWidth = 4*self.viaPitch
|
|
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
|
|
xPadMin += 3*vPitch
|
|
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
|
|
xPadMin -= 3*vPitch
|
|
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
|
|
# yPadMin += 3*hPitch
|
|
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
|
|
# yPadMin -= 3*hPitch
|
|
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: {:.1f}L {}\n'.format(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, chip ):
|
|
|
|
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.chip = chip
|
|
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] ]
|
|
self.powerCount = 0
|
|
self.conf.cfg.chip.padCoreSide = None
|
|
if self.conf.cfg.chip.padCoreSide.lower() == 'south':
|
|
self.padOrient = Transformation.Orientation.MY
|
|
trace( 550, '\tchip.padCoreSide: {}\n'.format(self.conf.cfg.chip.padCoreSide) )
|
|
self._allPadsAnalysis()
|
|
self.conf.cfg.chip.minPadSpacing = None
|
|
self.conf.cfg.chip.padSpacers = None
|
|
self.conf.cfg.chip.padCorner = None
|
|
if self.conf.cfg.chip.padSpacers is not None:
|
|
for spacerName in self.conf.cfg.chip.padSpacers.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 self.conf.cfg.chip.padCorner is not None:
|
|
self.padCorner = self.padLib.getCell( self.conf.cfg.chip.padCorner )
|
|
if self.conf.cfg.chip.minPadSpacing is None:
|
|
self.conf.cfg.chip.minPadSpacing = 0
|
|
|
|
@property
|
|
def conf ( self ): return self.chip.conf
|
|
|
|
@property
|
|
def supplyRailWidth ( self ): return self.conf.cfg.chip.supplyRailWidth
|
|
|
|
@property
|
|
def supplyRailPitch ( self ): return self.conf.cfg.chip.supplyRailPitch
|
|
|
|
@property
|
|
def minPadSpacing ( self ): return self.conf.cfg.chip.minPadSpacing
|
|
|
|
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 component is None: continue
|
|
bb = component.getBoundingBox()
|
|
hspan = Interval( bb.getXMin(), bb.getXMax() )
|
|
axis = bb.getCenter().getY()
|
|
if isinstance(component,Horizontal):
|
|
width = component.getWidth()
|
|
elif isinstance(component,Vertical):
|
|
width = bb.getHeight()
|
|
else:
|
|
continue
|
|
# Specific hack for Alliance pxlib pad library.
|
|
if self.conf.ioPadGauge.getName() == 'pxlib':
|
|
if isinstance(component,Vertical):
|
|
continue
|
|
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:
|
|
pass
|
|
else:
|
|
axis = self.conf.ioPadHeight - axis
|
|
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, 'Corona._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, 'Corona._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
|
|
, width ) )
|
|
|
|
def _createCoreWire ( self, chipIntNet, padNet, padInstance, count ):
|
|
trace( 550, ',+', '\tCorona._createCoreWire()\n' )
|
|
trace( 550, '\tchipIntNet:{}\n'.format(chipIntNet) )
|
|
trace( 550, '\tpadNet:{}\n'.format(padNet) )
|
|
trace( 550, '\tpadInstance:{}\n'.format(padInstance) )
|
|
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:
|
|
trace( 550, '-' )
|
|
return count
|
|
if chipIntNet.isPower() and not padInstance.getName().startswith('p_vdd'):
|
|
trace( 550, '-' )
|
|
return count
|
|
if chipIntNet.isGround() and not padInstance.getName().startswith('p_vss'):
|
|
trace( 550, '-' )
|
|
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 self.conf.routingGauge.hasPowerSupply():
|
|
if chipIntNet.isPower() or chipIntNet.isGround():
|
|
trace( 550, '\t| Skipped, pads uses distributed power terminals.\n' )
|
|
continue
|
|
if self.conf.chipConf.ioPadGauge == 'LibreSOCIO':
|
|
if chipIntNet.isPower() or chipIntNet.isGround():
|
|
if side.type == North or side.type == South:
|
|
#bb.inflate( -u(0.6), 0 )
|
|
if bb.getWidth() > u(35.0):
|
|
bb.inflate( (u(34.0) - bb.getWidth()) / 2, 0 )
|
|
else:
|
|
#bb.inflate( 0, -u(0.6) )
|
|
if bb.getHeight() > u(35.0):
|
|
bb.inflate( 0, (u(34.0) - bb.getHeight()) / 2 )
|
|
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:
|
|
side.addCoreWire( CoreWire( self, chipIntNet, segment, bb, side.type, inPreferredDir, count ) )
|
|
side.updateGap( side.coreWires[-1].gapWidth )
|
|
#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() )
|
|
count += 1
|
|
else:
|
|
if not (chipIntNet.isGlobal() or chipIntNet.isSupply() or chipIntNet.isClock()):
|
|
trace( 550, '-' )
|
|
raise ErrorMessage( 1, [ 'Corona._createCoreWire(): In I/O pad "{}" ({},{}),' \
|
|
.format( padInstance.getMasterCell().getName()
|
|
, padInstance.getName(), padInstance.getTransformation() )
|
|
, 'connector "{}" has no suitable segment for net "{}".' \
|
|
.format( padNet, chipIntNet.getName() )
|
|
] )
|
|
trace( 550, '-' )
|
|
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:
|
|
if not (chipIntNet.isSupply() and self.conf.routingGauge.hasPowerSupply()):
|
|
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 _supplyToPad ( self, chipNet, coronaNet, coronaAxis, stripeWidth, side ):
|
|
trace( 550, ',+', '\tCorona.Builder._supplyToPads()\n' )
|
|
supplyLayerDepth = self.conf.routingGauge.getPowerSupplyGauge().getDepth()
|
|
supplyLayer = self.conf.routingGauge.getPowerSupplyGauge().getLayer()
|
|
chipLayer = self.conf.getRoutingLayer( self.conf.routingGauge.getPowerSupplyGauge().getDepth() - 1 )
|
|
coronaAb = self.conf.icorona.getAbutmentBox()
|
|
chipAxis = coronaAxis + self.conf.icorona.getTransformation().getTx()
|
|
trace( 550, '\tchipLayer={}\n'.format(chipLayer) )
|
|
for rail in self.padRails:
|
|
net = rail[0]
|
|
layer = rail[1]
|
|
railAxis = rail[2]
|
|
width = rail[3]
|
|
if net != chipNet or chipLayer.getMask() != layer.getMask():
|
|
continue
|
|
if side == North:
|
|
trace( 550, '\tcoronaAb={}\n'.format(coronaAb) )
|
|
trace( 550, '\tcoronaAxis={}\n'.format(DbU.getValueString(coronaAxis)) )
|
|
trace( 550, '\tchipAxis={}\n'.format(DbU.getValueString(chipAxis)) )
|
|
trace( 550, '\trailNet={} <-> {}\n'.format(net,chipNet) )
|
|
trace( 550, '\trailAxis={}\n'.format(DbU.getValueString(railAxis)) )
|
|
Vertical.create( chipNet
|
|
, supplyLayer
|
|
, chipAxis
|
|
, stripeWidth
|
|
, coronaAb.getYMax()
|
|
, self.conf.chipAb.getYMax() - railAxis
|
|
)
|
|
via = BigVia( chipNet
|
|
, supplyLayerDepth
|
|
, chipAxis
|
|
, self.conf.chipAb.getYMax() - railAxis
|
|
, stripeWidth
|
|
, width
|
|
, BigVia.AllowAllExpand
|
|
)
|
|
trace( 550, '\tpower depth: {}\n'.format( self.conf.routingGauge.getPowerSupplyGauge().getDepth() ))
|
|
via.mergeDepth( self.conf.routingGauge.getPowerSupplyGauge().getDepth()-1 )
|
|
via.doLayout()
|
|
pin = Pin.create( coronaNet
|
|
, '{}.{}'.format(coronaNet.getName(),self.powerCount)
|
|
, Pin.Direction.NORTH
|
|
, Pin.PlacementStatus.FIXED
|
|
, supplyLayer
|
|
, coronaAxis
|
|
, self.conf.icorona.getMasterCell().getAbutmentBox().getYMax()
|
|
, stripeWidth
|
|
, DbU.fromLambda( 1.0 )
|
|
)
|
|
trace( 550, '\tpin={}\n'.format(pin) )
|
|
self.powerCount += 1
|
|
elif side == South:
|
|
Vertical.create( chipNet
|
|
, supplyLayer
|
|
, chipAxis
|
|
, stripeWidth
|
|
, self.conf.chipAb.getYMin() + railAxis
|
|
, coronaAb.getYMin()
|
|
)
|
|
via = BigVia( chipNet
|
|
, supplyLayerDepth
|
|
, chipAxis
|
|
, self.conf.chipAb.getYMin() + railAxis
|
|
, stripeWidth
|
|
, width
|
|
, BigVia.AllowAllExpand
|
|
)
|
|
via.mergeDepth( supplyLayerDepth-1 )
|
|
via.doLayout()
|
|
pin = Pin.create( coronaNet
|
|
, '{}.{}'.format(coronaNet.getName(),self.powerCount)
|
|
, Pin.Direction.SOUTH
|
|
, Pin.PlacementStatus.FIXED
|
|
, supplyLayer
|
|
, coronaAxis
|
|
, self.conf.icorona.getMasterCell().getAbutmentBox().getYMin()
|
|
, stripeWidth
|
|
, DbU.fromLambda( 1.0 )
|
|
)
|
|
self.powerCount += 1
|
|
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 )
|
|
|
|
def doPowerLayout ( self ):
|
|
if not self.conf.routingGauge.hasPowerSupply(): return
|
|
with UpdateSession():
|
|
capViaWidth = self.conf.vDeepRG.getPitch()*3
|
|
coreAb = self.conf.coreAb
|
|
powerNet = None
|
|
groundNet = None
|
|
chipPowerNet = None
|
|
chipGroundNet = None
|
|
corona = self.conf.corona
|
|
for net in corona.getNets():
|
|
if net.isPower (): powerNet = net
|
|
if net.isGround(): groundNet = net
|
|
if powerNet:
|
|
if powerNet.isGlobal():
|
|
chipPowerNet = self.conf.chip.getNet( powerNet.getName() )
|
|
else:
|
|
for net in self.conf.chip.getNets():
|
|
if net.getName() == powerNet.getName():
|
|
chipPowerNet = net
|
|
break
|
|
if not chipPowerNet:
|
|
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No core power net not connected in "{}"' \
|
|
.format(self.conf.chip.getName()) )
|
|
else:
|
|
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No power net found in "{}"' \
|
|
.format(corona.getName()) )
|
|
if groundNet:
|
|
if groundNet.isGlobal():
|
|
chipGroundNet = self.conf.chip.getNet( groundNet.getName() )
|
|
else:
|
|
for net in self.conf.chip.getNets():
|
|
if net.getName() == groundNet.getName():
|
|
chipGroundNet = net
|
|
break
|
|
if not chipGroundNet:
|
|
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No core power net not connected in "{}"' \
|
|
.format(self.conf.chip.getName()) )
|
|
else:
|
|
raise ErrorMessage( 1, 'pads.Corona.doPowerLayout(): No ground net found in "{}"' \
|
|
.format(corona.getName()) )
|
|
|
|
icore = self.conf.icore
|
|
xcore = icore.getTransformation().getTx()
|
|
stripeSpecs = []
|
|
stripesNb = int( (coreAb.getWidth() - 8*capViaWidth + self.supplyRailWidth) \
|
|
/ self.supplyRailPitch - 1 )
|
|
offset = (coreAb.getWidth() - self.supplyRailPitch*(stripesNb-1)) / 2
|
|
stripeSpecs.append( [ xcore + capViaWidth/2 , capViaWidth ] )
|
|
stripeSpecs.append( [ xcore + 2*capViaWidth + capViaWidth/2 , capViaWidth ] )
|
|
if self.chip.spares and len(self.chip.spares.rleafX) > 1:
|
|
rleafX = self.chip.spares.rleafX
|
|
spacing = (rleafX[1] - rleafX[0]) / 2
|
|
stepOffset = 0
|
|
step = 1
|
|
trace( 550, '\trleafX\n' )
|
|
for i in range(len(rleafX)):
|
|
trace( 550, '\t| rleafX[{}] = {}\n'.format(i,DbU.getValueString(rleafX[i])))
|
|
if spacing < self.supplyRailPitch:
|
|
stepOffset = 1
|
|
step = 2
|
|
spacing = (rleafX[2] - rleafX[0]) / 2
|
|
if step == 1:
|
|
stripeSpecs.append( [ rleafX[0] - spacing, self.supplyRailWidth ] )
|
|
trace( 550, '\tstripe[N/A] @{}\n'.format(DbU.getValueString(stripeSpecs[-1][0])))
|
|
else:
|
|
stripeSpecs.append( [ rleafX[0], self.supplyRailWidth ] )
|
|
trace( 550, '\tstripe[N/A] @{}\n'.format(DbU.getValueString(stripeSpecs[-1][0])))
|
|
for i in range( stepOffset, len(rleafX)-stepOffset, step ):
|
|
if step == 1:
|
|
stripeSpecs.append( [ rleafX[i], self.supplyRailWidth ] )
|
|
trace( 550, '\tstripe[{}] @{}\n'.format(i,DbU.getValueString(stripeSpecs[-1][0])))
|
|
stripeSpecs.append( [ rleafX[i] + spacing, self.supplyRailWidth ] )
|
|
trace( 550, '\tstripe[{}] @{}\n'.format(i,DbU.getValueString(stripeSpecs[-1][0])))
|
|
else:
|
|
for i in range(stripesNb):
|
|
stripeSpecs.append( [ xcore + offset + i*self.supplyRailPitch
|
|
, self.supplyRailWidth
|
|
] )
|
|
stripeSpecs.append( [ xcore + coreAb.getWidth() - 2*capViaWidth - capViaWidth/2 , capViaWidth ] )
|
|
stripeSpecs.append( [ xcore + coreAb.getWidth() - capViaWidth/2 , capViaWidth ] )
|
|
|
|
trace( 550, '\ticoreAb={}\n'.format(icore.getAbutmentBox()) )
|
|
trace( 550, '\tcapViaWidth={}\n'.format(DbU.getValueString(capViaWidth)))
|
|
for i in range(len(stripeSpecs)):
|
|
if i % 2:
|
|
chipNet = chipPowerNet
|
|
coronaNet = powerNet
|
|
else:
|
|
chipNet = chipGroundNet
|
|
coronaNet = groundNet
|
|
trace( 550, '\tstripe[{}] @{}\n'.format(i,DbU.getValueString(stripeSpecs[i][0])))
|
|
self._supplyToPad( chipNet, coronaNet, stripeSpecs[i][0], stripeSpecs[i][1], North )
|
|
self._supplyToPad( chipNet, coronaNet, stripeSpecs[i][0], stripeSpecs[i][1], South )
|
|
#for istripe in range(stripesNb):
|
|
# trace( 550, '\tistripe={}\n'.format(istripe) )
|
|
# axis = xcore + offset + istripe*self.supplyRailPitch
|
|
# if istripe % 2:
|
|
# coronaNet = groundNet
|
|
# chipNet = chipGroundNet
|
|
# else:
|
|
# coronaNet = powerNet
|
|
# chipNet = chipPowerNet
|
|
# self._supplyToPad( chipNet, coronaNet, axis, North )
|
|
# self._supplyToPad( chipNet, coronaNet, axis, South )
|
|
|