FILE: CheckImpl.mesa
Last edited by Ousterhout, August 23, 1983 1:03 pm
Christian LeCocq December 19, 1986 11:42:16 am PST
DIRECTORY
Check,
Flow,
Globals,
Hash,
HashTable,
IO,
Model,
Parse,
Printout,
Rope;
CheckImpl: CEDAR PROGRAM
IMPORTS
Flow,
Globals,
Hash,
HashTable,
IO,
Parse,
Printout
EXPORTS Check =
BEGIN
OPEN 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 Globals.CmdProc ~ {
CheckProc: HashTable.EachPairAction =
BEGIN
p: Globals.Pointer;
f: Globals.Fet;
node: Globals.Node ← NARROW[value];
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[Globals.StdOut, "Node %s doesn't connect to any transistors.\n", IO.rope[Printout.NodeRope[node, globalVars]]]
ELSE skipped ← skipped + 1;
IF msg1 = msgLimit
THEN
IO.PutRope[Globals.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[Globals.StdOut, "No flow through FET: %s\n", IO.rope[Printout.FetRope[f, globalVars]]];
IF msg4 = 1 THEN IO.PutRope[Globals.StdOut, " Maybe you haven't marked all the inputs and outputs?\n"];
END
ELSE skipped ← skipped + 1;
IF msg4 = msgLimit THEN IO.PutRope[Globals.StdOut, " No more of these messages will be printed.\n"];
END;
5. If transistor is bidirectional but has no flow indicator,
IF f.flowFromSource
AND f.flowFromDrain
AND f.firstFlow = NIL THEN
BEGIN
msg5 ← msg5 + 1;
IF msg5 <= msgLimit
THEN
BEGIN
IO.PutF[Globals.StdOut, "Bidirectional FET: %s\n", IO.rope[Printout.FetRope[f, globalVars]]];
IF msg5 = 1 THEN IO.PutRope[Globals.StdOut, " Maybe you should label flow?\n"];
END
ELSE skipped ← skipped + 1;
IF msg5 = msgLimit THEN IO.PutRope[Globals.StdOut, " No more of these messages will be printed.\n"];
END;
6. If transistor connects Vdd and Ground, error.
IF (f.drain = globalVars.VddNode
AND f.source = globalVars.GroundNode)
OR (f.source = globalVars.VddNode AND f.drain = globalVars.GroundNode) THEN
BEGIN
msg6 ← msg6 + 1;
IF msg6 <= msgLimit THEN IO.PutF[Globals.StdOut, "FET between Vdd and Ground: %s\n", IO.rope[Printout.FetRope[f, globalVars]]]
ELSE skipped ← skipped + 1;
IF msg6 = msgLimit THEN IO.PutRope[Globals.StdOut, " No more of these messages will be printed.\n"];
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[Globals.StdOut, "Node doesn't drive anything: %s.\n", IO.rope[Printout.NodeRope[node, globalVars]]];
IF msg3 = 1 THEN IO.PutRope[Globals.StdOut, " Maybe you haven't marked all the inputs and outputs?\n"];
END
ELSE skipped ← skipped + 1;
IF msg3 = msgLimit THEN IO.PutRope[Globals.StdOut, " No more of these messages will be printed.\n"];
END;
IF
NOT (node.input
OR driven)
THEN
BEGIN
msg2 ← msg2 + 1;
IF msg2 <= msgLimit
THEN
BEGIN
IO.PutF[Globals.StdOut, "Node isn't driven: %s.\n", IO.rope[Printout.NodeRope[node, globalVars]]];
IF msg2 = 1 THEN IO.PutRope[Globals.StdOut, " Maybe you haven't marked all the inputs and outputs?\n"];
END
ELSE skipped ← skipped + 1;
IF msg2 = msgLimit THEN IO.PutRope[Globals.StdOut, " No more of these messages will be printed.\n"];
END;
END;
msg1 ← 0;
msg2 ← 0;
msg3 ← 0;
msg4 ← 0;
msg5 ← 0;
msg6 ← 0;
skipped ← 0;
Hash.Enumerate[table: globalVars.nodeTable, pattern: "*", proc: checkProc, errorStream: Globals.StdOut];
[] ← HashTable.Pairs[globalVars.nodeTable, CheckProc]
};
RatioCmd: PUBLIC Globals.CmdProc =
BEGIN
RatioProc: HashTable.EachPairAction =
BEGIN
p: Globals.Pointer;
node: Globals.Node ← NARROW[value];
IF (node = globalVars.VddNode) OR (node = globalVars.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, globalVars: globalVars];
END;
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[Globals.StdOut, "Bad limit %s: try normalhigh, normallow, ", IO.rope[args.rope]];
IO.PutRope[Globals.StdOut, "passhigh, or passlow.\n"];
args ← args.next;
LOOP;
END;
args ← args.next;
IF args = NIL THEN
BEGIN
IO.PutF[Globals.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[Globals.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: globalVars.nodeTable, pattern: "*", proc: ratioProc, errorStream: Globals.StdOut];
[] ← HashTable.Pairs[globalVars.nodeTable, RatioProc];
ratioTable ← NIL;
IF totalRatioErrors > totalRatioLimit THEN
IO.PutF[Globals.StdOut, "Ratio checking aborted after %d errors.\n", IO.int[totalRatioErrors]];
IF ratioDups > 0 THEN
IO.PutF[Globals.StdOut, "%d duplicate ratio errors not printed.\n", IO.int[ratioDups]];
END;
checkRatio: PROC[origin, current: Globals.Node, loadSize, curSize: REAL, gotPass: BOOLEAN, globalVars: Globals.GlobalVars] =
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: Globals.Pointer;
f: Globals.Fet;
newSize, ratio: REAL;
newPass: BOOLEAN;
error: Rope.ROPE;
count: REF IntRec;
next: Globals.Node;
Set a flag to avoid infinite recursion through circular structures.
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 = globalVars.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;
newSize ← curSize + f.aspect;
IF next = globalVars.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[Globals.StdOut, "Ratio error at node %s: %s\n", IO.rope[Printout.NodeRope[origin, globalVars]], IO.rope[error]];
IO.PutF[Globals.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, globalVars: globalVars];
IF f.firstFlow # NIL THEN Flow.Unlock[f, next];
ENDLOOP;
current.inPath ← FALSE;
END.