ArbSysTests.mesa
Copyright c 1986 by Xerox Corporation. All rights reserved.
Last Edited by: McCreight September 27, 1988 3:10:40 pm PDT
Jean-Marc Frailong October 4, 1988 11:51:04 am PDT
Don Curry September 7, 1988 11:15:00 am PDT
DIRECTORY
Arbiter, BitOps, CedarProcess, Core, CoreFlat, CoreProperties, IO, MTSVector, Ports, Random, Rosemary, RosemaryUser;
ArbSysTests: CEDAR PROGRAM
IMPORTS BitOps, CedarProcess, CoreFlat, CoreProperties, IO, MTSVector, Ports, Random, Rosemary, RosemaryUser
EXPORTS Arbiter =
BEGIN OPEN Arbiter;
December 31, 1987 4:04:24 pm PST -- In ArbNov87A, the first fabrication of the Arbiter, there is a bug in the handling of nStopAct. The bug manifests itself as simultaneous grants from two arbiters when nStopAct is released. The problem is that nStopAct inhibits local grants, but part of the Arbiter's logic believes that system-wide grants are happening anyway. Approximately two cycles after the first erroneous "system-wide grant", if the grant was made at priority level 0 or 1 the grantee arbiter will compute the internal signal NoHold4 differently from the other arbiters. Then, assuming that there are other requests pending and BusyIn is not aserted, the grantee arbiter will assume a second erroneous "system-wide grant" while the other arbiters will not (until one cycle later, when conditions may have changed). This one cycle of difference allows the arbiters to get out of synchronization, a sure prescription for disaster later on.
The code below incorporates the following patch: When nStopAct asserts, we wait thereafter until BusyIn deasserts for one cycle. Then we assert BusyIn for a minimum of five cycles, or until nStopAct deasserts, whichever is longer.
State:  TYPE = REF StateRec ← NIL;
StateRec: TYPE = RECORD [
tester:   RosemaryUser.Tester ← NIL,
simulation: Rosemary.Simulation ← NIL,
cellType:  Core.CellType ← NIL,
capture:  MTSVector.Capture ← NIL,
testCycles: INT ← 100000,
p:    Ports.Port ← NIL,
eval: PROC [memory: BOOLTRUE, clockEval: BOOLFALSE, checkPorts: BOOLTRUE]←NIL,
cycle, patchState: INT ← -1,
noteBusBusyLast: INT ← -1,
forceStop, forceBusy: BOOLFALSE,
nRequestOuts, nGrants, nHiPGrants, nLongGrants, ArbReqs, DBus,
nOwnerOuts, nBOwnerIn, nOwnerIns,
nSharedOuts, nBShareds, nSharedIns,
nSStopOuts, nBSStops, nSStopIns, nStopAct,
SlotNos, nDHybridSels, DBdSels,
nDynaBusBusy, nHolding, nBusyOuts, nBusyIn, Ck,
TIOBus, --RecAdj, --TRec2v, TInv: NAT ← 0,
..indexes in cellType.public
arbs: ARRAY Arbiters OF REFALL[NIL],
devs: ARRAY Arbiters OF ARRAY Devices OF REFALL[ALL[NIL]] ];
lastState: State ← NIL;
rand:  Random.RandomStream ← Random.Create[];
NewState: PROC [
cellType: Core.CellType,
p:   Ports.Port,
eval: PROC [memory: BOOLTRUE, clockEval: BOOLFALSE, checkPorts: BOOLTRUE]]
RETURNS [ st: State ] = {
GetState: PROC [ instName: IO.ROPE ] RETURNS [ ref: REF ] = {
ref ← Rosemary.GetState[
st.tester.display.simulation,
NEW[CoreFlat.FlatCellTypeRec ←
CoreFlat.ParseCellTypePath[st.cellType, instName]] ] };
lastState ← st ← NEW[StateRec ←
[tester: NARROW[CoreProperties.GetProp[cellType.properties, $Tester]] ]];
st.simulation ← st.tester.display.simulation;
st.cellType ← st.tester.display.cellType;
st.p ← p;
TRUSTED { st.eval ← eval };
[st.nRequestOuts, st.nGrants, st.nHiPGrants, st.nLongGrants, st.ArbReqs] ← Ports.PortIndexes
[st.cellType.public, "nRequestOuts", "nGrants", "nHiPGrants", "nLongGrants", "ArbReqs"];
[st.DBus] ← Ports.PortIndexes[st.cellType.public, "DBus"];
[st.SlotNos, st.nDHybridSels, st.DBdSels] ← Ports.PortIndexes[st.cellType.public,
"SlotNos", "nDHybridSels", "DBdSels"];
[st.nOwnerOuts, st.nBOwnerIn, st.nOwnerIns] ← Ports.PortIndexes[st.cellType.public,
"nOwnerOuts", "nBOwnerIn", "nOwnerIns"];
[st.nSharedOuts, st.nBShareds, st.nSharedIns] ← Ports.PortIndexes[st.cellType.public,
"nSharedOuts", "nBShareds", "nSharedIns"];
[st.nSStopOuts, st.nBSStops, st.nSStopIns, st.nStopAct] ← Ports.PortIndexes[st.cellType.public,
"nSStopOuts", "nBSStops", "nSStopIns", "nStopAct"];
[st.nDynaBusBusy, st.nHolding, st.nBusyOuts, st.nBusyIn, st.Ck] ← Ports.PortIndexes[st.cellType.public,
"nDynaBusBusy", "nHolding", "nBusyOuts", "nBusyIn", "Ck"];
[st.TIOBus, --st.RecAdj, -- st.TRec2v, st.TInv] ← Ports.PortIndexes[st.cellType.public,
"TIOBus", --"RecAdj", --"TRec2v", "TInv"];
FOR a: Arbiters IN Arbiters DO
st.arbs[a] ← GetState[IO.PutFR["/Arb%d/ArbiterTop", IO.int[a]]];
-- until I figure out how to do this ....
FOR d: Devices IN Devices DO
st.devs[a][d] ← GetState[IO.PutFR["/Arb%dReq%d", IO.int[a], IO.int[d]]];
ENDLOOP;
ENDLOOP};
WhoAmI: PROC [ ref: REF ] RETURNS [ name: IO.ROPE ] = {
FOR a: Arbiters IN Arbiters DO
IF ref = lastState.arbs[a] THEN RETURN[IO.PutFR["Arbiter %d", IO.int[a]]];
FOR d: Devices IN Devices DO
IF ref = lastState.devs[a][d] THEN RETURN[IO.PutFR["Device %d of arbiter %d", IO.int[d], IO.int[a]]] ENDLOOP ENDLOOP;
name ← "Darned if I know!"};
extraResetCycles:  NAT ← 1;
nCheckBits:    NAT ← 5;
shouldMaskRev1Bug: BOOLFALSE;
RunTest: PROC [ st: State, initializeAll: BOOLFALSE ] = {
PipeDepth: NAT = 10;
Eval: PROC [ ignoreX: BOOLFALSE ] = {
ENABLE Rosemary.Stop =>
IF ignoreX AND (reason = $BoolWireHasX) THEN RESUME ELSE REJECT;
MTSVector.EvalAndCapture[st.capture, st.eval, FALSE];
};
EvalIgnoreX: PROC = {Eval[ignoreX: TRUE]};
ReallyRunTest: PROC = {
OrPorts: TYPE = {owner, shared, stop};
OrPipeEnt: TYPE = RECORD [
A: ARRAY Arbiters OF BOOLALL[FALSE],
inD: BOOLFALSE ];
OrPortParts: TYPE = RECORD [
outD, A, inD: NAT,
pipe: ARRAY [0..PipeDepth) OF OrPipeEnt ← ALL[] ];
orPortState: ARRAY OrPorts OF OrPortParts ← [
owner: [outD: st.nOwnerOuts, A: st.nBOwnerIn, inD: st.nOwnerIns],
shared: [outD: st.nSharedOuts, A: st.nBShareds, inD: st.nSharedIns],
stop:  [outD: st.nSStopOuts, A: st.nBSStops, inD: st.nSStopIns]];
DoClk: PROC [ cycles: INT ← 1, ignoreX: BOOLFALSE ] = {
FOR i: INT IN [0..cycles) DO
IgnoreOrPorts[];
MaskBug[];
st.p[st.Ck].b ← FALSE;
Eval[ignoreX];
PredictOrPorts[];
st.p[st.Ck].b ← TRUE;
Eval[ignoreX];
st.cycle ← st.cycle+1;
ENDLOOP};
DoClkIgnoreX: PROC [ cycles: INT ← 1 ] = {DoClk[cycles, TRUE]};
DAShift: PROC [ val: BitOps.BitWord ] = {
st.p[st.DBus][DAddress].b ← TRUE;
EvalIgnoreX[];
DDShift[16, val];
st.p[st.DBus][DAddress].b ← FALSE;
EvalIgnoreX[]};
DDShift: PROC [ size: [1..16], val: BitOps.BitWord ] = {
FOR i: NAT IN [0..size) DO
st.p[st.DBus][DSerialIn].b ← BitOps.EBFW[val, i, size];
st.p[st.DBus][DShiftCK].b ← FALSE;
EvalIgnoreX[];
st.p[st.DBus][DShiftCK].b ← TRUE;
EvalIgnoreX[];
ENDLOOP};
DDShiftCheck: PROC [ size: [1..16], valIn: BitOps.BitWord, valOut: BitOps.BitWord ] = {
FOR i: NAT IN [0..size) DO
st.p[st.DBus][DSerialIn].b ← BitOps.EBFW[valIn, i, size];
st.p[st.DBus][DShiftCK].b ← FALSE;
st.p[st.DBus][DSerialOut].b ← BitOps.EBFW[valOut, i, size];
st.p[st.DBus][DSerialOut].d ← expect;
EvalIgnoreX[];
st.p[st.DBus][DSerialOut].d ← none;
st.p[st.DBus][DShiftCK].b ← TRUE;
EvalIgnoreX[];
ENDLOOP};
IgnoreOrPorts: PROC = {
FOR port: OrPorts IN [owner..owner] DO
FOR dev: Devices IN Devices DO
FOR arb: Arbiters IN Arbiters DO
st.p[orPortState[port].outD][arb].d ← inspect;
st.p[orPortState[port].inD][arb].d ← none;
ENDLOOP;
st.p[orPortState[port].A].d ← none ENDLOOP ENDLOOP;
FOR port: OrPorts IN [shared..stop] DO
FOR arb: Arbiters IN Arbiters DO
FOR dev: Devices IN Devices DO
st.p[orPortState[port].outD][arb][dev].d ← inspect;
ENDLOOP;
st.p[orPortState[port].A][arb].d ← none;
st.p[orPortState[port].inD][arb].d ← none ENDLOOP ENDLOOP};
MaskBug: PROC = {
k: INT ← st.cycle+PipeDepth;
Glue logic to mask the bug in ArbNov87A:
SELECT TRUE FROM
k <= 2*PipeDepth => st.patchState ← 0;
(st.patchState = 0) AND orPortState[stop].pipe[(k-3) MOD PipeDepth].inD =>
st.patchState ← 1;
shouldMaskRev1Bug AND (st.patchState = 1) AND st.p[st.nBusyIn].b => st.patchState ← 2;
shouldMaskRev1Bug AND st.patchState IN [2..7] => st.patchState ← st.patchState+1;
shouldMaskRev1Bug AND (st.patchState = 8) AND
NOT orPortState[stop].pipe[(k-3) MOD PipeDepth].inD =>
st.patchState ← 0;
NOT shouldMaskRev1Bug AND (st.patchState = 1) AND
NOT orPortState[stop].pipe[(k-3) MOD PipeDepth].inD =>
st.patchState ← 0;
ENDCASE => NULL;
st.p[st.nStopAct].b ← NOT (st.forceStop OR (st.patchState > 0));
IF NOT st.p[st.nStopAct].b THEN st.noteBusBusyLast ← st.cycle+6;
st.p[st.nBusyOuts][maxArbiters].d ← drive;
st.p[st.nBusyOuts][maxArbiters].b ← (st.patchState <= 2);
IF st.forceBusy OR (st.cycle < st.noteBusBusyLast)
THEN {
st.p[st.nDynaBusBusy].d ← drive;
st.p[st.nDynaBusBusy].b ← FALSE}
ELSE st.p[st.nDynaBusBusy].d ← none};
PredictOrPorts: PROC = {
k: INT ← st.cycle+PipeDepth;
FOR port: OrPorts IN [owner..owner] DO
orOutD: BOOLFALSE;
orA: BOOL ← orPortState[port].pipe[(k-1) MOD PipeDepth].inD;
orInD: BOOL ← orPortState[port].pipe[(k-3) MOD PipeDepth].inD;
FOR arb: Arbiters IN Arbiters DO
orOutD ← orOutD OR NOT st.p[orPortState[port].outD][arb].b;
IF k > 2*PipeDepth THEN {
st.p[orPortState[port].inD][arb].d ← expect;
st.p[orPortState[port].inD][arb].b ← NOT orInD} ENDLOOP;
IF k > 2*PipeDepth THEN {
st.p[orPortState[port].A].d ← expect;
st.p[orPortState[port].A].b ← NOT orA};
orPortState[port].pipe[k MOD PipeDepth].inD ← orOutD ENDLOOP;
FOR port: OrPorts IN [shared..stop] DO
orOutD: BOOLFALSE;
FOR arb: Arbiters IN Arbiters DO
orOutDA: BOOLFALSE;
FOR dev: Devices IN Devices DO
orOutDA ← orOutDA OR NOT st.p[orPortState[port].outD][arb][dev].b ENDLOOP;
orPortState[port].pipe[k MOD PipeDepth].A[arb] ← orOutDA;
orOutD ← orOutD OR orOutDA;
IF k > 2*PipeDepth THEN {
st.p[orPortState[port].A][arb].d ← expect;
st.p[orPortState[port].A][arb].b ← NOT orPortState[port].pipe[(k-1) MOD PipeDepth].A[arb];
st.p[orPortState[port].inD][arb].d ← expect;
st.p[orPortState[port].inD][arb].b ← NOT orPortState[port].pipe[(k-3) MOD PipeDepth].inD} ENDLOOP;
orPortState[port].pipe[k MOD PipeDepth].inD ← orOutD ENDLOOP};
dBusInits: Arbiter.DBusInitList ← NARROW[
CoreProperties.GetProp[st.cellType.properties, $DBusInits]];
st.p[st.nStopAct].d ← drive;
FOR i: NAT IN (DSerialOut..DShiftCK] DO
st.p[st.DBus][i].d ← drive;
st.p[st.DBus][i].b ← FALSE ENDLOOP;
st.p[st.DBus][DSerialOut].d ← none;
FOR a: Arbiters IN Arbiters DO
st.p[st.SlotNos][a].c ← 8H+a ENDLOOP;
st.p[st.DBus][DExecute].b ← FALSE;
st.p[st.DBus][nDFreeze].b ← TRUE;
st.p[st.DBus][nDReset].b ← FALSE;
st.p[st.DBus][DSerialIn].b ← FALSE;
st.p[st.DBus][DShiftCK].b ← FALSE;
st.p[st.DBus][DAddress].b ← FALSE;
st.p[st.DBus][DSerialOut].b ← TRUE;
st.p[st.DBus][DSerialOut].d ← driveWeak;
FOR i: NAT IN [0..6) DO
st.p[st.TIOBus][i].d ← driveStrong; -- we really don't care about this bus
st.p[st.TIOBus][i].b ← FALSE ENDLOOP;
FOR i: NAT IN [0..2) DO
st.p[st.TRec2v][i].d ← drive;
st.p[st.TRec2v][i].b ← TRUE ENDLOOP;
st.p[st.TRec2v][2].d ← expect;
st.p[st.TRec2v][2].b ← NOT st.p[st.TRec2v][0].b;
st.p[st.RecAdj].d ← drive;
st.p[st.RecAdj].b ← TRUE;
st.p[st.TInv][0].d ← drive;
st.p[st.TInv][0].b ← FALSE;
st.p[st.TInv][1].d ← expect;
st.p[st.TInv][1].b ← NOT st.p[st.TInv][0].b;
st.cycle ← 0;
st.noteBusBusyLast ← -1;
st.forceStop ← TRUE;
st.forceBusy ← TRUE; -- no starvation errors yet
DoClkIgnoreX[6]; -- allow some X's to relax out
st.forceStop ← FALSE; -- release Stop while holding Reset
st.p[st.DBus][DShiftCK].b ← TRUE;
DoClkIgnoreX[6]; -- allow more X's to relax out
IF st.cycle > st.testCycles THEN RETURN;
IF dBusInits # NIL THEN {
checkBits: Ports.BoolSequence ← NEW[Ports.BoolSequenceRec[nCheckBits]];
a: Arbiters ← 0;
DAShift[08000H+1000H*a]; -- check signature
DDShiftCheck[16, 0, 5041H];
Check out the two ways of doing hybrid select: decoded and encoded
DAShift[08000H+1000H*a+2]; -- load arbiter number
DDShift[4, 2*a+1]; -- decoded hybrid select
DAShift[08000H+1000H*a+300H]; -- address hybrid 3
st.p[st.nDHybridSels][a].d ← expect;
st.p[st.nDHybridSels][a].c ← 01FH-4; -- decoded
EvalIgnoreX[];
st.p[st.nDHybridSels][a].d ← none;
DAShift[08000H+1000H*a+2]; -- load arbiter number
DDShift[4, 2*a]; -- now encoded
DAShift[08000H+1000H*a+300H]; -- address hybrid 3
st.p[st.nDHybridSels][a].d ← expect;
st.p[st.nDHybridSels][a].c ← 01FH-6; -- encoded, 5 bits
EvalIgnoreX[];
st.p[st.nDHybridSels][a].d ← none;
DAShift[08000H+1000H*a+400H]; -- address hybrid 4
DDShift[1, 0]; -- load BdVersion shadow
DAShift[08000H+1000H*a+3H]; -- read the board version
DDShiftCheck[2, 0, 2];
FOR dbir: LIST OF REF ANY ← dBusInits, dbir.rest WHILE dbir#NIL DO
WITH dbir.first SELECT FROM
dbi: REF DBusInitItem => {
DAShift[08000H+dbi.addr]; -- load DBus destination number
FOR i: NAT IN [0..dbi.bits.size+checkBits.size) DO
outVal: [0..2);
cbi: INT = i - dbi.bits.size;
IF i < checkBits.size THEN {
outVal ← rand.ChooseInt[max: 1];
checkBits[i] ← (outVal > 0) }
ELSE outVal ← (IF dbi.bits[i-checkBits.size] THEN 1 ELSE 0);
IF cbi < 0 THEN DDShift[1, outVal]
ELSE DDShiftCheck[1, outVal, (IF checkBits[cbi] THEN 1 ELSE 0)] ENDLOOP };
ENDCASE => ERROR ENDLOOP };
st.forceStop ← TRUE;
... assert Stop. This causes LclGrant4 to become FALSE, and begins clearing X's out of the ArbReq and Grant paths.
DoClkIgnoreX[10]; -- allow more X's to relax out
st.forceStop ← FALSE;
... release Stop. This applies a final reset to the Arbiter's input counters and the rover pointers.
DoClkIgnoreX[10]; -- allow final X's to relax out
DoClk[extraResetCycles]; -- see if any X's left
st.forceStop ← TRUE; -- assert Stop, this terminates Arbiter's reset
DoClk[6];
st.p[st.DBus][nDReset].b ← TRUE; -- release Reset, this terminates everybody else's reset
st.p[st.DBus][nDFreeze].b ← TRUE; -- release Freeze, whatever that means
DoClk[5];
st.forceStop ← FALSE; -- release Stop, begin arbitration
DoClk[15];
st.forceBusy ← FALSE; -- allow starvation errors
DoClk[st.testCycles - 1166]}; -- 1166 to init
CedarProcess.DoWithPriority[priority: background, action: ReallyRunTest]};
simHighProb:  REF ProbabilitiesRec ← NEW[ProbabilitiesRec ← []];
simMediumProb: REF ProbabilitiesRec ← NEW[ProbabilitiesRec ← []];
simLowProb:  REF ProbabilitiesRec ← NEW[ProbabilitiesRec ← []];
normalProb: ProbabilitiesRec ←
[request: ALL[400], hold: 1000, stop: 1000, owner: 1000, shared: 1000];
zeroProb: ProbabilitiesRec ←
[request: ALL[10000000], hold: never, stop: never, owner: never, shared: never];
higherProb: ProbabilitiesRec ←
[request: ALL[30], hold: 100, stop: 100, owner: 100, shared: 100];
evenHigherProb: ProbabilitiesRec ←
[request: ALL[10], hold: 100, stop: 100, owner: 100, shared: 100];
userProb: ProbabilitiesRec ← normalProb;
GeneralArbSysTest: PROC
[cellType: Core.CellType,
p:    Ports.Port,
Eval: PROC [memory: BOOLTRUE, clockEval: BOOLFALSE, checkPorts: BOOLTRUE],
high, med, low: ProbabilitiesRec,
captureVectors: BOOLFALSE,
testCycles: INT ← 100000] = {
st: State = NewState[cellType, p, Eval];
st.testCycles ← testCycles;
FOR a: Arbiters IN Arbiters DO
FOR d: Devices IN Devices DO
rs: REF RequesterStateRec = NARROW[st.devs[a][d]];
rs.probs ← (SELECT TRUE FROM
(a = 0) AND (d = 0) => simHighProb,
(a = 0) => simMediumProb,
ENDCASE => simLowProb) ENDLOOP ENDLOOP;
simHighProb^ ← high;
simMediumProb^ ← med;
simLowProb^ ← low;
IF captureVectors THEN st.capture ← MTSVector.CreateCapture[cellType];
RunTest[st, FALSE];
MTSVector.CloseCapture[st.capture]};
Test1Arb1Req: RosemaryUser.TestProc = {GeneralArbSysTest
[cellType, p, Eval, evenHigherProb, zeroProb, zeroProb, FALSE, 100000]};
Test1Arb8Req: RosemaryUser.TestProc = {GeneralArbSysTest
[cellType, p, Eval, higherProb, higherProb, zeroProb, FALSE, 100000]};
Test8Arb8Req: RosemaryUser.TestProc = {GeneralArbSysTest
[cellType, p, Eval, normalProb, normalProb, normalProb, FALSE, 100000]};
Test1Arb8Req10: RosemaryUser.TestProc = {GeneralArbSysTest
[cellType, p, Eval, higherProb, higherProb, zeroProb, TRUE, 10]};
Test1Arb8Req2000: RosemaryUser.TestProc = {GeneralArbSysTest
[cellType, p, Eval, higherProb, higherProb, zeroProb, TRUE, 2000]};
Test8Arb8ReqVec2000: RosemaryUser.TestProc = {GeneralArbSysTest
[cellType, p, Eval, normalProb, normalProb, normalProb, TRUE, 2000]};
Test8Arb8ReqVec10000: RosemaryUser.TestProc = {GeneralArbSysTest
[cellType, p, Eval, normalProb, normalProb, normalProb, TRUE, 10000]};
GetSystemTests: PUBLIC PROC RETURNS [listOfTests: LIST OF IO.ROPE] = {
listOfTests ← LIST[
"Test1Arb1Req",
"Test1Arb8Req",
"Test8Arb8Req",
"Test1Arb8Req10",
"Test1Arb8Req2000",
"Test8Arb8ReqVec2000",
"Test8Arb8ReqVec10000" ]};
RosemaryUser.RegisterTestProc["Test1Arb1Req",    Test1Arb1Req];
RosemaryUser.RegisterTestProc["Test1Arb8Req",    Test1Arb8Req];
RosemaryUser.RegisterTestProc["Test8Arb8Req",    Test8Arb8Req];
RosemaryUser.RegisterTestProc["Test1Arb8Req10",   Test1Arb8Req10];
RosemaryUser.RegisterTestProc["Test1Arb8Req2000",   Test1Arb8Req2000];
RosemaryUser.RegisterTestProc["Test8Arb8ReqVec2000", Test8Arb8ReqVec2000];
RosemaryUser.RegisterTestProc["Test8Arb8ReqVec10000", Test8Arb8ReqVec10000];
END.