(defMathConstants 'mconst) mfgGrid = 100.0 debug = nil ; SNAPS STUFF TO GRID ; uses global mfgGrid to do so, which gets redefined in generator ; to the tech mfg grid using the techFile (defun snap (p) floor(p*mfgGrid)/mfgGrid ) ; DEBUG output function, uncomment to get all output (procedure output( @rest args) (when debug (apply 'printf args)) ) ; VECTOR HELPER FUNCTIONS ; add a vector vect to a point p (defun addVect (p vect) (car(p)+car(vect)):(cadr(p)+cadr(vect)) ) ; multiplies a vector vect with a scalar (defun multVect (vect scalar) (car(vect)*scalar):(cadr(vect)*scalar) ) ; multiplies a vector vect with -1 (inverse) (defun invVect (vect) (multVect vect -1) ) ; subtracts two vectors from one another ; (does vect2 - vect1) (defun subVect (vect1 vect2) (car(vect2)-car(vect1)):(cadr(vect2)-cadr(vect1)) ) ; returns length of a vector (defun lenVect (vect) sqrt((1.0*car(vect)**2)+(1.0*cadr(vect)**2)) ) ; rotates a vector vect by 90 CCW (defun rotVect (vect) cadr(vect):-car(vect) ) ; normalizes a vector vect by its length (defun normalizeVect (vect) (multVect vect 1.0/(lenVect vect)) ) ; multiplies a point p with a 2x2 matrix m (defun matmult (m p) result = list() for(j 1 2 temp = 0 for(i 1 2 a = nthelem(j nthelem(i m)) b = nthelem(i p) temp += a*b ) result = append(result list(temp)) ) result ) ; Tests if a float x is zero in respect to the manufacturing grid ; (smaller then one grid spacing) (defun ind_isZero (x) abs(x)<1/mfgGrid ) ; return the sign of the input ; (ind_sign 20) = 1 ; (ind_sign -10) = -1 ; (ind_sign 0) = 0 (defun ind_sign (x) (if ind_isZero(x) then 0 else (if (x > 0) then 1 else -1 ) ) ) ; only returns vectors with their sign as components ; e.g. (cleanupDirVect 0.1:-20) == 1:-1 (defun cleanDirVect (vect) (ind_sign car(vect)):(ind_sign cadr(vect)) )) ; DRAWING HELPER FUNCTIONS ; creates a pin using database functions ; and a corresponding net with name name ; does so on the layer layer (defun ind_createPin (id name points layer) (leCreatePin id layer "polygon" points name "inputOutput" '("none") ) /* net = dbCreateNet(id name) fig = dbCreatePolygon(id layer points) trm = dbCreateTerm(net name "inputOutput") pin = dbCreatePin(net fig name trm) pin~>accessDir = '( "none" ) */ lab = dbCreateLabel(id '("TEXT") cadr(points) name "centerLeft" "R0" "roman" 3) ;output("created pin %s\n", name) ) ; Creates the end connection for the inductor and ; places a pin there ; TODO: create underpass here (defun ind_createEnd (id start w dir) (output "[createEnd] start=%P dir=%P\n", start, dir) (let (center points left right up viaDef) viaDef = (techFindViaDefByName (techGetTechFile id) "TM2_TM1") center = (addVect start (multVect dir (snap -w/2))) left = (addVect center (multVect dir (snap -w/2))) right = (addVect center (multVect dir (snap w/2))) up = (multVect (rotVect dir) (snap w/2)) points = list( (addVect left up) (addVect right up) (addVect right (invVect up)) (addVect left (invVect up)) ) /*(dbCreateVia id viaDef (addVect center (multVect dir -w)) "R0" list( list("cutWidth" (snap w-0.6)) list("cutHeight" (snap w-0.6)) ) )*/ ; create underpass /*(dbCreatePathSeg id underpassLayer (addVect center (multVect dir -w)) (addVect (addVect center (multVect dir -w)) (multVect up -20)) w*2 "extend" "extend" )*/ /*(ind_createPin id "inner" points list("TopMetal2" "drawing") )*/ ) ) ; Creates the start of the inductor ; and places a pin there (defun ind_createStart (id start width dir) (let (center points) center = start; (addVect start multVect((invVect dir) width/2)) points = list( (addVect center (-width):width) (addVect center width:width) (addVect center width:(-width)) (addVect center (-width):(-width)) ) (ind_createPin id "outer" points list("TopMetal2" "drawing") ) ) ) ; creates one segment of the inductor and calls itself recursively to complete ; one inductor (defun ind_drawSegment (id n seg start r) dx = snap(r*talpha) (case mod(seg, 8) (0 end = dx:r ) (1 end = r:dx ) (2 end = r:-dx ) (3 end = dx:-r ) (4 end = -dx:-r ) (5 end = -r:-dx ) (6 end = -r:dx ) (7 r -= pitch end = (addVect start (r-dx):(r-dx)) ) ) dir = (subVect start end) ndir = (normalizeVect dir) isstart = (seg == 0) isend = (n < 1) ; DEBUG (output "seg: %d, n: %L (start: %L, end: %L)\n", mod(seg, nseg), n, isstart, isend) when(isend l = (lenVect dir) if((mod(seg,2)==0) then end = addVect(end multVect(ndir (snap l*(n-1)))) else end = addVect(start multVect(ndir (snap l*(n-1)))) ) ) if((mod(seg, 2) == 0) then (dbCreatePathSeg id inductorLayer start end w if(!isstart then "custom" else "extend") if(!isend then "custom" else "truncate") list(if(isstart then 0 else whalv) if(isend then 0 else whalv) list(w2halv w2halv whalv w2halv w2halv whalv)) ) else (dbCreatePathSeg id inductorLayer start end w2 "truncate" "truncate" ) ) ; FIXME: broken, and underpass is not generated anyway ;when(isstart ind_createStart(id start whalv ndir)) ;when((n<=1) ind_createEnd(id end whalv ndir)) when((and (not non_of_that_ihp_rubbish) isstart) (dbCreateRect id '("IND" "drawing") list(-whalv:(r+whalv) whalv:(r-whalv)) ) (dbCreateLabel id '("TEXT" "drawing") 0:r "L2_TM2" "centerCenter" "R0" "roman" whalv ) ) n -= 1 (output "n=%L\n", n) (when (n > 0) (ind_drawSegment id n seg+1 end r) ) ; when ) (defun ind_drawOctagon (r w1 w2 layer shorting) (let (a b start end) ; define start a = (snap r*tanalpha) ; a = (floor r*tanalpha*mfgGrid)/(0.25*mfgGrid) b = (snap r) start = 0:0 end = 0:0 (for n 0 8 (let (w) w = (if mod(n 2)==1 then w2 else w1) (case n (0 start = -a:b end = a:b ) (1 start = b:-a end = a:-b ) (2 start = b:-a end = b:a ) (3 start = a:b end = b:a ) (4 start = a:-b end = -a:-b ) (5 start = -a:-b end = -b:-a ) (6 start = -b:-a end = -b:a ) (7 start = -b:a end = (addVect -a:b -shorting:-shorting) ) ) ; draw ring segment (apply 'dbCreatePathSeg id gndshield_layer start end w if(mod(n 2)==0 then list( "truncate" "truncate" list(0 0) ) else list( "custom" (if n==7 then "truncate" else "custom") list( w1/2 (if n==7 then 0 else w1/2) list(w1/2 w1/2 w2/2 w1/2 w1/2 w2/2) ) ) ) ) ; END apply ) ; END let ) ; END for ) ) (defun ind_gndshield2 (yr) (let (a l) a = min((yr*m + gap) r) l = (r-a) when(l>0 ; DEBUG (output "(r=%L, m=%L, w=%L) yr=%L, a=%L, length=%L\n" m r w yr a l) (when ((mod n 2) == 1) l /= sqrt(2) ) l = (snap l) dir = (cleanDirVect matmult(rotmat 0:-1)) start = matmult(rotmat yr:r) ; start = (snap (car start)):(snap (cadr start)) start = (floor((car start)*50.0)/50.0):(floor((cadr start)*50.0)/50.0) end = (addVect start (multVect dir l)) ; DEBUG (output "start=%P, end=%P (dir=%P)\n" start end dir) (dbCreatePathSeg id gndshield_layer start end w "truncate" "truncate" ) when(yr != 0 ; DEBUG (output "create mirror at y=%L\n" -yr) start = -car(start):cadr(start) end = -car(end):cadr(end) dbCreatePathSeg( id gndshield_layer start end w "truncate" "truncate" ) ) (ind_gndshield2 yr+pitch) ) ; END when ) ; END let ) (defun ind_gndshield (r w1 w2 pitch gap window) (let (n w) for(n 0 nseg-1 ; DEBUG angle = mconst.PI*2/nseg * n rotmat = list( list(cos(angle) -sin(angle)) list(sin(angle) cos(angle)) ) w = if((mod n 2)==1 then w2 else w1) (output "draw a gndshield part with w=%L\n" w) (ind_gndshield2 0) ) ; END for ) ) ; uses ind_drawSegment to create a complete inductor with ; the given specification defun(drawInductor (n r width pitch) (let (id w whalv w2 w2halv ahalv talpha hl nseg) ; the PCell cell view id id = pcCellView (when (not id) (error "could not find cell view") ) ; when ; set multiple globals inductorLayer = '("TopMetal2", "drawing") underpassLayer = '("TopMetal1", "drawing") output("generating inductor with r=%L, turns=%L, pitch=%L and width=%L\n" r n pitch width) whalv = snap(width/2) w = whalv*2 ; diagonal segment width w2halv = snap(width*sqrt(2)/2) w2 = 2*w2halv pitch = snap(pitch + w) ; pitch from segment sides r = snap(r - whalv) ; middle radius nseg = 8 ; octagonal inductor ; calculation globals ahalv = mconst.PI/nseg ; angle between segments talpha = tan(ahalv) hl = snap((r-w/2)*talpha) ; start point ; draw inductor (let (dir) (ind_drawSegment id n*nseg 0 -hl:r r) ) ) ) (defun drawGndShield (r layer pitch width window) ; setup gndshield (let (id gndshield_layer nseg dir tanalpha m w1 w2 ymax) r -= (snap width/2) w1 = (snap width*1.0) w2 = (snap width*1.0*sqrt(2)) id = pcCellView gndshield_layer = layer nseg = 8 tanalpha = tan(mconst.PI/nseg) segw = r/2.0*tanalpha pitch += width m = 1/tanalpha ymax = floor(segw/(2*pitch)) (output "draw gnd shield with nfins=%L, r=%L, segw=%L, segcount=%L and w=%L on %L\n" ymax r segw (floor segw/(2*pitch)) width gndshield_layer) ; draw the gndshield itself (ind_gndshield r w1 w2 pitch width*2 (snap window)) ; draw contact ring (ind_drawOctagon r w1 w2 gndshield_layer window) ) ) (defun ind_getOctagonPoints (r w1 w2 shorting) (let (a b start end) ; define start a = (floor r*tan(mconst.PI/8)*mfgGrid*0.5)/(0.5*mfgGrid) b = (snap r) start = 0:0 end = 0:0 result=list() (for n 0 8 (let (w) w = (if mod(n 2)==1 then w2 else w1) (case n (0 start = -a:b end = a:b ) (1 start = a:b end = b:a ) (2 start = b:a end = b:-a ) (3 start = b:-a end = a:-b ) (4 start = a:-b end = -a:-b ) (5 start = -a:-b end = -b:-a ) (6 start = -b:-a end = -b:a ) (7 start = -b:a end = (addVect -a:b -shorting:-shorting) ) ) result=(append result list(list(start end w))) ) ) ) result ) (defun ind_segmentsToPolygon (seg) (output "segments=%L\n" seg) result = list(caar(seg)) seg = cdr(seg) (when seg result = (append result (ind_segmentsToPolygon seg)) ) result ) (defun drawBlockLayer (points layers) (let (layer) layer = car(layers) layers = cdr(layers) ; DEBUG (output "draw block layer %L\n", layer) (dbCreatePolygon pcCellView layer points ) (when layers (drawBlockLayer points layers) ) ) ) (defun drawBlockLayers (r layers) points = (ind_segmentsToPolygon (ind_getOctagonPoints r 0 0 0)) (output "polygon points %L\n", points) (drawBlockLayer points layers) ) (defun setup () ; get manufacturing grid spacing and set global accordingly mfgGrid=1/techGetMfgGridResolution(techGetTechFile(pcCellView)) (output "using manufacturing grid of %L\n", mfgGrid) ) ; defines the inductor PCell (pcDefinePCell list( (ddGetObj "mmic14_inductors") "inductorPcell" "layout") list( ( turns "float" 1.0 ) ( radius "float" 100.0 ) ( width "float" 2.1 ) ( pitch "float" 2.1 ) ; gndshield parameters ( gndshield "boolean" nil ) ( gndpitch "float" 2.1 ) ( gndwidth "float" 2.1 ) ( gndoverlap "float" 3 ) ( gndwindow "float" 2.1 ) ; layer setup ( blockLayers "ilList" '( ("Metal1" "nofill") ("Metal2" "nofill") ("Metal3" "nofill") ("TopMetal1" "nofill") ("TopMetal2" "nofill") ("GatPoly" "nofill") ("Activ" "nofill") ("PWell" "block") ("NoRCX" "drawing") ) ) ( gndShieldLayer "ilList" '("Metal1" "drawing")) ( non_of_that_ihp_rubbish "boolean" nil) ) (let () (setup) (drawInductor turns radius width pitch) (when gndshield (drawGndShield radius+gndoverlap gndShieldLayer gndwidth gndpitch gndwindow) ) (when (not non_of_that_ihp_rubbish) (when blockLayers (drawBlockLayers radius+gndoverlap blockLayers) ) ) ) )