2018-10-18 11:10:01 -05:00
|
|
|
# -*- Mode:Python -*-
|
|
|
|
#
|
|
|
|
# This file is part of the Coriolis Software.
|
|
|
|
# Copyright (c) UPMC 2016-2018, All Rights Reserved
|
|
|
|
#
|
|
|
|
# +-----------------------------------------------------------------+
|
|
|
|
# | C O R I O L I S |
|
|
|
|
# | B o r a - A n a l o g S l i c i n g T r e e |
|
|
|
|
# | |
|
|
|
|
# | Author : Jean-Paul Chaput |
|
|
|
|
# | E-mail : Jean-Paul.Chaput@lip6.fr |
|
|
|
|
# | =============================================================== |
|
|
|
|
# | Python : "./karakaze/AnalogDesign.py" |
|
|
|
|
# +-----------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
Support for mixing real pads & symbolic core. Wrapper around s2r.
* Change: In Hurricane::Error constructors disable the backtrace generation.
(*very* slow).
* Change: In Hurricane::Library::getHierarchicalname(), more compact
naming. Remove the name of the root library.
* New: In Hurricane::Net, new type "FUSED", for component with no net.
More efficient than having one net for each.
* Change: In CellViewer, BreakpointWidget, use Angry Birds icons.
* Change: In CellWidget::State, use the hierarchical name (cached) as key
to the state. This allow to load two cells with the same name but from
different libraries in the widget history.
* Change: In PyGraphics, export "isEnabled()" and "isHighDpi()" functions.
* Change: In CRL/etc/symbolic/cmos/plugin.conf, and
CRL/etc/common/plugin.conf use the physical dimensions converters.
* Change: In CRL/etc/symbolic/cmos/technology.conf, make the GDS layer
table coherent with the default Alliance cmos.rds.
* New: CRL/python/helpers/io.py, put ErrorMessage new implementation here,
along with a new ErrorWidget written in PyQt4. It seems finally that
PyQt4 can be used alongside Coriolis Qt widgets.
New ErrorMessage.catch() static function to manage all exceptions
in except clauses.
* Change: In CRL/python/helpers/, no longer use ErrorMessage.wrapPrint(),
directly print it.
Rewrite the utilities to display Python stack traces "textStacktrace()"
and "showStacktrace()".
* Change: In CRL::AllianceFramework, shorten the names of the libraries.
* Change: In CRL::ApParser & CRL::ApDriver, more accurate translation between
Alliance connectors (C record) and Hurricane::Pin objects. Pin are no
longer made square but thin and oriented in the connecting direction.
Use the new fused net for unnamed components.
* New: In CRL::GdsParser, implementation of SREF parsing, i.e. instances.
Due to the unordered nature of the GDS stream, instances creation are
delayed until the whole stream has been parsed and only then are they
created.
For the sake of reading back Alliance s2r GDS, we assume that any
TEXT following a boundary is the Net name the boundary (component)
belongs to.
Create abutment box for Cells, computed from the bounding box, so
the Hurricane QuadTree could work properly.
Make use of the fused net for unnamed components.
* New: In Cumulus/plugins/chip, complete rewrite of the I/O pad management.
Now we can mix real (foundry) pads and a symbolic core.
To cleanly support the de-coupling between the real part and the
symbolic one we introduce a new intermediary hierarchical level, the
corona. We have now:
Chip --> Pads + Corona --> Core.
At chip level (and if we are using real pads) the layout is fully
real (excepting the corona).
The Corona contains everything that is symbolic. It has symbolic
wires extending outward the abutment box to make contact with the
real wires coming from the pads.
In the pad ring we can use corners instances (or not), pad spacers
or directly draw wires between connectors ring pads.
Provide two flavors: placement only or full place & route.
WARNING: If routing in a second step, *do not route* the *Chip* but
the *Corona*.
* Change: In Cumulus/plugins/clocktree, give the modified Cell an
additional extension of "_cts" (Clock Tree Synthesis) instead of
"_clocked", to follow the common convention.
* New: In cumulus/plugins/S2R.py, encapsulate call to Alliance S2R and
reload the translated Cell in the editor.
* New: In cumulus/plugins/core2chip, provide an utility to automatically
create a chip from a core. To work this plugins must have a basic
understanding of the pad functionalities which may differs from
foundry to foundry. So a base class CoreToChip is created, then for
each supported pad foundry a derived class is added. Currently we
support AMS c35b4 and Alliance symbolic cmos.
* Bug: In Anabatic::Configuration, read the right configuration parameter
"anabatic.topRoutinglayer" (Katana), and not the one for Katabatic...
* Change: In Unicorn/cgt.py, process the plugins in alphabetical order
to ensure a reproductible ordering of the menus...
2019-05-22 07:34:32 -05:00
|
|
|
from Hurricane import *
|
|
|
|
from Hurricane import DataBase
|
2018-10-18 11:10:01 -05:00
|
|
|
import CRL
|
|
|
|
import helpers
|
Support for mixing real pads & symbolic core. Wrapper around s2r.
* Change: In Hurricane::Error constructors disable the backtrace generation.
(*very* slow).
* Change: In Hurricane::Library::getHierarchicalname(), more compact
naming. Remove the name of the root library.
* New: In Hurricane::Net, new type "FUSED", for component with no net.
More efficient than having one net for each.
* Change: In CellViewer, BreakpointWidget, use Angry Birds icons.
* Change: In CellWidget::State, use the hierarchical name (cached) as key
to the state. This allow to load two cells with the same name but from
different libraries in the widget history.
* Change: In PyGraphics, export "isEnabled()" and "isHighDpi()" functions.
* Change: In CRL/etc/symbolic/cmos/plugin.conf, and
CRL/etc/common/plugin.conf use the physical dimensions converters.
* Change: In CRL/etc/symbolic/cmos/technology.conf, make the GDS layer
table coherent with the default Alliance cmos.rds.
* New: CRL/python/helpers/io.py, put ErrorMessage new implementation here,
along with a new ErrorWidget written in PyQt4. It seems finally that
PyQt4 can be used alongside Coriolis Qt widgets.
New ErrorMessage.catch() static function to manage all exceptions
in except clauses.
* Change: In CRL/python/helpers/, no longer use ErrorMessage.wrapPrint(),
directly print it.
Rewrite the utilities to display Python stack traces "textStacktrace()"
and "showStacktrace()".
* Change: In CRL::AllianceFramework, shorten the names of the libraries.
* Change: In CRL::ApParser & CRL::ApDriver, more accurate translation between
Alliance connectors (C record) and Hurricane::Pin objects. Pin are no
longer made square but thin and oriented in the connecting direction.
Use the new fused net for unnamed components.
* New: In CRL::GdsParser, implementation of SREF parsing, i.e. instances.
Due to the unordered nature of the GDS stream, instances creation are
delayed until the whole stream has been parsed and only then are they
created.
For the sake of reading back Alliance s2r GDS, we assume that any
TEXT following a boundary is the Net name the boundary (component)
belongs to.
Create abutment box for Cells, computed from the bounding box, so
the Hurricane QuadTree could work properly.
Make use of the fused net for unnamed components.
* New: In Cumulus/plugins/chip, complete rewrite of the I/O pad management.
Now we can mix real (foundry) pads and a symbolic core.
To cleanly support the de-coupling between the real part and the
symbolic one we introduce a new intermediary hierarchical level, the
corona. We have now:
Chip --> Pads + Corona --> Core.
At chip level (and if we are using real pads) the layout is fully
real (excepting the corona).
The Corona contains everything that is symbolic. It has symbolic
wires extending outward the abutment box to make contact with the
real wires coming from the pads.
In the pad ring we can use corners instances (or not), pad spacers
or directly draw wires between connectors ring pads.
Provide two flavors: placement only or full place & route.
WARNING: If routing in a second step, *do not route* the *Chip* but
the *Corona*.
* Change: In Cumulus/plugins/clocktree, give the modified Cell an
additional extension of "_cts" (Clock Tree Synthesis) instead of
"_clocked", to follow the common convention.
* New: In cumulus/plugins/S2R.py, encapsulate call to Alliance S2R and
reload the translated Cell in the editor.
* New: In cumulus/plugins/core2chip, provide an utility to automatically
create a chip from a core. To work this plugins must have a basic
understanding of the pad functionalities which may differs from
foundry to foundry. So a base class CoreToChip is created, then for
each supported pad foundry a derived class is added. Currently we
support AMS c35b4 and Alliance symbolic cmos.
* Bug: In Anabatic::Configuration, read the right configuration parameter
"anabatic.topRoutinglayer" (Katana), and not the one for Katabatic...
* Change: In Unicorn/cgt.py, process the plugins in alphabetical order
to ensure a reproductible ordering of the menus...
2019-05-22 07:34:32 -05:00
|
|
|
from helpers import isderived
|
|
|
|
from helpers import trace
|
|
|
|
from helpers.io import ErrorMessage as Error
|
|
|
|
from Analog import Device
|
|
|
|
from Analog import Transistor
|
|
|
|
from Analog import CommonDrain
|
|
|
|
from Analog import CommonGatePair
|
|
|
|
from Analog import CommonSourcePair
|
|
|
|
from Analog import CrossCoupledPair
|
|
|
|
from Analog import DifferentialPair
|
|
|
|
from Analog import LevelShifter
|
|
|
|
from Analog import SimpleCurrentMirror
|
|
|
|
from Analog import LayoutGenerator
|
|
|
|
from Bora import SlicingNode
|
|
|
|
from Bora import HSlicingNode
|
|
|
|
from Bora import VSlicingNode
|
|
|
|
from Bora import DSlicingNode
|
|
|
|
from Bora import RHSlicingNode
|
|
|
|
from Bora import RVSlicingNode
|
2018-10-18 11:10:01 -05:00
|
|
|
import karakaze.Oceane
|
|
|
|
import Anabatic
|
|
|
|
import Katana
|
|
|
|
import Bora
|
|
|
|
|
|
|
|
|
|
|
|
NMOS = Transistor.NMOS
|
|
|
|
PMOS = Transistor.PMOS
|
|
|
|
Center = SlicingNode.AlignCenter
|
|
|
|
Left = SlicingNode.AlignLeft
|
|
|
|
Right = SlicingNode.AlignRight
|
|
|
|
Top = SlicingNode.AlignTop
|
|
|
|
Bottom = SlicingNode.AlignBottom
|
|
|
|
Unknown = SlicingNode.AlignBottom
|
|
|
|
VNode = 1
|
|
|
|
HNode = 2
|
|
|
|
DNode = 3
|
|
|
|
|
|
|
|
|
|
|
|
def toDbU ( value ): return DbU.fromPhysical( value, DbU.UnitPowerMicro )
|
|
|
|
def toLength ( value ): return float(value) * 1e+6
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AnalogDesign ( object ):
|
|
|
|
|
|
|
|
def __init__ ( self ):
|
|
|
|
self.cellName = None
|
|
|
|
self.netCache = {}
|
|
|
|
self.rg = None
|
|
|
|
self.library = None
|
|
|
|
self.cell = None
|
|
|
|
self.netCache = {}
|
|
|
|
self.slicingTree = None
|
|
|
|
self.stack = []
|
|
|
|
self.stack2 = []
|
|
|
|
self.toleranceRatioH = 0
|
|
|
|
self.toleranceRatioW = 0
|
|
|
|
self.toleranceBandH = 0
|
|
|
|
self.toleranceBandW = 0
|
|
|
|
self.parameters = karakaze.Oceane.Parameters()
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def setCellName ( self, name ):
|
|
|
|
self.cellName = name
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def beginCell ( self, cellName ):
|
|
|
|
self.setCellName( cellName )
|
|
|
|
UpdateSession.open()
|
|
|
|
self.rg = CRL.AllianceFramework.get().getRoutingGauge()
|
|
|
|
self.cell = CRL.AllianceFramework.get().createCell( self.cellName )
|
|
|
|
self.library = Library.create( DataBase.getDB().getRootLibrary(), 'AnalogRootLibrary' )
|
|
|
|
self.generator = LayoutGenerator()
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def endCell ( self ):
|
|
|
|
UpdateSession.close()
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def checkBeginCell ( self, function ):
|
|
|
|
if not self.cell:
|
|
|
|
raise Error( 3, [ 'AnalogDesign: \"AnalogDevice.beginCell()\" must be called *before* \"%s\".' \
|
|
|
|
% function
|
|
|
|
] )
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def checkConnexion ( self, count, net, connexion ):
|
|
|
|
if not isinstance(connexion,tuple):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.netSpecs\" in \"%s\", connexion [%d] is *not* a tuple.' \
|
|
|
|
% (net.getName(),count)
|
|
|
|
, '%s' % str(connexion) ] )
|
|
|
|
if len(connexion) != 2:
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.devicesSpecs\" in \"%s\", connexion [%d] has %d items instead of 2 .' \
|
|
|
|
% (net.getName(),count,len(connexion))
|
|
|
|
, '%s' % str(connexion) ] )
|
|
|
|
if not isinstance(connexion[0],str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.devicesSpecs\" in \"%s\", connexion [%d], field [0] (instance) is *not* a string.' \
|
|
|
|
% (net.getName(),count)
|
|
|
|
, '%s' % str(connexion) ] )
|
|
|
|
if not isinstance(connexion[1],str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doNets(): \"self.devicesSpecs\" in \"%s\", connexion [%d], field [1] (terminal) is *not* a string.' \
|
|
|
|
% (net.getName(),count)
|
|
|
|
, '%s' % str(connexion) ] )
|
|
|
|
return
|
|
|
|
|
|
|
|
def checkRail( self, net, metal, npitch, cellName, instanceName ):
|
|
|
|
#Net verification missing
|
|
|
|
if not isinstance(metal,str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"metal\" is *not* a string.' ] )
|
|
|
|
if not isinstance(npitch,int):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"NPitch\" is *not* an int.' ] )
|
|
|
|
if not isinstance(cellName,str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"cellName\" is *not* a string.' ] )
|
|
|
|
if not isinstance(instanceName,str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.checkRail(): \"instanceName\" is *not* a string.' ] )
|
|
|
|
return
|
|
|
|
|
|
|
|
def connect ( self, instanceName, masterNetName, net ):
|
|
|
|
instance = getattr( self, instanceName )
|
|
|
|
masterNet = instance.getMasterCell().getNet( masterNetName )
|
|
|
|
instance.getPlug( masterNet ).setNet( net )
|
|
|
|
|
|
|
|
state = NetRoutingExtension.get(net)
|
|
|
|
device = instance.getMasterCell()
|
|
|
|
if masterNetName=='B':
|
|
|
|
device.getParameter('B.w').setValue(int(state.getWPitch()))
|
|
|
|
if masterNetName=='G':
|
|
|
|
device.getParameter('G.w').setValue(int(state.getWPitch()))
|
|
|
|
if masterNetName=='G1':
|
|
|
|
device.getParameter('G1.w').setValue(int(state.getWPitch()))
|
|
|
|
if masterNetName=='G2':
|
|
|
|
device.getParameter('G2.w').setValue(int(state.getWPitch()))
|
|
|
|
if masterNetName=='D':
|
|
|
|
device.getParameter('D.w').setValue(int(state.getWPitch()))
|
|
|
|
if masterNetName=='D1':
|
|
|
|
device.getParameter('D1.w').setValue(int(state.getWPitch()))
|
|
|
|
if masterNetName=='D2':
|
|
|
|
device.getParameter('D2.w').setValue(int(state.getWPitch()))
|
|
|
|
if masterNetName=='S':
|
|
|
|
device.getParameter('S.w').setValue(int(state.getWPitch()))
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def getNet ( self, netName, create=True ):
|
|
|
|
net = None
|
|
|
|
if self.netCache.has_key(netName):
|
|
|
|
net = self.netCache[netName]
|
|
|
|
elif create:
|
|
|
|
net = Net.create( self.cell, netName )
|
|
|
|
self.netCache[ netName ] = net
|
|
|
|
return net
|
|
|
|
|
|
|
|
|
|
|
|
def doNets ( self ):
|
|
|
|
self.checkBeginCell( 'AnalogDesign.doNets()' )
|
|
|
|
|
|
|
|
if not hasattr(self,'netSpecs'):
|
|
|
|
raise Error( 3, 'AnalogDesign.doNets(): Mandatory attribute \"self.netSpecs\" has not been defined.' )
|
|
|
|
if not isinstance(self.netSpecs,dict):
|
|
|
|
raise Error( 3, 'AnalogDesign.doNets(): Attribute \"self.netSpecs\" *must* be a Python dict.' )
|
|
|
|
|
|
|
|
for netName, netType in self.netTypes.items():
|
|
|
|
if not isinstance(netName,str):
|
|
|
|
raise Error( 3, 'AnalogDesign.doNets(): Dict key (net name) of \"self.netTypes\" *must* be a string (%s).' % str(netName) )
|
|
|
|
|
|
|
|
net = self.getNet( netName )
|
|
|
|
isExternal = False
|
|
|
|
if netType.has_key('isExternal'): isExternal = netType['isExternal']
|
|
|
|
|
|
|
|
for netName, connexions in self.netSpecs.items():
|
|
|
|
if not isinstance(netName,str):
|
|
|
|
raise Error( 3, 'AnalogDesign.doNets(): Dict key (net name) of \"self.netSpecs\" *must* be a string (%s).' % str(netName) )
|
|
|
|
|
|
|
|
net = self.getNet( netName )
|
|
|
|
state = NetRoutingExtension.create( net, NetRoutingState.AutomaticGlobalRoute|NetRoutingState.Analog )
|
|
|
|
count = 1
|
|
|
|
for connexion in connexions:
|
|
|
|
if isinstance(connexion,tuple):
|
|
|
|
self.checkConnexion( count, net, connexion )
|
|
|
|
self.connect( connexion[0], connexion[1], net )
|
|
|
|
count += 1
|
|
|
|
else:
|
|
|
|
if isinstance(connexion,dict): state.setWPitch(long(connexion['W']))
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def checkDSpec ( self, count, dspec ):
|
|
|
|
if not isinstance(dspec,list):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], is *not* a list.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if len(dspec) < 12:
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], has %d items instead of 12 .' \
|
|
|
|
% (count,len(dspec))
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isderived(dspec[0],Device):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [0] is *not* a Device class.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[1],str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [1] (model name) is *not* a string.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[2],str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [2] (layout style) is *not* a string.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if dspec[3] not in [NMOS, PMOS]:
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [3] (type) must be either NMOS or PMOS.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[4],float):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [4] (WE) is *not* a float.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[5],float):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [5] (LE) is *not* a float.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[6],int):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [6] (M) is *not* an int.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if (not dspec[7] is None) and (not isinstance(dspec[7],int)):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [7] (Mint) is neither an int nor None.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[8],int):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [8] (external dummies) is *not* an int.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[9],bool):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [9] (source first) is *not* a boolean.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[10],int):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [10] (bulk) is *not* an int.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
else:
|
|
|
|
if dspec[10] > 0xf:
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [10] (bulk) is greater than 0xf.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[11],bool):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [11] (bulk connected) is *not* a boolean.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
return
|
|
|
|
|
|
|
|
def checkDSpecDigital ( self, count, dspec ):
|
|
|
|
# if not isinstance(dspec[0],str):
|
|
|
|
# raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [0] (model name) is *not* a string.' % count
|
|
|
|
# , '%s' % str(dspec) ])
|
|
|
|
if not isinstance(dspec[1],str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doDevices(): \"self.devicesSpecs\" entry [%d], field [1] (model name) is *not* a string.' % count
|
|
|
|
, '%s' % str(dspec) ])
|
|
|
|
return
|
|
|
|
|
|
|
|
def readParameters ( self, path ):
|
|
|
|
trace( 110, ',+', '\tReading Oceane parameters from \"%s\"\n' % path )
|
|
|
|
|
|
|
|
if not path: return
|
|
|
|
self.parameters.read( path );
|
|
|
|
for dspec in self.devicesSpecs:
|
|
|
|
if len(dspec) > 2:
|
|
|
|
Tname = dspec[1].split('_')[0]
|
|
|
|
Tparameters = self.parameters.getTransistor( Tname )
|
|
|
|
if not Tparameters:
|
|
|
|
raise Error( 3, [ 'AnalogDesign.readParameters(): Missing parameters for \"%s\".' % Tname ] )
|
|
|
|
continue
|
|
|
|
dspec[4] = toLength( Tparameters.W )
|
|
|
|
dspec[5] = toLength( Tparameters.L )
|
|
|
|
dspec[6] = Tparameters.M
|
|
|
|
trace( 110, '\t- \"%s\" : W:%f L:%f M:%d\n' % (Tname
|
|
|
|
,dspec[4]
|
|
|
|
,dspec[5]
|
|
|
|
,dspec[6]) )
|
|
|
|
trace( 110, '-,' )
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def doDevice ( self, count, dspec ):
|
|
|
|
self.checkBeginCell( 'AnalogDesign.doDevice()' )
|
|
|
|
if len(dspec) == 2:
|
|
|
|
self.checkDSpecDigital( count, dspec )
|
|
|
|
if isinstance( dspec[0], str ):
|
|
|
|
masterCell = CRL.AllianceFramework.get().getCell( dspec[0], CRL.Catalog.State.Views )
|
|
|
|
instance = Instance.create( self.cell, dspec[1], masterCell, Transformation() )
|
|
|
|
instance.setPlacementStatus( Instance.PlacementStatus.UNPLACED )
|
|
|
|
self.__dict__[ dspec[1] ] = instance
|
|
|
|
else:
|
|
|
|
masterCell = dspec[0]
|
|
|
|
instance = Instance.create( self.cell, dspec[1], masterCell, Transformation() )
|
|
|
|
instance.setPlacementStatus( Instance.PlacementStatus.UNPLACED )
|
|
|
|
self.__dict__[ dspec[1] ] = instance
|
|
|
|
else:
|
|
|
|
self.checkDSpec( count, dspec )
|
|
|
|
|
|
|
|
trace( 110, '\tBuilding \"%s\"\n' % dspec[1] )
|
|
|
|
device = dspec[0].create( self.library, dspec[1], dspec[3], dspec[11] )
|
|
|
|
device.getParameter( 'Layout Styles' ).setValue( dspec[2] )
|
|
|
|
device.getParameter( 'W' ).setValue( toDbU(dspec[4]) )
|
|
|
|
device.getParameter( 'L' ).setValue( toDbU(dspec[5]) )
|
|
|
|
device.getParameter( 'M' ).setValue( dspec[6] )
|
|
|
|
device.setSourceFirst( dspec[9] )
|
|
|
|
device.setBulkType ( dspec[10] )
|
|
|
|
|
|
|
|
if (len(dspec) > 12): device.getParameter( 'NERC' ).setValue(int (dspec[12]))
|
|
|
|
if (len(dspec) > 13): device.getParameter( 'NIRC' ).setValue(int (dspec[13]))
|
|
|
|
|
|
|
|
if not (dspec[7] is None): device.setMint ( dspec[7] )
|
|
|
|
if dspec[8]: device.setExternalDummy( dspec[8] )
|
|
|
|
|
|
|
|
self.generator.setDevice ( device )
|
|
|
|
self.generator.drawLayout()
|
Support for separated NDA tree. Big cleanup of the Python init system.
* Change: In Hurricane::Script, when running a script, no longer do it
inside a Python sun-interpreter, use the current one. This way we
no longer have our modules initialized twice or more, which was
starting to be unmanageable (with the NDA support).
The settings were re-read multiple time to the same value, so it
was working, but still...
I hope I didn't left some dangling Python objects now.
* Bug: In Hurricane::LayoutGenerator::drawLayout(), get the device abutment
box though a Pyhon object *before* finalizing which removes that objet.
* New: In cumulus/plugins/__init__.py, add a "loadPlugins()" and static
initialisation to preload plugins modules.
We use that pre-loading step to append to the module __path__ attribute
the alternate directory where a NDA covered may be found. This assume that
the directory tree under the NDA root is identical to the one under the
public root.
* New: In cumulus/plugins/chip/__init__.py, small utility function
importContants() to import the constants inside another module namespace,
to have more consise notations.
* Change: In cumulus/plugins/, in the various plugins sub-modules import
use the full path from plugins, that is, for example:
from plugins.core2chip.CoreToChip import IoPad
* Change: In Unicorn/python/unicornInit.py, no longer directly load the
plugins modules, this is now done by cumulus/plugins/__init__.py.
Instead, iterate through sys.modules for the ones starting by "plugins/"
and try to execute a Unicorn hook, if present.
* Change: In Karakaze/python/AnalogDesign.py, update for the new Instance.create()
prototype (added placement parameter).
2019-10-11 10:36:54 -05:00
|
|
|
instance = Instance.create( self.cell
|
|
|
|
, dspec[1]
|
|
|
|
, device
|
|
|
|
, Transformation()
|
|
|
|
, Instance.PlacementStatus.UNPLACED )
|
2018-10-18 11:10:01 -05:00
|
|
|
|
|
|
|
self.__dict__[ dspec[1] ] = instance
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def doDevices ( self ):
|
|
|
|
trace( 110, ',+', '\tAnalogDesign.doDevices()\n' )
|
|
|
|
|
|
|
|
if not hasattr(self,'devicesSpecs'):
|
|
|
|
raise Error( 3, 'AnalogDesign.doDevices(): Mandatory attribute \"self.devicesSpecs\" has not been defined.' )
|
|
|
|
if not isinstance(self.devicesSpecs,list):
|
|
|
|
raise Error( 3, 'AnalogDesign.doDevices(): Attribute \"self.devicesSpecs\" *must* be a Python list.' )
|
|
|
|
|
|
|
|
count = 1
|
|
|
|
for dspec in self.devicesSpecs:
|
|
|
|
self.doDevice( count, dspec )
|
|
|
|
count += 1
|
|
|
|
trace( 110, '-,' )
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def showNode ( self, node ):
|
|
|
|
lines = [ '{' ]
|
|
|
|
for key, value in node.items():
|
|
|
|
if key == 'children':
|
|
|
|
lines += [ "%20s { ... }" % "'children':" ]
|
|
|
|
else:
|
|
|
|
skey = "'%s':" % str(key)
|
|
|
|
lines += [ "%20s %s" % (skey,str(value)) ]
|
|
|
|
lines += [ '}' ]
|
|
|
|
return lines
|
|
|
|
|
|
|
|
|
|
|
|
def checkNode ( self, node, isRoot ):
|
|
|
|
if not isinstance(node,dict):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Node element is *not* a dict.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not node.has_key('type'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"type\" key/element.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
nodeType = node['type']
|
|
|
|
if nodeType not in [VNode, HNode, DNode]:
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"type\" must be one of VNode, HNode or DNode.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
|
|
|
|
if nodeType == DNode:
|
|
|
|
if not node.has_key('device'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"device\" key/element.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not isinstance(node['device'],str):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"device\" value *must* be of type str.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not node.has_key('span'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"span\" key/element.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not isinstance(node['span'],tuple) \
|
|
|
|
or len(node['span']) != 3 \
|
|
|
|
or not isinstance(node['span'][0],float) \
|
|
|
|
or not isinstance(node['span'][1],float) \
|
|
|
|
or not isinstance(node['span'][2],float):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"span\" value *must* be a tuple of 3 floats.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not node.has_key('NF'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"NF\" key/element.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not isinstance(node['NF'],int):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"NF\" value *must* be of type int.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
else:
|
|
|
|
if isRoot:
|
|
|
|
if not node.has_key('toleranceRatioH'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceRationH\" key/element in root node.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not node.has_key('toleranceRatioW'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceRationW\" key/element in root node.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not node.has_key('toleranceBandH'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceBandH\" key/element in root node.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not node.has_key('toleranceBandW'):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): Missing mandatory \"toleranceBandW\" key/element in root node.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if not node.has_key('children'):
|
|
|
|
print Error( 3, [ 'AnalogDesign.doSlicingTree(): Suspicious root node without children.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
if node.has_key('children'):
|
|
|
|
if not isinstance(node['children'],list):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"children\" value *must* be of type list.' ]
|
|
|
|
+ self.showNode(node) )
|
|
|
|
|
|
|
|
if node.has_key('symmetries'):
|
|
|
|
symmetries = node['symmetries']
|
|
|
|
if not isinstance(symmetries,list):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"symmetries\" value *must* be of type list.'
|
|
|
|
] + self.showNode(node) )
|
|
|
|
for i in range(len(symmetries)):
|
|
|
|
if not isinstance(symmetries[i],tuple) \
|
|
|
|
or len(symmetries[i]) != 2 \
|
|
|
|
or not isinstance(symmetries[i][0],int) \
|
|
|
|
or not isinstance(symmetries[i][1],int):
|
|
|
|
raise Error( 3, [ 'AnalogDesign.doSlicingTree(): \"symmetries\" entry [%d] *must* be a tuple of 2 int.' % i ]
|
|
|
|
+ self.showNode(node) )
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def beginSlicingTree ( self ):
|
|
|
|
trace( 110, ',+', '\tAnalogDesign.beginSlicingTree()\n' )
|
|
|
|
return
|
|
|
|
|
|
|
|
def topNode ( self ): return self.stack[-1][0]
|
|
|
|
def topSymmetries ( self ): return self.stack[-1][1]
|
|
|
|
def topSymmetriesNet ( self ): return self.stack[-1][2]
|
|
|
|
|
|
|
|
def setToleranceRatioH ( self, u ): self.toleranceRatioH = toDbU(u)
|
|
|
|
def setToleranceRatioW ( self, u ): self.toleranceRatioW = toDbU(u)
|
|
|
|
def setToleranceBandH ( self, u ): self.toleranceBandH = toDbU(u)
|
|
|
|
def setToleranceBandW ( self, u ): self.toleranceBandW = toDbU(u)
|
|
|
|
|
|
|
|
def dupTolerances ( self, node ):
|
|
|
|
node.setToleranceRatioH( self.toleranceRatioH )
|
|
|
|
node.setToleranceRatioW( self.toleranceRatioW )
|
|
|
|
node.setToleranceBandH ( self.toleranceBandH )
|
|
|
|
node.setToleranceBandW ( self.toleranceBandW )
|
|
|
|
return
|
|
|
|
|
|
|
|
def pushNode ( self, node ):
|
|
|
|
trace( 110, ',+', '\tSlicingTree.pushNode() %s ' % str(node) )
|
|
|
|
parent = None
|
|
|
|
if len(self.stack):
|
|
|
|
parent = self.topNode()
|
|
|
|
parent.push_back( node )
|
|
|
|
trace( 110, '(parent id:%d)\n' % parent.getId() )
|
|
|
|
else:
|
|
|
|
trace( 110, '(Root)\n' )
|
|
|
|
self.slicingTree = node
|
|
|
|
node.setCell( self.cell )
|
|
|
|
|
|
|
|
self.stack.append( (node,[],[]) )
|
|
|
|
self.dupTolerances( node )
|
|
|
|
node.setRoutingGauge( self.rg )
|
|
|
|
#node.cprint()
|
|
|
|
return
|
|
|
|
|
|
|
|
def pushVNode ( self, alignment ):
|
|
|
|
self.pushNode( VSlicingNode.create( alignment ) )
|
|
|
|
return
|
|
|
|
|
|
|
|
def pushHNode ( self, alignment ):
|
|
|
|
self.pushNode( HSlicingNode.create( alignment ) )
|
|
|
|
return
|
|
|
|
|
|
|
|
def popNode ( self ):
|
|
|
|
for childIndex, copyIndex in self.topSymmetries():
|
|
|
|
self.topNode().addSymmetry( childIndex, copyIndex )
|
|
|
|
for type, net1, net2 in self.topSymmetriesNet():
|
|
|
|
if (net2 == None):
|
|
|
|
self.topNode().addSymmetryNet( type, net1 )
|
|
|
|
else:
|
|
|
|
self.topNode().addSymmetryNet( type, net1, net2 )
|
|
|
|
|
|
|
|
trace( 110, '-,', '\tSlicingTree.popNode() %s\n' % str(self.topNode()) )
|
|
|
|
|
|
|
|
if len(self.stack) == 1:
|
|
|
|
trace( 110, '\tAnalogDesign.endSlicingTree()\n' )
|
|
|
|
trace( 110, '-,', '\tSlicingTree %s stack size:%d\n' % (self.cell.getName(), len(self.stack)) )
|
|
|
|
#self.topNode().setCell( self.cell )
|
|
|
|
self.topNode().updateNetConstraints()
|
|
|
|
self.topNode().updateGlobalSize()
|
|
|
|
|
|
|
|
del self.stack[-1]
|
|
|
|
return
|
|
|
|
|
|
|
|
def addDevice ( self, name, align, span=(0, 0, 0), NF=0 ):
|
|
|
|
node = DSlicingNode.create( name, self.cell, span[0], span[1], span[2], self.rg )
|
|
|
|
node.setAlignment( align )
|
|
|
|
if NF != 0: node.setNFing( NF )
|
|
|
|
self.topNode().push_back( node )
|
|
|
|
trace( 110, '\tSlicingTree.addDevice() %s (parent id:%d)\n' % (str(node),self.topNode().getId()) )
|
|
|
|
#node.cprint()
|
|
|
|
return
|
|
|
|
|
|
|
|
def addHRail ( self, net, metal, npitch, cellName, instanceName ):
|
|
|
|
self.checkRail( net, metal, npitch, cellName, instanceName )
|
|
|
|
node = RHSlicingNode.create( net, DataBase.getDB().getTechnology().getLayer(metal), npitch, cellName, instanceName)
|
|
|
|
self.topNode().push_back( node )
|
|
|
|
trace( 110, '\tSlicingTree.addHRail() to %s\n' % (str(self.topNode())) )
|
|
|
|
#node.cprint()
|
|
|
|
return
|
|
|
|
|
|
|
|
def addVRail ( self, net, metal, npitch, cellName, instanceName ):
|
|
|
|
self.checkRail( net, metal, npitch, cellName, instanceName )
|
|
|
|
node = RVSlicingNode.create( net, DataBase.getDB().getTechnology().getLayer(metal), npitch, cellName, instanceName)
|
|
|
|
self.topNode().push_back( node )
|
|
|
|
trace( 110, '\tSlicingTree.addVRail() to %s\n' % (str(self.topNode())) )
|
|
|
|
#node.cprint()
|
|
|
|
return
|
|
|
|
|
|
|
|
def addSymmetry ( self, childIndex, copyIndex ):
|
|
|
|
self.topSymmetries().append( (childIndex,copyIndex) )
|
|
|
|
return
|
|
|
|
|
|
|
|
def addSymmetryNet ( self, type, net1, net2=None ):
|
|
|
|
self.topSymmetriesNet().append( (type, net1, net2) )
|
|
|
|
return
|
|
|
|
|
|
|
|
def endSlicingTree ( self ):
|
|
|
|
self.slicingTree.updateGlobalSize()
|
|
|
|
#bora = Bora.BoraEngine.get( self.cell )
|
|
|
|
#if not bora: bora = Bora.BoraEngine.create( self.cell )
|
|
|
|
#bora.updateSlicingTree()
|
|
|
|
return
|
|
|
|
|
|
|
|
def updatePlacement ( self, *args ):
|
|
|
|
if self.slicingTree:
|
|
|
|
bora = Bora.BoraEngine.get( self.cell )
|
|
|
|
if not bora: bora = Bora.BoraEngine.create( self.cell )
|
|
|
|
|
|
|
|
signatureMatched = True
|
|
|
|
if len(args) == 2: bora.updatePlacement( toDbU(args[0]), toDbU(args[1]) )
|
|
|
|
elif len(args) == 1: bora.updatePlacement( args[0] )
|
|
|
|
else: signatureMatched = False
|
|
|
|
|
|
|
|
#if signatureMatched:
|
|
|
|
# katana = Katana.KatanaEngine.get( self.cell )
|
|
|
|
# if katana:
|
|
|
|
# katana.loadGlobalRouting( Anabatic.EngineLoadGrByNet )
|
|
|
|
# katana.runNegociate( Katana.Flags.PairSymmetrics );
|
|
|
|
# #katana.destroy()
|
|
|
|
return
|