This is a short how-to manual on how to make and edit SKILL files to create Pcells for use in CADENCE.
This assumes you already have some experience creating and using Pcells using the graphic editor (Virtuoso) in CADENCE, in particular the stretch, conditional, and repeat commands. If you would like to learn how to make Pcells do things that you can't do from the graphical editor, read on.
We will look at a couple examples, "multiplank" and "honeycomb".
Here are the steps which make it easy to create and modify SKILL code:
1) Using the graphical editor, draw an approximate version of the Pcell you want to create. In this example, create a Pcell that has repeating rectangles. Don't worry about their length. Leave the default SKILL expression in the "stretch offset" field. (fix(pcRepeat)-1)*pcStep). Make sure that the rectangles have a stretching parameter, call it length. Put "length" in the dependent stretch field when creating the repeat.
2) Compile the Pcell to a SKILL file. By default this will go into your working directory, not the library you are wroking in. Change the cell name so that when you load the modified SKILL file back into CADENCE, you won't overwrite the Pcell that you are starting with.
3) Take a look at the code. It might look something like this:
pcDefinePCell(list(ddGetObj("skillexamples") "varylength" "layout") ((step float 1.0) (number float 1.0) ) let((length length0 length0offset pcMember pcStretchGroup stretchOffsetX stretchOffsetY number step pcLib pcMaster pcInst pcTerm pcPin pcPinName pcNet pcTermNet pcNetName pcTermNetName pcMosaicInst tpcParameters pcParamProp pcStep pcStepX pcStepYi pcRepeat pcRepeatX pcRepeatY pcIndexX pcIndexY pcLayer pcPurpose pcLabelText pcLabelHeight pcPropText pcParamText pcCoords pcPathWidth pcPolygonMargin ) (pcLib = (pcCellView~>lib)) (pcParameters = ((pcCellView~>parameters)~>value)) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "number") ) )) (number = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (number = (number == "TRUE")) ) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "step") ) )) (step = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (step = (step == "TRUE")) ) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "length") ) )) (length = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (length = (length == "TRUE")) ) (length0 = length) if(!numberp(length0) (length0 = 11.0) ) (pcStepX = 0) (pcStep = (pcStepY = step)) (pcRepeatX = 1) (pcRepeat = (pcRepeatY = number)) (length0 = (length0 + ((fix(pcRepeatY) - 1) * pcStepY))) (length0offset = (length0 - 11)) dbReplaceProp(pcCellView "viewSubType" "string" "maskLayoutParamCell") (pcRepeat = (pcRepeatX = (pcRepeatY = 1.0))) (pcStep = (pcStepX = (pcStepY = 0.0))) (pcStep = (pcStepY = car(errset(step)))) (pcRepeat = (pcRepeatY = number)) (pcStep = (pcStepY = step)) (pcIndexX = 0) for(pcIndexY 0 (fix(pcRepeatY) - 1) nil (pcLayer = 13) (pcPurpose = "drawing") (pcInst = dbCreateRect(pcCellView list(pcLayer pcPurpose) list((-5:(-1 + (pcIndexY * pcStepY))) ((6 + length0offset):(1 + (pcIndexY * pcStepY))) ) )) ) t ) )
In the first line, you see that when this is loaded into CADENCE a Pcell called "varylength", of type "layout" will be created in the library "skillexamples". the second and third lines give the default values of step and number, which will appear in the box when you create an instance of varylength. (the type of variable is float). "let" lists all the variables used in the Pcell. If you add any, be sure to add them to this list. Similarly, you should copy the stuff that starts with "(pcParamProp = ..." for each variable. I don't know what this does.
The last ten or so lines is the loop used to create the repeating rectangles. The loop variable is pcIndexY, and varies from 0 to pcRepeatY-1. 13 is the layer (MUMPS Poly0 in this case). dbCreateRect() creates rectangles. They are described by the coordinates of their corners. Note that the y coordinate depends on pcIndexY*pcStepY. This places each new rectangle in a new position. If you wanted to make the length in the x-direction vary, you can edit the code to put in whatever expression you like for the x coordinate, which can depend on pcIndexY. You also need to get rid of the dependence of length0 on the expression (fix(pcRepeatY)-1)*pcStepY. We left it in so that we could easily see what part of the loop depends on the length parameter.
In order for this to be useful, we have to load the edited SKILL code into CADENCE.
In the command window, type
load("varylength")
This instructs CADENCE to take the SKILL code and create a new cell in the library indicated in the first line of the SKILL code, in this case, "skillexamples". THIS NEW PCELL IS NOT EDITABLE WITHIN THE VIRTUOSO EDITOR. That is why we took steps to make sure we changed the name of the cell either when we compile the original Pcell to SKILL code, or changed the name of the created cell by editing the first line of code.
You can now create instances of this new Pcell in your layout the same way as if you had designed the Pcell graphically.
1) I sketched out the "unit cell" of a honeycomb on a piece of paper, and calculated the coordinates of the vertices. I assumed for nice round numbers that the side of the hexagon was 100 microns.
2) I then used Virtuoso to sketch out the shape of the unit cell. There's no need to get the vertices in exactly, because they should be fixed when you edit the SKILL file. I used the "path" commend to draw the lines, because the path width is a parameter that can be easily changed in the SKILL file. It makes thing easier if you line up some of the vertices with the origin and/or the axes.
3) I chose the "Repeat in X and Y" option in the Pcell menu, and put in the appropriate distances.
4) Compiled to SKILL file. Put in new variables "width" and "scale". I multiplied all the coordinates of the vertices by "scale", including the value of pcStep. The original and modified code are shown below.
original SKILL code:
pcDefinePCell(list(ddGetObj("deepmirrors") "honeycomb" "layout") ((xnumber float 1.0) (ynumber float 1.0) ) let((pcMember pcStretchGroup stretchOffsetX stretchOffsetY ynumber xnumber pcLib pcMaster pcInst pcTerm pcPin pcPinName pcNet pcTermNet pcNetName pcTermNetName pcMosaicInst tpcParameters pcParamProp pcStep pcStepX pcStepY pcRepeat pcRepeatX pcRepeatY pcIndexX pcIndexY pcLayer pcPurpose pcLabelText pcLabelHeight pcPropText pcParamText pcCoords pcPathWidth pcPolygonMargin ) (pcLib = (pcCellView~>lib)) (pcParameters = ((pcCellView~>parameters)~>value)) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "ynumber") ) )) (ynumber = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (ynumber = (ynumber == "TRUE")) ) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "xnumber") ) )) (xnumber = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (xnumber = (xnumber == "TRUE")) ) dbReplaceProp(pcCellView "viewSubType" "string" "maskLayoutParamCell") (pcRepeat = (pcRepeatX = (pcRepeatY = 1.0))) (pcStep = (pcStepX = (pcStepY = 0.0))) (pcStepX = car(errset(173.205))) (pcStepY = car(errset(300))) (pcRepeatX = xnumber) (pcRepeatY = ynumber) (pcStepX = 173.205) (pcStepY = 300) for(pcIndexX 0 (fix(pcRepeatX) - 1) for(pcIndexY 0 (fix(pcRepeatY) - 1) nil (pcLayer = 62) (pcPurpose = "drawing") (pcInst = dbCreatePath(pcCellView list(pcLayer pcPurpose) list(((87.801 + (pcIndexX * pcStepX)):(149.422 + (pcIndexY * pcStepY))) ((173.2 + (pcIndexX * pcStepX)):(100 + (pcIndexY * pcStepY))) ) 2 "truncateExtend" )) (pcLayer = 62) (pcPurpose = "drawing") (pcInst = dbCreatePath(pcCellView list(pcLayer pcPurpose) list(((0 + (pcIndexX * pcStepX)):(0 + (pcIndexY * pcStepY))) ((0 + (pcIndexX * pcStepX)):(100 + (pcIndexY * pcStepY))) ((86.8 + (pcIndexX * pcStepX)):(150 + (pcIndexY * pcStepY))) ((86.6 + (pcIndexX * pcStepX)):(250.1 + (pcIndexY * pcStepY))) ((173.2 + (pcIndexX * pcStepX)):(300 + (pcIndexY * pcStepY))) ((259.8 + (pcIndexX * pcStepX)):(249.9 + (pcIndexY * pcStepY))) ) 2 "truncateExtend" )) ) ) t ) )modified code: (lets you control wall width, overall size)
Note that I've created some new variables, scale???, to reduce the number of multiplications within loops. I don't think it helped much though.
pcDefinePCell(list(ddGetObj("deepmirrors") "honeycomb" "layout") ((xnumber float 20.0) (ynumber float 15.0) (width float 65.0) (scale float 1.0) ) let((pcMember pcStretchGroup stretchOffsetX stretchOffsetY ynumber width scale xnumber pcLib pcMaster pcInst pcTerm pcPin pcPinName pcNet pcTermNet pcNetName pcTermNetName pcMosaicInst tpcParameters pcParamProp pcStep pcStepX pcStepY pcRepeat pcRepeatX pcRepeatY pcIndexX pcIndexY pcLayer pcPurpose pcLabelText pcLabelHeight pcPropText pcParamText pcCoords pcPathWidth pcPolygonMargin ) (pcLib = (pcCellView~>lib)) (pcParameters = ((pcCellView~>parameters)~>value)) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "ynumber") ) )) (ynumber = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (ynumber = (ynumber == "TRUE")) ) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "width") ) )) (width = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (width = (width == "TRUE")) ) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "scale") ) )) (scale = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (scale = (scale == "TRUE")) ) (pcParamProp = car(exists(prop pcParameters ((prop~>name) == "xnumber") ) )) (xnumber = (pcParamProp~>value)) if(((pcParamProp~>valueType) == "boolean") (xnumber = (xnumber == "TRUE")) ) dbReplaceProp(pcCellView "viewSubType" "string" "maskLayoutParamCell") (pcRepeat = (pcRepeatX = (pcRepeatY = 1.0))) (pcStep = (pcStepX = (pcStepY = 0.0))) (pcStepX = car(errset(scale*173.205))) (pcStepY = car(errset(scale*300))) (pcRepeatX = xnumber) (pcRepeatY = ynumber) (pcStepX = scale*173.205) (pcStepY = scale*300) (scale86 = scale*86.6) (scale100 = scale*100) (scale150 = scale*150) (scale173 = scale*173.2) (scale250 = scale*250) (scale259 = scale*259.8) (scale300 = scale*300) for(pcIndexX 0 (fix(pcRepeatX) - 1) for(pcIndexY 0 (fix(pcRepeatY) - 1) nil (pcLayer = 62) (pcPurpose = "drawing") (pcInst = dbCreatePath(pcCellView list(pcLayer pcPurpose) list(((scale86 + (pcIndexX * pcStepX)):(scale150 + (pcIndexY * pcStepY))) ((scale173 + (pcIndexX * pcStepX)):(scale100 + (pcIndexY * pcStepY))) ) width "truncateExtend" )) (pcLayer = 62) (pcPurpose = "drawing") (pcInst = dbCreatePath(pcCellView list(pcLayer pcPurpose) list(((0 + (pcIndexX * pcStepX)):(0 + (pcIndexY * pcStepY))) ((0 + (pcIndexX * pcStepX)):(scale100 + (pcIndexY * pcStepY))) ((scale86 + (pcIndexX * pcStepX)):(scale150 + (pcIndexY * pcStepY))) ((scale86 + (pcIndexX * pcStepX)):(scale250 + (pcIndexY * pcStepY))) ((scale173 + (pcIndexX * pcStepX)):(scale300 + (pcIndexY * pcStepY))) ((scale259 + (pcIndexX * pcStepX)):(scale250 + (pcIndexY * pcStepY))) ) width "truncateExtend" )) ) ) t ) )Questions? contact me at jneumann@ece.cmu.edu