File StaticImpl.Mesa
Last changed by McCreight, January 23, 1984 6:37 pm
Stored on [indigo]<chipndale>top>static.df
DIRECTORY
Ascii,
Atom,
Commander,
CommandTool,
FS,
IO,
Real,
RefTab,
RefText,
Rope;
StaticImpl: CEDAR MONITOR IMPORTS Ascii, Atom, Commander, CommandTool, FS, IO, Real, RefTab, RefText, Rope =
BEGIN
PURPOSE and HISTORY: This program performs some consistency and validity checks on a circuit produced by a circuit extractor from a layout of an NMOS chip. It is called STATIC since the checks are static checks on the structure and shape of the circuit rather than checks on the dynamics of the circuit that one might get from a simulator. This program was originally written by Forest Baskett and was based on an eariler similar program done by Clark M. Baker. This version is more thorough, checks more conditions, is faster, and uses dynamic storage allocation. Ed McCreight has made some other changes to reduce false error messages.
DEFINITIONS and NOTES:
An equivalence class is a collection of nodes excluding VDD and GND that would be electrically connected if all gates were on.
A node belongs to exactly one equivalence class.
A gate belongs to exactly one equivalance class.
The node controlling that gate is an input to that equivalence class. That equivalence class is an output class of the class containing the node controlling that gate.
MAXPDL: NAT = 7;
pdlIndex: TYPE = [1..MAXPDL];
NodeRef: TYPE = REF Node;
NETransRef: TYPE = REF NETrans;
NDTransRef: TYPE = REF NDTrans;
ThreshCt: TYPE = [0..15]; -- a number of enhancement thresholds negative from vdd
Node: TYPE = RECORD [
IPD: BOOLFALSE, -- there is a path of enhancement transistors from this node to ground
GATE: BOOLFALSE, -- this node contains one or more gates
INPUT: BOOLFALSE, -- this node is an off-chip input (has a lightning arrestor)
MANY: BOOLFALSE, -- appeared more than once in the input file
threshCt: ThreshCt ← LAST[ThreshCt],
minimum number of enhancement thresholds in a path to Vdd
equiv: NodeRef ← NIL, -- Link for equivalence class of nodes connected together by enhancement-mode transistors. Canonical node has equiv=NIL, others connect to him by a chain of equiv links.
igates: NETransRef ← NIL, -- List of transistors whose gates control this node
highAssertions: INTEGER ← 0,
0 means no assumptions, <0 means asserted low, >0 means asserted high. Absolute values are used for accounting.
name: ATOM
];
NETrans: TYPE = RECORD [
gate, source, drain: NodeRef,
igate: NETransRef ← NIL,
all gates controlling an equivalence class are linked through this field.
elength, ewidth, x, y: INT,
next: NETransRef
];
NDTrans: TYPE = RECORD [
gate, source: NodeRef, -- drain is vdd
dlength, dwidth, x, y: INT,
next: NDTransRef
];
eBase: NETransRef;
dBase: NDTransRef;
out: IO.STREAM;
lowLimit,highLimit: REAL; -- limits on L/W (set by user)
degradationPerThresh: REAL;
amount by which to multiply the L/W of a transistor indirectly pulled up (i.e. , driven by a passgate) (set by user)
inputThreshCt: ThreshCt; -- number of thresholds down from vdd that an input high is
nodes: RefTab.Ref; -- table
pu: NDTransRef;
pd: ARRAY pdlIndex OF NETransRef;
nNodes, ndtrans: INT;
vdd,gnd: NodeRef;
WriteCoords: PROC [x, y: INT] = {out.PutF["%d %d ", IO.int[x], IO.int[y]]};
blank: Rope.ROPE = "blank"; -- doesn't print
Remark: PROC [remark: Rope.ROPE, t, n1, n2, n3: REFNIL] =
BEGIN
IF t#NIL THEN
WITH t SELECT FROM
e: NETransRef =>
BEGIN
WriteCoords[e.x, e.y];
IF n1=NIL THEN
BEGIN
n1 ← e.gate.name;
n2 ← e.source.name;
n3 ← e.drain.name;
END;
END;
d: NDTransRef =>
BEGIN
WriteCoords[d.x, d.y];
IF n1=NIL THEN
BEGIN
n1 ← d.gate.name;
n2 ← d.source.name;
n3 ← "Vdd";
END;
END;
n: NodeRef => IF n1=NIL THEN n1 ← n.name;
ENDCASE => NULL;
out.PutRope[remark];
IF n1#NIL AND n1#blank THEN out.PutF[" %g", IO.rope[ToRope[n1]]];
IF n2#NIL THEN out.PutF[" %g", IO.rope[ToRope[n2]]];
IF n3#NIL THEN out.PutF[" %g", IO.rope[ToRope[n3]]];
out.Put[IO.char['\n]];
END;
IsSD: PROC [e: NETransRef, a: NodeRef, b: NodeRef ← NIL] RETURNS [BOOL] = {
RETURN[( SELECT a FROM
e.source => b=NIL OR b=e.drain,
e.drain => b=NIL OR b=e.source,
ENDCASE => FALSE)]};
OtherSD: PROC [e: NETransRef, a: NodeRef] RETURNS [b: NodeRef] =
{b ← SELECT a FROM
e.source => e.drain,
e.drain => e.source,
ENDCASE => NIL};
caps: REF TEXT = RefText.New[200];
Lookup: PROC [s: REF TEXT] RETURNS [n: NodeRef] =
BEGIN
atom: ATOM;
found: BOOL;
ref: REF;
caps.length ← s.length;
FOR i: NAT IN [0..s.length) DO caps[i] ← Ascii.Upper[s[i]] ENDLOOP;
atom ← Atom.MakeAtomFromRefText[caps];
[found: found, val: ref] ← nodes.Fetch[atom];
IF found THEN {n ← NARROW[ref]; n.MANYTRUE}
ELSE
BEGIN
n ← NEW[Node ← [name: atom]];
[] ← nodes.Store[key: atom, val: n];
nNodes ← nNodes+1;
END;
END;
FindCanon: PROC [n: NodeRef] RETURNS [m: NodeRef] =
BEGIN
Find the node that is the exemplar of the equivalence class of which the given node is a member, and shorten the chain.
FOR m ← n, m.equiv UNTIL m.equiv = NIL DO ENDLOOP;
FOR k: NodeRef ← n, k.equiv UNTIL k=m DO k.equiv ← m ENDLOOP;
END;
IsEquiv: PROC [m,n: NodeRef] RETURNS [BOOL] = {RETURN[FindCanon[n] = FindCanon[m]]};
MakeEquiv: PROC [m,n: NodeRef] =
BEGIN
puts two nodes into the same equivalence class by finding the exemplars of both classes, and making one the exemplar of the other's class. The ability to be pulled up (indirectly) is inherited.
k: NodeRef = FindCanon[m];
l: NodeRef = FindCanon[n];
IF k # l THEN
BEGIN
k.equiv ← l;
IF k.IPD THEN l.IPDTRUE;
END;
END;
Pulldown: PROC [n: NodeRef] = {FindCanon[n].IPDTRUE};
DoPullups: PROC [] = BEGIN -- called once in main
a, b, c: NodeRef;
w, x: NDTransRef;
y, z: NETransRef;
looks for pullups of the form:
d A VDD A (W)
pullups: INT ← 0; -- number of pullups
looks for inverting superbuffers of the form:
d B VDD B (X)
e C B GN D (Y)
e C A GN D (Z)
nisbuffers: INT ← 0;  -- number of inverting superbuffers
looks for non-inverting superbuffers of the form:
d B VDD A (W)
e B C GND (Y)
d C VDD C (X)
e C A GND (Z)
nsbuffers: INT ← 0; -- number of non-inverting superbuffers
unknowns: INT;  -- number on unrecognizable depletion mode transistors
FOR w ← dBase, w.next UNTIL w = NIL DO
a ← w.source;
b ← w.gate;
IF a.threshCt = 0 THEN Remark[remark: "Node pulled up more than once:", n1: a.name];
a.threshCt ← 0;
IF a = b THEN {pullups ← pullups+1; LOOP -- ordinary pullup -- };
FOR y ← b.igates, y.igate UNTIL y = NIL DO
IF IsSD[y , b, gnd] THEN
BEGIN
c ← y.gate;
FOR z ← a.igates, z.igate UNTIL z = NIL DO
IF z.gate = c AND IsSD[z, a, gnd] THEN
FOR x ← dBase, x.next UNTIL x = NIL DO
IF x.source = b AND x.gate = b THEN
{nisbuffers ← nisbuffers+1; GOTO quit1 -- inverting superbuffer -- };
ENDLOOP;
ENDLOOP;
END;
REPEAT quit1 => LOOP;
ENDLOOP;
FOR z ← a.igates, z.igate UNTIL z = NIL DO
IF IsSD[z, a, gnd] THEN
BEGIN
c ← z.gate;
FOR y ← c.igates, y.igate UNTIL y = NIL DO
IF y.gate = b AND IsSD[y, c, gnd] THEN
FOR x ← dBase, x.next UNTIL x = NIL DO
IF x.gate = c AND x.source = c THEN
{nsbuffers ← nsbuffers+1; GOTO quit2 -- non-inverting superbuffer -- };
ENDLOOP;
ENDLOOP;
END;
REPEAT quit2 => LOOP;
ENDLOOP;
Remark["depletion transitor not a pullup or a superbuffer: d", w];
ENDLOOP;
out.PutF["pullups and non-inverting superbuffers: %7d %7d\n", IO.int[pullups], IO.int[ nsbuffers]];
unknowns ← ndtrans - pullups - nsbuffers - nisbuffers;
out.PutF["inverting superbuffers and unknowns: %7d %7d\n", IO.int[nisbuffers], IO.int[unknowns]];
END;
tChanged: BOOL;
ComputeThresholds: PROC =
BEGIN
tChanged ← TRUE;
WHILE tChanged DO
tChanged ← FALSE;
ForEachETran[PropagateEThresh];
ENDLOOP;
END;
PropagateEThresh: PROC [e: NETransRef] =
BEGIN
IF e.source#gnd AND MAX[e.drain.threshCt, e.gate.threshCt+1] < e.source.threshCt THEN
{e.source.threshCt ←MAX[e.drain.threshCt, e.gate.threshCt+1]; tChanged ← TRUE};
IF e.drain#gnd AND MAX[e.source.threshCt, e.gate.threshCt+1] < e.drain.threshCt THEN
{e.drain.threshCt ← MAX[e.source.threshCt, e.gate.threshCt+1]; tChanged ← TRUE};
END;
FindInput: PROC [e: NETransRef] =
BEGIN
input has gate = drain|source = gnd (lightning arrester)
IF e.gate = gnd THEN
BEGIN
o: NodeRef = OtherSD[e, gnd];
IF o#NIL AND NOT o.INPUT THEN
BEGIN
Remark["Assuming lightning arrested node is an Input:", e, o.name];
o.IPD ← o.INPUTTRUE;
o.threshCt ← MIN[inputThreshCt, o.threshCt];
END;
END;
END;
CheckThreshold: PROC [e: NETransRef] =
BEGIN
IF neither the gate nor the source are directly pulled up . . .
AND the drain drives at least one gate . . .
AND the gate and the source are not connected. . .
AND the gate # gnd . . .
AND the source # gnd
AND both the drain and source of the driven transistor are not inputs (if either one is, then
this is a bootstrap)
THEN this is a pass transistor driven by another pass transistor
and it is an error.
IF e.gate.threshCt IN [2..LAST[ThreshCt]) THEN
Remark[IO.PutFR["Gate is at least %d thresholds negative from Vdd:", IO.int[e.gate.threshCt]], e];
IF e.gate.threshCt>0 AND NOT IsSD[e, gnd] AND ((e.source.threshCt>0 AND e.drain.GATE) OR
(e.drain.threshCt>0 AND e.source.GATE)) THEN
Remark["Pass transistor driven by pass transistor or input:", e];
END;
CheckRatio: PROC [d: NDTransRef] =
BEGIN
n: NodeRef ← d.source;
IF n # vdd AND n.threshCt=0 AND NOT n.INPUT THEN
{pu ← d; DoPulldowns[n,1]};
END;
DoPulldowns: PROC [n: NodeRef, depth: pdlIndex] =
BEGIN
Recursive and called once in CheckRatios. On initial entry, pu has the NDTransRef for the Pullup under consideration, and n has the NodeRef for the node pulled up by this transistor.
IF AssertLow[n] THEN
BEGIN
IF n = gnd THEN
BEGIN
out.PutF["x"];
Bottom[depth];
out.PutF["y"];
END
ELSE FOR e: NETransRef ← n.igates, e.igate UNTIL e = NIL DO
look at all the transistors that control node n
o: NodeRef = OtherSD[e, n];
IF o#NIL AND (o=gnd OR (o.highAssertions=0 AND o.threshCt#0 AND NOT o.GATE)) AND depth < MAXPDL AND AssertHigh[e.gate] THEN
The node at the other end of the transistor under consideration is a candidate for continuation of the path if
1. it is ground, or else
1a. it is not already part of the path under consideration,
1b. it is not directly pulled up, and
1c. it does not drive a gate. The reason for excluding nodes that drive gates is to keep from chasing down a path that consists of a Pullup driving a passgate multiplexer. If the examination is not stopped at the node that drives the passgate, the ratio will almost always be too high, and will produce spurious error messages (since the gates of the pass transistors almost always have mutally exclusive control signals). This will miss some valid paths, but life is hard.
2. the path is not already too long, and
3. the transistor can be turned on.
IF AssertHigh[e.gate] THEN
BEGIN
pd[depth] ← e;
DoPulldowns[o,depth+1];
RelaxHighAssertion[e.gate];
END;
ENDLOOP;
RelaxLowAssertion[n];
END;
END;
AssertHigh: PROC [ n: NodeRef ] RETURNS [ BOOL ] =
BEGIN
SELECT n.highAssertions FROM
>0 => {n.highAssertions ← n.highAssertions+1; RETURN[TRUE]};
<0 => RETURN[FALSE]; -- this node is already asserted low
ENDCASE =>
BEGIN
FOR e: NETransRef ← n.igates, e.igate UNTIL e=NIL DO
o: NodeRef ← OtherSD[e, n];
IF o=NIL OR (o.highAssertions<0 AND NOT AssertLow[e.gate]) THEN
BEGIN
Relax low assertions we made, because this high assertion is is failing.
RelaxLowGates[n: n, stop: e];
RETURN[FALSE];
END;
ENDLOOP;
n.highAssertions ← 1;
RETURN[TRUE];
END;
END;
RelaxHighAssertion: PROC [ n: NodeRef ] =
BEGIN
SELECT n.highAssertions FROM
<=0 => ERROR; -- this node is not asserted high
>1 => n.highAssertions ← n.highAssertions-1;
ENDCASE =>
BEGIN
RelaxLowGates[n];
n.highAssertions ← 0;
END;
END;
AssertLow: PROC [ n: NodeRef ] RETURNS [ BOOL ] =
BEGIN
SELECT n.highAssertions FROM
<0 => {n.highAssertions ← n.highAssertions-1; RETURN[TRUE]};
>0 => RETURN[FALSE]; -- this node is already asserted high
ENDCASE =>
BEGIN
n.highAssertions ← -1;
RETURN[TRUE];
END;
END;
RelaxLowAssertion: PROC [ n: NodeRef ] =
BEGIN
SELECT n.highAssertions FROM
>=0 => ERROR; -- this node is not asserted low
<-1 => n.highAssertions ← n.highAssertions+1;
ENDCASE =>
BEGIN
n.highAssertions ← 0;
END;
END;
RelaxLowGates: PROC [ n: NodeRef, stop: NETransRef ← NIL ] =
BEGIN
FOR e: NETransRef ← n.igates, e.igate UNTIL e=stop DO
o: NodeRef ← OtherSD[e, n];
IF o#NIL AND o.highAssertions<0 THEN RelaxLowAssertion[e.gate];
ENDLOOP;
END;
Bottom: PROC [depth: pdlIndex] =
BEGIN
Degradation: PROC [e: NETransRef] RETURNS [REAL] =
{RETURN[1.0+e.gate.threshCt*degradationPerThresh]};
i: pdlIndex;
pdZ: REAL ← 0.0;
ratio:  REAL;
FOR i← 1, i+1 UNTIL i=depth DO
pdZ ← pdZ+ (IF pd[i].ewidth = 0 THEN 1.0e10 -- avoids divide by 0
ELSE REAL[pd[i].elength] / pd[i].ewidth*Degradation[pd[i]]);
ENDLOOP;
ratio ← IF pu.dwidth*pdZ = 0 THEN 1.0e10 ELSE pu.dlength/(pu.dwidth*pdZ);
IF ratio < lowLimit OR ratio > highLimit THEN
BEGIN
n: NodeRef ← pu.source;
WriteCoords[pu.x,pu.y];
out.PutF["Up/Down Z ratio = %5.2f: Node %g is pulled up by a %dx%d (l x w), and\n",IO.real[ratio], IO.rope[ToRope[pu.source.name]], IO.int[pu.dlength], IO.int[pu.dwidth]];
FOR i𡤁, i+1 UNTIL i=depth DO
degradation: REAL = Degradation[pd[i]];
n←OtherSD[pd[i],n];
WriteCoords[pd[i].x,pd[i].y];
out.PutF["pulled down (by %g) thru a %dx%d (l x w)",
IO.rope[ToRope[pd[i].gate.name]], IO.int[pd[i].elength], IO.int[pd[i].ewidth]];
IF degradation#1.0 THEN out.PutF[" (w/ %5.2f degradation)", IO.real[degradation]];
out.PutF[" to %g%g\n",
IO.rope[ToRope[n.name]], IO.rope[IF i<depth-1 THEN ", then" ELSE "."]]
ENDLOOP;
END;
END;
ForEachNode: PROC [p: PROC [n: NodeRef]] =
BEGIN
DoNode: PROC [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN] -- RefTab.EachPairAction -- =
{p[NARROW[val]]; RETURN[FALSE]};
[] ← nodes.Pairs[DoNode];
END;
ForEachETran: PROC [p: PROC [e: NETransRef]] =
{FOR e: NETransRef ← eBase, e.next UNTIL e = NIL DO p[e] ENDLOOP};
ForEachDTran: PROC [p: PROC [d: NDTransRef]] =
{FOR d: NDTransRef ← dBase, d.next UNTIL d = NIL DO p[d] ENDLOOP};
CheckNode: PROC [n: NodeRef] =
BEGIN
m: NodeRef = FindCanon[n];
n.IPD ← m.IPD;
IF n#gnd AND n#vdd THEN
BEGIN
IF NOT n.MANY THEN Remark[remark: "Node name only occurs once:", n1: n.name];
IF NOT n.IPD THEN Remark[remark: "Node cannot be set to 0:", n1: n.name];
IF n.threshCt=LAST[ThreshCt] THEN Remark[remark: "Node cannot be set to 1:", n1: n.name];
END;
END;
CheckSourceNodeUsed: PROC [d: NDTransRef] =
BEGIN
start at each Pullup, and make sure that some node in the class drives a gate.
FOR e: NETransRef ← d.source.igates, e.igate UNTIL e=NIL DO
IF e.source.GATE OR e.drain.GATE THEN EXIT;
REPEAT
FINISHED => {Remark["Value not used:", d, d.source.name]};
ENDLOOP;
END;
ReadSimFile: PROC [in: IO.STREAM] =
BEGIN
line: REF TEXTNEW[TEXT[300]];
token: REF TEXTNEW[TEXT[300]];
lineStream: IO.STREAM;
t0, t1, t2:  NodeRef;
l, w, x, y: INT;
d: NDTransRef;
netrans, ndfunny: INT ← 0;
nNodes ← ndtrans ← 0;
DO
ScanTransistor: PROC =
BEGIN
[] ← lineStream.SkipWhitespace[];
token ← lineStream.GetToken[buffer: token].token;
t0 ← Lookup[token];
[] ← lineStream.SkipWhitespace[];
token ← lineStream.GetToken[buffer: token].token;
t1 ← Lookup[token];
[] ← lineStream.SkipWhitespace[];
token ← lineStream.GetToken[buffer: token].token;
t2 ← Lookup[token];
l ← lineStream.GetInt[]; w ← lineStream.GetInt[];
x ← lineStream.GetInt[]; y ← lineStream.GetInt[];
END;
MakeETrans: PROC [t0, t1, t2: NodeRef] =
BEGIN
e: NETransRef = NEW[NETrans ← [gate: t0, source: t1, drain: t2, elength: l, ewidth: w, x: x, y: y, next: eBase]];
eBase ← e;
IF t0 # vdd AND t0 # gnd THEN e.gate.GATETRUE;
e.igate ← NIL;
IF NOT IsSD[e, vdd] AND NOT IsSD[e, gnd] AND e.gate # gnd THEN
if neither source nor drain are vdd or gnd, put the source and drain nodes in the same equivalence class
MakeEquiv[e.source, e.drain];
IF OtherSD[e, gnd]#NIL THEN Pulldown[OtherSD[e, gnd]];
IF e.gate = gnd AND e.source # gnd AND e.drain # gnd THEN
not a lightning arrester
Remark["Gate is GND: e", e];
IF e.gate = vdd THEN Remark["Gate is VDD: e", e];
IF e.gate # gnd AND (e.gate = e.source OR e.gate = e.drain) THEN
Remark[ "gate and source or drain equality: e", e];
END;
line ← in.GetLine[ line ! IO.EndOfStream => EXIT ];
lineStream ← IO.TIS[line, lineStream];
[] ← lineStream.SkipWhitespace[];
SELECT lineStream.GetChar[ ! IO.EndOfStream => LOOP ] FROM
'e, 'E =>
BEGIN -- enhancement transistor
ScanTransistor[];
MakeETrans[t0, t1, t2];
netrans ← netrans+1;
END;
'd, 'D =>
BEGIN -- depletion transistor
ScanTransistor[];
IF t1 # vdd AND t2 # vdd THEN BEGIN -- nonstandard use of NDTrans
ndfunny ← ndfunny+1;
IF t1 = t2 AND t1 # t0 THEN BEGIN -- capacitor
WriteCoords[x,y];
out.PutF["depletion capacitor - ignored: d %g %g %g\n", IO.rope[ToRope[t0.name]], IO.rope[ToRope[t1.name]], IO.rope[ToRope[t2.name]]];
LOOP;
END;
IF (t0 = t1) OR (t0 = t2) THEN BEGIN --resistor
WriteCoords[x,y];
out.PutF["depletion resistor: d %g %g %g\n", IO.rope[ToRope[t0.name]], IO.rope[ToRope[t1.name]], IO.rope[ToRope[t2.name]]];
MakeETrans[vdd, t1,t2];
LOOP;
END;
WriteCoords[x,y];
out.PutF["yellow transistor: d %g %g %g\n", IO.rope[ToRope[t0.name]], IO.rope[ToRope[t1.name]], IO.rope[ToRope[t2.name]]];
MakeETrans[vdd, t1, t2];
LOOP;
END;
d ← NEW[NDTrans];
d.next ← dBase;
dBase ← d;
d.gate ← t0;
IF (t1 = vdd) = (t2 = vdd) THEN { WriteCoords[x,y]; out.PutF["depletion error: d %g %g %g\n", IO.rope[ToRope[t0.name]], IO.rope[ToRope[t1.name]], IO.rope[ToRope[t2.name]]]};
d.source ← (IF t1= vdd THEN t2 ELSE t1);
d.dlength ← l;
d.dwidth ← w;
d.x ← x;
d.y ← y;
ndtrans ← ndtrans+1;
IF t0 # d.source THEN t0.GATETRUE;
END;
'n, 'N => NULL; -- node characteristics
'| => NULL; -- comment character
'= => NULL;
node equivalence (extractor has already assigned a common name for all nodes)
ENDCASE => out.PutF["syntax error: %s\n", IO.text[line]];
ENDLOOP; -- .sim file reading
out.PutF["Nodes, Etrans, Dtrans, FunnyDtrans: %7d %7d %7d %7d\n", IO.int[nNodes], IO.int[netrans], IO.int[ndtrans], IO.int[ndfunny]];
END;
LinkToEquiv: PROC [e: NETransRef] =
BEGIN
n: NodeRef = FindCanon[(SELECT e.source FROM
vdd, gnd => e.drain,
ENDCASE => e.source)];
Find the canonical node of the equivalence class containing this node.
IF n = gnd THEN {Remark["funny NETrans: e", e]};
Put this transistor on the list of the gates affecting this class.
e.igate ← n.igates;
n.igates ← e;
END;
CopyCanonIGates: PROC [n: NodeRef] = {n.igates ← FindCanon[n].igates};
ToRope: PROC [ r: REF ] RETURNS [ s: Rope.ROPE ] =
BEGIN
WITH r SELECT FROM
atom: ATOM => s ← Atom.GetPName[atom];
refText: REF TEXT => s ← Rope.FromRefText[refText];
rope: Rope.ROPE => s ← rope;
ENDCASE => s ← "?";
END;
ReallyDoStatic: ENTRY PROC [cmd: Commander.Handle] =
BEGIN
Release: PROC =
BEGIN
nodes ← NIL;
eBase ← NIL;
dBase ← NIL;
IF in#NIL THEN {in.Close[]; in ← NIL};
IF out#NIL THEN {out.Close[]; out ← NIL};
END;
in: IO.STREAMNIL;
out ← NIL;
BEGIN ENABLE UNWIND => Release[];
args: CommandTool.ArgumentVector = CommandTool.Parse[cmd];
RealArg: PROC [n: NAT, default: REAL] RETURNS [REAL] =
BEGIN
RETURN[IF args.argc>n THEN IO.RIS[args[n]].GetReal[ ! IO.Error, IO.EndOfStream =>
GOTO BadArg] ELSE default];
EXITS BadArg =>
ERROR CommandTool.Failed[Rope.Cat["""", args[n], """ isn't a number"]];
END;
IF args.argc<2 THEN ERROR CommandTool.Failed["Too few arguments"];
in ← FS.StreamOpen[args[1]];
out ← FS.StreamOpen[args[2], $create];
lowLimit ← RealArg[3, 4.0];
highLimit ← RealArg[4, 10.0];
degradationPerThresh ← RealArg[5, 1.0];
inputThreshCt ← Real.Fix[RealArg[6, 1]];
out.PutF["\n%g -> %g %g\n", IO.rope[args[1]], IO.rope[args[2]], IO.time[]];
nodes ← RefTab.Create[517];
eBase ← NIL;
dBase ← NIL;
vdd← Lookup["vdd"];
gnd ← Lookup["gnd"];
gnd.IPDTRUE;
vdd.threshCt ← 0;
vdd.highAssertions ← 1;
gnd.highAssertions ← -1;
ReadSimFile[in];
ForEachETran[LinkToEquiv];
ForEachNode[CopyCanonIGates];
DoPullups[];
ComputeThresholds[];
ForEachETran[FindInput];
ForEachNode[CheckNode];
ForEachDTran[CheckSourceNodeUsed];
ForEachETran[CheckThreshold];
ForEachDTran[CheckRatio];
END;
Release[];
END;
DoStatic: Commander.CommandProc =
BEGIN
BEGIN -- for EXITS
ReallyDoStatic[cmd !
CommandTool.Failed => {cmd.err.PutF["\nStatic failed on command line because %g\n", IO.rope[errorMsg]]; GOTO Explain};
IO.Error =>
{cmd.err.PutF["\nStatic failed trying to get real number in command line\n"]; GOTO Explain};
FS.Error => IF error.group=user THEN {cmd.err.PutF["\nStatic failed trying to open a file because %g\n", IO.rope[error.explanation]]; GOTO Explain} ELSE REJECT];
EXITS
Explain => cmd.err.PutF["%g\n", IO.rope[cmd.procData.doc]];
END;
END;
Commander.Register[key: "Static", proc: DoStatic, doc: "Static analyzes whether a .sim file represents a reasonable NMOS design. Format:\n static inputfile outputfile lowUp/DownZRatio𡤄.0 highUp/DownZRatio�.0 degradationPerThresh𡤁.0 inputThreshFromVdd𡤁"];
END.