Types
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.
PlotGraphData: TYPE ~ REF PlotGraphDataRec;
PlotGraphDataRec:
TYPE ~
RECORD [
source: REF ANY ← NIL, -- either RoseValues or RoseCellInstance
bits: Ports.LevelSequence ← NIL, -- computed values will go in there
clientData: REF ANY ← NIL, -- only for RoseCellInstances
ropeProc: StateToRopeProc, -- only if display style is hexaV
parts: SEQUENCE size: CARDINAL OF Sample -- basic information for reconstitution
];
This is the data structure that permits to recompute values of flatwires at each time
roseGraphClass: PlotGraph.GraphClass ←
NEW[PlotGraph.GraphClassRec ← [
enumerate: RoseGraphEnumerate]];
Value recovery and assembly
NewPlotGraphData:
PROC [source:
REF
ANY, clientData:
REF
ANY, ropeProc: StateToRopeProc]
RETURNS [pgd: PlotGraphData] ~ {
Create the PlotGraphData with the right sizes for the RoseValues. The contents of parts are left to NIL. The bits field initialization is deferreed for roseInstances to InitPlotGraphData.
partCount: NAT ← 0;
bits: Ports.LevelSequence ← NIL;
WITH source
SELECT
FROM
rvl: Rosemary.RoseValues => {
-- this plot is for a CoreFlat wire
bitCount: NAT ← 0;
UNTIL rvl=
NIL
DO
val: Rosemary.RoseValue = rvl.first;
bitCount ← bitCount+val.fieldWidth;
partCount ← partCount + 1;
rvl ← rvl.rest;
ENDLOOP;
bits ← NEW[Ports.LevelSequenceRec[bitCount]];
};
roseInstance: Rosemary.RoseCellInstance => {
-- this plot is for an unexpanded cell
The bits LS is not setup yet as we don't know the size...
It will be setup the 1st time we find a non-NIL sample on the instance...
partCount ← 1;
};
ENDCASE => ERROR; -- internal bug, invalid argument
pgd ← NEW[PlotGraphDataRec[partCount]];
pgd.source ← source;
pgd.clientData ← clientData;
pgd.ropeProc ← ropeProc;
pgd.bits ← bits;
};
InitPlotGraphData:
PROC [handle: RoseDisplay, pgd: PlotGraphData, minStep:
INT]
RETURNS [effectiveStep:
INT ← 0] ~ {
Initialize pgd parts so that a first sample is valid at the required step number.
Assumes that the corresponding values have been made resident early enough using EnterSourceInRBT/ReadSamplesUsingRBT.
Returns the step at which the value is correct (may be >minStep, LAST[INT] means no value may be established).
Must be called under backing store lock!
WITH pgd.source
SELECT
FROM
rvl: Rosemary.RoseValues => {
-- this is a wire plot
FOR part:
NAT
IN [0..pgd.size)
DO
-- go up the sample chain up to minStep
data: LogData = NARROW[rvl.first.roseWire.data];
sample: Sample;
IF data=NIL OR data.list=NIL THEN RETURN [LAST[INT]]; -- not initialized !
sample ← data.list.head; -- cannot be NIL !
IF sample.step<=minStep
THEN {
-- try going forwards
UNTIL sample.next=
NIL
OR sample.next.step>minStep
DO
sample ← sample.next;
ENDLOOP;
};
effectiveStep ← MAX[effectiveStep, sample.step];
pgd.parts[part] ← sample;
rvl ← rvl.rest;
ENDLOOP;
};
roseInstance: Rosemary.RoseCellInstance => {
-- this is a cell state plot
The bits field of pgd is initialized here if we have the information.
data: LogData = NARROW[roseInstance.data];
sample: Sample;
IF data=NIL OR data.list=NIL THEN RETURN [LAST[INT]]; -- no info available
sample ← data.list.head; -- cannot be NIL !
IF pgd.bits=NIL THEN pgd.bits ← NEW[Ports.LevelSequenceRec[sample.size]];
IF sample.step<=minStep
THEN {
-- try going forwards
UNTIL sample.next=
NIL
OR sample.next.step>minStep
DO
sample ← sample.next;
ENDLOOP;
};
effectiveStep ← sample.step;
pgd.parts[0] ← sample; -- there is exactly 1 part in a cell state pgd
};
ENDCASE => ERROR;
};
AssemblePlotGraphDataBits:
PROC [pgd: PlotGraphData]
RETURNS [nextStep:
INT] ~ {
Assemble the bits for the current step, and position everything for the next step. Returns LAST[INT] if the computed value is the last one. pgd.bits must have been initialized.
Must be called under backing store lock!
firstFreeBit: NAT ← 0;
bits: Ports.LevelSequence ← pgd.bits;
nextStep ← 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 nextStep ← MIN[sample.next.step, nextStep];
ENDLOOP;
IF nextStep=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.step=nextStep THEN pgd.parts[i] ← sample.next;
ENDLOOP;
};
GetValueAt:
PROC [handle: RoseDisplay, pgd: PlotGraphData, time:
INT]
RETURNS [ls: Ports.LevelSequence ←
NIL] ~ {
Update the pgd to get a decent value in bits for time, return NIL if it's impossible.
atStep: INT ← LastStep[handle, time]; -- convert to step specification
possibleStep: INT;
LockBackingStore[handle]; -- critical here...
possibleStep ← InitPlotGraphData[handle, pgd, atStep];
IF possibleStep>atStep
THEN {
-- try from disk...
table: RedBlackTree.Table ← EnterSourceInRBT[NIL, pgd.source, atStep];
IF table#NIL THEN ReadSamplesUsingRBT[handle, table, atStep];
possibleStep ← InitPlotGraphData[handle, pgd, atStep];
IF possibleStep>atStep THEN RETURN [NIL]; -- we lost the battle...
};
IF possibleStep<=atStep
THEN {
[] ← AssemblePlotGraphDataBits[pgd];
ls ← NEW [Ports.LevelSequenceRec[pgd.bits.size]];
Ports.CopyLS[from: pgd.bits, to: ls];
};
UnlockBackingStore[handle]; -- may now safely release
};
WireTimeValue:
PUBLIC
PROC [handle: RoseDisplay, flatWire: CoreFlat.FlatWire, time:
INT]
RETURNS [value: Ports.LevelSequence] = {
Return the value of a wire at a given time. Will return NIL if no value may be established for the required time.
roseValues: Rosemary.RoseValues ← Rosemary.GetValues[handle.simulation, flatWire];
value ← GetValueAt[handle, NewPlotGraphData[roseValues, NIL, NIL], time];
};
StateTimeValue:
PUBLIC
PROC [handle: RoseDisplay, flatCell: CoreFlat.FlatCellType, time:
INT]
RETURNS [value: Ports.LevelSequence] = {
Return the value of a wire at a given time. Will return NIL if no value may be established for the required time.
roseInstance: Rosemary.RoseCellInstance = NARROW [ RefTab.Fetch[handle.simulation.coreToRoseInstances, flatCell].val];
IF roseInstance=NIL THEN ERROR Rosemary.NotInstantiated[];
value ← GetValueAt[handle, NewPlotGraphData[roseInstance, NIL, NIL], time];
};
Client plotting interface
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<flat2.flatCell.path.length THEN RETURN [less];
FOR i: CoreFlat.InstancePathIndex
IN [0..flat1.flatCell.path.length)
DO
one: BOOL ← flat1.flatCell.path.bits[i];
other: BOOL ← flat2.flatCell.path.bits[i];
SELECT
TRUE
FROM
one AND (NOT other) => RETURN [greater];
(NOT one) AND other => RETURN [less];
ENDCASE;
ENDLOOP;
IF flat1.flatCell.recastCount>flat2.flatCell.recastCount THEN RETURN [greater];
IF flat1.flatCell.recastCount<flat2.flatCell.recastCount THEN RETURN [less];
RETURN[Rope.Compare[CoreOps.GetFullWireName[cell.public, flat1.wire], CoreOps.GetFullWireName[cell.public, flat2.wire]]];
};
FindLeaves: CoreOps.EachWireProc = {
IF Ports.WirePortType[cell, wire].levelType#composite
THEN {
subWires ← FALSE;
displayWires ← CONS[NEW[CoreFlat.FlatWireRec ← [flatCell: flatCell, wireRoot: public, wire: wire]], displayWires];
};
};
cell: Core.CellType ← CoreFlat.ResolveFlatCellType[root, flatCell].cellType;
IF CoreOps.VisitWire[cell.public, FindLeaves] THEN ERROR;
TRUSTED {displayWires ← LOOPHOLE[List.Sort[LOOPHOLE[displayWires], CompareFlatWires]]};
};
LSToRope: StateToRopeProc ~ {
This is restricted version of Ports.LSToRope that does not expand partial X's down to bit level. This is better for plotting as it guarantees that the resulting rope has a constant size.
rope ← Ports.LSToRope[value];
size: NAT = value.size;
scratch: REF TEXT ← RefText.New[(size+3)/4];
bitsInDigit: NAT;
digitBitCount: NAT ← 0;
someX: BOOL ← FALSE;
digitVal: CARDINAL ← 0;
bitsInDigit ← IF size MOD 4 = 0 THEN 4 ELSE size MOD 4;
bitsInDigit ← ((size+3) MOD 4) + 1;
FOR bit:
NAT
IN [0..size)
DO
SELECT value[bit]
FROM
H => digitVal ← 2*digitVal + 1;
L => digitVal ← 2*digitVal;
ENDCASE => someX ← TRUE; -- don't care about digitVal now...
digitBitCount ← digitBitCount + 1;
IF digitBitCount=bitsInDigit
THEN
{
IF someX THEN scratch ← RefText.InlineAppendChar[scratch, 'X]
ELSE scratch ← Convert.AppendCard[to: scratch, from: digitVal, base: 16, showRadix: FALSE];
bitsInDigit ← 4;
digitBitCount ← 0;
someX ← FALSE;
digitVal ← 0;
};
ENDLOOP;
rope ← Rope.FromRefText[scratch];
};
AddGraph:
PROC [handle: RoseDisplay, pgd: PlotGraphData, name:
ROPE, style: PlotGraph.DrawingStyle, maxChars:
NAT ← 8] ~ {
Add a graph to the plot
plot: PlotGraph.Plot ← handle.plot;
graph: PlotGraph.Graph ←
NEW[PlotGraph.GraphRec ← [
class: roseGraphClass,
data: pgd]];
axis: PlotGraph.Axis ←
NEW[PlotGraph.AxisRec ← [
graphs: LIST [graph],
bounds: [0.0, 0.0, 10.0, 6.0],
name: name,
style: style,
maxChars: maxChars,
axisData: [X: [ticks: 1.0, visible: TRUE], Y: [ticks: 1.0, visible: TRUE]]
]];
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];
};
AddWireToPlot:
PUBLIC
PROC [handle: RoseDisplay, wire: CoreFlat.FlatWire]
RETURNS [msg:
ROPE ←
NIL] = {
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...
roseValues: Rosemary.RoseValues ← Rosemary.GetValues[handle.simulation, wire ! Rosemary.NotInstantiated => GOTO NoSuchWire];
pgd: PlotGraphData ← NewPlotGraphData[roseValues, NIL, LSToRope];
name: ROPE ← CoreFlat.WirePathRope[handle.cellType, wire^];
IF Rope.Equal[Rope.Substr[name, 0, 7], "public."] THEN name ← Rope.Substr[name, 7];
IF pgd.bits.size=1 THEN AddGraph[handle, pgd, name, analog]
ELSE AddGraph[handle, pgd, name, hexaV, (pgd.bits.size+3)/4];
EXITS
NoSuchWire => msg ← "Not stored as a Rosemary wire";
};
stateToMaxCharsProcProp: PUBLIC ATOM ← $RoseStateToMaxCharsProc;
stateToRopeProcProp:
PUBLIC
ATOM ← $RoseStateToRopeProc;
AddStateToPlot:
PUBLIC
PROC [handle: RoseDisplay, flatCell: CoreFlat.FlatCellType, data:
REF
ANY ←
NIL]
RETURNS [msg:
ROPE ←
NIL] = {
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...
flat: CoreFlat.FlatCellType ← NEW[CoreFlat.FlatCellTypeRec ← flatCell^];
cellType: Core.CellType ← NIL;
cellInstance: CoreClasses.CellInstance ← NIL;
roseInstance: Rosemary.RoseCellInstance ← NIL;
IF CoreFlat.BelowCutSet[handle.cellType, flatCell^, handle.cutSet] THEN RETURN [msg: "Below simulation cutset"];
[,cellInstance, cellType] ← CoreFlat.ResolveFlatCellType[handle.cellType, flat^];
UNTIL CoreFlat.CutSetMemberResolved[flat^, cellInstance, cellType, handle.cutSet]
DO
cellType ← CoreOps.Recast[cellType];
flat.recastCount ← flat.recastCount + 1;
ENDLOOP;
roseInstance ← NARROW [ RefTab.Fetch[handle.simulation.coreToRoseInstances, flat].val];
IF roseInstance#
NIL
THEN {
charsProc: REF StateToMaxCharsProc ← NARROW [CoreProperties.InheritCellTypeProp[cellType, stateToMaxCharsProcProp]];
ropeProc: REF StateToRopeProc ← NARROW [CoreProperties.InheritCellTypeProp[cellType, stateToRopeProcProp]];
name: ROPE ← CoreFlat.CellTypePathRope[handle.cellType, flat^];
pgd: PlotGraphData ← NewPlotGraphData[roseInstance, data, IF ropeProc=NIL THEN LSToRope ELSE ropeProc^];
AddGraph[handle, pgd, name, hexaV, IF charsProc=NIL THEN (pgd.bits.size+3)/4 ELSE charsProc^[roseInstance.state, data]];
}
ELSE msg ← "Not stored as a Rosemary cell instance";
};
RemoveFromPlot:
PROC [handle: RoseDisplay, source:
REF
ANY, clientData:
REF
ANY]
RETURNS [found:
BOOL ←
FALSE] ~ {
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.source=source
AND pgd.clientData=clientData
THEN {
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];
IF found THEN PlotGraph.RefreshPlot[plot: plot, eraseFirst: TRUE];
};
RemoveWireFromPlot:
PUBLIC
PROC [handle: RoseDisplay, wire: CoreFlat.FlatWire]
RETURNS [found:
BOOL ←
FALSE] = {
roseValues: Rosemary.RoseValues ← Rosemary.GetValues[handle.simulation, wire ! Rosemary.NotInstantiated => GOTO NoSuchWire];
found ← RemoveFromPlot[handle, roseValues, NIL];
EXITS
NoSuchWire => NULL;
};
RemoveStateFromPlot:
PUBLIC
PROC [handle: RoseDisplay, flatCell: CoreFlat.FlatCellType, data:
REF
ANY ←
NIL]
RETURNS [found:
BOOL ←
FALSE] = {
roseInstance: Rosemary.RoseCellInstance = NARROW [ RefTab.Fetch[handle.simulation.coreToRoseInstances, flatCell].val];
IF roseInstance#NIL THEN found ← RemoveFromPlot[handle, roseInstance, data];
};
PlotGraph service
DeltaFinished:
PUBLIC
PROC [handle: RoseDisplay, time:
INT] = {
plot: PlotGraph.Plot ← handle.plot;
LogTime[handle, time];
IF plot#
NIL
AND plot.data#
NIL
THEN {
-- the plot viewer might have been destroyed !
oldUpper: REAL = plot.upperBounds.x-1.0;
newUpper: REAL = Real.Float[time];
plot.upperBounds.x ← newUpper;
PlotGraph.RefreshPlot[plot: plot, within: [oldUpper, 0.0, newUpper-oldUpper, 6.0]];
};
};
PGDStepProc:
TYPE ~
PROC [pgd: PlotGraphData, rTime:
REAL, notLast:
BOOL];
EnumeratePGD:
PROC [handle: RoseDisplay, pgd: PlotGraphData, from:
REAL, to:
REAL, eachStep: PGDStepProc, showSteps:
BOOL] ~ {
Enumerate the items in the pgd in the time interval [from..to] (those are the plot bounds), call eachTime for each sample
The details shown on the plot are specified by handle.plotStyle:
allSteps => everything is shown in full detail, may be unreadable, fractional time
waveSteps => show steps only if showSteps is TRUE (waveforms), fractional time
noSteps => never show steps, force all display point on integer time boundaries
Must be called under backing store lock
firstCur, lastCur, step: INT; -- steps for current time: firstCur<=step<=lastCur, in fromTime
firstLogged: INT; -- first time time at which we succeed to get a sample
fromTime: INT ← Real.Floor[MAX[from, 0.0]]; -- we should start roughly from here
fractional: BOOL; -- TRUE means time is fractional, FALSE time is forced to INT
firstCur ← FirstStep[handle, fromTime]; -- first step in fromTime
lastCur ← LastStep[handle, fromTime]; -- last step in fromTime
step ← firstCur;
firstLogged ← InitPlotGraphData[handle, pgd, firstCur];
IF firstLogged>firstCur
THEN {
-- read more data from disk
table: RedBlackTree.Table ← NIL;
plot: PlotGraph.Plot = handle.plot;
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 ← EnterSourceInRBT[table, thisPGD.source, firstCur]; -- collect source values
ENDLOOP;
ENDLOOP;
IF table#NIL THEN ReadSamplesUsingRBT[handle, table, firstCur];
firstLogged ← InitPlotGraphData[handle, pgd, firstCur];
};
-- At this point, firstLogged contains the 1st really available point of data.
SELECT handle.plotStyle
FROM
allSteps => {showSteps ← TRUE; fractional ← TRUE};
waveSteps => {fractional ← TRUE};
noSteps => {showSteps ← FALSE; fractional ← FALSE};
ENDCASE => ERROR;
IF step<=handle.simulation.mosSimTime
THEN
DO
nextStep: INT = AssemblePlotGraphDataBits[pgd];
rTime: REAL;
UNTIL step<=lastCur
DO
-- advance `fromTime' till it reaches step
fromTime ← fromTime+1;
firstCur ← lastCur+1;
lastCur ← LastStep[handle, fromTime];
ENDLOOP;
IF showSteps
OR nextStep>lastCur
THEN {
-- show step, or last of time slot
rTime ← Real.Float[fromTime];
IF fractional THEN rTime ← rTime - (lastCur-step)/Real.Float[lastCur-firstCur+1];
IF rTime<to THEN eachStep[pgd, rTime, TRUE] ELSE GO TO EndOfWindow;
};
step ← nextStep;
IF step>handle.simulation.mosSimTime THEN GO TO EndOfWindow;
REPEAT
EndOfWindow => eachStep[pgd, to, FALSE];
ENDLOOP;
};
RoseGraphEnumerate: PlotGraph.GraphEnumerateProc = {
[plot: Plot, graph: Graph, bounds: Rectangle, eachPoint: PointProc, data: REF ANY]
ENABLE ABORTED => GOTO Aborted; -- in order to help debug...
handle: RoseDisplay ← NARROW [plot.data];
pgd: PlotGraphData ← NARROW [graph.data];
low: REAL = MAX [bounds.x, 0];
high: REAL = handle.lastValidTime+1; -- bounds.w is incorrect in PlotGraph
LockBackingStore[handle]; -- critical here...
WITH pgd.source
SELECT
FROM
rvl: Rosemary.RoseValues => {
-- this plot is for a CoreFlat wire
value: REAL; notFirst: BOOL ← FALSE;
EachAnalogStep: PGDStepProc ~ {
IF notFirst THEN [] ← eachPoint[x: rTime, y: value]
ELSE notFirst ← TRUE;
IF notLast
THEN {
value ← SELECT pgd.bits[0] FROM L => 0.0, X => 2.5, H => 5.0, ENDCASE => ERROR;
[] ← eachPoint[x: rTime, y: value]
};
};
EachWireStep: PGDStepProc ~ {
value: ROPE = pgd.ropeProc[NIL, pgd.bits, pgd.clientData];
[] ← eachPoint[x: rTime, y: 0.0, rope: value]
};
IF pgd.bits.size=1 THEN EnumeratePGD[handle, pgd, low, high, EachAnalogStep, TRUE]
ELSE EnumeratePGD[handle, pgd, low, high, EachWireStep, FALSE];
};
roseInstance: Rosemary.RoseCellInstance => {
-- this plot is for an unexpanded cell
EachStateStep: PGDStepProc ~ {
value: ROPE = pgd.ropeProc[roseInstance.state, pgd.bits, pgd.clientData];
[] ← eachPoint[x: rTime, y: 0.0, rope: value]
};
EnumeratePGD[handle, pgd, low, high, EachStateStep, FALSE];
};
ENDCASE => ERROR;
UnlockBackingStore[handle]; -- may now safely release
EXITS
Aborted => UnlockBackingStore[NARROW [plot.data]];
};