<> <> <> <<>> <> <<>> DIRECTORY Basics, Convert, Core, CoreClasses, CoreFlat, CoreOps, CoreProperties, FS, IO, List, PlotGraph, Ports, Real, RedBlackTree, RefTab, RefText, Rope, Rosemary, RosemaryUser; RosemaryUserBackingImpl: CEDAR MONITOR IMPORTS Basics, Convert, CoreFlat, CoreOps, CoreProperties, FS, IO, List, PlotGraph, Ports, Real, RedBlackTree, RefTab, RefText, Rope, Rosemary, RosemaryUser EXPORTS RosemaryUser ~ BEGIN OPEN RosemaryUser; <> <> <> <<- samples are kept in memory only if needed (i.e. if they are plotted). Samples are time-stamped with the MosSim step number. Relationship between `Eval time' and steps is achieved through an array in the handle that keeps track of the endpoints of each Eval. This permits to support time-logging or step-logging, as well as time-plotting or step-plotting.>> <> <<- two samples are always kept in memory per wire in order to allow collapsing identical values (c.f. LogData). The information is attached as data field to RoseWires and RoseInstances.>> <<- samples on disk are chained backwards through disk pointers to accelerate recovery. Only deltas in file position and time are kept on disk, using a variable length encoding, to reduce storage. The exact format per sample is < where>> <<>> <<>> <> <> <<>> <> <<- 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...>> <> Sample: TYPE = REF SampleRec; SampleRec: TYPE = RECORD [ next: Sample _ NIL, step: INT _ LAST[INT], -- MosSim step number at which this event occured value: PACKED SEQUENCE size: CARDINAL OF Ports.Level ]; <> <<>> SampleList: TYPE = REF SampleListRec; SampleListRec: TYPE ~ RECORD [ head: Sample, -- the head of the list. The tail is always LogData.current beforeHeadPos: INT, -- the file position of the predecessor of head, <0 => no predecessor inTable: BOOL _ FALSE -- used internally to RBT management, don't modify ... ]; <> LogData: TYPE = REF LogDataRec; LogDataRec: TYPE = RECORD [ lastFilePos: INT _ -1, -- the file position of the last sample written list: SampleList _ NIL, -- if non-nil, the samples for this object are currently kept in memory previous: Sample _ NIL, -- the previous sample currentTime: INT _ -1, -- time of current sample current: Sample _ NIL -- the sample currently taken, not guaranteed if time=lastUpdateTime ]; <> <<- current.next = NIL>> <<- current.step = value of MosSimStep at last call to Update>> <<- current will never be modified once step of Update call > current.step>> <<- 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!).>> <> 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; }; WillWrite: PROC [handle: RoseDisplay, nBytes: NAT] ~ { <> to: REF TEXT = handle.wBuff; IF to.maxLength-nBytes < to.length THEN FlushWBuff[handle, nBytes]; }; FlushWBuff: PROC [handle: RoseDisplay, nBytes: NAT] ~ { <> IO.PutBlock[handle.ps, handle.wBuff]; IF handle.wBuff.maxLength> <> <<0XXXXXXX => delta IN [0..128)>> <<10XXXXXX XXXXXXXX => delta IN [0..16384)>> <<110XXXXX XXXXXXXX XXXXXXXX => delta IN [0..2097152)>> <<111XXXXX XXXXXXXX XXXXXXXX XXXXXXXX => delta IN [0..536870912)>> byte: CARD32 = 256; limit1: CARD32 = 080H; limit2: CARD32 = 040H*byte; limit3: CARD32 = 020H*byte*byte; limit4: CARD32 = 020H*byte*byte*byte; v: Basics.LongNumber; v.li _ delta; SELECT v.lc FROM { -- write single byte <> PutByte[handle, v.ll]; handle.filePos _ handle.filePos+1; }; { -- write 2 bytes <> v.lo _ Basics.BITOR[v.lo, 08000H]; <> PutByte[handle, v.lh]; PutByte[handle, v.ll]; handle.filePos _ handle.filePos+2; }; { -- write 3 bytes <> v.hi _ Basics.BITOR[v.hi, 000C0H]; <> PutByte[handle, v.hl]; PutByte[handle, v.lh]; PutByte[handle, v.ll]; handle.filePos _ handle.filePos+3; }; { -- write 4 bytes <> v.hi _ Basics.BITOR[v.hi, 0E000H]; <> PutByte[handle, v.hh]; PutByte[handle, v.hl]; PutByte[handle, v.lh]; PutByte[handle, v.ll]; handle.filePos _ handle.filePos+4; }; ENDCASE => ERROR; -- too large }; Read29Bits: PROC [s: IO.STREAM] RETURNS [delta: INT32] ~ { <> v: Basics.LongNumber; v.lc _ 0; v.ll _ IO.GetByte[s]; SELECT TRUE FROM Basics.BITAND[v.lo, 00080H]=0 => { -- single byte <=limit1 THEN ERROR;>> }; Basics.BITAND[v.lo, 00040H]=0 => { -- 2 bytes v.lo _ Basics.BITAND[Basics.BITSHIFT[v.lo, 8], 03F00H]; v.ll _ IO.GetByte[s]; <=limit2 THEN ERROR;>> }; Basics.BITAND[v.lo, 00020H]=0 => { -- 3 bytes v.hi _ Basics.BITAND[v.lo, 0001FH]; v.lh _ IO.GetByte[s]; v.ll _ IO.GetByte[s]; <=limit3 THEN ERROR;>> }; ENDCASE => { -- 4 bytes v.hi _ Basics.BITAND[Basics.BITSHIFT[v.lo, 8], 01F00H]; v.hl _ IO.GetByte[s]; v.lh _ IO.GetByte[s]; v.ll _ IO.GetByte[s]; <=limit4 THEN ERROR;>> }; RETURN [delta: v.li]; }; WriteSample: PROC [handle: RoseDisplay, sample: Sample, prvPos: INT, nextStep: INT] RETURNS [pos: INT] ~ { <> <> deltaPos: INT; WillWrite[handle, 8+sample.size]; -- max number of bytes that will be written pos _ handle.filePos; IF prvPos<0 THEN deltaPos _ 0 ELSE { deltaPos _ pos-prvPos; IF deltaPos<=0 THEN ERROR; -- something is very wrong... }; Write29Bits[handle, deltaPos]; Write29Bits[handle, nextStep - sample.step]; -- must be >=0 !!! FOR i: NAT IN [0..sample.size) DO PutByte[handle, ORD[sample.value[i]]]; ENDLOOP; handle.filePos _ handle.filePos+sample.size; }; ReadSample: PROC [s: IO.STREAM, pos: INT, into: Sample, nextStep: INT] RETURNS [prvPos: INT] ~ { <> <> deltaPos: INT; IO.SetIndex[s, pos]; deltaPos _ Read29Bits[s]; IF deltaPos=0 THEN prvPos _ -1 ELSE { prvPos _ pos - deltaPos; IF prvPos<0 THEN ERROR; -- inconsistency in file management }; into.step _ nextStep - Read29Bits[s]; FOR i: NAT IN [0..into.size) DO into.value[i] _ VAL[IO.GetByte[s]]; ENDLOOP; }; <> 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]]; }; EnterSourceInRBT: PROC [table: RedBlackTree.Table, source: REF ANY, min: INT _ 0] RETURNS [newTable: RedBlackTree.Table] ~ { <> <> <> EnterLogDataInRBT: PROC [data: LogData] ~ { IF data#NIL THEN { -- nothing ever logged for this guy... list: SampleList _ 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.step<=min THEN RETURN; -- nothing to fetch IF newTable=NIL THEN newTable _ RedBlackTree.Create[RBTGetKey, RBTCompare]; IF NOT list.inTable THEN { -- may not insert twice in a RedBlackTree RedBlackTree.Insert[newTable, list, list]; list.inTable _ TRUE; }; }; }; newTable _ table; WITH source SELECT FROM rvl: Rosemary.RoseValues => UNTIL rvl=NIL DO -- this is for a CoreFlat wire EnterLogDataInRBT[NARROW [rvl.first.roseWire.data]]; rvl _ rvl.rest; -- advance list pointer ENDLOOP; roseInstance: Rosemary.RoseCellInstance => EnterLogDataInRBT[NARROW [roseInstance.data]]; ENDCASE => ERROR; }; ReadSamplesUsingRBT: PROC [handle: RoseDisplay, table: RedBlackTree.Table, min: INT] ~ { <> <> <> stream: IO.STREAM = handle.ps; FlushWBuff[handle, 0]; -- ensure we are really at end of file and the buffer is empty 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, list.head.step]; sample.next _ list.head; list.head _ sample; -- insert as head of list IF sample.step>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, handle.filePos]; -- go back to end of file }; <