-- File StaticImpl.Mesa
-- October 1980 by Forest Baskett
-- Last changed: April 9, 1981 10:26 AM

DIRECTORY IODefs,Time,StreamDefs,StringDefs,SystemDefs,
SegmentDefs,Real,CWF,CWFReal,StaticDefs;
StaticImpl: PROGRAM IMPORTS IODefs,Time,StringDefs,SegmentDefs,Real,CWF,
CWFReal
EXPORTS StaticDefs = BEGIN OPEN io:IODefs,seg:SegmentDefs,StaticDefs;

--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
--
written by Forest Baskett and was based on an eariler similar
--
program done by Clark M. Baker. This version is more
--
horough, checks more conditions, is faster, and uses dynamic
--
storage allocation.

--DEFINITIONS and NOTES:
--
A subcircuit 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 subcircuit.
--
A gate belongs to exactly one subcircuit.
--
The node controlling that gate is an input to that
--
subcircuit.
--
That subcircuit is an output subcircuit of the subcircuit
--
containing the node controlling that gate.

MAXIDLENGTH:CARDINAL = 100;
HASHSIZE:
CARDINAL = 731; --May be small for big circuits
MAXPDL:
CARDINAL = 7;

nameIndex:
TYPE = [0..MAXIDLENGTH);
hashIndex:
TYPE = [0..HASHSIZE);
pdlIndex:
TYPE = [1..MAXPDL];

nodeLink:
TYPE = LONG POINTER TO node;
eTransLink:
TYPE = LONG POINTER TO eTrans;
dTransLink:
TYPE = LONG POINTER TO dTrans;
blockLink:
TYPE = LONG POINTER TO block;

node:
PUBLIC TYPE = RECORD
[PU,PD,IPU,GATE,MARK,INPUT,MANY: BOOLEAN,
next:nodeLink, --next node
equiv:nodeLink, --equivalence class
igates:eTransLink, --input gates
name:PACKED ARRAY [0..0) OF CHARACTER];

eTrans:
TYPE = RECORD [gate, source, drain:nodeLink,
igate:eTransLink, --next input gate
elength,ewidth:LONG CARDINAL,
xCoord, yCoord: LONG INTEGER,
next:eTransLink];

dTrans:
TYPE = RECORD [gate, source:nodeLink,
dlength,dwidth:LONG CARDINAL,
xCoord, yCoord: LONG INTEGER,
next:dTransLink];

block:
TYPE = RECORD [next:blockLink,
data: ARRAY[0..254] OF CARDINAL];
--Environment.wordsPerPage-SIZE[CARDINAL]

eBase:
eTransLink;
dBase:
dTransLink;
blockPointer:
blockLink ← NIL;
blockCount:
CARDINAL ← 254;

in:
StreamDefs.StreamHandle;
out1:
StreamDefs.StreamHandle;
out2:
StreamDefs.StreamHandle;
line:
STRING ← [MAXIDLENGTH];
lineIn:
STRING ← [MAXIDLENGTH];
lineOut1:
STRING ← [MAXIDLENGTH];
lineOut2:
STRING ← [MAXIDLENGTH];
eol:
BOOLEAN;
lowLimit:
REAL;
highLimit:
REAL;

hash:
ARRAY hashIndex OF nodeLink;
pd0:
dTransLink;
pd:
ARRAY pdlIndex OF eTransLink;

nnodes,netrans,ndtrans,ndfunny,ntrans:
LONG CARDINAL;
vdd,gnd:
nodeLink;

TimeStamp: PROCEDURE = BEGIN
timeString: STRING = [MAXIDLENGTH];
Time.Append[timeString, Time.Unpack[Time.Current[]]];
CWF.FWF[out1,"%s*n", timeString];
END;

WriteName: PUBLIC PROCEDURE [a: nodeLink, form: STRING, wp: PROCEDURE[CHARACTER]] = BEGIN
i:nameIndex;
i←0; WHILE a↑.name[i] # 0C DO {wp[a↑.name[i]]; i←i+1} ENDLOOP;
END;

RdID: PROCEDURE [id: STRING] = BEGIN
char:CHARACTER;
i:nameIndex;
i ← 0;
char ← io.ReadChar[];
WHILE char = io.SP OR char = io.TAB OR char = io.CR OR char = io.LF DO
char ← io.ReadChar[]; ENDLOOP;
WHILE char # io.SP AND char # io.CR AND char # io.LF AND char # io.TAB DO
id[i] ← char; i ← i+1; char←io.ReadChar[];
ENDLOOP;
id↑.length ← i; eol ← (char = io.CR OR char = io.LF);
END;

ReadLine: PROCEDURE [id: STRING] = BEGIN
char:CHARACTER;
i:nameIndex;
i ← 0;
IF NOT eol THEN BEGIN
char←io.ReadChar[];
WHILE char # io.CR AND char # io.LF DO
id[i] ← char; i ← i+1; char←io.ReadChar[];
ENDLOOP;
id↑.length ← i;
END; eol ← FALSE;
END;

ReadDecimal: PROCEDURE RETURNS [LONG INTEGER] = BEGIN
s:STRING ← [MAXIDLENGTH];
RdID[s];
RETURN[StringDefs.StringToLongNumber[s,10]];
END;

sscanf: PROCEDURE [line,g,s,d: STRING]
RETURNS [l, w: LONG CARDINAL, x, y: LONG INTEGER] = BEGIN
RdID[g]; RdID[s]; RdID[d];
l ← ReadDecimal[]; w ← ReadDecimal[];
x ← ReadDecimal[]; y ← ReadDecimal[];
ReadLine[line];
END;

WriteCoord: PROCEDURE [x, y: LONG INTEGER] = BEGIN
CWF.FWF[out2,"%8ld %8ld ", @x, @y];
END;

WriteNodeCoord: PROCEDURE [n: nodeLink] = BEGIN
e:eTransLink;
d:dTransLink;
x, y:LONG INTEGER ← 0;
FOR e ← n.igates, e↑.igate UNTIL e = NIL DO
IF e↑.source = n THEN EXIT;
IF e↑.drain = n THEN EXIT;
ENDLOOP;
IF e # NIL THEN BEGIN x ← e↑.xCoord; y ← e↑.yCoord; END
ELSE BEGIN
FOR d ← dBase, d↑.next UNTIL d = NIL DO
IF d↑.gate = n THEN EXIT;
IF d↑.source = n THEN EXIT;
ENDLOOP;
IF d # NIL THEN BEGIN x ← d↑.xCoord; y ← d↑.yCoord;
END
ELSE BEGIN
FOR e ← eBase, e↑.next UNTIL e = NIL DO
IF e↑.gate = n THEN EXIT;
ENDLOOP;
IF e # NIL THEN BEGIN
x ← e↑.xCoord; y ← e↑.yCoord; END;
END; END;
WriteCoord [x, y];
END;

eq: PROCEDURE [n: eTransLink, a,b: nodeLink] RETURNS [BOOLEAN] = BEGIN
RETURN[(n↑.source=a AND n↑.drain=b)OR(n↑.source=b AND n↑.drain=a)];
END;

other: PROCEDURE [n: eTransLink, a: nodeLink] RETURNS [nodeLink] = BEGIN
IF n↑.source = a THEN RETURN[n↑.drain];
IF n↑.drain = a THEN RETURN[n↑.source];
RETURN[NIL];
END;

hashcode: PROCEDURE [name: STRING] RETURNS [hashIndex] = BEGIN
j:nameIndex;
i:INTEGER;
i ← 0;
FOR j IN [0..name↑.length) DO
i ← i*10 + (name[j] - ’0);
IF name[j] >= ’a THEN i ← i - 32; --hash on upper case only
ENDLOOP;
i ← i MOD HASHSIZE;
RETURN[IF i<0 THEN i+HASHSIZE ELSE i];
END;

strcmp: PROCEDURE [s: STRING, n: nodeLink] RETURNS [BOOLEAN] = BEGIN
i:INTEGER;
j:nameIndex;
FOR j←0, j+1 UNTIL j=s↑.length DO
i ← n↑.name[j] - s[j]; --check for equality regardless of case
IF i = 0 THEN LOOP;
IF i = 32 AND s[j] >= ’A THEN LOOP;
IF i = -32 AND s[j] >= ’a THEN LOOP;
RETURN[FALSE];
REPEAT
FINISHED => RETURN[n↑.name[j] = 0C]
ENDLOOP;
END;

AllocateNode: PROCEDURE[size: CARDINAL] RETURNS [p: LONG POINTER] = BEGIN
q:blockLink;
IF size + blockCount > 254 THEN BEGIN
q ← seg.LongDataSegmentAddress[seg.NewDataSegment
[seg.DefaultANYBase, 1]];
q↑.next ← blockPointer;
blockPointer ← q;
blockCount ← 0;
END;
p ← @blockPointer↑.data[blockCount];
blockCount ← blockCount + size;
END;

FreeBlocks: PROCEDURE[] = BEGIN
p:blockLink;
WHILE blockPointer # NIL DO
p ← blockPointer;
blockPointer ← blockPointer↑.next;
seg.DeleteDataSegment[seg.LongVMtoDataSegment[p]];
ENDLOOP;
END;

lookup: PROCEDURE [s: STRING] RETURNS [nodeLink] = BEGIN
n:nodeLink;
j:CARDINAL;
h:hashIndex;
h ← hashcode[s];
FOR n←hash[h], n↑.next UNTIL n = NIL DO
IF strcmp[s,n] THEN {n↑.MANY ← TRUE; RETURN[n]} ENDLOOP;
n ← AllocateNode[SIZE[node]+(s↑.length+2)/2];
n↑.PU ← n↑.PD ← n↑.IPU ← n↑.GATE ← n↑.MARK ← n↑.INPUT ← FALSE;
n↑.MANY ← FALSE;
n↑.equiv ← NIL;
n↑.igates ← NIL;
n↑.next ← hash[h];
hash[h] ← n;
FOR j IN [0..s↑.length) DO n↑.name[j] ← s[j] ENDLOOP;
n↑.name[s↑.length] ← 0C;
nnodes ← nnodes+1;
RETURN[n];
END;

findRoot: PROCEDURE [n: nodeLink] RETURNS [m: nodeLink] = BEGIN
FOR m ← n, m↑.equiv UNTIL m↑.equiv = NIL DO ENDLOOP;
IF n↑.equiv # NIL THEN n↑.equiv ← m;
END;

isEquiv: PROCEDURE [m,n: nodeLink] RETURNS [BOOLEAN] = BEGIN
RETURN[findRoot[n] = findRoot[m]];
END;

makeEquiv: PROCEDURE [m,n: nodeLink] = BEGIN
k,l:nodeLink;
k ← findRoot[m];
l ← findRoot[n];
IF k # l THEN BEGIN
k↑.equiv ← l;
IF k↑.PU OR k↑.IPU THEN l↑.IPU ← TRUE;
IF k↑.PD THEN l↑.PD ← TRUE;
END;
END;

pullup: PROCEDURE [n: nodeLink] = BEGIN
m:nodeLink;
m ← findRoot[n];
m↑.IPU ← TRUE;
END;

pulldown: PROCEDURE [n: nodeLink] = BEGIN
m:nodeLink;
m ← findRoot[n];
m↑.PD ← TRUE;
END;

doPullups: PROCEDURE [] = BEGIN -- called once in main
a,b,c:nodeLink;
w,x:dTransLink;
y,z:eTransLink;
xc,yc:LONG INTEGER;
-- looks for pullups of the form:
--d A VDD A (W)
pullups:LONG CARDINAL ← 0;-- number of pullups
-- looks for inverting superbuffers of the form:
--d B VDD A (W)
--d B VDD B (X)
--e C B GND (Y)
--e C A GND (Z)
nisbuffers:LONG CARDINAL ← 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:LONG CARDINAL ← 0;-- number of non-inverting superbuffers
unknowns:LONG CARDINAL;-- number on unrecognizable depletion mode transistors

FOR w ← dBase, w↑.next UNTIL w = NIL DO
a ← w↑.source;
b ← w↑.gate;
xc ← w↑.xCoord;
yc ← w↑.yCoord;
IF a↑.PU THEN BEGIN
WriteCoord [xc, yc];
CWF.FWF[out2,"Node pulled up more than once : %n*n",a];
END;
pullup[a]; -- no matter what, this node is pulled up
a↑.PU ← TRUE;
IF a = b THEN BEGIN
pullups ← pullups+1;
LOOP;
END;

FOR y ← b↑.igates, y↑.igate UNTIL y = NIL DO
IF eq[y,b,gnd] THEN BEGIN
c ← y↑.gate;
FOR z ← a↑.igates, z↑.igate UNTIL z = NIL DO
IF z↑.gate = c AND eq[z,a,gnd] THEN
FOR x ← dBase, x↑.next UNTIL x = NIL DO
IF x↑.source = b AND x↑.gate = b THEN BEGIN
nisbuffers ← nisbuffers+1;
GOTO quit1;
END ENDLOOP
ENDLOOP END
REPEAT quit1 => LOOP; ENDLOOP;

FOR z ← a↑.igates, z↑.igate UNTIL z = NIL DO
IF eq[z,a,gnd] THEN BEGIN
c ← z↑.gate;
FOR y ← c↑.igates, y↑.igate UNTIL y = NIL DO
IF y↑.gate = b AND eq[y,c,gnd] THEN
FOR x ← dBase, x↑.next UNTIL x = NIL DO
IF x↑.gate = c AND x↑.source = c THEN BEGIN
nsbuffers ← nsbuffers+1;
GOTO quit2;
END ENDLOOP
ENDLOOP END
REPEAT quit2 => LOOP; ENDLOOP;

WriteCoord[xc, yc];
CWF.FWF[out2,"depletion transitor not a pullup or a superbuffer : d %n %n %n*n", w↑.gate,w↑.source,vdd];
ENDLOOP;
CWF.FWF[out1,"pullups and non-inverting superbuffers : %7ld %7ld*n", @pullups, @nsbuffers];
unknowns ← ndtrans-pullups-nsbuffers-nisbuffers;
CWF.FWF[out1,"inverting superbuffers and unknowns : %7ld %7ld*n", @nisbuffers, @unknowns];
END;


findInputs: PROCEDURE [] = BEGIN
-- called once in main
e:eTransLink;
n:nodeLink;
FOR e ← eBase, e↑.next UNTIL e = NIL DO
IF e↑.gate = gnd THEN
IF e↑.source = gnd AND (NOT e↑.drain↑.PU OR e↑.drain↑.INPUT) THEN BEGIN
WriteCoord[e↑.xCoord,e↑.yCoord];
CWF.FWF[out2,"Assuming lightning arrested node is an Input : %n*n", e↑.drain];
e↑.drain↑.IPU ← e↑.drain↑.PD ← e↑.drain↑.INPUT ← TRUE;
n ← findRoot[e↑.drain]; n↑.IPU ← TRUE END
ELSE IF e↑.drain = gnd AND (NOT e↑.source↑.PU OR e↑.source↑.INPUT) THEN BEGIN
WriteCoord[e↑.xCoord,e↑.yCoord];
CWF.FWF[out2,"Assuming lightning arrested node is an Input : %n*n", e↑.source];
e↑.source↑.IPU ← e↑.source↑.PD ← e↑.source↑.INPUT ← TRUE;
n ← findRoot[e↑.source]; n↑.IPU ← TRUE END;
ENDLOOP;
END;

bottom: PROCEDURE [depth: pdlIndex] = BEGIN
-- called once in doPulldowns
i:pdlIndex;
bad:BOOLEAN ← FALSE;
n:nodeLink;
sum:REAL ← 0.0;
ratio:REAL;
type:INTEGER;
IF pd0↑.dwidth = 0 THEN bad ← TRUE;
FOR i←1, i+1 UNTIL i=depth DO
IF pd[i]↑.ewidth = 0 THEN bad ← TRUE
ELSE sum ← sum + Real.Float[pd[i]↑.elength] / pd[i]↑.ewidth *
(IF pd[i]↑.gate↑.INPUT THEN 3
ELSE IF pd[i]↑.gate↑.PU THEN 1 ELSE 2);
ENDLOOP;
IF bad THEN ratio ← 0.0
ELSE ratio ← pd0↑.dlength / (pd0↑.dwidth * sum);
IF ratio < lowLimit OR ratio > highLimit THEN BEGIN
WriteCoord[pd0↑.xCoord, pd0↑.yCoord];
CWF.FWF[out2,"pullup/pulldown ratio = %5.2f node %n pulled up thru %ld by %ld*n", @ratio, pd0↑.source, @pd0↑.dlength, @pd0↑.dwidth];
n ← pd0↑.source;
FOR i←1, i+1 UNTIL i=depth DO
n←other[pd[i],n];
type ← IF pd[i]↑.gate↑.INPUT THEN 3 ELSE IF pd[i]↑.gate↑.PU THEN 1 ELSE 2;
WriteCoord[pd[i]↑.xCoord, pd[i]↑.yCoord];
CWF.FWF[out2,"*tpulled down (1/%d) by node %n thru %ld by %ld", @type, pd[i]↑.gate, @pd[i]↑.elength,@pd[i]↑.ewidth];
CWF.FWF[out2," to node %n*n", n]
ENDLOOP;
END;
END;

