-- 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.