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: BOOL ← FALSE, -- internal, enables sending commands to ElectroGlas
enableTester: BOOL ← FALSE, -- enables sending commands to Tester
enableSimulation: BOOL ← FALSE, -- 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: BOOL ← FALSE, -- internal, button monitor
testButtonList: LIST OF Buttons.Button, -- internal, list of registered test buttons
inStream: IO.STREAM ← NIL, -- 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.STREAM ← NIL --*** 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: BOOL ← FALSE; -- for debugging
alwaysDefinePort: BOOL ← FALSE; -- 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:
ROPE ←
NIL, clientHandle:
REF
ANY ←
NIL] = {
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
ANY ←
NIL]
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:
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.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:
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.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:
BOOLEAN ←
FALSE] = {
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: BOOLEAN ← 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];
};
};
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: BOOL ← IF 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.