ICTestImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Created by: Gasbarro December 3, 1985 6:15:34 pm PST
Last Edited by: Gasbarro May 28, 1986 5:34:01 pm PDT
DIRECTORY Basics, Buttons, ChoiceButtons, Commander, CommandTool, Containers, Convert, Core, CoreOps, EGlas, FS, ICTest, IO, IMSTester, MessageWindow, Ports, Process, Rope, --Rosemary,-- Rules, RuntimeError, SymTab, ViewerClasses, ViewerIO, ViewerOps, ViewerTools;
ICTestImpl: CEDAR PROGRAM
IMPORTS Basics, Buttons, ChoiceButtons, Commander, CommandTool, Containers, Convert, CoreOps, EGlas, FS, IO, IMSTester, MessageWindow, Ports, Process, Rope, --Rosemary,-- Rules, RuntimeError, SymTab, ViewerIO, ViewerOps, ViewerTools
EXPORTS ICTest
= BEGIN
OPEN ICTest;
maxErrors: NAT ← 10;
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
signalNameLength: CARDINAL = 6; --IMS signal name length restriction
groupNameLength: CARDINAL = 9; --IMS group name name length restriction
PodTimingGroups: TYPE = IMSTester.PodTimingGroups;
Board: TYPE = IMSTester.Board;
PodTiming: TYPE = IMSTester.PodTiming;
Channel: TYPE = IMSTester.Channel;
PodChannel: TYPE = IMSTester.PodChannel;
AbortDieSignal: PUBLIC ERROR = CODE;
AbortWaferSignal: PUBLIC ERROR = CODE;
InterruptSignal: PUBLIC ERROR = CODE;
Stop: TYPE = {dont, abortDie, abortWafer, interrupt};
DiePosition: TYPE = RECORD [x,y: NAT];
TestHandle: TYPE = REF TestHandleRec;
TestHandleRec: TYPE = RECORD[h: Handle, proc: TestProc];
MapRec: TYPE = RECORD[b: Board, p: PodChannel, port: Ports.Port, index: NAT];
BoardToSlot: TYPE = ARRAY Board OF RECORD[slot: NAT, programable: BOOL];
ForceSubGroupsRec: TYPE = RECORD[fullName: ROPE, subGroups: IMSTester.ForceGroups];
AcquireSubGroupsRec: TYPE = RECORD[fullName: ROPE, subGroups: IMSTester.AcquireGroups];
Handle: TYPE = REF ICTestRec;
ICTestRec: PUBLIC TYPE = RECORD [
-- control panel state --
waferFile: Viewer ← NIL,
run: Viewer ← NIL,
wafer: Viewer ← NIL,
die: Viewer ← NIL,
errorCycle: Viewer ← NIL,
period: Viewer ← NIL,
group: Viewer ← NIL,
delay: Viewer ← NIL,
width: Viewer ← NIL,
sample: Viewer ← NIL,
hiDrive: Viewer ← NIL,
loDrive: Viewer ← NIL,
threshold: Viewer ← NIL,
enableStepper: BOOLFALSE,
enableTester: BOOLFALSE,
enableSimulation: BOOLFALSE,
singleCycle: BOOLFALSE,
loopTest: BOOLFALSE,
repeatTest: BOOLFALSE,
testButtonList: LIST OF Buttons.Button,
currentTestProc: TestProc ← NIL,
currentDie: DiePosition, -- die to return to upon "Continue" button
stop: Stop ← dont, -- state of control buttons
backupToken: IO.STREAM, -- temp for file stream parsing
testInProgress: BOOLFALSE, -- button monitor
inStream: IO.STREAMNIL,
outStream: IO.STREAMNIL,
port: Ports.Port ← NIL,
cellType: Core.CellType ← NIL,
forceGroups: IMSTester.ForceGroups ← NIL,
acquireGroups: IMSTester.AcquireGroups ← NIL,
forceMap: LIST OF MapRec ← NIL,
acquireMap: LIST OF MapRec ← NIL,
cycle: IMSTester.Cycle ← 0,
buffer: IMSTester.Buffer ← NIL,
forceNamesTab: SymTab.Ref ← NIL,
acquireNamesTab: SymTab.Ref ← NIL,
groups: LIST OF Group ← NIL,
assignments: LIST OF Assignments ← NIL,
forceBoardToSlot: REF BoardToSlot ← NEW[BoardToSlot],
acquireBoardToSlot: REF BoardToSlot ← NEW[BoardToSlot],
forceSubGroups: LIST OF ForceSubGroupsRec,
acquireSubGroups: LIST OF AcquireSubGroupsRec
]; 
entryHeight: NAT = 15; -- how tall to make each line of items
entryVSpace: NAT = 4;  -- vertical leading space between lines
ptsPerIn: NAT = 72;  -- horizontal space for text ropes
pointsPerHalfInch: NAT = 36;  -- horizontal space for text ropes
col1: NAT = 0*pointsPerHalfInch; -- horizontal space to first column of buttons
col2: NAT = 4*pointsPerHalfInch; -- second column
col3: NAT = 7*pointsPerHalfInch; -- third column
col4: NAT = 10*pointsPerHalfInch; -- fourth column
col5: NAT = 13*pointsPerHalfInch; -- fifth column
Column: TYPE = NAT [0..4);
MakeStandardViewer: PUBLIC PROC [name: Core.ROPENIL, cellType: Core.CellType, testButtons: LIST OF TestButton, groups: LIST OF Group ← NIL, assignments: LIST OF Assignments ← NIL, period: Period ← 100] = {
CreateTestButton: PROC [buttonName: ROPE, proc: TestProc] = {
first: BOOLEAN ← h.testButtonList = NIL;
testHandle: TestHandle ← NEW[TestHandleRec ← [h, proc]];
h.testButtonList ← CONS[Buttons.Create[info: [name: buttonName,
wx: column*2*ptsPerIn, wy: height,
ww: 0,wh: entryHeight,
parent: viewer, border: TRUE], clientData: testHandle, proc: DoTestButton], h.testButtonList];
IF first THEN {
h.currentTestProc ← proc;
Buttons.SetDisplayStyle[h.testButtonList.first, $BlackOnGrey];
};
IF column = LAST[Column] THEN {
column ← 0;
height ← height + entryHeight + entryVSpace;
} ELSE column ← column+1;
};
Button: PROC [name: ROPE, col: NAT, proc: Buttons.ButtonProc, fork: BOOLTRUE] = {
[] ← Buttons.Create[info: [name: name, wx: col, wy: height, ww: 0, wh: entryHeight,
parent: viewer], clientData: h, proc: proc, fork: fork];
};
Prompt: PROC [name: ROPE, col: NAT, contents: ROPENIL] RETURNS [Viewer] = {
RETURN[ChoiceButtons.BuildTextPrompt[viewer, col, height, name, contents, NIL, 1*pointsPerHalfInch].textViewer];
};
h: Handle ← NEW[ICTestRec];
rule: Rules.Rule;
child: Viewer;
viewer: Viewer ← Containers.Create[[name: Rope.Concat["IC Test Tool - ", name], iconic: FALSE, column: left, scrollable: FALSE]];
column: Column ← 0;
height: CARDINAL ← 0;
Commander.Register[key: "///Commands/Name", proc: LookUpName,
doc: "Map an IMS signal name to/from the shortened version", clientData: h];
h.cellType ← cellType;
h.groups ← groups;
h.assignments ← assignments;
h.waferFile ← ChoiceButtons.BuildTextPrompt[viewer, col1, height, "Wafer File:", "SingleDie.dat", NIL, 7*ptsPerIn].textViewer;
height ← height + entryHeight + entryVSpace;
h.run ← Prompt["Run:", col1]; Button["Start Test", col2, StartTest]; Button["Interrupt", col3, Interrupt, FALSE]; Button["Single Cycle", col4, SingleCycle]; h.period ← Prompt["Period (nS):", col5, IO.PutR1[IO.int[period]]];
height ← height + entryHeight + entryVSpace;
h.wafer ← Prompt["Wafer:", col1]; Button["Abort Die", col2, AbortDie, FALSE]; Button["Continue", col3, Continue]; Button["Loop Test", col4, LoopTest];
height ← height + entryHeight + entryVSpace;
h.die ← Prompt["Die:", col1]; Button["Abort Wafer", col2, AbortWafer, FALSE]; Button["Enable Tester", col3, EnableTester]; Button["Stop Loop", col4, StopLoop]; Button["Repeat Test", col5, RepeatTest];
height ← height + entryHeight + entryVSpace;
h.errorCycle ← Prompt["Error Cycle:", col1, "0"]; Button["Abort Test", col2, AbortTest, FALSE]; Button["Enable Stepper", col3, EnableStepper]; Button["Get Errors", col4, GetErrors];
height ← height + entryHeight + entryVSpace/2+1;
rule ← Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]];
Containers.ChildXBound[viewer, rule];
height ← height + entryVSpace;
Button["Get Parameters", col1, GetParameters]; Button["Set Parameters", col2, SetParameters]; h.delay ← Prompt["Delay:", col3, "?"]; h.width ← Prompt["Width:", col4, "?"]; h.sample ← Prompt["Sample:", col5, "?"];
height ← height + entryHeight + entryVSpace;
h.group ← ChoiceButtons.BuildTextPrompt[viewer, col1, height, "Group:", "<Group name>", NIL, 2*ptsPerIn].textViewer; h.hiDrive ← Prompt["HiDrive:", col3, "?"]; h.loDrive ← Prompt["LoDrive:", col4, "?"]; h.threshold ← Prompt["Threshold:", col5, "?"];
height ← height + entryHeight + entryVSpace/2+1;
rule ← Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]];
Containers.ChildXBound[viewer, rule];
height ← height + entryVSpace;
FOR l: LIST OF TestButton ← testButtons, l.rest WHILE l#NIL DO
CreateTestButton[l.first.name, l.first.proc];
ENDLOOP;
height ← height + entryHeight + entryVSpace/2;
rule ← Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]];
Containers.ChildXBound[viewer, rule];
child ← ViewerOps.CreateViewer[flavor: $TypeScript, info:[parent: viewer, wy: height+2, ww: 7*ptsPerIn, wh: 6*ptsPerIn, scrollable: TRUE, border: FALSE]];
Containers.ChildYBound[viewer, child];
[ , h.outStream] ← ViewerIO.CreateViewerStreams[name: "ICTestTypescript", viewer: child];
height ← height + 1*ptsPerIn;
ViewerOps.SetOpenHeight[viewer, height];
};
DoTestButton: Buttons.ButtonProc = {
t: TestHandle ← NARROW[clientData];
h: Handle ← t.h;
selectedButton: Viewer ← NARROW[parent];
FOR l: LIST OF Buttons.Button ← h.testButtonList, l.rest WHILE l#NIL DO
Buttons.SetDisplayStyle[l.first, IF l.first = selectedButton THEN $BlackOnGrey ELSE $BlackOnWhite];
ENDLOOP;
h.currentTestProc ← t.proc;
};
DoTest: PROC [h: Handle] = {
NextToken: PROC [h: Handle] RETURNS [s: IO.STREAM] = {
s ← IF h.backupToken # NIL THEN h.backupToken ELSE IO.RIS[IO.GetTokenRope[h.inStream].token];
h.backupToken ← NIL;
};
BackupToken: PROC [s: IO.STREAM] RETURNS [] = {
h.backupToken ← s;
};
NextRun: PROC [h: Handle] RETURNS [done: BOOLEANFALSE] = {
c: CHAR;
s: IO.STREAM;
IF h.stop # interrupt THEN {
WHILE NOT done DO
s ← NextToken[h ! IO.EndOfStream => {done ← TRUE; CONTINUE}];
IF NOT done THEN SELECT (c ← IO.PeekChar[s]) FROM
'r, 'R => EXIT;
'w, 'W => NULL;
'd, 'D => NULL;
IN ['0..'9] => NULL;
ENDCASE => {Message["Error: Bad token in data file, aborting test"]; done ← TRUE};
ENDLOOP;
IF NOT done THEN {
ViewerTools.SetContents[h.run, IO.GetTokenRope[NextToken[h !
IO.EndOfStream => {done ← TRUE; CONTINUE}]].token];
IO.PutF[h.outStream, "\nRun %g", IO.rope[ViewerTools.GetContents[h.run]]];
};
IF done THEN {
IO.Close[h.inStream];
h.testInProgress ← FALSE;
};
};
};
NextWafer: PROC [h: Handle] RETURNS [done: BOOLEANFALSE] = {
c: CHAR;
s: IO.STREAM;
IF h.stop # interrupt THEN {
WHILE NOT done DO
s ← NextToken[h ! IO.EndOfStream => {done ← TRUE; CONTINUE}];
IF NOT done THEN SELECT (c ← IO.PeekChar[s]) FROM
'r, 'R => {done ← TRUE; BackupToken[s]};
'w, 'W => EXIT;
'd, 'D => NULL;
IN ['0..'9] => NULL;
ENDCASE => {Message["Error: Bad token in data file, aborting test"]; done ← TRUE};
ENDLOOP;
IF NOT done THEN {
ViewerTools.SetContents[h.wafer, IO.GetTokenRope[NextToken[h !
IO.EndOfStream => {done ← TRUE; CONTINUE}]].token];
IO.PutF[h.outStream, "\nWafer %g ", IO.rope[ViewerTools.GetContents[h.wafer]]];
IF h.enableStepper THEN {
EGlas.LampOn[];
IF h.testInProgress THEN EGlas.Load[]; --don't load wafer first time
} ;
};
};
};
NextDie: PROC [h: Handle] RETURNS [done: BOOLEANFALSE] = {
Eval: PROC [evalCycleType: EvalCycleType] = {
-- IF h.enableSimulation THEN Rosemary.Settle[Rosemary.InstantiateCellType[h.cellType, h.port]];
CheckStop[h];
SELECT evalCycleType FROM
force =>
ForceDataToBuffer[h];
sense => {
IF h.singleCycle THEN {
CompareDataToBuffer[h];
BufferToIMS[h, 1];
} ELSE {
CompareDataToBuffer[h];
h.cycle ← h.cycle+1;
IF h.cycle = h.buffer.cycle THEN NewBuffer[h];
};
};
ENDCASE => ERROR;
};
count: LONG CARDINAL;
c: CHAR;
s: IO.STREAM;
IF h.stop # interrupt THEN {
WHILE NOT done DO
s ← NextToken[h ! IO.EndOfStream => {done ← TRUE; CONTINUE}];
IF NOT done THEN SELECT (c ← IO.PeekChar[s]) FROM
'r, 'R => {done ← TRUE; BackupToken[s]};
'w, 'W => {done ← TRUE; BackupToken[s]};
'd, 'D => h.currentDie.y ← IO.GetInt[NextToken[h !
IO.EndOfStream => {done ← TRUE; CONTINUE}]];
IN ['0..'9] => EXIT;
ENDCASE => {Message["Error in data file, aborting test"]; done ← TRUE};
ENDLOOP;
IF NOT done THEN {
h.currentDie.x ← IO.GetInt[s];
ViewerTools.SetContents[h.die, IO.PutFR["%g,%g", IO.int[h.currentDie.y], IO.int[h.currentDie.x]]];
IO.PutF[h.outStream, "Die %g,%g ", IO.int[h.currentDie.y], IO.int[h.currentDie.x]];
};
};
IF NOT done THEN {
h.stop ← dont;
h.testInProgress ← TRUE;
IF h.enableStepper THEN {
EGlas.Seek[h.currentDie.x, h.currentDie.y];
EGlas.ZUp[];
EGlas.LampOff[];
};
h.currentTestProc[h.port, Eval];
{p: PROC ~ {h.currentTestProc[h]}; CedarProcess.DoWithPriority[priority: foreground, action: p]};
IF NOT h.singleCycle THEN BufferToIMS[h, h.cycle];
IF h.enableStepper THEN EGlas.LampOn[];
count ← IMSTester.ErrorCount[];
IO.PutF[h.outStream, IF count#0 THEN "Fail " ELSE "Pass; "];
IF count#0 THEN IO.PutF[h.outStream, "(%g); ", IO.card[count]];
};
};
IF h.inStream # NIL THEN
DO IF NextRun[h] THEN EXIT;
DO IF NextWafer[h] THEN EXIT;
DO IF NextDie[h !
AbortDieSignal => CONTINUE;
AbortWaferSignal => EXIT] THEN EXIT
ENDLOOP;
ENDLOOP;
ENDLOOP;
h.testInProgress ← FALSE;
};
NewBuffer: PROC [h: Handle] = {
oldBuffer: IMSTester.Buffer ← h.buffer;
h.buffer ← NEW[IMSTester.BufferRec[SELECT TRUE FROM
h.cycle<10 => 10,
h.cycle<100 => 100,
h.cycle<1000 => 1000,
h.cycle<10000 => 10000,
ENDCASE => 16383]];
FOR i: NAT IN [0..h.buffer.cycle) DO
h.buffer[i] ← IF i < h.cycle THEN oldBuffer[i] ELSE NEW[IMSTester.CycleDataRec];
ENDLOOP;
};
pause: INT ← 10;
StartTest: Buttons.ButtonProc = {
ENABLE IO.Error, RuntimeError.BoundsFault => {Message["Illegal parameter"]; CONTINUE};
firstTime: BOOLTRUE;
h: Handle ← NARROW[clientData];
WHILE firstTime OR h.repeatTest DO
firstTime ← FALSE;
IF NOT h.testInProgress THEN {
h: Handle ← NARROW[clientData];
done: BOOLEANFALSE;
IMSTester.stop ← FALSE;
h.stop ← dont;
IF h.inStream # NIL THEN IO.Close[h.inStream]; --just in case last test was aborted
Init[h];
DoTest[h ! InterruptSignal => CONTINUE];
Cleanup[h];
};
Process.Pause[Process.SecondsToTicks[pause]];
ENDLOOP;
};
AbortDie: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IMSTester.stop ← TRUE;
h.stop ← abortDie;
};
AbortWafer: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IMSTester.stop ← TRUE;
h.stop ← abortWafer;
};
AbortTest: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IMSTester.stop ← TRUE;
h.stop ← interrupt;
h.testInProgress ← FALSE;
};
CheckStop: PROC [h: Handle] = {
IMSTester.stop ← FALSE;
SELECT h.stop FROM
dont => NULL;
abortDie => {Message["Abort die"]; ERROR AbortDieSignal};
abortWafer => {Message["Abort wafer"]; ERROR AbortWaferSignal};
interrupt => {Message["Interrupt"]; ERROR InterruptSignal};
ENDCASE => ERROR;
};
EnableTester: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
selectedButton: Viewer ← NARROW[parent];
h.enableTester ← NOT h.enableTester;
Buttons.SetDisplayStyle[selectedButton, IF h.enableTester THEN $BlackOnGrey ELSE $BlackOnWhite];
};
EnableStepper: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
selectedButton: Viewer ← NARROW[parent];
h.enableStepper ← NOT h.enableStepper;
Buttons.SetDisplayStyle[selectedButton, IF h.enableStepper THEN $BlackOnGrey ELSE $BlackOnWhite];
};
GetParameters: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
ReallyGetParameters[h];
};
ReallyGetParameters: PROC [h: Handle] = {
targetGroup: ROPE ← ViewerTools.GetContents[h.group];
fg: IMSTester.ForceGroup ← NIL;
ag: IMSTester.AcquireGroup ← NIL;
FOR l: LIST OF ForceSubGroupsRec ← h.forceSubGroups, l.rest WHILE l#NIL DO
IF Rope.Equal[l.first.fullName, targetGroup] THEN {
fg ← l.first.subGroups.first;
ViewerTools.SetContents[h.delay, IO.PutR1[IO.int[fg.delay]]];
ViewerTools.SetContents[h.width, IO.PutR1[IO.int[fg.width]]];
ViewerTools.SetContents[h.hiDrive, IF fg.programable THEN IO.PutR1[IO.real[fg.hiDrive]] ELSE "TTL"];
ViewerTools.SetContents[h.loDrive, IF fg.programable THEN IO.PutR1[IO.real[fg.loDrive]] ELSE "TTL"];
EXIT;
};
REPEAT
FINISHED => {
ViewerTools.SetContents[h.delay, "?"];
ViewerTools.SetContents[h.width, "?"];
ViewerTools.SetContents[h.hiDrive, "?"];
ViewerTools.SetContents[h.loDrive, "?"];
};
ENDLOOP;
FOR l: LIST OF AcquireSubGroupsRec ← h.acquireSubGroups, l.rest WHILE l#NIL DO
IF Rope.Equal[l.first.fullName, targetGroup] THEN {
ag ← l.first.subGroups.first;
ViewerTools.SetContents[h.sample, IO.PutR1[IO.int[ag.sample]]];
ViewerTools.SetContents[h.threshold, IF ag.programable THEN IO.PutR1[IO.real[ag.threshold]] ELSE "TTL"];
EXIT;
};
REPEAT
FINISHED => {
ViewerTools.SetContents[h.sample, "?"];
ViewerTools.SetContents[h.threshold, "?"];
};
ENDLOOP;
IF fg=NIL AND ag=NIL THEN Message[IO.PutFR1["Group name \"%g\" not found.\n", IO.rope[targetGroup]]];
};
SetParameters: Buttons.ButtonProc = {
ENABLE IO.Error, RuntimeError.BoundsFault => {Message["Illegal parameter"]; CONTINUE};
h: Handle ← NARROW[clientData];
ReallySetParameters[h];
};
ReallySetParameters: PROC [h: Handle] = {
fg: IMSTester.ForceGroups ← NIL;
ag: IMSTester.AcquireGroups ← NIL;
targetGroup: ROPE ← ViewerTools.GetContents[h.group];
FOR l: LIST OF ForceSubGroupsRec ← h.forceSubGroups, l.rest WHILE l#NIL DO
IF Rope.Equal[l.first.fullName, targetGroup] THEN {
fg ← l.first.subGroups;
FOR f: IMSTester.ForceGroups ← fg, f.rest WHILE f#NIL DO
f.first.delay ← IO.GetInt[IO.RIS[ViewerTools.GetContents[h.delay]]];
f.first.width ← ((IO.GetInt[IO.RIS[ViewerTools.GetContents[h.width]]]+5)/10)*10;
IF f.first.programable THEN {
f.first.hiDrive ← IO.GetReal[IO.RIS[ViewerTools.GetContents[h.hiDrive]]];
f.first.loDrive ← IO.GetReal[IO.RIS[ViewerTools.GetContents[h.loDrive]]];
};
ENDLOOP;
};
ENDLOOP;
FOR l: LIST OF AcquireSubGroupsRec ← h.acquireSubGroups, l.rest WHILE l#NIL DO
IF Rope.Equal[l.first.fullName, targetGroup] THEN {
ag ← l.first.subGroups;
FOR a: IMSTester.AcquireGroups ← ag, a.rest WHILE a#NIL DO
a.first.sample ← IO.GetInt[IO.RIS[ViewerTools.GetContents[h.sample]]];
IF a.first.programable THEN a.first.threshold ← IO.GetReal[IO.RIS[ViewerTools.GetContents[h.threshold]]];
ENDLOOP;
};
ENDLOOP;
ReallyGetParameters[h];
IF fg#NIL OR ag#NIL THEN IMSTester.RedefineGroups[fg, ag]
ELSE Message[IO.PutFR1["Group name \"%g\" not found.\n", IO.rope[targetGroup]]];
};
Interrupt: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
h.stop ← interrupt;
};
Continue: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IMSTester.stop ← FALSE;
IF h.testInProgress AND (h.stop = interrupt) THEN {
DoTest[h ! InterruptSignal => CONTINUE];
Cleanup[h];
};
};
SingleCycle: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
selectedButton: Viewer ← NARROW[parent];
h.singleCycle ← NOT h.singleCycle;
Buttons.SetDisplayStyle[selectedButton, IF h.singleCycle THEN $BlackOnGrey ELSE $BlackOnWhite];
};
LoopTest: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
selectedButton: Viewer ← NARROW[parent];
h.loopTest ← NOT h.loopTest;
Buttons.SetDisplayStyle[selectedButton, IF h.loopTest THEN $BlackOnGrey ELSE $BlackOnWhite];
};
StopLoop: Buttons.ButtonProc = {
IMSTester.Stop[];
};
RepeatTest: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
selectedButton: Viewer ← NARROW[parent];
h.repeatTest ← NOT h.repeatTest;
Buttons.SetDisplayStyle[selectedButton, IF h.repeatTest THEN $BlackOnGrey ELSE $BlackOnWhite];
};
GetErrors: Buttons.ButtonProc = {
ENABLE IO.Error, Convert.Error, RuntimeError.BoundsFault => {Message["Illegal parameter"]; CONTINUE};
h: Handle ← NARROW[clientData];
errors: IMSTester.Errors;
IF h.enableTester THEN {
errors ← IMSTester.GetErrors[h.acquireGroups, h.buffer, maxErrors, Convert.IntFromRope[ViewerTools.GetContents[h.errorCycle]], h.cycle];
IO.PutChar[h.outStream, '\n];
FOR l: IMSTester.Errors ← errors, l.rest WHILE l#NIL DO
IO.PutF[h.outStream, "Cycle: %g, %g, Expected: %g\n", IO.int[l.first.cycle], IO.rope[GetName[h, l.first.pin.signalName].acquireName], IO.bool[l.first.expected]];
ViewerTools.SetContents[h.errorCycle, IO.PutR1[IO.int[l.first.cycle]]];
ENDLOOP;
};
};
Message: PROC [rope: ROPE] = {
MessageWindow.Append[rope, TRUE];
MessageWindow.Blink[];
MessageWindow.Append[rope, TRUE];
};
ForceDataToBuffer: PROC [h: Handle] = {
cycle: IMSTester.Cycle ← h.cycle;
FOR l: LIST OF MapRec ← h.forceMap, l.rest WHILE l#NIL DO
board: IMSTester.Board ← l.first.b;
pod: IMSTester.PodChannel ← l.first.p;
port: Ports.Port ← l.first.port;
index: NAT ← l.first.index;
inhibit: BOOLIF port.d=force THEN FALSE ELSE TRUE;
SELECT port.type FROM
l, ls => {
SELECT (IF port.type=l THEN port.l ELSE port.ls[index]) FROM
L => {
h.buffer[cycle][board][pod].forceData ← FALSE;
h.buffer[cycle][board][pod].inhibit ← FALSE;
};
H => {
h.buffer[cycle][board][pod].forceData ← TRUE;
h.buffer[cycle][board][pod].inhibit ← FALSE;
};
X => h.buffer[cycle][board][pod].inhibit ← TRUE;
ENDCASE => ERROR;
};
b, bs => {
h.buffer[cycle][board][pod].forceData ← IF port.type=b THEN port.b ELSE port.bs[index];
h.buffer[cycle][board][pod].inhibit ← inhibit;
};
c => {
bitMask: CARDINAL ← Basics.BITSHIFT[08000h, -port.fieldStart-index];
h.buffer[cycle][board][pod].forceData ← Basics.BITAND[port.c, bitMask]#0;
h.buffer[cycle][board][pod].inhibit ← inhibit;
};
lc => {
bitMask: LONG CARDINAL ← Basics.DoubleShift[[lc[080000000h]], -port.fieldStart-index].lc;
h.buffer[cycle][board][pod].forceData ← Basics.DoubleAnd[[lc[port.lc]], [lc[bitMask]]].lc#0;
h.buffer[cycle][board][pod].inhibit ← inhibit;
};
ENDCASE => ERROR;
CheckStop[h];
ENDLOOP;
};
CompareDataToBuffer: PROC [h: Handle] = {
cycle: IMSTester.Cycle ← h.cycle;
FOR l: LIST OF MapRec ← h.acquireMap, l.rest WHILE l#NIL DO
board: IMSTester.Board ← l.first.b;
pod: IMSTester.PodChannel ← l.first.p;
port: Ports.Port ← l.first.port;
index: NAT ← l.first.index;
mask: BOOLIF port.d=expect THEN FALSE ELSE TRUE;
SELECT port.type FROM
l, ls => {
SELECT (IF port.type=l THEN port.l ELSE port.ls[index]) FROM
L => {
h.buffer[cycle][board][pod].compareData ← FALSE;
h.buffer[cycle][board][pod].mask ← TRUE
};
H => {
h.buffer[cycle][board][pod].compareData ← TRUE;
h.buffer[cycle][board][pod].mask ← TRUE
};
X => h.buffer[cycle][board][pod].mask ← TRUE;
ENDCASE => ERROR;
};
b, bs => {
h.buffer[cycle][board][pod].compareData←IF port.type=b THEN port.b ELSE port.bs[index];
h.buffer[cycle][board][pod].mask ← mask;
};
c => {
bitMask: CARDINAL ← Basics.BITSHIFT[08000h, -port.fieldStart-index];
h.buffer[cycle][board][pod].compareData ← Basics.BITAND[port.c, bitMask]#0;
h.buffer[cycle][board][pod].mask ← mask;
};
lc => {
bitMask: LONG CARDINAL ← Basics.DoubleShift[[lc[080000000h]], -port.fieldStart-index].lc;
h.buffer[cycle][board][pod].compareData ← Basics.DoubleAnd[[lc[port.lc]], [lc[bitMask]]].lc#0;
h.buffer[cycle][board][pod].mask ← mask;
};
ENDCASE => ERROR;
CheckStop[h];
ENDLOOP;
};
BufferToIMS: PROC [h: Handle, cycles: IMSTester.Cycle] = {
halt: IMSTester.Cycle ← IF NOT h.loopTest THEN cycles-1 ELSE LAST[IMSTester.Cycle]; --i.e. never
jump: IMSTester.Jumps ← IF h.loopTest THEN LIST[[cycles-1, 0]] ELSE NIL;
IF cycles>0 THEN IMSTester.SetIMSMemory[forceGroups: h.forceGroups, acquireGroups: h.acquireGroups, buffer: h.buffer, cycles: cycles, halt: halt, jumps: jump];
IMSTester.Start[];
CheckStop[h];
};
MapPortToBuffer: PROC [h: Handle] = {
EachPair: PROC [wire: Core.Wire, port: Ports.Port] RETURNS [subElements: BOOLTRUE, quit: BOOLFALSE] --Ports.EachPortPairProc-- = {
podChannel: IMSTester.PodChannel ← (SELECT a.first.pod FROM A=>0, B=>8, AT=>16, BT=>17 ENDCASE=>ERROR) + a.first.channel;
IF CoreOps.IsFullWireName[h.cellType.public, wire, a.first.name] AND a.first.group#0 THEN {
IF port#NIL THEN rootPort ← port;
IF (g.first.directionality=force) OR (g.first.directionality=biDirectional) THEN {
FOR l: LIST OF MapRec ← h.forceMap, l.rest WHILE l#NIL DO
IF l.first.b=a.first.board AND l.first.p=podChannel THEN ERROR; --two force ports map to same buffer
ENDLOOP;
h.forceMap ← CONS[[a.first.board, podChannel, rootPort, IF port=NIL THEN count ELSE 0], h.forceMap];
};
IF (g.first.directionality=acquire) OR (g.first.directionality=biDirectional) THEN {
FOR l: LIST OF MapRec ← h.acquireMap, l.rest WHILE l#NIL DO
IF l.first.b=a.first.board AND l.first.p=podChannel THEN ERROR; --two acquire ports map to same buffer
ENDLOOP;
h.acquireMap ← CONS[[a.first.board, podChannel, rootPort, IF port=NIL THEN count ELSE 0], h.acquireMap];
};
RETURN[TRUE];
};
IF port#NIL AND port.type#composite THEN {count ← 0; rootPort ← port}
ELSE count ← count+1;
};
count: NAT ← 0;
a: LIST OF Assignments;
g: LIST OF Group;
rootPort: Ports.Port;
FOR g ← h.groups, g.rest WHILE g#NIL DO
FOR a ← h.assignments, a.rest WHILE a#NIL DO
IF g.first.number=a.first.group THEN [] ← Ports.VisitBinding[h.cellType.public, h.port, EachPair];
ENDLOOP;
ENDLOOP;
};
LimitNameLength: PROC [nameTab: SymTab.Ref, name: ROPE, length: NAT] RETURNS [newName: ROPE] = {
Looks for ropes in the form: "beforeRope, numericRope, afterRope". Truncates afterRope first and numericRope last in order to get rope to conform to length.
IF name = NIL THEN name ← "IMS";
IF Rope.Length[name] > length THEN {
c: CHAR;
beforeRope, numericRope, afterRope: ROPENIL;
beforeLength, numericLength, afterLength: NAT;
after: BOOLFALSE;
s: IO.STREAMIO.RIS[name];
WHILE NOT s.EndOf[] DO
SELECT (c ← s.GetChar[]) FROM
IN ['!..'$], IN ['&..'+], '-, '., '/, ':, '<, '>, '?, IN ['A..'~] => IF after THEN afterRope ← Rope.Concat[afterRope, Rope.FromChar[c]] ELSE beforeRope ← Rope.Concat[beforeRope, Rope.FromChar[c]]; --any printable char but SP, ', '= '% and '; are OK
'%, IN ['0..'9] => {
numericRope ← Rope.Concat[numericRope, Rope.FromChar[c]];
WHILE NOT s.EndOf[] AND (c ← s.PeekChar[]) >= '0 AND c <='9 DO
numericRope ← Rope.Cat[numericRope, Rope.FromChar[s.GetChar[]]];
ENDLOOP;
after ← TRUE;
};
ENDCASE => ERROR -- illegal character in name
ENDLOOP;
beforeLength ← Rope.Length[beforeRope];
numericLength ← Rope.Length[numericRope];
afterLength ← Rope.Length[afterRope];
IF numericLength < length THEN
IF (beforeLength + numericLength) < length THEN
IF (beforeLength + numericLength + afterLength) < length THEN newName ← name
ELSE newName ← Rope.Substr[name, 0, length]
ELSE newName ← Rope.Concat[Rope.Substr[beforeRope, 0, length-numericLength], numericRope]
ELSE newName ← Rope.Substr[numericRope, 0, length];
WHILE NOT SymTab.Insert[nameTab, newName, name] DO --if short name exists, add %## to it
number, index: INTEGER;
IF (index ← Rope.Find[newName, "%"]) # -1 THEN {
number ← (Rope.Fetch[newName, index+1]-'0)*10 + (Rope.Fetch[newName, index+2]-'0)+1;
newName ← Rope.Substr[newName, 0, index];
newName ← IO.PutFR["%g%%%02g", IO.rope[newName], IO.card[number]];
} ELSE {
newName ← Rope.Substr[newName, 0, length-3];
newName ← Rope.Concat[newName, "%00"];
};
ENDLOOP;
} ELSE newName ← name;
IF NOT SymTab.Insert[nameTab, name, newName] THEN ERROR; --name multiply defined
};
LimitGroupSize: PROC [h: Handle, groups: PodTimingGroups, boardToSlot: REF BoardToSlot] RETURNS [listOfGroups: LIST OF PodTimingGroups] = {
Splits up groups which are larger that the IMS maximum of 32 channels into subgroups. Also splits up groups which span programable drive level or acquire threshold modules so that such modules are referenced by unique group names (another IMS restriction).
newGroup, reversedGroup: PodTimingGroups;
reversedGroupList: LIST OF PodTimingGroups;
channels: NAT;
addToGroup: BOOL;
channels ← 0;
newGroup ← NIL;
reversedGroupList ← NIL;
FOR p: PodTimingGroups ← groups, p.rest WHILE p#NIL DO
FOR l: IMSTester.Pins ← p.first.pins, l.rest WHILE l#NIL DO
channels ← channels + 1;
ENDLOOP;
addToGroup ← channels <= 32 AND NOT boardToSlot[p.first.board].programable;
IF newGroup#NIL THEN addToGroup ← addToGroup AND NOT boardToSlot[newGroup.first.board].programable;
IF addToGroup THEN newGroup ← CONS[p.first, newGroup] ELSE {
re-reverse the elements on the list (for aesthetic reasons only)
reversedGroup ← NIL;
FOR p: PodTimingGroups ← newGroup, p.rest WHILE p#NIL DO
reversedGroup ← CONS[p.first, reversedGroup];
ENDLOOP;
listOfGroups ← CONS[reversedGroup, listOfGroups];
channels ← 0; newGroup ← LIST[p.first];
};
ENDLOOP;
add the leftovers to the list (in aesthetic order)
reversedGroup ← NIL;
IF newGroup#NIL THEN FOR p: PodTimingGroups ← newGroup, p.rest WHILE p#NIL DO
reversedGroup ← CONS[p.first, reversedGroup];
ENDLOOP;
listOfGroups ← CONS[reversedGroup, listOfGroups];
};
MakePodTimingGroup: PROC [h: Handle, g: Group, boardToSlot: REF BoardToSlot, nameTab: SymTab.Ref] RETURNS [podTimingGroups: IMSTester.PodTimingGroups] = {
FOR a: LIST OF Assignments ← h.assignments, a.rest WHILE a#NIL DO
IF a.first.group = g.number THEN {
FOR l: PodTimingGroups ← podTimingGroups, l.rest WHILE l#NIL DO
IF l.first.board = a.first.board AND l.first.podTiming = a.first.pod THEN {
FOR p: IMSTester.Pins ← l.first.pins, p.rest WHILE p#NIL DO
IF p.first.channel = a.first.channel THEN ERROR; --channel assigned twice
ENDLOOP;
l.first.pins ← CONS[NEW[IMSTester.PinRec ← [
channel: a.first.channel,
signalName: LimitNameLength[nameTab, a.first.name, signalNameLength],
packagePin: a.first.probeCardPin]], l.first.pins];
EXIT;
};
REPEAT
FINISHED =>
podTimingGroups ← CONS[NEW[IMSTester.PodTimingGroupRec ← [
slot: boardToSlot[a.first.board].slot,
board: a.first.board,
podTiming: a.first.pod,
pins: LIST[NEW[IMSTester.PinRec ← [
channel: a.first.channel,
signalName: LimitNameLength[nameTab, a.first.name, signalNameLength],
packagePin: a.first.probeCardPin]]]]], podTimingGroups];
ENDLOOP;
};
ENDLOOP;
};
MakeForceAcquireGroups: PROC [h: Handle] = {
listOfGroups: LIST OF PodTimingGroups;
count: NAT;
forceSubGroup: IMSTester.ForceGroup;
acquireSubGroup: IMSTester.AcquireGroup;
FOR g: LIST OF Group ← h.groups, g.rest WHILE g#NIL DO
IF g.first.directionality=force OR g.first.directionality=biDirectional THEN {
listOfGroups ← LimitGroupSize[h, MakePodTimingGroup[h, g.first, h.forceBoardToSlot, h.forceNamesTab], h.forceBoardToSlot];
h.forceSubGroups ← CONS[[g.first.name, NIL], h.forceSubGroups];
count ← 0;
FOR l: LIST OF PodTimingGroups ← listOfGroups, l.rest WHILE l#NIL DO
forceSubGroup ← NEW[IMSTester.ForceGroupRec ← [LimitNameLength[h.forceNamesTab, IO.PutFR["F%g%%%02g", IO.rope[g.first.name], IO.card[count]], groupNameLength], l.first, g.first.format, g.first.delay, g.first.width, g.first.programable, g.first.hiDrive, g.first.loDrive]];
h.forceGroups ← CONS[forceSubGroup, h.forceGroups];
h.forceSubGroups.first.subGroups ← CONS[forceSubGroup, h.forceSubGroups.first.subGroups];
count ← count+1;
ENDLOOP;
};
IF g.first.directionality=acquire OR g.first.directionality=biDirectional THEN {
listOfGroups ← LimitGroupSize[h, MakePodTimingGroup[h, g.first, h.acquireBoardToSlot, h.acquireNamesTab], h.acquireBoardToSlot];
h.acquireSubGroups ← CONS[[g.first.name, NIL], h.acquireSubGroups];
count ← 0;
FOR l: LIST OF PodTimingGroups ← listOfGroups, l.rest WHILE l#NIL DO
acquireSubGroup ← NEW[IMSTester.AcquireGroupRec ← [LimitNameLength[h.acquireNamesTab, IO.PutFR["A%g%%%02g", IO.rope[g.first.name], IO.card[count]], groupNameLength], l.first, g.first.sample, g.first.compare, g.first.programable, g.first.threshold]];
h.acquireGroups ← CONS[acquireSubGroup, h.acquireGroups];
h.acquireSubGroups.first.subGroups ← CONS[acquireSubGroup, h.acquireSubGroups.first.subGroups];
count ← count+1;
ENDLOOP;
};
ENDLOOP;
};
Init: PROC[h: Handle] = {
forceBoard, acquireBoard, programable: PACKED ARRAY IMSTester.SlotNumber OF BOOLEAN;
board: NAT;
filename: ROPE;
period: Period;
filename ← ViewerTools.GetContents[h.waferFile];
h.inStream ← FS.StreamOpen[filename !
FS.Error => {Message[IO.PutFR["Wafer file: %g not found", IO.rope[filename]]];
CONTINUE}];
IF h.port=NIL THEN h.port ← Ports.CreatePort[h.cellType.public, TRUE];
IMSTester.checkSyntax ← NOT h.enableTester;
IF h.forceNamesTab=NIL THEN { --only do it the first time "Start Test" is pressed
h.forceNamesTab ← SymTab.Create[];
h.acquireNamesTab ← SymTab.Create[];
[forceBoard, acquireBoard, programable] ← IMSTester.Initialize[];
board ← 0;
FOR slot: IMSTester.SlotNumber IN IMSTester.SlotNumber DO
IF forceBoard[slot] THEN {
h.forceBoardToSlot[board] ← [slot, programable[slot]];
board ← board+1;
};
ENDLOOP;
board ← 0;
FOR slot: IMSTester.SlotNumber IN IMSTester.SlotNumber DO
IF acquireBoard[slot] THEN {
h.acquireBoardToSlot[board] ← [slot, programable[slot]];
board ← board+1;
};
ENDLOOP;
MakeForceAcquireGroups[h];
MapPortToBuffer[h];
IMSTester.DefineGroups[h.forceGroups, h.acquireGroups];
h.buffer ← NEW[IMSTester.BufferRec[1]];
h.buffer[0] ← NEW[IMSTester.CycleDataRec];
};
h.cycle ← 0;
ViewerTools.SetContents[h.errorCycle, "0"];
IF h.enableStepper THEN EGlas.Init[];
period ← ((IO.GetInt[IO.RIS[ViewerTools.GetContents[h.period]]]+5)/10)*10;
ViewerTools.SetContents[h.period, IO.PutR1[IO.int[period]]];
IMSTester.SetCyclePeriod[period];
};
Cleanup: PROC [h: Handle] = {
IF h.enableStepper THEN EGlas.LampOn[];
IF NOT h.loopTest THEN IMSTester.Stop[];
};
GetName: PROC [h: Handle, key: Rope.ROPE] RETURNS [forceName, acquireName: Rope.ROPE] = {
val: SymTab.Val;
found: BOOL;
[found, val] ← SymTab.Fetch[h.forceNamesTab, key];
forceName ← IF found THEN NARROW[val, Rope.ROPE] ELSE NIL;
[found, val] ← SymTab.Fetch[h.acquireNamesTab, key];
acquireName ← IF found THEN NARROW[val, Rope.ROPE] ELSE NIL;
};
LookUpName: Commander.CommandProc = {
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd
! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
key, forceName, acquireName: Rope.ROPE;
FOR i: NAT IN [1..argv.argc) DO
key ← argv[i];
IF Rope.Length[key] = 0 THEN LOOP ELSE EXIT;
ENDLOOP;
[forceName, acquireName] ← GetName[NARROW[cmd.procData.clientData], key];
IF forceName#NIL THEN IO.PutF[cmd.out, "Force name: %g\n", IO.rope[forceName]];
IF acquireName#NIL THEN IO.PutF[cmd.out, "Acquire name: %g\n", IO.rope[forceName]];
IF forceName=NIL AND acquireName=NIL THEN IO.PutF[cmd.out, "%g not found\n", IO.rope[key]];
EXITS
failed => NULL;
};
END.