<> <> <> <> DIRECTORY CD USING [Rect, DesignNumber, DesignPosition, DesignRect, Level, ObjectProcs, ApplicationPtr], CDInline USING [RectAt, Extend, NonEmpty, universe], TerminalIO USING [WriteRope], CMos USING [ndif, pdif, pol, met, met2, nwel, wellSurround, nwelCont, pwelCont], CMosTransistors USING [TransistorPtr], CMosContacts USING [ContactPtr, ContactType], Atom USING [GetPropFromList, PutPropOnList], IO USING [PutRope, Put, char, rope, PutF, int], Rope USING [Cat, ROPE], SpinifexCircuit USING [AddBox, AddRect, AdjustNode, AttachedNode, CellPostProcessProc, Circuit, CircuitConstraint, CircuitNode, CombineNodePropertyProc, ConversionProc, CreateLinkage, Dimension, FindRootNode, IllegalConstruct, LinkageAttach, LookupNode, NodeLinkage, PaintErrorRect, EnumerateGeometry, PerRectProc, Rectangle, RectDelta, SpinifexLayerIndex, TechHandle, BoxMapProc], SpinifexOutput USING [LinkagePrintProc], SpinifexExtras USING [ProcessMosTransistor, PerDrawRectProc, --SWNGrow, NESGrow,-- WNEGrow, ESWGrow], CMosSpinifex ; CMosSpinifexObjConvImpl: CEDAR PROGRAM IMPORTS Atom, IO, Rope, CMos, CDInline, TerminalIO, SpinifexCircuit, SpinifexExtras, CMosSpinifex EXPORTS CMosSpinifex ~ BEGIN OPEN CMosSpinifex; <<-- New Regime Transistor converters (IE the Shand unified xstr representaion)>> ConvTransistor: PUBLIC SpinifexCircuit.ConversionProc -- [appl: CD.ApplicationPtr, cir: REF ] -- ~ { p: CMosTransistors.TransistorPtr = NARROW[appl.ob.specificRef]; xstr: REF SpinifexCircuit.NodeLinkage; gateNode, sourceNode, drainNode: REF SpinifexCircuit.CircuitNode; difSpinifexLayer: SpinifexCircuit.SpinifexLayerIndex ~ IF appl.ob.level = CMos.ndif THEN ndifSpinifex ELSE pdifSpinifex; difChannel: REF CircuitConstraint ~ IF appl.ob.level = CMos.ndif THEN nDifChannel ELSE pDifChannel; <> IF ~ p.angle THEN { -- Oh this is easy! <<-- Get the Xstr bounding box exclusive of nwel, i.e. only pol and dif.>> wellPolSurround: CD.DesignNumber ~ MAX[0, CMos.wellSurround-p.wExt]; pdBox: CD.Rect ~ IF appl.ob.level = CMos.ndif THEN [0, 0, appl.ob.size.x, appl.ob.size.y] ELSE [wellPolSurround, CMos.wellSurround, appl.ob.size.x-wellPolSurround, appl.ob.size.y-CMos.wellSurround]; polBox: CD.Rect ~ [pdBox.x1, pdBox.y1+p.lExt, pdBox.x2, pdBox.y2-p.lExt]; <> <> channel: CD.Rect ~ [pdBox.x1+p.wExt, pdBox.y1+p.lExt, pdBox.x2-p.wExt, pdBox.y2-p.lExt]; difExtSouth: CD.Rect ~ [pdBox.x1+p.wExt, pdBox.y1, pdBox.x2-p.wExt, pdBox.y1+p.lExt]; difExtNorth: CD.Rect ~ [pdBox.x1+p.wExt, pdBox.y2-p.lExt, pdBox.x2-p.wExt, pdBox.y2]; excludePol: REF CircuitConstraint ~ IF appl.ob.level = CMos.ndif THEN excludePolByNDif ELSE excludePolByPDif; IF appl.ob.level # CMos.ndif THEN { polWellSurround: CD.DesignNumber ~ MAX[0, CMos.wellSurround-p.wExt]; wellNode: REF SpinifexCircuit.CircuitNode _ cir.AddRect[ lev~ CMos.nwel, dim~ [0, polWellSurround, appl.ob.size.x, appl.ob.size.y-polWellSurround], appl~ appl, pos~ pos, orient~ orient]; wellNode.properties _ wellNode.properties.PutPropOnList[WellNodeAtom, WellNodeAtom]; }; <<-- Add poly boxes>> gateNode _ cir.AddRect[ lev~CMos.pol, dim~polBox, appl~appl, pos~ pos, orient~ orient]; <<-- Add rects for poly exclusion and channel lead-in.>> <<-- At the top>> [] _ cir.AddBox[ spinifexLayer~ polSpinifex, dim~ [difExtNorth.x1, difExtNorth.y1, difExtNorth.x2, difExtNorth.y1+difToPolSep], appl~ appl, pos~ pos, orient~ orient, value~ channelEdge]; [] _ cir.AddBox[ spinifexLayer~ polSpinifex, dim~ [difExtNorth.x1, difExtNorth.y1+difToPolSep, difExtNorth.x2, difExtNorth.y2], appl~ appl, pos~ pos, orient~ orient, interestBloat~ SpinifexExtras.WNEGrow[ difToPolExtSep], value~ excludePol]; <<-- ... and the bottom>> [] _ cir.AddBox[ spinifexLayer~ polSpinifex, dim~ [difExtSouth.x1, difExtSouth.y2-difToPolSep, difExtSouth.x2, difExtSouth.y2], appl~ appl, pos~ pos, orient~ orient, value~ channelEdge]; [] _ cir.AddBox[ spinifexLayer~ polSpinifex, dim~ [difExtSouth.x1, difExtSouth.y1, difExtSouth.x2, difExtSouth.y2-difToPolSep], appl~ appl, pos~ pos, orient~ orient, interestBloat~ SpinifexExtras.ESWGrow[ difToPolExtSep], value~ excludePol]; <<-- Fill channel gap>> [] _ cir.AddBox[ spinifexLayer~ difSpinifexLayer, dim~ channel, appl~ appl, pos~ pos, orient~ orient, value~ difChannel]; <<-- Add rects for diff width spacing and connection>> sourceNode _ cir.AddBox[ spinifexLayer~difSpinifexLayer, dim~difExtNorth, appl~appl, pos~ pos, orient~ orient, interestBloat~SpinifexExtras.WNEGrow[difSep]]; drainNode _ cir.AddBox[ spinifexLayer~ difSpinifexLayer, dim~ difExtSouth, appl~ appl, pos~ pos, orient~ orient, interestBloat~ SpinifexExtras.ESWGrow[difSep]] } ELSE { -- Oh no not a bent transistor yetcchh! sourceDrainList: LIST OF REF SpinifexCircuit.CircuitNode; sourceDrainCount: INTEGER; [gateNode, sourceDrainList, sourceDrainCount] _ SpinifexExtras.ProcessMosTransistor[appl, pos, orient, cir, difSpinifexLayer, polSpinifex, difChannel, channelEdge, difToPolSep, MapMaterial, cir]; IF sourceDrainCount = 2 THEN { sourceNode _ sourceDrainList.first; drainNode _ sourceDrainList.rest.first; } ELSE { TerminalIO.WriteRope[ Rope.Cat["Xstr with ", (SELECT sourceDrainCount FROM 0 => "no", 1 => "only 1", ENDCASE => "more than 2"), " sources/drains not included in circuit description\n"]]; RETURN } }; xstr _ cir.CreateLinkage[appl]; <<-- We're a lot smarter than this program, so let's adjust the area and perim values its got.>> SpinifexCircuit.AdjustNode[ gateNode, polSpinifex, (p.width+2*p.wExt)*p.length, ((p.width+2*p.wExt)+p.length)*2, absolute]; SpinifexCircuit.AdjustNode[ sourceNode, difSpinifexLayer, p.lExt*p.width, 2*p.lExt + p.width, absolute]; SpinifexCircuit.AdjustNode[ drainNode, difSpinifexLayer, p.lExt*p.width, 2*p.lExt + p.width, absolute]; -- Ignore perim leading into channel. SpinifexCircuit.LinkageAttach[link~xstr, attachType~$Gate, node~gateNode]; SpinifexCircuit.LinkageAttach[link~xstr, attachType~$Source, node~sourceNode]; SpinifexCircuit.LinkageAttach[link~xstr, attachType~$Drain, node~drainNode]; }; MapMaterial: SpinifexExtras.PerDrawRectProc -- [r: CD.DesignRect, l: CD.Level, data: REF ANY] RETURNS [TransistorMaterial] -- ~ { cir: REF SpinifexCircuit.Circuit ~ NARROW[ data, REF SpinifexCircuit.Circuit]; SELECT l FROM CMos.ndif, CMos.pdif => RETURN [diffusion]; CMos.pol => RETURN [polysilicon]; CMos.nwel => { wellNode: REF SpinifexCircuit.CircuitNode _ cir.AddRect[ lev~ CMos.nwel, dim~ r]; wellNode.properties _ wellNode.properties.PutPropOnList[WellNodeAtom, WellNodeAtom]; RETURN [nothing]; }; ENDCASE => RETURN [nothing]; }; ConvertPDifRect: PUBLIC SpinifexCircuit.ConversionProc -- [appl: CD.ApplicationPtr, cir: REF SpinifexCircuit.Circuit] -- ~ { wellNode: REF SpinifexCircuit.CircuitNode; nWellBox: CD.Rect _ CDInline.RectAt[pos~[0,0], size~appl.ob.size]; pDifBox: CD.Rect _ CDInline.Extend[nWellBox, -CMos.wellSurround]; IF ~CDInline.NonEmpty[pDifBox] THEN ERROR; [] _ cir.AddRect[ lev~CMos.pdif, dim~pDifBox, appl~appl, pos~ pos, orient~ orient]; wellNode _ cir.AddRect[ lev~CMos.nwel, dim~nWellBox, appl~appl, pos~ pos, orient~ orient]; wellNode.properties _ wellNode.properties.PutPropOnList[WellNodeAtom, WellNodeAtom]; }; <<-- Contact converters>> ContactMap: TYPE ~ RECORD [ rm: LIST OF RectMap, bm: LIST OF BoxMap ]; MapRectDelta: TYPE ~ RECORD [dx1, dy1, dx2, dy2: INT]; RectMap: TYPE ~ RECORD [ level: CD.Level, delta: MapRectDelta ]; BoxMap: TYPE ~ RECORD [ layer: SpinifexCircuit.SpinifexLayerIndex, value: REF ANY, delta: MapRectDelta ]; MapArray: TYPE ~ ARRAY {mnDif, mpDif, mnWCont, mpWCont, nDifShort, pDifShort, nbutt, pbutt, mPol, mm2 } OF ContactMap; ContactMaps: REF MapArray; InitContacts: PUBLIC PROCEDURE ~ { ws: CD.DesignNumber ~ CMos.wellSurround; ContactMaps _ NEW[MapArray _ ALL[ [NIL, NIL] ]]; <<`Normal' levels must precede nwelCont.>> ContactMaps[mnDif].rm _ LIST[[CMos.ndif, [0,0,0,0]], [CMos.met, [0,0,0,0]] ]; ContactMaps[mpDif].rm _ LIST[[CMos.pdif, [0,0,0,0]], [CMos.met, [0,0,0,0]], [CMos.nwel, [ws,ws,ws,ws]] ]; ContactMaps[mnWCont].rm _ LIST[[CMos.met, [0,0,0,0]], [CMos.nwelCont, [0,0,0,0]] ]; ContactMaps[mpWCont].rm _ LIST[[CMos.met, [0,0,0,0]], [CMos.pwelCont, [0,0,0,0]] ]; ContactMaps[mPol].rm _ LIST[[CMos.pol, [0,0,0,0]], [CMos.met, [0,0,0,0]] ]; ContactMaps[mm2].rm _ LIST[[CMos.met2, [0,0,0,0]], [CMos.met, [0,0,0,0]] ]; ContactMaps[nDifShort].rm _ LIST[[CMos.ndif, [0,0,0,-4* ContactMaps[pDifShort].rm _ LIST[[CMos.pdif, [0,0,0,-4* ContactMaps[nbutt].rm _ LIST[[CMos.ndif, [0,-3* ContactMaps[nbutt].bm _ LIST[[polSpinifex, channelEdge, [ [polSpinifex, polXorDif, [-4* false poly spacing reports if commented BoxMap is included. ContactMaps[pbutt].rm _ LIST[[CMos.pdif, [0,-3* [ws,ws-2* ContactMaps[pbutt].bm _ ContactMaps[nbutt].bm; }; ConvertContact: PUBLIC SpinifexCircuit.ConversionProc -- [appl: CD.ApplicationPtr, pos: CD.DesignPosition, orient: CD.Orientation, cir: REF SpinifexCircuit.Circuit] -- ~ { DeltaBox: PROCEDURE [box: CD.Rect, delta: MapRectDelta] RETURNS [CD.Rect] ~ INLINE { RETURN [ [x1~ box.x1-delta.dx1, y1~ box.y1-delta.dy1, x2~ box.x2+delta.dx2, y2~ box.y2+delta.dy2] ] }; SimpleMapping: PROCEDURE [map: ContactMap] ~ { node: REF SpinifexCircuit.CircuitNode _ NIL; box: CD.Rect _ appl.ob.p.insideRect[appl.ob]; FOR r: LIST OF RectMap _ map.rm, r.rest WHILE r # NIL DO SELECT r.first.level FROM CMos.nwel => { wellNode: REF SpinifexCircuit.CircuitNode _ cir.AddRect[ lev~r.first.level, dim~DeltaBox[box, r.first.delta], appl~appl, pos~ pos, orient~ orient]; wellNode.properties _ wellNode.properties.PutPropOnList[ WellNodeAtom, WellNodeAtom]; }; ENDCASE => node _ cir.AddRect[ lev~r.first.level, dim~DeltaBox[box, r.first.delta], appl~appl, pos~ pos, orient~ orient, value~node]; ENDLOOP; FOR b: LIST OF BoxMap _ map.bm, b.rest WHILE b # NIL DO node _ cir.AddBox[ spinifexLayer~ b.first.layer, dim~ DeltaBox[box, b.first.delta], appl~ appl, pos~ pos, orient~ orient, value~ IF b.first.value # NIL THEN b.first.value ELSE node]; ENDLOOP; }; cp: CMosContacts.ContactPtr ~ NARROW[appl.ob.specificRef]; SELECT cp.typ FROM burr => { <<-- Buried contacts come in three flavours, Pol-Surround, Dif-Surround and Crossing. The buried contacts in chipndale are parameterized by lExt and wExt, their interpretation is as follows: Diff always extends 1 above/below and to the right is determined by lExt and wExt respectively. 2 material is Pol, at 2 assumed that such combinations will not be created.)>> s: CD.DesignPosition ~ appl.ob.size; node: REF SpinifexCircuit.CircuitNode ~ cir.AddRect[ lev~CMos.pol, dim~[2* pos, orient~ orient]; IF appl.ob.level # CMos.ndif THEN ERROR SpinifexCircuit.IllegalConstruct[appl.ob.p.insideRect[appl.ob], "Buried Contact may only connect to n-Diffusion"]; [] _ cir.AddBox[ spinifexLayer~ ndifSpinifex, dim~ [2* buriedNDifPol, interestBloat~ [difSep, difSep, difSep, difSep]]; SELECT TRUE FROM cp.lExt < 2* <> wex: CD.DesignNumber ~ MAX[cp.wExt+ lex: CD.DesignNumber ~ MAX[cp.lExt+ [] _ cir.AddRect[ lev~appl.ob.level, dim~[ [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* value~channelEdge]; [] _ cir.AddBox[ spinifexLayer~ndifSpinifex, dim~[2* [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* }; cp.lExt < 2* <> lex: CD.DesignNumber ~ MAX[cp.lExt+ [] _ cir.AddBox[ spinifexLayer~ndifSpinifex, dim~[2* [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* value~polAndDif]; <> [] _ cir.AddRect[ lev~appl.ob.level, dim~[ [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* value~channelEdge]; <> [] _ cir.AddRect[ lev~appl.ob.level, dim~[s.x-cp.wExt, lex, s.x-(cp.wExt- value~node]; [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[s.x-cp.wExt, lex, s.x-(cp.wExt-difToPolSep), s.y-lex], appl~appl, pos~ pos, orient~ orient, value~channelEdge]; }; cp.lExt >= 2* <> wex: CD.DesignNumber ~ MAX[cp.wExt+ lex: CD.DesignNumber ~ cp.lExt- [] _ cir.AddBox[ spinifexLayer~ndifSpinifex, dim~[2* value~node]; [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* value~polAndDif]; <> [] _ cir.AddRect[ lev~appl.ob.level, dim~[ [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* value~channelEdge]; <> [] _ cir.AddRect[ lev~appl.ob.level, dim~[2* [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* orient~ orient, value~channelEdge]; <> [] _ cir.AddRect[ lev~appl.ob.level, dim~[2* [] _ cir.AddBox[ spinifexLayer~polSpinifex, dim~[2* value~channelEdge]; }; cp.lExt >= 2* <> ERROR SpinifexCircuit.IllegalConstruct[appl.ob.p.insideRect[appl.ob], "Polysilicon surrounded by diffusion or all sides in Buried Contact"]; }; ENDCASE => ERROR; }; mDif => SimpleMapping[ContactMaps[ SELECT appl.ob.level FROM CMos.ndif => mnDif, CMos.pdif => mpDif, CMos.nwelCont => mnWCont, CMos.pwelCont => mpWCont, ENDCASE => ERROR]]; difShort => SimpleMapping[ContactMaps[ SELECT appl.ob.level FROM CMos.ndif => nDifShort, CMos.pdif => pDifShort, ENDCASE => ERROR]]; butt => SimpleMapping[ContactMaps[ SELECT appl.ob.level FROM CMos.ndif => nbutt, CMos.pdif => pbutt, ENDCASE => ERROR]]; mPol => SimpleMapping[ContactMaps[mPol]]; mm2 => SimpleMapping[ContactMaps[mm2]]; ENDCASE => ERROR; }; RoseTransistor: PUBLIC SpinifexOutput.LinkagePrintProc -- [ stream: IO.STREAM, linkage: REF NodeLinkage, name: Rope.ROPE, PrintNode: NodePrintProc] -- ~ { PrintAttachment: PROCEDURE [ key: ATOM, portName: Rope.ROPE] ~ { stream.Put[ IO.rope[" (\""], IO.rope[portName], IO.rope["\" "]]; FOR attachments: LIST OF REF SpinifexCircuit.AttachedNode _ linkage.nodes, attachments.rest WHILE attachments # NIL DO IF attachments.first.attachmentType = key THEN { PrintNode[stream, attachments.first.node]; stream.PutRope[")"]; RETURN } ENDLOOP; stream.PutRope[ "\"?\")"]; }; p: CMosTransistors.TransistorPtr ~ NARROW[ linkage.source.ob.specificRef]; stream.Put[ IO.rope["(CI "], IO.rope[name], IO.rope[" \"Transistor[strength: drive, positive: "]]; stream.PutRope[ SELECT linkage.source.ob.level FROM CMos.ndif => "TRUE", CMos.pdif => "FALSE", ENDCASE => "UNKNOWN" ]; stream.PutRope[ ", mode: Enhancement, unidirectional: FALSE, biased: FALSE, offStrength: none]\""]; stream.PutRope[ " (CIC"]; PrintAttachment[ $Gate, "gate"]; PrintAttachment[ $Source, "ch1"]; PrintAttachment[ $Drain, "ch2"]; stream.PutRope[ "))"] }; ThymeTransistor: PUBLIC SpinifexOutput.LinkagePrintProc -- [ stream: IO.STREAM, linkage: REF NodeLinkage, name: Rope.ROPE, PrintNode: NodePrintProc] -- ~ { PrintAttachment: PROCEDURE [ key: ATOM] ~ { FOR attachments: LIST OF REF SpinifexCircuit.AttachedNode _ linkage.nodes, attachments.rest WHILE attachments # NIL DO IF attachments.first.attachmentType = key THEN { PrintNode[stream, attachments.first.node]; RETURN } ENDLOOP; stream.Put[ IO.char['?]]; }; p: CMosTransistors.TransistorPtr ~ NARROW[ linkage.source.ob.specificRef]; defaultW: INTEGER ~ 4* defaultL: INTEGER ~ 2* stream.Put[ IO.rope[name], IO.rope[": "]]; stream.PutRope[ SELECT linkage.source.ob.level FROM CMos.ndif => "ETran[", CMos.pdif => "CTran[", ENDCASE => "FunnyTrans[" ]; PrintAttachment[ $Gate]; stream.Put[ IO.char[',]]; PrintAttachment[ $Source]; stream.Put[ IO.char[',]]; PrintAttachment[ $Drain]; IF p.width # defaultW OR p.length # defaultL THEN { stream.Put[ IO.char['|]]; IF p.width # defaultW THEN stream.PutF[" W_%g", IO.int[p.width/ IF p.width # defaultW AND p.length # defaultL THEN stream.Put[ IO.char[',]]; IF p.length # defaultL THEN stream.PutF[" L_%g", IO.int[p.length/ }; stream.PutRope[ "];"] }; wellConnection: ATOM ~ $SpinifexCMosWellConnection; WellNodeAtom: ATOM ~ $SpinifexCMosNWellNode; CopyWellConnections: PUBLIC SpinifexCircuit.CombineNodePropertyProc -- [circuit: REF Circuit, to, from: Atom.PropList, fromNesting: LIST OF CD.ApplicationPtr] RETURNS [Atom.PropList] -- ~ { fromConnections: LIST OF REF SpinifexCircuit.CircuitNode _ NARROW[from.GetPropFromList[ wellConnection]]; toConnections: LIST OF REF SpinifexCircuit.CircuitNode _ NARROW[to.GetPropFromList[ wellConnection]]; FOR fl: LIST OF REF SpinifexCircuit.CircuitNode _ fromConnections, fl.rest WHILE fl # NIL DO fromNode: REF SpinifexCircuit.CircuitNode ~ IF fromNesting = NIL THEN fl.first ELSE circuit.FindRootNode[fl.first, fromNesting, TRUE].node; -- Side Effect !! may add fl.first to subcircuits ports. FOR tl: LIST OF REF SpinifexCircuit.CircuitNode _ toConnections, tl.rest WHILE tl # NIL DO IF fromNode.LookupNode[] = tl.first.LookupNode[] THEN EXIT; REPEAT FINISHED => toConnections _ CONS[fromNode, toConnections] ENDLOOP ENDLOOP; IF toConnections # NIL THEN to _ to.PutPropOnList[ wellConnection, toConnections]; IF from.GetPropFromList[ WellNodeAtom] # NIL AND to.GetPropFromList[ WellNodeAtom] = NIL THEN to _ to.PutPropOnList[ WellNodeAtom, WellNodeAtom]; RETURN [ to ] }; CheckWellConnections: PUBLIC SpinifexCircuit.CellPostProcessProc -- [cell: REF SpinifexCircuit.LogicalCell] -- ~ { FOR nl: LIST OF REF SpinifexCircuit.CircuitNode _ cell.circuit.nodes, nl.rest WHILE nl # NIL DO IF nl.first.properties.GetPropFromList[ WellNodeAtom] # NIL THEN { wellConnects: LIST OF REF SpinifexCircuit.CircuitNode _ NARROW[nl.first.properties.GetPropFromList[ wellConnection]]; <> FOR wl: LIST OF REF SpinifexCircuit.CircuitNode _ wellConnects, wl.rest WHILE wl # NIL DO wl.first _ wl.first.LookupNode[]; FOR restOfWl: LIST OF REF SpinifexCircuit.CircuitNode _ wl, restOfWl.rest WHILE restOfWl.rest # NIL DO IF wl.first = restOfWl.rest.first.LookupNode[] THEN restOfWl.rest _ restOfWl.rest.rest -- Drop this list element. ENDLOOP ENDLOOP; IF wellConnects = NIL OR wellConnects.rest # NIL THEN { <> PaintWellErrorOverGeom: SpinifexCircuit.PerRectProc -- [r: REF Rectangle, data: REF ANY] -- ~ { IF r.nodeInformation = data THEN cell.PaintErrorRect[ errorBox~ r.Dimension[], message~ message] }; message: Rope.ROPE ~ IF wellConnects = NIL THEN "Floating n-well" ELSE "n-well plugged to more than one node"; cell.circuit.EnumerateGeometry[wellSpinifex, CDInline.universe, PaintWellErrorOverGeom, nl.first] } } ENDLOOP }; AttachNWellContact: PUBLIC SpinifexCircuit.BoxMapProc -- [cir: REF Circuit, dim: CD.Rect, appl: CD.ApplicationPtr, pos: CD.DesignPosition, orient: CD.Orientation, node: REF CircuitNode] RETURNS [cirNode: REF CircuitNode _ NIL] -- ~ { wellCon: REF SpinifexCircuit.CircuitNode _ cir.AddBox[ spinifexLayer~ wellSpinifex, dim~ dim, appl~appl, pos~ pos, orient~ orient, interestBloat~ [ IF node = NIL THEN ERROR; wellCon.properties _ wellCon.properties.PutPropOnList[ wellConnection, NARROW[ LIST[node], LIST OF REF SpinifexCircuit.CircuitNode] ]; }; END.