ICTestNewImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Created by: Gasbarro December 3, 1985 6:15:34 pm PST
Last Edited by: Gasbarro December 19, 1985 4:47:13 pm PST
DIRECTORY Basics, Buttons, ChoiceButtons, Containers, Core, CoreOps, CoreProperties, EGlas, FS, ICTestNew, IO, IMSTester, List, MessageWindow, Ports, RefTab, Rope, --Rosemary,-- Rules, ViewerClasses, ViewerIO, ViewerOps, ViewerTools;
ICTestNewImpl: CEDAR PROGRAM
IMPORTS Basics, Buttons, ChoiceButtons, Containers, CoreOps, CoreProperties, EGlas, FS, IO, IMSTester, List, MessageWindow, Ports, RefTab, Rope, --Rosemary,-- Rules, ViewerIO, ViewerOps, ViewerTools
EXPORTS ICTestNew
= BEGIN
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
NSec: TYPE = IMSTester.NSec;
FormatType: TYPE = IMSTester.FormatType;
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: ICTestNew.TestProc];
Handle: TYPE = REF ICTestRec;
ICTestRec: PUBLIC TYPE = RECORD [
currentDie: DiePosition, -- die to return to upon "Continue" button
enableStepper: BOOLFALSE, -- internal, enables sending commands to ElectroGlas
enableTester: BOOLFALSE, -- enables sending commands to Tester
enableSimulation: BOOLFALSE, -- enables Rosemary simulation
waferFile: Viewer ← NIL, -- handle for waferFile text viewer
run: Viewer ← NIL, -- handle for run text viewer
wafer: Viewer ← NIL, -- handle for wafer text viewer
die: Viewer ← NIL, -- handle for die text viewer
cutSet: Viewer ← NIL, -- handle for cutSet text viewer
stop: Stop ← dont, -- internal, state of control buttons
backupToken: IO.STREAM, -- internal, file stream parsing
testInProgress: BOOLFALSE, -- internal, button monitor
testButtonList: LIST OF Buttons.Button, -- internal, list of registered test buttons
inStream: IO.STREAMNIL, -- wafer file input stream
currentTestProc: ICTestNew.TestProc,
port: Ports.Port,
cellType: Core.CellType,
forceGroups: IMSTester.ForceGroups,
acquireGroups: IMSTester.AcquireGroups,
buffer: IMSTester.Buffer,
forceTab: RefTab.Ref,
acquireTab: RefTab.Ref,
cycle: IMSTester.Cycle,
viewerout: IO.STREAMNIL --*** This should disappear***
]; 
IMSGroup: ATOM = $IMSGroup;
GroupData: TYPE = REF GroupDataRec;
GroupDataRec: TYPE = RECORD[groupName: ROPE , format: FormatType, delay: NSec, width: NSec, hiDrive: REAL, loDrive: REAL, sample: NSec, threshold: REAL];
IMSChannel: ATOM = $IMSChannel;
ChannelData: TYPE = REF ChannelDataRec;
ChannelDataRec: TYPE = RECORD[board: Board, podTiming: PodTiming, channel: Channel];
BufferIndex: TYPE = REF BufferIndexRec;
BufferIndexRec: TYPE = RECORD[b: Board, p: PodChannel];
The array element forceBoardToSlot[n] should contain the slot number of the nth force board. Numbering of slots is sequential stating at slot 1 in the Master mainframe and ending with slot 48 in the third Slave mainframe. This information is intended to be initialized from the interpreter at runtime to allow dynamic assignment of modules to slots. acquireBoardToSlot is used in a similar manner.
forceBoardToSlot: REF BoardToSlot;
acquireBoardToSlot: REF BoardToSlot;
BoardToSlot: TYPE = ARRAY Board OF IMSTester.SlotNumber;
The array element progBoard[n] should be TRUE if either the force or acquire module corresponding to board n has programmable drive levels or threshold levels.
progBoard: REF ProgBoard;
ProgBoard: TYPE = ARRAY Board OF BOOL;
alwaysDefineGroups: BOOLFALSE; -- for debugging
alwaysDefinePort: BOOLFALSE; -- for debugging
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 strings
firstColumn: NAT = 0*pointsPerInch; -- horizontal space to second column of buttons
secondColumn: NAT = 2*pointsPerInch; -- horizontal space to second column of buttons
thirdColumn: NAT = 4*pointsPerInch; -- horizontal space to third column of buttons
Column: TYPE = NAT [0..2);
MakeStandardViewer: PUBLIC PROC [testButtons: ICTestNew.TestButtonsCreateProc, cellType: Core.CellType, name: ROPENIL, clientHandle: REF ANYNIL] = {
viewer: Viewer;
height: CARDINAL;
height ← MakeStandardButtons[viewer ← Containers.Create[[-- construct the outer container  
name: Rope.Concat["IC Test Tool - ", name],
iconic: FALSE,
column: left,
scrollable: FALSE ]],
0, testButtons, cellType, clientHandle];
ViewerOps.SetOpenHeight[viewer, height];
ViewerOps.PaintViewer[viewer, all];
};
MakeStandardButtons: PUBLIC PROC [viewer: ViewerClasses.Viewer, height: CARDINAL, testButtons: ICTestNew.TestButtonsCreateProc, cellType: Core.CellType, clientHandle: REF ANYNIL] RETURNS [newHeight: CARDINAL] = {
CreateTestButton: PUBLIC PROC [buttonName: ROPE, proc: ICTestNew.TestProc] = {
first: BOOLEAN ← h.testButtonList = NIL;
testHandle: TestHandle ← NEW[TestHandleRec ← [h, proc]];
h.testButtonList ← CONS[Buttons.Create[info: [name: buttonName,
wx: column*2*pointsPerInch, wy: height,
ww: 0,wh: entryHeight,
parent: viewer, border: TRUE], clientData: testHandle, proc: TestButton], h.testButtonList];
height ← height + entryHeight + entryVSpace;
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;
};
rule: Rules.Rule;
h: Handle;
column: Column ← 0;
h ← NEW[ICTestRec];
h.cellType ← cellType;
h.waferFile ← ChoiceButtons.BuildTextPrompt[viewer, firstColumn, height, "Wafer File:", "SingleDie.dat", NIL, 7*pointsPerInch].textViewer;
height ← height + entryHeight + entryVSpace;
h.run ← ChoiceButtons.BuildTextPrompt[viewer, firstColumn, height, "Run:", NIL, NIL, 1*pointsPerInch].textViewer;
[] ← Buttons.Create[info: [name: "Start Test",
wx: secondColumn, wy: height,
ww: 0,wh: entryHeight,
parent: viewer, border: TRUE], clientData: h, proc: StartTest];
[] ← Buttons.Create[info: [name: "Interrupt", wx: thirdColumn, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: h, proc: Interrupt];
height ← height + entryHeight + entryVSpace;
h.wafer ← ChoiceButtons.BuildTextPrompt[viewer, firstColumn, height, "Wafer:", NIL, NIL, 1*pointsPerInch].textViewer;
[] ← Buttons.Create[info: [name: "Abort Die",
wx: secondColumn, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: h, fork: FALSE, proc: AbortDie];
[] ← Buttons.Create[info: [name: "Continue", wx: thirdColumn, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: h, proc: Continue];
height ← height + entryHeight + entryVSpace;
h.die ← ChoiceButtons.BuildTextPrompt[viewer, firstColumn, height, "Die:", NIL, NIL, 1*pointsPerInch].textViewer;
[] ← Buttons.Create[info: [name: "Abort Wafer", wx: secondColumn, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: h, proc: AbortWafer, guarded: TRUE];
[] ← Buttons.Create[info: [name: "Enable Tester", wx: thirdColumn, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: h, proc: EnableTester];
height ← height + entryHeight + entryVSpace;
h.cutSet ← ChoiceButtons.BuildTextPrompt[viewer, firstColumn, height, "Cut Set:", NIL, NIL, 1*pointsPerInch].textViewer;
[] ← Buttons.Create[info: [name: "Abort Test", wx: secondColumn, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: h, proc: AbortTest, guarded: TRUE];
[] ← Buttons.Create[info: [name: "Enable Stepper", wx: thirdColumn, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: h, proc: EnableStepper];
height ← height + entryHeight + entryVSpace/2;
rule ← Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]];
Containers.ChildXBound[viewer, rule];
height ← height + entryHeight + entryVSpace/2;
testButtons[CreateTestButton];
height ← height + entryHeight + entryVSpace/2;
rule ← Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]];
Containers.ChildXBound[viewer, rule];
[ , h.viewerout] ← ViewerIO.CreateViewerStreams[name: "ICTestTypescript", viewer:ViewerOps.CreateViewer[flavor: $TypeScript, info:[parent: viewer, wy: height+2, ww: 7*pointsPerInch, wh: 56 * entryHeight, scrollable: TRUE, border: FALSE]]];
height ← height + 10 * entryHeight;
height ← height + entryHeight + entryVSpace/2;
RETURN[height];
};
TestButton: 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;
};
DefineGroup: PUBLIC PROC [wire: Core.Wire, groupName: ROPE ← NIL, format: FormatType, delay: NSec ← 0, width: NSec ← 0, hiDrive: REAL ← 2.4, loDrive: REAL ← 0.4, sample: NSec ← 0, threshold: REAL ← 1.4] = {
groupData: GroupData ← NEW[GroupDataRec ← [groupName, format, delay, width, hiDrive, loDrive, sample, threshold]];
CoreProperties.PutWireProp[wire, IMSGroup, groupData];
};
DefineChannel: PUBLIC PROC [atomicWire: Core.Wire, board: Board, podTiming: PodTiming, channel: Channel ← 0] = {
channelData: ChannelData ← NEW[ChannelDataRec ← [board, podTiming, channel]];
IF atomicWire.size # 0 THEN ERROR;
CoreProperties.PutWireProp[atomicWire, IMSChannel, channelData];
};
NextChannel: PUBLIC PROC [board: Board, podTiming: PodTiming, channel: Channel] RETURNS [newBoard: Board, newPodTiming: PodTiming, newChannel: Channel] = {
IF channel=LAST[ICTestNew.Channel] THEN
IF podTiming=B THEN
IF board=LAST[ICTestNew.Board] THEN ERROR
ELSE {board ← board + 1; podTiming ← A; channel ← 0}
ELSE {podTiming ← B; channel ← 0}
ELSE channel ← channel + 1;
RETURN[board, podTiming, channel];
};
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.viewerout, "\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.viewerout, "\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: ICTestNew.EvalCycleType] = {
-- IF h.enableSimulation THEN Rosemary.Settle[Rosemary.InstantiateCellType[h.cellType, h.port]];
IF h.enableTester AND evalCycleType = force THEN ForceDataToIMS[h]
ELSE AcquireDataToIMS[h];
};
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.x], IO.int[h.currentDie.y]]];
IO.PutF[h.viewerout, "Die (%g,%g) ", IO.int[h.currentDie.x], IO.int[h.currentDie.y]];
};
};
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 h.enableStepper THEN EGlas.LampOn[];
};
};
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;
};
StartTest: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
IF NOT h.testInProgress THEN {
h: Handle ← NARROW[clientData];
done: BOOLEANFALSE;
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];
h.stop ← abortDie;
};
AbortWafer: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
h.stop ← abortWafer;
};
AbortTest: Buttons.ButtonProc = {
h: Handle ← NARROW[clientData];
h.stop ← interrupt;
h.testInProgress ← FALSE;
};
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];
};
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];
};
};
Message: PROC [rope: ROPE] = {
MessageWindow.Append[rope, TRUE];
MessageWindow.Blink[];
MessageWindow.Append[rope, TRUE];
};
AcquireDataToIMS: PROC [h: Handle] = {
};
ForceDataToIMS: PROC [h: Handle] = {
PortToBuffer: RefTab.EachPairAction = {
port: Ports.Port ← NARROW[key];
index: BufferIndex ← NARROW[val];
board: Board ← index.b;
podChannel: PodChannel ← index.p;
drive: BOOLIF port.d=force THEN FALSE ELSE TRUE;
SELECT port.type FROM
l => {
SELECT port.l FROM
L => {h.buffer[h.cycle][board][podChannel].force ← FALSE;
h.buffer[h.cycle][board][podChannel].inhibit ← FALSE};
H => {h.buffer[h.cycle][board][podChannel].force ← TRUE;
h.buffer[h.cycle][board][podChannel].inhibit ← FALSE};
X => h.buffer[h.cycle][board][podChannel].inhibit ← TRUE;
ENDCASE => ERROR;
};
ls => {
FOR i: NAT IN [0..port.ls.size) DO
SELECT port.ls[i] FROM
L => {h.buffer[h.cycle][board][podChannel].force ← FALSE;
h.buffer[h.cycle][board][podChannel].inhibit ← FALSE};
H => {h.buffer[h.cycle][board][podChannel].force ← TRUE;
h.buffer[h.cycle][board][podChannel].inhibit ← FALSE};
X => h.buffer[h.cycle][board][podChannel].inhibit ← TRUE;
ENDCASE => ERROR;
IF podChannel > 15 THEN {podChannel ← 0; board ← board+1} ELSE podChannel ← podChannel+1;
ENDLOOP;
};
b => {h.buffer[h.cycle][board][podChannel].force ← port.b;
h.buffer[h.cycle][board][podChannel].inhibit ← drive};
bs => {
FOR i: NAT IN [0..port.bs.size) DO
h.buffer[h.cycle][board][podChannel].force ← port.bs[i];
h.buffer[h.cycle][board][podChannel].inhibit ← drive;
IF podChannel > 15 THEN {podChannel ← 0; board ← board+1} ELSE podChannel ← podChannel+1;
ENDLOOP;
};
c => {
mask: CARDINAL ← 1;
end: NAT ← Basics.bitsPerWord*SIZE[CARDINAL]-port.fieldStart;
FOR i:NAT IN [0..end) DO
h.buffer[h.cycle][board][podChannel].force ← Basics.BITAND[port.c, mask]#0;
h.buffer[h.cycle][board][podChannel].inhibit ← drive;
IF i = end-1 THEN EXIT;
IF podChannel >= 15 THEN {podChannel ← 0; board ← board+1} ELSE podChannel ← podChannel+1;
mask ← mask*2;
ENDLOOP;
};
lc => {
mask: LONG CARDINAL ← 1;
end: NAT ← Basics.bitsPerWord*SIZE[LONG CARDINAL]-port.fieldStart;
FOR i:NAT IN [0..Basics.bitsPerWord*SIZE[LONG CARDINAL]-port.fieldStart) DO
h.buffer[h.cycle][board][podChannel].force ← Basics.DoubleAnd[[lc[port.lc]], [lc[mask]]].lc#0;
h.buffer[h.cycle][board][podChannel].inhibit ← drive;
IF i = end-1 THEN EXIT;
IF podChannel >= 15 THEN {podChannel ← 0; board ← board+1} ELSE podChannel ← podChannel+1;
mask ← mask*2;
ENDLOOP;
};
ENDCASE => ERROR;
RETURN[FALSE];
};
[] ← RefTab.Pairs[h.forceTab, PortToBuffer];
IMSTester.SetIMSMemory[h.forceGroups, h.buffer, 1, 0];
};
Init: PROC[h: Handle] = {
MakePodTimingGroup: Ports.EachWirePortPairProc = {
Searches the wire below the point where the group property was found. If a port leaf is reached which correponds to a composite wire, map the channels of the composite wire to the output buffer but don't add to PodTimingGroup. If a unique atomic wire is found that has the "IMSChannel" property put it in the right place in a PodTimingGroup. If there is a corresponding leaf port, map it to the output buffer.
c: ChannelData;
FindFirstChannel: CoreOps.EachWireProc = {
chan: ChannelData;
c ← NEW[ChannelDataRec ← [LAST[Board], LAST[PodTiming], LAST[Channel]]];
IF wire.size = 0 AND (chan ← NARROW[CoreProperties.GetWireProp[wire, IMSChannel]])#NIL THEN {
IF chan.board < c.board THEN
{c.board ← chan.board; c.podTiming ← chan.podTiming; c.channel ← chan.channel}
ELSE IF chan.board = c.board THEN {
IF chan.podTiming < c.podTiming THEN
{c.podTiming ← chan.podTiming; c.channel ← chan.channel}
ELSE IF chan.podTiming = c.podTiming THEN
IF chan.channel < c.channel THEN c.channel ← chan.channel;
};
};
};
Case 1: The port is a unique leaf node and the corresponding wire is composite. Make the entry in the mapTab to connect the port value and the buffer to the IMS.
IF port#NIL THEN IF port.type#composite AND NOT RefTab.Fetch[x: mapTab, key: port].found AND wire.size#0 THEN {
[] ← CoreOps.VisitWire[wire, FindFirstChannel];
IF NOT RefTab.Insert[x: mapTab, key: port, val: NEW[BufferIndexRec ← [c.board, (IF c.podTiming = A THEN 0 ELSE 8) + c.channel]]] THEN ERROR;
};
Case 2: The wire is unique and atomic and has the "IMSChannel" property.
IF wire.size = 0 AND NOT RefTab.Fetch[x: visitTab, key: wire].found AND (c ← NARROW[CoreProperties.GetWireProp[wire, IMSChannel]])#NIL THEN {
IF port#NIL THEN IF port.type=composite THEN ERROR ELSE
IF NOT RefTab.Insert[x: mapTab, key: port, val: NEW[BufferIndexRec ← [c.board, (IF c.podTiming = A THEN 0 ELSE 8) + c.channel]]] THEN ERROR;
IF NOT RefTab.Insert[x: visitTab, key: wire, val: $Visited] THEN ERROR;
FOR l: PodTimingGroups ← podTimingGroups, l.rest WHILE l#NIL DO
IF l.first.module.board = c.board AND l.first.podTiming = c.podTiming THEN {
FOR p: IMSTester.Pins ← l.first.pins, p.rest WHILE p#NIL DO
IF p.first.channel = c.channel THEN ERROR; --channel assigned twice
ENDLOOP;
l.first.pins ← CONS[NEW[IMSTester.PinRec ← [
channel: c.channel,
signalName: NARROW[CoreProperties.GetWireProp[wire, CoreOps.nameProp]],
packagePin: 0]], l.first.pins];
EXIT;
};
REPEAT
FINISHED =>
podTimingGroups ← CONS[NEW[IMSTester.PodTimingGroupRec ← [
module: NEW[IMSTester.ModuleRec ← [
slot: boardToSlot[c.board],
board: c.board,
threshold: g.threshold,
hiDrive: g.hiDrive,
loDrive: g.loDrive]],
podTiming: c.podTiming,
pins: LIST[NEW[IMSTester.PinRec ← [
channel: c.channel,
signalName: NARROW[CoreProperties.GetWireProp[wire, CoreOps.nameProp]],
packagePin: 0]]]]], podTimingGroups];
ENDLOOP;
};
};
LimitGroupSize: PROC [groups: PodTimingGroups] 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 programmable 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
TRUSTED {channels ← channels + List.Length[LOOPHOLE[p.first.pins]]};
addToGroup ← channels <= 32 AND NOT progBoard[p.first.module.board];
IF newGroup#NIL THEN addToGroup ← addToGroup AND NOT progBoard[newGroup.first.module.board];
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];
};
FindForceGroups: Ports.EachWirePortPairProc = {
If the current wire has the "IMSGroup" property then makes the PodTimingGroup for the wire below, and adds it to the list of force groups.
listOfGroups: LIST OF PodTimingGroups;
char: CHAR;
podTimingGroups ← NIL;
char ← 'A;
IF (g ← NARROW[CoreProperties.GetWireProp[wire, IMSGroup]]) # NIL THEN {
[] ← Ports.VisitBinding[wire, port, MakePodTimingGroup];
listOfGroups ← LimitGroupSize[podTimingGroups];
FOR l: LIST OF PodTimingGroups ← listOfGroups, l.rest WHILE l#NIL DO
h.forceGroups ← CONS[NEW[IMSTester.ForceGroupRec ← [IO.PutFR["F%g%g", IO.char[char], IO.rope[g.groupName]], l.first, g.format, g.delay, g.width]], h.forceGroups];
char ← char+1;
ENDLOOP;
};
};
FindAcquireGroups: Ports.EachWirePortPairProc = {
Same as above but for acquire groups.
listOfGroups: LIST OF PodTimingGroups;
char: CHAR;
podTimingGroups ← NIL;
char ← 'A;
IF (g ← NARROW[CoreProperties.GetWireProp[wire, IMSGroup]]) # NIL THEN {
[] ← Ports.VisitBinding[wire, port, MakePodTimingGroup];
listOfGroups ← LimitGroupSize[podTimingGroups];
FOR l: LIST OF PodTimingGroups ← listOfGroups, l.rest WHILE l#NIL DO
h.acquireGroups ← CONS[NEW[IMSTester.AcquireGroupRec ← [IO.PutFR["A%g%g", IO.char[char], IO.rope[g.groupName]], l.first, g.sample]], h.acquireGroups];
char ← char+1;
ENDLOOP;
};
};
visitTab, mapTab: RefTab.Ref;
g: GroupData;
boardToSlot: REF BoardToSlot;
podTimingGroups: PodTimingGroups;
filename: ROPE;
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.enableStepper THEN EGlas.Init[];
IF h.enableTester OR h.enableSimulation THEN h.port ← Ports.CreatePort[h.cellType.public];
IF (h.enableTester AND CoreProperties.GetCellTypeProp[h.cellType, $GroupsDefined]=NIL) OR alwaysDefineGroups THEN {
h.forceGroups ← NIL;
h.acquireGroups ← NIL;
h.buffer ← NEW[IMSTester.BufferRec[1]];
h.cycle ← 0;
IMSTester.Initialize[];
visitTab ← RefTab.Create[];
mapTab ← RefTab.Create[];
boardToSlot ← forceBoardToSlot;
[] ← Ports.VisitBinding[h.cellType.public, h.port, FindForceGroups];
h.forceTab ← mapTab;
visitTab ← RefTab.Create[];
mapTab ← RefTab.Create[];
boardToSlot ← acquireBoardToSlot;
[] ← Ports.VisitBinding[h.cellType.public, h.port, FindAcquireGroups];
h.acquireTab ← mapTab;
IMSTester.DefineGroups[h.forceGroups, h.acquireGroups];
h.buffer ← NEW[IMSTester.BufferRec[1]];
h.buffer[0] ← NEW[IMSTester.CycleDataRec];
CoreProperties.PutCellTypeProp[h.cellType, $GroupsDefined, $True];
};
};
Cleanup: PROC [h: Handle] = {
IF h.enableStepper THEN EGlas.LampOn[];
IF h.enableTester THEN IMSTester.Stop[]; ---***only for debugging
};
CheckStop: PUBLIC PROC [h: Handle] = {
SELECT h.stop FROM
dont => NULL;
abortDie => ERROR AbortDieSignal;
abortWafer => ERROR AbortWaferSignal;
interrupt => ERROR InterruptSignal;
ENDCASE => ERROR;
};
END.