<> <> <> <> 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:", "", 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; <> }; 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] = { <> 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] = { <> 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 { <> 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; <> reversedGroup _ NIL; IF newGroup#NIL THEN FOR p: PodTimingGroups _ newGroup, p.rest WHILE p#NIL DO reversedGroup _ CONS[p.first, reversedGroup]; ENDLOOP; listOfGroups _ CONS[reversedGroup, listOfGroups]; }; MakePodTimingGroup: PROC [h: Handle, g: Group, boardToSlot: REF BoardToSlot, nameTab: SymTab.Ref] RETURNS [podTimingGroups: IMSTester.PodTimingGroups] = { FOR a: LIST OF Assignments _ h.assignments, a.rest WHILE a#NIL DO IF a.first.group = g.number THEN { FOR l: PodTimingGroups _ podTimingGroups, l.rest WHILE l#NIL DO IF l.first.board = a.first.board AND l.first.podTiming = a.first.pod THEN { FOR p: IMSTester.Pins _ l.first.pins, p.rest WHILE p#NIL DO IF p.first.channel = a.first.channel THEN ERROR; --channel assigned twice ENDLOOP; l.first.pins _ CONS[NEW[IMSTester.PinRec _ [ channel: a.first.channel, signalName: LimitNameLength[nameTab, a.first.name, signalNameLength], packagePin: a.first.probeCardPin]], l.first.pins]; EXIT; }; REPEAT FINISHED => podTimingGroups _ CONS[NEW[IMSTester.PodTimingGroupRec _ [ slot: boardToSlot[a.first.board].slot, board: a.first.board, podTiming: a.first.pod, pins: LIST[NEW[IMSTester.PinRec _ [ channel: a.first.channel, signalName: LimitNameLength[nameTab, a.first.name, signalNameLength], packagePin: a.first.probeCardPin]]]]], podTimingGroups]; ENDLOOP; }; ENDLOOP; }; MakeForceAcquireGroups: PROC [h: Handle] = { listOfGroups: LIST OF PodTimingGroups; count: NAT; forceSubGroup: IMSTester.ForceGroup; acquireSubGroup: IMSTester.AcquireGroup; FOR g: LIST OF Group _ h.groups, g.rest WHILE g#NIL DO IF g.first.directionality=force OR g.first.directionality=biDirectional THEN { listOfGroups _ LimitGroupSize[h, MakePodTimingGroup[h, g.first, h.forceBoardToSlot, h.forceNamesTab], h.forceBoardToSlot]; h.forceSubGroups _ CONS[[g.first.name, NIL], h.forceSubGroups]; count _ 0; FOR l: LIST OF PodTimingGroups _ listOfGroups, l.rest WHILE l#NIL DO forceSubGroup _ NEW[IMSTester.ForceGroupRec _ [LimitNameLength[h.forceNamesTab, IO.PutFR["F%g%%%02g", IO.rope[g.first.name], IO.card[count]], groupNameLength], l.first, g.first.format, g.first.delay, g.first.width, g.first.programable, g.first.hiDrive, g.first.loDrive]]; h.forceGroups _ CONS[forceSubGroup, h.forceGroups]; h.forceSubGroups.first.subGroups _ CONS[forceSubGroup, h.forceSubGroups.first.subGroups]; count _ count+1; ENDLOOP; }; IF g.first.directionality=acquire OR g.first.directionality=biDirectional THEN { listOfGroups _ LimitGroupSize[h, MakePodTimingGroup[h, g.first, h.acquireBoardToSlot, h.acquireNamesTab], h.acquireBoardToSlot]; h.acquireSubGroups _ CONS[[g.first.name, NIL], h.acquireSubGroups]; count _ 0; FOR l: LIST OF PodTimingGroups _ listOfGroups, l.rest WHILE l#NIL DO acquireSubGroup _ NEW[IMSTester.AcquireGroupRec _ [LimitNameLength[h.acquireNamesTab, IO.PutFR["A%g%%%02g", IO.rope[g.first.name], IO.card[count]], groupNameLength], l.first, g.first.sample, g.first.compare, g.first.programable, g.first.threshold]]; h.acquireGroups _ CONS[acquireSubGroup, h.acquireGroups]; h.acquireSubGroups.first.subGroups _ CONS[acquireSubGroup, h.acquireSubGroups.first.subGroups]; count _ count+1; ENDLOOP; }; ENDLOOP; }; Init: PROC[h: Handle] = { forceBoard, acquireBoard, programable: PACKED ARRAY IMSTester.SlotNumber OF BOOLEAN; board: NAT; filename: ROPE; period: Period; filename _ ViewerTools.GetContents[h.waferFile]; h.inStream _ FS.StreamOpen[filename ! FS.Error => {Message[IO.PutFR["Wafer file: %g not found", IO.rope[filename]]]; CONTINUE}]; IF h.port=NIL THEN h.port _ Ports.CreatePort[h.cellType.public, TRUE]; IMSTester.checkSyntax _ NOT h.enableTester; IF h.forceNamesTab=NIL THEN { --only do it the first time "Start Test" is pressed h.forceNamesTab _ SymTab.Create[]; h.acquireNamesTab _ SymTab.Create[]; [forceBoard, acquireBoard, programable] _ IMSTester.Initialize[]; board _ 0; FOR slot: IMSTester.SlotNumber IN IMSTester.SlotNumber DO IF forceBoard[slot] THEN { h.forceBoardToSlot[board] _ [slot, programable[slot]]; board _ board+1; }; ENDLOOP; board _ 0; FOR slot: IMSTester.SlotNumber IN IMSTester.SlotNumber DO IF acquireBoard[slot] THEN { h.acquireBoardToSlot[board] _ [slot, programable[slot]]; board _ board+1; }; ENDLOOP; MakeForceAcquireGroups[h]; MapPortToBuffer[h]; IMSTester.DefineGroups[h.forceGroups, h.acquireGroups]; h.buffer _ NEW[IMSTester.BufferRec[1]]; h.buffer[0] _ NEW[IMSTester.CycleDataRec]; }; h.cycle _ 0; ViewerTools.SetContents[h.errorCycle, "0"]; IF h.enableStepper THEN EGlas.Init[]; period _ ((IO.GetInt[IO.RIS[ViewerTools.GetContents[h.period]]]+5)/10)*10; ViewerTools.SetContents[h.period, IO.PutR1[IO.int[period]]]; IMSTester.SetCyclePeriod[period]; }; Cleanup: PROC [h: Handle] = { IF h.enableStepper THEN EGlas.LampOn[]; IF NOT h.loopTest THEN IMSTester.Stop[]; }; GetName: PROC [h: Handle, key: Rope.ROPE] RETURNS [forceName, acquireName: Rope.ROPE] = { val: SymTab.Val; found: BOOL; [found, val] _ SymTab.Fetch[h.forceNamesTab, key]; forceName _ IF found THEN NARROW[val, Rope.ROPE] ELSE NIL; [found, val] _ SymTab.Fetch[h.acquireNamesTab, key]; acquireName _ IF found THEN NARROW[val, Rope.ROPE] ELSE NIL; }; LookUpName: Commander.CommandProc = { argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO failed}]; key, forceName, acquireName: Rope.ROPE; FOR i: NAT IN [1..argv.argc) DO key _ argv[i]; IF Rope.Length[key] = 0 THEN LOOP ELSE EXIT; ENDLOOP; [forceName, acquireName] _ GetName[NARROW[cmd.procData.clientData], key]; IF forceName#NIL THEN IO.PutF[cmd.out, "Force name: %g\n", IO.rope[forceName]]; IF acquireName#NIL THEN IO.PutF[cmd.out, "Acquire name: %g\n", IO.rope[forceName]]; IF forceName=NIL AND acquireName=NIL THEN IO.PutF[cmd.out, "%g not found\n", IO.rope[key]]; EXITS failed => NULL; }; END.