-- File: SimMOSRun.mesa
-- Adapted by Martin Newell, January 1980, from MIT’s MOSSIM
-- Last Updated: December 4, 1980 12:47 PM

--
For DStar compile using the /l switch

DIRECTORY
InlineDefs: FROM "InlineDefs" USING[BITSHIFT
, BITAND, BITOR],
IODefs: FROM "IODefs" USING[WriteString, WriteLine, WriteChar,
CR, SP],
SimMOSAccessDefs: FROM "SimMOSAccessDefs" USING[SetNodeFlags, SetNodeEquivClass, GetNodeFlags, GetNodeEquivClass, GetNodeName, GetNodeNext, SetTransNext, GetTransSource, GetTransDrain, GetTransNext],
SimMOSDefs: FROM "SimMOSDefs" USING[Node, Transistor],
SimMOSUtilitiesDefs
: FROM "SimMOSUtilitiesDefs" USING[OnP, OffP, SimETrans, SimXTrans, SimDTrans, InputP, PullupP, StorageP, GateP, HasInputGnd, NewXFlag, NewGndFlag, OldGndFlag, NewVddFlag, OldVddFlag, NewVddGndFlags, OldVddGndFlags, NewToOldFlags, RootFlag, PermanentAndOldFlags, NewFlags1ToOldFlags2, SimAtomsLength, SimAtoms, HasInputVdd, HasPullup, HasGateVdd, HasGateGnd, DescriptorFlags, RootP, NewFlags, OldFlags, AllButRootFlag, CopyOldToNewFlags, PrintNode, SetDescriptor];

SimMOSRun: PROGRAM
IMPORTS InlineDefs, IODefs,
SimMOSAccessDefs, SimMOSUtilitiesDefs
EXPORTS
SimMOSDefs =
BEGIN OPEN InlineDefs, IODefs,
SimMOSAccessDefs, SimMOSDefs, SimMOSUtilitiesDefs;

SimSolve: PUBLIC PROCEDURE[abort: PROCEDURE RETURNS[BOOLEAN]] =
-- step - run simulation until no changes are detected, or abort returns TRUE
BEGIN
changed,shorts: CARDINAL;
DO
[changed, shorts] ← MicroStep[FALSE,FALSE];
IF changed=0 OR abort[] THEN EXIT;
ENDLOOP;
IF changed=0 AND shorts#0 THEN [,] ← MicroStep[FALSE,TRUE];
END;

--An Equivalence Class, EC, is a set of nodes connected by ON enhancement mode transistors
--The equivalence class is maintained as an inverted tree with all chains leading to
--the Root, which has the RootFlag set. The Root is always the highest precedence
--member of the class, where input>pullup>storage>other.

--A SuperClass, SC, is a set of equivalence classes that are connected by X transistors.
--This is also an inverted tree, in which the Root is identified by pointing to itself.
--The Root of a superclass is not necessarily the highest precedence member, but it
--contains a descriptor of the set of types and values of its members. These are
--used to establish possible conflicts if any subset of the X transistors was on.

MicroStep: PUBLIC PROCEDURE[reportChanges,reportShorts: BOOLEAN]
RETURNS[changed,shorts: CARDINAL] =
-- Execute one simulation iteration.
-- Upon entry, it is assumed
-- that the potential flags reflect the last simulation step and that
-- each node has been made equivalent to itself.
BEGIN
nptr,next: Node;
tptr,tnext,tprev: Transistor;
i: CARDINAL;
MakeEquiv: PROCEDURE [node1,node2: Node] RETURNS[BOOLEAN] =
--Merge equivalence classes of node1 and node2. Merges which short
-- two inputs having different values are not allowed
BEGIN
node1EC,node2EC: Node;
Equiv: PROCEDURE [class1,class2: Node] =
--make class1 point to class2, as well as node1 and node2
--remove RootFlag from class1 - beware side-effects!
BEGIN
class1Flags: WORD ← GetNodeFlags[class1];
class1Flags ← AllButRootFlag[class1Flags];
SetNodeFlags[class1,class1Flags];
SetNodeEquivClass[node1,class2];
SetNodeEquivClass[node2,class2];
SetNodeEquivClass[class1,class2];
END;

