ALS652Tests.mesa
Copyright c 1986 by Xerox Corporation. All rights reserved.
E. McCreight, May 15, 1987 4:28:01 pm PDT
Last Edited by: McCreight January 13, 1988 12:06:05 pm PST
Jean-Marc Frailong January 25, 1988 1:27:13 pm PST
DIRECTORY
BitOps, Commander, CommandTool, Convert, Core, CoreOps, FS, IO, Ports, Random, Rosemary, RosemaryUser, MTSVector;
ALS652Tests: CEDAR PROGRAM
IMPORTS BitOps, Commander, CommandTool, Convert, CoreOps, FS, IO, Ports, Random, Rosemary, RosemaryUser, MTSVector
=
BEGIN
makeMTSVectors: BOOLTRUE;
makeOtherVectors: BOOLFALSE;
controlledClocks: BOOLFALSE;
ALS652Wires: TYPE = {A, B, nGBA, GAB, CBA, CAB, SBA, SAB};
ALS652WireNames: ARRAY ALS652Wires OF IO.ROPE = ["A", "B", "nGBA", "GAB", "CBA", "CAB", "SBA", "SAB"];
State: TYPE = REF StateRec ← NIL;
StateRec: TYPE = RECORD [
cellType: Core.CellType ← NIL,
p: Ports.Port ← NIL,
eval: RosemaryUser.TestEvalProc ← NIL,
pi: ARRAY ALS652Wires OF NATALL[0], -- indexes in cellType.public
Vdd, Gnd: NAT ← 0,
AVal, BVal: ARRAY [0..8) OF Ports.Level ← ALL[X]
];
lastState: State ← NIL;
GetAllState: PROC [cellType: Core.CellType, p: Ports.Port, eval: RosemaryUser.TestEvalProc] RETURNS [ st: State ] = {
lastState ← st ← NEW[StateRec ← []];
st.cellType ← cellType;
st.p ← p;
TRUSTED { st.eval ← eval };
[st.Vdd, st.Gnd] ← Ports.PortIndexes[st.cellType.public, "Vdd", "Gnd"];
FOR aw: ALS652Wires IN ALS652Wires DO
[st.pi[aw]] ← Ports.PortIndexes[st.cellType.public, ALS652WireNames[aw]];
ENDLOOP;
};
PortToStream: PROC [ p: Ports.Port, s: IO.STREAM ] = {
PutBool: PROC [ b: BOOL, d: Ports.Drive ] = {
s.PutRope[(SELECT d FROM
< none => (IF b THEN "H" ELSE "L"),
none => "X",
<= force => (IF b THEN "T" ELSE "F"),
ENDCASE => (IF b THEN "1" ELSE "0")
)];
};
PutLevel: PROC [ l: Ports.Level, d: Ports.Drive ] = {
s.PutRope[(SELECT d FROM
< none => (SELECT l FROM
H => "H",
L => "L",
ENDCASE => "X"),
none => "X",
<= force => (SELECT l FROM
H => "T",
L => "F",
ENDCASE => "X"),
ENDCASE => (SELECT l FROM
H => "1",
L => "0",
ENDCASE => "X")
)];
};
IF p.levelType=composite THEN FOR i: NAT IN [0..p.size) DO
PortToStream[p[i], s];
ENDLOOP
ELSE {
SELECT p.levelType FROM
l => PutLevel[p.l, p.d];
ls =>
FOR i: NAT IN [0..p.ls.size) DO
PutLevel[p.ls[i], (IF p.driveType=aggregate THEN p.d ELSE p.ds[i])];
ENDLOOP;
b => PutBool[p.b, p.d];
bs =>
FOR i: NAT IN [0..p.bs.size) DO
PutBool[p.bs[i], (IF p.driveType=aggregate THEN p.d ELSE p.ds[i])];
ENDLOOP;
c =>
FOR i: NAT IN [p.fieldStart..16) DO
PutBool[BitOps.EBFW[p.c, i], p.d];
ENDLOOP;
lc =>
FOR i: NAT IN [p.fieldStart..32) DO
PutBool[BitOps.EBFW[p.lc, i], p.d];
ENDLOOP;
q =>
FOR i: NAT IN [p.fieldStart..64) DO
PutBool[BitOps.EBFW[p.q[i/16], (i MOD 16)], p.d];
ENDLOOP;
ENDCASE => ERROR;
};
};
Closem: PROC [mts: MTSVector.File, ascii: IO.STREAM] ~ {
IF mts # NIL THEN MTSVector.Close[mts];
IF ascii # NIL THEN ascii.Close[];
};
RunTest: PROC [st: State, mts: MTSVector.File, ascii: IO.STREAM, initializeAll: BOOLFALSE ] = {
ENABLE UNWIND => Closem[mts, ascii];
Eval: PROC = {
st.eval[memory: FALSE, clockEval: TRUE, checkPorts: FALSE];
st.eval[memory: FALSE, clockEval: FALSE, checkPorts: TRUE];
IF mts # NIL THEN MTSVector.WriteVectorFromPort[mts, st.p];
IF ascii # NIL THEN {
FOR aw: ALS652Wires IN ALS652Wires DO
PortToStream[st.p[st.pi[aw]], ascii];
ENDLOOP;
ascii.PutF["\n"];
};
};
EvalIgnoreX: PROC = {
Eval[ ! Rosemary.Stop => IF reason = $BoolWireHasX THEN RESUME ELSE REJECT ];
};
st.p[st.Vdd].d ← st.p[st.Gnd].d ← inspect;
st.p[st.Vdd].b ← TRUE;
st.p[st.Gnd].b ← FALSE;
st.p[st.pi[CAB]].d ← st.p[st.pi[CBA]].d ← drive;
st.p[st.pi[CAB]].b ← st.p[st.pi[CBA]].b ← FALSE;
st.p[st.pi[nGBA]].d ← st.p[st.pi[GAB]].d ← drive;
st.p[st.pi[nGBA]].b ← TRUE;
st.p[st.pi[GAB]].b ← FALSE;
st.p[st.pi[SAB]].d ← st.p[st.pi[SBA]].d ← drive;
st.p[st.pi[SAB]].b ← st.p[st.pi[SBA]].b ← FALSE;
st.p[st.pi[A]].d ← st.p[st.pi[B]].d ← drive;
FOR i: NAT IN [0..8) DO
st.p[st.pi[A]].ls[i] ← st.p[st.pi[B]].ls[i] ← st.AVal[i] ← st.BVal[i] ← L;
ENDLOOP;
EvalIgnoreX[];
--
st.p[st.pi[CAB]].b ← st.p[st.pi[CBA]].b ← TRUE; -- clear internal flipflops
EvalIgnoreX[];
--
st.p[st.pi[CAB]].b ← st.p[st.pi[CBA]].b ← FALSE;
EvalIgnoreX[];
--
st.p[st.pi[A]].d ← st.p[st.pi[B]].d ← force;
Eval[];
--
FOR i: NAT IN [0..8) DO
st.p[st.pi[A]].ls[i] ← st.p[st.pi[B]].ls[i] ← st.AVal[i] ← st.BVal[i] ← H;
ENDLOOP;
Eval[];
--
FOR i: NAT IN [0..8) DO
st.p[st.pi[A]].ls[i] ← st.p[st.pi[B]].ls[i] ← st.AVal[i] ← st.BVal[i] ← L;
ENDLOOP;
st.p[st.pi[A]].d ← st.p[st.pi[B]].d ← drive;
Eval[];
--
FOR i: NAT IN [0..2000) DO
SimulateALS652Data: PROC = {
FOR i: NAT IN [0..8) DO
baMux: Ports.Level ← (SELECT TRUE FROM
st.p[st.pi[SBA]].b => st.BVal[i],
NOT st.p[st.pi[GAB]].b => st.p[st.pi[B]].ls[i],
st.p[st.pi[SAB]].b => st.AVal[i],
st.p[st.pi[nGBA]].b => st.p[st.pi[A]].ls[i],
ENDCASE => ERROR -- logical loop -- );
abMux: Ports.Level ← (SELECT TRUE FROM
st.p[st.pi[SAB]].b => st.AVal[i],
st.p[st.pi[nGBA]].b => st.p[st.pi[A]].ls[i],
st.p[st.pi[SBA]].b => st.BVal[i],
NOT st.p[st.pi[GAB]].b => st.p[st.pi[B]].ls[i],
ENDCASE => ERROR -- logical loop -- );
IF st.p[st.pi[GAB]].b THEN st.p[st.pi[B]].ls[i] ← abMux;
IF NOT st.p[st.pi[nGBA]].b THEN st.p[st.pi[A]].ls[i] ← baMux;
ENDLOOP;
};
gba, gab, sba, sab, cba, cab: BOOL;
didEval: BOOLFALSE;
Choose at most one random thing to do next....
DO
gba ← NOT st.p[st.pi[nGBA]].b;
gab ← st.p[st.pi[GAB]].b;
sba ← st.p[st.pi[SBA]].b;
sab ← st.p[st.pi[SAB]].b;
cba ← st.p[st.pi[CBA]].b;
cab ← st.p[st.pi[CAB]].b;
SELECT genRS.ChooseInt[max: 8] FROM
0 => gba ← NOT gba;
1 => gab ← NOT gab;
2 => sba ← NOT sba;
3 => sab ← NOT sab;
4 => cba ← NOT cba;
5 => cab ← NOT cab;
ENDCASE => NULL;
Check that it's legal...
IF NOT gba OR NOT gab OR sba OR sab THEN EXIT; -- it's OK
ENDLOOP;
To avoid complications of high-impedance buses oscillating, we overlap drive of tester and DUT by one test vector.
SELECT TRUE FROM
st.p[st.pi[GAB]].b AND NOT gab => { -- device port B turning off, tester drives the same value for one vector
st.p[st.pi[GAB]].b ← FALSE;
st.p[st.pi[B]].d ← drive;
Eval[];
};
NOT st.p[st.pi[GAB]].b AND gab => { -- device port B turning on
st.p[st.pi[GAB]].b ← TRUE;
SimulateALS652Data[];
figure out what device port B would be saying if it were on now, and drive that from the tester before turning device port B on.
st.p[st.pi[GAB]].b ← FALSE;
Eval[];
st.p[st.pi[GAB]].b ← TRUE; -- now turn device port B on
Eval[];
st.p[st.pi[B]].d ← expect; -- now disable tester drive
};
NOT st.p[st.pi[nGBA]].b AND NOT gba => { -- device port A turning off, tester drives the same value for one vector
st.p[st.pi[nGBA]].b ← NOT FALSE;
st.p[st.pi[A]].d ← drive;
Eval[];
};
st.p[st.pi[nGBA]].b AND gba => { -- device port A turning on
st.p[st.pi[nGBA]].b ← NOT TRUE;
SimulateALS652Data[];
figure out what device port A would be saying if it were on now, and drive that from the tester before turning device port A on.
st.p[st.pi[nGBA]].b ← NOT FALSE;
Eval[];
st.p[st.pi[nGBA]].b ← NOT TRUE; -- now turn device port A on
Eval[];
st.p[st.pi[A]].d ← expect; -- now disable tester drive
};
NOT st.p[st.pi[CAB]].b AND cab => { -- clock A->B rising
st.p[st.pi[CAB]].b ← TRUE;
FOR i: NAT IN [0..8) DO
st.AVal[i] ← st.p[st.pi[A]].ls[i];
ENDLOOP;
SimulateALS652Data[];
Eval[];
IF controlledClocks THEN {
st.p[st.pi[CAB]].b ← FALSE; -- for clock edge noise immunity
Eval[];
};
};
NOT st.p[st.pi[CBA]].b AND cba => { -- clock B->A rising
st.p[st.pi[CBA]].b ← TRUE;
FOR i: NAT IN [0..8) DO
st.BVal[i] ← st.p[st.pi[B]].ls[i];
ENDLOOP;
SimulateALS652Data[];
Eval[];
IF controlledClocks THEN {
st.p[st.pi[CBA]].b ← FALSE; -- for clock edge noise immunity
Eval[];
};
};
ENDCASE => {
st.p[st.pi[SAB]].b ← sab;
st.p[st.pi[SBA]].b ← sba;
st.p[st.pi[CAB]].b ← cab;
st.p[st.pi[CBA]].b ← cba;
FOR i: NAT IN [0..8) DO
IF st.p[st.pi[A]].d = drive THEN -- tester is driving port A
st.p[st.pi[A]].ls[i] ← (IF genRS.ChooseInt[max: 1] < 1 THEN H ELSE L);
IF st.p[st.pi[B]].d = drive THEN -- tester is driving port B
st.p[st.pi[B]].ls[i] ← (IF genRS.ChooseInt[max: 1] < 1 THEN H ELSE L);
ENDLOOP;
SimulateALS652Data[];
Eval[];
};
ENDLOOP;
Closem[mts, ascii];
};
ALS652Test: RosemaryUser.TestProc ~ {
st: State = GetAllState[cellType, p, Eval];
mts: MTSVector.File ← NIL; -- MTS vector file handle
ascii: IO.STREAMNIL; -- readable vector format stream
IF makeMTSVectors THEN
mts ← MTSVector.Create[ct: st.cellType, fileBase: "ALS652Tests"];
IF makeOtherVectors THEN {
ascii ← FS.StreamOpen[fileName: "ALS652T.av", accessOptions: $create];
ascii.PutF[" %g\n", IO.time[]];
FOR aw: ALS652Wires IN ALS652Wires DO
ascii.PutF[" %g [%g]\n", IO.rope[ALS652WireNames[aw]],
IO.int[CoreOps.WireBits[st.cellType.public[st.pi[aw]]]]];
ENDLOOP;
ascii.PutRope["\n\n"];
};
RunTest[st, mts, ascii, TRUE];
};
lastGenSeed: INT ← 1354117939;
genRS: Random.RandomStream ← NIL;
SetupSeed: PROC [ seed: INT ← 0 ] RETURNS [ IO.ROPE ] = {
IF seed = 0 THEN seed ← lastGenSeed;
genRS ← Random.Create[seed: seed];
lastGenSeed ← genRS.seed;
RETURN[IO.PutFR["New ALS652 random number seed is %d.\n", IO.int[lastGenSeed]]];
};
ALS652RandSeedProc: PROC [ cmd: Commander.Handle ] RETURNS [ result: REFNIL, msg: IO.ROPENIL ] -- Commander.CommandProc -- = {
seed: INT ← 0;
argv: CommandTool.ArgumentVector = CommandTool.Parse[cmd];
IF argv.argc > 1 THEN seed ← Convert.IntFromRope[ argv[1] ! Convert.Error => CONTINUE ];
cmd.out.PutRope[SetupSeed[seed]];
};
Commander.Register["ALS652RandSeed", ALS652RandSeedProc, "Reseeds the random number generator for the ALS652 simulation. Usage is\n ALS652RandSeed n\n where n is an integer. 0 recovers the previous seed."];
RosemaryUser.RegisterTestProc["ALS652Test", ALS652Test];
END.