; * ************************************************************* ; * sfvSkillFuncs.il - General purpose SKILL utility functions ; * ************************************************************* ; * ; * Version 1.1 - 2026-07-27 ; * Tested with Virtuoso IC6.1.8 ; * ; * Copyright (c) 2026 Greg Warwar ; * SPDX-License-Identifier: MIT ; * Licensed under the MIT License. See LICENSE for full text. ; * ; * Contributed to the IEEE Solid-State Circuits Society ; * San Fernando Valley Chapter ; * This code is provided as-is, with no warranty. ; * https://sfv-sscs.org/CODE ; * ************************************************************* ; jstStr = centerLeft centerCenter centerRight lowerLeft lowerCenter lowerRight upperLeft upperCenter upperRight ; oriStr = R0 R90 R180 R270 MY MYR90 MX MXR90 ; 0 1 2 3 4 5 6 7 ; * ************************************************************* ; * Globals ; * ; * cv and gv are here for interactive work at the CIW prompt. ; * cv = the cellview being drawn into ; * gv = the symbol about to be placed ; * The functions below never read them. Pass the cellview in explicitly ; * so a function can be pointed at any cellview, not just the active one. ; * ************************************************************* cv = nil gv = nil ; * ************************************************************* ; * Named layer-purpose pairs ; * ; * Names keep tech-library specifics out of the source. If a name does ; * not resolve in your tech library, change it here and nowhere else. ; * ************************************************************* sfvLayerDraw = list( "device" "drawing" ) sfvLayerText = list( "text" "drawing" ) sfvLayerPin = list( "pin" "drawing" ) sfvLayerPinNm = list( "pin" "label" ) sfvLayerInst = list( "instance" "drawing" ) sfvSnap = 0.0625 ; Half width of the square drawn at a symbol pin connection point. sfvPinSize = 0.03125 sfvPi = 3.141592653589793 ; * ************************************************************* ; * Math helpers ; * ************************************************************* procedure( sfvNorm360( ang ) prog( ( a ) a = ang while( a < 0.0 a = a + 360.0 ) while( a >= 360.0 a = a - 360.0 ) return( a ) ) ) ; Bounding box of the arc that starts at angA and sweeps angS degrees ; counter clockwise around the circle at xc yc with radius rad. ; The extremes are the two end points plus any axis crossing the sweep ; passes through. procedure( sfvArcBBox( xc yc rad angA angS ) prog( ( angB angList ang k dAng px py xLo yLo xHi yHi first ) angB = angA + angS angList = list( angA angB ) k = 0 while( k < 4 ang = k * 90.0 dAng = sfvNorm360( ang - angA ) if( dAng <= angS then angList = cons( ang angList ) ) k = k + 1 ) xLo = 0.0 yLo = 0.0 xHi = 0.0 yHi = 0.0 first = t foreach( ang angList px = xc + rad * cos( ang * sfvPi / 180.0 ) py = yc + rad * sin( ang * sfvPi / 180.0 ) if( first then xLo = px xHi = px yLo = py yHi = py first = nil else if( px < xLo then xLo = px ) if( px > xHi then xHi = px ) if( py < yLo then yLo = py ) if( py > yHi then yHi = py ) ) ) return( list( list( xLo yLo ) list( xHi yHi ) ) ) ) ) ; * ************************************************************* ; * Cellview open and close ; * ************************************************************* procedure( sfvOpenSch( lname cname ) prog( ( cellID ) cellID = dbOpenCellViewByType( lname cname "schematic" "schematic" "w" ) return( cellID ) ) ) ; Open for writing, to build or edit a symbol. procedure( sfvOpenSym( lname cname ) prog( ( cellID ) cellID = dbOpenCellViewByType( lname cname "symbol" "schematicSymbol" "w" ) return( cellID ) ) ) ; Open for reading, to place with sfvPutSym. procedure( sfvGetSym( lname cname ) prog( ( cellID ) cellID = dbOpenCellViewByType( lname cname "symbol" "schematicSymbol" "r" ) return( cellID ) ) ) procedure( sfvCloseSaveSch( cid ) prog( () dbSave( cid ) dbClose( cid ) return( t ) ) ) procedure( sfvCloseSaveSym( cid ) prog( () dbSave( cid ) dbClose( cid ) return( t ) ) ) procedure( sfvCloseSch( cid ) prog( () dbClose( cid ) return( t ) ) ) procedure( sfvCloseSym( cid ) prog( () dbClose( cid ) return( t ) ) ) ; * ************************************************************* ; * Schematic connectivity - wires, labels, ports ; * ; * These create real connectivity. Use them in schematic cellviews. ; * For artwork that carries no connectivity, use the graphics section. ; * ************************************************************* procedure( sfvAddWire( cellID x0 y0 x1 y1 ) prog( ( ptList wireList ) ptList = list( list( x0 y0 ) list( x1 y1 ) ) wireList = schCreateWire( cellID "draw" "full" ptList sfvSnap 0.0 0.0 ) return( car( wireList ) ) ) ) procedure( sfvAddLabl( cellID wireID xloc yloc labStr jstStr oriStr hei ) prog( ( lablID ) lablID = schCreateWireLabel( cellID wireID list( xloc yloc ) labStr jstStr oriStr "stick" hei nil ) return( lablID ) ) ) procedure( sfvPutSym( schID symID instName xloc yloc oriStr ) prog( ( instID ) instID = dbCreateInst( schID symID instName list( xloc yloc ) oriStr ) return( instID ) ) ) PortINP = dbOpenCellViewByType( "basic" "ipin" "symbol" ) PortOUT = dbOpenCellViewByType( "basic" "opin" "symbol" ) PortNum = 0 procedure( sfvAddPortOut( cellID xloc yloc pname oriStr ) prog( ( pinNam pinStr portID netID trmID pinID ) sprintf( pinNam "PIN%d" PortNum ) sprintf( pinStr "P__%d" PortNum ) PortNum = PortNum + 1 portID = dbCreateInst( cellID PortOUT pinNam list( xloc yloc ) oriStr 0 ) portID~>purpose = "pin" netID = dbMakeNet( cellID pname nil ) trmID = dbCreateTerm( netID pname "output" ) pinID = dbCreatePin( netID portID pinStr ) return( pinID ) ) ) procedure( sfvAddPortInp( cellID xloc yloc pname oriStr ) prog( ( pinNam pinStr portID netID trmID pinID ) sprintf( pinNam "PIN%d" PortNum ) sprintf( pinStr "P__%d" PortNum ) PortNum = PortNum + 1 portID = dbCreateInst( cellID PortINP pinNam list( xloc yloc ) oriStr 0 ) portID~>purpose = "pin" netID = dbMakeNet( cellID pname nil ) trmID = dbCreateTerm( netID pname "input" ) pinID = dbCreatePin( netID portID pinStr ) return( pinID ) ) ) ; * ************************************************************* ; * Graphics - no connectivity ; * ; * Works in both schematic and symbol cellviews. The layer argument is ; * optional and defaults to the globals above. ; * ************************************************************* procedure( sfvAddLine( cellID x0 y0 x1 y1 @optional (layPur sfvLayerDraw) ) prog( ( lineID ptList ) ptList = list( list( x0 y0 ) list( x1 y1 ) ) lineID = dbCreateLine( cellID layPur ptList ) return( lineID ) ) ) procedure( sfvAddText( cellID xloc yloc labStr jstStr oriStr hei @optional (layPur sfvLayerText) ) prog( ( lablID ) lablID = dbCreateLabel( cellID layPur list( xloc yloc ) labStr jstStr oriStr "stick" hei ) lablID~>isDrafted = t lablID~>isOverbar = nil lablID~>isVisible = t return( lablID ) ) ) procedure( sfvAddRect( cellID x0 y0 x1 y1 @optional (layPur sfvLayerDraw) ) prog( ( rectID bBox ) bBox = list( list( x0 y0 ) list( x1 y1 ) ) rectID = dbCreateRect( cellID layPur bBox ) return( rectID ) ) ) ; ptList is a list of points, each point a list of two numbers. ; Example: list( list( 0.0 0.0 ) list( 0.5 0.0 ) list( 0.25 0.4 ) ) procedure( sfvAddPoly( cellID ptList @optional (layPur sfvLayerDraw) ) prog( ( polyID ) polyID = dbCreatePolygon( cellID layPur ptList ) return( polyID ) ) ) ; Centered at xc yc with x radius rx and y radius ry. ; Pass rx equal to ry for a true circle. procedure( sfvAddCircle( cellID xc yc rx ry @optional (layPur sfvLayerDraw) ) prog( ( ellID bBox ) bBox = list( list( xc - rx yc - ry ) list( xc + rx yc + ry ) ) ellID = dbCreateEllipse( cellID layPur bBox ) return( ellID ) ) ) ; Draws the arc passing through the three points in order. ; Point 0 and point 2 are the end points, point 1 is any point along the way. ; The point order sets the direction, so swapping 0 and 2 gives the same ; curve drawn the other way around. procedure( sfvAddArc( cellID x0 y0 x1 y1 x2 y2 @optional (layPur sfvLayerDraw) ) prog( ( arcID xa ya xb yb dxa dya dxb dyb det xn yn xc yc rad ang0 ang1 ang2 d1 d2 angA angS eBox aBox ) xa = (x1 + x0) / 2.0 ya = (y1 + y0) / 2.0 xb = (x2 + x1) / 2.0 yb = (y2 + y1) / 2.0 dxa = x1 - x0 dya = y1 - y0 dxb = x2 - x1 dyb = y2 - y1 det = dxb * dya - dxa * dyb if( det == 0.0 then printf( "Error - sfvAddArc - the three points are collinear\n" ) return( nil ) ) xn = xb * dxb * dya - xa * dxa * dyb + (yb - ya) * dya * dyb yn = ya * dxb * dya - yb * dxa * dyb + (xa - xb) * dxa * dxb xc = xn / det yc = yn / det rad = sqrt( (x0 - xc) * (x0 - xc) + (y0 - yc) * (y0 - yc) ) ang0 = sfvNorm360( atan2( y0 - yc x0 - xc ) * 180.0 / sfvPi ) ang1 = sfvNorm360( atan2( y1 - yc x1 - xc ) * 180.0 / sfvPi ) ang2 = sfvNorm360( atan2( y2 - yc x2 - xc ) * 180.0 / sfvPi ) d1 = sfvNorm360( ang1 - ang0 ) d2 = sfvNorm360( ang2 - ang0 ) if( d1 <= d2 then angA = ang0 angS = d2 else angA = ang2 angS = 360.0 - d2 ) eBox = list( list( xc - rad yc - rad ) list( xc + rad yc + rad ) ) aBox = sfvArcBBox( xc yc rad angA angS ) arcID = dbCreateArc( cellID layPur eBox aBox ) return( arcID ) ) ) ; * ************************************************************* ; * Symbol construction ; * ; * Draw the body with the graphics functions above, then add pins, ; * the selection box, and the instance name label with these. ; * ************************************************************* ; dirStr = input output inputOutput switch jumper unused tristate procedure( sfvAddSymPin( symID xloc yloc pname dirStr jstStr oriStr hei ) prog( ( bBox pinFig netID trmID pinID lablID ) bBox = list( list( xloc - sfvPinSize yloc - sfvPinSize ) list( xloc + sfvPinSize yloc + sfvPinSize ) ) pinFig = dbCreateRect( symID sfvLayerPin bBox ) netID = dbMakeNet( symID pname nil ) trmID = dbCreateTerm( netID pname dirStr ) pinID = dbCreatePin( netID pinFig ) lablID = dbCreateLabel( symID sfvLayerPinNm list( xloc yloc ) pname jstStr oriStr "stick" hei ) lablID~>parent = pinFig lablID~>isDrafted = t lablID~>isOverbar = nil lablID~>isVisible = t return( pinID ) ) ) ; The selection box. This is the outline the schematic editor uses when ; the symbol is placed, so make it enclose the body and the pins. procedure( sfvAddSymBox( symID x0 y0 x1 y1 ) prog( ( rectID bBox ) bBox = list( list( x0 y0 ) list( x1 y1 ) ) rectID = dbCreateRect( symID sfvLayerInst bBox ) return( rectID ) ) ) ; A label that Virtuoso evaluates rather than prints. ; Pass "[@instanceName]" or "[@partName]" as nlpStr. procedure( sfvAddSymNLP( symID xloc yloc nlpStr jstStr oriStr hei @optional (layPur sfvLayerText) ) prog( ( lablID ) lablID = dbCreateLabel( symID layPur list( xloc yloc ) nlpStr jstStr oriStr "stick" hei ) lablID~>labelType = "NLPLabel" lablID~>isDrafted = t lablID~>isOverbar = nil lablID~>isVisible = t return( lablID ) ) ) ; * ************************************************************* ; * End - General Purpose Skill Functions ; * ************************************************************* ; cv = sfvOpenSch( "junkLib" "junkBlock" ) ; cv = hiGetCurrentWindow()->cellView ; gv = sfvGetSym( "junkLib" "junkInv" ) ; sfvPutSym( cv gv "x21" 0.0 0.0 "R0" ) ; sfvPutSym( cv gv "x12" 3.25 5.0 "R180" ) ; wID = sfvAddWire( cv 3.25 5.0 3.25 5.5 ) ; sfvAddLabl( cv wID 3.25 5.5 "Shelby" "centerLeft" "R90" 0.05 ) ; sfvAddLine( cv 0.0 0.0 1.0 0.0 ) ; sfvAddText( cv 0.0 0.25 "note" "centerLeft" "R0" 0.05 ) ; sfvAddRect( cv 0.0 0.0 0.5 0.25 ) ; sfvAddPoly( cv list( list( 0.0 0.0 ) list( 0.5 0.0 ) list( 0.25 0.4 ) ) ) ; sfvAddCircle( cv 0.6 0.2 0.05 0.05 ) ; sfvAddArc( cv 0.5 0.0 0.354 0.354 0.0 0.5 ) ; sfvAddPortOut( cv 6.0 3.0 "PCC33" "MY" ) ; sfvCloseSaveSch( cv ) ; * ************************************************************* ; * Building an inverter symbol end to end ; * ************************************************************* ; sv = sfvOpenSym( "junkLib" "junkInv" ) ; sfvAddPoly( sv list( list( 0.0 -0.25 ) list( 0.0 0.25 ) list( 0.4 0.0 ) ) ) ; sfvAddCircle( sv 0.45 0.0 0.05 0.05 ) ; sfvAddLine( sv -0.25 0.0 0.0 0.0 ) ; sfvAddLine( sv 0.5 0.0 0.75 0.0 ) ; sfvAddSymPin( sv -0.25 0.0 "A" "input" "centerRight" "R0" 0.05 ) ; sfvAddSymPin( sv 0.75 0.0 "Y" "output" "centerLeft" "R0" 0.05 ) ; sfvAddSymBox( sv -0.25 -0.25 0.75 0.25 ) ; sfvAddSymNLP( sv -0.25 0.3 "[@instanceName]" "lowerLeft" "R0" 0.05 ) ; sfvCloseSaveSym( sv ) ; * *************************************************************