Re-enable and check the building of I/O ring with pad spacers.

* New: In cumulus/block.bigvia, add a getBoundingBox() method.

* New: In cumulus/block.configuration.GaugeConf.rpAccess(),
    add a vertical strap segment in case the RP is not high enough to
    accomodate the potential offset of the contact.
      In case of gauge with only two routing layers, if the RP
    is vertically accessed, do not put a VIA12 but just a METAL2
    contact (there will be *no* turn).

* Change: In cumulus/chip.CoreWire.drawWire(), the wire at *chip level*
    going to the pad was shrunk of 3 pitch when *not* in the preferred
    routing direction. Removing it as it creates gaps in some cases.
      This was likely needed for a specific kind of I/O pad so should
    be re-enabled on targeted cases in the future.

* Change: In cumulus/chip.corona.VerticalRail, manage in a smarter way
    the conflicts when a rail is accessed from both sides overlapping
    on an Y position. That is, from the supply I/O pads *and* from the
    *core* supply lines.
      Formerly, we just didn't connect the core power line, which was
    a mistake potentially leaving power rails unconnected (it it did
    occur on both sides).
      Also checks if the conflict really arise, that is, the power lines
    are both on top or bottom.

* Change: In cumulus/chip.pads.Side._placePad(), manage I/O pads with
    a bottom left corner of abutment box *not* at (0,0). Argh!

* Bug: In cumulus/chip.pads, create the filler pad instances in the
    chip, not in the corona.
This commit is contained in:
Jean-Paul Chaput 2023-01-14 12:39:22 +01:00
parent 0e17b91692
commit 181b2e1080
6 changed files with 152 additions and 58 deletions

View File

