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: BOOL ← TRUE, clockEval: BOOL←FALSE, checkPorts: BOOL←TRUE]←NIL,
cycle, patchState: INT ← -1,
noteBusBusyLast: INT ← -1,
forceStop, forceBusy: BOOL ← FALSE,
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 REF ← ALL[NIL],
devs: ARRAY Arbiters OF ARRAY Devices OF REF ← ALL[ALL[NIL]] ];
lastState: State ← NIL;
rand: Random.RandomStream ← Random.Create[];
NewState:
PROC [
cellType: Core.CellType,
p: Ports.Port,
eval:
PROC [memory:
BOOL ←
TRUE, clockEval:
BOOL ←
FALSE, checkPorts:
BOOL ←
TRUE]]
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:
BOOL ←
FALSE;
RunTest:
PROC [ st: State, initializeAll:
BOOL ←
FALSE ] = {
PipeDepth: NAT = 10;
Eval:
PROC [ ignoreX:
BOOL ←
FALSE ] = {
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 BOOL ← ALL[FALSE],
inD: BOOL ← FALSE ];
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:
BOOL ←
FALSE ] = {
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: BOOL ← FALSE;
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: BOOL ← FALSE;
FOR arb: Arbiters
IN Arbiters
DO
orOutDA: BOOL ← FALSE;
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: BOOL ← TRUE, clockEval: BOOL ← FALSE, checkPorts: BOOL ← TRUE],
high, med, low: ProbabilitiesRec,
captureVectors: BOOL ← FALSE,
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.