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: BOOL ← FALSE, -- there is a path of enhancement transistors from this node to ground
GATE: BOOL ← FALSE, -- this node contains one or more gates
INPUT: BOOL ← FALSE, -- this node is an off-chip input (has a lightning arrestor)
MANY: BOOL ← FALSE, -- 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:
REF ←
NIL] =
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.MANY ← TRUE}
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.IPD ← TRUE;
END;
END;
Pulldown: PROC [n: NodeRef] = {FindCanon[n].IPD ← TRUE};
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.INPUT ← TRUE;
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 TEXT ← NEW[TEXT[300]];
token: REF TEXT ← NEW[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.GATE ← TRUE;
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.GATE ← TRUE;
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.STREAM ← NIL;
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.IPD ← TRUE;
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.