@ -89,6 +89,12 @@ class BigVia ( object ):
def getNet ( self ): return self.net
def getBoundingBox ( self, depth ):
bb = Box( self.x, self.y )
bb.inflate( self.widths[depth] // 2, self.heights[depth] // 2 )
return bb
def getPlate ( self, metal ):
if not self.hasLayout: return None
for plate in self.plates.values():

View File

@ -319,21 +319,60 @@ class GaugeConf ( object ):
if flags & GaugeConf.OffsetTop2: yoffset = -2
trace( 550, '\tyoffset:{}\n'.format(yoffset) )
if startDepth == 0:
contact1 = Contact.create( rp, self._routingGauge.getContactLayer(0), 0, 0 )
ytrack = self.getTrack( contact1.getY(), self.horizontalDeepDepth, yoffset )
dy = ytrack - contact1.getY()
trace( 550, '\tPut on Y-tracks:{}\n'.format(DbU.getValueString(ytrack)) )
contact1.setDy( dy )
rg = self.routingGauge.getLayerGauge( 0 )
rpContact = Contact.create( rp, rg.getLayer(), 0, 0 )
ytrack = self.getTrack( rpContact.getY(), self.horizontalDeepDepth, yoffset )
contact1 = Contact.create( rp.getNet()
, self._routingGauge.getContactLayer( 0 )
, rpContact.getX()
, ytrack
, rg.getViaWidth()
, rg.getViaWidth()
)
segment = Vertical.create( rpContact
, contact1
, rpContact.getLayer()
, rpContact.getX()
, rg.getWireWidth()
)
#dy = ytrack - contact1.getY()
#trace( 550, '\tPut on Y-tracks:{}\n'.format(DbU.getValueString(ytrack)) )
#contact1.setDy( dy )
else:
contact1 = Contact.create( rp, self._routingGauge.getContactLayer(startDepth), 0, 0 )
ytrack = self.getTrack( contact1.getY(), startDepth, 0 )
dy = ytrack - contact1.getY()
rg = self.routingGauge.getLayerGauge( startDepth )
rpContact = Contact.create( rp, rg.getLayer(), 0, 0 )
ytrack = self.getTrack( rpContact.getY(), startDepth, 0 )
#dy = ytrack - contact1.getY()
contact1 = Contact.create( rp.getNet()
, self._routingGauge.getContactLayer( startDepth )
, rpContact.getX()
, ytrack
, rg.getViaWidth()
, rg.getViaWidth()
)
segment = Vertical.create( rpContact
, contact1
, rpContact.getLayer()
, rpContact.getX()
, rg.getWireWidth()
)
trace( 550, '\trpContact:{}\n'.format( rpContact ))
trace( 550, '\tcontact1: {}\n'.format( contact1 ))
trace( 550, '\tsegment: {}\n'.format( segment ))
startDepth += 1
trace( 550, contact1 )
trace( 550, '\tcontact1={}\n'.format( contact1 ))
if flags & GaugeConf.HAccess: stopDepth = hdepth
else: stopDepth = vdepth
trace( 550, '\tstopDepth:%d\n' % stopDepth )
trace( 550, '\trange(startDepth={},stopDepth={})={}\n' \
.format( startDepth, stopDepth, list(range(startDepth,stopDepth)) ))
if startDepth >= stopDepth:
if not flags & GaugeConf.HAccess:
contact1.setLayer( rpContact.getLayer() )
self._rpToAccess[rp] = contact1
trace( 550, '-' )
return contact1
for depth in range(startDepth,stopDepth):
rg = self.routingGauge.getLayerGauge(depth)

View File

@ -154,12 +154,13 @@ class HTree ( object ):
ckParentNet = qt.bInputPlug(0).getNet()
driverContact = gaugeConf.rpAccessByPlugName( qt.buffers[0], bufferConf.input, ckParentNet )
driverY = driverContact.getY()
trace( 550, '\tdriverContact={}\n'.format( driverContact ))
if qt.bl:
trace( 550, '+,', '\tblContact\n' )
blContact = gaugeConf.rpAccessByPlugName( qt.bl.buffers[0], bufferConf.input, ckNet, GaugeConf.OffsetLeft1 )
trace( 550, ',-', '\tblContact={}\n'.format(blContact) )
if qt.br:
trace( 550, '+,', '\trlContact\n' )
trace( 550, '+,', '\tbrContact\n' )
brContact = gaugeConf.rpAccessByPlugName( qt.br.buffers[0], bufferConf.input, ckNet, GaugeConf.OffsetLeft1 )
trace( 550, ',-', '\tbrContact={}\n'.format(brContact) )
if qt.tl:

View File

@ -203,9 +203,11 @@ class CoreWire ( object ):
coronaAb = self.conf.getInstanceAb( self.conf.icorona )
coronaTransf = self.conf.icorona.getTransformation()
self._computeCoreLayers()
trace( 550, '\tbbSegment: {}\n'.format(self.bbSegment) )
padLayer = self.padSegment.getLayer()
if not isinstance(padLayer,BasicLayer):
padLayer = padLayer.getBasicLayer()
trace( 550, '\tpadLayer={}\n'.format( padLayer ))
if self.side == West or self.side == East:
flags = OnHorizontalPitch
hPitch = self.conf.getPitch( self.symSegmentLayer )
@ -222,18 +224,20 @@ class CoreWire ( object ):
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
trace( 550, '\txPadMin: {}\n'.format(DbU.getValueString( xPadMin )))
#if not self.preferredDir:
# #xPadMax += self.bbSegment.getHeight()//2
# xPadMin += 3*vPitch
# trace( 550, '\txPadMin: {}\n'.format(DbU.getValueString( xPadMin )))
else:
accessDirection = Pin.Direction.EAST
xPadMax = self.bbSegment.getXMax()
xContact = self.corona.coreSymBb.getXMax() + self.offset * 2*vPitch
xPadMin = xContact
xCore = coronaAb.getXMax() - vPitch
if not self.preferredDir:
#xPadMin -= self.bbSegment.getHeight()//2
xPadMin -= 3*vPitch
#if not self.preferredDir:
# #xPadMin -= self.bbSegment.getHeight()//2
# xPadMin -= 3*vPitch
if self.addJumper:
rg = self.conf.routingGauge
gaugeM5 = rg.getLayerGauge( 4 )
@ -296,6 +300,7 @@ class CoreWire ( object ):
, xPadMin
, xPadMax
)
trace( 550, '\thChip={}\n'.format( hChip ))
trace( 550, '\tself.arraySize: %s\n' % str(self.arraySize) )
if self.arraySize:
contacts = self.conf.coronaContactArray( self.chipNet
@ -331,6 +336,7 @@ class CoreWire ( object ):
, xContact
, xCore
)
trace( 550, '\thCorona={}\n'.format( hCorona ))
trace( 550, '\tCORONA PIN: {} {}\n'.format(self.chipNet, self.count) )
pin = self.conf.coronaPin( self.chipNet
, self.count
@ -342,22 +348,26 @@ class CoreWire ( object ):
, self.bbSegment.getHeight()
)
hChipBb = hChip.getBoundingBox( padLayer )
trace( 550, '\thChipBb={}\n'.format( hChipBb ))
vStrapBb.merge( vStrapBb.getXMin(), hChipBb.getYMin() )
vStrapBb.merge( vStrapBb.getXMin(), hChipBb.getYMax() )
hCoronaBb = hCorona.getBoundingBox( padLayer )
#hCoronaBb = hCorona.getBoundingBox( padLayer )
hCoronaBb = hCorona.getBoundingBox()
self.conf.icorona.getTransformation().applyOn( hCoronaBb )
trace( 550, '\thCoronaBb={}\n'.format( hCoronaBb ))
vStrapBb.merge( vStrapBb.getXMin(), hCoronaBb.getYMin() )
vStrapBb.merge( vStrapBb.getXMin(), hCoronaBb.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()
)
v = Vertical.create( self.chipNet
, self.padSegment.getLayer()
, vStrapBb.getCenter().getX()
, vStrapBb.getWidth()
, vStrapBb.getYMin()
, vStrapBb.getYMax()
)
trace( 550, '\tvChip={}\n'.format( v ))
else:
flags = OnVerticalPitch
hPitch = self.conf.getPitch( self.padSegment.getLayer() )

