-- FILE: CheckImpl.mesa -- Last edited by Ousterhout, August 23, 1983 1:03 pm DIRECTORY Check, Flow, Globals, Hash, IO, Model, Parse, Printout, Rope; CheckImpl: CEDAR PROGRAM IMPORTS Flow, Globals, Hash, IO, Parse, Printout EXPORTS Check = BEGIN OPEN Globals, Check; -- Limit of how many message of any one type to print: msgLimit: INT _ 100; -- Limits for valid transistor ratios: normalLow: REAL _ 3.8; normalHigh: REAL _ 4.2; passLow: REAL _ 7.8; passHigh: REAL _ 8.2; -- Info used to suppress printing of many identical ratio errors: ratioLimit: PUBLIC INT _ 5; ratioTable: Hash.Table; ratioDups: INT; totalRatioErrors: INT; totalRatioLimit: PUBLIC INT _ 1000; cmdTable: ARRAY[0..3] OF Rope.ROPE _ [ "normalhigh", "normallow", "passhigh", "passlow" ]; -- Info shared between CheckCmd and checkProc: msg1, msg2, msg3, msg4, msg5, msg6, skipped: INT; IntRec: TYPE = RECORD[val: INT]; CheckCmd: PUBLIC CmdProc = BEGIN msg1 _ 0; msg2 _ 0; msg3 _ 0; msg4 _ 0; msg5 _ 0; msg6 _ 0; skipped _ 0; Hash.Enumerate[table: NodeTable, pattern: "*", proc: checkProc, errorStream: StdOut]; END; checkProc: Hash.EnumProc = BEGIN p: Pointer; f: Fet; node: Node _ NARROW[entry.clientData]; drives, driven: BOOLEAN _ FALSE; -- 1. Make sure the node has transistors attached. IF node.firstPointer = NIL THEN BEGIN msg1 _ msg1 + 1; IF msg1 <= msgLimit THEN IO.PutF[StdOut, "Node %s doesn't connect to any transistors.\n", IO.rope[Printout.NodeRope[node]]] ELSE skipped _ skipped + 1; IF msg1 = msgLimit THEN IO.PutRope[StdOut, " No more of these messages will be printed.\n"]; RETURN; END; -- Go through all the transistors attached to the node. See if the -- node is driven and if it drives anything, and also check out the -- transistors on the way (only check a transistor when its gate is -- seen, to avoid duplication). FOR p _ node.firstPointer, p.next UNTIL p = NIL DO f _ p.fet; IF f.source = node THEN BEGIN IF f.flowFromDrain THEN driven _ TRUE; IF f.flowFromSource THEN drives _ TRUE; END; IF f.drain = node THEN BEGIN IF f.flowFromSource THEN driven _ TRUE; IF f.flowFromDrain THEN drives _ TRUE; END; IF f.gate = node THEN BEGIN drives _ TRUE; -- 4. If transistor can't flow at all, error. IF NOT (f.flowFromSource OR f.flowFromDrain) THEN BEGIN msg4 _ msg4 + 1; IF msg4 <= msgLimit THEN BEGIN IO.PutF[StdOut, "No flow through FET: %s\n", IO.rope[Printout.FetRope[f]]]; IF msg4 = 1 THEN BEGIN IO.PutRope[StdOut, " Maybe you haven't marked all"]; IO.PutRope[StdOut, " the inputs and outputs?\n"]; END; END ELSE skipped _ skipped + 1; IF msg4 = msgLimit THEN BEGIN IO.PutRope[StdOut, " No more of these messages"]; IO.PutRope[StdOut, " will be printed.\n"]; END; END; -- 5. If transistor is bidirectional but has no flow indicator, -- issue a warning. IF f.flowFromSource AND f.flowFromDrain AND f.firstFlow = NIL THEN BEGIN msg5 _ msg5 + 1; IF msg5 <= msgLimit THEN BEGIN IO.PutF[StdOut, "Bidirectional FET: %s\n", IO.rope[Printout.FetRope[f]]]; IF msg5 = 1 THEN IO.PutRope[StdOut, " Maybe you should label flow?\n"]; END ELSE skipped _ skipped + 1; IF msg5 = msgLimit THEN BEGIN IO.PutRope[StdOut, " No more of these messages"]; IO.PutRope[StdOut, " will be printed.\n"]; END; END; -- 6. If transistor connects Vdd and Ground, error. IF (f.drain = VddNode AND f.source = GroundNode) OR (f.source = VddNode AND f.drain = GroundNode) THEN BEGIN msg6 _ msg6 + 1; IF msg6 <= msgLimit THEN IO.PutF[StdOut, "FET between Vdd and Ground: %s\n", IO.rope[Printout.FetRope[f]]] ELSE skipped _ skipped + 1; IF msg6 = msgLimit THEN BEGIN IO.PutRope[StdOut, " No more of these messages"]; IO.PutRope[StdOut, " will be printed.\n"]; END; END; END; ENDLOOP; -- 3 & 4. Make sure that the node can drive and be driven. IF NOT (node.output OR drives) THEN BEGIN msg3 _ msg3 + 1; IF msg3 <= msgLimit THEN BEGIN IO.PutF[StdOut, "Node doesn't drive anything: %s.\n", IO.rope[Printout.NodeRope[node]]]; IF msg3 = 1 THEN BEGIN IO.PutRope[StdOut, " Maybe you haven't marked all"]; IO.PutRope[StdOut, " the inputs and outputs?\n"]; END; END ELSE skipped _ skipped + 1; IF msg3 = msgLimit THEN BEGIN IO.PutRope[StdOut, " No more of these messages"]; IO.PutRope[StdOut, " will be printed.\n"]; END; END; IF NOT (node.input OR driven) THEN BEGIN msg2 _ msg2 + 1; IF msg2 <= msgLimit THEN BEGIN IO.PutF[StdOut, "Node isn't driven: %s.\n", IO.rope[Printout.NodeRope[node]]]; IF msg2 = 1 THEN BEGIN IO.PutRope[StdOut, " Maybe you haven't marked all"]; IO.PutRope[StdOut, " the inputs and outputs?\n"]; END; END ELSE skipped _ skipped + 1; IF msg2 = msgLimit THEN BEGIN IO.PutRope[StdOut, " No more of these messages"]; IO.PutRope[StdOut, " will be printed.\n"]; END; END; END; RatioCmd: PUBLIC CmdProc = BEGIN index: INT; val: REAL; ok: BOOLEAN; ratioTable _ Hash.NewTable[]; ratioDups _ 0; totalRatioErrors _ 0; normalLow _ 3.8; normalHigh _ 4.2; passLow _ 7.8; passHigh _ 8.2; -- Read in parameters, if there are any. WHILE args # NIL DO TRUSTED {index _ Parse.Lookup[args.rope, DESCRIPTOR[cmdTable]]}; IF index < 0 THEN BEGIN IO.PutF[StdOut, "Bad limit %s: try normalhigh, normallow, ", IO.rope[args.rope]]; IO.PutRope[StdOut, "passhigh, or passlow.\n"]; args _ args.next; LOOP; END; args _ args.next; IF args = NIL THEN BEGIN IO.PutF[StdOut, "No value given for %s.\n", IO.rope[cmdTable[index]]]; EXIT; END; [ok, val] _ Parse.Real[args]; args _ args.next; IF NOT ok THEN BEGIN IO.PutF[StdOut, "Bad value given for %s.\n", IO.rope[cmdTable[index]]]; LOOP; END; SELECT index FROM =0 => normalHigh _ val; =1 => normalLow _ val; =2 => passHigh _ val; =3 => passLow _ val; ENDCASE; ENDLOOP; -- Process all of the nodes in the hash table. Hash.Enumerate[table: NodeTable, pattern: "*", proc: ratioProc, errorStream: StdOut]; ratioTable _ NIL; IF totalRatioErrors > totalRatioLimit THEN IO.PutF[StdOut, "Ratio checking aborted after %d errors.\n", IO.int[totalRatioErrors]]; IF ratioDups > 0 THEN IO.PutF[StdOut, "%d duplicate ratio errors not printed.\n", IO.int[ratioDups]]; END; ratioProc: Hash.EnumProc = BEGIN p: Pointer; node: Node _ NARROW[entry.clientData]; IF (node = VddNode) OR (node = GroundNode) THEN RETURN; FOR p _ node.firstPointer, p.next UNTIL p = NIL DO IF p.fet.type = Model.fetNLoad THEN EXIT; ENDLOOP; IF p = NIL THEN RETURN; checkRatio[origin: node, current: node, loadSize: p.fet.aspect, curSize: 0.0, gotPass: FALSE]; END; checkRatio: PROC[origin, current: Node, loadSize, curSize: REAL, gotPass: BOOLEAN] = -- This is a recursive procedure that does all of the real work in checking -- transistor ratios. It scans recursively through pulldowns and checks -- ratios whenever it reaches ground. Error messages are printed if ratio -- errors are found. BEGIN p, p2: Pointer; f: Fet; newSize, ratio: REAL; newPass: BOOLEAN; error: Rope.ROPE; count: REF IntRec; next: Node; h: Hash.Entry; -- Set a flag to avoid infinite recursion through circular structures. current.inPath _ TRUE; -- Skim through all of the transistors attached to the node. If the -- transistor connects to ground, check the ratio. Otherwise, call this -- routine recursively to check on the pother side. FOR p _ current.firstPointer, p.next UNTIL p = NIL DO IF totalRatioErrors > totalRatioLimit THEN BEGIN current.inPath _ FALSE; RETURN; END; f _ p.fet; -- Make sure that the transistor's source or drain connects to this -- node and that flow in this direction is OK. IF f.source = current THEN BEGIN next _ f.drain; IF NOT f.flowFromDrain THEN LOOP; END ELSE IF f.drain = current THEN BEGIN next _ f.source; IF NOT f.flowFromSource THEN LOOP; END ELSE LOOP; -- Ignore circular paths and paths to Vdd. IF next.inPath THEN LOOP; IF next = VddNode THEN LOOP; IF f.firstFlow # NIL THEN IF NOT Flow.Lock[f, next] THEN LOOP; -- See if the gate of this transistor is load driven or pass -- transistor driven. Inputs are considered to be equal to -- load driven nodes. FOR p2 _ f.gate.firstPointer, p2.next UNTIL p2 = NIL DO IF (p2.fet.type = Model.fetNLoad) OR (p2.fet.type = Model.fetNSuper) THEN EXIT; ENDLOOP; IF (p2 = NIL) AND NOT f.gate.input THEN newPass _ TRUE ELSE newPass _ gotPass; -- Now check the ratio. newSize _ curSize + f.aspect; IF next = GroundNode THEN BEGIN ratio _ loadSize/newSize; IF newPass THEN BEGIN IF (ratio >= passLow) AND (ratio <= passHigh) THEN BEGIN IF f.firstFlow # NIL THEN Flow.Unlock[f, next]; LOOP; END; END ELSE IF (ratio >= normalLow) AND (ratio <= normalHigh) THEN BEGIN IF f.firstFlow # NIL THEN Flow.Unlock[f, next]; LOOP; END; -- There's been an error. This piece of code eliminates extra messages: -- there can be no more than one message about a particular node, no -- more than a certain number of messages about a particular ratio, and -- no more than a certain total number of messages. IF origin.ratioError THEN BEGIN ratioDups _ ratioDups + 1; IF f.firstFlow # NIL THEN Flow.Unlock[f, next]; LOOP; END; origin.ratioError _ TRUE; totalRatioErrors _ totalRatioErrors + 1; error _ IO.PutFR["pullup = %.1f, pulldown = %.1f", IO.real[loadSize], IO.real[newSize]]; h _ Hash.Find[table: ratioTable, name: error]; IF h.clientData = NIL THEN h.clientData _ NEW[IntRec _ [0]]; count _ NARROW[h.clientData]; count.val _ count.val + 1; IF count.val > ratioLimit THEN ratioDups _ ratioDups + 1 ELSE BEGIN IO.PutF[StdOut, "Ratio error at node %s: %s\n", IO.rope[Printout.NodeRope[origin]], IO.rope[error]]; IO.PutF[StdOut, " Last pulldown is at (%.1f, %.1f)\n", IO.real[f.x/Printout.Units], IO.real[f.y/Printout.Units]]; END; END ELSE checkRatio[origin: origin, current: next, loadSize: loadSize, curSize: newSize, gotPass: newPass]; IF f.firstFlow # NIL THEN Flow.Unlock[f, next]; ENDLOOP; current.inPath _ FALSE; END; END.