热度 1| |||
前几天写了一个简单的金属电阻的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)