View File

@ -13,8 +13,9 @@
# +-----------------------------------------------------------------+
import sys
import bisect
from operator import methodcaller
from operator import methodcaller, xor
import Cfg
from Hurricane import DbU, Point, Interval, Box, Transformation, \
Path, Occurrence, Net, Contact, Horizontal, \
@ -100,8 +101,7 @@ class HorizontalRail ( Rail ):
return '<HorizontalRail "{}" ({}) @{}>'.format( self.side.getRailNet(self.order).getName()
, self.order
, DbU.getValueString(self.axis) )
def connect ( self, contact ):
def isReachable ( self, contact ):
trace( 550, ',+', '\tTry to connect to: {}\n'.format(self) )
trace( 550, '\tContact {}\n'.format(contact) )
contactBb = contact.getBoundingBox()
@ -132,6 +132,10 @@ class HorizontalRail ( Rail ):
trace( 550, ',-', '\tFailed: overlap with existing contact @{}.\n' \
.format(self.vias[keys[insertIndex-1]][2]) )
return False
trace( 550, ',-' )
return True
def connect ( self, contact ):
viaWidth = contact.getWidth()
viaHeight = self.side.hRailWidth
if self.conf.routingGauge.isSymbolic():
@ -152,7 +156,6 @@ class HorizontalRail ( Rail ):
, DbU.getValueString(contact.getX())
, DbU.getValueString(self.axis)) )
self.vias[ contact.getX() ][1].mergeDepth( self.side.getLayerDepth(contact.getLayer()) )
trace( 550, '-' )
return True
def doLayout ( self ):
@ -218,8 +221,11 @@ class VerticalRail ( Rail ):
, self.side.vRailWidth
)
def connect ( self, contact ):
contactBb = contact.getBoundingBox()
def isReachable ( self, contact ):
railDepth = self.conf.routingGauge.getLayerDepth( self.side.getVLayer() )
contactDepth = self.conf.routingGauge.getLayerDepth( contact.getLayer() )
contactBb = contact.getBoundingBox()
contactAbove = True if contactDepth > railDepth else False
if contactBb.getYMin() < self.side.innerBb.getYMin() \
or contactBb.getYMax() > self.side.innerBb.getYMax():
# XXX turn this into a non-fatal case. ERROR is still printed,
@ -228,7 +234,7 @@ class VerticalRail ( Rail ):
, 'power pad is likely to be to far off north or south.'
, '(core:{})'.format(self.side.innerBb) ] ) )
if contact.getY() in self.vias: return False
trace( 550, ',+', '\tVerticalRail.connect() [{}] @{}\n'.format(self.order,DbU.getValueString(self.axis)) )
trace( 550, ',+', '\tVerticalRail.isReachable() [{}] @{}\n'.format(self.order,DbU.getValueString(self.axis)) )
trace( 550, '\t{}\n'.format(contact) )
keys = list( self.vias.keys() )
keys.sort()
@ -241,20 +247,43 @@ class VerticalRail ( Rail ):
if len(keys) > 0:
if insertIndex < len(keys):
insertPosition = keys[ insertIndex ]
trace( 550, '\tinsertIndex:{}'.format(insertIndex) )
trace( 550, '\tinsertIndex:{}\n'.format(insertIndex) )
trace( 550, '\tCheck NEXT contactBb:{} via:{}\n' \
.format( contactBb
, self.vias[insertPosition][2].getBoundingBox()) )
if contactBb.getYMax() >= self.vias[insertPosition][2].getBoundingBox().getYMin():
, self.vias[insertPosition][1].getBoundingBox(railDepth)) )
if self.vias[insertPosition][1].bottomDepth < railDepth: bigviaAbove = False
elif self.vias[insertPosition][1].topDepth > railDepth: bigviaAbove = True
else:
print( ErrorMessage( 1, [ '{} neither above nor below' \
.format( self.vias[insertPosition][1] ) ] ))
trace( 550, '\tcontactAbove={} bigviaAbove={}\n'.format( contactAbove, bigviaAbove ))
if contactBb.getYMax() >= self.vias[insertPosition][1].getBoundingBox(railDepth).getYMin() \
and not xor(contactAbove,bigviaAbove):
trace( 550, ',--', '\tReject {} intersect NEXT\n'.format(contact) )
return False
if insertIndex > 0:
insertPosition = keys[ insertIndex-1 ]
trace( 550, '\tcheck PREVIOUS contactBb:{} via:{}\n' \
.format( contactBb
, self.vias[keys[insertIndex-1]][2].getBoundingBox()) )
if self.vias[keys[insertIndex-1]][2].getBoundingBox().getYMax() >= contactBb.getYMin():
, self.vias[insertPosition][1].getBoundingBox(railDepth)) )
if self.vias[insertPosition][1].bottomDepth < railDepth: bigviaAbove = False
elif self.vias[insertPosition][1].topDepth > railDepth: bigviaAbove = True
else:
print( ErrorMessage( 1, [ '{} neither above nor below' \
.format( self.vias[insertPosition][1] ) ] ))
trace( 550, '\tcontactAbove={} bigviaAbove={}\n'.format( contactAbove, bigviaAbove ))
if self.vias[insertPosition][1].getBoundingBox(railDepth).getYMax() >= contactBb.getYMin() \
and not xor(contactAbove,bigviaAbove):
trace( 550, ',--', '\tReject {} intersect PREVIOUS\n'.format(contact) )
return False
trace( 550, ',--' )
return True
def connect ( self, contact ):
trace( 550, '\tVerticalRail.connect() {}\n'.format(contact) )
if self.net != contact.getNet():
trace( 550, '\tReject {} vs. {}\n'.format( self.net, contact.getNet() ))
return False
viaWidth = self.side.vRailWidth
viaHeight = contact.getHeight()
if self.conf.routingGauge.isSymbolic():
@ -269,8 +298,8 @@ class VerticalRail ( Rail ):
, contact.getHeight() - DbU.fromLambda(1.0)
, flags=BigVia.AllowAllExpand )
, contact ]
trace(550, ',--' '\tADD {}\n'.format(contact) )
self.vias[ contact.getY() ][1].mergeDepth( self.side.getLayerDepth(contact.getLayer()) )
trace( 550, '\t-> BigVIA {}\n'.format(self.vias[ contact.getY() ][1]) )
return True
@ -348,15 +377,18 @@ class Side ( object ):
for terminal in blockSide.terminals:
trace( 550, '\tterminal:{}\n'.format(terminal) )
for rail in self.rails:
if not rail.isReachable( terminal[1] ):
break
rail.connect( terminal[1] )
def getRailRange ( self, net ):
if net.isClock(): return range(len(self.rails))
if not net.isSupply(): return []
if self.side & HORIZONTAL:
trace( 550, '\tHORIZONTAL rail.\n' )
trace( 550, '\tHorizontal rail.\n' )
return range( len(self.coronaCks), len(self.rails) )
else:
trace( 550, '\tVertical rail.\n' )
trace( 550, '\t{} > {}\n'.format(self.horizontalDepth,self.verticalDepth) )
if self.horizontalDepth > self.verticalDepth:
return range( len(self.coronaCks), len(self.rails) )

