-- 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; blockCount _ 254; 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 i: hashIndex; e: eTransLink; m, 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; 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^.PU OR n^.IPU OR n^.PD) THEN BEGIN WriteNodeCoord[n]; CWF.FWF[out2,"Assuming unsettable node is an Input : %n*n",n]; n^.IPU _ n^.PD _ n^.INPUT _ TRUE; m^.IPU _ TRUE; END; ENDLOOP; 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 OR o^.INPUT) 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 NULL -- Assumed input 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 AND NOT e^.gate^.INPUT 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; 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; = 'N => 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] ELSE n _ 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. (1792)\f8 270f0 7f8