File: [Cherry]<Thyme>System>Cedar5.1>spBomb.mesa
Last Edited by: SChen, September 23, 1984 11:39:18 am PDT
Last Edited by: Ousterhout, March 5, 1985 2:39:10 pm PST
DIRECTORY
IO USING [PutF, rope],
Rope USING [Cat, Concat, Equal, Fetch, Length, ROPE],
spGlobals USING[Aborted, argNames, attPtr, Canned, conLinkPtr, error2, ErrorSignal, ErrorStrings, eval, expressionPtr, Handle, namePtr, RealNode, RealParm, realThingPtr, realThing, realThings, RefBranchRec, RefCircuitRec, RefCktInstRec, RefFunctionRec, RefNodeRec, RefParmRec, RefRealNode, RefRealParm, RefUnReal, refUnReal, ValueSeq],
ThymeParser;
spBomb:
CEDAR
PROGRAM
IMPORTS IO, Rope, spGlobals
EXPORTS spGlobals=
BEGIN OPEN spGlobals;
pushCopies:
PROC[nameList: namePtr]= {
For each namePtr linked with the nameList, initialize its realCopy as follows:
- if the namePtr points to a circuit, a circuit instance, a model or a function, its realCopy is assigned nil;
- otherwise, allocate and initialize its realCopy as following:
-- the "nextThing" of the new realCopy is the original realCopy,
-- its "newLevel" is the default (TRUE),
-- its "thing" is unReal, unless the namePtr is a parameter name pointer with default, in which case "thing" points to the default value.
The reason behind pushCopies and popCopies is that a particular circuit may be instantiated recursively. The "real copy" of each element refers to the instances that are being used at the current level. The real copy is actually a stack of copies, so that when we're finsihed expanding this instance of the circuit we can pop the stack to make the previous "real copy" current again. This pushing and popping only needs to be done for things like nodes and branches and parameters that may be different in different instances.
FOR nl: namePtr← nameList, nl.srchLink
UNTIL nl =
NIL
DO
nl.realCopy←
WITH nl.details
SELECT
FROM
x: RefCircuitRec => NIL,
x: RefCktInstRec => NIL,
x: RefFunctionRec => NIL,
p: RefParmRec =>
NEW[realThing← [
nextThing: nl.realCopy,
thing:
IF p.default
THEN
NEW[RealParm← [rp: p.dfltValue]]
ELSE refUnReal] ],
ENDCASE =>
NEW[realThing← [
nextThing: nl.realCopy,
thing: refUnReal] ];
ENDLOOP;
}; -- pushCopies
advanceLevel:
PROC[nameList: namePtr]= {
FOR nl: namePtr← nameList, nl.srchLink
UNTIL nl =
NIL
DO
IF nl.realCopy # NIL THEN nl.realCopy.newLevel← FALSE;
ENDLOOP;
}; -- advanceLevel
popCopies:
PROC[handle: Handle, nameList: namePtr]= {
Back off the latest version of "realCopies" for the elements of the given list. This restores the previous "realCopy" (if any) that was defined in the parent cell.
FOR nl: namePtr← nameList, nl.srchLink
UNTIL nl =
NIL
DO
IF nl.realCopy # NIL THEN nl.realCopy ← nl.realCopy.nextThing;
ENDLOOP;
}; -- popCopies
getParmValue:
PUBLIC
PROC[handle: Handle, nPtr: namePtr]
RETURNS[
REAL]= {
ENABLE ErrorSignal => ErrorSignal[331, s];
getRealThing[handle, nPtr];
IF handle.vars.n # NIL THEN RETURN[LOOPHOLE[handle.vars.n]] ELSE
RETURN[handle.vars.p]; -- *
}; -- getParmValue
getRealThing:
PROC[handle: Handle, nPtr: namePtr]= {
Reset n,b,m,and p; If nPtr and its realCopy is not NIL then depending on the type of its realCopy (or the non-nil nextThing of its realCopy, if its realCopy.newLevel is TRUE), set n,b,m, or p accordingly; but if its realCopy (or the nextThing, see above) is NIL and nPtr.details.default then set p with the default.
rCopy: realThingPtr;
handle.vars.n← NIL; handle.vars.p← 0.0;
IF nPtr=NIL THEN RETURN;
rCopy← nPtr.realCopy;
IF rCopy # NIL THEN IF rCopy.newLevel THEN rCopy← rCopy.nextThing;
IF rCopy #
NIL
THEN
WITH rCopy.thing
SELECT
FROM
rt: RefRealNode => handle.vars.n← rt.rn;
rt: RefRealParm => handle.vars.p← rt.rp;
rt: RefUnReal => ErrorSignal[330, nPtr.name];
ENDCASE
ELSE
WITH nPtr.details
SELECT
FROM
pn: RefParmRec =>
IF pn.default THEN handle.vars.p← pn.dfltValue ELSE ErrorSignal[331, nPtr.name];
ENDCASE => error2[handle, 390, nPtr];
}; -- getRealThing
putRealThing:
PROC[handle: Handle, nPtr: namePtr, t: realThings]= {
Depending on the type of t, initialize nPtr.realCopy.thing to point to (the content of) n, b, p, or m.
rCopy: realThingPtr← nPtr.realCopy;
IF rCopy #
NIL
THEN
SELECT t
FROM
realNode => rCopy.thing← NEW[RealNode← [handle.vars.n]];
realParm => rCopy.thing← NEW[RealParm← [handle.vars.p]];
ENDCASE;
}; -- putRealThing
putParmValue:
PUBLIC
PROC[handle: Handle, nPtr: namePtr, val:
REAL]= {
handle.vars.p← val;
putRealThing[handle, nPtr, realParm];
}; -- putParmValue
makeConnections:
PROC[handle: Handle, connections: conLinkPtr, fakes: namePtr]= {
This procedure is called to bind formal parameters to actual parameters. The formal parameters for a circuit are stored in its fakeNodes list, whereas the actual parameters are contained in the connections list.
UNTIL fakes=
NIL
DO
getRealThing[handle, connections.namedNode];
putRealThing[handle, fakes, realNode];
fakes← fakes.nextName;
connections← connections.nextLink;
ENDLOOP;
}; -- makeConnections
apply:
PROC[handle: Handle, apList: expressionPtr]= {
FOR ex: expressionPtr ← apList, ex.next
UNTIL ex=
NIL
DO
[]← eval[handle, ex];
ENDLOOP;
}; -- apply
explodeInstance:
PROC[handle: Handle, instNamePtr: namePtr] = {
inst: RefCktInstRec← NARROW[instNamePtr.details];
ckt: namePtr← NARROW[inst.of];
cktDetails: RefCircuitRec← NARROW[ckt.details];
oldPath: Rope.ROPE;
type: ThymeParser.TypeList;
In order to use this code for Crystal, keep around a path name of cell instances traversed. This procedure updates the path name for use in exploding this instance, then restores the old value when it's done.
oldPath ← handle.vars.pathName;
handle.vars.pathName ← Rope.Cat[oldPath, instNamePtr.name, "/"];
pushCopies[cktDetails.names];
makeConnections[handle, inst.connections, cktDetails.fakeNodes];
apply[handle, inst.actualParms];
advanceLevel[cktDetails.names];
FOR exp: expressionPtr ← cktDetails.assertions, exp.next
UNTIL exp=
NIL
DO
IF eval[handle, exp]=0.0 THEN error2[handle, 341, instNamePtr];
ENDLOOP;
Before exploding the circuit, see if it's a transistor. If it is, then don't bother to explode it in the normal way. Process transistors specially.
FOR type ← handle.vars.fetTable, type.next
UNTIL type =
NIL
DO
IF Rope.Equal[type.name, ckt.name]
THEN {
explodeFet[handle, inst, cktDetails, type.clientData];
EXIT;
};
ENDLOOP;
IF type = NIL THEN explode[handle, ckt];
popCopies[handle, cktDetails.names];
handle.vars.pathName ← oldPath;
}; -- explodeInstance
explodeBranch:
PROC[handle: Handle, branchNamePtr: namePtr] = {
pNode, nNode: Rope.ROPE;
value: REAL;
brDetails: RefBranchRec← NARROW[branchNamePtr.details];
getRealThing[handle, brDetails.posNode];
pNode← handle.vars.n;
getRealThing[handle, brDetails.negNode];
nNode← handle.vars.n;
value ← eval[handle, brDetails.valExpr];
IF brDetails.controller #
NIL
THEN {
IO.PutF[handle.msgStream, "Ignoring branch %s%s (in %s): it's controlled by a function (%s).\n",
IO.rope[handle.vars.pathName], IO.rope[branchNamePtr.name],
IO.rope[handle.vars.curCkt.name], IO.rope[brDetails.controller.name]];
RETURN;
};
SELECT brDetails.branchType
FROM
resistor => {
IF handle.vars.resProc #
NIL
THEN
handle.vars.resProc[nodeA: pNode, nodeB: nNode, ohms: value,
clientData: handle.vars.clientData];
handle.vars.resCount ← handle.vars.resCount + 1;
};
capacitor=> {
IF handle.vars.capProc #
NIL
THEN
handle.vars.capProc[nodeA: pNode, nodeB: nNode, pfs: value*1.0e12,
clientData: handle.vars.clientData];
handle.vars.capCount ← handle.vars.capCount + 1;
};
inductor => {
IO.PutF[handle.msgStream, "Ignoring inductor %s%s (in %s).\n",
IO.rope[handle.vars.pathName], IO.rope[branchNamePtr.name],
IO.rope[handle.vars.curCkt.name]];
};
vSource => {
IO.PutF[handle.msgStream, "Ignoring voltage source %s% (in %s).\n",
IO.rope[handle.vars.pathName], IO.rope[branchNamePtr.name],
IO.rope[handle.vars.curCkt.name]];
};
iSource => {
IO.PutF[handle.msgStream, "Ignoring current source %s%s (in %s).\n",
IO.rope[handle.vars.pathName], IO.rope[branchNamePtr.name],
IO.rope[handle.vars.curCkt.name]];
};
ENDCASE => error2[handle, 391, branchNamePtr];
}; -- explodeBranch
findName:
PROC[list: namePtr, name: Rope.
ROPE]
RETURNS [namePtr] = {
This procedure is used to look up the node that is associated with the parameter
named "name" in circuit "ckt". It's used to find things like transistor gates.
NIL is returned if the name didn't match anything.
WHILE list #
NIL
DO
IF Rope.Equal[list.name, name] THEN RETURN [list];
list ← list.nextName;
ENDLOOP;
RETURN [NIL];
}; -- findName
findParm:
PROC[handle:Handle, list: namePtr, name: Rope.
ROPE]
RETURNS [REAL] = {
This procedure is used to look up the value of the parameter named "name" in circuit "ckt". It's used to find things like transistor lengths. 0.0 is returned if the name didn't match anything.
WHILE list #
NIL
DO
IF Rope.Equal[list.name, name] THEN RETURN [getParmValue[handle, list]];
list ← (NARROW[list.details, RefParmRec]).nextParm;
ENDLOOP;
RETURN [0.0];
}; -- findParm
explodeFet:
PROC[handle: Handle, inst: RefCktInstRec, ckt: RefCircuitRec, clientData: REF ANY] = {
This procedure examines a transistor that corresponds to a transistor, reads out the terminals and parameters from the circuit structure, and generates a Crystal transistor for it.
source, drain, gate: Rope.ROPE;
l, w, x, y: REAL;
fetHandle: REF ANY ← NIL;
l ← findParm[handle, ckt.parms, "l"];
w ← findParm[handle, ckt.parms, "w"];
x ← findParm[handle, ckt.parms, "x"];
y ← findParm[handle, ckt.parms, "y"];
getRealThing[handle, findName[ckt.fakeNodes, "source"]];
source ← handle.vars.n;
getRealThing[handle, findName[ckt.fakeNodes, "drain"]];
drain ← handle.vars.n;
getRealThing[handle, findName[ckt.fakeNodes, "gate"]];
gate ← handle.vars.n;
IF handle.vars.fetProc #
NIL
THEN
fetHandle ← handle.vars.fetProc[gate, source, drain, clientData, l, w, x, y, handle.vars.clientData];
handle.vars.fetCount ← handle.vars.fetCount + 1;
IF handle.vars.attProc #
NIL
THEN {
FOR att: attPtr ← inst.attributes, att.next
UNTIL att =
NIL
DO
handle.vars.attProc[fetHandle, att.name, att.value, handle.vars.clientData];
ENDLOOP;
};
RETURN;
explode:
PROC[handle: Handle, ckt: namePtr]= {
curName: namePtr;
cktDetails: RefCircuitRec← NARROW[ckt.details];
prevCkt: namePtr ← handle.vars.curCkt;
IF Canned[handle] THEN SIGNAL Aborted;
Read through the circuit and build up node data structures.
handle.vars.curCkt ← ckt;
FOR curName← cktDetails.names, curName.srchLink
UNTIL curName=
NIL
DO
WITH curName.details
SELECT
FROM
x: RefNodeRec => {
handle.vars.n← Rope.Cat[handle.vars.pathName, curName.name];
putRealThing[handle, curName, realNode];
IF handle.vars.nodeProc #
NIL
THEN
handle.vars.nodeProc[handle.vars.n, handle.vars.clientData];
handle.vars.nodeCount ← handle.vars.nodeCount + 1;
};
ENDCASE;
ENDLOOP;
FOR curName← cktDetails.names, curName.srchLink
UNTIL curName=
NIL
DO
ENABLE ErrorSignal => {
ErrorStrings[handle, error, Rope.Concat[handle.vars.pathName, curName.name], s];
LOOP;
};
WITH curName.details
SELECT
FROM
x: RefBranchRec => {
explodeBranch[handle, curName];
};
x: RefCktInstRec => {
explodeInstance[handle, curName];
};
ENDCASE;
ENDLOOP;
handle.vars.curCkt ← prevCkt;
}; -- explode
AlphaNumeric:
PROC[name: Rope.
ROPE]
RETURNS[an:
BOOL←
FALSE]= {
tChar: CHAR← Rope.Fetch[name, 0];
an← tChar IN ['a..'z] OR tChar IN ['A..'Z]; -- first char should not be numeric
IF an
THEN
FOR i:
INT
IN [1..Rope.Length[name])
DO
tChar← Rope.Fetch[name, i];
an← tChar IN ['a..'z] OR tChar IN ['A..'Z] OR tChar IN ['0..'9];
IF ~an THEN RETURN;
ENDLOOP;
}; -- AlphaNumeric
bomb:
PUBLIC
PROC[handle: Handle]= {
This is the top-level procedure for expanding a hierarchical description into a flattened version.
cktRootDetails: RefCircuitRec← NARROW[handle.vars.cktRoot.details];
pushCopies[cktRootDetails.names];
advanceLevel[cktRootDetails.names];
explode[handle, handle.vars.cktRoot];
popCopies[handle, cktRootDetails.names];
}; -- bomb
END.
CHANGE LOG
Wilhelm, March 16, 1982 9:41 AM
Barth, 7-May-82 10:48:59 PDT
Chen, February 12, 1984 7:50 PM, modified to support oldArgVector.
Chen, June 10, 1984 9:47:59 pm PDT, cedarized.