RoseRunImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reversed.
Barth, August 1, 1985 2:31:39 pm PDT
Spreitzer, November 18, 1985 10:12:40 pm PST
DIRECTORY Core, CoreFlatten, IO, PrincOps, PrincOpsUtils, Rope, RoseBehavior, RoseBind, RoseBindPrivate, RoseControl, RoseEngine, RoseEvents, RosePrivates, RoseSimTypes, RoseWireTwiddling, RoseWireTypes, RoseWiring;
RoseRunImpl: CEDAR PROGRAM
IMPORTS PrincOpsUtils, RoseEngine, RoseEvents, RoseSimTypes, RoseWireTwiddling, RoseWireTypes
EXPORTS RoseBind, RoseControl, RoseEngine
=
Simulator Assumptions
A cell's newIO doesn't change, except when the cell's ValsChanged or EvalSimple is called, or when this module changes it.
An effective port is transduced iff the node connected to it is switch-level and the cell type speaks simple.
Simulator Invariants
All simple effective ports are always linked according to their curStrength, which tries to track what the cell is saying, but may get behind.
For simple ports, the cell's newIO always contains the current value.
For transduced effective ports, the newIO and switchIO fields are always related by Transduce.
Simulator Theorems
You can't do a non-steady-state simulation when you have any x-phobic wires.
BEGIN OPEN RoseWireTypes, RoseSimTypes, RoseEngine;
BehaviorClassRec: PUBLIC TYPE = RoseBindPrivate.BehaviorClassRec;
SimulationRec: PUBLIC TYPE = RoseSimTypes.SimulationRec;
Error: PUBLIC ERROR [msg: ROPE, data: REF ANYNIL] = CODE;
Warning: PUBLIC SIGNAL [msg: ROPE, data: REF ANYNIL] = CODE;
Stop: PUBLIC SIGNAL [msg: ROPE, data: REF ANYNIL] = CODE;
StepType: TYPE = {noStep, switchStep, otherStep};
stop: BOOLFALSE;
Initialize: PUBLIC PROC [simulation: Simulation, steady: BOOL] = {
sch: Scheduling = simulation.sch;
bbTableSpace: PrincOps.BBTableSpace;
bbTable: PrincOps.BitBltTablePtr;
InitWire: PROC [w: Wire] = {
wg: WireGroup ← GetWireGroup[w];
SELECT wg FROM
belowLeaf => ERROR;
aboveLeaf => {
IF StructureOfWire[w] = atom THEN ERROR;
FOR i: INT IN [0 .. w.elements.size) DO
InitWire[w.elements[i]];
ENDLOOP;
w ← w;
};
leaf => {
rw: RoseWire ← GetRoseWire[simulation, w];
IF rw.XPhobic AND NOT steady THEN ERROR;
rw.type.class.super.Initialize[
rw.type,
rw.valPtr,
steady];
rw.winnerStrength ← rw.holdStrength ← rw.capStrength;
FOR s: Strength IN (none .. LAST[Strength]] DO
w ← w;
WHILE rw.byStrength[s].first # nilSlot DO
ReStrengthLink[rw, rw.byStrength[s].first, none];
ENDLOOP;
w ← w;
ENDLOOP;
rw.nextPerturbed ← rw.nextAffected ← rw.nextDelayed ← rw.prevDelayed ← notInWireList;
};
ENDCASE => ERROR;
};
StartWire: PROC [w: Wire] = {
wg: WireGroup ← GetWireGroup[w];
SELECT wg FROM
belowLeaf => ERROR;
aboveLeaf => {
IF StructureOfWire[w] = atom THEN ERROR;
FOR i: INT IN [0 .. w.elements.size) DO
StartWire[w.elements[i]];
ENDLOOP;
w ← w;
};
leaf => {
rw: RoseWire ← GetRoseWire[simulation, w];
SELECT rw.type.class.super.flavor FROM
switch => PerturbWire[rw, nilModelSlot];
simple => UpdateCurrent[rw, bbTable, nilModelSlot];
drive => ERROR;
ENDCASE => ERROR;
};
ENDCASE => ERROR;
};
TRUSTED {bbTable ← PrincOpsUtils.AlignedBBTable[@bbTableSpace]};
IF FIRST[Strength] # none THEN ERROR;
IF sch.running THEN ERROR;
sch.schedFirst ← sch.schedLast ← sch.firstNeeded ← NIL;
sch.firstPerturbed ← sch.firstAffected ← sch.firstDelayed ← sch.lastDelayed ← NIL;
InitWire[simulation.rootRecordType.internal];
simulation ← simulation;
FOR cil: CellInstanceList ← simulation.rootRecordType.instances, cil.rest WHILE cil # NIL DO
ci: CellInstance = cil.first;
rci: RoseCellInstance = GetRoseCell[simulation, ci];
rci.schedNext ← rci.nextNeeded ← rci.nextNoted ← notInCellList;
rci.affectedFlags ← ALL[FALSE];
rci.initQed ← rci.propQed ← rci.initUDed ← rci.propUDed ← FALSE;
ScheduleCell[rci];
FOR epi: EffectivePortIndex IN [0 .. rci.effectivePorts.length) DO
rw: RoseWire = rci.connectedWires[epi];
drivePtr: Ptr = rci.effectivePorts[epi].newDrive;
targType: RoseWireType = rci.effectivePorts[epi].implType;
modelType: RoseWireType = rci.effectivePorts[epi].type;
portPtr: Ptr = SELECT targType.class.super.flavor FROM
simple => rci.effectivePorts[epi].newSimple,
switch => rci.effectivePorts[epi].switch,
drive => ERROR,
ENDCASE => ERROR;
SELECT modelType.class.super.flavor FROM
simple => NULL;
switch => LOOP;
drive => ERROR;
ENDCASE => ERROR;
SELECT targType.class.super.flavor FROM
simple => IF rci.effectivePorts[epi].curStrength # none THEN ERROR;
switch => NULL;
drive => ERROR;
ENDCASE => ERROR;
RoseWireTwiddling.WriteDrive[drivePtr, none];
RoseWireTwiddling.CopyVal[
fromT: rw.type,
fromP: rw.valPtr,
toT: targType,
toP: portPtr,
bbTable: bbTable];
IF rci.effectivePorts[epi].transduced THEN {
modelPtr: Ptr = rci.effectivePorts[epi].newSimple;
IF modelPtr = portPtr THEN ERROR;
targType.class.super.Transduce[
fromS: FIRST[Strength],
fromT: targType,
fromP: portPtr,
toT: modelType,
toP: modelPtr];
modelType.class.super.Transduce[
fromS: none,
fromT: modelType,
fromP: modelPtr,
toT: targType,
toP: portPtr];
};
ENDLOOP;
simulation ← simulation;
ENDLOOP;
StartWire[simulation.rootRecordType.internal];
simulation ← simulation;
};
Settle: PUBLIC PROC [simulation: Simulation] = {
simulation ← simulation;
WHILE StepSim[simulation] # noStep DO
IF stop THEN SIGNAL Stop[msg: "Stopped", data: simulation];
ENDLOOP;
simulation ← simulation;
};
StepSim: PROC [sim: Simulation] RETURNS [stepType: StepType] =
BEGIN
SELECT stepType ← Next[sim] FROM
noStep => NULL;
switchStep => SwitchStepSim[sim];
otherStep => SimpleStepSim[sim];
ENDCASE => ERROR;
IF stepType # noStep AND Next[sim] = noStep THEN {
RoseEvents.Notify[event: $Settled, watched: sim];
CheckDelays[sim];
};
END;
Next: PUBLIC PROC [sim: Simulation] RETURNS [stepType: StepType] =
BEGIN
sch: Scheduling = sim.sch;
IF sch.schedFirst # NIL THEN RETURN [otherStep];
IF sch.firstPerturbed # NIL THEN RETURN [switchStep];
stepType ← noStep;
END;
CheckDelays: PROC [sim: Simulation] = {
sch: Scheduling = sim.sch;
wires: RoseWireList ← NIL;
FOR rw: RoseWire ← sch.firstDelayed, rw.nextDelayed WHILE rw # NIL DO
IF rw = notInWireList THEN ERROR;
wires ← CONS[rw, wires];
PerturbWire[rw, nilModelSlot];
ENDLOOP;
IF wires # NIL THEN SIGNAL Warning["Wires want Xes", wires];
};
SimpleStepSim: PROC [sim: Simulation] = {
sch: Scheduling = sim.sch;
bbTableSpace: PrincOps.BBTableSpace;
bbTable: PrincOps.BitBltTablePtr;
TRUSTED {bbTable ← PrincOpsUtils.AlignedBBTable[@bbTableSpace]};
IF sch.running THEN ERROR ELSE sch.running ← TRUE;
BEGIN ENABLE UNWIND => {sch.running ← FALSE};
cell: RoseCellInstance;
IF sch.schedFirst = notInCellList THEN ERROR;
cell ← PickFromSchedule[sch];
IF cell.type.behaviorClass.details.EvalSimple # NIL THEN
BEGIN
Perturb: PROC [portPath: PortPath] = {
PerturbPP[cell.core.actual, portPath, sch, [cell, portPath], FALSE];
};
PreSimple[cell, bbTable];
cell.type.behaviorClass.details.EvalSimple[
argsAny: cell.args,
switchAny: cell.switchIO,
simpleAny: cell.newIO,
strengthAny: cell.newDrive,
stateAny: cell.state,
perturb: Perturb
!UNWIND => {
ReallyCleanUpAfterModify[cell, sch, TRUE, FALSE, FALSE, bbTable];
Schedule[cell, sch];
}
];
ReallyCleanUpAfterModify[cell, sch, TRUE, FALSE, FALSE, bbTable];
RoseEvents.Notify[event: $Eval, watched: cell];
END;
END;
sch.running ← FALSE;
};
PerturbPP: PROC [w: Wire, portPath: PortPath, sch: Scheduling, agitator: ModelSlot, evenIfInput: BOOL] = {
wg: WireGroup = GetWireGroup[w];
SELECT wg FROM
belowLeaf => ERROR;
leaf => ReallyPerturb[GetRoseWire[sch.sim, w], sch, sch.sim, agitator, evenIfInput];
aboveLeaf => PerturbPP[w.elements[portPath.first], portPath.rest, sch, agitator, evenIfInput];
ENDCASE => ERROR;
};
PickFromSchedule: PROC [sch: Scheduling] RETURNS [rci: RoseCellInstance] =
BEGIN
rci ← sch.schedFirst;
IF (sch.schedFirst ← rci.schedNext) = NIL THEN sch.schedLast ← NIL;
rci.schedNext ← notInCellList;
RoseEvents.Notify[event: $Schedule, watched: rci];
IF sch.schedFirst = NIL THEN RETURN;
RoseEvents.Notify[event: $Schedule, watched: sch.schedFirst];
END;
ScheduleCell: PUBLIC PROC [cell: RoseCellInstance] =
{Schedule[cell, cell.schIn]};
Schedule: PROC [rci: RoseCellInstance, sch: Scheduling] =
BEGIN
IF rci.schedNext # notInCellList THEN RETURN;
rci.schedNext ← NIL;
IF sch.schedLast = NIL THEN sch.schedFirst ← rci ELSE sch.schedLast.schedNext ← rci;
sch.schedLast ← rci;
RoseEvents.Notify[event: $Schedule, watched: rci];
END;
PreSimple: PROC [rci: RoseCellInstance, bbTable: PrincOps.BitBltTablePtr, reallyCopy: BOOLTRUE] =
BEGIN
IF reallyCopy AND rci.hasTransducedPort THEN {
simpleType: RoseWireType = rci.type.wireTypes[simple];
driveType: RoseWireType = rci.type.wireTypes[drive];
RoseWireTwiddling.CopyVal[
fromT: simpleType,
toT: simpleType,
fromP: RoseWireTwiddling.RefToPtr[rci.newIO, simpleType],
toP: RoseWireTwiddling.RefToPtr[rci.oldIO, simpleType],
bbTable: bbTable
];
RoseWireTwiddling.CopyVal[
fromT: driveType,
toT: driveType,
fromP: RoseWireTwiddling.RefToPtr[rci.newDrive, driveType],
toP: RoseWireTwiddling.RefToPtr[rci.oldDrive, driveType],
bbTable: bbTable
];
};
END;
ReallyCleanUpAfterModify: PROC [rci: RoseCellInstance, sch: Scheduling, outputsOnly, blindly, mayRescheduleSelf: BOOLEAN, bbTable: PrincOps.BitBltTablePtr] =
BEGIN
FOR effectivePortIndex: EffectivePortIndex IN [0 .. rci.effectivePorts.length) DO
ep: EffectivePort = rci.effectivePorts[effectivePortIndex];
newPtr: Ptr = ep.newSimple;
rw: RoseWire = rci.connectedWires[effectivePortIndex];
newStrength: Strength;
valDiff, strengthDiff: BOOLEANFALSE;
simpleBits: NAT;
SELECT ep.type.class.super.flavor FROM
simple => NULL;
switch => LOOP;
drive => ERROR;
ENDCASE => ERROR;
newStrength ← RoseWireTwiddling.ReadDrive[ep.newDrive];
IF (NOT ep.output) AND newStrength > none THEN ERROR Error["Someone lied about outputness of port"];
IF outputsOnly AND NOT ep.output THEN LOOP;
simpleBits ← ep.type.class.super.Bits[ep.type.class];
IF ep.transduced THEN {
oldStrength: Strength = RoseWireTwiddling.ReadDrive[ep.oldDrive];
oldPtr: Ptr = ep.oldSimple;
IF blindly THEN valDiff ← strengthDiff ← TRUE
ELSE {
strengthDiff ← oldStrength # newStrength;
valDiff ← NOT RoseWireTwiddling.EqualVal[ep.type, ep.type, oldPtr, newPtr];
};
IF valDiff OR strengthDiff THEN {
implPtr: Ptr = ep.switch;
ep.type.class.super.Transduce[fromS: newStrength, fromT: ep.type, toT: ep.implType, fromP: newPtr, toP: implPtr];
};
IF (valDiff AND newStrength >= rw.holdStrength) OR (strengthDiff AND (newStrength >= rw.holdStrength OR oldStrength >= rw.holdStrength)) THEN {
ReallyPerturb[rw: rw, sch: sch, sim: sch.sim, agitator: [rci, ep.path]];
};
}
ELSE {
handled: BOOLFALSE;
strengthDiff ← newStrength # rci.effectivePorts[effectivePortIndex].curStrength;
valDiff ← NOT RoseWireTwiddling.EqualVal[ep.type, rw.type, newPtr, rw.valPtr];
IF strengthDiff THEN {
ReStrengthLink[rw, [rci, effectivePortIndex], newStrength];
IF EmptyStrengthSet[rw, rw.winnerStrength] OR newStrength > rw.winnerStrength THEN {
UpdateCurrent[rw, bbTable, [rci, ep.path]];
handled ← TRUE};
};
IF newStrength > rw.winnerStrength THEN ERROR;
IF newStrength = rw.winnerStrength AND NOT handled THEN {
IF valDiff THEN {
ep: EffectivePort = rci.effectivePorts[effectivePortIndex];
another: Slot =
IF ep.strengthNext # head THEN ep.strengthNext ELSE
IF ep.strengthPrev # head THEN ep.strengthPrev ELSE
nilSlot;
alone: BOOL ← another = nilSlot AND newStrength > rw.holdStrength;
IF alone THEN --alone at the top-- {
Distribute[
baseType: ep.type,
base: newPtr,
fromWire: rw,
avoidCell: IF NOT mayRescheduleSelf THEN rci ELSE NIL,
sch: sch,
bbTable: bbTable,
agitator: [rci, ep.path]];
}
ELSE SIGNAL Warning["Inconsistency"];
};
};
IF newStrength < rw.winnerStrength THEN {
RoseWireTwiddling.CopyVal[fromT: rw.type, fromP: rw.valPtr, toT: ep.type, toP: newPtr, bbTable: bbTable];
};
};
blindly ← blindly;
ENDLOOP;
blindly ← blindly;
END;
ReStrengthLink: PROC [rw: RoseWire, slot: Slot, newStrength: Strength] = {
t: Slot;
eps: EffectivePortS ← slot.cell.effectivePorts;
IF eps[slot.effectivePortIndex].curStrength IN Strength THEN {
oldStrength: Strength ← eps[slot.effectivePortIndex].curStrength;
IF (t ← eps[slot.effectivePortIndex].strengthNext) # head THEN {
IF t = slot THEN ERROR;
t.cell.effectivePorts[t.effectivePortIndex].strengthPrev ← eps[slot.effectivePortIndex].strengthPrev
}
ELSE rw.byStrength[oldStrength].last ← eps[slot.effectivePortIndex].strengthPrev;
IF (t ← eps[slot.effectivePortIndex].strengthPrev) # head THEN {
IF t = slot THEN ERROR;
t.cell.effectivePorts[t.effectivePortIndex].strengthNext ← eps[slot.effectivePortIndex].strengthNext
}
ELSE rw.byStrength[oldStrength].first ← eps[slot.effectivePortIndex].strengthNext;
};
eps[slot.effectivePortIndex].strengthNext ← rw.byStrength[newStrength].first;
eps[slot.effectivePortIndex].strengthPrev ← head;
IF (t ← rw.byStrength[newStrength].first) # head
THEN t.cell.effectivePorts[t.effectivePortIndex].strengthPrev ← slot
ELSE rw.byStrength[newStrength].last ← slot;
rw.byStrength[newStrength].first ← slot;
eps[slot.effectivePortIndex].curStrength ← newStrength;
};
EmptyStrengthSet: PROC [rw: RoseWire, strength: Strength] RETURNS [empty: BOOL] =
{empty ← rw.byStrength[strength] = emptyHead};
UpdateCurrent: PROC [rw: RoseWire, bbTable: PrincOps.BitBltTablePtr, agitator: ModelSlot] = {
aPtr: Ptr ← nilPtr;
aType: RoseWireType ← NIL;
SELECT rw.type.class.super.flavor FROM
simple => NULL;
switch, drive => ERROR;
ENDCASE => ERROR;
FOR s: Strength DECREASING IN Strength DO
IF (NOT EmptyStrengthSet[rw, s]) OR (s = rw.holdStrength) THEN {rw.winnerStrength ← s; EXIT};
ENDLOOP;
IF rw.winnerStrength = rw.holdStrength THEN RETURN;
IF rw.winnerStrength < rw.holdStrength THEN ERROR;
FOR s: Slot ← rw.byStrength[rw.winnerStrength].first, s.cell.effectivePorts[s.effectivePortIndex].strengthNext WHILE s # head DO
ePtr: Ptr = s.cell.effectivePorts[s.effectivePortIndex].newSimple;
eType: RoseWireType = s.cell.effectivePorts[s.effectivePortIndex].type;
SELECT aPtr FROM
=nilPtr => {aPtr ← ePtr; aType ← eType};
#nilPtr => {
IF NOT RoseWireTwiddling.EqualVal[aType, eType, aPtr, ePtr] THEN SIGNAL Warning["Inconsistency"];
};
ENDCASE => ERROR;
ENDLOOP;
IF aPtr = nilPtr THEN ERROR;
Distribute[
baseType: aType,
base: aPtr,
fromWire: rw,
avoidStrength: rw.winnerStrength,
sch: rw.schIn,
bbTable: bbTable,
agitator: agitator
];
};
Distribute: PROC
[
baseType: RoseWireType,
base: Ptr,
fromWire: RoseWire,
avoidCell: RoseCellInstance ← NIL,
avoidStrength: Drive ← test,
sch: Scheduling,
bbTable: PrincOps.BitBltTablePtr,
agitator: ModelSlot] =
BEGIN
SELECT fromWire.type.class.super.flavor FROM
simple => NULL;
switch, drive => ERROR;
ENDCASE => ERROR;
IF base # fromWire.valPtr THEN RoseWireTwiddling.CopyVal[fromT: baseType, fromP: base, toT: fromWire.type, toP: fromWire.valPtr, bbTable: bbTable];
FOR s: Strength IN Strength DO
IF avoidStrength # s THEN FOR dest: Slot ← fromWire.byStrength[s].first, dest.cell.effectivePorts[dest.effectivePortIndex].strengthNext WHILE dest # head DO
dep: EffectivePort ← dest.cell.effectivePorts[dest.effectivePortIndex];
RoseWireTwiddling.CopyVal[fromT: baseType, fromP: base, toT: dep.type, toP: dep.newSimple, bbTable: bbTable];
IF dest.cell # avoidCell THEN Schedule[dest.cell, sch];
ENDLOOP;
ENDLOOP;
reportSlot^ ← agitator;
RoseEvents.Notify[event: $ChangeEarly, watched: fromWire, arg: reportSlot];
RoseEvents.Notify[event: $ChangeLate, watched: fromWire, arg: reportSlot];
END;
SwitchStepSim: PROC [sim: Simulation] =
BEGIN
sch: Scheduling = sim.sch;
bbTableSpace: PrincOps.BBTableSpace;
bbTable: PrincOps.BitBltTablePtr;
TRUSTED {bbTable ← PrincOpsUtils.AlignedBBTable[@bbTableSpace]};
StrFindVicinity[sch, FALSE];
StrInitQ[sch];
StrPropQWork[sch];
StrInitUD[sch];
StrPropUDWork[sch];
StrFinalUD[sch, bbTable];
END;
StrFindVicinity: PROC [sch: Scheduling, evenIfInput: BOOL] =
BEGIN
this: RoseWire;
WHILE sch.firstPerturbed # NIL DO
IF sch.firstPerturbed = notInWireList THEN ERROR;
this ← sch.firstPerturbed;
sch.firstPerturbed ← this.nextPerturbed;
this.nextPerturbed ← notInWireList;
ReallyFindVicinity[sch, this, this.isInput, nilModelSlot];
ENDLOOP;
END;
ReallyFindVicinity: PROC [sch: Scheduling, rw: RoseWire, evenIfInput: BOOL, from: ModelSlot] =
BEGIN
IF rw.found THEN RETURN;
IF rw.isInput AND NOT evenIfInput THEN RETURN;
rw.found ← TRUE;
IF sch.firstAffected = notInWireList THEN ERROR;
rw.nextAffected ← sch.firstAffected;
sch.firstAffected ← rw;
reportSlot^ ← from;
NoteNews[$Found, rw, reportSlot];
FOR sl: SlotList ← rw.switchConnections, sl.rest WHILE sl # NIL DO
s: Slot = sl.first;
rci: RoseCellInstance = s.cell;
RecursivelyFindVicinity: PROC [portPath: PortPath] = {
FindPPVicinity[sch, rci.core.actual, portPath, evenIfInput, [s.cell, portPath]];
};
IF rci.type.behaviorClass.details.EnumerateVicinity # NIL THEN rci.type.behaviorClass.details.EnumerateVicinity[
argsAny: rci.args,
stateAny: rci.state,
portPath: rci.effectivePorts[s.effectivePortIndex].path,
evenIfInput: evenIfInput,
consume: RecursivelyFindVicinity
];
rci.affectedFlags[Specialty[s]] ← TRUE;
ENDLOOP;
END;
FindPPVicinity: PROC [sch: Scheduling, w: Wire, portPath: PortPath, evenIfInput: BOOL, from: ModelSlot] = {
wg: WireGroup = GetWireGroup[w];
SELECT wg FROM
belowLeaf => ERROR;
leaf => ReallyFindVicinity[sch, GetRoseWire[sch.sim, w], evenIfInput, from];
aboveLeaf => {
FindPPVicinity[sch, w.elements[portPath.first], portPath.rest, evenIfInput, from];
};
ENDCASE => ERROR;
};
StrInitQ: PROC [sch: Scheduling] =
BEGIN
FOR n: RoseWire ← sch.firstAffected, n.nextAffected WHILE n # NIL DO
IF n = notInWireList THEN ERROR;
IF n.type.class.super.InitQ # NIL THEN n.type.class.super.InitQ[n.type, n.valPtr, n.holdStrength];
NoteNews[news: $NewNodeQ, rw: n, arg: NIL];
Need1[sch, n, InitQ];
ENDLOOP;
END;
InitQ: PROC [cell: RoseCellInstance] = {
IF NOT cell.initQed THEN {
cell.initQed ← TRUE;
CallEval[cell, cell.type.behaviorClass.details.InitQ];
};
};
CallEval: PROC [rci: RoseCellInstance, Proc: RoseBehavior.EvalProc] = INLINE {
IF Proc # NIL THEN Proc[
argsAny: rci.args,
switchAny: rci.switchIO,
simpleAny: rci.newIO,
strengthAny: rci.newDrive,
stateAny: rci.state,
perturb: NIL];
};
CellProc: TYPE = PROC [RoseCellInstance];
Need1: PROC [sch: Scheduling, n: RoseWire, also: CellProc] =
BEGIN
FOR sl: SlotList ← n.switchConnections, sl.rest WHILE sl # NIL DO
IF sl.first.cell.nextNeeded = notInCellList THEN
BEGIN
IF sch.firstNeeded = notInCellList THEN ERROR;
sl.first.cell.nextNeeded ← sch.firstNeeded;
sch.firstNeeded ← sl.first.cell;
IF also # NIL THEN also[sl.first.cell];
END;
ENDLOOP;
END;
StrPropQWork: PROC [sch: Scheduling] =
BEGIN
WHILE sch.firstNeeded # NIL DO
needed: RoseCellInstance ← sch.firstNeeded;
IF needed = notInCellList THEN ERROR;
sch.firstNeeded ← needed.nextNeeded;
needed.nextNeeded ← notInCellList;
needed.initQed ← FALSE;
IF NOT (needed.type.behaviorClass.details.PropQ # NIL OR needed.hasTransducedPort) THEN LOOP;
IF needed.type.behaviorClass.details.PropQ # NIL THEN {
FOR epi: EffectivePortIndex IN [0 .. needed.effectivePorts.length) DO
ep: EffectivePort ← needed.effectivePorts[epi];
IF
ep.implType.class.super.flavor = switch AND NOT ep.transduced
THEN RoseWireTwiddling.CopyQ[
fromT: needed.connectedWires[epi].type,
fromP: needed.connectedWires[epi].valPtr,
toT: ep.implType,
toP: ep.switch
];
ENDLOOP;
CallEval[needed, needed.type.behaviorClass.details.PropQ];
};
FOR epi: EffectivePortIndex IN [0 .. needed.effectivePorts.length) DO
n: RoseWire ← needed.connectedWires[epi];
change: BOOL;
IF n.type.class.super.flavor # switch THEN LOOP;
IF n.nextAffected = notInWireList THEN ERROR;
change ← RoseWireTwiddling.MaxinQ[
fromT: needed.effectivePorts[epi].implType,
fromP: needed.effectivePorts[epi].switch,
toT: n.type,
toP: n.valPtr];
IF change THEN {
reportSlot^ ← [needed, needed.effectivePorts[epi].path];
NoteNews[news: $NewNodeQ, rw: n, arg: reportSlot];
Need2[sch, n, needed];
};
ENDLOOP;
sch ← sch;
ENDLOOP;
sch ← sch;
END;
Need2: PROC [sch: Scheduling, n: RoseWire, except: RoseCellInstance] =
BEGIN
FOR sl: SlotList ← n.switchConnections, sl.rest WHILE sl # NIL DO
IF sl.first.cell.nextNeeded = notInCellList AND sl.first.cell # except THEN {
IF sch.firstNeeded = notInCellList THEN ERROR;
sl.first.cell.nextNeeded ← sch.firstNeeded;
sch.firstNeeded ← sl.first.cell;
};
ENDLOOP;
sch ← sch;
END;
StrInitUD: PROC [sch: Scheduling] =
BEGIN
FOR n: RoseWire ← sch.firstAffected, n.nextAffected WHILE n # NIL DO
IF n = notInWireList THEN ERROR;
IF n.type.class.super.InitUD # NIL THEN n.isInput ← n.type.class.super.InitUD[n.type, n.valPtr, n.holdStrength];
NoteNews[news: $NewNodeUD, rw: n, arg: NIL];
Need1[sch, n, InitUD];
ENDLOOP;
END;
InitUD: PROC [cell: RoseCellInstance] = {
IF NOT cell.initUDed THEN {
cell.initUDed ← TRUE;
CallEval[cell, cell.type.behaviorClass.details.InitUD];
};
};
StrPropUDWork: PROC [sch: Scheduling] =
BEGIN
WHILE sch.firstNeeded # NIL DO
needed: RoseCellInstance ← sch.firstNeeded;
IF needed = notInCellList THEN ERROR;
sch.firstNeeded ← needed.nextNeeded;
needed.nextNeeded ← notInCellList;
needed.initUDed ← FALSE;
IF (needed.type.behaviorClass.details.PropUD = NIL) AND (NOT needed.hasTransducedPort) THEN LOOP;
IF needed.type.behaviorClass.details.PropUD # NIL THEN {
FOR epi: EffectivePortIndex IN [0 .. needed.effectivePorts.length) DO
ep: EffectivePort ← needed.effectivePorts[epi];
IF
ep.implType.class.super.flavor = switch AND NOT ep.transduced
THEN RoseWireTwiddling.CopyUD[
fromT: needed.connectedWires[epi].type,
fromP: needed.connectedWires[epi].valPtr,
toT: ep.implType,
toP: ep.switch];
ENDLOOP;
CallEval[needed, needed.type.behaviorClass.details.PropUD];
};
FOR epi: EffectivePortIndex IN [0 .. needed.effectivePorts.length) DO
n: RoseWire ← needed.connectedWires[epi];
change: BOOL;
IF n.type.class.super.flavor # switch THEN LOOP;
IF n.nextAffected = notInWireList THEN ERROR;
change ← RoseWireTwiddling.MaxinUD[
fromT: needed.effectivePorts[epi].implType,
fromP: needed.effectivePorts[epi].switch,
toT: n.type,
toP: n.valPtr];
IF change THEN {
reportSlot^ ← [needed, needed.effectivePorts[epi].path];
NoteNews[news: $NewNodeUD, rw: n, arg: reportSlot];
Need2[sch, n, needed];
};
ENDLOOP;
sch ← sch;
ENDLOOP;
sch ← sch;
END;
StrFinalUD: PROC [sch: Scheduling, bbTable: PrincOps.BitBltTablePtr] =
BEGIN
firstNoted, next: RoseCellInstance ← NIL;
FOR n: RoseWire ← sch.firstAffected, n.nextAffected WHILE n # NIL DO
delay: BOOLFALSE;
IF n = notInWireList THEN ERROR;
n.found ← FALSE;
IF n.type.class.super.ComputeLevel # NIL THEN delay ← n.type.class.super.ComputeLevel[n.type, n.valPtr, n.XPhobic];
SetDelayedness[n, delay];
IF NOT delay THEN DistributeSwitch[n, bbTable, FALSE];
FOR sl: SlotList ← n.switchConnections, sl.rest WHILE sl # NIL DO
IF sl.first.cell.nextNoted = notInCellList THEN
{sl.first.cell.nextNoted ← firstNoted; firstNoted ← sl.first.cell};
ENDLOOP;
ENDLOOP;
sch.firstAffected ← NIL;
FOR firstNoted ← firstNoted, next WHILE firstNoted # NIL DO
IF firstNoted = notInCellList THEN ERROR;
next ← firstNoted.nextNoted;
firstNoted.nextNoted ← notInCellList;
CallEval[firstNoted, firstNoted.type.behaviorClass.details.FinalUD];
IF firstNoted.affectedFlags[transducedToSwitch] THEN ScheduleCell[firstNoted];
ValsChanged[firstNoted, sch, bbTable];
firstNoted.affectedFlags ← ALL[FALSE];
ENDLOOP;
sch ← sch;
END;
SetDelayedness: PROC [n: RoseWire, delay: BOOL] = {
sch: Scheduling = n.schIn;
IF delay = (n.nextDelayed # notInWireList) THEN RETURN;
IF delay THEN {
IF sch.firstDelayed = notInWireList THEN ERROR;
n.nextDelayed ← sch.firstDelayed;
n.prevDelayed ← NIL;
IF n.nextDelayed = NIL THEN sch.lastDelayed ← n ELSE n.nextDelayed.prevDelayed ← n;
sch.firstDelayed ← n;
}
ELSE {
IF n.nextDelayed = NIL THEN sch.lastDelayed ← n.prevDelayed ELSE n.nextDelayed.prevDelayed ← n.prevDelayed;
IF n.prevDelayed = NIL THEN sch.firstDelayed ← n.nextDelayed ELSE n.prevDelayed.nextDelayed ← n.nextDelayed;
n.nextDelayed ← n.prevDelayed ← notInWireList;
};
};
DistributeSwitch: PROC [node: RoseWire, bbTable: PrincOps.BitBltTablePtr, perturb: BOOL] = {
IF node.type.class.super.flavor # switch THEN ERROR;
IF perturb THEN PerturbWire[node, nilModelSlot, TRUE];
FOR sl: SlotList ← node.switchConnections, sl.rest WHILE sl # NIL DO
cell: RoseCellInstance ← sl.first.cell;
epi: EffectivePortIndex ← sl.first.effectivePortIndex;
ep: EffectivePort ← cell.effectivePorts[epi];
implPtr: Ptr ← ep.switch;
IF ep.implType.class.super.flavor # simple THEN RoseWireTwiddling.CopyLevel[
fromT: cell.connectedWires[epi].type,
fromP: cell.connectedWires[epi].valPtr,
toT: ep.implType,
toP: implPtr];
IF ep.transduced THEN {
modelPtr: Ptr ← ep.newSimple;
ep.implType.class.super.Transduce[
fromS: FIRST[Strength],
fromT: ep.implType,
fromP: implPtr,
toT: ep.type,
toP: modelPtr];
};
ENDLOOP;
RoseEvents.Notify[event: $ChangeEarly, watched: node];
RoseEvents.Notify[event: $ChangeLate, watched: node];
};
ValsChanged: PROC [cell: RoseCellInstance, sch: Scheduling, bbTable: PrincOps.BitBltTablePtr] =
BEGIN
IF cell.type.behaviorClass.details.ValsChanged # NIL THEN {
Perturb: PROC [portPath: PortPath] = {
PerturbPP[cell.core.actual, portPath, sch, [cell, portPath], FALSE];
};
PreSimple[cell, bbTable];
cell.type.behaviorClass.details.ValsChanged[
argsAny: cell.args,
switchAny: cell.switchIO,
simpleAny: cell.newIO,
strengthAny: cell.newDrive,
stateAny: cell.state,
perturb: Perturb
!UNWIND => ReallyCleanUpAfterModify[rci: cell, sch: sch, outputsOnly: TRUE, blindly: FALSE, mayRescheduleSelf: TRUE, bbTable: bbTable]];
ReallyCleanUpAfterModify[rci: cell, sch: sch, outputsOnly: TRUE, blindly: FALSE, mayRescheduleSelf: TRUE, bbTable: bbTable];
};
END;
PerturbWire: PUBLIC PROC [rw: RoseWire, agitator: ModelSlot, evenIfInput: BOOLFALSE] = {
sch: Scheduling = rw.schIn;
ReallyPerturb[rw, sch, sch.sim, agitator, evenIfInput];
};
ReallyPerturb: PROC [rw: RoseWire, sch: Scheduling, sim: Simulation, agitator: ModelSlot, evenIfInput: BOOLFALSE] =
BEGIN
SELECT rw.type.class.super.flavor FROM
switch => NULL;
simple, drive => ERROR;
ENDCASE => ERROR;
IF rw.isInput AND NOT evenIfInput THEN RETURN;
IF rw.nextPerturbed = notInWireList THEN {
IF sch.firstPerturbed = notInWireList THEN ERROR;
rw.nextPerturbed ← sch.firstPerturbed;
sch.firstPerturbed ← rw;
reportSlot^ ← agitator;
NoteNews[news: $Perturbed, rw: rw, arg: reportSlot];
};
END;
reportSlot: REF ModelSlot ← NEW [ModelSlot ← nilModelSlot];
NoteNews: PROC [news: ATOM, rw: RoseWire, arg: REF ANY] = {
RoseEvents.Notify[event: news, watched: rw, arg: arg];
FOR pl: PieceList ← node.parentPieces, pl.rest WHILE pl # NIL DO
NoteNews[news, pl.first.twardDesign, arg];
ENDLOOP;
};
Specialty: PROC [st: Slot] RETURNS [sy: Speciality] = {
ep: EffectivePort ← st.cell.effectivePorts[st.effectivePortIndex];
IF ep.implType.class.super.flavor # switch THEN ERROR;
sy ← IF ep.transduced THEN transducedToSwitch ELSE modeledAsSwitch;
};
NoteTweak: PUBLIC PROC [rw: RoseWire, bbTable: PrincOps.BitBltTablePtr] = {
SELECT rw.type.class.super.flavor FROM
drive => ERROR;
simple => Distribute[
baseType: rw.type,
base: rw.valPtr,
fromWire: rw,
sch: rw.schIn,
bbTable: bbTable,
agitator: nilModelSlot];
switch => PerturbWire[rw, nilModelSlot, TRUE];
ENDCASE => ERROR;
};
END.