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: BOOL ← FALSE ];
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): BOOLEAN ← FALSE,
checkFirst (0: 3..3): BOOLEAN ← FALSE];
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: BOOL ← FALSE,
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.
ROPE ←
NIL]
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): BOOL ← FALSE,
notReset (0: 15..15): BOOL ← TRUE];
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:
BOOL ←
FALSE ]
RETURNS [ window: Tester ] =
{RETURN[InternalPutDisables[t, p, makeBufWindow ! UNWIND => NULL]]};
InternalPutDisables:
PROC [ t: Tester, p: ChannelVecRef, makeBufWindow:
BOOL ←
FALSE ]
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:
BOOL ←
FALSE ]
RETURNS [ window: Tester ] =
{RETURN[InternalPutABuf[t, p, makeBufWindow ! UNWIND => NULL]]};
InternalPutABuf:
PROC [ t: Tester, p: ChannelVecRef, makeBufWindow:
BOOL ←
FALSE ]
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:
BOOL ←
FALSE ]
RETURNS [ window: Tester ] =
{RETURN[InternalPutBBuf[t, p, makeBufWindow ! UNWIND => NULL]]};
InternalPutBBuf:
PROC [ t: Tester, p: ChannelVecRef, makeBufWindow:
BOOL ←
FALSE ]
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:
BOOL ←
FALSE ]
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): BOOL ← TRUE,
unused (0: 1..15): [0..0] ← 0
];
NoOp: JustStrobe = [strobe: FALSE];
LoadABD:
TYPE =
MACHINE
DEPENDENT
RECORD [
strobe (0: 0..0): BOOL ← FALSE,
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): BOOL ← FALSE,
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): BOOL ← FALSE,
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 REF ← NIL;
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.