DIRECTORY Atom, Basics, BasicTime, Buttons, ChoiceButtons, Containers, Convert, Core, CoreClasses, CoreFlat, CoreOps, CoreProperties, FS, RefTab, IO, List, PlotGraph, Ports, Process, ProcessProps, Rope, ReadEvalPrint, Real, RedBlackTree, Rosemary, RosemaryUser, Rules, SymTab, ViewerClasses, ViewerEvents, ViewerIO, ViewerOps, ViewerTools; RosemaryUserImpl: CEDAR MONITOR IMPORTS Basics, BasicTime, Buttons, ChoiceButtons, Containers, Convert, CoreClasses, CoreFlat, CoreOps, CoreProperties, FS, RefTab, IO, List, PlotGraph, Ports, Process, ProcessProps, Rope, ReadEvalPrint, Real, RedBlackTree, Rosemary, Rules, SymTab, ViewerEvents, ViewerIO, ViewerOps, ViewerTools EXPORTS RosemaryUser = BEGIN OPEN RosemaryUser; entryHeight: NAT = 15; -- how tall to make each line of items entryVSpace: NAT = 4; -- vertical leading space between lines pointsPerInch: NAT = 72; -- horizontal space for text ropes borderOffset: NAT = 2; Column: TYPE = NAT [0..3); ColumnStart: ARRAY Column OF NAT = [entryVSpace, 1*pointsPerInch+entryVSpace, 4*pointsPerInch+entryVSpace]; testProcTable: SymTab.Ref _ SymTab.Create[]; rosemaryHandle: ATOM _ $RosemaryUserRoseDisplay; -- CellType property holding RoseDisplay DisplayViewer: PUBLIC PROC [simulation: Rosemary.Simulation, cellType: Core.CellType, name: ROPE _ NIL, displayWires: CoreFlat.FlatWires _ NIL, graphWires: CoreFlat.FlatWires _ NIL] RETURNS [handle: RoseDisplay] = { viewer: ViewerClasses.Viewer_ Containers.Create[[ name: name, iconic: FALSE, 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]; }; TestProcedureViewer: PUBLIC PROC [cellType: Core.CellType, testButtons: LIST OF ROPE, name: ROPE _ NIL, displayWires: CoreFlat.FlatWires _ NIL, graphWires: CoreFlat.FlatWires _ NIL, cutSet: CoreFlat.CutSet _ NIL, historySize: NAT _ 0, steady: BOOL _ FALSE, recordDeltas: BOOL _ TRUE] RETURNS [tester: Tester] = { viewer: ViewerClasses.Viewer; -- created after simulation has been expanded height: CARDINAL _ entryVSpace; tester _ NEW[TesterRec]; tester.display _ NEW[RoseDisplayRec]; tester.steadyInit _ steady; tester.recordDeltas _ recordDeltas; tester.display.cutSet _ cutSet; tester.display.cellType _ cellType; tester.intermediatePort _ Ports.CreatePort[cellType, TRUE]; 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: FALSE, column: left, scrollable: FALSE]]; CoreProperties.PutCellTypeProp[cellType, rosemaryHandle, tester.display]; -- for retrieval ViewerOps.AddProp[viewer, rosemaryHandle, tester.display]; -- for retrieval [] _ ViewerEvents.RegisterEventProc[proc: TesterDestroyed, event: destroy, filter: viewer, before: TRUE]; -- call proc just before tester viewer is destroyed [] _ Buttons.Create[info: [name: "Start Test", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: ButtonStartTest]; tester.runState _ ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[1], y: height-borderOffset, title: "Run State:", textViewerWidth: 2*pointsPerInch].textViewer; ViewerTools.InhibitUserEdits[tester.runState]; ViewerTools.SetContents[tester.runState, "Idle"]; IF historySize>0 THEN tester.historyTrigger _ ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[2], y: height-borderOffset, title: "History Trigger:", textViewerWidth: 2*pointsPerInch].textViewer; height _ height + entryHeight + entryVSpace; [] _ Buttons.Create[info: [name: "Interrupt", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: Interrupt]; tester.evalsSinceStart _ ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[1], y: height-borderOffset, title: "Evals Since Start:", textViewerWidth: 1*pointsPerInch].textViewer; ViewerTools.InhibitUserEdits[tester.evalsSinceStart]; IF historySize>0 THEN [] _ Buttons.Create[info: [name: "Next State", wx: ColumnStart[2], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: NextState]; height _ height + entryHeight + entryVSpace; [] _ Buttons.Create[info: [name: "Proceed", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: Proceed]; tester.proceedUntil _ ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[1], y: height-borderOffset, title: "Proceed Until:", textViewerWidth: 1*pointsPerInch].textViewer; IF historySize>0 THEN [] _ Buttons.Create[info: [name: "Previous State", wx: ColumnStart[2], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: PreviousState]; height _ height + entryHeight + entryVSpace; [] _ Buttons.Create[info: [name: "Abort", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: Abort, guarded: TRUE]; [] _ Buttons.Create[info: [name: "Single Eval", wx: ColumnStart[1]+borderOffset, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: SingleEval]; height _ height + entryHeight + entryVSpace; Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]]; height _ height + entryVSpace; IF testButtons.rest#NIL THEN { 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, $BlackOnGrey]; Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]]; height _ height + entryVSpace; }; tester.currentTestProc _ testButtons.first; MakeDisplayAndBrowse[displayWires, graphWires, height, tester.display, viewer, name]; }; MakeDisplayAndBrowse: PROC [displayWires: CoreFlat.FlatWires, graphWires: CoreFlat.FlatWires, height: CARDINAL, handle: RoseDisplay, viewer: ViewerClasses.Viewer, name: ROPE] = { handle.wDir _ ProcessProps.GetProp[$WorkingDirectory]; { rep: ReadEvalPrint.Handle; [handle.tsin, handle.tsout] _ ViewerIO.CreateViewerStreams[name: Rope.Cat[name, " Rosemary Script"]]; 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]; }; IF displayWires#NIL THEN { Column: TYPE = NAT [0..2); ColumnStart: ARRAY Column OF NAT = [entryVSpace, 3*pointsPerInch+pointsPerInch/4+entryVSpace]; column: Column _ 0; count: INT _ 0; 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^]; IF Rope.Equal[Rope.Substr[dn, 0, 7], "public."] THEN dn _ Rope.Substr[dn, 7]; handle.displayWires _ CONS[ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[column], y: height-borderOffset, title: dn, textViewerWidth: 2*pointsPerInch, clientdata: dws.first], handle.displayWires]; ViewerTools.InhibitUserEdits[handle.displayWires.first.textViewer]; IF column=LAST[Column] THEN { column _ 0; height _ height + entryHeight + entryVSpace; } ELSE column _ column+1; ENDLOOP; IF column>0 THEN height _ height + entryHeight + entryVSpace; Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]]; height _ height + entryVSpace; }; [] _ Buttons.Create[info: [name: "Update", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: handle, proc: UpdateDisplayButton]; [] _ Buttons.Create[info: [name: "Log", wx: ColumnStart[1], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: handle, proc: LogChanges]; height _ height + entryHeight + entryVSpace; handle.path _ ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[0], y: height-borderOffset, title: "Instantiation Path:", textViewerWidth: 4*pointsPerInch].textViewer; height _ height + entryHeight + entryVSpace; handle.currentWire _ ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[0], y: height-borderOffset, title: "Current Wire:", textViewerWidth: 4*pointsPerInch].textViewer; height _ height + entryHeight + entryVSpace; Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]]; ViewerOps.SetOpenHeight[viewer, height]; ViewerOps.PaintViewer[viewer, all]; handle.name _ name; FOR wires: CoreFlat.FlatWires _ graphWires, wires.rest UNTIL wires=NIL DO IF AddWireToPlot[handle, wires.first]#NIL THEN ERROR; ENDLOOP; }; LogChanges: Buttons.ButtonProc = { h: RoseDisplay _ NARROW[clientData]; IF (h.logChanges _ ~h.logChanges) THEN { Buttons.SetDisplayStyle[NARROW[parent], $WhiteOnBlack]; } ELSE Buttons.SetDisplayStyle[NARROW[parent], $BlackOnWhite]; }; RegisterTestProc: PUBLIC PROC [name: ROPE, proc: TestProc] = { [] _ SymTab.Store[x: testProcTable, key: name, val: NEW[TestProc _ proc]]; }; 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 $BlackOnGrey ELSE $BlackOnWhite]; ENDLOOP; h.currentTestProc _ selectedButton.name; }; AbortSignal: SIGNAL = CODE; LogSettle: PUBLIC PROC [handle: RoseDisplay, time: INT, memory: BOOL _ TRUE] = { UpdateWire: Rosemary.UpdateProc = { coreWire: CoreFlat.FlatWireRec; RecordDelta[handle, roseWire, time]; IF NOT handle.logChanges THEN RETURN; UpdateDisplay[handle]; coreWire _ roseWire.wire; wireKey^ _ coreWire; IO.PutF[handle.tsout, "%g_%g ", IO.rope[CoreFlat.WirePathRope[handle.cellType, coreWire]], IO.rope[Ports.LevelSequenceToRope[Rosemary.WireValue[ handle.simulation, wireKey]]]]; }; wireKey: CoreFlat.FlatWire _ NEW[CoreFlat.FlatWireRec]; Rosemary.Settle[handle.simulation, UpdateWire, memory]; DeltaFinished[handle, time]; IF handle.logChanges THEN IO.PutRope[handle.tsout, "\n\n"]; }; ButtonStartTest: Buttons.ButtonProc = { h: Tester _ NARROW[clientData]; StartTest[h]; }; 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: BOOL _ TRUE, clockEval: BOOL _ FALSE, checkPorts: BOOL _ TRUE] = { UpdateWire: Rosemary.UpdateProc = { coreWire: CoreFlat.FlatWireRec; IF h.abort THEN SIGNAL AbortSignal; IF h.recordDeltas THEN RecordDelta[h.display, roseWire, h.evalSinceStartCount+1]; IF h.display.logChanges THEN { UpdateDisplay[h.display]; coreWire _ roseWire.wire; wireKey^ _ coreWire; IO.PutF[h.display.tsout, "%g_%g ", IO.rope[CoreFlat.WirePathRope[h.display.cellType, coreWire]], IO.rope[Ports.LevelSequenceToRope[Rosemary.WireValue[ h.display.simulation, wireKey]]]]; }; }; 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]; IF h.display.logChanges 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; } ELSE EXIT; ENDLOOP; }; 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[simulation: h.display.simulation, steady: h.steadyInit ! 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.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; }; 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]; }; 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; }; NextState: Buttons.ButtonProc = { h: Tester _ NARROW[clientData]; DoNextState[h]; }; 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]; }; }; }; PreviousState: Buttons.ButtonProc = { h: Tester _ NARROW[clientData]; DoPreviousState[h]; }; DoPreviousState: ENTRY PROC [h: Tester] = { IF h.waiting AND NOT h.singleEval THEN { ndsp: NAT _ IF 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]; }; }; }; SetProceedUntil: PUBLIC PROC [tester: Tester, count: INT] = { ViewerTools.SetContents[tester.proceedUntil, Convert.RopeFromInt[from: count, showRadix: FALSE]]; }; 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: BOOL _ FALSE; 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; }; UpdateDisplayButton: Buttons.ButtonProc = { handle: RoseDisplay _ NARROW[clientData]; UpdateDisplay[handle]; }; 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; }; NextBuf: PROC [h: Tester, buf: NAT] RETURNS [next: NAT] = { next _ (buf + 1) MOD h.historySize; }; UpdateESSC: PROC [h: Tester, new: INT] = { h.evalSinceStartCount _ new; ViewerTools.SetContents[h.evalsSinceStart, Convert.RopeFromCard[from: h.evalSinceStartCount, showRadix: FALSE]]; }; TesterDestroyed: ViewerEvents.EventProc ~ { ref: REF ANY _ ViewerOps.FetchProp[viewer, rosemaryHandle]; display: RoseDisplay; IF event#destroy OR before#TRUE OR ref=NIL OR NOT ISTYPE[ref, RoseDisplay] THEN RETURN; -- someone is trying to play games with us display _ NARROW[ref]; -- safe now TRUSTED { Process.Detach[FORK TesterCleanup[display, viewer]] }; -- fork for safety }; TesterCleanup: PROC [display: RoseDisplay, initial: ViewerClasses.Viewer] ~ { CoreProperties.PutCellTypeProp[display.cellType, rosemaryHandle, 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 }; RoseDisplayFor: PUBLIC PROC [cellType: Core.CellType] RETURNS [RoseDisplay] ~ { RETURN [NARROW [CoreProperties.GetCellTypeProp[cellType, rosemaryHandle]]]; }; 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: ROPE _ NIL ] = { 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.STREAM _ IO.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.STREAM _ IO.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: BOOL _ TRUE, quit: BOOL _ FALSE] = { 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; }; DisplayPortLeafWires: PUBLIC PROC [root: Core.CellType, flatCell: CoreFlat.FlatCellTypeRec _ CoreFlat.rootCellType] RETURNS [displayWires: CoreFlat.FlatWires] = { CompareFlatWires: List.CompareProc = { flat1: CoreFlat.FlatWire _ NARROW[ref1]; flat2: CoreFlat.FlatWire _ NARROW[ref2]; IF flat1.flatCell.path.length>flat2.flatCell.path.length THEN RETURN [greater]; IF flat1.flatCell.path.length RETURN [greater]; (NOT one) AND other => RETURN [less]; ENDCASE; ENDLOOP; IF flat1.flatCell.recastCount>flat2.flatCell.recastCount THEN RETURN [greater]; IF flat1.flatCell.recastCount no predecessor inTable: BOOL _ FALSE -- used internally to RBT management, don't modify ... ]; WireData: TYPE = REF WireDataRec; WireDataRec: TYPE = RECORD [ lastFilePos: INT _ -1, -- the file position of the last sample written list: SampleList _ NIL, -- if non-nil, the samples for this wire are currently kept in memory previous: Sample _ NIL, -- the previous sample current: Sample _ NIL -- the sample currently taken, not guaranteed if time=lastUpdateTime ]; LockBackingStore: ENTRY PROC [h: RoseDisplay] = { UNTIL NOT h.psLock DO WAIT h.psWait ENDLOOP; h.psLock _ TRUE; }; UnlockBackingStore: ENTRY PROC [h: RoseDisplay] = { h.psLock _ FALSE; BROADCAST h.psWait; }; WriteSample: PROC [s: IO.STREAM, sample: Sample, prvPos: INT] RETURNS [pos: INT] ~ { pos _ IO.GetIndex[s]; IO.PutFWord[s, LOOPHOLE[prvPos]]; -- write position IO.PutFWord[s, LOOPHOLE[sample.time]]; -- write time FOR i: NAT IN [0..sample.size) DO IO.PutByte[s, LOOPHOLE[sample.value[i]]]; ENDLOOP; }; ReadSample: PROC [s: IO.STREAM, at: INT, into: Sample] RETURNS [prv: INT] ~ { IO.SetIndex[s, at]; prv _ LOOPHOLE[IO.GetFWord[s]]; into.time _ LOOPHOLE[IO.GetFWord[s]]; FOR i: NAT IN [0..into.size) DO into.value[i] _ LOOPHOLE[IO.GetByte[s]]; ENDLOOP; }; SampleWire: PROC [wire: Rosemary.RoseWire, sample: Sample] ~ { IF wire.currentValue=NIL THEN sample.value[0] _ wire.wireLevel ELSE FOR i: NAT IN [0.. sample.size) DO sample.value[i] _ wire.currentValue[i] ENDLOOP; }; DifferSample: PROC [s1, s2: Sample] RETURNS [BOOL] ~ { FOR i: CARDINAL IN [0..s1.size) DO IF s1.value[i]#s2.value[i] THEN RETURN [TRUE] ENDLOOP; RETURN [FALSE]; }; InitializeDeltas: PUBLIC PROC [handle: RoseDisplay] = { NewWireData: RefTab.EachPairAction = { wire: Rosemary.RoseWire _ NARROW[val]; data: WireData _ NEW [WireDataRec]; nbits: CARDINAL = IF wire.currentValue=NIL THEN 1 ELSE wire.currentValue.size; data.lastFilePos _ -1; data.list _ NIL; -- not kept in memory (yet...) data.previous _ NIL; -- no previous sample data.current _ NEW[SampleRec[nbits]]; data.current.time _ 0; data.current.next _ NIL; SampleWire[wire, data.current]; wire.data _ data; }; LockBackingStore[handle]; -- Don't forget it here... IF handle.ps=NIL THEN handle.ps _ FS.StreamOpen["///Temp/RoseBackingStore.bin", $create] ELSE IO.SetLength[handle.ps, 0]; -- completely erase the file if already there... [] _ RefTab.Pairs[x: handle.simulation.coreToRoseWires, action: NewWireData]; handle.lastValidTime _ 0; UnlockBackingStore[handle]; -- we don't want to hold onto it during plotting ! IF handle.plot#NIL AND handle.plot.data#NIL THEN PlotGraph.RefreshPlot[plot: handle.plot, eraseFirst: TRUE]; }; RecordDelta: PUBLIC PROC [handle: RoseDisplay, wire: Rosemary.RoseWire, time: INT] ~ { data: WireData = NARROW [wire.data]; nbits: NAT = data.current.size; LockBackingStore[handle]; -- let's be paranoid... IF time#data.current.time THEN { -- this is a different Eval SELECT TRUE FROM data.previous=NIL => { -- this the 1st sample, make it previous & allocate new current data.previous _ data.current; data.current _ NEW[SampleRec[nbits]]; data.previous.next _ data.current; data.current.next _ NIL; -- just to make sure... }; DifferSample[data.previous, data.current] => { -- commit current sample data.lastFilePos _ WriteSample[handle.ps, data.previous, data.lastFilePos]; -- write it data.current.next _ IF data.list=NIL THEN data.previous ELSE NEW[SampleRec[nbits]]; data.previous _ data.current; data.current _ data.previous.next; -- update data data.current.next _ NIL; -- necessary if data.previous was reused }; ENDCASE => NULL; -- collapse current with previous data.current.time _ time; -- this is the new date for the curent sample }; SampleWire[wire, data.current]; -- keep current up to date UnlockBackingStore[handle]; }; RBTGetKey: RedBlackTree.GetKey ~ { RETURN [data]; -- keys are same as data... }; RBTCompare: RedBlackTree.Compare ~ { list1: SampleList = NARROW [k]; list2: SampleList = NARROW [data]; RETURN [Basics.CompareInt[list1.beforeHeadPos, list2.beforeHeadPos]]; }; EnterRoseValuesInRBT: PROC [table: RedBlackTree.Table, roseValues: Rosemary.RoseValues, min: INT _ 0] RETURNS [newTable: RedBlackTree.Table] ~ { UNTIL roseValues=NIL DO data: WireData = NARROW [roseValues.first.roseWire.data]; list: SampleList; roseValues _ roseValues.rest; -- advance list pointer IF data=NIL THEN LOOP; -- InitializeDeltas has not been called yet, discard... list _ data.list; IF list=NIL THEN { -- must create list head list _ NEW [SampleListRec _ [ head: IF data.previous#NIL THEN data.previous ELSE data.current, beforeHeadPos: data.lastFilePos, inTable: FALSE]]; data.list _ list; }; IF list.beforeHeadPos<0 OR list.head.time<=min THEN LOOP; -- nothing to fetch IF table=NIL THEN table _ RedBlackTree.Create[RBTGetKey, RBTCompare]; IF NOT list.inTable THEN { -- may not insert twice in a RedBlackTree RedBlackTree.Insert[table, list, list]; list.inTable _ TRUE; }; ENDLOOP; newTable _ table; }; ReadSamplesUsingRBT: PROC [stream: IO.STREAM, table: RedBlackTree.Table, min: INT] ~ { initialPosition: INT = IO.GetIndex[stream]; -- save current file position (EOF) IF table#NIL THEN WHILE RedBlackTree.Size[table]#0 DO list: SampleList _ NARROW [RedBlackTree.LookupLargest[table]]; node: RedBlackTree.Node _ RedBlackTree.Delete[table, list]; sample: Sample _ NEW [SampleRec[list.head.size]]; list.beforeHeadPos _ ReadSample[stream, list.beforeHeadPos, sample]; sample.next _ list.head; list.head _ sample; -- insert as head of list IF sample.time>min AND list.beforeHeadPos>=0 THEN RedBlackTree.InsertNode[table, node, list] -- this one must still go on... ELSE list.inTable _ FALSE; -- it's no more there... ENDLOOP; IO.SetIndex[stream, initialPosition]; -- go back to where we came from }; PlotGraphData: TYPE ~ REF PlotGraphDataRec; PlotGraphDataRec: TYPE ~ RECORD [ roseValues: Rosemary.RoseValues _ NIL, -- the roseValues that constitute the flat wire bits: Ports.LevelSequence _ NIL, -- computed values will go in there parts: SEQUENCE size: CARDINAL OF Sample -- basic information for reconstitution ]; roseGraphClass: PlotGraph.GraphClass _ NEW[PlotGraph.GraphClassRec _ [ enumerate: RoseGraphEnumerate]]; NewPlotGraphData: PROC [rvl: Rosemary.RoseValues] RETURNS [pgd: PlotGraphData] ~ { partCount: NAT _ 0; bitCount: NAT _ 0; FOR vals: Rosemary.RoseValues _ rvl, vals.rest UNTIL vals=NIL DO val: Rosemary.RoseValue = vals.first; bitCount _ bitCount+val.fieldWidth; partCount _ partCount + 1; ENDLOOP; pgd _ NEW[PlotGraphDataRec[partCount]]; pgd.roseValues _ rvl; pgd.bits _ NEW[Ports.LevelSequenceRec[bitCount]]; }; InitPlotGraphData: PROC [handle: RoseDisplay, pgd: PlotGraphData, min: INT] RETURNS [ok: BOOL _ TRUE] ~ { rvl: Rosemary.RoseValues _ pgd.roseValues; FOR part: NAT IN [0..pgd.size) DO -- go up the sample chain up to min data: WireData = NARROW[rvl.first.roseWire.data]; sample: Sample; IF data=NIL OR data.list=NIL THEN RETURN [ok: FALSE]; -- not initialized ! sample _ data.list.head; IF sample.time>min THEN RETURN [ok: FALSE]; -- unable to go early enough ... UNTIL sample.next=NIL OR sample.next.time>min DO sample _ sample.next ENDLOOP; pgd.parts[part] _ sample; rvl _ rvl.rest; ENDLOOP; }; AssemblePlotGraphDataBits: PROC [pgd: PlotGraphData] RETURNS [nextTime: INT] ~ { firstFreeBit: NAT _ 0; bits: Ports.LevelSequence _ pgd.bits; nextTime _ LAST[INT]; FOR i: NAT IN [0..pgd.size) DO -- build up the bits sample: Sample = pgd.parts[i]; FOR bit: NAT IN [0..sample.size) DO bits[firstFreeBit+bit] _ sample.value[bit] ENDLOOP; firstFreeBit _ firstFreeBit+sample.size; IF sample.next#NIL THEN nextTime _ MIN[sample.next.time, nextTime]; ENDLOOP; IF nextTime=LAST[INT] THEN RETURN; -- nothing more to do... FOR i: NAT IN [0..pgd.size) DO -- advance samples for nextTime sample: Sample = pgd.parts[i]; IF sample.next#NIL AND sample.next.time=nextTime THEN pgd.parts[i] _ sample.next; ENDLOOP; }; AddWireToPlot: PUBLIC PROC [handle: RoseDisplay, wire: CoreFlat.FlatWire] RETURNS [msg: Rope.ROPE _ NIL] = { roseValues: Rosemary.RoseValues _ Rosemary.GetValues[handle.simulation, wire ! Rosemary.NotInstantiated => GOTO NoSuchWire]; plot: PlotGraph.Plot _ handle.plot; pgd: PlotGraphData _ NewPlotGraphData[roseValues]; graph: PlotGraph.Graph _ NEW[PlotGraph.GraphRec _ [ class: roseGraphClass, data: pgd]]; axis: PlotGraph.Axis _ NEW[PlotGraph.AxisRec _ [ bounds: [0.0, 0.0, 10.0, 6.0], name: CoreFlat.WirePathRope[handle.cellType, wire^], axisData: [X: [ticks: 1.0, visible: TRUE], Y: [ticks: 1.0, visible: TRUE]], graphs: LIST [graph]]]; IF Rope.Equal[Rope.Substr[axis.name, 0, 7], "public."] THEN axis.name _ Rope.Substr[axis.name, 7]; IF pgd.bits.size=1 THEN axis.style _ analog ELSE {axis.style _ hexaV; axis.maxChars _ (pgd.bits.size+3)/4}; IF plot=NIL OR plot.data=NIL THEN { -- don't forget the plot might have been destroyed plot _ handle.plot _ PlotGraph.CreatePlot[handle.name]; PlotGraph.LockPlot[plot]; plot.lowerBounds _ [0.0, 0.0]; plot.upperBounds _ [Real.Float[handle.lastValidTime], 5.0]; plot.data _ handle; } ELSE PlotGraph.LockPlot[plot]; IF plot.axis#NIL THEN axis.bounds _ plot.axis.first.bounds; plot.axis _ CONS[axis, plot.axis]; PlotGraph.UnlockPlot[plot]; PlotGraph.RefreshPlot[plot: plot, eraseFirst: TRUE]; EXITS NoSuchWire => msg _ "Not stored as a Rosemary wire"; }; RemoveWireFromPlot: PUBLIC PROC [handle: RoseDisplay, wire: CoreFlat.FlatWire] RETURNS [found: BOOL _ FALSE] = { roseValues: Rosemary.RoseValues _ Rosemary.GetValues[handle.simulation, wire ! Rosemary.NotInstantiated => GOTO NoSuchWire]; plot: PlotGraph.Plot _ handle.plot; trail: PlotGraph.AxisList _ NIL; IF plot=NIL OR plot.data=NIL THEN RETURN; PlotGraph.LockPlot[plot]; FOR axis: PlotGraph.AxisList _ plot.axis, axis.rest UNTIL axis=NIL DO pgd: PlotGraphData _ NARROW[axis.first.graphs.first.data]; IF pgd.roseValues=roseValues THEN { -- roseValues are unique for a given FlatWire... IF trail=NIL THEN plot.axis _ axis.rest ELSE trail.rest _ axis.rest; -- excise guilty axis found _ TRUE; EXIT; }; trail _ axis; ENDLOOP; PlotGraph.UnlockPlot[plot]; PlotGraph.RefreshPlot[plot: plot, eraseFirst: TRUE]; EXITS NoSuchWire => NULL; }; DeltaFinished: PUBLIC PROC [handle: RoseDisplay, time: INT] = { plot: PlotGraph.Plot _ handle.plot; handle.lastValidTime _ time; IF plot#NIL AND plot.data#NIL THEN { -- the plot viewer might have been destroyed ! oldUpper: REAL = plot.upperBounds.x; newUpper: REAL = Real.Float[time]; plot.upperBounds.x _ newUpper; PlotGraph.RefreshPlot[plot: plot, within: [oldUpper, 0.0, newUpper-oldUpper, 6.0]]; }; }; RoseGraphEnumerate: PlotGraph.GraphEnumerateProc = { handle: RoseDisplay _ NARROW [plot.data]; pgd: PlotGraphData _ NARROW [graph.data]; analog: BOOL = pgd.bits.size=1; time: INT _ MAX [Real.InlineFix[bounds.x], 0]; maxTime: INT = handle.lastValidTime; -- should take bounds in account, but for now... LockBackingStore[handle]; -- critical here... IF NOT InitPlotGraphData[handle, pgd, time] THEN { table: RedBlackTree.Table _ NIL; FOR al: PlotGraph.AxisList _ plot.axis, al.rest UNTIL al=NIL DO -- pass over all axis FOR gl: PlotGraph.GraphList _ al.first.graphs, gl.rest UNTIL gl=NIL DO -- and all graphs thisPGD: PlotGraphData = NARROW [gl.first.data]; table _ EnterRoseValuesInRBT[table, thisPGD.roseValues, time]; -- collect roseValues ENDLOOP; ENDLOOP; IF table#NIL THEN ReadSamplesUsingRBT[handle.ps, table, time]; IF NOT InitPlotGraphData[handle, pgd, time] THEN { -- well, we did try... UnlockBackingStore[handle]; -- Release before leaving RETURN; }; }; IF pgd.bits.size=1 THEN WHILE time<=maxTime DO -- analog plot nextTime: INT = AssemblePlotGraphDataBits[pgd]; value: REAL = SELECT pgd.bits[0] FROM L => 0.0, X => 2.5, H => 5.0, ENDCASE => ERROR; IF eachPoint[x: Real.Float[time], y: value] THEN EXIT; IF eachPoint[x: Real.Float[MIN[nextTime, maxTime]], y: value] THEN EXIT; time _ nextTime; ENDLOOP ELSE WHILE time<=maxTime DO -- hexa plot nextTime: INT = AssemblePlotGraphDataBits[pgd]; IF eachPoint[x: Real.Float[time], y: 0.0, rope: Ports.LSToRope[pgd.bits]] THEN EXIT; time _ nextTime; ENDLOOP; UnlockBackingStore[handle]; -- may now safely release }; WireTimeValue: PUBLIC PROC [handle: RoseDisplay, flatWire: CoreFlat.FlatWire, time: INT] RETURNS [value: Ports.LevelSequence] = { roseValues: Rosemary.RoseValues _ Rosemary.GetValues[handle.simulation, flatWire]; pgd: PlotGraphData _ NewPlotGraphData[roseValues]; LockBackingStore[handle]; -- critical here... IF NOT InitPlotGraphData[handle, pgd, time] THEN { table: RedBlackTree.Table _ EnterRoseValuesInRBT[NIL, pgd.roseValues, time]; IF table#NIL THEN ReadSamplesUsingRBT[handle.ps, table, time]; IF NOT InitPlotGraphData[handle, pgd, time] THEN { -- well, we did try... UnlockBackingStore[handle]; -- Release before leaving RETURN [NIL]; }; }; [] _ AssemblePlotGraphDataBits[pgd]; value _ pgd.bits; UnlockBackingStore[handle]; -- may now safely release }; CheckCoverage: PUBLIC PROC [handle: RoseDisplay] RETURNS [ok: BOOL] ~ { Entry: TYPE ~ REF EntryRep; levelSeen: TYPE ~ RECORD [wasH, wasL: BOOL]; EntryRep: TYPE ~ RECORD [ pos: INT _ -1, roseWire: Rosemary.RoseWire, -- to print the wire name in case of error sample: Sample, -- to read the sample seen: SEQUENCE size: CARDINAL OF levelSeen ]; GetKey: RedBlackTree.GetKey ~ { RETURN [data]; -- keys are same as data... }; Compare: RedBlackTree.Compare ~ { e1: Entry = NARROW [k]; e2: Entry = NARROW [data]; RETURN [Basics.CompareInt[e1.pos, e2.pos]]; }; MergeSample: PROC [entry: Entry, sample: Sample] RETURNS [allSeen: BOOL _ TRUE] ~ { FOR i: CARDINAL IN [0..entry.size) DO SELECT sample.value[i] FROM H => { entry.seen[i].wasH _ TRUE; allSeen _ allSeen AND entry.seen[i].wasL; }; L => { entry.seen[i].wasL _ TRUE; allSeen _ allSeen AND entry.seen[i].wasH; }; ENDCASE => allSeen _ allSeen AND entry.seen[i].wasH AND entry.seen[i].wasL; ENDLOOP; }; InsertInRBT: RefTab.EachPairAction ~ { wire: Rosemary.RoseWire = NARROW[val]; data: WireData = NARROW [wire.data]; nBits: CARDINAL = data.current.size; entry: Entry _ NEW [EntryRep[nBits]]; entry.pos _ data.lastFilePos; entry.roseWire _ wire; entry.sample _ NEW [SampleRec[nBits]]; FOR i: CARDINAL IN [0..nBits) DO entry.seen[i] _ [FALSE, FALSE] ENDLOOP; IF MergeSample[entry, data.current] THEN RETURN; -- all bits have seen H and L IF data.previous#NIL AND MergeSample[entry, data.previous] THEN RETURN; -- H,L seen SELECT TRUE FROM entry.pos<0 => ReportEntry[entry]; -- we've seen all about it, and it fails RedBlackTree.LookupNode[table, entry]=NIL => RedBlackTree.Insert[table, entry, entry]; ENDCASE => NULL; -- discard the entry, it's already in the RBT somehow ... }; ReportEntry: PROC [entry: Entry] ~ { ReportAtomic: PROC [wire: Core.Wire] ~ { msg: Rope.ROPE; h, l: BOOL; [wasH: h, wasL: l] _ entry.seen[bit]; bit _ bit+1; SELECT TRUE FROM h AND l => RETURN; -- good bit h => msg _ "L"; l => msg _ "H"; ENDCASE => msg _ "H nor L"; flatWire.wire _ wire; handle.tsout.PutF["Wire %g does not go through %g\n", IO.rope[CoreFlat.WirePathRope[handle.cellType, flatWire]], IO.rope[msg]]; ok _ FALSE; -- at least one bit in error }; flatWire: CoreFlat.FlatWireRec _ entry.roseWire.wire; wireSize: NAT = CoreOps.WireBits[flatWire.wire]; bit: NAT _ 0; IF wireSize#entry.size THEN ERROR; -- we're in BIG trouble IF wireSize>1 THEN CoreOps.VisitRootAtomics[flatWire.wire, ReportAtomic] ELSE ReportAtomic[flatWire.wire]; }; table: RedBlackTree.Table _ RedBlackTree.Create[GetKey, Compare]; initialPosition: INT; -- to save & restore file position LockBackingStore[handle]; -- the whole procedure assumes the backing store does not change ok _ TRUE; -- until proven false ... [] _ RefTab.Pairs[x: handle.simulation.coreToRoseWires, action: InsertInRBT]; initialPosition _ IO.GetIndex[handle.ps]; -- save current file position (EOF) WHILE RedBlackTree.Size[table]#0 DO entry: Entry _ NARROW [RedBlackTree.LookupLargest[table]]; node: RedBlackTree.Node _ RedBlackTree.Delete[table, entry]; entry.pos _ ReadSample[handle.ps, entry.pos, entry.sample]; IF MergeSample[entry, entry.sample] THEN LOOP; -- entry has seen H and L, discard IF entry.pos>=0 THEN RedBlackTree.InsertNode[table, node, entry] -- keep entry active ELSE ReportEntry[entry]; -- no more samples, but disagreement ENDLOOP; IO.SetIndex[handle.ps, initialPosition]; -- go back to where we came from UnlockBackingStore[handle]; }; END. ®RosemaryUserImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Barth, September 17, 1987 1:34:15 pm PDT Bertrand Serlet October 17, 1986 10:07:27 pm PDT Last Edited by: Gasbarro October 14, 1987 10:27:09 am PDT Christian Le Cocq June 5, 1987 3:03:24 pm PDT Jean-Marc Frailong December 8, 1987 1:46:12 pm PST Management of tester viewer The RoseDisplay is put as a CellType property and a tester viewer property to ease up data structure destrcution when the viewer is destroyed. check new state same as recorded state; Called when the tester is destroyed, before erasure by window manager Remove extraneous viewers, destroy backing file, remove circular references Management of typescript Wire Display Backup file management Current misfeatures and how to correct them: - using RedBlackTree is not very efficient, but it is not critical to efficiency since about anything is better than reading bits from the disk... Rewriting equivalent specialized code is both tedious & non-kosher. - reading in strictly decreasing order is not very good due to the forwards buffering of FS. I don't see any way out of that, save by making the file management much more complex. Performance seems adequate now, especially if PlotGraph is corrected to pass a correct window... - some performance might be gained by using IO.PutBlock/IO.GetBlock, but this is real boring... - the backing file (handle.ps) never gets closed properly, nor erased/destroyed, resulting in an excessive wastage of disk space. This should be done by the same code (now in LogicRosemaryImpl, but should move out) that destroys cleanly the tester viewer and similar crap. An alternative is to finalize RoseDisplay, but that sounds as an extreme measure for such a simple thing... - It would be real fine to save the drive with which a wire has been driven, in order to distinguish X from Z (driven to garbage from tri-stated). Two problems for that: RoseWires that have LS attached do not carry the drive around (Rick says that can be twiddled around), and PlotGraph does not provide the capacity to display that information. The nice thing is that the cost in storage/disk is very small... The value of the rose wire at time was value, until a further sample The in-memory representation of a list of samples. The samples back in time may still be on disk, starting at beforeHeadPos. The data field of a RoseWire. The following assertions always hold: - current.next = NIL - current.time = value of time at last call to Update - current will never be modified once time of Update call > current.time - previous is the sample that was valid just before current. It is necessary to allow collapsing curent with a previous value. previous has not yet been written to disk! - if previous#NIL, then previous.next=current - if list#NIL, then samples are kept in memory. Then, either list.head=previous=NIL, or previous may be reached by following next links from list.head (possibly 0 times!). Write the specified sample at the current file position, using prvPos as backpointer, and return in pos the fileposition at which this sample was read. The initial and final file position are at EOF. Must be called under backing store lock! Read a sample from the specified location on the file. prv is the pointer to the previous sample in the chain. The initial and final file position are undefined. Must be called under backing store lock! Copy the value of the wire into the sample's data field Returns TRUE iff the two samples have different value fields (size must be same!) Create the Wire data and initialize it properly... Maintain the wire data field when the wire is updated. At this point, we assume the file pointer to be at EOF Get the key for a SampleList in the RedBlackTree: it's identity... Compare key & data in the RedBlackTree Add the specified roseValues to the table so that they will be fetched from disk up to the required minimum time. The initial table may be empty. The RedBlackTree is used to read as much information as possible in a single pass over the sample file. This is the only place where SampleList are created and beforeHeadPos modified. Must be called under backing store lock! Read all the samples described in the RBT, going backwards & in order through the file. Must be called under backing store lock! Plotting Known misfeatures and how to improve on them: - PlotGraph almost always calls the GraphEnumerateProc with bounds=infinite (on fact, due to a PlotGraph bug, empty). As a result, when a new wire is displayed, it is always read back to the origin of times. It is not possible from the client code to infer the correct bounds (gathering them from the viewer will hamper IP generation). This should be fixed in PlotGraph (anyway, there are other things to be fixed there...). - It would be more efficient to add multiple wires to the plot at once than one at a time because refreshing requires reading the disk. Either have AddWiresToPlot instead of AddWireToPlot, or (better) leave the responsability of refreshing to the client. - It would be faster is InitPlotGraphData could avoid rewinding back to the beginning of times. This could be achieved by adding a "currentTime" field to the pgd, but this raises serious questions when the simulation is re-initialized because we then must invalidate all pgd's. This is the data structure that permits to recompute values of flatwires at each time Create the PlotGraphData with the right sizes for the RoseValues. Initialize pgd parts so that a first sample is valid at the required time. Assumes that the corresponding values have been made resident early enough using EnterRoseValuesInRBT/ReadSamplesUsingRBT. Will return FALSE if no valid sample may be established for the required date Must be called under backing store lock! Assemble the bits for the current time, and position everything for the next time. Returns LAST[INT] if the computed value is the last one. Must be called under backing store lock! For reasons of efficiency, this procedure should NOT refresh the plot because refreshing the plot forces reading the backing store, and the more there is to do at once the better... [plot: Plot, graph: Graph, bounds: Rectangle, eachPoint: PointProc, data: REF ANY] We don't have enough information yet. Try to make all the wires in the plot resident early enough simultaneously, in order to reduce disk activity. Return the value of a wire at a given time. Will return NIL if no value may be established for the required time. We don't have enough information yet. Read it from the sample file. Odds and Ends Returns TRUE iff all nodes in the simulation reach the H and L levels. Nodes that do not are printed on the simulation log. All information is recovered from the backing file. The algorithm is: - build an RBT containing all of the simulations roseWires - perform an algorithm similar to ReadSamplesUsingRBT: - when a wire has been both H and L, remove it from RBT - when a wire reaches the starting point and it has not been through both values, log a message & remove it from RBT Works only for atomic RoseWires, this needs fixing ... Get the key for a SampleList in the RedBlackTree: it's identity... Compare key & data in the RedBlackTree Add information from sample into entry, return TRUE iff the result has allSeen. Report on the Rosemary typescript information about this wire not seing H and L The algorithm to find the correct flat wire for structured wires follows exactly the method used in RosemaryImpl.InitWire. -- Build the Red-Black tree -- Read the backing file backwards Ê/ú– "cedar" style˜codešœ™Kšœ Ïmœ7™BK™(K™0K™9K™-K™2K™—KšÏk œ}žœ žœ¿˜ÓK˜•StartOfExpansion[]šÑblnœžœž˜Kšžœqžœ žœ¡˜§Kšžœ ˜Kšœžœžœ˜—head™Jšœ žœÏc&˜=Jšœ žœ '˜>Jšœžœ "˜<šœžœ˜J˜—Jšœžœžœ˜šÏn œžœžœžœK˜kJ˜—Jšœ,˜,šœžœ (˜YJ˜—š¡ œžœžœBžœžœ%žœ#žœžœ˜×šœ3˜3Jšœ ˜ Jšœžœ˜Jšœ ˜ Jšœ žœ˜—Jšœ žœ˜Jšœ˜Jšœ˜JšœŠ˜ŠK˜J˜—š"¡œžœžœ(žœžœžœžœžœ%žœ#žœžœžœžœžœžœžœžœ˜¸KšœŽ™ŽJšœ -˜KJšœžœ˜Jšœ žœ ˜Jšœžœ˜%Jšœ˜Jšœ#˜#Jšœ˜Jšœ#˜#Kšœ5žœ˜;Kšœi˜išžœžœ˜Kšœ!˜!Kšœžœ(˜Ešžœžœžœž˜!Kšœ8žœ˜>Kšžœ˜—K˜—Kšœ0žœžœ˜YKšœJ ˜ZKšœ; ˜KKšœcžœ 3˜J˜šœ.˜.Jšœ˜Jšœ˜Jšœžœ.˜J—Jšœ­˜­Jšœ.˜.Jšœ1˜1Jšžœžœº˜ÏJšœ,˜,J˜Jšœ~žœ(˜ªJšœ¼˜¼Jšœ5˜5Jšžœžœ€žœ(˜ÁJšœ,˜,J˜Jšœ|žœ&˜¦Jšœµ˜µJšžœžœ„žœ,˜ÉJšœ,˜,J˜Jšœzžœ-žœ˜±Jšœžœ)˜ºJšœ,˜,J˜Jšœa˜aJšœ˜J˜šžœžœžœ˜Jšœžœžœ˜Jš¡ œžœžœžœK˜kJšœ˜Jšœžœ˜š žœžœžœžœžœžœž˜>šœ=˜=Jšœ$˜$Jšœ˜Jšœžœ-˜I—Jšœžœ˜:Jšžœžœžœ˜šžœžœ žœ˜Jšœ ˜ Jšœ,˜,Jšœ˜—Jšžœ˜Jšžœ˜—Jšžœ žœ-˜=Jšœ-˜-Jšœa˜aJšœ˜J˜—Jšœ+˜+J˜JšœU˜UJ˜J˜—š¡œžœLžœ;žœ˜²Kšœ6˜6šœ˜Kšœ˜Kšœe˜eKšœ…žœ˜‹Kšœ˜Kšœ+žœ˜0K˜—šžœžœžœ˜Jšœžœžœ˜Jš¡ œžœžœžœ>˜^Jšœ˜Jšœžœ˜šžœ2žœžœž˜EK˜Kšžœ˜—Kšžœ žœžœ]žœ ˜š žœžœ2žœžœž˜JKšœžœ6˜>Kšžœ.žœ˜MKšœžœ¹˜ÓJšœC˜Cšžœžœ žœ˜Jšœ ˜ Jšœ,˜,Jšœ˜—Jšžœ˜Kšžœ˜—Jšžœ žœ-˜=Kšœa˜aKšœ˜K˜J˜—Jšœ{žœ2˜±Jšœxžœ)˜¥Jšœ,˜,Jšœ²˜²Jšœ,˜,Jšœ³˜³Jšœ,˜,J˜Jšœa˜aJšœ(˜(Jšœ#˜#K˜šžœ4žœžœž˜IJšžœ$žœžœžœ˜5Jšžœ˜—K˜K˜—š¡ œ˜"Kšœžœ ˜$šžœ žœ˜(Kšœžœ˜7K˜—Kšžœžœ˜Kšœ4žœ˜JKšœ˜K˜—š¡œ˜&Kšœ žœ ˜Kšœ'žœ ˜6š žœžœžœ+žœžœž˜GKšœ!žœžœžœ˜cKšžœ˜—Kšœ(˜(Kšœ˜K˜—š¡ œžœžœ˜K˜—š ¡ œžœžœžœ žœžœ˜PK˜š¡ œ˜#Kšœ˜Kšœ$˜$Kšžœžœžœžœ˜%Kšœ˜Kšœ˜Kšœ˜Kšžœžœ9žœS˜°K˜—K˜Kšœžœ˜7Kšœ7˜7Kšœ˜Kšžœžœžœ˜;˜K˜——š¡œ˜'Kšœ žœ ˜Kšœ ˜ K˜K˜—š¡ œžœžœ˜+š¡œžœ˜Kšžœžœžœ˜7K˜—Kšœžœžœ^˜Kšœ˜K˜—š¡ œžœ˜!K˜š¡œžœ žœžœ žœžœžœžœ˜VK˜š¡ œ˜#Kšœ˜Kšžœ žœžœ ˜#Kšžœžœ;˜Qšžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšžœ!žœ<žœV˜¹K˜—K˜K˜—šž˜Kšœžœ-žœ˜LJšœžœžœ7žœ˜pJšœ<˜žœžœž˜`Kšœžœ˜6Kšœžœ˜ KšœG˜GKšœ'˜'Kšžœžœ˜1KšžœžœAžœ5˜€Kšžœ˜—K˜K˜—š ¡œžœžœžœžœ˜;Kšœžœ˜#K˜K˜—š¡ œžœžœ˜*Kšœ˜Kšœhžœ˜pK˜K˜—š¡œ˜+K™EKšœžœžœ/˜;K˜Kšžœžœžœžœžœžœžœžœžœžœ *˜‚Kšœ žœ  ˜"Kšžœžœ$ ˜SK˜K˜—š¡ œžœ:˜MKšœK™KKšœAžœ ˜VKš žœžœžœžœžœ$˜YK˜EKš žœ žœžœžœžœ ˜[K˜K˜—š¡œžœžœžœ˜OKšžœžœ=˜KK˜——™š ¡œžœžœžœžœ˜FK˜—š¡ œžœžœžœ˜:Kšžœ"˜(K˜K˜—š¡ œ˜+Kšžœ&žœ˜7J˜š ¡œžœžœžœžœ˜1Jš œ žœžœžœžœ ˜%Jšœžœ žœžœ˜KJšœ(˜(Jšœ˜J˜—Kšœžœ˜+Kšœ3žœ˜8Kšœ˜Kšœ0˜0Kšœ(˜(KšœQ˜Qšžœžœž˜šœ˜Kšœžœ˜7KšœL˜LKšœ(˜(Kšœ˜—šœ˜Kš œžœžœžœžœ˜#Kšœn˜nKšœ[˜[Kšœ.˜.Kšœ žœ˜&K˜—šœ ˜ Kšœn˜nKšœ[˜[Kšœ/˜/K˜—šœ ˜ Kš œžœžœžœžœ˜#Kšœn˜nKšœ[˜[Kšœ1˜1Kšœ?žœ˜]K˜—KšœAžœ&˜kšœ˜K˜š¡ œžœžœ žœžœžœžœ˜[šžœžœ:žœ˜FKšœ˜Kšœ žœ˜Kšœ˜KšœS˜SKšœUžœ ˜dKšœB˜BKšžœ9˜>K˜—K˜K˜—Kšœ#˜#Kšœžœ˜8Kšœ˜KšœT˜TKšœL˜LKšœ˜K˜Kšžœ/žœžœ˜˜_K˜—KšœAžœ&˜lKšœG˜GKšžœžœžœžœÖ˜„—šž˜Kšœžœ˜ —K˜K™—š ¡ œžœ!žœžœžœ˜RKšœžœ˜3KšœH˜HKšœ^žœ ˜nKšžœ#˜(Kšœ˜K˜—š ¡ œžœžœ!žœžœ žœ˜fKšœžœ˜7Kšœžœ˜9Kšœ˜KšœL˜LKšœ>˜>Kšœ žœA˜RKšœ žœ žœžœ°˜ÍKšžœJ˜NK˜K˜—š ¡ œžœžœUžœ žœ˜•K˜š¡œžœ"˜/KšœM˜Mšžœžœžœ˜#šžœ žœ˜Kšœ!žœžœžœ˜GKšœN˜NKšœL˜LKšœH˜HKšœJ˜JKšœJ˜JKšœV˜VK˜—KšœI˜IK˜—Kšžœ;˜?K˜K˜—š¡œžœ(˜5KšœT˜TKšžœ žœN˜aK˜K˜—š¡ œ ˜,Kšžœžœ˜6K˜K˜—Kšœ˜K˜Kšœ˜Kšœ˜Kšœ-˜-š žœžœžœžœ žœžœ ž˜UKšœ5˜5KšœE˜EKšœ ˜ Kšœ*˜*Kšžœžœžœžœžœ žœRžœžœžœžœ:žœžœ˜‘Kš œžœžœžœžœ˜^KšœA˜Ašžœ žœ˜Kšœ$˜$Kšžœ žœL˜rš žœžœžœžœž˜2Kšžœžœ ˜+KšœC˜CKšœžœžœžœ˜MKšžœ˜—KšœR˜RKšœN˜NKšœS˜SK˜—Kšœ ˜ Kšžœ˜—Kšœ(˜(š žœžœžœžœ žœžœž˜QK˜K™7Kšžœžœžœ!˜>Kš žœžœžœžœžœ(žœ˜WK˜K˜—š¡ œžœžœžœ˜6KšœžœE™Qšžœžœžœ ˜Kš žœžœžœžœžœ˜0Kšžœ˜—Kšžœžœ˜K˜K˜—š¡œžœžœ˜7š¡ œ˜&K™2Kšœžœ˜&Kšœžœ˜#Kš œžœžœžœžœžœ˜NKšœ˜Kšœ žœ ˜/Kšœžœ ˜*Kšœžœ˜%Kšœ+žœ!˜OKšœ˜K˜—Kšœ ˜4Kš žœ žœžœ žœ5žœžœ 0˜ªKšœM˜MK˜Kšœ 2˜NKš žœ žœžœžœžœ6žœ˜lK˜K˜—š¡ œžœžœ6žœ˜VK™6Kšœžœ ˜$Kšœžœ˜Kšœ ˜1Kšœ3ž™6šžœžœ ˜<šžœžœž˜šœžœ ?˜VK˜Kšœžœ˜%Kšœ"˜"Kšœžœ ˜0Kšœ˜—šœ/ ˜GKšœL  ˜WKš œžœ žœžœžœžœ˜SKšœA ˜OKšœžœ (˜AKšœ˜—Kšžœžœ !˜2—Kšœ -˜GKšœ˜—Kšœ  ˜:Kšœ˜K˜K˜—š¡ œ˜"KšœB™BKšžœ  ˜*Kšœ˜K˜—š¡ œ˜$K™&Kšœžœ˜Kšœžœ˜"Kšžœ?˜EKšœ˜K˜—š¡œžœCžœžœ#˜Kšœ‘™‘Kšœg™gKšœO™OKš¢(™(šžœ žœž˜Kšœžœ"˜9Kšœ˜Kšœ ˜5Kš žœžœžœžœ 7˜NKšœ˜šžœžœžœ ˜+šœžœ˜Kš œžœžœžœžœ˜@Kšœ ˜ Kšœ žœ˜—K˜K˜—Kš žœžœžœžœ ˜MKšžœžœžœ4˜Ešžœžœžœ )˜DKšœ'˜'Kšœžœ˜Kšœ˜—Kšžœ˜—Kšœ˜K˜K˜—š ¡œžœ žœžœ"žœ˜VKšœ&žœ.™WKš¢(™(Kšœžœžœ #˜Oš žœžœžœžœž˜5Kšœžœ%˜>Kšœ;˜;Kšœžœ˜1KšœD˜DKšœ- ˜FKšžœžœžœ, ˜|Kšžœžœ ˜3Kšžœ˜—Kšžœ$  ˜FK˜——™™-KšœÀžœf™¨K™þKšœ•™•K™—Kšœžœžœ˜+šœžœžœ˜!Kšœ"žœ /˜VKšœžœ #˜DKšœžœžœžœ '˜PK˜K™UK™—šœ'žœ˜Fšœ ˜ K˜——š¡œžœžœ˜RKšœA™AKšœ žœ˜Kšœ žœ˜šžœ,žœžœž˜@Kšœ%˜%Kšœ#˜#Kšœ˜Kšžœ˜—Kšœžœ˜'Kšœ˜Kšœ žœ#˜1K˜K˜—š ¡œžœ0žœžœžœžœ˜iK™JKšœz™zKšœ žœ<™MKš¢(™(Kšœ*˜*š žœžœžœžœ #˜EKšœžœ˜1K˜Kšžœžœžœ žœžœžœžœ ˜JK˜Kš žœžœžœžœ  ˜LKš žœ žœžœžœžœ˜NK˜Kšœ˜Kšžœ˜—K˜K˜—š¡œžœžœ žœ˜PKšœ[žœžœ(™‹Kš¢(™(Kšœžœ˜Kšœ%˜%Kšœ žœžœ˜š žœžœžœžœ ˜3K˜Kš žœžœžœžœ,žœ˜WKšœ(˜(Kšžœ žœžœ žœ˜CKšžœ˜—Kš žœ žœžœžœžœ ˜;š žœžœžœžœ ˜>K˜Kšžœ žœžœžœ˜QKšžœ˜—K˜K˜—š ¡ œžœžœ0žœ žœžœ˜lKšœ1žœ™µKšœkžœ ˜|K˜#Kšœ2˜2šœžœ˜3Kšœ˜Kšœ ˜ —šœžœ˜0K˜Kšœ4˜4Kšœ$žœžœ˜KKšœžœ ˜—Kšžœ5žœ'˜bKšžœžœ˜+Kšžœ;˜?š žœžœžœ žœžœ 2˜VJšœ7˜7K˜Jšœ˜Jšœ;˜;K˜J˜—Jšžœ˜Jšžœ žœžœ&˜;Kšœ žœ˜"Kšœ˜Kšœ.žœ˜4šž˜Kšœ4˜4—K˜K˜—š ¡œžœžœ0žœ žœžœ˜pKšœkžœ ˜|K˜#Kšœžœ˜ Jš žœžœžœ žœžœžœ˜)K˜šžœ1žœžœž˜EKšœžœ˜:šžœžœ 0˜TKš žœžœžœžœ ˜ZKšœžœ˜ Kšžœ˜K˜—K˜ Kšžœ˜—Kšœ˜Kšœ.žœ˜4šž˜Kšœžœ˜—K˜K˜—š¡ œžœžœžœ˜?Kšœ#˜#Kšœ˜š žœžœžœ žœžœ .˜SKšœ žœ˜$Kšœ žœ˜"Kšœ˜KšœS˜SK˜—K˜K˜—š¡œ"˜4KšœJžœžœ™RKšœžœ ˜)Kšœžœ˜)Kšœžœ˜Kšœžœžœ˜.Kšœ žœ 0˜UKšœ ˜-šžœžœ&žœ˜2K™“Kšœžœ˜ š žœ-žœžœžœ ˜Uš žœ4žœžœžœ ˜XKšœžœ˜0Kšœ? ˜TKšžœ˜—Kšžœ˜—Kšžœžœžœ-˜>šžœžœ&žœ ˜IKšœ ˜5Kšžœ˜Kšœ˜—Kšœ˜—š žœžœžœžœ ˜=Kšœ žœ"˜/Kš œžœžœ žœžœžœ˜UKšžœ*žœžœ˜6Kšžœžœ žœžœ˜HKšœ˜Kšž˜—šžœžœžœ  ˜(Kšœ žœ"˜/KšžœHžœžœ˜TKšœ˜Kšžœ˜—Kšœ ˜5K˜K˜—š ¡ œžœžœ:žœžœ!˜Kšœq™qKšœR˜RKšœ2˜2Kšœ ˜-šžœžœ&žœ˜2K™CKšœ1žœ˜LKšžœžœžœ-˜>šžœžœ&žœ ˜IKšœ ˜5Kšžœžœ˜ Kšœ˜—Kšœ˜—Kšœ$˜$Kšœ˜Kšœ ˜5K˜——™ š¡ œž œžœžœ˜GKšœžœ£™¯™Kšœ žœ,™:šœ6™6Kšœ4ž™7Kšœqž™t——Kšœžœžœ ˜Kšœ žœžœžœ˜,šœ žœžœ˜Kšœžœ˜Kšœ *˜GKšœ ˜%Jšœžœžœžœ ˜*Kšœ˜K™6—š¡œ˜KšœB™BKšžœ  ˜*Kšœ˜—š¡œ˜!K™&Kšœ žœ˜Kšœ žœ˜Kšžœ%˜+Kšœ˜—š ¡ œžœ žœ žœžœ˜SKšœ/žœ™Ošžœžœžœž˜%šžœž˜šœ˜Kšœžœ˜Kšœžœ˜)K˜—šœ˜Kšœžœ˜Kšœžœ˜)K˜—Kšžœžœžœ˜K—Kšžœ˜—K˜—š¡ œ˜&Kšœžœ˜&Kšœžœ ˜$Kšœžœ˜$Kšœžœ˜%Kšœ˜K˜Kšœžœ˜&Kšžœžœžœ žœžœžœžœ˜HKšžœ"žœžœ ˜NKš žœžœžœ#žœžœ  ˜Sšžœžœž˜Kšœ# (˜KKšœ&žœ-˜VKšžœžœ 9˜J—K˜—š¡ œžœ˜$K™OK™zš¡ œžœ˜(Kšœ žœ˜Kšœžœ˜ Kšœ%˜%Kšœ ˜ šžœžœž˜Kšœžœžœ  ˜Kšœ˜Kšœ˜Kšžœ˜—Kšœ˜Kšœ6žœ9žœ ˜Jšœžœ ˜(K˜—Kšœ5˜5Kšœ žœ#˜0Kšœžœ˜ Kš žœžœžœ  Ðbc ˜:Kšžœ žœ6˜HKšžœ˜!K˜—KšœA˜AKšœžœ "˜8Kšœ @˜ZKšœžœ ˜$Kš ™KšœM˜MKš "™"Kšœžœ #˜Mšžœž˜#Kšœžœ%˜:Kšœ<˜