前几天写了一个简单的金属电阻的pcell,经过这几天的摸索,在此编写了一个简单的 mos pcell 示例,用以演示一些常用命令的使用方法,并作为演示的 demo 供大家参考。
Pycell studio 集成了丰富多样的 API,使得诸如 place、align、enclose、fill、devicecontact 等功能的实现变得极为简便。例如,smart placement 命令 fgPlace 能够依据 tech 中定义的规则,自动帮助放置并保持 minSpace 。在这个示例中,我们还尝试使用了 stretchHandle ,它能够支持 pcell 关键组件 size 的拉伸。
下面我们来详细介绍一下这个示例的关键步骤:
defineParamSpecs :用于定义参数规格。
setupParams :对参数进行初始化操作。
genLayout :负责创建 pcell 的形状。
整个示例的代码总计不到 150 行,并且每个部分都有清晰的功能说明,方便大家理解和学习。下面是主要的pcell结构。
同样先制作对于的symbol,调用symbol设置好对于的参数,然后创建SDL layout 关系。
如下所以,可以在custom compiler中使用SDL调用创建的pcell。保证连接关系与电路的match。
mos的pcell新增加支持handstretch功能,关键size可以通过拖拽改变。
from cni.dlo import *
from cni.geo import *
from cni.constants import *
from cni.integ.common import *
class MyTransistor(DloGen):
@classmethod
def defineParamSpecs(cls, specs):
# first use variables to set default values for all parameters
tranType = 'pmos'
oxide = 'thin'
width = 0.4
length = 0.2
# now use these default parameter values in the parameter definitions
specs('tranType', tranType, 'MOSFET type (pmos or nmos)', ChoiceConstraint(['pmos', 'nmos']))
specs('oxide', oxide, 'Oxide (thin or thick)', ChoiceConstraint(['thin', 'thick']))
specs('width', width, 'device width', RangeConstraint(width, 10*width, USE_DEFAULT))
specs('length', length, 'device length', RangeConstraint(length, 10*length, USE_DEFAULT))
specs('sourceDiffOverlap', 0.0)
specs('drainDiffOverlap', 0.0)
specs('dumFill', True)
def setupParams(self, params):
# save parameter values using class variables
self.width = params['width']
self.length = params['length']
self.oxide = params['oxide']
self.tranType = params['tranType']
self.sourceDiffOverlap = params['sourceDiffOverlap']
self.drainDiffOverlap = params['drainDiffOverlap']
self.dumFill = params['dumFill']
self.dumFillLayer = Layer('poly','dummy')
# readjust width and length parameter values, since minimum values may be different
self.width = max(self.width, 0.4)
self.length = max(self.length, 0.2)
# also snap width and length values to nearest grid points
grid = Grid(self.tech.getGridResolution())
self.width = grid.snap(self.width, SnapType.ROUND)
self.length = grid.snap(self.length, SnapType.ROUND)
# save layer values using class variables
self.diffLayer = Layer('diff','drawing')
self.gateLayer = Layer('poly','drawing')
self.metalLayer = Layer('m1','drawing')
self.m1Width =0.12
#test layer
self.testlayer =Layer('m3','drawing')
# define the layers which should be used for enclosure rectangles
if self.tranType == 'nmos':
self.encLayers = [Layer('nplus','drawing')]
else:
self.encLayers = [Layer('pplus','drawing'), Layer('nwell','drawing')]
if self.oxide == 'thick':
self.encLayers.append(Layer('diff25','drawing'))
# determine minimum extension for gate poly layer
self.endcap = 0.1
def genLayout(self):
# first construct the rectangle for the gate
gateBox = Box(0,-self.endcap, self.length ,(self.width + self.endcap))
sdBox = Box(0,0,self.m1Width,self.width)
gateRect = Rect(self.gateLayer, gateBox)
# now construct device contacts for source and drain
self.sourceContact = DeviceContact(self.diffLayer, self.metalLayer, sdBox, name='S')
self.drainContact = DeviceContact(self.diffLayer, self.metalLayer, sdBox, name='D')
# stretch the source and drain contacts to full transistor extent
sourceBox = self.sourceContact.getRect1().getBBox()
self.sourceContact.stretch(self.metalLayer, sourceBox)
#Rect(self.testlayer, sourceBox)
drainBox = self.drainContact.getRect1().getBBox()
self.drainContact.stretch(self.metalLayer, drainBox)
# use "smart place" to place gate between source and drain the drc space base on tf
fgPlace(self.sourceContact, WEST, gateRect)
fgPlace(self.drainContact, EAST, gateRect)
# add any extra diffusion outside source and drain contacts
sBox = self.sourceContact.getBBox(self.diffLayer)
sBox.setLeft(sBox.left - self.sourceDiffOverlap)
sourceRect=Rect(self.diffLayer, sBox)
dBox = self.drainContact.getBBox(self.diffLayer)
dBox.setRight(dBox.right + self.drainDiffOverlap)
drainRect=Rect(self.diffLayer, dBox)
left = sourceRect.getBBox().left
right = drainRect.getBBox().right
bottom = sourceRect.getBBox().top
top = drainRect.getBBox().bottom
diffBox = Box(left, bottom,right, top)
diffRect = Rect(self.diffLayer, diffBox)
# create gate dummy rectangle, from top of source to bottom of drain
if self.dumFill == True :
dumgateLRect = Rect(self.dumFillLayer, Box(0,-self.endcap, self.m1Width ,(self.width + self.endcap)))
dumgateRRect = Rect(self.dumFillLayer, Box(0,-self.endcap, self.m1Width ,(self.width + self.endcap)))
dumgateRRect.place(EAST,diffRect,0.1)
dumgateLRect.place(WEST,diffRect,0.1)
# fgPlace(dumgateRRect, EAST, diffRect)
# fgPlace(dumgateLRect, WEST, diffRect)
# define the enclosure rectangles on the enclosure layers for transistor
fgAddEnclosingRects(self.makeGrouping(), self.encLayers)
#stretch handle define
stretchHandle(
name = 'gateRect',
shape = gateRect,
parameter = "width",
location = Location.UPPER_CENTER,
direction = Direction.NORTH_SOUTH,
stretchType = "relative",
minVal = 0.4,
userSnap = "%f" % (2.0 * self.tech.getGridResolution())
)
stretchHandle(
name = 'gateRect',
shape = gateRect,
parameter = "length",
location = Location.CENTER_RIGHT,
direction = Direction.EAST_WEST,
stretchType = "relative",
minVal = 0.2,
userSnap = "%f" % (2.0 * self.tech.getGridResolution())
)
# add the terminals for this transistor unit
self.addTerm('S', TermType.INPUT_OUTPUT) # source terminal
self.addTerm('D', TermType.INPUT_OUTPUT) # drain terminal
self.addTerm('G', TermType.INPUT) # gate terminal
self.addTerm('B', TermType.INPUT_OUTPUT) # bulk (substrate) terminal
self.setTermOrder(['D', 'G', 'S', 'B'])
# also define the pins for this transistor unit
self.addPin('G', 'G', gateBox, self.gateLayer)
self.addPin('S', 'S', self.sourceContact.getBBox(self.metalLayer), self.metalLayer)
self.addPin('D', 'D', self.drainContact.getBBox(self.metalLayer), self.metalLayer)