View File

@ -150,7 +150,7 @@ class Corner ( object ):
def _instanciateCorner ( self ):
name, transformation = self._getTransformation()
corner = Instance.create( self.conf.cell
corner = Instance.create( self.conf.chip
, name, self.corona.padCorner
, transformation
, Instance.PlacementStatus.FIXED
@ -278,7 +278,7 @@ class Side ( object ):
iPadSpacer = _nextSpacer( iPadSpacer )
while iPadSpacer < len(self.corona.padSpacers) and gapWidth > 0:
gapWidth -= _getWidth( self.corona.padSpacers[iPadSpacer] )
spacer = Instance.create( self.conf.cell
spacer = Instance.create( self.conf.chip
, self.spacerNames % self.spacerCount
, self.corona.padSpacers[iPadSpacer])
self.spacerCount += 1
@ -286,13 +286,14 @@ class Side ( object ):
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)) )
print( 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 ):
padAb = padInstance.getMasterCell().getAbutmentBox()
if self.type == North:
x = self.conf.chipAb.getXMin() + self.u
x = self.conf.chipAb.getXMin() + self.u - padAb.getXMin()
y = self.conf.chipAb.getYMax()
if self.corona.padOrient == Transformation.Orientation.ID:
orientation = Transformation.Orientation.MY
@ -301,7 +302,7 @@ class Side ( object ):
y -= self.conf.ioPadHeight
x = self.toGrid( x )
elif self.type == South:
x = self.conf.chipAb.getXMin() + self.u
x = self.conf.chipAb.getXMin() + self.u - padAb.getXMin()
y = self.conf.chipAb.getYMin()
if self.corona.padOrient == Transformation.Orientation.ID:
orientation = Transformation.Orientation.ID
@ -311,27 +312,29 @@ class Side ( object ):
x = self.toGrid( x )
elif self.type == West:
x = self.conf.chipAb.getXMin()
y = self.conf.chipAb.getYMin() + self.u
y = self.conf.chipAb.getYMin() + self.u + padAb.getXMin()
if self.corona.padOrient == Transformation.Orientation.ID:
orientation = Transformation.Orientation.R3
y += padInstance.getMasterCell().getAbutmentBox().getWidth()
y += padAb.getWidth()
else:
orientation = Transformation.Orientation.R1
x += padInstance.getMasterCell().getAbutmentBox().getHeight()
x += padAb.getHeight()
y = self.toGrid( y )
elif self.type == East:
x = self.conf.chipAb.getXMax()
y = self.conf.chipAb.getYMin() + self.u
if padAb.getXMin():
y += padAb.getXMin() + padAb.getWidth()
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()
x -= padAb.getHeight()
y += padAb.getWidth()
y = self.toGrid( y )
padInstance.setTransformation ( Transformation( x, y, orientation ) )
padInstance.setPlacementStatus( Instance.PlacementStatus.FIXED )
self.u += padInstance.getMasterCell().getAbutmentBox().getWidth()
self.u += padAb.getWidth()
p = None
if self.conf.ioPadGauge.getName() == 'pxlib':
p = re.compile( r'p(?P<power>v[sd]{2}[ei])ck_px' )
@ -510,12 +513,15 @@ 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 _cmpPad ( pad ):
return pad.getAbutmentBox().getWidth()
#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 = []
@ -560,7 +566,7 @@ class Corona ( object ):
if spacerCell: self.padSpacers.append( spacerCell )
else:
raise ErrorMessage( 1, 'Corona.__init__(): Missing spacer cell "{}"'.format(spacerName) )
self.padSpacers.sort( _cmpPad )
self.padSpacers = sorted( self.padSpacers, key=_cmpPad, reverse=True )
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: