-- File StaticImpl.Mesa -- Last changed: December 19, 1982 by CPT --Stored on [indigo]<chipmonk>static> DIRECTORY IODefs,Time,StreamDefs,StringDefs,SystemDefs,SegmentDefs, --from [ivy]<mesa>system> Real, --from [indigo]<cedarlib>real>alto> CWF,CWFReal; --from [indigo]<cedarlib>wf>dstar StaticImpl: PROGRAM IMPORTS IODefs,Time,StreamDefs,StringDefs,SegmentDefs,Real,CWF,CWFReal = BEGIN OPEN io:IODefs,seg:SegmentDefs; --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. --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. MAXIDLENGTH:CARDINAL = 256; 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; node: TYPE = RECORD [ PU,PD,IPU,GATE,MARK,INPUT,MANY: BOOLEAN, --GATE means that this node contains one or more gates. next: nodeLink, --next node equiv: nodeLink, --The exemplar of the class has equiv = NIL, and all other members of the class are linked (through equiv) to the exemplar. igates: eTransLink, --List of transistors whose gates control this node name: PACKED ARRAY[0..0) OF CHARACTER ]; eTrans: TYPE = RECORD [gate, source, drain: nodeLink, igate: eTransLink, --all gates controlling an equivalence class are linked through this field. elength,ewidth: LONG CARDINAL, x, y: LONG INTEGER, next: eTransLink]; dTrans: TYPE = RECORD [gate, source:nodeLink, dlength,dwidth: LONG CARDINAL, x, y: LONG INTEGER, next: dTransLink]; eBase: eTransLink; dBase: dTransLink; blockPointer: LONG POINTER; blockCount: CARDINAL ← 0; in: StreamDefs.StreamHandle; out: StreamDefs.StreamHandle; line: STRING ← [MAXIDLENGTH]; fname: STRING ← [MAXIDLENGTH]; inname: STRING ← [MAXIDLENGTH]; eol: BOOLEAN; lowLimit,highLimit: REAL; -- limits on L/W (set by user) infactor, ipufactor: REAL; -- amount by which to multiply the L/W --of a transistor connected to an input or indirectly --pulled up (i.e. , driven by a passgate) (set by user) hash: ARRAY hashIndex OF nodeLink; pd0: dTransLink; pd: ARRAY pdlIndex OF eTransLink; nnodes,netrans,ndtrans,ndfunny: LONG CARDINAL; vdd,gnd: nodeLink; TimeStamp: PROCEDURE = BEGIN timeString: STRING = [MAXIDLENGTH]; Time.Append[timeString, Time.Unpack[Time.Current[]]]; CWF.WF["%s*n", timeString]; END; WriteName: 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; WriteCoords: PROCEDURE [x, y: LONG INTEGER] = BEGIN CWF.WF["%ld %ld ", @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 ← 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 IF size > blockCount THEN BEGIN blockPointer ← seg. LongDataSegmentAddress[seg.NewDataSegment [seg.DefaultANYBase, 1]]; blockCount ← 256; END; blockCount ← blockCount - size; p ← blockPointer; blockPointer ← blockPointer + size; END; lookup: PROCEDURE [s: STRING] RETURNS [nodeLink] =BEGIN -- returns the nodeLink corresponding to the string name. -- Makes a new node and initializes it if there is no such node. 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 -- find the node --that is the exemplar of the equivalence class of which the given node is a member. FOR m ← n, m.equiv UNTIL m.equiv = NIL DO ENDLOOP; END; isEquiv: PROCEDURE [m,n: nodeLink] RETURNS [BOOLEAN] = BEGIN RETURN[findRoot[n] = findRoot[m]]; END; makeEquiv: PROCEDURE [m,n: nodeLink] = 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,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; --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 B (X) --e C B GN D (Y) --e C A GN D (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; IF a.PU THEN CWF.WF["Node pulled up more than once: %n*n",a]; 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; WriteCoords[w.x,w.y]; CWF.WF["depletion transitor not a pullup or a superbuffer: d %n %n %n *n", w.gate , w.source, vdd]; ENDLOOP; CWF.WF["pullups and non-inverting superbuffers: %7ld %7ld*n", @pullups, @ nsbuffers]; unknowns ← ndtrans - pullups - nsbuffers - nisbuffers; CWF.WF["inverting superbuffers and unknowns: %7ld %7ld*n", @nisbuffers, @unknowns]; END; findInputs: PROCEDURE [ ] = BEGIN -- called once in main --looks for eTrans with gate = drain|source = gnd (lightning arrester) 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.INPUT THEN BEGIN WriteCoords[e.x,e.y]; CWF.WF["Assuming lightning arrested node is an Input: %n*n", e.drain]; e.drain.PU ← e.drain.PD ← e.drain.INPUT ← TRUE; n ← findRoot [e.drain]; n.IPU ← TRUE END ELSE IF e.drain = gnd AND NOT e.source.INPUT THEN BEGIN WriteCoords[e.x,e.y]; CWF.WF["Assuming lightning arrested node is an Input: %n*n", e.source]; e.source.PU← e.source.PD ← e.source.INPUT←TRUE; n ← findRoot[e.source]; n.IPU←TRUE END; ENDLOOP; END; checkThresholds: PROCEDURE [ ] = BEGIN -- called once in main e: eTransLink; FOR e ← eBase, e. next UNTIL e = NIL DO -- 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 NOT (e.gate.PU OR e.source.PU) AND e.drain.GATE AND e.gate # e.source AND e.gate # gnd AND e.source # gnd THEN { WriteCoords[e.x,e.y]; CWF.WF["Pass transistor driven by pass transistor: %n %n %n*n",e.gate,e.source,e.drain] }; -- Same check as above, with source and drain interchanged. IF NOT (e.gate.PU OR e.drain.PU) AND e.source.GATE AND e.gate # e.drain AND e.gate # gnd AND e.drain # gnd THEN { WriteCoords[e.x,e.y]; CWF.WF["Pass transistor driven by pass transistor: %n %n %n*n",e.gate,e.source,e.drain] }; ENDLOOP; END; makeETrans: PROCEDURE [t0, t1, t2: nodeLink, l,w: LONG CARDINAL,x,y: LONG INTEGER] = BEGIN e: eTransLink← 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.x ← x; e.y ← y; IF e.source # vdd AND e.source # gnd AND e.drain # gnd AND e.drain # vdd 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 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 --not a lightning arrester { WriteCoords[e.x,e.y]; CWF.WF["Gate is GND: e %n %n %n*n", e.gate,e.source,e.drain] }; IF e.gate # gnd AND (e.gate = e.source OR e.gate = e.drain) THEN { WriteCoords[e.x,e.y]; CWF.WF[ "gate and source or drain equality: e %n %n %n *n", e.gate, e.source, e.drain] }; netrans← netrans+1; END; checkRatios: PROCEDURE [] = BEGIN -- called once in main d: dTransLink; n: nodeLink; 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; doPulldowns: PROCEDURE [n: nodeLink, depth: pdlIndex] = BEGIN --recursive and called once in CheckRatios --On initial entry, pd0 has the dTransLink for the pullup under consideration, --and n has the nodeLink for the node pulled up by this transistor e: eTransLink; o: nodeLink; IF n = gnd THEN BEGIN -- CWF.WF["x"]; bottom[depth]; -- CWF.WF["y"]; RETURN; END; n.MARK ← TRUE; FOR e ← n.igates, e.igate UNTIL e = NIL DO --look at all the transistors that control node n IF (o ← other[e,n]) # NIL THEN IF NOT ((o.MARK OR o.PU OR o.GATE) AND o # gnd) THEN --examine more of a path if... --the node at the other end of the transistor under consideration is ground, OR --the node is not MARKed (examined earlier) AND not pulled up --AND 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. IF depth < MAXPDL THEN BEGIN pd[depth] ← e; doPulldowns[o,depth+1]; END ENDLOOP; n.MARK ← FALSE; END; bottom: PROCEDURE [depth: pdlIndex] = BEGIN --called once in do Pulldowns i: pdlIndex; bad: BOOLEAN ← FALSE; n: nodeLink; sum: REAL ← 0.0; ratio: REAL; type: REAL; 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 infactor ELSE IF pd[i].gate.IPU THEN ipufactor ELSE 1.0); ENDLOOP; IF bad THEN ratio ← 0.0 ELSE ratio ← pd0.dlength / (pd0.dwidth* sum); IF ratio < lowLimit OR ratio > highLimit THEN BEGIN WriteCoords[pd0.x,pd0.y]; CWF.WF["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 infactor ELSE IF pd[i].gate.IPU THEN ipufactor ELSE 1.0; WriteCoords[pd[i].x,pd[i].y]; CWF.WF["*tpulled down by node %n thru %ld by %ld (x%5.2f)", pd[i].gate,@pd[i].elength,@pd[i].ewidth,@type]; CWF.WF[" to node %n*n",n] ENDLOOP; END; 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 CWF.WF["Node name only occurs once: %n*n",n]; IF NOT (n.PU OR n.IPU OR n.PD) THEN CWF.WF["Node can never be given a value: %n*n",n] ELSE IF NOT (n.PU OR n.IPU) AND n # gnd THEN CWF.WF["Node can never be set to 1: %n*n",n] ELSE IF NOT n.PD AND n # vdd THEN CWF.WF["Node can never be set to 0: %n*n", n] ENDLOOP ENDLOOP; FOR d ← dBase, d.next UNTIL d = NIL DO --start at each pullup, and make sure --that some node in the class drives a gate. FOR e ← d.source.igates, e.igate UNTIL e=NIL DO IF e.source.GATE OR e.drain.GATE THEN EXIT; REPEAT FINISHED => {WriteCoords[d.x,d.y];CWF.WF["Value not used: %n*n", d.source]}; ENDLOOP ENDLOOP; END; initStatic: PROCEDURE [] = 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; 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; echo ← io.SetEcho[FALSE]; WHILE NOT in.endof [in] DO eol ← FALSE; SELECT io.ReadChar[ ] FROM = 015C => LOOP; -- blank line =' => LOOP; --leadingblanks ='e, 'z => BEGIN -- enhancement transistor [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 { WriteCoords[x,y]; CWF.WF["Gate is VDD: e %n %n %n*n", t0, t1, t2]}; END; ='d, ='x => BEGIN -- depletion transistor [l, w, x, y] ← sscanf[line,g,s,dr]; t0 ← lookup[g]; t1 ← lookup[s]; t2 ← lookup[dr]; IF t1 # vdd AND t2 # vdd THEN BEGIN -- nonstandard use of dTrans ndfunny ← ndfunny+1; IF t1 = t2 AND t1 # t0 THEN BEGIN -- capacitor WriteCoords[x,y]; CWF.WF["depletion capacitor - ignored: d %n %n %n*n", t0, t1, t2,]; LOOP; END; IF (t0 = t1) OR (t0 = t2) THEN BEGIN --resistor WriteCoords[x,y]; CWF.WF["depletion resistor: d %n %n %n*n", t0, t1, t2]; makeETrans[vdd, t1,t2,l,w, x, y]; netrans ← netrans-1; LOOP; END; WriteCoords[x,y]; CWF.WF["yellow transistor: d %n %n %n *n", t0, t1, t2,]; makeETrans[vdd, t1, t2, l,w,x,y]; netrans ← netrans-1; LOOP; END; d ← AllocateNode[SIZE[dTrans]]; d.next ← dBase; dBase ← d; d.gate ← t0; IF (t1 = vdd) = (t2 = vdd) THEN { WriteCoords[x,y]; CWF.WF["depletion error: d %n %n %n*n", t0, t1, t2]}; 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 => ReadLine[line]; -- node characteristics = '| => ReadLine[line]; -- comment character = '= => ReadLine[line]; -- node equivalence (extractor has already -- assigned a common name for all nodes ENDCASE => BEGIN ReadLine[line]; CWF.WF ["syntax error: %s*n",line]; END ENDLOOP; -- link together (starting at the exemplar's igates field) all gates for transistors in each equivalence class FOR e ← eBase, e.next UNTIL e = NIL DO IF e.source # vdd AND e.source # gnd THEN n ← findRoot [e.source] --find the node that is the exemplar of the equivalence --class of which this node is a part ELSE n ← findRoot [e.drain]; IF n = gnd THEN {WriteCoords[e.x,e.y]; CWF.WF["funny eTrans: e %n %n %n*n", e.gate, e.source, e.drain]}; e.igate ← n.igates; --put this transistor on the list of the gates that affect --the class of which the transistor is a part. n.igates ← e; ENDLOOP; -- copy the igates field of the exemplary node for a class into the igates field of each --member of the class. FOR i IN hashIndex DO FOR n ← hash [i], n.next UNTIL n= NIL DO m ← findRoot[n]; n.igates ← m.igates; ENDLOOP ENDLOOP; CWF.WF["Nodes, Etrans, Dtrans, FunnyDtrans: %7ld %7ld %7ld %7ld*n", @nnodes, @netrans, @ndtrans,@ndfunny]; END; --the body Real.InitReals[ ]; CWFReal.InitCWFReals[ ]; CWF.SetCode['n, WriteName]; CWF.WF["Name of the file to be checked (no extension): "]; io.ReadID[fname]; StringDefs.AppendString[inname,fname]; StringDefs.AppendString[inname,".sim"]; StringDefs.AppendString[fname,".erclog"]; in ← StreamDefs.NewByteStream[inname, StreamDefs.Read]; out ← StreamDefs.NewByteStream[fname,StreamDefs.Write+StreamDefs.Append]; CWF.WF["*nLow Ratio: "]; io.ReadID[line]; lowLimit ← Real.StringToReal[line]; CWF.WF["*nHigh Ratio: "]; io.ReadID[line]; highLimit ← Real.StringToReal[line]; CWF.WF["*nL/W multiplier for input transistors: "]; io.ReadID[line]; infactor ← Real.StringToReal[line]; CWF.WF["*nL/W multiplier for transistors with indirectly pulled up gates: "]; io.ReadID[line]; ipufactor ← Real.StringToReal[line]; io.SetInputStream[in]; io.SetOutputStream[out]; CWF.WF["*n"]; TimeStamp[ ]; initStatic[ ]; doPullups[ ]; findInputs[ ]; checkValues[ ]; checkThresholds[ ]; checkRatios[ ]; CWF.ResetCode['n]; out.destroy[out]; END.