successful ring created around add.ap
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 28 Feb 2020 18:35:36 +0000 (18:35 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 28 Feb 2020 18:35:36 +0000 (18:35 +0000)
experiments5/doAlu16.py
experiments5/ringoscillator.py [new file with mode: 0755]

index e827f79bb3466917ec808c95d6169feabb1f89f4..177967cbea0389c2b7bffb9aced45b7e6375eba4 100755 (executable)
@@ -105,7 +105,7 @@ Cfg.getParamBool      ( 'misc.bug'                    ).setBool      ( False   )
 Cfg.getParamBool      ( 'misc.logMode'                ).setBool      ( True   )
 Cfg.getParamBool      ( 'misc.verboseLevel1'          ).setBool      ( True    )
 Cfg.getParamBool      ( 'misc.verboseLevel2'          ).setBool      ( True    )
-#Cfg.getParamInt       ( 'misc.minTraceLevel'          ).setInt       ( 159     )
+Cfg.getParamInt       ( 'misc.minTraceLevel'          ).setInt       ( 159     )
 #Cfg.getParamInt       ( 'misc.maxTraceLevel'          ).setInt       ( 160     )
 Cfg.getParamEnumerate ( 'etesian.effort'              ).setInt       ( 2       )
 Cfg.getParamPercentage( 'etesian.spaceMargin'         ).setPercentage( 20.0    )
@@ -245,17 +245,17 @@ def add ( **kw ):
     UpdateSession.open()
     #net = cell.getNet('b(%d)' % 0)
     net = cell.getNet('vdd')
-    build_downtrace(net, VIA23, metal3, 5, 10, 20)
-    build_downtrace(net, VIA23, metal3, 10, 10, 20)
-    build_crosstrace(net, VIA23, metal3, 5, 10, 10)
-    build_crosstrace(net, VIA23, metal3, 5, 10, 20)
+    build_downtrace(net, VIA23, metal2, -5, -10, -20)
+    build_downtrace(net, VIA23, metal2, -10, -10, -20)
+    build_crosstrace(net, VIA23, metal2, -5, -10, -10)
+    build_crosstrace(net, VIA23, metal2, -5, -10, -20)
     for i in range(16):
         if False:
             net = cell.getNet('b(%d)' % i)
             x = 20.0*i + 10.0 + 10
             y = height-10
             build_downtrace(net, metal2, x, y, y+10)
-    ab.inflate ( l(2.0) )
+    ab.inflate ( l(30.0) )
     cell.setAbutmentBox( ab )
     UpdateSession.close()
 
diff --git a/experiments5/ringoscillator.py b/experiments5/ringoscillator.py
new file mode 100755 (executable)
index 0000000..8451eb5
--- /dev/null
@@ -0,0 +1,714 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import re
+import traceback
+import os.path
+import optparse
+import Cfg
+import Hurricane
+from   Hurricane import DbU
+from   Hurricane import DataBase
+from   Hurricane import UpdateSession
+from   Hurricane import Breakpoint
+from   Hurricane import Box
+from   Hurricane import Transformation
+from   Hurricane import Instance
+from   Hurricane import Contact
+from   Hurricane import Vertical
+from   Hurricane import Horizontal
+from   Hurricane import Pin
+from   Hurricane import Net
+from   Hurricane import NetExternalComponents
+import Viewer
+import CRL
+import Etesian
+import Anabatic
+import Katana
+import Unicorn
+from   helpers   import l, u, n
+import clocktree.ClockTree
+import plugins.RSavePlugin
+import plugins.ClockTreePlugin
+import symbolic.cmos
+
+af = CRL.AllianceFramework.get()
+
+
+def build_crosstrace(net, via, layer, x, x1, y):
+
+    contacts = \
+      [ Contact.create( net, via, l(x), l(y), l(1.0), l(1.0) )
+      , Contact.create( net, via, l(x1), l(y), l(1.0), l(1.0) )
+      ]
+
+    createHorizontal( contacts, l(y), layer )
+    print "slaves", contacts[-1].getSlaveComponents()
+    for component in contacts[-1].getSlaveComponents():
+        NetExternalComponents.setExternal(component)
+
+def build_downtrace(net, via, layer, x, y, y1):
+
+    contacts = \
+      [ Contact.create( net, via, l(x), l(y), l(1.0), l(1.0) )
+      , Contact.create( net, via, l(x), l(y1), l(1.0), l(1.0) )
+      ]
+
+    createVertical( contacts, l(x), layer )
+    print "slaves", contacts[-1].getSlaveComponents()
+    for component in contacts[-1].getSlaveComponents():
+        NetExternalComponents.setExternal(component)
+
+#print "af", dir(af)
+#sys.exit(0)
+
+Cfg.Configuration.pushDefaultPriority( Cfg.Parameter.Priority.UserFile )
+
+cellsTop = '~/alliance-check-toolkit/cells'
+env = af.getEnvironment()
+env.addSYSTEM_LIBRARY( library=cellsTop+'/nsxlib', mode=CRL.Environment.Prepend )
+env.addSYSTEM_LIBRARY( library=cellsTop+'/mpxlib', mode=CRL.Environment.Prepend )
+
+
+Cfg.getParamBool      ( 'misc.catchCore'              ).setBool      ( False   )
+Cfg.getParamBool      ( 'misc.info'                   ).setBool      ( False   )
+Cfg.getParamBool      ( 'misc.paranoid'               ).setBool      ( False   )
+Cfg.getParamBool      ( 'misc.bug'                    ).setBool      ( False   )
+Cfg.getParamBool      ( 'misc.logMode'                ).setBool      ( True   )
+Cfg.getParamBool      ( 'misc.verboseLevel1'          ).setBool      ( True    )
+Cfg.getParamBool      ( 'misc.verboseLevel2'          ).setBool      ( True    )
+Cfg.getParamInt       ( 'misc.minTraceLevel'          ).setInt       ( 159     )
+#Cfg.getParamInt       ( 'misc.maxTraceLevel'          ).setInt       ( 160     )
+Cfg.getParamEnumerate ( 'etesian.effort'              ).setInt       ( 2       )
+Cfg.getParamPercentage( 'etesian.spaceMargin'         ).setPercentage( 20.0    )
+Cfg.getParamPercentage( 'etesian.aspectRatio'         ).setPercentage( 100.0   )
+Cfg.getParamBool      ( 'etesian.uniformDensity'      ).setBool      ( True    )
+Cfg.getParamInt       ( 'anabatic.edgeLenght'         ).setInt       ( 24      )
+Cfg.getParamInt       ( 'anabatic.edgeWidth'          ).setInt       ( 8       )
+Cfg.getParamString    ( 'anabatic.topRoutingLayer'    ).setString    ( 'METAL5')
+Cfg.getParamInt       ( 'katana.eventsLimit'          ).setInt       ( 1000000 )
+Cfg.getParamInt       ( 'katana.hTracksReservedLocal' ).setInt       ( 7       )
+Cfg.getParamInt       ( 'katana.vTracksReservedLocal' ).setInt       ( 6       )
+#Cfg.getParamInt       ( 'clockTree.minimumSide'       ).setInt       ( l(1000) )
+
+env = af.getEnvironment()
+env.setCLOCK( '^clk$|m_clock' )
+env.setPOWER( 'vdd' )
+env.setGROUND( 'vss' )
+
+Cfg.Configuration.popDefaultPriority()
+
+###################
+# add
+
+def add ( cell, **kw ):
+    editor = None
+    if kw.has_key('editor') and kw['editor']:
+        editor = kw['editor']
+
+    db = DataBase.getDB()
+    print db, dir(db)
+    metal2 = DataBase.getDB().getTechnology().getLayer( 'metal2' )
+    metal3 = DataBase.getDB().getTechnology().getLayer( 'metal3' )
+    metal5 = DataBase.getDB().getTechnology().getLayer( 'metal5' )
+
+    #cell = af.getCell( 'add', CRL.Catalog.State.Logical )
+    print cell.getNet('a(0)')
+
+    if not cell:
+        print '[ERROR] Unable to load cell "alu16.vst", aborting .'
+        return False
+    kw[ 'cell' ] = cell
+
+    width = 350.0
+    height = 405.0
+
+    ab = Box( l(    0.0 )
+            , l(    0.0 )
+            , l( width )
+            , l( height ) )
+
+    cellGauge   = af.getCellGauge()
+    spaceMargin = (Cfg.getParamPercentage('etesian.spaceMargin').asPercentage()+5) / 100.0
+    aspectRatio =  Cfg.getParamPercentage('etesian.aspectRatio').asPercentage()    / 100.0
+    clocktree.ClockTree.computeAbutmentBox( cell,
+                                spaceMargin, aspectRatio, cellGauge )
+    ab2 = cell.getAbutmentBox()
+    print "box", ab, ab.getHeight(), ab.getWidth()
+    print "calc box", ab2, ab2.getHeight(), ab2.getWidth()
+
+    #height = ab.getHeight()
+    #width = ab.getWidth()
+
+    #UpdateSession.open()
+    cell.setAbutmentBox( ab )
+
+    for i in range(16):
+        if True:
+            x = 20.0*i + 10.0
+            y = height
+            net = cell.getNet('a(%d)' % i)
+            print "net", net
+            Pin.create( net
+                  , 'a(%d).0' % i
+                  , Pin.Direction.NORTH
+                  , Pin.PlacementStatus.FIXED
+                  , metal3
+                  , l( x ), l( y - 0 )   # Position.
+                  , l( 2.0 )            , l( 2.0 )  # Size.
+                  )
+    for i in range(16):
+        if True:
+            Pin.create( cell.getNet('o(%d)' % i)
+                  , 'o(%d).0' % i
+                  , Pin.Direction.SOUTH
+                  , Pin.PlacementStatus.FIXED
+                  , metal3
+                  , l( 10.0*i + 100.0 ), l( 0)   # Position.
+                  , l( 2.0 )            , l( 2.0 )  # Size.
+                  )
+
+    for i in range(16):
+        if True:
+            net = cell.getNet('b(%d)' % i)
+            x = 20.0*i + 10.0 + 10
+            y = height - 0
+            #build_downtrace(net, metal3, x, y+11, y)
+            #continue
+            Pin.create( net
+                  , 'b(%d).0' % i
+                  , Pin.Direction.NORTH
+                  , Pin.PlacementStatus.FIXED
+                  , metal3
+                  , l( x ), l( y - 0 )   # Position.
+                  , l( 2.0 )            , l( 2.0 )  # Size.
+                  )
+    if False:
+        Pin.create( cell.getNet('rst')
+              , 'p_reset.0'
+              , Pin.Direction.WEST
+              , Pin.PlacementStatus.FIXED
+              , metal2
+              , l(   0.0 )
+              , l( 140.0 )
+              , l(   2.0 )
+              , l(   2.0 )
+              )
+    #UpdateSession.close()
+
+    if True:
+          if editor: editor.setCell( cell )
+
+          etesian = Etesian.EtesianEngine.create(cell)
+          etesian.place()
+
+          katana = Katana.KatanaEngine.create(cell)
+          katana.digitalInit          ()
+          #katana.runNegociatePreRouted()
+          print dir(katana)
+          katana.runGlobalRouter      (0)
+          katana.loadGlobalRouting    ( Anabatic.EngineLoadGrByNet )
+          katana.layerAssign          ( Anabatic.EngineNoNetLayerAssign )
+          katana.runNegociate         ( Katana.Flags.NoFlags )
+          katana.finalizeLayout       ()
+          print dir(katana)
+          success = katana.getSuccessState()
+          katana.destroy()
+
+    if False:
+        VIA23 = DataBase.getDB().getTechnology().getLayer( 'VIA23' )
+        UpdateSession.open()
+        #net = cell.getNet('b(%d)' % 0)
+        net = cell.getNet('vdd')
+        build_downtrace(net, VIA23, metal2, -5, -10, -20)
+        build_downtrace(net, VIA23, metal2, -10, -10, -20)
+        build_crosstrace(net, VIA23, metal2, -5, -10, -10)
+        build_crosstrace(net, VIA23, metal2, -5, -10, -20)
+        for i in range(16):
+            if False:
+                net = cell.getNet('b(%d)' % i)
+                x = 20.0*i + 10.0 + 10
+                y = height-10
+                build_downtrace(net, metal2, x, y, y+10)
+        ab.inflate ( l(30.0) )
+        cell.setAbutmentBox( ab )
+        UpdateSession.close()
+
+    #af.saveCell( cell, CRL.Catalog.State.Views )
+    #plugins.RSavePlugin.ScriptMain( **kw )
+
+
+
+#def toDbU(v): return DbU.fromLambda(v)
+def toDbU(v): return l(v)
+
+
+
+def doBreak(level, message):
+    UpdateSession.close()
+    Breakpoint.stop(level, message)
+    UpdateSession.open()
+
+
+class Model (object):
+
+    HorizontalAccess = 1
+    VerticalAccess = 2
+
+    def __init__(self, modelName):
+        UpdateSession.open()
+        self.fillerCount = 0
+        self.af = CRL.AllianceFramework.get()
+        #self.cell = af.createCell(modelName)
+        self.cell = af.getCell( modelName, CRL.Catalog.State.Logical )
+        self.createNet('vss', direction=Net.Direction.IN,
+                       isExternal=True, isGlobal=True, type=Net.Type.POWER)
+        self.createNet('vdd', direction=Net.Direction.IN,
+                       isExternal=True, isGlobal=True, type=Net.Type.GROUND)
+
+        self.build()
+        UpdateSession.close()
+        return
+
+    def createNet(self, netName, **attributes):
+        net = self.cell.getNet(netName)
+        if not net:
+            net = Net.create(self.cell, netName)
+
+        if 'direction' in attributes:
+            net.setDirection(attributes['direction'])
+        if 'isExternal' in attributes:
+            net.setExternal(attributes['isExternal'])
+        if 'isGlobal' in attributes:
+            net.setGlobal(attributes['isGlobal'])
+        if 'type' in attributes:
+            net.setType(attributes['type'])
+        return net
+
+    def getNet(self, netName):
+        return self.createNet(netName)
+
+    def connect(self, instanceRef, pin, netRef):
+        if isinstance(instanceRef, str):
+            instance = self.getInstance(instanceRef)
+        else:
+            instance = instanceRef
+
+        if isinstance(netRef, str):
+            net = self.getNet(netRef)
+        else:
+            net = netRef
+
+        masterNet = instance.getMasterCell().getNet(pin)
+        if not masterNet:
+            print '[ERROR] Master cell "%s" of instance "%s" ' \
+                  'has no connector named "%s".' \
+                % (instance.getMasterCell().getName(), instance.getName(), pin)
+
+        instance.getPlug(instance.getMasterCell().getNet(pin)).setNet(net)
+        return
+
+    def place(self, instanceRef, x, y, orient):
+        if isinstance(instanceRef, str):
+            instance = self.getInstance(instanceRef)
+        else:
+            instance = instanceRef
+
+        instance.setTransformation(Transformation(x, y, orient))
+        instance.setPlacementStatus(Instance.PlacementStatus.PLACED)
+        return
+
+    def createInstance(self, instanceName, modelRef, portmap={}, transf=None):
+        instance = self.cell.getInstance(instanceName)
+        if not instance:
+            if isinstance(modelRef, str):
+                model = af.getCell(modelRef, CRL.Catalog.State.Views)
+            else:
+                model = modelRef
+            instance = Instance.create(self.cell, instanceName, model)
+            for pin, net in portmap.items():
+                self.connect(instance, pin, net)
+
+            if transf:
+                self.place(instance, transf[0], transf[1], transf[2])
+        return instance
+
+    def createAccess(self, termPath, x, y, flags):
+        insName, pinName = termPath.split('.')
+        instance = self.cell.getInstance(insName)
+        if not instance:
+            print '[ERROR] Model "%s" has no instance named "%s"' \
+                % (self.cell.getName(), insName)
+        try:
+            plug = instance.getPlug(instance.getMasterCell().getNet(pinName))
+        except BaseException:
+            print '[ERROR] Model "%s" of instance "%s" has ' \
+                  'no terminal named "%s"' \
+                % (instance.getMasterCell().getName(),
+                   instance.getName(), pinName)
+
+        net = plug.getNet()
+        VIA12 = self.getLayer('VIA12')
+        VIA23 = self.getLayer('VIA23')
+        METAL2 = self.getLayer('METAL2')
+
+        rp = RoutingPad.create(
+            net, Occurrence(plug), RoutingPad.BiggestArea)
+        rpCenter = rp.getPosition()
+
+        if y is None:
+            y = rpCenter.getY()
+        contact1 = Contact.create(rp, VIA12, toDbU(0.0), y - rpCenter.getY())
+
+        if flags & Model.VerticalAccess:
+            contact2 = contact1
+            contact1 = Contact.create(net, VIA23, x, y)
+            Horizontal.create(contact2, contact1, METAL2, y, toDbU(2.0))
+        return contact1
+
+    def createVertical(self, contacts, x, width=None, layer=None):
+        def yincrease(lhs, rhs): return int(lhs.getY() - rhs.getY())
+
+        contacts.sort(yincrease)
+
+        if width is None:
+            width = toDbU(2.0)
+
+        if layer is None:
+            layer = self.getLayer("METAL3")
+        for i in range(1, len(contacts)):
+            Vertical.create(contacts[i - 1], contacts[i], layer, x, width)
+        return
+
+    def createHorizontal(self, contactPaths, y, width=None, layer=None):
+        def xincrease(lhs, rhs): return int(lhs.getX() - rhs.getX())
+
+        if isinstance(contactPaths[0], str):
+            contacts = []
+            for termPath in contactPaths:
+                contacts.append(self.createAccess(
+                    termPath, None, y, Model.HorizontalAccess))
+        else:
+            contacts = contactPaths
+
+        if width is None:
+            width = toDbU(2.0)
+
+        contacts.sort(xincrease)
+
+        if layer is None:
+            layer = self.getLayer("METAL2")
+        for i in range(1, len(contacts)):
+            Horizontal.create(contacts[i - 1], contacts[i], layer, y, width)
+        return
+
+    def createSerpentine(
+            self, contactPaths, ymin, ymax, width=None, layer=None):
+        def xincrease(lhs, rhs): return int(lhs.getX() - rhs.getX())
+
+        if isinstance(contactPaths[0], str):
+            contacts = []
+            for termPath in contactPaths:
+                contacts.append(self.createAccess(
+                    termPath, None, None, Model.HorizontalAccess))
+        else:
+            contacts = contactPaths
+
+        if len(contacts) != 2:
+            print '[ERROR] Model.createSerpentine() takes exactly ' \
+                  'two points, not %d.' % len( contacts)
+
+        if layer is None:
+            layer = self.getLayer("METAL2")
+        if width is None:
+            width = toDbU(2.0)
+
+        contacts.sort(xincrease)
+
+        turn0 = contacts[0]
+        trackPitch = toDbU(5.0)
+        for i in range((contacts[1].getX() - contacts[0].getX()) / trackPitch):
+            y = ymin
+            if i % 2:
+                y = ymax
+
+            x = turn0.getX()
+            turn1 = Contact.create(
+                turn0.getNet(), layer, x, y, width, width)
+            turn2 = Contact.create(
+                turn0.getNet(), layer, x + trackPitch, y, width, width)
+            Vertical  .create(turn0, turn1, layer, x, width)
+            Horizontal.create(turn1, turn2, layer, y, width)
+
+            turn0 = turn2
+
+        Vertical.create(turn0, contacts[1], layer, contacts[1].getX(), width)
+
+        return
+
+    def addFillersRow(self, x, y, orient, length):
+        tieWidth = self.getMasterCell("tie_x0").getAbutmentBox().getWidth()
+        i = 0
+        for i in range(length / tieWidth):
+            self.createInstance(
+                "filler_%d_i" %
+                self. fillerCount, "tie_x0", transf=(
+                    x + tieWidth * i, y, orient))
+            self.fillerCount += 1
+        if length % tieWidth:
+            delta = 0
+            if length > tieWidth:
+                delta = 1
+            self.createInstance("filler_%d_i" % self.fillerCount, "rowend_x0",
+                                transf=(x + tieWidth * (i + delta), y, orient))
+            self.fillerCount += 1
+        return
+
+    def getLayer(self, name):
+        return DataBase.getDB().getTechnology().getLayer(name)
+
+    def getCell(self): return self.cell
+
+    def getMasterCell( self, name):
+        return self.af.getCell(name, CRL.Catalog.State.Views)
+
+    def setAbutmentBox(self, ab): self.cell.setAbutmentBox(ab)
+
+    def getAbutmentBox(self): return self.cell.getAbutmentBox()
+
+    def getCellWidth(self, name):
+        return self.getMasterCell(name).getAbutmentBox().getWidth()
+
+    def save(self):
+        self.af.saveCell(self.cell, CRL.Catalog.State.Physical)
+
+    def build(self):
+        print '[ERROR] Model.build() base class method should never be called.'
+
+
+class Add (Model):
+
+    def build(self):
+        #self.setAbutmentBox(
+            #Box(toDbU(0.0), toDbU(0.0), toDbU(1595.0), toDbU(450.0)))
+        add(self.cell)
+        self.buildPower()
+
+    def addFillers(self):
+        self.addFillersRow(toDbU(1580.0), toDbU(0.0),
+                           Transformation.Orientation.ID, toDbU(15.0))
+        self.addFillersRow(toDbU(1570.0), toDbU(100.0),
+                           Transformation.Orientation.MY, toDbU(5.0))
+        self.addFillersRow(toDbU(1570.0), toDbU(100.0),
+                           Transformation.Orientation.ID, toDbU(25.0))
+        self.addFillersRow(toDbU(1530.0), toDbU(200.0),
+                           Transformation.Orientation.MY, toDbU(45.0))
+        self.addFillersRow(toDbU(1550.0), toDbU(200.0),
+                           Transformation.Orientation.ID, toDbU(10.0))
+        self.addFillersRow(toDbU(1550.0), toDbU(300.0),
+                           Transformation.Orientation.MY, toDbU(25.0))
+        self.addFillersRow(toDbU(1570.0), toDbU(300.0),
+                           Transformation.Orientation.ID, toDbU(25.0))
+        self.addFillersRow(toDbU(1550.0), toDbU(400.0),
+                           Transformation.Orientation.MY, toDbU(25.0))
+
+    def buildPower(self):
+        METAL1 = self.getLayer("METAL1")
+        METAL2 = self.getLayer("METAL2")
+        METAL3 = self.getLayer("METAL3")
+        VIA12 = self.getLayer("VIA12")
+        VIA23 = self.getLayer("VIA23")
+        powerWidth = toDbU(2.0)
+        powerSpacing = toDbU(5.0)
+        ab = self.getAbutmentBox()
+        vdd = self.getNet("vdd")
+        vss = self.getNet("vss")
+        vddAxis = Box(ab).inflate(powerSpacing + powerWidth / 2)
+        vssAxis = Box(ab).inflate(
+            powerSpacing + powerWidth / 2 + powerWidth + toDbU(5.0))
+
+       # Building "vdd" power ring.
+        westContactsVdd = [
+            Contact.create( vdd, VIA23, vddAxis.getXMin(), vddAxis.getYMin(),
+                            powerWidth, powerWidth),
+            Contact.create( vdd, VIA23, vddAxis.getXMin(), vddAxis.getYMax(),
+                            powerWidth, powerWidth)]
+        eastContactsVdd = [
+            Contact.create( vdd, VIA23, vddAxis.getXMax(), vddAxis.getYMin(),
+                            powerWidth, powerWidth),
+            Contact.create( vdd, VIA23, vddAxis.getXMax(), vddAxis.getYMax(),
+                            powerWidth, powerWidth)]
+
+        for i in range(0):
+            ywidth = toDbU(12.0)
+            if i == 4:
+                ywidth = toDbU(6.0)
+            yaxis = ab.getXMin() + toDbU(50.0) * (1 + i * 2)
+
+            westContact = Contact.create( vdd, VIA12, vddAxis.getXMin(),
+                                          yaxis, powerWidth, ywidth)
+            eastContact = Contact.create( vdd, VIA12, vddAxis.getXMax(),
+                                          yaxis, powerWidth, ywidth)
+            self.createHorizontal( [westContact, eastContact],
+                                   yaxis, ywidth, layer=METAL1)
+
+            westContact = Contact.create(
+                vdd, VIA23, vddAxis.getXMin(), yaxis, powerWidth, ywidth)
+            eastContact = Contact.create(
+                vdd, VIA23, vddAxis.getXMax(), yaxis, powerWidth, ywidth)
+
+            westContactsVdd.insert(-1, westContact)
+            eastContactsVdd.insert(-1, eastContact)
+
+        self.createVertical(westContactsVdd, vddAxis.getXMin(), powerWidth)
+        self.createVertical(eastContactsVdd, vddAxis.getXMax(), powerWidth)
+
+        xcenter = vddAxis.getCenter().getX() + powerWidth / 2 + toDbU(5.0)
+        if False:
+            accessContactsVdd = [
+            Contact.create(
+                vdd,
+                VIA23,
+                xcenter,
+                vddAxis.getYMax(),
+                powerWidth,
+                powerWidth),
+            Contact.create(
+                vdd,
+                METAL3,
+                xcenter,
+                toDbU(528.0),
+                powerWidth,
+                toDbU(2.0))]
+
+        northContactsVdd = \
+            [westContactsVdd[-1], 
+                    #accessContactsVdd[0],
+                    eastContactsVdd[-1]
+             ]
+        southContactsVdd = \
+            [westContactsVdd[0], eastContactsVdd[0]
+             ]
+        self.createHorizontal(
+            southContactsVdd, vddAxis.getYMin(), powerWidth)
+        self.createHorizontal(
+            northContactsVdd, vddAxis.getYMax(), powerWidth)
+        #self.createVertical(
+            #accessContactsVdd, xcenter, powerWidth)
+        #for component in accessContactsVdd[-1].getSlaveComponents():
+            #NetExternalComponents.setExternal(component)
+
+       # Building "vss" power ring.
+        westContactsVss = [
+            Contact.create(
+                vss,
+                VIA23,
+                vssAxis.getXMin(),
+                vssAxis.getYMin(),
+                powerWidth,
+                powerWidth),
+            Contact.create(
+                vss,
+                VIA23,
+                vssAxis.getXMin(),
+                vssAxis.getYMax(),
+                powerWidth,
+                powerWidth)]
+        eastContactsVss = [
+            Contact.create(
+                vss,
+                VIA23,
+                vssAxis.getXMax(),
+                vssAxis.getYMin(),
+                powerWidth,
+                powerWidth),
+            Contact.create(
+                vss,
+                VIA23,
+                vssAxis.getXMax(),
+                vssAxis.getYMax(),
+                powerWidth,
+                powerWidth)]
+
+        for i in range(5):
+            ywidth = toDbU(12.0)
+            if i == 0:
+                ywidth = toDbU(6.0)
+            yaxis = ab.getXMin() + toDbU(50.0) * (i * 2)
+
+            westContact = Contact.create(
+                vss, VIA12, vssAxis.getXMin(), yaxis, powerWidth, ywidth)
+            eastContact = Contact.create(
+                vss, VIA12, vssAxis.getXMax(), yaxis, powerWidth, ywidth)
+            self.createHorizontal(
+                [westContact, eastContact], yaxis, ywidth, layer=METAL1)
+
+            westContact = Contact.create(
+                vss, VIA23, vssAxis.getXMin(), yaxis, powerWidth, ywidth)
+            eastContact = Contact.create(
+                vss, VIA23, vssAxis.getXMax(), yaxis, powerWidth, ywidth)
+
+            westContactsVss.insert(-1, westContact)
+            eastContactsVss.insert(-1, eastContact)
+
+        self.createVertical(westContactsVss, vssAxis.getXMin(), powerWidth)
+        self.createVertical(eastContactsVss, vssAxis.getXMax(), powerWidth)
+
+        xcenter = vssAxis.getCenter().getX() - powerWidth / 2 - toDbU(5.0)
+        if False:
+            accessContactsVss = [
+            Contact.create(
+                vss,
+                VIA23,
+                xcenter,
+                vssAxis.getYMax(),
+                powerWidth,
+                powerWidth),
+            Contact.create(
+                vss,
+                METAL3,
+                xcenter,
+                toDbU(528.0),
+                powerWidth,
+                toDbU(2.0))]
+        northContactsVss = \
+            [westContactsVss[-1], 
+                   # accessContactsVss[0],
+                    eastContactsVss[-1]
+             ]
+        southContactsVss = \
+            [westContactsVss[0], eastContactsVss[0]
+             ]
+
+        self.createHorizontal(
+            southContactsVss, vssAxis.getYMin(), powerWidth)
+        self.createHorizontal(
+            northContactsVss, vssAxis.getYMax(), powerWidth)
+        #self.createVertical(
+        #    accessContactsVss, xcenter, powerWidth)
+        #for component in accessContactsVss[-1].getSlaveComponents():
+            #NetExternalComponents.setExternal(component)
+
+        ab.inflate(powerSpacing + powerWidth * 2 + toDbU(20.0))
+        self.setAbutmentBox(ab)
+
+        return
+
+
+def ScriptMain(**kw):
+    editor = None
+    if "editor" in kw and kw["editor"]:
+        editor = kw["editor"]
+
+    ringo = Add('add')
+    ringo.save()
+    if editor:
+        editor.setCell(ringo.getCell())
+        editor.fit()
+    return True
+
+
+if __name__ == "__main__":
+    ScriptMain()
+    sys.exit(0)