RosemaryUserViewerImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Jean-Marc Frailong February 3, 1988 12:20:52 pm PST
Pradeep Sindhu February 19, 1988 6:21:48 am PST
Management of Rosemary viewers and the Tester abstraction
DIRECTORY Atom, BasicTime, Buttons, ChoiceButtons, Containers, Convert, Core, CoreClasses, CoreFlat, CoreOps, CoreProperties, IO, PlotGraph, Ports, Process, ProcessProps, ReadEvalPrint, RefTab, Rope, Rosemary, RosemaryUser, Rules, SymTab, TypeScript, ViewerClasses, ViewerEvents, ViewerIO, ViewerOps, ViewerTools;
RosemaryUserViewerImpl: CEDAR MONITOR
IMPORTS BasicTime, Buttons, ChoiceButtons, Containers, Convert, CoreClasses, CoreFlat, CoreOps, CoreProperties, IO, PlotGraph, Ports, Process, ProcessProps, ReadEvalPrint, RefTab, Rope, Rosemary, RosemaryUser, Rules, SymTab, TypeScript, ViewerEvents, ViewerIO, ViewerOps, ViewerTools
EXPORTS RosemaryUser
~ BEGIN OPEN RosemaryUser;
Types & constants
entryHeight: NAT = 15; -- how tall to make each line of items
entryVSpace: NAT = 4;  -- vertical leading space between lines
entryHSpace: NAT = 4;  -- horizontal leading space between buttons
pointsPerInch: NAT = 72;  -- horizontal space for text ropes
borderOffset: NAT = 2;
Display viewer
rosemaryHandle: ATOM ← $RosemaryUserRoseDisplay; -- CellType property holding RoseDisplay
DisplayViewer: PUBLIC PROC [simulation: Rosemary.Simulation, cellType: Core.CellType, name: ROPENIL, displayWires: CoreFlat.FlatWires ← NIL, graphWires: CoreFlat.FlatWires ← NIL] RETURNS [handle: RoseDisplay] = {
viewer: ViewerClasses.Viewer← Containers.Create[[  
name: name,
iconic: TRUE,
column: left,
scrollable: FALSE ]];
handle ← NEW[RoseDisplayRec];
handle.simulation ← simulation;
handle.cellType ← cellType;
MakeDisplayAndBrowse[displayWires: displayWires, graphWires: graphWires, height: entryVSpace, handle: handle, viewer: viewer, name: name];
};
MakeDisplayAndBrowse: PROC [displayWires: CoreFlat.FlatWires, graphWires: CoreFlat.FlatWires, height: CARDINAL, handle: RoseDisplay, viewer: ViewerClasses.Viewer, name: ROPE] ~ {
AddButton: PROC [name: ROPE, proc: Buttons.ButtonProc] ~ {
button: ViewerClasses.Viewer ← Buttons.Create[
info: [name: name, wx: xPos, wy: height, parent: viewer, border: TRUE],
proc: proc, clientData: handle];
xPos ← xPos+button.ww+entryHSpace;
};
AddPrompt: PROC [title: ROPE] RETURNS [v: ViewerClasses.Viewer]~ {
v ← ChoiceButtons.BuildTextPrompt[
viewer: viewer,
x: entryHSpace, y: height,
x: entryHSpace, y: height-borderOffset,
title: title, textViewerWidth: 4*pointsPerInch].textViewer;
height ← height + entryHeight + entryVSpace;
xPos ← entryHSpace; -- force beginning of new line
};
AddChoice: PROC [title: ROPE, proc: ChoiceButtons.SelectionNotifierProc, list: ChoiceButtons.ButtonList] ~ {
Default is always list.first.
button: ChoiceButtons.EnumTypeRef ← ChoiceButtons.BuildEnumTypeSelection[
viewer: viewer,
x: xPos, y: height,
title: title, buttonNames: list, default: list.first,
borderOnButtons: TRUE,
notifyClientProc: proc, clientdata: handle,
style: flipThru];
xPos ← button.nextx;
};
xPos: INT ← entryHSpace; -- current x position in viewer to put buttons correctly
handle.wDir ← ProcessProps.GetProp[$WorkingDirectory];
{
rep: ReadEvalPrint.Handle;
title: ROPE = Rope.Cat[name, " Rosemary Script"];
[handle.tsin, handle.tsout] ← ViewerIO.CreateViewerStreams[name: title, viewer: TypeScript.Create[info: [name: title, iconic: TRUE, column: left]]];
rep ← ReadEvalPrint.CreateStreamEvaluator[clientProc: ExploreDesign, prompt: "%l> %l", in: handle.tsin, out: handle.tsout, topLevel: TRUE];
rep.clientData ← handle;
ReadEvalPrint.MainLoop[h: rep, properties: NIL];
};
AddButton["Trace", TraceChanges];
AddChoice["Record:", RecordChoice, LIST["Evals", "MosSim steps", "Nothing"]];
AddChoice[" Plot:", PlotChoice, LIST["Evals", "MosSim steps (waves only)", "MosSim steps (all graphs)"]];
height ← height + entryHeight + entryVSpace; xPos ← entryHSpace; -- new line
ViewerOps.SetOpenHeight[viewer, height]; -- this is where the viewer will open by default
IF displayWires#NIL THEN AddButton["Update", UpdateDisplayButton];
height ← height + entryHeight + entryVSpace; xPos ← entryHSpace; -- new line
handle.path ← AddPrompt["Instantiation Path:"];
handle.currentWire ← AddPrompt["Current Wire:"];
IF displayWires#NIL THEN {
Column: TYPE = NAT [0..2);
columnWidth: NAT = 3*pointsPerInch+pointsPerInch/4;
column: Column ← 0;
xPos: NAT ← entryHSpace; -- current x position in viewer
count: INT ← 0;
Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]];
height ← height + entryVSpace;
FOR dws: CoreFlat.FlatWires ← displayWires, dws.rest UNTIL dws=NIL DO
count ← count + 1;
ENDLOOP;
IF count>100 THEN IO.PutF[handle.tsout, "You really don't want to display %g wires, maybe you should try again", IO.int[count]]
ELSE FOR dws: CoreFlat.FlatWires ← displayWires, dws.rest UNTIL dws=NIL DO
dn: ROPE ← CoreFlat.WirePathRope[handle.cellType, dws.first^];
prompt: ChoiceButtons.PromptDataRef;
IF Rope.Equal[Rope.Substr[dn, 0, 7], "public."] THEN dn ← Rope.Substr[dn, 7];
prompt ← ChoiceButtons.BuildTextPrompt[viewer: viewer, x: xPos, y: height-borderOffset, title: dn, textViewerWidth: 2*pointsPerInch, clientdata: dws.first];
prompt.textViewer.ww ← (xPos+columnWidth)-(prompt.textViewer.wx+entryHSpace);
ViewerTools.InhibitUserEdits[prompt.textViewer];
handle.displayWires ← CONS[prompt, handle.displayWires];
IF column=LAST[Column] THEN {
column ← 0;
height ← height + entryHeight + entryVSpace; xPos ← entryHSpace;
}
ELSE {
column ← column+1;
xPos ← xPos+columnWidth;
};
ENDLOOP;
IF column>0 THEN height ← height + entryHeight + entryVSpace;
};
ViewerOps.OpenIcon[viewer];
handle.name ← name;
FOR wires: CoreFlat.FlatWires ← graphWires, wires.rest UNTIL wires=NIL DO
IF AddWireToPlot[handle, wires.first]#NIL THEN ERROR;
ENDLOOP;
};
RecordChoice: ChoiceButtons.SelectionNotifierProc = {
h: RoseDisplay ← NARROW[clientdata];
SELECT TRUE FROM
Rope.Equal[name, "Evals"] => {h.recordDeltas ← TRUE; h.recordSteps ← FALSE};
Rope.Equal[name, "MosSim steps"] => {h.recordDeltas ← TRUE; h.recordSteps ← TRUE};
Rope.Equal[name, "Nothing"] => {h.recordDeltas ← FALSE; h.recordSteps ← FALSE};
ENDCASE => ERROR; -- something is wrong in the code...
};
PlotChoice: ChoiceButtons.SelectionNotifierProc = {
h: RoseDisplay ← NARROW[clientdata];
SELECT TRUE FROM
Rope.Equal[name, "Evals"] => h.plotStyle ← noSteps;
Rope.Equal[name, "MosSim steps (waves only)"] => h.plotStyle ← waveSteps;
Rope.Equal[name, "MosSim steps (all graphs)"] => h.plotStyle ← allSteps;
ENDCASE => ERROR; -- something is wrong in the code...
IF h.plot#NIL AND h.plot.data#NIL THEN PlotGraph.RefreshPlot[plot: h.plot, eraseFirst: TRUE];
};
TraceChanges: Buttons.ButtonProc = {
h: RoseDisplay ← NARROW[clientData];
IF (h.traceChanges ← ~h.traceChanges) THEN {
Buttons.SetDisplayStyle[NARROW[parent], $WhiteOnBlack];
}
ELSE Buttons.SetDisplayStyle[NARROW[parent], $BlackOnWhite];
};
UpdateDisplayButton: Buttons.ButtonProc = {
handle: RoseDisplay ← NARROW[clientData];
UpdateDisplay[handle];
};
LogSettle: PUBLIC PROC [handle: RoseDisplay, time: INT, memory: BOOLTRUE] = {
UpdateWire: Rosemary.UpdateProc = {
RecordDelta[handle, roseWire, time];
};
UpdateCell: Rosemary.UpdateCellProc = {
RecordStateSample[handle, roseInstance, stateValue, time];
};
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
Rosemary.Settle[simulation: handle.simulation, updateProc: UpdateWire, memory: memory, updateCellProc: UpdateCell];
DeltaFinished[handle, time];
IF handle.traceChanges THEN IO.PutRope[handle.tsout, "\n\n"];
};
UpdateDisplay: PUBLIC PROC [handle: RoseDisplay] = {
FOR dwvs: LIST OF ChoiceButtons.PromptDataRef ← handle.displayWires, dwvs.rest UNTIL dwvs=NIL DO
dw: CoreFlat.FlatWire ← NARROW[dwvs.first.clientdata];
new: ROPE;
value: Ports.LevelSequence ← Rosemary.WireValue[handle.simulation, dw];
new ← Ports.LevelSequenceToRope[value];
IF Rope.Length[new] > 16 THEN new ← "Typescript";
IF NOT Rope.Equal[new, ViewerTools.GetContents[dwvs.first.textViewer]] THEN ViewerTools.SetContents[dwvs.first.textViewer, new];
ENDLOOP;
};
RoseDisplayFor: PUBLIC PROC [cellType: Core.CellType] RETURNS [RoseDisplay] ~ {
RETURN [NARROW [CoreProperties.GetCellTypeProp[cellType, rosemaryHandle]]];
};
Tester abstraction
testProcTable: SymTab.Ref ← SymTab.Create[];
RegisterTestProc: PUBLIC PROC [name: ROPE, proc: TestProc] = {
[] ← SymTab.Store[x: testProcTable, key: name, val: NEW[TestProc ← proc]];
};
StartTest: PUBLIC PROC [tester: Tester] = {
GrumbleStartTest: PROC = {
IF NOT AlreadyStarted[tester] THEN DoStartTest[tester];
};
ProcessProps.AddPropList[LIST[NEW[Atom.DottedPairNode ← [key: $WorkingDirectory, val: tester.display.wDir]]], GrumbleStartTest]
};
DoStartTest: PROC [h: Tester] = {
Eval: PROC [memory: BOOLTRUE, clockEval: BOOLFALSE, checkPorts: BOOLTRUE] ~ {
UpdateCell: Rosemary.UpdateCellProc = {
IF h.abort THEN SIGNAL AbortSignal;
RecordStateSample[h.display, roseInstance, stateValue, h.evalSinceStartCount+1];
};
UpdateWire: Rosemary.UpdateProc = {
IF h.abort THEN SIGNAL AbortSignal;
RecordDelta[h.display, roseWire, h.evalSinceStartCount+1];
};
DO
inBuf: BOOL ← h.displayedStatePoint#h.currentStatePoint AND h.validStates>0;
testPort: Ports.Port ← IF inBuf THEN h.testVectorBuffer[NextBuf[h, h.displayedStatePoint]] ELSE currentTestPort;
Ports.CopyPortValue[from: testPort, to: h.intermediatePort];
Rosemary.Settle[h.display.simulation, UpdateWire, memory, clockEval, UpdateCell];
IF h.display.traceChanges THEN IO.PutRope[h.display.tsout, "\n\n"];
UpdateESSC[h, h.evalSinceStartCount+1];
DeltaFinished[h.display, h.evalSinceStartCount];
IF h.evalSinceStartCount>=h.historyTriggerCount AND NOT inBuf AND h.historySize>0 THEN {
h.currentStatePoint ← NextBuf[h, h.currentStatePoint];
h.displayedStatePoint ← h.currentStatePoint;
IF h.validStates < h.historySize THEN h.validStates ← h.validStates + 1;
Rosemary.StatePoint[h.display.simulation, h.currentStatePoint];
Ports.CopyPortValue[from: currentTestPort, to: h.testVectorBuffer[h.currentStatePoint]];
};
IF NOT clockEval AND checkPorts THEN Ports.CheckPortValue[root: h.display.cellType.public, truth: testPort, question: h.intermediatePort ! Ports.CheckError => UpdateDisplay[h.display]];
IF EvalsFinished[h] THEN SIGNAL AbortSignal;
IF inBuf THEN {
h.displayedStatePoint ← NextBuf[h, h.displayedStatePoint];
IF h.displayedStatePoint=h.currentStatePoint THEN EXIT;
check new state same as recorded state;
}
ELSE EXIT;
ENDLOOP;
};
InitCell: Rosemary.UpdateCellProc = {
IF h.abort THEN SIGNAL AbortSignal;
IF h.display.recordDeltas AND stateValue#NIL THEN { -- data ← copy of stateValue
copy: Ports.LevelSequence ← NEW [Ports.LevelSequenceRec[stateValue.size]];
Ports.CopyLS[from: stateValue, to: copy];
roseInstance.data ← copy;
}
ELSE roseInstance.data ← NIL; -- or NIL to note this cell doesn't log its state
};
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
testProc: TestProc ← NARROW[SymTab.Fetch[x: testProcTable, key: h.currentTestProc].val, REF TestProc]^;
currentTestPort: Ports.Port ← Ports.CreatePort[h.display.cellType, TRUE];
time: BasicTime.GMT ← BasicTime.nullGMT;
IF testProc=NIL THEN ERROR;
UpdateESSC[h, 0];
h.validStates ← 0;
SetEvalUntil[h: h, interrupt: FALSE, abort: FALSE];
ViewerTools.SetContents[h.runState, "Initializing"];
time ← BasicTime.Now[];
Rosemary.Initialize[h.display.simulation, h.steadyInit, InitCell !
UNWIND => FinishedTest[h]];
InitializeDeltas[h.display];
Rosemary.PrintPeriod["Rosemary initialized in", time, BasicTime.Now[]];
ViewerTools.SetContents[h.runState, "Running"];
time ← BasicTime.Now[];
testProc[h.display.simulation, h.display.cellType, currentTestPort, Eval ! Rosemary.Stop => IF reason = $BoolWireHasX THEN {IO.PutF[h.display.tsout, "\nWire %g settled to an X and has boolean ports", IO.rope[CoreFlat.WirePathRope[h.display.cellType, NARROW[data, CoreFlat.FlatWire]^]]]; RESUME}; AbortSignal => CONTINUE; UNWIND => FinishedTest[h]];
Rosemary.PrintPeriod["Rosemary ran in", time, BasicTime.Now[]];
FinishedTest[h];
};
AlreadyStarted: ENTRY PROC [h: Tester] RETURNS [started: BOOL] = {
started ← h.testStarted OR h.singleEval;
h.testStarted ← TRUE;
BROADCAST h.proceed;
};
FinishedTest: ENTRY PROC [h: Tester] = {
UpdateDisplay[h.display];
ViewerTools.SetContents[h.runState, "Idle"];
h.testStarted ← FALSE;
BROADCAST h.proceed;
};
DoSingleEval: ENTRY PROC [h: Tester] = {
h.singleEval ← TRUE;
IF h.testStarted THEN {
h.interrupt ← TRUE;
UNTIL h.waiting OR NOT h.testStarted DO WAIT h.proceed ENDLOOP;
IF h.testStarted THEN {
count: INT ← h.evalSinceStartCount;
until: INT ← h.evalUntil;
h.evalUntil ← count+1;
h.interrupt ← FALSE;
BROADCAST h.proceed;
UNTIL h.evalSinceStartCount > count OR h.abort OR NOT h.testStarted DO
WAIT h.proceed;
ENDLOOP;
h.interrupt ← TRUE;
h.evalUntil ← until;
};
}
ELSE TRUSTED {
h.evalUntil ← 1;
h.testStarted ← TRUE;
Process.Detach[FORK DoStartTest[h]];
UNTIL h.waiting OR h.abort DO WAIT h.proceed ENDLOOP;
};
h.singleEval ← FALSE;
};
DoNextState: ENTRY PROC [h: Tester] = {
IF h.waiting AND NOT h.singleEval THEN {
IF h.displayedStatePoint=h.currentStatePoint THEN IO.PutRope[h.display.tsout, "No next state"]
ELSE {
h.displayedStatePoint ← NextBuf[h, h.displayedStatePoint];
Rosemary.RestoreState[h.display.simulation, h.displayedStatePoint];
UpdateESSC[h, h.evalSinceStartCount+1];
UpdateDisplay[h.display];
};
};
};
DoPreviousState: ENTRY PROC [h: Tester] = {
IF h.waiting AND NOT h.singleEval THEN {
ndsp: NATIF h.displayedStatePoint=0 THEN h.historySize-1 ELSE h.displayedStatePoint-1;
SELECT TRUE FROM
ndsp>=h.validStates => IO.PutRope[h.display.tsout, "No previous state, not enough evals since history trigger"];
ndsp=h.currentStatePoint => IO.PutRope[h.display.tsout, "No previous state, buffer depth exceeded"];
ENDCASE => {
h.displayedStatePoint ← ndsp;
Rosemary.RestoreState[h.display.simulation, h.displayedStatePoint];
UpdateESSC[h, h.evalSinceStartCount-1];
UpdateDisplay[h.display];
};
};
};
SetEvalUntil: PROC [h: Tester, interrupt: BOOL, abort: BOOL] = {
GetCount: PROC [v: ViewerClasses.Viewer] RETURNS [count: INT] = {
IF v=NIL THEN count ← 0
ELSE {
vRope: ROPE ← ViewerTools.GetContents[v];
count ← LAST[INT];
IF vRope#NIL THEN count ← Convert.IntFromRope[vRope ! Convert.Error => CONTINUE];
};
};
SetUntil[h, GetCount[h.proceedUntil], GetCount[h.historyTrigger], interrupt, abort];
};
SetUntil: ENTRY PROC [h: Tester, count: INT, trigger: INT, interrupt: BOOL, abort: BOOL] = {
IF NOT h.singleEval THEN {
h.evalUntil ← count;
h.historyTriggerCount ← trigger;
IF trigger>h.evalSinceStartCount THEN h.validStates ← 0;
h.interrupt ← interrupt;
};
h.abort ← abort;
BROADCAST h.proceed;
};
EvalsFinished: ENTRY PROC [h: Tester] RETURNS [abort: BOOL] = {
resetRunState: BOOLFALSE;
WHILE (h.evalUntil<=h.evalSinceStartCount OR h.interrupt) AND NOT h.abort DO
UpdateDisplay[h.display];
ViewerTools.SetContents[h.runState, "Interrupted"];
resetRunState ← TRUE;
BROADCAST h.proceed;
h.waiting ← TRUE;
WAIT h.proceed;
h.waiting ← FALSE;
ENDLOOP;
IF resetRunState THEN ViewerTools.SetContents[h.runState, "Running"];
abort ← h.abort;
};
NextBuf: PROC [h: Tester, buf: NAT] RETURNS [next: NAT] = {
next ← (buf + 1) MOD h.historySize;
};
Tester viewer
AbortSignal: SIGNAL = CODE;
testerProp: ATOM ← $RosemaryUserTester;
TestProcedureViewer: PUBLIC PROC [cellType: Core.CellType, testButtons: LIST OF ROPE, name: ROPENIL, displayWires: CoreFlat.FlatWires ← NIL, graphWires: CoreFlat.FlatWires ← NIL, cutSet: CoreFlat.CutSet ← NIL, historySize: NAT ← 0, steady: BOOLFALSE, recordDeltas: BOOLTRUE] RETURNS [tester: Tester] = {
The RoseDisplay is put as a CellType property and a tester viewer property to ease up data structure destrcution when the viewer is destroyed.
AddButton: PROC [name: ROPE, proc: Buttons.ButtonProc, guarded: BOOLFALSE] ~ {
button: ViewerClasses.Viewer ← Buttons.Create[
info: [name: name, wx: xPos, wy: height, parent: viewer, border: TRUE],
guarded: guarded,
proc: proc, clientData: tester];
xPos ← xPos+button.ww+entryHSpace;
};
AddPrompt: PROC [title: ROPE, width: CARDINAL] RETURNS [v: ViewerClasses.Viewer] ~ {
v ← ChoiceButtons.BuildTextPrompt[
viewer: viewer,
x: xPos, y: height,
x: xPos, y: height-borderOffset,
title: title, textViewerWidth: width].textViewer;
xPos ← v.wx+v.ww+entryHSpace;
};
viewer: ViewerClasses.Viewer; -- created after simulation has been expanded
height: CARDINAL ← entryVSpace;
xPos: CARDINAL ← entryHSpace;
tester ← NEW[TesterRec];
tester.display ← NEW[RoseDisplayRec];
tester.steadyInit ← steady;
tester.display.cutSet ← cutSet;
tester.intermediatePort ← Ports.CreatePort[cellType, TRUE];
tester.display.cellType ← cellType;
tester.display.recordDeltas ← recordDeltas;
tester.display.simulation ← Rosemary.Instantiate[cellType, tester.intermediatePort, cutSet, historySize];
IF historySize>0 THEN {
tester.historySize ← historySize;
tester.testVectorBuffer ← NEW[Rosemary.PortSequenceRec[historySize]];
FOR b: NAT IN [0..historySize) DO
tester.testVectorBuffer[b] ← Ports.CreatePort[cellType, TRUE];
ENDLOOP;
};
viewer ← Containers.Create[[name: name, iconic: TRUE, column: right, scrollable: FALSE]];
CoreProperties.PutCellTypeProp[cellType, rosemaryHandle, tester.display]; -- for retrieval
ViewerOps.AddProp[viewer, testerProp, tester]; -- for retrieval
[] ← ViewerEvents.RegisterEventProc[proc: TesterDestroyed, event: destroy, filter: viewer, before: TRUE]; -- call proc just before tester viewer is destroyed
AddButton["Start Test", ButtonStartTest];
tester.runState ← AddPrompt[" Run State:", 3*pointsPerInch/2];
ViewerTools.InhibitUserEdits[tester.runState];
ViewerTools.SetContents[tester.runState, "Idle"];
tester.evalsSinceStart ← AddPrompt["Evals Since Start:", 1*pointsPerInch];
ViewerTools.InhibitUserEdits[tester.evalsSinceStart];
height ← height + entryHeight + entryVSpace; xPos ← entryHSpace; -- new line
AddButton["Abort", Abort, TRUE];
AddButton["Interrupt", Interrupt];
AddButton["Single Eval", SingleEval];
AddButton["Proceed", Proceed];
tester.proceedUntil ← AddPrompt["Until:", 1*pointsPerInch];
height ← height + entryHeight + entryVSpace; xPos ← entryHSpace; -- new line
IF historySize>0 THEN {
AddButton["Next State", NextState];
AddButton["Previous State", PreviousState];
tester.historyTrigger ← AddPrompt["History Trigger:", 2*pointsPerInch];
height ← height + entryHeight + entryVSpace; xPos ← entryHSpace; -- new line
};
IF testButtons.rest#NIL THEN { -- more than 1 test proc, show them all for user choice
Column: TYPE = NAT [0..3);
ColumnStart: ARRAY Column OF NAT = [entryVSpace, 2*pointsPerInch+entryVSpace, 4*pointsPerInch+entryVSpace];
column: Column ← 0;
first: Buttons.Button ← NIL;
FOR tbs: LIST OF ROPE ← testButtons, tbs.rest UNTIL tbs=NIL DO
this: Buttons.Button ← Buttons.Create[info: [name: tbs.first,
wx: ColumnStart[column], wy: height,
ww: 0, wh: entryHeight,
parent: viewer, border: TRUE], clientData: tester, proc: TestButtonProc];
tester.testButtonList ← CONS[this, tester.testButtonList];
IF first=NIL THEN first ← this;
IF column=LAST[Column] THEN {
column ← 0;
height ← height + entryHeight + entryVSpace;
}
ELSE column ← column+1;
ENDLOOP;
IF column>0 THEN height ← height + entryHeight + entryVSpace;
Buttons.SetDisplayStyle[first, $WhiteOnBlack];
};
tester.currentTestProc ← testButtons.first;
MakeDisplayAndBrowse[displayWires, graphWires, height, tester.display, viewer, name];
};
TestButtonProc: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
selectedButton: ViewerClasses.Viewer ← NARROW[parent];
FOR l: LIST OF Buttons.Button ← h.testButtonList, l.rest WHILE l#NIL DO
Buttons.SetDisplayStyle[l.first, IF l.first = selectedButton THEN $WhiteOnBlack ELSE $BlackOnWhite];
ENDLOOP;
h.currentTestProc ← selectedButton.name;
};
ButtonStartTest: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
StartTest[h];
};
Interrupt: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
SetEvalUntil[h: h, interrupt: TRUE, abort: FALSE];
};
Proceed: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
SetEvalUntil[h: h, interrupt: FALSE, abort: FALSE];
};
Abort: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
SetEvalUntil[h: h, interrupt: FALSE, abort: TRUE];
};
SingleEval: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
DoSingleEval[h];
SetEvalUntil[h: h, interrupt: TRUE, abort: FALSE];
};
NextState: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
DoNextState[h];
};
PreviousState: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
DoPreviousState[h];
};
SetProceedUntil: PUBLIC PROC [tester: Tester, count: INT] = {
ViewerTools.SetContents[tester.proceedUntil, Convert.RopeFromInt[from: count, showRadix: FALSE]];
};
UpdateESSC: PROC [h: Tester, new: INT] = {
h.evalSinceStartCount ← new;
ViewerTools.SetContents[h.evalsSinceStart, Convert.RopeFromCard[from: h.evalSinceStartCount, showRadix: FALSE]];
};
TesterDestroyed: ViewerEvents.EventProc ~ {
Called when the tester is destroyed, before erasure by window manager
ref: REF ANY ← ViewerOps.FetchProp[viewer, testerProp];
tester: Tester;
IF event#destroy OR before#TRUE OR ref=NIL OR NOT ISTYPE[ref, Tester] THEN RETURN; -- someone is trying to play games with us
tester ← NARROW[ref]; -- safe now
TRUSTED { Process.Detach[FORK TesterCleanup[tester, viewer]] }; -- fork for safety
};
TesterCleanup: PROC [tester: Tester, initial: ViewerClasses.Viewer] ~ {
Remove extraneous viewers, destroy backing file, remove circular references if any
display: RoseDisplay ← tester.display;
displayOnCellType: RoseDisplay ← NARROW[CoreProperties.GetCellTypeProp[display.cellType, rosemaryHandle]];
SetUntil[h: tester, count: LAST[INT], trigger: LAST[INT], interrupt: FALSE, abort: TRUE]; -- try to abort simulation, may fail. Should not refer to viewer here as it may be dead!
IF displayOnCellType=display THEN CoreProperties.PutCellTypeProp[display.cellType, rosemaryHandle, NIL]; -- NIL circular ref
IF display.plot#NIL AND display.plot.private#NIL THEN PlotGraph.DeletePlot[display.plot];
ViewerOps.DestroyViewer[ViewerIO.GetViewerFromStream[display.tsout]];
IF display.ps#NIL THEN {IO.SetLength[display.ps, 0]; IO.Close[display.ps]}; -- backing file
};
Management of typescript
DebugEvalProcSignal: SIGNAL [port: Ports.Port, state: REF ANY] = CODE;
DebugEvalProc: PROC [port: Ports.Port, state: REF ANY] = {
SIGNAL DebugEvalProcSignal[port, state];
};
ExploreDesign: ReadEvalPrint.ClientProc = {
ENABLE CoreFlat.PathError => {result ← msg; GOTO Done};
Chop: PROC RETURNS [first, rest: ROPENIL ] = {
dStream: IO.STREAM = IO.RIS[command];
first ← dStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token;
rest ← command.Substr[dStream.GetIndex];
};
handle: RoseDisplay ← NARROW[h.clientData];
operation, rest, pathName, fullPathName, wireName: ROPE;
[operation, rest] ← Chop[];
pathName ← ViewerTools.GetContents[handle.path];
fullPathName ← Rope.Cat[pathName, rest];
wireName ← Rope.Cat[pathName, ViewerTools.GetContents[handle.currentWire], rest];
SELECT TRUE FROM
Rope.Equal[operation, "a"] => {
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
wireKey^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
result ← AddWireToPlot[handle, wireKey];
};
Rope.Equal[operation, "c"] => {
resultStream: IO.STREAMIO.ROS[];
flatCell: CoreFlat.FlatCellTypeRec ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType: Core.CellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
CoreOps.PrintCellType[cellType, resultStream];
result ← IO.RopeFromROS[resultStream];
};
Rope.Equal[operation, "cn"] => {
flatCell: CoreFlat.FlatCellTypeRec ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType: Core.CellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
result ← CoreOps.InheritCellTypeName[cellType];
};
Rope.Equal[operation, "cp"] => {
resultStream: IO.STREAMIO.ROS[];
flatCell: CoreFlat.FlatCellTypeRec ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType: Core.CellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
CoreOps.PrintWire[cellType.public, resultStream];
result ← Rope.Cat[CoreOps.InheritCellTypeName[cellType], ": ", IO.RopeFromROS[resultStream]];
};
Rope.Equal[operation, "d"] => result ← DescribeWire[goryDetails: TRUE, handle: handle, wireName: wireName];
Rope.Equal[operation, "p"] => {
PrintPort: PROC [public: Core.Wire] RETURNS [subWires: BOOLTRUE, quit: BOOLFALSE] = {
IF NOT Ports.WirePortType[cellType, public].levelType=composite THEN {
value: Ports.LevelSequence;
subWires ← FALSE;
thisWire.wire ← public;
result ← Rope.Cat[result, CoreOps.GetFullWireName[cellType.public, public], " - "];
value ← Rosemary.WireValue[handle.simulation, thisWire ! Rosemary.NotInstantiated => GOTO NotFound];
result ← Rope.Cat[result, Ports.LevelSequenceToRope[value], "\n"];
EXITS NotFound => result ← Rope.Cat[result, "No such wire\n"];
};
};
flatCell: CoreFlat.FlatCellTypeRec;
thisWire: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
cellType: Core.CellType;
flatCell ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
thisWire.flatCell ← flatCell;
thisWire.wireRoot ← public;
IF CoreOps.VisitWire[cellType.public, PrintPort] THEN ERROR;
};
Rope.Equal[operation, "r"] => {
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
wireKey^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
IF NOT RemoveWireFromPlot[handle, wireKey] THEN result ← "No such wire plotted";
};
Rope.Equal[operation, "s"] => {
flatCell: CoreFlat.FlatCellType ← NEW[CoreFlat.FlatCellTypeRec];
roseInstance: Rosemary.RoseCellInstance;
flatCell^ ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
roseInstance ← NARROW[RefTab.Fetch[x: handle.simulation.coreToRoseInstances, key: flatCell].val];
IF roseInstance=NIL THEN result ← "No such instance"
ELSE TRUSTED {Process.Detach[FORK DebugEvalProc[roseInstance.publicPort, roseInstance.state]]};
};
Rope.Equal[operation, "t"] => result ← DescribeWire[goryDetails: FALSE, handle: handle, wireName: wireName];
Rope.Equal[operation, "v"] => result ← WireValueRope[handle, wireName];
ENDCASE => IF NOT Rope.IsEmpty[operation] THEN result ← Rope.Cat["No such command: ", operation, ", commands are (a)dd wire to plot, (r)emove wire from plot, (s)tate, (v)alue, (t)race, (p)orts, (c)elltype, (c)elltype(n)ame, (c)elltype(p)ublic, and (d)rivers"];
EXITS
Done => NULL;
};
WireValueRope: PROC [handle: RoseDisplay, wireName: ROPE] RETURNS [rope: ROPE] = {
key: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
key^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
rope ← Ports.LSToRope[Rosemary.WireValue[handle.simulation, key ! Rosemary.NotInstantiated => GOTO NotFound]];
EXITS NotFound => rope ← "No such wire";
};
DescribeWire: PROC [goryDetails: BOOL, handle: RoseDisplay, wireName: ROPE] RETURNS [result: ROPE] = {
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
parentKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
roseWire: Rosemary.RoseWire;
wireKey^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
parentKey^ ← CoreFlat.CanonizeWire[handle.cellType, wireKey^];
roseWire ← NARROW[RefTab.Fetch[handle.simulation.coreToRoseWires, parentKey].val];
result ← IF roseWire=NIL THEN Rope.Cat[CoreFlat.WirePathRope[handle.cellType, wireKey^], " = ", WireValueRope[handle, wireName], "\nThis wire is not stored as a Rosemary wire, try a parent or child of it"]
ELSE RoseWirePrint[goryDetails, handle.simulation, handle.cellType, roseWire];
};
RoseWirePrint: PROC [goryDetails: BOOL, simulation: Rosemary.Simulation, root: Core.CellType, roseWire: Rosemary.RoseWire] RETURNS [result: ROPE] = {
CatWire: PROC [roseWire: Rosemary.RoseWire] = {
result ← Rope.Cat[result, CoreFlat.WirePathRope[root, roseWire.wire], " - "];
IF roseWire.currentValue=NIL THEN {
IF goryDetails THEN {
result ← Rope.Cat[result, "m: ", IF roseWire.memory THEN "T" ELSE "F"];
result ← Rope.Cat[result, "cd: ", Ports.driveNames[roseWire.connectionDrive]];
result ← Rope.Cat[result, ", sd: ", Ports.driveNames[roseWire.switchDrive]];
result ← Rope.Cat[result, ", ud: ", Ports.driveNames[roseWire.upDrive]];
result ← Rope.Cat[result, ", dd: ", Ports.driveNames[roseWire.downDrive]];
result ← Rope.Cat[result, ", wd: ", Ports.driveNames[roseWire.wireDrive]];
result ← Rope.Cat[result, ", cl: ", Ports.levelNames[roseWire.connectionLevel], ", "];
};
result ← Rope.Cat[result, "wl: ", Ports.levelNames[roseWire.wireLevel]];
}
ELSE result ← Ports.LevelSequenceToRope[roseWire.currentValue];
};
CatTran: PROC [roseTran: Rosemary.RoseTransistor] = {
result ← Rope.Cat[result, " tt: ", CoreClasses.transistorTypeNames[roseTran.type]];
IF goryDetails THEN result ← Rope.Cat[result, ", tc: ", Ports.driveNames[roseTran.conductivity]];
};
FindPortWire: Ports.EachWirePortPairProc = {
IF (quit ← port=clientPort) THEN coreWire.wire ← wire;
};
clientPort: Ports.Port;
coreWire: CoreFlat.FlatWireRec;
coreWire.wireRoot ← public;
CatWire[roseWire];
result ← Rope.Cat[result, "\nConnections\n"];
IF roseWire.connections#NIL THEN FOR fields: NAT IN [0..roseWire.connections.size) DO
field: Rosemary.Field ← roseWire.connections[fields];
roseInstance: Rosemary.RoseCellInstance ← field.portBinding.instance;
result ← Rope.Cat[result, " "];
clientPort ← field.portBinding.clientPort;
IF NOT Ports.VisitBinding[wire: IF roseInstance=NIL THEN root.public ELSE CoreFlat.ResolveFlatCellType[root, roseInstance.instance].cellType.public, port: IF roseInstance=NIL THEN simulation.testPort ELSE roseInstance.publicPort, eachWirePortPair: FindPortWire] THEN ERROR;
coreWire.flatCell ← IF roseInstance=NIL THEN CoreFlat.rootCellType ELSE roseInstance.instance;
result ← Rope.Cat[result, CoreFlat.WirePathRope[root, coreWire]];
IF goryDetails THEN {
result ← Rope.Cat[result, " - d: "];
IF clientPort.driveType=aggregate THEN result ← Rope.Cat[result, Ports.driveNames[field.portBinding.currentDrive]]
ELSE FOR i: NAT IN [0..field.currentDrive.size) DO
IF i=0 THEN result ← Rope.Cat[result, "("];
result ← Rope.Cat[result, Ports.driveNames[field.currentDrive[i]]];
result ← Rope.Cat[result, IF i=field.currentDrive.size-1 THEN ")" ELSE ", "];
ENDLOOP;
result ← Rope.Cat[result, ", v: ", Ports.LevelSequenceToRope[field.currentValue]];
result ← Rope.Cat[result, ", sb: ", Convert.RopeFromCard[field.portStartBit]];
result ← Rope.Cat[result, ", fs: ", Convert.RopeFromCard[field.currentValue.size]];
};
result ← Rope.Cat[result, "\n"];
ENDLOOP;
result ← Rope.Cat[result, "Channels\n"];
IF roseWire.channels#NIL THEN FOR channels: NAT IN [0..roseWire.channels.size) DO
tran: Rosemary.RoseTransistor ← roseWire.channels[channels];
otherWire: Rosemary.RoseWire ← tran.ch1;
CatTran[tran];
result ← Rope.Cat[result, "\n oc: "];
IF otherWire=roseWire THEN otherWire ← tran.ch2;
CatWire[otherWire];
result ← Rope.Cat[result, "\n g: ", ];
CatWire[tran.gate];
result ← Rope.Cat[result, "\n"];
ENDLOOP;
result ← Rope.Cat[result, "Gates\n"];
IF roseWire.gates#NIL THEN FOR gates: NAT IN [0..roseWire.gates.size) DO
tran: Rosemary.RoseTransistor ← roseWire.gates[gates];
CatTran[tran];
result ← Rope.Cat[result, "\n c1: "];
CatWire[tran.ch1];
result ← Rope.Cat[result, "\n c2: ", ];
CatWire[tran.ch2];
result ← Rope.Cat[result, "\n"];
ENDLOOP;
};
END.