-- FILE: MarkImpl.mesa
-- Last edited by Ousterhout, August 30, 1983 11:48 am
DIRECTORY
Flow,
Globals,
Hash,
IO,
Mark,
Model,
Parse,
Printout,
Rope;
MarkImpl: CEDAR PROGRAM
IMPORTS
Flow,
Globals,
IO,
Hash,
Model,
Parse,
Printout,
Rope
EXPORTS Mark =
BEGIN
OPEN Globals, Mark;
-- The following variables are used to pass information from
-- high-level command procedures to low-level search action
-- routines.
flag: {input, output, bus, precharged, watched};
value: REAL;
ivalue: INT;
-- The following variables are used to record statistics.
nodes0: INT ← 0; -- number of nodes forced to zero.
nodes1: INT ← 0; -- number of nodes forced to one.
dynamic: INT ← 0; -- number of nodes marked dynamic.
simCount: INT ← 0; -- counts calls to checkNode and findStrength.
-- The following flags determine whether or not we print out node
-- names whenever they get set to particular values or marked dynamic.
SeeSettings: PUBLIC BOOLEAN ← FALSE;
SeeAllSettings: PUBLIC BOOLEAN ← FALSE;
SeeDynamic: PUBLIC BOOLEAN ← FALSE;
-- The following stuff is used to cause a graceful abort in findStrength
-- when it's spending too long searching.
FSLimit: PUBLIC INT ← 1000;
fSCount: INT;
InputCmd: PUBLIC CmdProc =
BEGIN
flag ← input;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: flagProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
OutputCmd: PUBLIC CmdProc =
BEGIN
flag ← output;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: flagProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
BusCmd: PUBLIC CmdProc =
BEGIN
flag ← bus;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: flagProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
PrechargedCmd: PUBLIC CmdProc =
BEGIN
flag ← precharged;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: flagProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
WatchCmd: PUBLIC CmdProc =
BEGIN
flag ← watched;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: flagProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
flagProc: Hash.EnumProc =
BEGIN
node: Node ← NARROW[entry.clientData];
SELECT flag FROM
input => node.input ← TRUE;
output => node.output ← TRUE;
bus => node.bus ← TRUE;
watched => node.watched ← TRUE;
precharged =>
BEGIN
node.precharged ← TRUE;
node.bus ← TRUE;
END;
ENDCASE;
END;
ResCmd: PUBLIC CmdProc =
BEGIN
ok: BOOLEAN;
IF args = NIL THEN
BEGIN
IO.PutRope[StdOut, "No resistance value given.\n"];
RETURN;
END;
[ok, value] ← Parse.Real[args];
IF (NOT ok) OR (value < 0) THEN
BEGIN
IO.PutRope[StdOut, "Bad resistance value.\n"];
RETURN;
END;
args ← args.next;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: resProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
resProc: Hash.EnumProc =
BEGIN
node: Node ← NARROW[entry.clientData];
node.res ← value;
END;
CapCmd: PUBLIC CmdProc =
BEGIN
ok: BOOLEAN;
IF args = NIL THEN
BEGIN
IO.PutRope[StdOut, "No capacitance value given.\n"];
RETURN;
END;
[ok, value] ← Parse.Real[args];
IF (NOT ok) OR (value < 0) THEN
BEGIN
IO.PutRope[StdOut, "Bad capacitance value.\n"];
RETURN;
END;
args ← args.next;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: capProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
capProc: Hash.EnumProc =
BEGIN
node: Node ← NARROW[entry.clientData];
node.cap ← value;
END;
SetNodeValue: PUBLIC PROC[node: Node, value: INT, propAnyway: BOOLEAN ← FALSE] =
BEGIN
p: Pointer;
f: Fet;
-- Make sure that there is no conflict in setting the node's
-- value, then set the flags on the node. If the level was
-- already forced, then we can return immediately (this check
-- prevents circular loops).
IF value = 0 THEN
BEGIN
IF node.always1 THEN
IO.PutF[StdOut, "Node forced to 1, then to 0 (0 wins): %s.\n",
IO.rope[Printout.NodeRope[node]]];
node.always1 ← FALSE;
IF node.always0 AND NOT propAnyway THEN RETURN;
node.always0 ← TRUE;
nodes0 ← nodes0 + 1;
END
ELSE
BEGIN
IF node.always0 THEN
IO.PutF[StdOut, "Node forced to 0, then to 1 (1 wins): %s.\n",
IO.rope[Printout.NodeRope[node]]];
node.always0 ← FALSE;
IF node.always1 AND NOT propAnyway THEN RETURN;
node.always1 ← TRUE;
nodes1 ← nodes1 + 1;
END;
IF SeeAllSettings OR (SeeSettings AND
((Rope.Fetch[node.name, 0] < '0) OR (Rope.Fetch[node.name, 0] > '9))) THEN
IO.PutF[StdOut, "Node %s forced to %d.\n",
IO.rope[Printout.NodeRope[node]], IO.int[value]];
-- Find all of the transistors connecting to this node, and take action
-- (if necessary) to propagate the level setting.
FOR p ← node.firstPointer, p.next UNTIL p = NIL DO
f ← p.fet;
-- Where this node connects to sources or drains, check to
-- see if level information will propagate to the next node over.
IF (f.source = node) AND f.flowFromSource AND
(f.onAlways OR f.forcedOn) THEN checkNode[f.drain];
IF (f.drain = node) AND f.flowFromDrain AND
(f.onAlways OR f.forcedOn) THEN checkNode[f.source];
-- If the node connects to a transistor's gate and turns the
-- transistor on, this may cause one of the nodes on either
-- side to become fixed in value. If the transistor is now
-- turned off, then the source or drain may become fixed
-- because of the reduced "competition" for that node.
IF f.gate = node THEN
BEGIN
IF ((value=1) AND f.on1) OR ((value=0) AND f.on0) THEN
f.forcedOn ← TRUE
ELSE IF f.on1 OR f.on0 THEN f.forcedOff ← TRUE
ELSE LOOP;
IF f.flowFromDrain THEN checkNode[f.source];
IF f.flowFromSource THEN checkNode[f.drain];
END;
ENDLOOP;
END;
checkNode: PROC[node: Node] =
-- This procedure checks to see if a node is forced to a value,
-- and sets it if necessary. It operates in two stages. First, it
-- finds the strongest certain source of 0 or 1 for the node
-- (certain sources are those connected to the node by transistors
-- that are definitely turned on). Then it finds the strongest
-- possible source of the opposite value (a possible source is one
-- connected to the node by transistors that aren't definitely
-- turned off). If the certain source is stronger than any possible
-- opposition, then the node value is set. If in the process of all
-- this we discover that there are no transistors that can source
-- information to the node, then the procedure is called recursively
-- on all nodes that this node can source (this handles the case
-- where the bottom transistor of a NAND gate turns off).
BEGIN
p: Pointer;
f: Fet;
other: Node;
strength, strength2, value: INT;
simCount ← simCount+1;
-- Find strongest certain source of a 0 or 1.
strength ← 0;
FOR p ← node.firstPointer, p.next UNTIL p = NIL DO
f ← p.fet;
IF NOT (f.onAlways OR f.forcedOn) THEN LOOP;
IF f.source = node THEN
BEGIN
other ← f.drain;
IF NOT f.flowFromDrain THEN LOOP;
END
ELSE IF f.drain = node THEN
BEGIN
other ← f.source;
IF NOT f.flowFromSource THEN LOOP;
END
ELSE LOOP;
IF other.always0 THEN
BEGIN
IF Model.TypeTable[f.type].strengthLo > strength THEN
BEGIN
value ← 0;
strength ← Model.TypeTable[f.type].strengthLo;
END;
END
ELSE
BEGIN
IF Model.TypeTable[f.type].strengthHi > strength THEN
BEGIN
value ← 1;
strength ← Model.TypeTable[f.type].strengthHi;
END;
END;
ENDLOOP;
-- See if there is a potential source of the opposite signal that is
-- at least as strong as the certain source. If not, then it's OK to
-- set the node's value.
fSCount ← 0;
IF strength > 0 THEN
BEGIN
strength2 ← findStrength[node: node, value: 1-value,
beatThis: strength-1];
IF strength > strength2 THEN SetNodeValue[node, value];
END
ELSE
-- There's no certain source. Now see if there is any possible
-- source. If not, mark all the transistors that flow away from
-- this node as disconnected and call ourselves recursively to
-- handle the nodes on the other side.
IF (findStrength[node: node, value: 1, beatThis: 0] = 0) AND
(findStrength[node: node, value: 0, beatThis: 0] = 0) THEN
BEGIN
FOR p ← node.firstPointer, p.next UNTIL p=NIL DO
f ← p.fet;
IF f.source = node THEN
BEGIN
IF NOT f.flowFromSource THEN LOOP;
f.flowFromSource ← FALSE;
checkNode[f.drain];
END
ELSE IF f.drain = node THEN
BEGIN
IF NOT f.flowFromDrain THEN LOOP;
f.flowFromDrain ← FALSE;
checkNode[f.source];
END ;
ENDLOOP;
END;
END;
findStrength: PROC[node: Node, value: INT, beatThis: INT]
RETURNS [INT] =
-- This is a recursive local procedure that looks for the
-- strongest possible source of a zero or one signal in a
-- (potentially) connected piece of circuit. Only sources
-- stronger than beatThis are considered. If a stronger
-- source is found, it's strength is returned. Otherwise
-- beatThis is returned. If a negative value is returned,
-- it means that we looked and looked and eventually
-- gave up without finishing all the possibilities.
BEGIN
p: Pointer;
f: Fet;
other: Node;
strength, strength2: INT;
simCount ← simCount+1;
-- Abort gracefully if we can't finish the search with a
-- reasonable effort.
fSCount ← fSCount + 1;
IF fSCount >= FSLimit THEN
BEGIN
IO.PutF[StdOut, "Aborting simulation at %s:\n",
IO.rope[Printout.NodeRope[node]]];
IO.PutRope[StdOut, " It's taking too long to find all the "];
IO.PutRope[StdOut, "potential\n signal sources. You probably "];
IO.PutRope[StdOut, "need to add more\n flow control info. "];
IO.PutRope[StdOut, "A list of nodes in the\n area follows:\n"];
RETURN [-1];
END;
-- Use the inPath flag to avoid infinite recursion.
node.inPath ← TRUE;
-- Check all transistors whose sources or drains connect
-- to this node to see if they could potnetially provide a
-- strong enough source of the right value. This may involve
-- recursive calls.
FOR p ← node.firstPointer, p.next UNTIL p=NIL DO
f ← p.fet;
IF f.forcedOff THEN LOOP;
IF value = 0 THEN
strength ← Model.TypeTable[f.type].strengthLo
ELSE strength ← Model.TypeTable[f.type].strengthHi;
IF strength <= beatThis THEN LOOP;
IF f.source = node THEN
BEGIN
other ← f.drain;
IF NOT f.flowFromDrain THEN LOOP;
END
ELSE IF f.drain = node THEN
BEGIN
other ← f.source;
IF NOT f.flowFromSource THEN LOOP;
END
ELSE LOOP;
IF f.firstFlow # NIL THEN
IF NOT Flow.Lock[fet: f, input: other] THEN LOOP;
-- If an adjacent node is fixed in value, we know the
-- strength right away: it's the strength of the connecting
-- transistor. Otherwise we have to find the weakest
-- transistor in the path to a driven node. Be sure to
-- consider only nodes driven to the right value.
IF other.always1 OR other.always0 OR other.input THEN
BEGIN
IF value = 0 THEN
BEGIN
IF other.always1 THEN
BEGIN
IF f.firstFlow # NIL THEN Flow.Unlock[fet: f, input: other];
LOOP;
END;
END
ELSE IF other.always0 THEN
BEGIN
IF f.firstFlow # NIL THEN Flow.Unlock[fet: f, input: other];
LOOP;
END;
END
ELSE
BEGIN
IF other.inPath THEN
BEGIN
IF f.firstFlow # NIL THEN Flow.Unlock[fet: f, input: other];
LOOP;
END;
strength2 ← findStrength[node: other, value: value, beatThis: beatThis];
IF strength2 < 0 THEN
BEGIN
IO.PutF[StdOut, " %s\n", IO.rope[Printout.NodeRope[node]]];
node.inPath ← FALSE;
IF f.firstFlow # NIL THEN Flow.Unlock[fet: f, input: other];
RETURN [-1];
END;
IF strength2 < strength THEN strength ← strength2;
IF f.firstFlow # NIL THEN Flow.Unlock[fet: f, input: other];
END;
IF strength > beatThis THEN beatThis ← strength;
ENDLOOP;
node.inPath ← FALSE;
RETURN [beatThis];
END;
SetCmd: PUBLIC CmdProc =
BEGIN
ok: BOOLEAN;
IF args = NIL THEN
BEGIN
IO.PutRope[StdOut, "\"Set\" needs a 0/1 value and node names.\n"];
RETURN;
END;
[ok, ivalue] ← Parse.Int[args];
IF (NOT ok) OR (ivalue < 0) OR (ivalue > 1) THEN
BEGIN
IO.PutRope[StdOut, "Value must be 0 or 1.\n"];
RETURN;
END;
args ← args.next;
WHILE args # NIL DO
Hash.Enumerate[table: NodeTable, pattern: args.rope,
proc: setProc, errorStream: StdOut];
args ← args.next;
ENDLOOP;
END;
setProc: Hash.EnumProc =
BEGIN
node: Node ← NARROW[entry.clientData];
SetNodeValue[node, ivalue, TRUE];
END;
Stats: PUBLIC PROC[] =
BEGIN
IO.PutF[StdOut, "Total nodes set to 0: %d.\n",
IO.int[nodes0]];
IO.PutF[StdOut, "Total nodes set to 1: %d.\n",
IO.int[nodes1]];
IO.PutF[StdOut, "Total nodes marked dynamic: %d.\n",
IO.int[dynamic]];
IO.PutF[StdOut, "Total number of recursive simulation calls: %d.\n",
IO.int[simCount]];
END;
MarkDynamic: PUBLIC PROC[] =
BEGIN
Hash.Enumerate[table: NodeTable, pattern: "*", proc: markDynamicProc,
errorStream: StdOut];
END;
markDynamicProc: Hash.EnumProc =
BEGIN
p: Pointer;
f: Fet;
node: Node ← NARROW[entry.clientData];
node.dynamic ← FALSE;
IF node.input OR node.always0 OR node.always1 THEN RETURN;
FOR p ← node.firstPointer, p.next UNTIL p = NIL DO
f ← p.fet;
IF f.source = node THEN
BEGIN
IF NOT f.flowFromDrain THEN LOOP;
END
ELSE IF f.drain = node THEN
BEGIN
IF NOT f.flowFromSource THEN LOOP;
END
ELSE LOOP;
IF NOT f.forcedOff THEN RETURN;
ENDLOOP;
node.dynamic ← TRUE;
dynamic ← dynamic + 1;
IF SeeDynamic THEN IO.PutF[StdOut, "%s is dynamic.\n",
IO.rope[Printout.NodeRope[node]]];
END;
END.