[Indigo]<Rosemary>®>Rosemary.DF=>RoseTestingImpl.Mesa
Last Edited by: Spreitzer, July 2, 1985 9:08:24 pm PDT
Last Edited by: Barth, January 25, 1984 3:44 pm
Last Edited by: Gasbarro, July 17, 1984 3:52:04 pm PDT
DIRECTORY AMBridge, AMTypes, Ascii, BitTwiddling, Cucumber, IO, OrderedSymbolTableRef, PrincOps, PrincOpsUtils, Rope, RoseClocks, RoseCreate, RoseRun, RoseStateIO, RoseTypes;
RoseTestingImpl: CEDAR PROGRAM
IMPORTS AMBridge, AMTypes, Ascii, BitTwiddling, Cucumber, IO, OrderedSymbolTableRef, PrincOpsUtils, Rope, RoseClocks, RoseCreate, RoseRun, RoseStateIO, RoseTypes
EXPORTS RoseCreate, RoseRun, RoseTypes =
BEGIN OPEN RoseTypes, RoseRun, RoseCreate;
InterfaceMismatch: PUBLIC ERROR [cell: Cell,
index: CARDINAL,
expected, got: NodeType] = CODE;
TV: TYPE = AMTypes.TV;
Type: TYPE = AMTypes.Type;
WrapTop: PUBLIC PROC [rootName, typeName: ROPE, decider: ExpandDeciderClosure, clocks: ROPENIL, sim: Simulation] =
BEGIN
rootType: CellType;
rootType ← GetWrappingType[typeName, clocks];
CreateTopCell[instanceName: rootName, typeName: rootType.name, decider: decider, sim: sim];
END;
GetWrappingType: PROC [subName, clocks: ROPE] RETURNS [wrappingType: CellType] =
BEGIN
wrappingName: ROPE = subName.Cat["-wrap-", clocks];
subType: CellType;
IF (wrappingType ← GetCellType[wrappingName]) # NIL THEN RETURN;
IF (subType ← GetCellType[subName]) = NIL THEN ERROR Error[msg: "No such type", data: subName];
wrappingType ← RegisterCellType[
name: wrappingName,
expandProc: ExpandWrapper,
evals: [],
ports: NEW [PortsRep[0]],
typeData: NEW [WrapTypeRep ← [subType, clocks]]];
END;
WrapType: TYPE = REF WrapTypeRep; WrapTypeRep: TYPE = RECORD [
subType: CellType,
clocks: ROPE];
ExpandWrapper: PROC [thisCell: Cell, to: ExpansionReceiver] --ExpandProc-- =
BEGIN
wc: WrapType ← NARROW[thisCell.type.typeData];
IF wc.clocks # NIL THEN {
clockType: CellType ← RoseClocks.ClockGen[[]];
PerClock: PROC [key: Key, nodeName: ROPE] = {
portIndex: PortIndex ← nilPortIndex;
WITH key SELECT FROM
x: Key.position => portIndex ← x.index;
x: Key.keyword => portIndex ← GetIndex[clockType.ports, x.name];
ENDCASE => ERROR;
[] ← to.class.NodeInstance[erInstance: to.instance, name: nodeName, type: clockType.ports[portIndex].type];
};
EnumerateConnections[wc.clocks, PerClock];
[] ← to.class.CellInstance[erInstance: to.instance, instanceName: "clockGen", typeName: clockType.name, interfaceNodes: wc.clocks];
};
EnsureEnvironment[in: thisCell, forType: wc.subType, to: to];
[] ← to.class.CellInstance[erInstance: to.instance, instanceName: LowerFirst[wc.subType.name], typeName: wc.subType.name, interfaceNodes: ""];
END;
LowerFirst: PROC [u: ROPE] RETURNS [l: ROPE] = {
IF u.Length[] = 0 THEN RETURN [u];
l ← Rope.FromChar[Ascii.Lower[u.Fetch[0]]].Concat[u.Substr[start: 1]]};
EnsureEnvironment: PUBLIC PROC [in: Cell, forType: CellType, to: ExpansionReceiver] =
BEGIN
FOR portIndex: CARDINAL IN [0 .. forType.ports.length) DO
port: Port = forType.ports[portIndex];
node: Node ← NARROW[in.internalNodes.Lookup[port.name]];
s: Strength ← charge;
iv: ROPENIL;
IF node # NIL THEN LOOP;
IF port.name.Equal["gnd", FALSE] THEN {s ← input; iv ← "gnd"} ELSE
IF port.name.Equal["vdd", FALSE] THEN {s ← input; iv ← "vdd"} ELSE
IF port.input AND NOT port.output THEN s ← input ELSE
IF port.output AND NOT port.input THEN s ← chargeWeak;
[] ← to.class.NodeInstance[erInstance: to.instance, name: port.name, type: port.type, initialValue: iv, initialValueFormat: IF iv # NIL THEN "init" ELSE NIL, initData: NEW [Strength ← s]];
ENDLOOP;
END;
ProcToInt: PROC [p: PROC ANY RETURNS ANY] RETURNS [i: INT] =
TRUSTED {i ← LOOPHOLE[p, INTEGER]};
MakeTesterPorts: PROC [testee: Ports, alwaysOutput, alwaysInput: BOOLFALSE] RETURNS [tester: Ports] =
BEGIN
tester ← NEW [PortsRep[testee.length]];
FOR i: CARDINAL IN [0 .. testee.length) DO
tester[i] ← testee[i];
IF testee[i].instructionsSimple # testee[i].type.simple THEN {
IF testee[i].type.simple THEN ERROR;
tester[i].type ← testee[i].type.procs.SimpleEquivalent[testee[i].type];
};
tester[i].input ← alwaysInput OR testee[i].output;
tester[i].output ← alwaysOutput OR testee[i].input;
ENDLOOP;
END;
CreateTest: PUBLIC PROC [rootName, typeName, testName: ROPE, decider: ExpandDeciderClosure, sim: Simulation, dual: BOOLFALSE] =
BEGIN
testeeType: CellType ← GetCellType[typeName];
test: CellTest;
testerName: ROPE = "tester";
testeeName: ROPE = "testee";
singleType, testerType: CellType;
singleTypeName, topTypeName: ROPE;
sd: SingleData;
td: TesterData;
cth: CellTestHandle;
IF testeeType = NIL THEN ERROR Error[msg: "No such type", data: typeName];
test ← GetTest[name: testName, from: testeeType];
sd ← NEW [SingleDataRep ← [
testeeType: testeeType,
testerTypeName: IO.PutFR["%g-Tester(%g)", IO.rope[typeName], IO.rope[testName]],
testeeTypeName: typeName,
testerName: testerName,
testeeName: testeeName]];
td ← NEW [TesterDataRep ← [
testeeType: testeeType,
test: test]];
singleTypeName ← IO.PutFR["%g-TesterSingle(%g)", IO.rope[typeName], IO.rope[testName]];
IF (testerType ← GetCellType[sd.testerTypeName]) = NIL
THEN testerType ← RegisterCellType[
name: sd.testerTypeName,
ioCreator: CreateTesterIO,
initializer: InitTester,
evals: testerEvals,
ports: MakeTesterPorts[testee: testeeType.ports, alwaysOutput: TRUE],
driveCreator: CreateTesterDrive,
typeData: td];
IF (singleType ← GetCellType[singleTypeName]) = NIL
THEN singleType ← RegisterCellType[
name: singleTypeName,
expandProc: ExpandTestSingle,
evals: [],
ports: NEW [PortsRep[0]],
typeData: sd];
IF dual THEN {
dualTypeName: ROPE ← Rope.Cat[singleTypeName, "-Dual"];
dualType: CellType ← GetCellType[dualTypeName];
dd: DualData ← NEW [DualDataRep ← [singleTypeName]];
IF dualType = NIL THEN dualType ← RegisterCellType[
name: dualTypeName,
expandProc: ExpandDualTester,
evals: [],
ports: NEW [PortsRep[0]],
typeData: dd];
topTypeName ← dualTypeName;
cth ← NEW [CellTestHandleRep.dual ← [sim: sim, test: test, testerType: testerType, testeeType: testeeType, variant: dual[]]];
}
ELSE {
topTypeName ← singleTypeName;
cth ← NEW [CellTestHandleRep.single ← [sim: sim, test: test, testerType: testerType, testeeType: testeeType, variant: single[]]];
};
sim.cth ← cth;
CreateTopCell[instanceName: rootName, typeName: topTypeName, decider: decider, sim: sim];
WITH cth SELECT FROM
scth: SingleCTH => {
scth.testee ← LookupCell[path: LIST[testeeName], from: sim.root];
scth.tester ← LookupCell[path: LIST[testerName], from: sim.root];
};
dcth: DualCTH => {
dcth.testee[1] ← LookupCell[path: LIST["1", testeeName], from: sim.root];
dcth.tester[1] ← LookupCell[path: LIST["1", testerName], from: sim.root];
dcth.testee[2] ← LookupCell[path: LIST["2", testeeName], from: sim.root];
dcth.tester[2] ← LookupCell[path: LIST["2", testerName], from: sim.root];
};
ENDCASE => ERROR;
END;
TesterData: TYPE = REF TesterDataRep;
TesterDataRep: TYPE = RECORD [
testeeType: CellType,
test: CellTest];
SingleData: TYPE = REF SingleDataRep;
SingleDataRep: TYPE = RECORD [
testeeType: CellType,
testerTypeName, testeeTypeName, testerName, testeeName: ROPE];
DualData: TYPE = REF DualDataRep;
DualDataRep: TYPE = RECORD [
singleTypeName: ROPE];
ExpandTestSingle: PROC [thisCell: Cell, to: ExpansionReceiver] --ExpandProc-- =
BEGIN
sd: SingleData ← NARROW[thisCell.type.typeData];
FOR i: INT IN [0 .. sd.testeeType.ports.length) DO
p: Port ← sd.testeeType.ports[i];
[] ← to.class.NodeInstance[erInstance: to.instance, name: p.name, type: p.type];
ENDLOOP;
[] ← to.class.CellInstance[erInstance: to.instance, instanceName: sd.testeeName, typeName: sd.testeeTypeName, interfaceNodes: ""];
[] ← to.class.CellInstance[erInstance: to.instance, instanceName: sd.testerName, typeName: sd.testerTypeName, interfaceNodes: ""];
END;
ExpandDualTester: PROC [thisCell: Cell, to: ExpansionReceiver] --ExpandProc-- =
BEGIN
dd: DualData ← NARROW[thisCell.type.typeData];
[] ← to.class.CellInstance[erInstance: to.instance, instanceName: "1", typeName: dd.singleTypeName, interfaceNodes: ""];
[] ← to.class.CellInstance[erInstance: to.instance, instanceName: "2", typeName: dd.singleTypeName, interfaceNodes: ""];
END;
testerEvals: EvalProcs ← [
PropQ: PropTesterQUD,
PropUD: PropTesterQUD,
ValsChanged: TesterValsChanged,
EvalSimple: TesterSimple];
CellTestHandle: TYPE = REF CellTestHandleRep;
CellTestHandleRep: PUBLIC TYPE = RECORD [
sim: Simulation,
parms: RoseRun.TestParms ← NIL,
switchInstructionsAsAny: REF ANYNIL,
switchInstructionsAsWP: WordPtr ← NIL,
simpleInstructionsAsAny: REF ANYNIL,
simpleInstructionsAsWP: WordPtr ← NIL,
driveAsAny: REF ANYNIL,
driveAsDrive: Drive ← NIL,
test: CellTest,
testerType, testeeType: CellType,
variant: SELECT kind: * FROM
single => [tester, testee: Cell ← NIL],
dual => [
tester, testee: ARRAY [1 .. 2] OF Cell ← ALL[NIL],
altSwitchInstructionsAsAny: REF ANYNIL,
altSwitchInstructionsAsWP: WordPtr ← NIL,
altSimpleInstructionsAsAny: REF ANYNIL,
altSimpleInstructionsAsWP: WordPtr ← NIL
],
ENDCASE
];
SingleCTH: TYPE = REF CellTestHandleRep.single;
DualCTH: TYPE = REF CellTestHandleRep.dual;
Waiting: TYPE = {Tester, Testee};
NarrowToCellTestHandle: PUBLIC PROC [any: REF ANY] RETURNS [cth: CellTestHandle] = {cth ← NARROW[any]};
GetSimulationFromCellTestHandle: PUBLIC PROC [cth: CellTestHandle] RETURNS [sim: Simulation] = {sim ← cth.sim};
TransferCellTestHandle: PROC [whole: REF ANY, part: Cucumber.Path, where: IO.STREAM, direction: Cucumber.Direction, data: REF ANY] --Cucumber.PartTransferProc-- = TRUSTED
BEGIN
cth: CellTestHandle ← NARROW[whole];
SELECT part.first FROM
$sim, $tester, $testee, $testeeInitData, $switchInstructionsAsAny, $simpleInstructionsAsAny, $driveAsDrive => NULL;
$parms => Cucumber.Transfer[what: cth.parms, where: where, direction: direction];
$switchInstructionsAsWP => RoseStateIO.TransferWords[cth.switchInstructionsAsWP, cth.testerType.switchWordCount, where, direction];
$simpleInstructionsAsWP => RoseStateIO.TransferWords[cth.simpleInstructionsAsWP, cth.testerType.simpleWordCount, where, direction];
$driveAsAny => Cucumber.Transfer[what: cth.driveAsAny, where: where, direction: direction];
ENDCASE => ERROR;
END;
CreateTesterIO: PROC [ct: CellType, switch: BOOL] RETURNS [ioAsAny: REF ANY] --IOCreator-- =
BEGIN
td: TesterData ← NARROW[ct.typeData];
ioAsAny ← td.testeeType.ioCreator[td.testeeType, switch];
END;
CreateTesterDrive: PROC [ct: CellType] RETURNS [ioAsAny: REF ANY] --DriveCreator-- =
BEGIN
td: TesterData ← NARROW[ct.typeData];
ioAsAny ← td.testeeType.driveCreator[td.testeeType];
END;
InitTester: PROC [cell: Cell] --Initializer-- =
BEGIN
td: TesterData ← NARROW[cell.type.typeData];
cth: CellTestHandle ← cell.sim.cth;
cth.switchInstructionsAsAny ← td.testeeType.ioCreator[td.testeeType, TRUE];
cth.switchInstructionsAsWP ← LOOPHOLE[cth.switchInstructionsAsAny];
cth.simpleInstructionsAsAny ← td.testeeType.ioCreator[td.testeeType, FALSE];
cth.simpleInstructionsAsWP ← LOOPHOLE[cth.simpleInstructionsAsAny];
cth.driveAsAny ← NIL;
cth.driveAsDrive ← NIL;
IF cell.type.driveCreator # NIL THEN {
driveAsAny: REF ANY ← td.testeeType.driveCreator[td.testeeType];
IF driveAsAny # NIL THEN {
driveAsTV: TV;
gotSize: INT;
neededSize: INTSIZE[DriveRep[cell.type.ports.length]];
TRUSTED {driveAsTV ← AMBridge.TVForReferent[driveAsAny]};
gotSize ← AMTypes.TVSize[driveAsTV];
IF gotSize < neededSize THEN ERROR Error[IO.PutFR["DriveCreator for cell type %g returned one too small", IO.rope[cell.type.name]], driveAsAny];
cth.driveAsAny ← driveAsAny;
TRUSTED {cth.driveAsDrive ← LOOPHOLE[driveAsAny]};
FOR pi: NAT IN [0 .. cell.type.ports.length) DO
cth.driveAsDrive[pi] ← IF cell.type.ports[pi].input THEN test ELSE IF cell.type.ports[pi].name.Equal["vdd", FALSE] OR cell.type.ports[pi].name.Equal["gnd", FALSE] THEN input ELSE drive;
ENDLOOP;
};
};
cell.realCellStuff.state ← cth;
END;
PropTesterQUD: CellProc--PROC [cell: Cell]-- =
BEGIN
cth: CellTestHandle ← NARROW[cell.realCellStuff.state];
CopyIO[from: cth.switchInstructionsAsWP, to: cell.realCellStuff.switchIOAsWP, by: cell.type.ports, what: forward, simple: FALSE, drive: NIL];
END;
DirectionInstruction: TYPE = {none, forward, backward, check, moveByDrive, checkTests, checkIns};
CopyIO: PROC [from, to: WordPtr, by: Ports, what: DirectionInstruction, simple: BOOLEAN, drive: Drive, cell: Cell ← NIL] =
BEGIN OPEN BitTwiddling;
bbTableSpace: PrincOps.BBTableSpace;
bbTable: PrincOps.BitBltTablePtr;
TRUSTED {bbTable ← PrincOpsUtils.AlignedBBTable[@bbTableSpace]};
FOR portIndex: CARDINAL IN [0 .. by.length) DO
port: Port ← by[portIndex];
di: DirectionInstruction;
bitCount, leftPad: INTEGER;
IF port.type.simple # simple THEN LOOP;
[data: bitCount, leftPad: leftPad] ← port.type.procs.Bits[port.type];
{
field: Field ← IF port.type.simple THEN port.simple ELSE port.switch;
fromPtr: Ptr ← OffsetPtr[WPField[from, field], leftPad];
toPtr: Ptr ← OffsetPtr[WPField[to, field], leftPad];
SELECT what FROM
moveByDrive => IF port.type.simple
THEN {
di ← SELECT drive.drives[portIndex] FROM
test => none,
see => backward,
ignore => none,
chargeWeak, chargeMediumWeak, charge, chargeMediumStrong, chargeStrong, chargeVeryStrong, driveWeak, driveMediumWeak, drive, driveMediumStrong, driveStrong, driveVeryStrong, input => forward,
ENDCASE => ERROR;
}
ELSE ERROR;
checkTests => IF port.type.simple
THEN {
di ← SELECT drive.drives[portIndex] FROM
test => check,
see => none,
ignore => none,
chargeWeak, chargeMediumWeak, charge, chargeMediumStrong, chargeStrong, chargeVeryStrong, driveWeak, driveMediumWeak, drive, driveMediumStrong, driveStrong, driveVeryStrong, input => none,
ENDCASE => ERROR;
}
ELSE ERROR;
checkIns => IF port.type.simple
THEN {
di ← SELECT drive.drives[portIndex] FROM
test => check,
see => check,
ignore => none,
chargeWeak, chargeMediumWeak, charge, chargeMediumStrong, chargeStrong, chargeVeryStrong, driveWeak, driveMediumWeak, drive, driveMediumStrong, driveStrong, driveVeryStrong, input => none,
ENDCASE => ERROR;
}
ELSE ERROR;
none, forward, backward, check => di ← what;
ENDCASE => ERROR;
SELECT di FROM
none => NULL;
forward => Copy[fromPtr, toPtr, bitCount, bbTable];
backward => Copy[toPtr, fromPtr, bitCount, bbTable];
check => IF NOT Equal[fromPtr, toPtr, bitCount] THEN SIGNAL Warning[IO.PutFR["test failed on %g", IO.rope[RoseCreate.LongPortName[cell, portIndex]]]];
moveByDrive, checkTests => ERROR;
ENDCASE => ERROR;
}
ENDLOOP;
END;
Fmt: PROC [n: Node, p: Ptr] RETURNS [rope: ROPE] = {
fmt: Format ← n.type.procs.GetFormat[n.type, ""];
rope ← fmt.FormatValue[n, fmt, p];
};
TesterValsChanged: PROC [cell: Cell, perturb: PROC [portIndex: PortIndex]] --SimpleEval-- =
BEGIN OPEN BitTwiddling;
cth: CellTestHandle ← NARROW[cell.realCellStuff.state];
swp: WordPtr ← WITH cth SELECT FROM
dcth: DualCTH => SELECT cell FROM
dcth.tester[1] => dcth.switchInstructionsAsWP,
dcth.tester[2] => dcth.altSwitchInstructionsAsWP,
ENDCASE => ERROR,
scth: SingleCTH => scth.switchInstructionsAsWP,
ENDCASE => ERROR;
FOR portIndex: NAT IN [0 .. cell.type.ports.length) DO
port: Port ← cell.type.ports[portIndex];
IF (NOT port.type.simple) THEN {
leftPad, bits: INT;
[data: bits, leftPad: leftPad] ← port.type.procs.Bits[port.type];
port.type.procs.CopyVal[
nt: port.type,
from: OffsetPtr[WPField[cell.realCellStuff.switchIOAsWP, port.switch], leftPad],
to: OffsetPtr[WPField[swp, port.switch], leftPad]
];
};
ENDLOOP;
END;
TesterSimple: PROC [cell: Cell, perturb: PROC [portIndex: PortIndex]] --SimpleEval-- =
BEGIN
cth: CellTestHandle ← NARROW[cell.realCellStuff.state];
swp: WordPtr ← cth.simpleInstructionsAsWP;
cellDrive: Drive ← cell.realCellStuff.newDrive;
WITH cth SELECT FROM
dcth: DualCTH => SELECT cell FROM
dcth.tester[1] => NULL;
dcth.tester[2] => {
swp ← dcth.altSimpleInstructionsAsWP;
};
ENDCASE => ERROR;
scth: SingleCTH => NULL;
ENDCASE => ERROR;
FOR pi: PortIndex IN [0 .. cell.type.ports.length) DO
cellDrive[pi] ← MAX[cth.driveAsDrive[pi], FIRST[Strength]];
ENDLOOP;
CopyIO[from: swp, to: cell.realCellStuff.newIOAsWP, by: cell.type.ports, what: moveByDrive, simple: TRUE, drive: cth.driveAsDrive];
cell ← cell;
END;
Eval: PUBLIC PROC [handle: CellTestHandle, returnAfter: ReturnAfter ← returnWhenSettled] RETURNS [happened: StepType] =
BEGIN
iowp: WordPtr ← NIL;
tester: Cell ← NIL;
WITH handle SELECT FROM
dcth: DualCTH => {
ScheduleCell[dcth.tester[1]];
ScheduleCell[dcth.tester[2]];
PerturbDifferences[dcth, dcth.tester[1]];
PerturbDifferences[dcth, dcth.tester[2]];
CopyIO[from: dcth.simpleInstructionsAsWP, to: dcth.altSimpleInstructionsAsWP, by: dcth.testerType.ports, what: forward, simple: TRUE, drive: dcth.driveAsDrive];
CopyIO[from: dcth.switchInstructionsAsWP, to: dcth.altSwitchInstructionsAsWP, by: dcth.testerType.ports, what: forward, simple: FALSE, drive: dcth.driveAsDrive];
};
scth: SingleCTH => {
ScheduleCell[scth.tester];
PerturbDifferences[scth, scth.tester];
};
ENDCASE => ERROR;
IF handle.parms.stopBefore THEN SIGNAL Stop["About to eval"];
DO
happened ← StepSim[handle.sim];
IF (IF happened IN MaskableStepType THEN returnAfter[happened] ELSE TRUE) THEN EXIT;
ENDLOOP;
WITH handle SELECT FROM
dcth: DualCTH => {
tester ← dcth.tester[1];
CopyIO[from: dcth.simpleInstructionsAsWP, to: dcth.altSimpleInstructionsAsWP, by: dcth.testerType.ports, what: checkIns, simple: TRUE, drive: dcth.driveAsDrive, cell: tester];
CopyIO[from: dcth.switchInstructionsAsWP, to: dcth.altSwitchInstructionsAsWP, by: dcth.testerType.ports, what: check, simple: FALSE, drive: dcth.driveAsDrive, cell: tester];
};
scth: SingleCTH => {
tester ← scth.tester;
};
ENDCASE => ERROR;
iowp ← tester.realCellStuff.newIOAsWP;
CopyIO[from: handle.simpleInstructionsAsWP, to: iowp, by: handle.testerType.ports, what: checkTests, simple: TRUE, drive: handle.driveAsDrive, cell: tester];
IF handle.parms.stopAfter THEN SIGNAL Stop["Just eval'd"];
END;
PerturbDifferences: PROC [cth: CellTestHandle, cell: Cell] =
BEGIN OPEN BitTwiddling;
FOR portIndex: PortIndex IN [0 .. cell.type.ports.length) DO
port: Port ← cell.type.ports[portIndex];
different: BOOLFALSE;
leftPad: INT;
IF port.type.simple THEN LOOP;
leftPad ← port.type.procs.Bits[port.type].leftPad;
TRUSTED {different ← port.type.procs.CompareUD[
port.type,
OffsetPtr[WPField[cth.switchInstructionsAsWP, port.switch], leftPad],
OffsetPtr[WPField[cell.realCellStuff.switchIOAsWP, port.switch], leftPad]
]};
IF different THEN PerturbNode[node: cell.interfaceNodes[portIndex], agitator: nilSlot, evenIfInput: TRUE];
ENDLOOP;
END;
Test: PUBLIC PROC [sim: Simulation, parms: TestParms, testData: REF ANYNIL] =
BEGIN
cth: CellTestHandle ← sim.cth;
test: CellTest ← cth.test;
state: REF ANYNIL;
IF test = NIL THEN ERROR Error["Type has no such test proc", cth];
cth.parms ← parms;
IF test.stateToo THEN state ← WITH cth SELECT FROM
scth: SingleCTH => scth.testee.realCellStuff.state,
dcth: DualCTH =>
IF dcth.testee[1].realCellStuff # NIL THEN dcth.testee[1].realCellStuff.state ELSE
IF dcth.testee[2].realCellStuff # NIL THEN dcth.testee[2].realCellStuff.state ELSE ERROR,
ENDCASE => ERROR;
test.proc[handle: cth, testeeType: cth.testeeType, testData: testData, switchInstructions: cth.switchInstructionsAsAny, simpleInstructions: cth.simpleInstructionsAsAny, driveAsAny: cth.driveAsAny, stateAsAny: state];
WHILE StepSim[sim] # noStep DO NULL ENDLOOP;
END;
cthHandler: Cucumber.Handler ← NEW [Cucumber.HandlerRep ← [TransferCellTestHandle]];
cthHandler.Register[CODE[CellTestHandleRep]];
END.