<> <> <> <> DIRECTORY Basics, Buttons, ChoiceButtons, Commander, CommandTool, Containers, Convert, Core, CoreOps, --EGlas,-- FS, HashTable, ICTest, IO, IMSTester, MessageWindow, Ports, Rope, Rules, RuntimeError, SymTab, 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, 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]; Handle: TYPE = REF ICTestRec; ICTestRec: PUBLIC TYPE = RECORD [ <<-- Standard button state -->> waferFile: Viewer _ NIL, run: Viewer _ NIL, wafer: Viewer _ NIL, die: Viewer _ NIL, errorCycle: 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, 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: 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 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: LIST OF Group, assignments: LIST OF 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.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["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]; 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: 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:", "", 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]; 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; }; RegisterTestProc: PUBLIC PROC [testName: ROPE, procName: ROPE, proc: TestProc] = { 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]]]; }; 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 { <> <> } ; }; }; }; NextDie: PROC [h: Handle] RETURNS [done: BOOLEAN _ FALSE] = { Eval: PROC = { CheckStop[h]; IF (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 { <> <> <> }; 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 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 address %g) Done\n", IO.int[test.start]]; 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 = { < {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]; 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]; }; }; 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 $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 = { 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 $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 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: 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: 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, 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 { --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.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; }; h.cycle _ 0; ViewerTools.SetContents[h.errorCycle, "0"]; <> IMSTester.SetCyclePeriod[period]; }; Cleanup: PROC [h: Handle] = { <> 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.