-- ChipSimFile.mesa
-- Subroutines to analyze the tree of cell calls and structure
-- of nets constructed by the net extraction.
-- last modified by E. McCreight, December 21, 1983 10:19 AM
-- last modified by R. Barth, July 8, 1983 12:11 PM
-- written by E. McCreight, November 3, 1981 3:06 PM
DIRECTORY
Ascii,
ChipNetDefs,
ChipUserInt,
InlineDefs,
ppdddefs,
ppdefs,
ppMainDefs,
StreamDefs,
StringDefs;
ChipSimFile: PROGRAM
IMPORTS ChipNetDefs, ChipUserInt,
InlineDefs, ppdddefs, ppdefs, ppMainDefs,
StreamDefs, StringDefs
EXPORTS ChipNetDefs =
BEGIN OPEN StringDefs, StreamDefs, ppdefs, ChipUserInt,
ChipNetDefs;
HashIndex: TYPE = INTEGER[0..253);
XstrHashTable: TYPE = ARRAY HashIndex OF XstrCallPtr;
XstrHashPtr: TYPE = LONG POINTER TO XstrHashTable;
simFile, thyFile: DiskHandle;
simInCrystalFormat: BOOLEAN ← FALSE;
showHier, showEquivalences: BOOLEAN;
h: XstrHashPtr;
transNo: LONG CARDINAL;
WriteSimFile: PUBLIC PROCEDURE[c: CellCallPtr] =
BEGIN
ShouldMakeFile: PROCEDURE[name: STRING]
RETURNS[BOOLEAN] =
BEGIN
question: STRING ← [100];
question.length ← 0;
AppendString[to: question, from: "Make "];
AppendString[to: question, from: name];
AppendString[to: question, from: "?"];
RETURN[HeSetsParamsAndSaysYes[question]];
END;
s: STRING ← [300];
name: STRING;
h ← uz.NEW[XstrHashTable];
name ← RequestString["Output file name?"L];
IF name=NIL OR name.length=0 THEN
name ← newString[ppMainDefs.fileName];
IF ShouldMakeFile[name ←
FixExtension[name, ".sim"L]]
THEN
BEGIN
simFile ← NewByteStream[name, WriteAppend];
simInCrystalFormat ← HeSaysYes["Want it in Crystal format?"];
END
ELSE simFile ← NIL;
thyFile ← IF ShouldMakeFile[name ←
FixExtension[name, ".thy"L]] THEN
NewByteStream[name, WriteAppend] ELSE NIL;
showHier ← HeSetsParamsAndSaysYes["Show hierarchy?"L];
showEquivalences ← simFile#NIL AND showHier AND
HeSetsParamsAndSaysYes["Show terminal equivalences?"L];
IF simFile#NIL THEN
BEGIN
HashTransistors[c];
IF simInCrystalFormat THEN
BEGIN
s.length ← 0;
AppendString[s, "| units: "];
AppendCoord[s, ppdddefs.pCifScale/Lambda];
SaySimLine[s];
END;
s.length ← 0;
WriteCellInst[s, c, 0, SimFileCellInst];
TruncateDiskStream[simFile];
END;
IF thyFile#NIL THEN
BEGIN -- introductory boiler plate
pCifScalesPerLambda: INTEGER = 100;
-- pCifScale is in centimicrons per lambda
s.length ← 0;
SayThyLine[""];
SayThyLine["-- N o d e s"L];
SayThyLine[""];
WriteThyNodes[s, c, 0];
HashTransistors[c];
transNo ← 0;
SayThyLine[""];
SayThyLine[""];
SayThyLine["-- T r a n s i s t o r s"L];
SayThyLine[""];
WriteCellInst[s, c, 0, ThyFileCellInst];
SayThyLine[""];
TruncateDiskStream[thyFile];
END;
FreeString[name];
uz.FREE[@h];
END; -- of WriteSimFile
WriteCellInst: PROCEDURE[s: STRING, call: CellCallPtr,
depth: CARDINAL,
HandleCell: PROCEDURE[s: STRING, call: CellCallPtr,
depth: CARDINAL]] =
BEGIN
IF depth>0 THEN HashTransistors[call];
HandleCell[s, call, depth];
FOR c: InstancePtr ← call.offspring, c.sibling
WHILE c#NIL DO
WITH dc: c SELECT FROM
cell =>
WriteCellInst[s, @dc,
depth+(IF showHier THEN 1 ELSE 0), HandleCell];
ENDCASE => NULL;
ENDLOOP;
END; -- of WriteCellInst
SimFileCellInst: PROCEDURE[s: STRING, call: CellCallPtr,
depth: CARDINAL] =
BEGIN
c: InstancePtr;
FOR c ← call.offspring, c.sibling WHILE c#NIL DO
WITH dc: c SELECT FROM
xstr => EXIT;
ENDCASE => NULL;
ENDLOOP;
IF depth>0 AND (c#NIL OR call.nets#NIL OR call.clusters#NIL)
THEN
BEGIN
SaySimLine[""];
Indent[s, depth];
AppendString[s, "| "];
AppendCallerChain[s, call];
SaySimLine[s];
END;
FOR c ← c, c.sibling WHILE c#NIL DO
WITH dc: c SELECT FROM
xstr => {Indent[s, depth]; WriteSimTransistor[s, c]};
ENDCASE => NULL;
ENDLOOP;
FOR netId: NetIdPtr ← call.nets, netId.next WHILE netId#NIL DO
WITH normNetId: netId SELECT FROM
normal =>
{Indent[s, depth]; WriteSimNetLine[s, @normNetId]};
ENDCASE => NULL;
ENDLOOP;
FOR cluster: ClusterPtr ← call.clusters, cluster.next
WHILE cluster#NIL DO
id: NormalNetIdPtr ← GetNormalNetId[@cluster.first.net];
idMakesItemRef: BOOLEAN;
itemRef: ItemRef;
-- print a net = line if it is not an identity
WITH did: id SELECT FROM
qualified =>
BEGIN
idMakesItemRef ← TRUE;
itemRef ← [did.source, did.see];
END;
ENDCASE => idMakesItemRef ← FALSE;
IF showEquivalences AND (NOT idMakesItemRef OR
itemRef#cluster.first.source) THEN
BEGIN
Indent[s, depth];
AppendString[s, "= "];
cluster.first.net ← AppendNet[s, cluster.first.net];
Space[s];
AppendTerminalName[s, cluster.first.source];
SaySimLine[s];
END;
ENDLOOP;
END; -- of SimFileCellInst
ThyFileCellInst: PROCEDURE[s: STRING, call: CellCallPtr,
depth: CARDINAL] =
BEGIN
c: InstancePtr;
FOR c ← call.offspring, c.sibling WHILE c#NIL DO
WITH dc: c SELECT FROM
xstr => EXIT;
ENDCASE => NULL;
ENDLOOP;
IF depth>0 AND c#NIL THEN
BEGIN
SayThyLine[""];
Indent[s, depth+2];
AppendString[s, "-- "];
AppendCallerChain[s, call];
SayThyLine[s];
END;
FOR c ← c, c.sibling WHILE c#NIL DO
WITH dc: c SELECT FROM
xstr => {Indent[s, depth+2]; WriteThyTransistor[s, c]};
ENDCASE => NULL;
ENDLOOP;
END; -- of ThyFileCellInst
WriteThyNodes: PROCEDURE[s: STRING, call: CellCallPtr,
depth: CARDINAL] =
BEGIN
IF depth>0 AND call.nets#NIL THEN
BEGIN
SayThyLine[""];
Indent[s, depth+2];
AppendString[s, "-- "];
AppendCallerChain[s, call];
SayThyLine[s];
END;
FOR netId: NetIdPtr ← call.nets, netId.next WHILE netId#NIL DO
WITH normNetId: netId SELECT FROM
normal =>
{Indent[s, depth+2]; WriteThyNetLine[s, @normNetId]};
ENDCASE => NULL;
ENDLOOP;
FOR c: InstancePtr ← call.offspring, c.sibling
WHILE c#NIL DO
WITH dc: c SELECT FROM
cell => WriteThyNodes[s, @dc,
depth+(IF showHier THEN 1 ELSE 0)];
ENDCASE => NULL;
ENDLOOP;
END; -- of WriteThyNodes
HashTransistors: PROCEDURE[c: CellCallPtr] =
BEGIN
HashLevel: PROCEDURE[c: CellCallPtr] =
BEGIN
FOR instance: InstancePtr ← c.offspring, instance.sibling
WHILE instance#NIL DO
WITH dinst: instance SELECT FROM
cell => IF NOT showHier THEN HashLevel[@dinst];
xstr =>
BEGIN
i: HashIndex;
dinst.inSimFile ← FALSE;
FOR t: XstrTerminals IN XstrTerminals DO
dinst.map[t] ← CanonNet[dinst.map[t]];
ENDLOOP;
i ← XstrHashFn[@dinst];
dinst.sameHash ← h[i];
h[i] ← @dinst;
END;
ENDCASE => NULL;
ENDLOOP;
END;
FOR i: HashIndex IN HashIndex DO
h[i] ← NIL;
ENDLOOP;
HashLevel[c];
END; -- of HashTransistors
CollectSimilarTransistors: PROCEDURE[model, candidates:
XstrCallPtr, modelOb: LONG POINTER TO xstr object]
RETURNS[locNum] =
BEGIN
width: CARDINAL ← 0; -- in 10ths of a Lambda
FOR cand: XstrCallPtr ← candidates, cand.sameHash
WHILE cand#NIL DO
IF cand.map[gate]=model.map[gate] AND
((cand.map[source]=model.map[source] AND
cand.map[drain]=model.map[drain]) OR
(cand.map[source]=model.map[drain] AND
cand.map[drain]=model.map[source])) THEN
BEGIN
WITH candOb: cand.proto.ob SELECT FROM
xstr =>
BEGIN
IF modelOb.length=candOb.length AND
modelOb.impl=candOb.impl AND
modelOb.l=candOb.l THEN
BEGIN
width ← width+(candOb.width*10)/Lambda;
cand.inSimFile ← TRUE;
END;
END;
ENDCASE;
END;
ENDLOOP;
RETURN[(Lambda*width)/10];
END;
XstrHashFn: PROCEDURE[xstr: XstrCallPtr]
RETURNS[HashIndex] =
BEGIN
RETURN[InlineDefs.LowHalf[
(LOOPHOLE[xstr.map[gate], LONG CARDINAL]+
LOOPHOLE[xstr.map[source], LONG CARDINAL]+
LOOPHOLE[xstr.map[drain], LONG CARDINAL]) MOD
(LAST[HashIndex]+1)]];
END;
WriteSimTransistor: PROCEDURE[s: STRING, t: InstancePtr] =
BEGIN
WITH trans: t SELECT FROM
xstr => IF NOT trans.inSimFile THEN
BEGIN
WITH tob: t.proto.ob SELECT FROM
xstr =>
BEGIN
p: Point;
AppendChar[s, IF tob.l=pdif -- should know whether CMOS or NMOS here
THEN (IF simInCrystalFormat THEN 'p ELSE 'c)
ELSE (SELECT tob.impl FROM
enhancement => 'e,
strongDepletion => 'd,
zeroThresh => 'z,
ENDCASE => 'x)];
FOR term: XstrTerminals IN XstrTerminals DO
Space[s];
AppendNetId[s, GetNormalNetId[@trans.map[term]]];
ENDLOOP;
AppendLocNum[s, tob.length];
AppendLocNum[s,
CollectSimilarTransistors[@trans,
h[XstrHashFn[@trans]], @tob] -- width --];
p ← RefCoordPt[ItemInWorld[t.caller]];
AppendCoord[s, p.x/Lambda];
AppendCoord[s, p.y/Lambda];
IF simInCrystalFormat THEN FOR term: XstrTerminals IN XstrTerminals DO
attr: Atom = SELECT term FROM
gate => crystalG,
source => crystalS,
ENDCASE -- drain -- => crystalD;
value: Atom = FindPropValue[lp: ItemRefToLp[trans.caller], attribute: attr];
IF value#NIL THEN
BEGIN
Space[s];
AppendString[s, SELECT term FROM
gate => "g=Cr:",
source => "s=Cr:",
ENDCASE -- drain -- => "d=Cr:"];
AppendLongString[s, AtomToString[value]];
END;
ENDLOOP;
SaySimLine[s];
END;
ENDCASE => NULL;
END;
ENDCASE => NULL
END; -- of WriteSimTransistor
crystalS: Atom = MakeAtom["Crystal-S"];
crystalG: Atom = MakeAtom["Crystal-G"];
crystalD: Atom = MakeAtom["Crystal-D"];
WriteThyTransistor: PROCEDURE[s: STRING, t: InstancePtr] =
BEGIN
WITH trans: t SELECT FROM
xstr => IF NOT trans.inSimFile THEN
BEGIN
WITH tob: t.proto.ob SELECT FROM
xstr =>
BEGIN
defaultL: ARRAY DepletionStrength OF locNum =
[ -- enhancement -- 2*Lambda, -- 0-thresh -- 2*Lambda,
-- wk depl -- 4*Lambda, -- strong depl -- 4*Lambda];
defaultW: ARRAY DepletionStrength OF locNum =
[-- enhancement -- 4*Lambda, -- 0-thresh -- 4*Lambda,
-- wk depl -- 2*Lambda, -- strong depl -- 2*Lambda];
first: BOOLEAN;
AppendChar[s, 'Q];
AppendCard[s, transNo ← transNo+1];
AppendChar[s, ':];
AppendChar[s, IF tob.l=pdif -- should know whether CMOS or NMOS here
THEN 'C
ELSE (SELECT tob.impl FROM
enhancement => 'E,
strongDepletion => 'D,
zeroThresh => 'Z,
ENDCASE => 'X)];
AppendString[s, "Tran["];
FOR term: XstrTerminals IN XstrTerminals DO
IF term#FIRST[XstrTerminals] THEN
AppendChar[s, ',];
AppendThyNetId[s, GetNormalNetId[@trans.map[term]]];
ENDLOOP;
first ← AppendThyDist[s, tob.length, "L",
defaultL[tob.impl], TRUE];
[] ← AppendThyDist[s, CollectSimilarTransistors[@trans,
h[XstrHashFn[@trans]], @tob] -- width --, "W",
defaultW[tob.impl], first];
AppendString[s, "];"];
SayThyLine[s];
END;
ENDCASE => NULL;
END;
ENDCASE => NULL
END; -- of WriteThyTransistor
WriteSimNetLine: PROCEDURE[s: STRING, id: NormalNetIdPtr] =
BEGIN
AppendString[s, "N "];
AppendNetId[s, id];
IF simInCrystalFormat THEN
BEGIN
AppendLayerArea[s, id.caps[ndif].area+id.caps[pdif].area];
AppendLayerPerim[s, id.caps[ndif].perimeter+id.caps[pdif].perimeter];
AppendLayerArea[s, id.caps[poly].area];
AppendLayerPerim[s, id.caps[poly].perimeter];
AppendLayerArea[s, id.caps[metal].area+id.caps[metal2].area];
AppendLayerPerim[s, id.caps[metal].perimeter+id.caps[metal2].perimeter];
END
ELSE
BEGIN
AppendLayerArea[s, id.caps[metal].area];
AppendLayerArea[s, id.caps[poly].area];
AppendLayerArea[s, id.caps[ndif].area+id.caps[pdif].area];
AppendLayerPerim[s, id.caps[ndif].perimeter+id.caps[pdif].perimeter];
AppendLayerArea[s, id.caps[metal2].area];
END;
SaySimLine[s];
END;
WriteThyNetLine: PROCEDURE[s: STRING, id: NormalNetIdPtr] =
BEGIN
nodeName: STRING ← [100];
first: BOOLEAN;
nodeName.length ← 0;
AppendThyNetId[nodeName, id];
SELECT TRUE FROM
EquivalentString[nodeName, "Gnd"],
EquivalentString[nodeName, "Vdd"] =>
AppendString[s, "-- "]; -- not allowed to describe these
ENDCASE => NULL;
AppendString[s, nodeName];
AppendString[s, ":node; ?:Stray["];
AppendString[s, nodeName];
first ← AppendThyLayerArea[s, id.caps[metal2].area, "aM2", TRUE];
first ← AppendThyLayerArea[s, id.caps[metal].area, "aM", first];
first ← AppendThyLayerArea[s, id.caps[poly].area, "aP", first];
first ← AppendThyLayerArea[s, id.caps[ndif].area, "anD", first];
first ← AppendThyDist[s, id.caps[ndif].perimeter,
"pnD", 0, first];
first ← AppendThyLayerArea[s, id.caps[pdif].area, "apD", first];
[] ← AppendThyDist[s, id.caps[pdif].perimeter,
"ppD", 0, first];
AppendString[s, "];"];
SayThyLine[s];
END;
AppendThyNetId: PROCEDURE[s: STRING, id: NormalNetIdPtr] =
BEGIN
name: STRING ← [100];
name.length ← 0;
AppendNetId[name, id];
SELECT TRUE FROM
EquivalentString[name, "Gnd"] =>
AppendString[s, "Gnd"];
EquivalentString[name, "Vdd"] =>
AppendString[s, "Vdd"];
ENDCASE =>
FOR i: CARDINAL IN [0..name.length) DO
SELECT name[i] FROM
IN ['0..'9], IN ['a..'z], IN ['A..'Z] => NULL;
ENDCASE => GOTO HasSpecialChar;
REPEAT
HasSpecialChar =>
BEGIN
AppendChar[s, '$];
AppendString[s, name];
AppendChar[s, '$];
END;
FINISHED => AppendString[s, name];
ENDLOOP;
END;
AppendLayerArea: PROCEDURE[s: STRING, area: LONG CARDINAL -- square locNum's --] =
BEGIN
Space[s];
AppendCard[s, area/(Lambda*Lambda)];
END;
AppendLayerPerim: PROCEDURE[s: STRING, perimeter: LONG CARDINAL -- locNum's --] =
BEGIN
Space[s];
AppendCard[s, perimeter/Lambda];
END;
AppendThyLayerArea: PROCEDURE[s: STRING, area: LONG CARDINAL -- square locNum's --,
layer: STRING, first: BOOLEAN] RETURNS[BOOLEAN] =
BEGIN
IF area>0 THEN
BEGIN
AppendChar[s, IF first THEN '| ELSE ',];
AppendString[s, layer];
AppendString[s, "←"];
AppendCard[s, area/(Lambda*Lambda)];
RETURN[FALSE];
END;
RETURN[first];
END;
AppendThyDist: PROCEDURE[s: STRING, dist: LONG CARDINAL -- locNum's --,
layer: STRING, default: CARDINAL -- locNum's --, first: BOOLEAN]
RETURNS[BOOLEAN] =
BEGIN
IF dist#default THEN
BEGIN
AppendChar[s, IF first THEN '| ELSE ',];
AppendString[s, layer];
AppendString[s, "←"];
AppendCard[s, dist/Lambda];
RETURN[FALSE];
END;
RETURN[first];
END;
Space: PROCEDURE[s: STRING] = INLINE
{AppendChar[s, ' ]};
Indent: PROCEDURE[s: STRING, depth: CARDINAL] =
BEGIN
FOR i: CARDINAL IN [0..2*depth) DO s[i] ← ' ENDLOOP;
s.length ← 2*depth;
END;
SaySimLine: PROCEDURE[s: STRING] =
BEGIN
IF simFile#NIL THEN
BEGIN
FOR i: CARDINAL IN [0..s.length) DO
simFile.put[simFile, s[i]] ENDLOOP;
simFile.put[simFile, Ascii.CR];
END;
END;
SayThyLine: PROCEDURE[s: STRING] =
BEGIN
IF thyFile#NIL THEN
BEGIN
FOR i: CARDINAL IN [0..s.length) DO
thyFile.put[thyFile, s[i]] ENDLOOP;
thyFile.put[thyFile, Ascii.CR];
END;
END;
END. -- of ChipSimFile