-- 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