doPulldowns: PROCEDURE [n: nodeLink, depth: pdlIndex] = BEGIN
-- recursive and called once in checkRatios
e:eTransLink;
o:nodeLink;
IF n = gnd THEN BEGIN bottom[depth]; RETURN; END;
n↑.MARK ← TRUE;
FOR e ← n↑.igates, e↑.igate UNTIL e = NIL DO IF (o←other[e,n]) # NIL THEN
IF NOT((o↑.MARK OR o↑.PU OR o↑.GATE) AND o # gnd) THEN
IF depth < MAXPDL THEN BEGIN
pd[depth] ← e;
doPulldowns[o,depth+1];
END
ENDLOOP;
n↑.MARK ← FALSE;
END;

checkValues: PROCEDURE [] = BEGIN
-- called once in main
i:hashIndex;
m,n:nodeLink;
e:eTransLink;
d:dTransLink;

FOR i IN hashIndex DO FOR n ← hash[i], n↑.next UNTIL n = NIL DO
m ← findRoot[n];
n↑.IPU ← m↑.IPU;
n↑.PD ← m↑.PD;
IF NOT n↑.MANY THEN BEGIN
WriteNodeCoord[n];
CWF.FWF[out2,"Node name only occurs once : %n*n",n]; END;
IF NOT(n↑.PU OR n↑.IPU OR n↑.PD) THEN BEGIN
WriteNodeCoord[n];
CWF.FWF[out2,"Node can never be given a value : %n*n",n]; END
ELSE IF NOT(n↑.PU OR n↑.IPU) AND n # gnd THEN BEGIN
WriteNodeCoord[n];
CWF.FWF[out2,"Node can never be set to 1 : %n*n",n]; END
ELSE IF NOT n↑.PD AND n # vdd THEN BEGIN
WriteNodeCoord[n];
CWF.FWF[out2,"Node can never be set to 0 : %n*n",n]; END
ENDLOOP
ENDLOOP;

FOR d ← dBase, d↑.next UNTIL d = NIL DO
FOR e ← d↑.source↑.igates, e↑.igate UNTIL e = NIL DO
IF e↑.source↑.GATE OR e↑.drain↑.GATE THEN EXIT;
REPEAT
FINISHED => BEGIN
WriteCoord[d↑.xCoord, d↑.yCoord];
CWF.FWF[out2,"Value not used : %n*n", d↑.source]; END;
ENDLOOP
ENDLOOP;
END;

checkPower: PROCEDURE [] = BEGIN
-- called once in main
d:dTransLink;
sum:REAL ← 0.0;

FOR d ← dBase, d↑.next UNTIL d = NIL DO
sum ← sum + Real.Float[d↑.dwidth]/d↑.dlength;
ENDLOOP;
CWF.FWF[out1,"Sum of 1/Z’s for all pullups : %7.4f*n",
@sum];
END;

checkThresholds: PROCEDURE [] = BEGIN
-- called once in main
e:eTransLink;

FOR e ← eBase, e↑.next UNTIL e = NIL DO
IF NOT e↑.gate↑.PU THEN BEGIN
IF NOT e↑.source↑.PU AND e↑.source↑.GATE AND
e↑.gate # e↑.source AND e↑.gate # gnd AND
e↑.source # gnd THEN BEGIN
WriteCoord[e↑.xCoord, e↑.yCoord];
CWF.FWF[out2,"Non-restored node controlling pass transistor : %n %n*n", e↑.gate, e↑.source]; END;
IF NOT e↑.drain↑.PU AND e↑.drain↑.GATE AND
e↑.gate # e↑.drain AND e↑.gate # gnd AND
e↑.drain # gnd THEN BEGIN
WriteCoord[e↑.xCoord, e↑.yCoord];
CWF.FWF[out2,"Non-restored node controlling pass transistor : %n %n*n", e↑.gate, e↑.drain]; END;
IF e↑.source↑.INPUT AND
e↑.gate # e↑.source AND e↑.gate # gnd THEN BEGIN
WriteCoord[e↑.xCoord, e↑.yCoord];
CWF.FWF[out2,"Non-restored node controlling input : %n %n*n", e↑.gate, e↑.source]; END;
IF e↑.drain↑.INPUT AND
e↑.gate # e↑.drain AND e↑.gate # gnd THEN BEGIN
WriteCoord[e↑.xCoord, e↑.yCoord];
CWF.FWF[out2,"Non-restored node controlling input : %n %n*n", e↑.gate, e↑.drain]; END;
END;
ENDLOOP;
END;

checkRatios: PROCEDURE [low, high: REAL] = BEGIN
-- called once in main
d:dTransLink;
n:nodeLink;
lowLimit ← low;
highLimit ← high;
FOR d ← dBase, d↑.next UNTIL d = NIL DO
n ← d↑.source;
IF n # gnd AND n # vdd AND n↑.PU AND NOT(n↑.INPUT) THEN BEGIN
pd0 ← d;
doPulldowns[n,1];
END ENDLOOP;
END;

makeETrans: PROCEDURE [t0,t1,t2: nodeLink, l,w: LONG CARDINAL, x, y: LONG INTEGER] = BEGIN
e:eTransLink;
e ← AllocateNode[SIZE[eTrans]];
e↑.next ← eBase;
eBase ← e;
e↑.gate ← t0;
IF t0 # vdd AND t0 # gnd THEN e↑.gate↑.GATE ← TRUE;
e↑.source ← t1;
e↑.drain ← t2;
e↑.igate ← NIL;
e↑.elength ← l;
e↑.ewidth ← w;
e↑.xCoord ← x;
e↑.yCoord ← y;
IF e↑.source#vdd AND e↑.source#gnd AND e↑.drain#gnd AND
e↑.drain#vdd AND e↑.gate#gnd THEN
makeEquiv[e↑.source,e↑.drain];
IF e↑.source = vdd THEN pullup[e↑.drain];
IF e↑.drain = vdd THEN pullup[e↑.source];
IF e↑.source = gnd THEN pulldown[e↑.drain];
IF e↑.drain = gnd THEN pulldown[e↑.source];
IF e↑.gate = gnd AND e↑.source # gnd AND e↑.drain # gnd THEN BEGIN
WriteCoord[e↑.xCoord, e↑.yCoord];
CWF.FWF[out2,"Gate is GND : e %n %n %n*n",
e↑.gate,e↑.source,e↑.drain]; END;
IF e↑.gate # gnd AND
(e↑.gate = e↑.source OR
e↑.gate = e↑.drain) THEN BEGIN
WriteCoord[e↑.xCoord, e↑.yCoord];
CWF.FWF[out2,"gate is the same node as source or drain : e %n %n %n*n",
e↑.gate, e↑.source,e↑.drain]; END;
netrans←netrans+1;
END;

initStatic: PROCEDURE [si: StreamDefs.StreamHandle] = BEGIN
-- called once in main
line:STRING ← [MAXIDLENGTH];
g:STRING ← [MAXIDLENGTH];
s:STRING ← [MAXIDLENGTH];
dr:STRING ← [MAXIDLENGTH];
t0,t1,t2:nodeLink;
l,w:LONG CARDINAL;
x,y:LONG INTEGER;
d:dTransLink;
m,n:nodeLink;
e:eTransLink;
i:hashIndex;
echo:BOOLEAN;
saveIn:StreamDefs.StreamHandle;
nnodes ← netrans ← ndtrans ← ndfunny ← 0;
FOR i IN hashIndex DO hash[i] ← NIL ENDLOOP;
eBase ← NIL;
dBase ← NIL;
eol ← FALSE;
gnd ← lookup["gnd"];
vdd ← lookup["vdd"];
vdd↑.PU ← gnd↑.PD ← TRUE;
saveIn ← io.GetInputStream[];
io.SetInputStream[si];
echo ← io.SetEcho[FALSE];
WHILE NOT si↑.endof[si] DO
eol ← FALSE;
SELECT io.ReadChar[] FROM
= ’e => BEGIN
[l,w,x,y] ← sscanf[line,g,s,dr];
t0 ← lookup[g];
t1 ← lookup[s];
t2 ← lookup[dr];
makeETrans[t0,t1,t2,l,w,x,y];
IF t0 = vdd THEN BEGIN
WriteCoord[x, y];
CWF.FWF[out2,"Gate is VDD : e %n %n %n*n", t0,t1,t2]; END;
END;
= ’d => BEGIN
[l,w,x,y] ← sscanf[line,g,s,dr];
t0 ← lookup[g];
t1 ← lookup[s];
t2 ← lookup[dr];
IF t0 = t1 AND t2 # vdd THEN BEGIN
WriteCoord[x, y];
CWF.FWF[out2,"node might be VDD : d %n %n %n*n", t0, t1, t2]; END
ELSE IF t0 = t2 AND t1 # vdd THEN BEGIN
WriteCoord[x, y];
CWF.FWF[out2,"node might be VDD : d %n %n %n*n", t0, t1, t2]; END;
IF t1 # vdd AND t2 # vdd THEN BEGIN
makeETrans[vdd,t1,t2,l,w,x,y];
ndfunny ← ndfunny+1;
LOOP;
END;
d ← AllocateNode[SIZE[dTrans]];
d↑.next ← dBase;
dBase ← d;
d↑.gate ← t0;
IF (t1 = vdd) = (t2 = vdd) THEN BEGIN
WriteCoord[x, y];
CWF.FWF[out2,"depletion error : d %n %n %n*n",t0,t1,t2]; END;
d↑.source ← (IF t1=vdd THEN t2 ELSE t1);
d↑.dlength ← l;
d↑.dwidth ← w;
d↑.xCoord ← x;
d↑.yCoord ← y;
ndtrans ← ndtrans+1;
IF t0 # d↑.source THEN t0↑.GATE ← TRUE;
END;
= ’i => BEGIN
RdID[g];
t1←lookup[g];
t1↑.PU ← t1↑.PD ← t1↑.INPUT ← TRUE;
END;
= ’C =>ReadLine[line]; -- node capacitance
= ’| =>ReadLine[line]; -- comment character
ENDCASE => BEGIN ReadLine[line];
CWF.FWF[out1,"syntax error : %s*n",line] END
ENDLOOP;

-- push each gate onto the appropriate input gate list
FOR e ← eBase, e↑.next UNTIL e = NIL DO
IF e↑.source # vdd AND e↑.source # gnd THEN
n ← findRoot[e↑.source]
ELSEn ← findRoot[e↑.drain];
e↑.igate ← n↑.igates;
n↑.igates ← e;
ENDLOOP;

--let each node point to it’s subcircuit input gate list
FOR i IN hashIndex DO FOR n ← hash[i], n↑.next UNTIL n = NIL DO
m ← findRoot[n]; n↑.igates ← m↑.igates;
ENDLOOP ENDLOOP;

ntrans ← netrans-ndfunny;
CWF.FWF[out1,"nodes, transistors, pullups : %7ld %7ld %7ld*n", @nnodes, @ntrans, @ndtrans];
io.SetInputStream[saveIn];
[] ← io.SetEcho[echo];

END;

RunStatic: PUBLIC PROCEDURE[sim,erc,erclog: StreamDefs.StreamHandle,
lowLimit,highLimit: REAL] =
BEGIN
Real.InitReals[];
CWFReal.InitCWFReals[];
CWF.SetCode[’n, WriteName];
in ← sim;
out1 ← erc;
out2 ← erclog;
TimeStamp[];
initStatic[in];
doPullups[];
findInputs[];
checkValues[];
checkThresholds[];
checkRatios[lowLimit, highLimit];
checkPower[];
FreeBlocks[];
CWF.ResetCode[’n];
TimeStamp[];
END;

END.