--find classes of node1 and node2
FOR node1EC ← node1, GetNodeEquivClass[node1EC] UNTIL RootP[node1EC]
DO ENDLOOP;
FOR node2EC ← node2, GetNodeEquivClass[node2EC] UNTIL RootP[node2EC]
DO ENDLOOP;
--see if already in same class, and shorten chains if so
IF node1EC=node2EC THEN
BEGIN
SetNodeEquivClass[node1,node1EC];
SetNodeEquivClass[node2,node1EC];
RETURN[TRUE];
END;
--make classes equivalent, retaining higher precedence at root
SELECT TRUE FROM
InputP[node1EC] =>
BEGIN
node1ECFlags: WORD ← GetNodeFlags[node1EC];
node2ECFlags: WORD ← GetNodeFlags[node2EC];
IF InputP[node2EC] AND OldFlags[node1ECFlags]#OldFlags[node2ECFlags] THEN
BEGIN --short between two inputs having different values
shorts ← shorts + 1;
IF reportShorts THEN
BEGIN
WriteLine["Attempt to connect two inputs"];
WriteString["Nodes are: "]; PrintNode[node1];
IF node1#node1EC THEN
BEGIN
WriteString[" equivalent to input: "];
PrintNode[node1EC];
END;
WriteChar[CR];
WriteString["and: "]; PrintNode[node2];
IF node2#node2EC THEN
BEGIN
WriteString[" equivalent to input: "];
PrintNode[node2EC];
END;
WriteChar[CR];
END;
RETURN[FALSE];
END
ELSE Equiv[node2EC,node1EC];
END;
PullupP[node1EC] =>
BEGIN
IF InputP[node2EC]
THEN Equiv[node1EC,node2EC]
ELSE Equiv[node2EC,node1EC];
END;
StorageP[node1EC] =>
BEGIN
IF InputP[node2EC] OR PullupP[node2EC]
THEN Equiv[node1EC,node2EC]
ELSE Equiv[node2EC,node1EC];
END;
ENDCASE => --node1EC is weakest possible
Equiv[node1EC,node2EC];
RETURN[TRUE];
END;

shorts ← 0;
changed ← 0;

-- for each enhancement mode transistor, if its gate is on
-- make the source and drain nodes equivalent.
-- If gate is x then move transistor to x list for subsequent scan
tprev ← NIL;
FOR tptr ← SimETrans, tnext UNTIL tptr=NIL DO
tnext ← GetTransNext[tptr];
SELECT TRUE FROM
OnP[tptr] =>
[] ← MakeEquiv[GetTransSource[tptr], GetTransDrain[tptr]];
~OffP[tptr] => --i.e. x - move to SimXTrans
BEGIN
IF tprev=NILTHEN SimETrans ← tnext
ELSE SetTransNext[tprev,tnext];
SetTransNext[tptr,SimXTrans];
SimXTrans ← tptr;
LOOP;--i.e. leave tprev alone
END;
ENDCASE; --i.e. transistor is off
tprev ← tptr;
ENDLOOP;

-- treat each depletion mode transistor as wire - i.e. join source and drain
-- remember that only non-pullups are explicitly represented
FOR tptr ← SimDTrans, GetTransNext[tptr] UNTIL tptr=NIL DO
[] ← MakeEquiv[GetTransSource[tptr], GetTransDrain[tptr]];
ENDLOOP;

--grow superclasses of classes joined by X transistors
--propogate descriptor of members of superclass (types & values)
--merge towards drain (arbitrary choice)
tprev ← NIL; --will be left pointing to last member
FOR tptr ← SimXTrans, GetTransNext[tptr] UNTIL tptr=NIL DO
BEGIN
source: Node ← GetTransSource[tptr];
drain: Node ← GetTransDrain[tptr];
sourceSC,drainSC: Node;
sourceSCFlags,drainSCFlags: WORD;
--get superclasses of source and drain (next=self)
--Source:
FOR sourceSC ← source, next DO
next ← GetNodeEquivClass[sourceSC];
IF next=sourceSC THEN EXIT;
ENDLOOP;
sourceSCFlags ← GetNodeFlags[sourceSC];
--and Drain:
FOR drainSC ← drain, next DO
next ← GetNodeEquivClass[drainSC];
IF next=drainSC THEN EXIT;
ENDLOOP;
drainSCFlags ← GetNodeFlags[drainSC];
--merge classes
--avoid merging if either SC is of type input, but always propogate
-- descriptor to non-input class, if any
IF InputP[sourceSC] THEN
BEGIN --don’t merge classes, investigate propogating descriptor to drainSC
IF ~InputP[drainSC] THEN
BEGIN
drainSCFlags ← BITOR[drainSCFlags,DescriptorFlags[sourceSCFlags]];
SetNodeFlags[drainSC,drainSCFlags];
END;
END
ELSE
BEGIN
IF InputP[drainSC] THEN
BEGIN --don’t merge classes, but do propogate descriptor to source
sourceSCFlags ← BITOR[sourceSCFlags,DescriptorFlags[drainSCFlags]];
SetNodeFlags[sourceSC,sourceSCFlags];
END
ELSE
BEGIN --neither is input - merge classes towards drain
drainSCFlags ← BITOR[drainSCFlags,DescriptorFlags[sourceSCFlags]];
SetNodeFlags[drainSC,drainSCFlags];
--merge classes towards drainSC
SetNodeEquivClass[sourceSC,drainSC];
END;
END;
tprev ← tptr;
END;
ENDLOOP;
--Return X transistors to SimETrans:
IF SimXTrans#NIL THEN
BEGIN
SetTransNext[tprev,SimETrans];
SimETrans ← SimXTrans;
SimXTrans ← NIL;
END;

