CMosSpinifexObjConvImpl.mesa
Copyright © 1984 by Xerox Corporation.  All rights reserved.
Written by Shand, September 12, 1983 11:40 pm
Last Edited by: Shand, September 4, 1984 9:03:48 pm PDT
 
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.bendList = NIL THEN { -- Oh this is easy!
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];
polExtWest: CD.Rect ~ [pdBox.x1, pdBox.y1+p.lExt, pdBox.x1+p.wExt, pdBox.y2-p.lExt];
polExtEast: CD.Rect ~ [pdBox.x2-p.wExt, 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*l]], [CMos.met, [0,0,0,0]] ];
ContactMaps[pDifShort].rm ← LIST[[CMos.pdif, [0,0,0,-4*l]], [CMos.met, [0,0,0,0]], [CMos.nwel, [ws,ws,ws,ws-4*l]] ];
ContactMaps[nbutt].rm ← LIST[[CMos.ndif, [0,-3*l,0,0]], [CMos.pol, [0,0,0,-3*l]], [CMos.met, [0,0,0,0]] ];
ContactMaps[nbutt].bm ← LIST[[polSpinifex, channelEdge, [l, -3*l, l, -2*l]], [polSpinifex, polXorDif, [l, -2*l, -4*l, -3*l]], [polSpinifex, polXorDif, [-4*l, -2*l, l, -3*l]]--, [polSpinifex, polAndDif, [0, -2*l, 0, -3*l]]-- ];  -- Causes problems with false poly spacing reports if commented BoxMap is included.
ContactMaps[pbutt].rm ← LIST[[CMos.pdif, [0,-3*l,0,0]], [CMos.pol, [0,0,0,-3*l]], [CMos.met, [0,0,0,0]], [CMos.nwel, [ws,ws-2*l,ws,ws]] ];
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 1l to the left, material above/below and to the right is determined by lExt and wExt respectively.  2l is the pivotal value, below 2l the material is Pol, at 2l or above the material is Diff.  (Note:  some combinations give Diff on all four sides, it is 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*l, cp.lExt, s.x-cp.wExt, s.y-cp.lExt], appl~appl, pos~ 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*l, 2*l, s.x-2*l, s.y-2*l], appl~ appl, pos~ pos, orient~ orient, value~ buriedNDifPol, interestBloat~ [difSep, difSep, difSep, difSep]];
SELECT 
TRUE 
FROM
cp.lExt < 2*
l 
AND cp.wExt < 2*
l => {
Polysilicon Surround
wex: CD.DesignNumber ~ MAX[cp.wExt+l, 2*l];
lex: CD.DesignNumber ~ MAX[cp.lExt+l, 2*l];
[] ← cir.AddRect[ lev~appl.ob.level, dim~[l, lex, 2*l, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l-difToPolSep, lex, 2*l, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~channelEdge];
[] ← cir.AddBox[ spinifexLayer~ndifSpinifex, dim~[2*l, lex, s.x-wex, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l, lex, s.x-wex, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~polAndDif];
};
cp.lExt < 2*
l 
AND cp.wExt >= 2*
l => {
Polysilicon/Diffusion Crossing.
lex: CD.DesignNumber ~ MAX[cp.lExt+l, 2*l];
[] ← cir.AddBox[ spinifexLayer~ndifSpinifex, dim~[2*l, lex, s.x-cp.wExt, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l, lex, s.x-cp.wExt, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~polAndDif];
Left side.
[] ← cir.AddRect[ lev~appl.ob.level, dim~[l, lex, 2*l, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l-difToPolSep, lex, 2*l, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~channelEdge];
Right side.
[] ← cir.AddRect[ lev~appl.ob.level, dim~[s.x-cp.wExt, lex, s.x-(cp.wExt-l), s.y-lex], appl~appl, pos~ pos, orient~ orient, 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*
l 
AND cp.wExt < 2*
l => {
Diffusion Surround
wex: CD.DesignNumber ~ MAX[cp.wExt+l, 2*l];
lex: CD.DesignNumber ~ cp.lExt-l;
[] ← cir.AddBox[ spinifexLayer~ndifSpinifex, dim~[2*l, cp.lExt, s.x-cp.wExt, s.y-cp.lExt], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l, cp.lExt, s.x-cp.wExt, s.y-cp.lExt], appl~appl, pos~ pos, orient~ orient, value~polAndDif];
Left side.
[] ← cir.AddRect[ lev~appl.ob.level, dim~[l, lex, 2*l, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l-difToPolSep, lex, 2*l, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~channelEdge];
Top.
[] ← cir.AddRect[ lev~appl.ob.level, dim~[2*l, s.y-cp.lExt, s.x-wex, s.y-lex], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l, s.y-cp.lExt, s.x-wex, s.y-(cp.lExt-difToPolSep)], appl~appl, pos~ pos, orient~ orient, value~channelEdge];
Bottom.
[] ← cir.AddRect[ lev~appl.ob.level, dim~[2*l, lex, s.x-wex, cp.lExt], appl~appl, pos~ pos, orient~ orient, value~node];
[] ← cir.AddBox[ spinifexLayer~polSpinifex, dim~[2*l, cp.lExt-difToPolSep, s.x-wex, cp.lExt], appl~appl, pos~ pos, orient~ orient, value~channelEdge];
};
cp.lExt >= 2*
l 
AND cp.wExt >= 2*
l => {
Diffusion Surround ! on all sides ???
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*l;
defaultL: INTEGER ~ 2*l;
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/l]];
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/l]]
};
 
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]];
Clean up list to eliminate REFs to superceded nodes.
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 {
n-well ERROR
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~ [l, l, l, l]];
IF node = NIL THEN ERROR;
wellCon.properties ← wellCon.properties.PutPropOnList[ wellConnection, NARROW[ LIST[node], LIST OF REF SpinifexCircuit.CircuitNode] ];
};
 
END.