(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 ) ; 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) 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" ) ) when(isstart ind_createStart(id start whalv ndir)) when((n<=1) ind_createEnd(id end whalv ndir)) n -= 1 (output "n=%L\n", n) (when (n > 0) (ind_drawSegment id n seg+1 end r) ) ; when ) defun(ind_gndshield (r width pitch gap window) (output "draw gnd shield with r=%L and w=%L\n", r, width) r2 = r + width m = 1/tan(mconst.PI/nseg) segw = 2*r/m pitch += width (defun iszero (x) abs(x) < 1/mfgGrid ) (defun sign (x) (if iszero(x) then 0 else (if (x > 0) then 1 else -1 ) ) ) (defun cleanup (vect) (sign car(vect)):(sign cadr(vect)) ) for(n 0 nseg-1 angle = mconst.PI*2/nseg * n rotmat = list( list(cos(angle) -sin(angle)) list(sin(angle) cos(angle)) ) w1 = width w = w1 (when ((mod n 2) == 1) w2 = w1*sqrt(2) w= w2 ) w1 = (snap w1) w2 = (snap w2) w = (snap w) ; contact_ring = append(contact_ring list(list(r2*cos(angle-mconst.PI/nseg) r2*sin(angle-mconst.PI/nseg)))) for(y 0 floor(segw/(2*pitch)) yr = (snap y*pitch) a = (snap min(yr*m + gap, r)) l = (r-a) (when ((mod n 2) == 1) l /= sqrt(2) ) l = (snap l) dir = (cleanup matmult(rotmat 0:-1)) (output "%P\n", dir) start = matmult(rotmat yr:r) start = (floor((car start)*50.0)/50.0):(floor((cadr start)*50.0)/50.0) end = (addVect start (multVect dir l)) (dbCreatePathSeg id gndshield_layer start end w "truncate" "truncate" ) when(y != 0 start = -car(start):cadr(start) end = -car(end):cadr(end) dbCreatePathSeg( id gndshield_layer start end w "truncate" "truncate" ) ) ) ; for a = (floor r/m*mfgGrid/2)/(0.5*mfgGrid) b = (snap r) start = 0:0 end = 0:0 (print n) (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 -window:-window) ) ) ; draw contact ring (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) ) ) ) ) ) ) ) ; uses ind_drawSegment to create a complete inductor with ; the given specification defun(drawInductor (n r width pitch) ; 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 (ind_drawSegment id n*nseg 0 -hl:r r) ) (defun drawGndShield (r pitch width window) ; setup gndshield id = pcCellView gndshield_layer = '("Metal1" "drawing") nseg = 8 (ind_gndshield r pitch width width*2 (snap window)) ) (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 ); techGetSpacingRule(techGetTechFile(pcCellView) "minWidth" "TopMetal2" )) ( pitch "float" 2.1 ) ( gndshield "boolean" nil ) ( gndpitch "float" 2.1 ) ( gndwidth "float" 2.1 ) ( gndoverlap "float" 3 ) ( gndwindow "float" 2.1 ) ) (let () (setup) (drawInductor turns radius width pitch) (when gndshield (drawGndShield radius+gndoverlap gndwidth gndpitch gndwindow) ) ) )