ChipTestImpl.mesa - Petit Dolphin chip tester client interface
last edited by McCreight, January 24, 1984 6:51 pm
DIRECTORY
SystemVersion USING [machineType],
ChipTest,
ChipTestRemote,
ChipTestRemoteRpcControl USING [InterfaceRecord, ImportNewInterface],
Basics USING [BITAND, BITXOR],
PrincOps USING [zMISC],
Process USING [Detach, Pause, MsecToTicks],
PupDefs USING [GetPupAddress, GetMyName],
PupTypes USING [fillInSocketID],
Rope USING [ROPE, Length],
SafeStorage USING [EstablishFinalization, CantEstablishFinalization,
ReEstablishFinalization, FinalizationQueue, NewFQ, FQNext],
VM USING [Interval, nullInterval, PageNumber, Allocate, Free, Pin, Unpin,
AddressForPageNumber, WordsForPages, PagesForWords];
ChipTestImpl: CEDAR MONITOR IMPORTS SystemVersion, ChipTestRemoteRpcControl, Basics, Process, PupDefs, Rope, SafeStorage, VM
EXPORTS ChipTest =
BEGIN OPEN ChipTest;
trues: PUBLIC ChannelVecRef ← NEW[ChannelVec = ALL[TRUE]];
falses: PUBLIC ChannelVecRef ← NEW[ChannelVec = ALL[FALSE]];
Types that go in the data field of a TesterOb...
LocalTester: TYPE = RECORD [ reserved: BOOLFALSE ];
LocalTesterRef: TYPE = REF LocalTester;
RemoteTester: TYPE = RECORD [
interface: ChipTestRemoteRpcControl.InterfaceRecord,
stores: ChipTestRemote.RegStores
];
RemoteTesterRef: TYPE = REF RemoteTester;
StepControl: TYPE = MACHINE DEPENDENT RECORD [
quadWdCnt (0: 0..1): [1..4],
checkSecond (0: 2..2): BOOLEANFALSE,
checkFirst (0: 3..3): BOOLEANFALSE];
StepControlArray: TYPE = PACKED ARRAY [0..0) OF StepControl;
TestDataArray: TYPE = RECORD [
finalI: ChannelVec,
t: ARRAY [0..0) OF WORD];
BufDesc: TYPE = RECORD [
base: Tester,
unSteppedTestData: [0..16] ← 0,
i: ChannelVecRef ← NIL,
stepCount, stepMaxCount: INT ← 0,
stepInterval: VM.Interval ← VM.nullInterval,
steps: LONG POINTER TO StepControlArray ← NIL,
testDataSize, testDataMaxSize: INT ← 0,
testDataInterval: VM.Interval ← VM.nullInterval,
testData: LONG POINTER TO TestDataArray ← NIL
];
BufRef: TYPE = REF BufDesc;
Window: TYPE = RECORD [
buf: Tester,
firstWord: INT,
check, abd: BOOLFALSE,
comm: LoadABD ← [command: loadD]
];
WindowRef: TYPE = REF Window;
localTester: Tester = NEW[TesterOb ← [
flush: FlushLocal,
writeR: WriteRLocal,
readI: ReadILocal,
data: NEW[LocalTester ← []]]];
OpenTester: PUBLIC PROC [server: Rope.ROPENIL] RETURNS [Tester] =
BEGIN
t: Tester;
IF server = NIL OR Rope.Length[server]=0 OR
PupDefs.GetPupAddress[PupTypes.fillInSocketID, PupDefs.GetMyName[]]=PupDefs.GetPupAddress[PupTypes.fillInSocketID, server] THEN
BEGIN
IF SystemVersion.machineType # dolphin THEN SIGNAL IsntADolphin;
t ← localTester;
END
ELSE
BEGIN
interface: ChipTestRemoteRpcControl.InterfaceRecord = ChipTestRemoteRpcControl.ImportNewInterface[interfaceName: [instance: server]];
t ← NEW[TesterOb ← [
flush: FlushRemote,
writeR: WriteRRemote,
readI: ReadIRemote,
data: NEW[RemoteTester ← [interface: interface]]]];
finalizeList ← CONS[t.data, finalizeList];
END;
Initialize[t];
RETURN[t];
END;
Initialize: PUBLIC ENTRY PROC [t: Tester, aToB, aToI: NSec ← 100] =
TRUSTED BEGIN
ENABLE UNWIND => NULL;
Initializer: TYPE = MACHINE DEPENDENT RECORD [
unused (0: 0..13): [0..0] ← 0,
enableWakeup (0: 14..14): BOOLFALSE,
notReset (0: 15..15): BOOLTRUE];
t.d ← t.a ← t.b ← NIL;
t.writeR[t: t, reg: init, value: LOOPHOLE[Initializer[]]];
t.writeR[t: t, reg: control, value: LOOPHOLE[SetFirstIGroup[group: 0]]];
InternalPutABDelay[t: t, d: aToB];
InternalPutAIDelay[t: t, d: aToI];
t.flush[t: t];
END;
aToBOffset: NSec ← 0;
PutABDelay: PUBLIC ENTRY PROC [t: Tester, d: NSec] = {InternalPutABDelay[t: t, d: d ! UNWIND => NULL]};
InternalPutABDelay: PROC [t: Tester, d: NSec] = INLINE
{PutComputedDelay[t, [clock: B], d-aToBOffset]};
aToIOffset: NSec ← 0;
PutAIDelay: PUBLIC ENTRY PROC [t: Tester, d: NSec] = {InternalPutAIDelay[t: t, d: d ! UNWIND => NULL]};
InternalPutAIDelay: PROC [t: Tester, d: NSec] = INLINE
{PutComputedDelay[t, [clock: I], d-aToIOffset]};
PutComputedDelay: PROC [t: Tester, comm: LoadDelay, delay: NSec] =
TRUSTED BEGIN
delay ← MAX[0, MIN[180, delay]];
Petit claims that delay greater than this will cause the pulse in the delay line to collapse due to asymmetries.
comm.delay1 ← delay/16;
delay ← delay MOD 16;
comm.delay2 ← delay/2;
comm.delay3 ← delay MOD 2;
t.writeR[t: t, reg: control, value: LOOPHOLE[comm]];
END;
PutDisables: PUBLIC ENTRY PROC [ t: Tester, p: ChannelVecRef, makeBufWindow: BOOLFALSE ] RETURNS [ window: Tester ] =
{RETURN[InternalPutDisables[t, p, makeBufWindow ! UNWIND => NULL]]};
InternalPutDisables: PROC [ t: Tester, p: ChannelVecRef, makeBufWindow: BOOLFALSE ] RETURNS [ window: Tester ] =
BEGIN
IF p#NIL THEN
BEGIN
window ← DoLoadABD[t: t, comm: LoadABD[command: loadD], old: t.d, new: p, makeBufWindow: makeBufWindow];
IF t.d=NIL THEN t.d ← NEW[ChannelVec];
t.d^ ← p^;
IF window#NIL THEN {window.d ← t.d; t.d ← NIL};
END
ELSE window ← NIL;
END;
GetDisables: PUBLIC ENTRY PROC [t: Tester] RETURNS [ChannelVecRef] =
{RETURN[IF t.d=NIL THEN NIL ELSE NEW[ChannelVec ← t.d^]]};
PutABuf: PUBLIC ENTRY PROC [ t: Tester, p: ChannelVecRef, makeBufWindow: BOOLFALSE ] RETURNS [ window: Tester ] =
{RETURN[InternalPutABuf[t, p, makeBufWindow ! UNWIND => NULL]]};
InternalPutABuf: PROC [ t: Tester, p: ChannelVecRef, makeBufWindow: BOOLFALSE ] RETURNS [ window: Tester ] =
BEGIN
IF p#NIL THEN
BEGIN
window ← DoLoadABD[t: t, comm: LoadABD[command: loadA], old: t.a, new: p, makeBufWindow: makeBufWindow];
IF t.a=NIL THEN t.a ← NEW[ChannelVec];
t.a^ ← p^;
IF window#NIL THEN {window.d ← t.a; t.a ← NIL};
END
ELSE window ← NIL;
END;
GetABuf: PUBLIC ENTRY PROC [t: Tester] RETURNS [ChannelVecRef] =
{RETURN[IF t.a=NIL THEN NIL ELSE NEW[ChannelVec ← t.a^]]};
PutBBuf: PUBLIC ENTRY PROC [ t: Tester, p: ChannelVecRef, makeBufWindow: BOOLFALSE ] RETURNS [ window: Tester ] =
{RETURN[InternalPutBBuf[t, p, makeBufWindow ! UNWIND => NULL]]};
InternalPutBBuf: PROC [ t: Tester, p: ChannelVecRef, makeBufWindow: BOOLFALSE ] RETURNS [ window: Tester ] =
BEGIN
IF p#NIL THEN
BEGIN
window ← DoLoadABD[t: t, comm: LoadABD[command: loadB], old: t.b, new: p, makeBufWindow: makeBufWindow];
IF t.b=NIL THEN t.b ← NEW[ChannelVec];
t.b^ ← p^;
IF window#NIL THEN {window.d ← t.b; t.b ← NIL};
END
ELSE window ← NIL;
END;
GetBBuf: PUBLIC ENTRY PROC [t: Tester] RETURNS [ChannelVecRef] =
{RETURN[IF t.b=NIL THEN NIL ELSE NEW[ChannelVec ← t.b^]]};
DoLoadABD: PROC [ t: Tester, comm: LoadABD, old, new: ChannelVecRef, makeBufWindow: BOOL ] RETURNS [ window: Tester ← NIL ] =
TRUSTED BEGIN
IF makeBufWindow AND new=NIL THEN new ← NEW[ChannelVec ← ALL[FALSE]];
IF new#NIL THEN
BEGIN
WITH t.data SELECT FROM
b: BufRef =>
BEGIN
IF makeBufWindow THEN
BEGIN
window ← NEW[TesterOb ← [
data: NEW[Window ← [buf: t, firstWord: b.testDataSize,
abd: TRUE, comm: comm]]
]];
old ← NIL;
END;
FOR i: ByteIndex IN ByteIndex DO
IF old=NIL OR
LOOPHOLE[new, REF ChannelByteVec][i]#LOOPHOLE[old, REF ChannelByteVec][i] THEN
BEGIN
comm.pinGroup ← i;
comm.data ← LOOPHOLE[new, REF ChannelByteVec][i];
t.writeR[t: t, reg: control, value: LOOPHOLE[comm]];
END;
ENDLOOP;
END;
w: WindowRef =>
BEGIN
IF w.abd AND w.comm=comm THEN
BEGIN
buf: BufRef = NARROW[w.buf.data];
FOR i: ByteIndex IN ByteIndex DO
LOOPHOLE[@buf.testData.t[w.firstWord+i], LONG POINTER TO LoadABD].data ← LOOPHOLE[new, REF ChannelByteVec][i];
ENDLOOP;
END
ELSE ERROR IllegalOp;
END;
ENDCASE =>
FOR i: ByteIndex IN ByteIndex DO
comm.pinGroup ← i;
comm.data ← LOOPHOLE[new, REF ChannelByteVec][i];
t.writeR[t: t, reg: control, value: LOOPHOLE[comm]];
ENDLOOP;
END;
END;
Step: PUBLIC ENTRY PROC [t: Tester] =
TRUSTED BEGIN
ENABLE UNWIND => NULL;
IF t.d = NIL THEN [] ← InternalPutDisables[t: t, p: NEW[ChannelVec ← ALL[TRUE]]];
IF t.a = NIL THEN [] ← InternalPutABuf[t: t, p: NEW[ChannelVec ← ALL[FALSE]]];
IF t.b = NIL THEN [] ← InternalPutBBuf[t: t, p: NEW[ChannelVec ← ALL[TRUE]]];
WITH t.data SELECT FROM
b: BufRef =>
BEGIN
IF b.unSteppedTestData>0 AND NOT
LOOPHOLE[b.testData.t[b.testDataSize-1], JustStrobe].strobe THEN
LOOPHOLE[b.testData.t[b.testDataSize-1], JustStrobe].strobe ← TRUE
turn on the strobe bit in the last command
ELSE t.writeR[t: t, reg: control, value: LOOPHOLE[JustStrobe[]]];
b.i ← NIL;
END;
ENDCASE => t.writeR[t: t, reg: control, value: LOOPHOLE[JustStrobe[]]];
END;
GetIBuf: PUBLIC ENTRY PROC [t: Tester] RETURNS [ChannelVecRef] =
BEGIN
ENABLE UNWIND => NULL;
WITH t.data SELECT FROM
buf: BufRef => RETURN[buf.i];
ENDCASE => {RETURN[NEW[ChannelVec ← t.readI[t: t]]]};
END;
ci: RWChannelVecRef ← NIL;
CheckI: PUBLIC ENTRY PROC [ t: Tester, expected: ChannelVecRef, mask: ChannelVecRef ← NIL, makeBufWindow: BOOLFALSE ] RETURNS [ CheckResult ] =
TRUSTED BEGIN
ENABLE UNWIND => NULL;
IF mask^ = falses^ THEN RETURN[[0, NIL]]; -- not a real test
WITH t.data SELECT FROM
buf: BufRef =>
BEGIN
PutCheckIntoBuffer: PROC [min: WordIndex] =
TRUSTED BEGIN
IncreaseTestData[buf, 8];
FOR i: [0..3] IN [0..3] DO
buf.testData.t[buf.testDataSize-8+i] ← LOOPHOLE[expected, REF ChannelWordVec][i+min];
buf.testData.t[buf.testDataSize-4+i] ← LOOPHOLE[mask, REF ChannelWordVec][i+min];
ENDLOOP;
END;
window: Tester ← NIL;
buf: BufRef = NARROW[t.data];
IF NOT makeBufWindow AND mask^ = falses^ THEN RETURN[[0, NIL]]; -- not a real test
FinishStep[t];
WHILE buf.steps[buf.stepCount-1].checkSecond OR buf.steps[buf.stepCount-1].checkFirst DO
t.writeR[t: t, reg: control, value: LOOPHOLE[NoOp]];
FinishStep[t];
ENDLOOP;
IF makeBufWindow THEN
BEGIN
window ← NEW[TesterOb ← [
data: NEW[Window ← [buf: t, firstWord: buf.testDataSize, check: TRUE]]
]];
END;
FOR i: WordIndex IN [0..3] DO
IF makeBufWindow OR LOOPHOLE[mask, REF ChannelWordVec][i]#0 THEN
BEGIN
buf.steps[buf.stepCount-1].checkFirst ← TRUE;
PutCheckIntoBuffer[min: 0];
EXIT;
END;
ENDLOOP;
FOR i: WordIndex IN [4..7] DO
IF makeBufWindow OR LOOPHOLE[mask, REF ChannelWordVec][i]#0 THEN
BEGIN
buf.steps[buf.stepCount-1].checkSecond ← TRUE;
PutCheckIntoBuffer[min: 4];
EXIT;
END;
ENDLOOP;
RETURN[[buf.stepCount, window]];
END;
w: WindowRef =>
BEGIN
ResetCheckInBuffer: PROC [min: WordIndex] =
TRUSTED BEGIN
FOR i: [0..3] IN [0..3] DO
buf.testData.t[w.firstWord+i] ← LOOPHOLE[expected, REF ChannelWordVec][i+min];
buf.testData.t[w.firstWord+4+i] ← LOOPHOLE[mask, REF ChannelWordVec][i+min];
ENDLOOP;
END;
buf: BufRef = NARROW[w.buf.data];
IF NOT w.check THEN ERROR IllegalOp;
ResetCheckInBuffer[min: 0];
ResetCheckInBuffer[min: 4];
RETURN[[0, NIL]];
END;
ENDCASE =>
BEGIN
IF ci = NIL THEN ci ← NEW[ChannelVec];
ci^ ← t.readI[t: t];
FOR j: WordIndex IN WordIndex DO
diff: WORD = Basics.BITAND[LOOPHOLE[mask, REF ChannelWordVec][j],
Basics.BITXOR[LOOPHOLE[ci, REF ChannelWordVec][j],
LOOPHOLE[expected, REF ChannelWordVec][j]]];
IF diff#0 THEN
BEGIN
FOR k: Channel IN [16*j..16*(j+1)) DO
IF mask[k] AND (expected[k]#ci[k]) THEN
ERROR CheckFailure[checkId: 1, mask: mask, expected: expected, was: ci,
errChannel: k];
REPEAT
FINISHED => ERROR;
ENDLOOP;
END;
ENDLOOP;
RETURN[[1, NIL]];
END;
END;
CheckFailure: PUBLIC ERROR [checkId: CARDINAL,
mask, expected, was: ChannelVecRef, errChannel: Channel] = CODE;
IllegalOp: PUBLIC ERROR = CODE;
This second group of procedures is used to generate and execute buffers containing several tester steps. These buffers conform to the specifications of the Dolphin ccaKludge instruction, whose microcode is in MesaIO.mc (see Fiala). The major limitation is that with the ccaKludge instruction you cannot ask about the results of the steps (except for the last one); you must predict the results (under a mask), and step execution will halt if masked prediction and reality do not match. This is fine for testing but not so good for debugging.
BufferR: PROC [t: Tester, reg, value: WORD] =
TRUSTED BEGIN
buf: BufRef = NARROW[t.data];
IF reg#control THEN ERROR IllegalOp;
IF buf.unSteppedTestData>=16 THEN FinishStep[t];
IncreaseTestData[buf, 1];
buf.testData.t[buf.testDataSize-1] ← value;
buf.unSteppedTestData ← buf.unSteppedTestData+1;
END;
FinishStep: PROC [t: Tester] =
TRUSTED BEGIN
buf: BufRef = NARROW[t.data];
IF buf.testDataSize=0 THEN t.writeR[t: t, reg: control, value: LOOPHOLE[NoOp]];
IF buf.unSteppedTestData>0 THEN
BEGIN
WHILE buf.unSteppedTestData MOD 4 # 0 DO -- fill out quadword
t.writeR[t: t, reg: control, value: LOOPHOLE[NoOp]];
ENDLOOP;
IncreaseSteps[buf, 1];
buf.steps[buf.stepCount-1] ← StepControl[quadWdCnt: buf.unSteppedTestData/4];
buf.unSteppedTestData ← 0;
END;
END;
IncreaseTestData: PROC [buf: BufRef, increase: CARDINAL] =
TRUSTED BEGIN
newTestDataSize: CARDINAL = buf.testDataSize+increase;
IF newTestDataSize>buf.testDataMaxSize THEN
BEGIN
newPagesNeeded: NAT = VM.PagesForWords[(13*newTestDataSize)/10]+2;
130% + 2 pages
newInterval: VM.Interval = VM.Allocate[count: newPagesNeeded];
newTestData: LONG POINTER TO TestDataArray = LOOPHOLE[VM.AddressForPageNumber[newInterval.page]];
FOR i: INT IN [0..buf.testDataSize) DO
newTestData.t[i] ← buf.testData.t[i];
ENDLOOP;
IF buf.testData # NIL THEN VM.Free[buf.testDataInterval];
buf.testDataMaxSize ← VM.WordsForPages[newPagesNeeded]-SIZE[TestDataArray];
buf.testDataInterval ← newInterval;
buf.testData ← newTestData;
END;
buf.testDataSize ← newTestDataSize;
END;
IncreaseSteps: PROC [buf: BufRef, increase: CARDINAL] =
TRUSTED BEGIN
newStepCount: CARDINAL = buf.stepCount+increase;
IF newStepCount>buf.stepMaxCount THEN
BEGIN
newPagesNeeded: NAT = VM.PagesForWords[(13*buf.stepMaxCount)/(4*10)]+1;
130% + 1 page
newInterval: VM.Interval = VM.Allocate[count: newPagesNeeded];
newSteps: LONG POINTER TO StepControlArray = LOOPHOLE[VM.AddressForPageNumber[newInterval.page]];
FOR i: INT IN [0..buf.stepCount) DO
newSteps[i] ← buf.steps[i];
ENDLOOP;
IF buf.steps # NIL THEN VM.Free[buf.stepInterval];
buf.stepMaxCount ← 4*VM.WordsForPages[newPagesNeeded];
buf.stepInterval ← newInterval;
buf.steps ← newSteps;
END;
buf.stepCount ← newStepCount;
END;
ExecuteBuffer: PUBLIC ENTRY PROC [t: Tester] = {InternalExecuteBuffer[t ! UNWIND => NULL]};
InternalExecuteBuffer: PROC [t: Tester] =
TRUSTED BEGIN
SendTestBuf: PROC [stepCount: CARDINAL,
stepControl: LONG POINTER, -- resident
testData: LONG POINTER -- quadword-aligned, resident -- ]
RETURNS [undoneSteps: CARDINAL] =
TRUSTED MACHINE CODE BEGIN
PrincOps.zMISC, 14B -- miscSendBuf --;
END;
WITH t.data SELECT FROM
buf: BufRef =>
BEGIN
undoneSteps: CARDINAL;
IF buf.testDataSize=0 THEN RETURN;
IF buf.unSteppedTestData>0 THEN FinishStep[t];
VM.Pin[buf.testDataInterval];
VM.Pin[buf.stepInterval];
NOTE: This doesn't initialize the tester. If you want to do that, call Initialize on the buffer or on the buffer's base tester first.
undoneSteps ← IF SystemVersion.machineType=dolphin THEN
SendTestBuf[stepCount: buf.stepCount,
stepControl: LOOPHOLE[buf.steps], testData: LOOPHOLE[buf.testData]]
ELSE 0;
VM.Unpin[buf.testDataInterval];
VM.Unpin[buf.stepInterval];
buf.i ← NEW[ChannelVec ← buf.testData.finalI];
IF undoneSteps = 0 THEN RETURN;
Analyze the results and generate a CheckFailure ERROR
BEGIN
expected: ChannelVecRef = NEW[ChannelVec ← ALL[FALSE]];
mask: ChannelVecRef = NEW[ChannelVec ← ALL[FALSE]];
failedStep: CARDINAL = buf.stepCount-undoneSteps;
badSpot: CARDINAL ← 0;
FOR j: CARDINAL IN [0..failedStep] DO
badSpot ← badSpot+4*buf.steps[j].quadWdCnt
+(IF buf.steps[j].checkSecond THEN 8 ELSE 0)
+(IF buf.steps[j].checkFirst THEN 8 ELSE 0);
ENDLOOP;
IF buf.steps[failedStep].checkSecond THEN
BEGIN
FOR i: [0..3] IN [0..3] DO
LOOPHOLE[expected, REF ChannelWordVec][i+4] ← buf.testData.t[badSpot-8+i];
LOOPHOLE[mask, REF ChannelWordVec][i+4] ← buf.testData.t[badSpot-4+i];
ENDLOOP;
badSpot ← badSpot-8;
END;
IF buf.steps[failedStep].checkFirst THEN
BEGIN
FOR i: [0..3] IN [0..3] DO
LOOPHOLE[expected, REF ChannelWordVec][i] ← buf.testData.t[badSpot-8+i];
LOOPHOLE[mask, REF ChannelWordVec][i] ← buf.testData.t[badSpot-4+i];
ENDLOOP;
END;
FOR errChannel: Channel IN Channel DO
IF mask[errChannel] AND (buf.i[errChannel]#expected[errChannel]) THEN
ERROR CheckFailure[checkId: failedStep+1, mask: mask, expected: expected,
was: buf.i, errChannel: errChannel];
ENDLOOP;
ERROR; -- SendTestQueue failed but we couldn't figure out why
END;
END;
w: WindowRef => InternalExecuteBuffer[w.buf];
ENDCASE => ERROR IllegalOp;
END;
OpenBuffer: PUBLIC ENTRY PROC [ base: Tester ← NIL ] RETURNS [ Tester ] =
BEGIN
ENABLE UNWIND => NULL;
buf: BufRef = NEW[BufDesc ← [base: IF base=NIL THEN OpenTester[] ELSE base]];
finalizeList ← CONS[buf, finalizeList];
RETURN[NEW[TesterOb ← [
writeR: BufferR,
flush: FlushLocal,
data: buf]]];
END;
Procedures that implement register read/write for a window (which are illegal)
FlushError: PROC [ t: Tester ] = {ERROR IllegalOp};
WriteRError: PROC [ t: Tester, reg, value: WORD ] = {ERROR IllegalOp};
ReadIError: PROC [ t: Tester ] RETURNS [ ChannelVec ] = {ERROR IllegalOp};
Procedures that implement remote tester read/write
WriteRRemote: PROC [ t: Tester, reg, value: WORD ] =
BEGIN
rt: RemoteTesterRef = NARROW[t.data];
WHILE rt.stores.length>=ChipTestRemote.regStoresMaxLength DO FlushRT[rt] ENDLOOP;
rt.stores.s[rt.stores.length] ← [reg: reg, value: value];
rt.stores.length ← rt.stores.length+1;
END;
FlushRemote: PROC [ t: Tester ] = {FlushRT[NARROW[t.data]]};
FlushRT: PROC [ rt: RemoteTesterRef ] =
BEGIN
IF rt.stores.length>0 THEN
BEGIN
interface: ChipTestRemoteRpcControl.InterfaceRecord = rt.interface;
interface.WriteRegs[rt.stores];
rt.stores.length ← 0;
END;
END;
ReadIRemote: PROC [ t: Tester ] RETURNS [ ChannelVec ] =
BEGIN
rt: RemoteTesterRef = NARROW[t.data];
interface: ChipTestRemoteRpcControl.InterfaceRecord = rt.interface;
FlushRT[rt];
RETURN[interface.ReadIBuf[]];
END;
SyncFlusher: ENTRY PROC [ l: LIST OF REF ] =
BEGIN
ENABLE UNWIND => NULL;
FOR rl: LIST OF REF ← l, rl.rest WHILE rl#NIL DO
WITH rl.first SELECT FROM
rt: RemoteTesterRef => FlushRT[rt];
ENDCASE => NULL;
ENDLOOP;
END;
Flusher: PROC =
BEGIN
DO
Process.Pause[Process.MsecToTicks[1000]];
SyncFlusher[finalizeList];
ENDLOOP;
END;
The procedures that implement a local tester on a Dolphin
ReadILocal: PROC [ t: Tester ] RETURNS [ i: ChannelVec ] =
TRUSTED BEGIN
ReadR: PROC [reg: CARDINAL] RETURNS [value: WORD] =
TRUSTED MACHINE CODE BEGIN
PrincOps.zMISC, 5B -- input --;
END;
WriteRLocal[t: t, reg: control, value: LOOPHOLE[SetFirstIGroup[group: 0]]];
FOR j: WordIndex IN WordIndex DO
LOOPHOLE[@i, POINTER TO ChannelWordVec][j] ← ReadR[selectedIGroup];
automatically steps to next IGroup with each read
ENDLOOP;
END;
Byte: TYPE = [0..377B];
status: Byte = 0; -- input register numbers
selectedIGroup: Byte = 1;
FlushLocal: PROC [ t: Tester ] = {NULL};
WriteRLocal: PROC [ t: Tester, reg, value: WORD ] =
BEGIN
DoWriteRLocal: PROC [ value: WORD, reg: WORD ] =
TRUSTED MACHINE CODE BEGIN
PrincOps.zMISC, 6B -- output --;
END;
DoWriteRLocal[value, reg];
END;
init: Byte = 0; -- output register numbers and types
control: Byte = 1;
JustStrobe: TYPE = MACHINE DEPENDENT RECORD [
strobe (0: 0..0): BOOLTRUE,
unused (0: 1..15): [0..0] ← 0
];
NoOp: JustStrobe = [strobe: FALSE];
LoadABD: TYPE = MACHINE DEPENDENT RECORD [
strobe (0: 0..0): BOOLFALSE,
command (0: 1..2): {loadD(1), loadA(2), loadB(3)},
pinGroup (0: 3..7): [0..31] ← 0,
data (0: 8..15): Byte ← 0];
LoadDelay: TYPE = MACHINE DEPENDENT RECORD [
strobe (0: 0..0): BOOLFALSE,
clock (0: 1..4): {B(1), I(2)},
unused (0: 5..5): [0..0] ← 0,
delay1 (0: 6..9): [0..17B] ← 0,
delay2 (0: 10..12): [0..7B] ← 0,
delay3 (0: 13..15): [0..7B] ← 0];
SetFirstIGroup: TYPE = MACHINE DEPENDENT RECORD [
strobe (0: 0..0): BOOLFALSE,
command (0: 1..5): {selectIGroup(1)} ← selectIGroup,
unused (0: 6..11): [0..0] ← 0,
group (0: 12..15): [0..15] ← 0];
IsntADolphin: SIGNAL = CODE;
Finalization (when the next-to-last REF to something disappears)
finalizeList: LIST OF REFNIL;
SyncFinalizer: ENTRY PROC [r: REF] =
BEGIN
ENABLE UNWIND => NULL;
Remove: PROC [l: LIST OF REF] RETURNS [result: LIST OF REF] =
BEGIN
SELECT TRUE FROM
l = NIL => result ← NIL;
l.first = r => result ← l.rest;
ENDCASE => {result ← l; l.rest ← Remove[l.rest]};
END;
WITH r SELECT FROM
buf: BufRef =>
BEGIN
Gives back Pilot Spaces in the BufDesc before the BufDesc disappears...
IF buf.testData # NIL THEN
TRUSTED {VM.Free[buf.testDataInterval]; buf.testData ← NIL};
IF buf.steps # NIL THEN
TRUSTED {VM.Free[buf.stepInterval]; buf.steps ← NIL};
END;
ENDCASE => NULL;
finalizeList ← Remove[finalizeList];
END;
Finalizer: PROC [fq: SafeStorage.FinalizationQueue] =
BEGIN
DO
r: REF = SafeStorage.FQNext[fq];
SyncFinalizer[r];
ENDLOOP;
END;
Module start code..
fq: SafeStorage.FinalizationQueue = SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[CODE[BufDesc], 1, fq !
SafeStorage.CantEstablishFinalization =>
SafeStorage.ReEstablishFinalization[CODE[BufDesc], 1, fq]];
SafeStorage.EstablishFinalization[CODE[RemoteTester], 1, fq !
SafeStorage.CantEstablishFinalization =>
SafeStorage.ReEstablishFinalization[CODE[RemoteTester], 1, fq]];
TRUSTED BEGIN
Process.Detach[FORK Finalizer[fq]];
Process.Detach[FORK Flusher[]];
END;
END.