-- Run through all the nodes, and determine their values
-- by chasing to the root of the equiv class. A second loop resets
-- the NewFlags and equivalence classes for next iteration.
FOR i IN [0..SimAtomsLength) DO
FOR nptr ← SimAtoms[i], GetNodeNext[nptr] UNTIL nptr=NIL DO
BEGIN
nptrFlags: WORD ← GetNodeFlags[nptr];
nptrEC,nptrSC: Node;
nptrECFlags,nptrSCFlags: WORD;
--find root of equivalence class, nptrEC
FOR nptrEC ← nptr,GetNodeEquivClass[nptrEC] UNTIL RootP[nptrEC]
DO ENDLOOP;
nptrECFlags ← GetNodeFlags[nptrEC];
--find root of superclass, nptrSC
FOR nptrSC ← nptrEC, next DO
next ← GetNodeEquivClass[nptrSC];
IF next=nptrSC THEN EXIT;
ENDLOOP;
nptrSCFlags ← GetNodeFlags[nptrSC];
--set nominal value of EC, if not already set, depending on type of the equiv class
IF NewFlags[nptrECFlags]=0 THEN
BEGIN
SELECT TRUE FROM
InputP[nptrEC] => --take its value regardless of superclass
nptrECFlags ← CopyOldToNewFlags[nptrECFlags];
PullupP[nptrEC] => --check SC for HasInputGnd
nptrECFlags ←IF BITAND[nptrSCFlags,HasInputGnd]#0
THEN BITOR[nptrECFlags, NewXFlag]
ELSE BITOR[nptrECFlags, NewVddFlag];
StorageP[nptrEC] =>
nptrECFlags ←
SELECT TRUE FROM
BITAND[nptrECFlags,OldGndFlag]#0 =>
--check if InputVdd, Pullup or GateVdd in SC
IFBITAND[nptrSCFlags,HasInputVdd+HasPullup+HasGateVdd]#0
THEN BITOR[nptrECFlags, NewXFlag]
ELSE BITOR[nptrECFlags, NewGndFlag],
BITAND[nptrECFlags,OldVddFlag]#0 =>
--check for InputGnd or GateGnd in SC
IFBITAND[nptrSCFlags,HasInputGnd+HasGateGnd]#0
THEN BITOR[nptrECFlags, NewXFlag]
ELSE BITOR[nptrECFlags, NewVddFlag],
ENDCASE => BITOR[nptrECFlags, NewXFlag];
ENDCASE => nptrECFlags ← BITOR[nptrECFlags, NewXFlag];
SetNodeFlags[nptrEC,nptrECFlags];
END;
--check for charge sharing between nptr and nptrEC
IF StorageP[nptr] AND StorageP[nptrEC] AND OldFlags[nptrFlags]#OldFlags[nptrECFlags]
THEN
BEGIN -- charge sharing
WriteLine["Charge sharing"];
WriteString["Nodes are: "]; PrintNode[nptr];
WriteString[" and "]; PrintNode[nptrEC];
WriteChar[CR];
--set EC to X
--*should make another pass to set to X those nodes in
--this EC that have already been processed
nptrECFlags ← BITOR[PermanentAndOldFlags[nptrECFlags], NewXFlag];
SetNodeFlags[nptrEC,nptrECFlags];
END;
--copy value from EC into nptr
nptrFlags ← NewFlags1ToOldFlags2[nptrECFlags,nptrFlags];
SetNodeFlags[nptr,nptrFlags];
END;
ENDLOOP;
ENDLOOP;


--now visit all nodes again to move new values to old and reset equivalence classes
IF reportChanges THEN WriteString["Nodes changing:"];
FOR i IN [0..SimAtomsLength) DO
FOR nptr ← SimAtoms[i], GetNodeNext[nptr] UNTIL nptr=NIL DO
BEGIN
nptrFlags: WORD ← GetNodeFlags[nptr];
IF GateP[nptr] AND NewVddGndFlags[nptrFlags]#OldVddGndFlags[nptrFlags] THEN
BEGIN
changed ← changed + 1;
IF reportChanges THEN
BEGIN
nodeName: STRING ← [50];
WriteChar[SP];
WriteString[GetNodeName[nptr,nodeName]];
END;
END;
nptrFlags ← NewToOldFlags[nptrFlags];
nptrFlags ← BITOR[nptrFlags, RootFlag];
nptrFlags ← SetDescriptor[nptrFlags];
SetNodeFlags[nptr, nptrFlags];
SetNodeEquivClass[nptr, nptr];
END;
ENDLOOP;
ENDLOOP;
IF reportChanges THEN WriteChar[CR];

RETURN[changed, shorts]
END;

END.