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 February 12, 1987 9:06:04 am PST
DIRECTORY Basics, Buttons, ChoiceButtons, Commander, CommandTool, Containers, Convert, Core, CoreOps, --EGlas,--
FS, HashTable, ICTest,
IO, IMSTester, MessageWindow, Ports, Rope, Rules, RuntimeError, SymTab, TypeScript, ViewerClasses, ViewerIO, ViewerOps, ViewerTools;
ICTestImpl: CEDAR PROGRAM
IMPORTS Basics, Buttons, ChoiceButtons, Commander, CommandTool, Containers, Convert, CoreOps, --EGlas,-- FS, HashTable, IO, IMSTester, MessageWindow, Ports, Rope, Rules, RuntimeError, SymTab, TypeScript, ViewerIO, ViewerOps, ViewerTools
EXPORTS ICTest
= BEGIN
OPEN ICTest;
maxErrors: NAT ← 40;
testNamesTable: HashTable.Table ← HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
Button: TYPE = Buttons.Button;
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;
Cycle: TYPE = IMSTester.Cycle;
Jumps: TYPE = IMSTester.Jumps;
AbortDieSignal: PUBLIC ERROR = CODE;
AbortWaferSignal: PUBLIC ERROR = CODE;
InterruptSignal: PUBLIC ERROR = CODE;
Test: TYPE = REF TestRec;
TestRec: TYPE = RECORD[proc: TestProc, start: Cycle, length: Cycle];
Stop: TYPE = {dont, abortDie, abortWafer, interrupt};
DiePosition: TYPE = RECORD [x,y: NAT];
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];
periodChangeProc: ROPE = "periodChangeProc";
Handle: TYPE = REF ICTestRec;
ICTestRec:
PUBLIC
TYPE =
RECORD [
-- Standard button state --
waferFile: Viewer ← NIL,
run: Viewer ← NIL,
wafer: Viewer ← NIL,
die: Viewer ← NIL,
memCycle: Viewer ← NIL,
startTest: Button ← 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,
-- Test buttons state--
testButtonContainer: Viewer ← NIL,
testButtonList: LIST OF Button ← NIL,
testProcsTable: HashTable.Table ← NIL,
currentTestProc: ROPE ← NIL,
-- Typescript --
typeScriptContainer: Viewer ← NIL,
outStream: IO.STREAM ← NIL,
lastPeriod: Period ← 50,
wDir: ROPE ← NIL,
firstFreeCycle: Cycle ← 0,
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,
port: Ports.Port ← NIL,
cellType: Core.CellType ← NIL,
clockAName: ROPE ← NIL,
clockBName: ROPE ← NIL,
clockAPort: Ports.Port ← NIL,
clockBPort: Ports.Port ← NIL,
forceGroups: IMSTester.ForceGroups ← NIL,
acquireGroups: IMSTester.AcquireGroups ← NIL,
forceMap: LIST OF MapRec ← NIL,
acquireMap: LIST OF MapRec ← NIL,
cycle: Cycle ← 0,
buffer: IMSTester.Buffer ← NIL,
nullCycleData: IMSTester.CycleData ← NIL,
forceNamesTab: SymTab.Ref ← NIL,
acquireNamesTab: SymTab.Ref ← NIL,
groups: Groups ← NIL,
assignments: 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
pointsPerInch: 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..5);
ColumnStart: ARRAY Column OF NAT = [col1, col2, col3, col4, col5];
MakeStandardViewer:
PUBLIC
PROC [testName:
ROPE, cellType: Core.CellType, clockAName:
ROPE, clockBName:
ROPE ←
NIL, groups: Groups, assignments: Assignments, period: Period] = {
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: standardButtons], clientData: h, proc: proc, fork: fork];
};
Prompt:
PROC [name:
ROPE, col:
NAT, contents:
ROPE ←
NIL]
RETURNS [Viewer] = {
RETURN[ChoiceButtons.BuildTextPrompt[standardButtons, col, height, name, contents,
NIL, 1*pointsPerHalfInch].textViewer];
};
h: Handle ← NEW[ICTestRec];
rule: Rules.Rule;
typeScript: Viewer;
height: CARDINAL ← 0;
viewer: Viewer ← Containers.Create[[name: Rope.Concat["IC Test Tool - ", testName], scrollable: FALSE]];
***Standard Buttons***
standardButtons: Viewer ← Containers.Create[[scrollable: FALSE, parent: viewer, border: FALSE]];
Containers.ChildXBound[viewer, standardButtons];
Containers.ChildYBound[viewer, standardButtons];
h.wDir ← CommandTool.CurrentWorkingDirectory[];
h.waferFile ← ChoiceButtons.BuildTextPrompt[standardButtons, col1, height, "Wafer File:", "SingleDie.dat", NIL, 7*pointsPerInch].textViewer;
height ← height + entryHeight + entryVSpace;
h.run ← Prompt["Run:", col1];
h.startTest ← Buttons.Create[info: [name: "Start Test", wx: col2, wy: height, wh: entryHeight,
parent: standardButtons], clientData: h, proc: StartTest, fork: TRUE];
Button["Abort Die", col3, AbortDie, FALSE];
Button["Single Cycle", col4, SingleCycle];
h.period ← Prompt["Period (nS):", col5, IO.PutR1[IO.int[period]]];
h.lastPeriod ← period;
height ← height + entryHeight + entryVSpace;
h.wafer ← Prompt["Wafer:", col1];
Button["Enable Tester", col2, EnableTester];
Button["Abort Wafer", col3, AbortWafer, FALSE];
Button["Loop Test", col4, LoopTest];
Button["Dump Memory", col5, DumpMemory];
height ← height + entryHeight + entryVSpace;
h.die ← Prompt["Die:", col1];
Button["Get Memory", col2, GetMemory];
Button["Abort Test", col3, AbortTest, FALSE];
Button["Stop!", col4, StopLoop];
Button["Dump Errors", col5, DumpErrors];
height ← height + entryHeight + entryVSpace;
h.memCycle ← Prompt["Mem Cycle:", col1, "0"];
Button["Get Errors", col2, GetErrors];
Button["Enable Stepper", col3, EnableStepper];
Button["Interrupt", col3, Interrupt, FALSE];
Button["Continue", col3, Continue];
height ← height + entryHeight + entryVSpace/2+1;
rule ← Rules.Create[[parent: standardButtons, wy: height, wh: 2]];
Containers.ChildXBound[standardButtons, rule];
height ← height + entryVSpace;
Button["Get Parameters", col1+2, 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[standardButtons, col1, height, "Group:", "<Group name>", NIL, 2*pointsPerInch].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: standardButtons, wy: height, wh: 2]];
Containers.ChildXBound[standardButtons, rule];
height ← height + 2;
***Test Buttons***
h.testButtonContainer ← Containers.Create[[wy: height, wh: entryVSpace, scrollable: FALSE, parent: viewer, border: FALSE]];
Containers.ChildXBound[viewer, h.testButtonContainer];
height ← height + entryVSpace;
***TypeScript***
h.typeScriptContainer ← Containers.Create[[wy: height, scrollable: FALSE, parent: viewer, border: FALSE]];
Containers.ChildXBound[viewer, h.typeScriptContainer];
Containers.ChildYBound[viewer, h.typeScriptContainer];
rule ← Rules.Create[[parent: h.typeScriptContainer, wy: 0, wh: 2]];
Containers.ChildXBound[h.typeScriptContainer, rule];
typeScript ← ViewerOps.CreateViewer[flavor: $TypeScript, info:[parent: h.typeScriptContainer, wy: 2, ww: 7*pointsPerInch, wh: 6*pointsPerInch, scrollable: TRUE, border: FALSE]];
Containers.ChildXBound[h.typeScriptContainer, typeScript];
Containers.ChildYBound[h.typeScriptContainer, typeScript];
[ , h.outStream] ← ViewerIO.CreateViewerStreams[name: "ICTestTS", viewer: typeScript];
TypeScript.ChangeLooks[typeScript, 'f];
Commander.Register[key: "///Commands/Name", proc: LookUpName,
doc: "Map an IMS signal name to/from the shortened version", clientData: h];
h.testProcsTable ← HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
[] ← HashTable.Store[table: testNamesTable, key: testName, value: h];
h.cellType ← cellType;
h.groups ← groups;
h.assignments ← assignments;
h.clockAName ← clockAName;
h.clockBName ← clockBName;
ViewerOps.OpenIcon[icon: viewer, bottom: FALSE];
};
RegisterTestProc:
PUBLIC
PROC [testName:
ROPE, procName:
ROPE, proc: TestProc, autoLoad:
BOOL ←
FALSE] = {
h: Handle ← NIL;
ref: REF ← NIL;
found: BOOL ← FALSE;
row, column: CARDINAL;
count: CARDINAL ← 0;
button: Button ← NIL;
[found, ref] ← HashTable.Fetch[testNamesTable, testName];
IF NOT found THEN ERROR; --couldn't find the tester handle, probably bad testName
h ← NARROW[ref];
IF
NOT HashTable.Fetch[h.testProcsTable, procName].found
THEN {
--add a new button
FOR l: LIST OF Button ← h.testButtonList, l.rest WHILE l#NIL DO count ← count+1 ENDLOOP;
row ← count/(LAST[Column]+1);
column ← count MOD (LAST[Column]+1);
IF column=
FIRST[Column]
THEN {
--make a new row
ViewerOps.MoveViewer[h.testButtonContainer, h.testButtonContainer.wx, h.testButtonContainer.wy, h.testButtonContainer.ww, h.testButtonContainer.wh+entryHeight+entryVSpace];
ViewerOps.MoveViewer[h.typeScriptContainer, h.typeScriptContainer.wx, h.typeScriptContainer.wy+entryHeight+entryVSpace, h.typeScriptContainer.ww, h.typeScriptContainer.wh];
};
button ← Buttons.Create[info: [name: procName,
wx: ColumnStart[column]+2, wy: row*(entryHeight+entryVSpace)+entryVSpace,
ww: 0, wh: entryHeight,
parent: h.testButtonContainer, border: TRUE], clientData: h, proc: TestButtonProc];
h.testButtonList ← CONS[button, h.testButtonList];
};
[] ← HashTable.Store[table: h.testProcsTable, key: procName, value: NEW[TestRec ← [proc, 0, 0]]];
IF autoLoad
AND NOT h.testInProgress
THEN {
IF h.loopTest
THEN Message["Can't auto load. LoopTest is
TRUE"]
ELSE {
oldTest: ROPE ← h.currentTestProc;
h.currentTestProc ← procName;
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];
h.currentTestProc ← oldTest;
};
};
};
RegisterPeriodChangeProc:
PUBLIC
PROC [testName:
ROPE, proc: PeriodChangeProc] = {
h: Handle ← NIL;
ref: REF ← NIL;
found: BOOL ← FALSE;
[found, ref] ← HashTable.Fetch[testNamesTable, testName];
IF NOT found THEN ERROR; --couldn't find the tester handle, probably bad testName
h ← NARROW[ref];
[] ← HashTable.Store[table: h.testProcsTable, key: periodChangeProc, value: NEW[PeriodChangeProc ← proc]];
};
TestButtonProc: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
selectedButton: ViewerClasses.Viewer ← NARROW[parent];
FOR l:
LIST
OF Button ← h.testButtonList, l.rest
WHILE l#
NIL
DO
Buttons.SetDisplayStyle[l.first, IF l.first = selectedButton THEN $WhiteOnBlack ELSE $BlackOnWhite];
ENDLOOP;
h.currentTestProc ← selectedButton.name;
};
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, "\n%g: Run %g, ", IO.rope[h.currentTestProc], IO.rope[ViewerTools.GetContents[h.run]]];
};
IF done
THEN {
IO.Close[h.inStream];
IF NOT h.loopTest THEN 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, "Wafer %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 [memory:
BOOL ←
TRUE] = {
CheckStop[h];
IF (h.clockAPort=
NIL
AND h.clockBPort=
NIL)
OR (h.clockAPort#
NIL
AND h.clockAPort.b)
OR (h.clockBPort#
NIL
AND h.clockBPort.b)
THEN {
ForceDataToBuffer[h];
CompareDataToBuffer[h];
IF h.singleCycle
THEN {
BufferToIMS[h: h, cycles: 1, start: 0, halt: 0];
IMSTester.Start[];
} ELSE {
h.cycle ← h.cycle+1;
IF h.cycle = h.buffer.cycle THEN NewBuffer[h];
};
};
};
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[];
};
Ports.RenewPort[h.cellType, h.port, TRUE];
ViewerTools.SetContents[h.memCycle, IO.PutR1[IO.int[test.start]]];
IF h.singleCycle
THEN test.proc[h.cellType, h.port, Eval]
ELSE {
IF test.start=0
THEN {
test.proc[h.cellType, h.port, Eval];
IF h.cycle=0
THEN {
Message["Error: test has zero cycles (maybe you never set clock TRUE?)"];
RETURN[];
};
IF h.firstFreeCycle+h.cycle >
LAST[Cycle]
THEN {
Message["Out of vector memory space"];
RETURN[];
};
BufferToIMS[h: h, cycles: h.cycle, start: h.firstFreeCycle];
test.start ← h.firstFreeCycle;
test.length ← h.cycle;
h.firstFreeCycle ← h.firstFreeCycle+h.cycle+1; --reserve one word for halt inst.
};
IF h.loopTest
THEN Jump[h, test.start+test.length, test.start]
ELSE
Halt[h, test.start+test.length];
Dispatch[h, test.start];
IF h.enableStepper THEN EGlas.LampOn[];
IF
NOT h.loopTest
THEN {
count ← IMSTester.ErrorCount[];
IO.PutF[h.outStream, IF count#0 THEN "Fail " ELSE "Pass; "];
IF count#0 THEN IO.PutF[h.outStream, "%g errors; ", IO.card[count]];
};
};
};
};
test: Test ← NARROW[HashTable.Fetch[table: h.testProcsTable, key: h.currentTestProc].value];
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;
IO.PutF[h.outStream, "(Start: %g, Stop: %g) Done\n", IO.int[test.start], IO.int[test.start+test.length-1]];
IF NOT h.loopTest THEN h.testInProgress ← FALSE;
};
Jump:
PROC [h: Handle, source: Cycle, dest: Cycle] ~ {
tempCycleData: IMSTester.CycleData ← h.buffer[0];
h.buffer[0] ← h.nullCycleData;
BufferToIMS[h: h, cycles: 1, start: source, jumps: LIST[[source, dest]]];
h.buffer[0] ← tempCycleData;
};
Halt:
PROC [h: Handle, cycle: Cycle] ~ {
tempCycleData: IMSTester.CycleData ← h.buffer[0];
h.buffer[0] ← h.nullCycleData;
BufferToIMS[h: h, cycles: 1, start: cycle, halt: cycle];
h.buffer[0] ← tempCycleData;
};
Dispatch:
PROC [h: Handle, start: Cycle] ~ {
Jump[h, 0, start];
IMSTester.Start[];
};
BufferToIMS:
PROC [h: Handle, cycles, start, halt: Cycle←
LAST[Cycle], jumps: Jumps←
NIL] = {
IMSTester.SetIMSMemory[h.forceGroups, h.acquireGroups, h.buffer, cycles, start, halt, jumps];
CheckStop[h];
};
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;
};
StartTest: Buttons.ButtonProc = {
ENABLE IO.Error, RuntimeError.BoundsFault => {Message["Illegal parameter"]; CONTINUE};
h: Handle ← NARROW[clientData];
selectedButton: Viewer ← NARROW[parent];
IF h.testInProgress THEN Message["Test in progress"];
IF h.currentTestProc=NIL THEN Message["Please select a test procedure"];
IF
NOT h.testInProgress
AND h.currentTestProc#
NIL
THEN {
IF h.loopTest THEN Buttons.SetDisplayStyle[selectedButton, $BlackOnGrey];
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];
};
};
AbortDie: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IMSTester.Abort[];
h.stop ← abortDie;
};
AbortWafer: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IMSTester.Abort[];
h.stop ← abortWafer;
};
AbortTest: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IMSTester.Abort[];
h.stop ← interrupt;
h.testInProgress ← FALSE;
};
CheckStop:
PROC [h: Handle] = {
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 $WhiteOnBlack 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 $WhiteOnBlack ELSE $BlackOnWhite];
};
GetParameters: Buttons.ButtonProc = {
ENABLE IO.Error, RuntimeError.BoundsFault => {Message["Illegal parameter"]; CONTINUE};
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};
ReallySetParameters:
PROC [h: Handle] = {
delay: Delay ← 0;
width: Width ← 20;
sample: Sample ← 0;
hiDrive: REAL ← 2.4;
loDrive: REAL ← 0.4;
threshold: REAL ← 1.4;
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 {
delay ← IO.GetInt[IO.RIS[ViewerTools.GetContents[h.delay]]];
width ← ((IO.GetInt[IO.RIS[ViewerTools.GetContents[h.width]]]+5)/10)*10;
IF l.first.subGroups.first.programable
THEN {
hiDrive ← IO.GetReal[IO.RIS[ViewerTools.GetContents[h.hiDrive]]];
loDrive ← IO.GetReal[IO.RIS[ViewerTools.GetContents[h.loDrive]]];
};
};
ENDLOOP;
FOR l:
LIST
OF AcquireSubGroupsRec ← h.acquireSubGroups, l.rest
WHILE l#
NIL
DO
IF Rope.Equal[l.first.fullName, targetGroup]
THEN {
sample ← IO.GetInt[IO.RIS[ViewerTools.GetContents[h.sample]]];
IF l.first.subGroups.first.programable THEN threshold ← IO.GetReal[IO.RIS[ViewerTools.GetContents[h.threshold]]];
};
ENDLOOP;
DoSetParameters[h, targetGroup, delay, width, sample, hiDrive, loDrive, threshold];
};
h: Handle ← NARROW[clientData];
ReallySetParameters[h];
};
DoSetParameters:
PROC [h: Handle, targetGroup:
ROPE, delay: Delay, width: Width, sample: Sample, hiDrive, loDrive, threshold:
REAL] = {
fg: IMSTester.ForceGroups ← NIL;
ag: IMSTester.AcquireGroups ← 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;
FOR f: IMSTester.ForceGroups ← fg, f.rest
WHILE f#
NIL
DO
f.first.delay ← delay;
f.first.width ← ((width+5)/10)*10;
IF f.first.programable
THEN {
f.first.hiDrive ← hiDrive;
f.first.loDrive ← 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 ← sample;
IF a.first.programable THEN a.first.threshold ← threshold;
ENDLOOP;
};
ENDLOOP;
ViewerTools.SetContents[h.group, targetGroup];
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];
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 $WhiteOnBlack 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 $WhiteOnBlack ELSE $BlackOnWhite];
};
StopLoop: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IF h.testInProgress AND h.loopTest THEN Buttons.SetDisplayStyle[h.startTest, $BlackOnWhite];
IMSTester.Stop[];
h.testInProgress ← FALSE;
};
GetErrors: Buttons.ButtonProc = {
ENABLE IO.Error, Convert.Error, RuntimeError.BoundsFault => {Message["Illegal ""Mem Cycle"" parameter"]; CONTINUE};
ReallyGetErrors:
PROC [h: Handle] = {
errors: IMSTester.Errors;
test: Test ← NARROW[HashTable.Fetch[table: h.testProcsTable, key: h.currentTestProc].value];
start: Cycle ← Convert.IntFromRope[ViewerTools.GetContents[h.memCycle]];
valid: BOOL ← FALSE; errorCycle: Cycle;
IF start<test.start THEN start ← test.start;
IF start>test.start+test.length-1 THEN start ← test.start+test.length-1;
SELECT mouseButton
FROM
red => {
[valid, errorCycle] ← IMSTester.GetRawErrors[h.outStream, h.forceGroups, h.acquireGroups, test.start+test.length-1, start, test.start-1];
};
yellow => ViewerTools.SetContents[h.memCycle, IO.PutR1[IO.int[start-1]]];
blue => {
errors ← IMSTester.GetErrors[h.acquireGroups, h.buffer, test.start, test.start+test.length-1, start, test.start-1];
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]];
ENDLOOP;
IF errors#
NIL
THEN {
valid ← TRUE;
errorCycle ← errors.first.cycle;
};
};
ENDCASE => ERROR;
IF valid THEN ViewerTools.SetContents[h.memCycle, IO.PutR1[IO.int[errorCycle+1]]];
};
h: Handle ← NARROW[clientData];
ReallyGetErrors[h];
};
GetMemory: Buttons.ButtonProc = {
ENABLE IO.Error, Convert.Error, RuntimeError.BoundsFault => {Message["Illegal ""Mem Cycle"" parameter"]; CONTINUE};
ReallyGetMemory:
PROC [h: Handle] = {
test: Test ← NARROW[HashTable.Fetch[table: h.testProcsTable, key: h.currentTestProc].value];
cycle: Cycle ← Convert.IntFromRope[ViewerTools.GetContents[h.memCycle]];
IF mouseButton#yellow
THEN {
IF cycle<test.start THEN cycle ← test.start;
IF cycle>test.start+test.length-1 THEN cycle ← test.start+test.length-1;
};
IF h.enableTester THEN IMSTester.GetMemory[h.outStream, cycle, h.forceGroups, h.acquireGroups, test.start-1];
cycle ← cycle+(IF mouseButton#blue THEN 1 ELSE -1);
ViewerTools.SetContents[h.memCycle, IO.PutR1[IO.int[cycle]]];
};
h: Handle ← NARROW[clientData];
ReallyGetMemory[h];
};
DumpErrors: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
test: Test ← NARROW[HashTable.Fetch[table: h.testProcsTable, key: h.currentTestProc].value];
valid: BOOL; errorCycle: Cycle;
s: IO.STREAM ← FS.StreamOpen[fileName: Rope.Cat[h.currentTestProc, ".err"], accessOptions: $create, wDir: h.wDir];
errorCycle ← test.start;
DO
[valid, errorCycle] ← IMSTester.GetRawErrors[s, h.forceGroups, h.acquireGroups, test.start+test.length-1, errorCycle, test.start-1];
IF NOT valid THEN EXIT;
errorCycle ← errorCycle+1;
ENDLOOP;
IO.Close[s];
};
DumpMemory: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
test: Test ← NARROW[HashTable.Fetch[table: h.testProcsTable, key: h.currentTestProc].value];
s: IO.STREAM ← FS.StreamOpen[fileName: Rope.Cat[h.currentTestProc, ".mem"], accessOptions: $create, wDir: h.wDir];
FOR cycle: Cycle
IN [test.start..test.start+test.length)
DO
IMSTester.GetMemory[s, cycle, h.forceGroups, h.acquireGroups, test.start-1];
ENDLOOP;
IO.Close[s];
};
Message:
PROC [rope:
ROPE] = {
MessageWindow.Append[rope, TRUE];
MessageWindow.Blink[];
MessageWindow.Append[rope, TRUE];
};
ForceDataToBuffer:
PROC [h: Handle] = {
cycle: 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.levelType
FROM
l, ls => {
SELECT (
IF port.levelType=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.levelType=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: 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.levelType
FROM
l, ls => {
SELECT (
IF port.levelType=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.levelType=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;
};
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.levelType#composite
THEN {count ← 0; rootPort ← port}
ELSE count ← count+1;
};
count: NAT ← 0;
a: Assignments;
g: Groups;
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
(
IF newGroup#
NIL
THEN
boardToSlot[newGroup.first.board].programable=boardToSlot[p.first.board].programable
ELSE TRUE);
IF addToGroup THEN newGroup ← CONS[p.first, newGroup];
IF
NOT addToGroup
THEN {
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];
FOR l: IMSTester.Pins ← p.first.pins, l.rest
WHILE l#
NIL
DO
channels ← channels + 1;
ENDLOOP;
};
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: 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: Groups ← 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;
};
NotifyPeriodChange:
PROC [h: Handle, period: Period] ~ {
proc: ICTest.PeriodChangeProc ← NARROW[HashTable.Fetch[table: h.testProcsTable, key: periodChangeProc].value, REF ICTest.PeriodChangeProc]^;
IMSTester.SetCyclePeriod[period];
FOR g: Groups ← proc[period], g.rest
WHILE g#
NIL
DO
first: Group ← g.first;
DoSetParameters[h, first.name, first.delay, first.width, first.sample, first.hiDrive, first.loDrive, first.threshold];
ENDLOOP;
};
Init:
PROC[h: Handle] = {
filename: ROPE;
period: Period;
filename ← ViewerTools.GetContents[h.waferFile];
h.inStream ←
FS.StreamOpen[fileName: filename, wDir: h.wDir !
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, TRUE];
IF h.clockAName#NIL THEN h.clockAPort ← h.port[Ports.PortIndex[h.cellType.public, h.clockAName]];
IF h.clockBName#NIL THEN h.clockBPort ← h.port[Ports.PortIndex[h.cellType.public, h.clockBName]];
IF h.clockAPort=NIL AND h.clockBPort=NIL THEN ERROR; --can't find clocks
IMSTester.checkSyntax ← NOT h.enableTester;
period ← ((IO.GetInt[IO.RIS[ViewerTools.GetContents[h.period]]]+5)/10)*10;
ViewerTools.SetContents[h.period, IO.PutR1[IO.int[period]]];
IF h.forceNamesTab=NIL THEN FirstStart[h, period]; --first time Start Test is pressed
IF h.lastPeriod#period THEN NotifyPeriodChange[h, period];
h.lastPeriod ← period;
h.cycle ← 0;
IF h.enableStepper THEN EGlas.Init[];
};
FirstStart:
PROC [h: Handle, period: Period] ~ {
forceBoard, acquireBoard, programable: PACKED ARRAY IMSTester.SlotNumber OF BOOLEAN;
board: NAT;
h.forceNamesTab ← SymTab.Create[];
h.acquireNamesTab ← SymTab.Create[];
[forceBoard, acquireBoard, programable] ← IMSTester.Initialize[h.wDir];
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.SetCyclePeriod[period]; --have to do this BEFORE defining groups
IMSTester.DefineGroups[h.forceGroups, h.acquireGroups];
h.buffer ← NEW[IMSTester.BufferRec[1]];
h.buffer[0] ← NEW[IMSTester.CycleDataRec];
h.firstFreeCycle ← 1; -- reserve location zero for dispatches and singlecycle execution
h.nullCycleData ← NEW[IMSTester.CycleDataRec];
FOR b: Board
IN Board
DO
FOR p: PodChannel
IN PodChannel
DO
h.nullCycleData[b][p].forceData ← FALSE;
h.nullCycleData[b][p].inhibit ← TRUE;
h.nullCycleData[b][p].compareData ← FALSE;
h.nullCycleData[b][p].mask ← TRUE;
ENDLOOP;
ENDLOOP;
};
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.