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: BOOL ← FALSE,
enableTester: BOOL ← FALSE,
enableSimulation: BOOL ← FALSE,
singleCycle: BOOL ← FALSE,
loopTest: BOOL ← FALSE,
repeatTest: BOOL ← FALSE,
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: BOOL ← FALSE, -- button monitor
inStream: IO.STREAM ← NIL,
outStream: IO.STREAM ← NIL,
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.
ROPE ←
NIL, 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:
BOOL ←
TRUE] = {
[] ← 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:
ROPE ←
NIL]
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:
BOOLEAN ←
FALSE] = {
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:
BOOLEAN ←
FALSE] = {
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:
BOOLEAN ←
FALSE] = {
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: BOOL ← TRUE;
h: Handle ← NARROW[clientData];
WHILE firstTime
OR h.repeatTest
DO
firstTime ← FALSE;
IF
NOT h.testInProgress
THEN {
h: Handle ← NARROW[clientData];
done: BOOLEAN ← FALSE;
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: BOOL ← IF 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: BOOL ← IF 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:
BOOL ←
TRUE, quit:
BOOL ←
FALSE]
--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: ROPE ← NIL;
beforeLength, numericLength, afterLength: NAT;
after: BOOL ← FALSE;
s: IO.STREAM ← IO.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]];
};
END.