Updated Metrology_Services to support importing Tencor data into CLEAN_INSP records associated with WM_OUT lots.

This commit is contained in:
Infineon\StieberD
2025-11-11 16:32:44 -07:00
parent 169fabdac6
commit 77d2f5f6ab
2 changed files with 240 additions and 196 deletions

View File

@ -2089,18 +2089,19 @@ end service
Service ImportTencorData(RunData) Service ImportTencorData(RunData)
ErrorMessage = '' ErrorMessage = ''
Machine = 'Tencor' Machine = 'Tencor'
Timestamp = RunData<9> Timestamp = RunData<9>
HazeAvg = RunData<10> HazeAvg = RunData<10>
RDSKeyID = RunData<28> LegacyLotId = RunData<28>
ScanRecipe = RunData<30> ScanRecipe = RunData<30>
SoDAvg = RunData<39> SoDAvg = RunData<39>
SoDMax = RunData<40> SoDMax = RunData<40>
SoDMin = RunData<41> SoDMin = RunData<41>
ScanTool = RunData<43> ScanTool = RunData<43>
Swap '%' with ' ' in ScanTool Swap '%' with ' ' in ScanTool
LegacyLotId@ = RDSKeyID LegacyLotId@ = LegacyLotId
Convert @Lower_Case to @Upper_Case in ScanTool Convert @Lower_Case to @Upper_Case in ScanTool
Swap ' AM' with 'AM' in Timestamp Swap ' AM' with 'AM' in Timestamp
@ -2112,7 +2113,7 @@ Service ImportTencorData(RunData)
SODVals = '' SODVals = ''
SortVals = '' SortVals = ''
SODStartTime = Time() SODStartTime = Time()
SODWaferArray = QA_Services('GetSODPerWafer', RDSKeyID, ScanRecipe, Timestamp) SODWaferArray = QA_Services('GetSODPerWafer', LegacyLotId, ScanRecipe, Timestamp)
SODStopTime = Time() SODStopTime = Time()
TimeTaken = SODStopTime - SODStartTime TimeTaken = SODStopTime - SODStartTime
LogData = '' LogData = ''
@ -2121,20 +2122,61 @@ Service ImportTencorData(RunData)
LogData<3> = LegacyLotId@ LogData<3> = LegacyLotId@
LogData<4> = 'Time taken to import SOD data: ':TimeTaken:' seconds.' LogData<4> = 'Time taken to import SOD data: ':TimeTaken:' seconds.'
Logging_Services('AppendLog', objSODPerfLog, LogData, @RM, @FM) Logging_Services('AppendLog', objSODPerfLog, LogData, @RM, @FM)
If SODWaferArray NE '' then If SODWaferArray NE '' then
WaferNos = SODWaferArray<1> WaferNos = SODWaferArray<1>
SODVals = SODWaferArray<2> SODVals = SODWaferArray<2>
SortVals = SODWaferArray<3> SortVals = SODWaferArray<3>
For each WaferNo in WaferNos using @VM For each WaferNo in WaferNos using @VM
If WaferNo NE '' then QA_Services('PostWaferImageRequest', RDSKeyID, WaferNo, ScanRecipe) If WaferNo NE '' then QA_Services('PostWaferImageRequest', LegacyLotId, WaferNo, ScanRecipe)
Next Wafer Next Wafer
end end
LotId = Lot_Services('GetLotIdByLegacyLotIdAndType', RDSKeyID, 'RDS')
Swap '.' with '*' in LegacyLotId ; // Swap . with * used on labels
Swap 'O' with '' in LegacyLotId ; // Remove 'O' prefix used on labels for WM_OUT lots
Begin Case
Case RowExists('RDS', LegacyLotId)
LegacyLotType = 'RDS'
ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', LegacyLotId)
If Error_Services('NoError') then
CIKeyStages = ReactRunRec<REACT_RUN_CI_STAGE$>
CIKeyIDs = ReactRunRec<REACT_RUN_CI_NO$>
PSN = Xlate('RDS', LegacyLotId, 'PROD_SPEC_ID', 'X')
If PSN EQ '' then
ErrorMessage = 'Error in ':Service:' service. Null PSN returned from RDS ':LegacyLotId
Metrology_Services('LogResults', LegacyLotId, Machine, 'UID001', Service : ' : Error reading PSN from RDS.')
end
end else
ErrorMessage = Error_Services('GetMessage')
Metrology_Services('LogResults', LegacyLotId, Machine, 'UID001', Service : ' : ' : ErrorMessage)
end
Case RowExists('WM_OUT', LegacyLotId)
LegacyLotType = 'WM_OUT'
CIKeyStages = ''
CIKeyIDs = Xlate('WM_OUT', LegacyLotId, 'EPO_CI_NO', 'X')
If CIKeyIDs NE '' then CIKeyStages = Xlate('CLEAN_INSP', CIKeyIDs, CLEAN_INSP_STAGE$, 'X')
PSN = Xlate('WM_OUT', LegacyLotId, 'PS_NO', 'X')
If PSN EQ '' then
ErrorMessage = 'Error in ':Service:' service. Null PSN returned from WM_OUT ':LegacyLotId
Metrology_Services('LogResults', LegacyLotId, Machine, 'UID001', Service : ' : Error reading PSN from WM_OUT.')
end
Case Otherwise$
LegacyLotType = ''
ErrorMessage = 'Error in ':Service:' services. Legacy lot id not found in RDS or WM_OUT tables.'
End Case
LotId = Lot_Services('GetLotIdByLegacyLotIdAndType', LegacyLotId, LegacyLotType)
If LotId NE '' then If LotId NE '' then
If DCount(LotId, @VM) EQ 1 then If DCount(LotId, @VM) EQ 1 then
// Import into new metrology data structures // Import into new metrology data structures
MetTestId = Met_Test_Services('CreateMetTest', LotId, RDSKeyID, '', ScanRecipe) MetTestId = Met_Test_Services('CreateMetTest', LotId, LegacyLotId, '', ScanRecipe)
If Error_Services('NoError') then If Error_Services('NoError') then
Met_Test_Services('SetPropsAndLimits', MetTestId) Met_Test_Services('SetPropsAndLimits', MetTestId)
If Error_Services('HasError') then If Error_Services('HasError') then
@ -2187,17 +2229,12 @@ Service ImportTencorData(RunData)
end end
end else end else
Swap @VM with ',' in LotId Swap @VM with ',' in LotId
ErrorMsg = 'Error in ':Service:' service. Number of lot ids returned does not equal 1. Lot ids: ':LotId MetTestErrorMessage = 'Error in ':Service:' service. Number of lot ids returned does not equal 1. Lot ids: ':LotId
Metrology_Services('LogResults', LegacyLotId@, Machine, 'UID001', Service : ' : ' : ErrorMsg) Metrology_Services('LogResults', LegacyLotId@, Machine, 'UID001', Service : ' : ' : MetTestErrorMessage)
end end
end end
If RowExists('RDS', RDSKeyID) then If (ErrorMessage EQ '') then
// Try to read the CleanInsp datarow. Use this to identify the RDSKeyID.
ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSKeyID)
If Error_Services('NoError') then
// Get the PSN to determine if this recipe is part of the LWI stage or the POST stage.
PSN = Xlate('RDS', RDSKeyID, 'PROD_SPEC_ID', 'X')
PRSStageKeys = Xlate('PROD_SPEC', PSN, 'PRS_STAGE_KEY', 'X') PRSStageKeys = Xlate('PROD_SPEC', PSN, 'PRS_STAGE_KEY', 'X')
StageFound = False$ StageFound = False$
Stage = '' Stage = ''
@ -2208,8 +2245,6 @@ Service ImportTencorData(RunData)
Stage = Field(PRSStageKey, '*', 2, 1) Stage = Field(PRSStageKey, '*', 2, 1)
end end
Next PRSStageKey Next PRSStageKey
CIKeyStages = ReactRunRec<REACT_RUN_CI_STAGE$>
CIKeyIDs = ReactRunRec<REACT_RUN_CI_NO$>
Locate Stage in CIKeyStages using @VM setting vPos then Locate Stage in CIKeyStages using @VM setting vPos then
// Stage is prescribed or user created the record from the UI. // Stage is prescribed or user created the record from the UI.
CIKeyID = CIKeyIDs<0, vPos> CIKeyID = CIKeyIDs<0, vPos>
@ -2255,14 +2290,14 @@ Service ImportTencorData(RunData)
Database_Services('WriteDataRow', 'CLEAN_INSP', CIKeyID, CleanInspRec, True$, False$, True$) Database_Services('WriteDataRow', 'CLEAN_INSP', CIKeyID, CleanInspRec, True$, False$, True$)
end else end else
ErrorMessage = 'Tencor recipe ' : ScanRecipe : ' not found in SurfScan recipe list.' ErrorMessage = 'Tencor recipe ' : ScanRecipe : ' not found in SurfScan recipe list.'
Metrology_Services('LogResults', RDSKeyID, Machine, 'UID001', Service : ' : ' : ErrorMessage) Metrology_Services('LogResults', LegacyLotId, Machine, 'UID001', Service : ' : ' : ErrorMessage)
// Set recipe mismatch flag for MFS purposes // Set recipe mismatch flag for MFS purposes
CleanInspRec<CLEAN_INSP_SCAN_RECIPE_MISMATCH$> = ScanRecipe CleanInspRec<CLEAN_INSP_SCAN_RECIPE_MISMATCH$> = ScanRecipe
Database_Services('WriteDataRow', 'CLEAN_INSP', CIKeyID, CleanInspRec, True$, False$, True$) Database_Services('WriteDataRow', 'CLEAN_INSP', CIKeyID, CleanInspRec, True$, False$, True$)
end end
end else end else
ErrorMessage = 'Scan Recipe is missing.' ErrorMessage = 'Scan Recipe is missing.'
Metrology_Services('LogResults', RDSKeyID, Machine, 'UID001', Service : ' : ' : ErrorMessage) Metrology_Services('LogResults', LegacyLotId, Machine, 'UID001', Service : ' : ' : ErrorMessage)
end end
Database_Services('ReleaseKeyIDLock', 'CLEAN_INSP', CIKeyID) Database_Services('ReleaseKeyIDLock', 'CLEAN_INSP', CIKeyID)
end else end else
@ -2270,54 +2305,61 @@ Service ImportTencorData(RunData)
// metrology file is not deleted so that the service can try to import it // metrology file is not deleted so that the service can try to import it
// again once the lock is released. // again once the lock is released.
ErrorMessage = 'Error in service ':Service:'. Failed to lock CLEAN_INSP record: ':CIKeyID:'.' ErrorMessage = 'Error in service ':Service:'. Failed to lock CLEAN_INSP record: ':CIKeyID:'.'
Metrology_Services('LogResults', RDSKeyID, Machine, 'UID002', Service : ' : ' : ErrorMessage) Metrology_Services('LogResults', LegacyLotId, Machine, 'UID002', Service : ' : ' : ErrorMessage)
end end
end else end else
ErrorMessage = 'CI Key ID is missing.' ErrorMessage = 'CI Key ID is missing.'
Metrology_Services('LogResults', RDSKeyID, Machine, 'UID001', Service : ' : ' : ErrorMessage) Metrology_Services('LogResults', LegacyLotId, Machine, 'UID001', Service : ' : ' : ErrorMessage)
end end
end else end else
// Added 8/13/2020 JRO // Added 8/13/2020 JRO
// We can enter in some conditional logic here for Production UAT // Ad-hoc POST stage SURFSCAN logic
// If statement to gate from production below
PostStartTime = Time() PostStartTime = Time()
if Indexc(ScanRecipe, 'POST', 1) then if IndexC(ScanRecipe, 'POST', 1) then
// Locate 'POST' in ScanRecipe Using "" setting pPos then
ReactRunRec = Xlate('REACT_RUN',RDSKeyID, '', 'X') Begin Case
WONo = ReactRunRec<REACT_RUN_WO_NO$>;//WONo Case LegacyLotType EQ 'RDS'
WOStep = ReactRunRec<REACT_RUN_WO_STEP$>;//WOStep ReactRunRec = Xlate('REACT_RUN', LegacyLotId, '', 'X')
CassNo = ReactRunRec<REACT_RUN_CASS_NO$>;//CassNo WONo = ReactRunRec<REACT_RUN_WO_NO$>
Stage = 'POST';//Stages WOStep = ReactRunRec<REACT_RUN_WO_STEP$>
PSNo = XLATE('RDS',RDSKeyID,RDS_PROD_SPEC_ID$,'X');//PSNo CassNo = ReactRunRec<REACT_RUN_CASS_NO$>
DefectSpec = Xlate('PRS_STAGE', PSNo : '*LWI', PRS_STAGE_SURF_DEFECTS$, 'X');//Since there is no set spec for a non-existent stage we need to take one from the unloading step. Case LegacyLotType EQ 'WM_OUT'
HazeSpec = Xlate('PRS_STAGE', PSNo : '*LWI', PRS_STAGE_SURF_HAZE$, 'X');//Since there is no set spec for a non-existent stage we need to take one from the unloading step. WONo = Field(LegacyLotId, '*', 1, 1)
WOStep = Field(LegacyLotId, '*', 2, 1)
CassNo = Field(LegacyLotId, '*', 3, 1)
Case Otherwise$
ErrorMessage = 'Error in ':Service:' services. Invalid LegacyLotType.'
End Case
Stage = 'POST'
DefectSpec = Xlate('PRS_STAGE', PSN : '*LWI', PRS_STAGE_SURF_DEFECTS$, 'X');//Since there is no set spec for a non-existent stage we need to take one from the unloading step.
HazeSpec = Xlate('PRS_STAGE', PSN : '*LWI', PRS_STAGE_SURF_HAZE$, 'X');//Since there is no set spec for a non-existent stage we need to take one from the unloading step.
// Check if CI was created already(usually during cleans) JRO-9-28 // Check if CI was created already(usually during cleans) JRO-9-28
CIStages = ReactRunRec<REACT_RUN_CI_STAGE$> Locate 'POST' in CIKeyStages using @VM setting sPos then
Locate 'POST' in CIStages using @VM setting sPos then
// Existing POST CI record found, fetch it. // Existing POST CI record found, fetch it.
CINo = ReactRunRec<REACT_RUN_CI_NO$,sPos> CINo = CIKeyIDs<0, sPos>
CleanInspRec = Xlate('CLEAN_INSP', CINo,'','X') CleanInspRec = Xlate('CLEAN_INSP', CINo,'','X')
IF CleanInspRec<CLEAN_INSP_SCAN_SOD_PER_WAFER$> NE '' THEN IF CleanInspRec<CLEAN_INSP_SCAN_SOD_PER_WAFER$> NE '' THEN
// Append a value mark to the end of the field so that we can add another set of data // Append a value mark to the end of the field so that we can add another set of data
CleanInspRec<CLEAN_INSP_SCAN_SOD_PER_WAFER$> = CleanInspRec<CLEAN_INSP_SCAN_SOD_PER_WAFER$> : @VM CleanInspRec<CLEAN_INSP_SCAN_SOD_PER_WAFER$> = CleanInspRec<CLEAN_INSP_SCAN_SOD_PER_WAFER$> : @VM
END END
exists = True$ Exists = True$
end else end else
// No Exisiting POST CI record found, Create new CI record // No Exisiting POST CI record found, Create new CI record
exists = False$ Exists = False$
oCIParms = '' oCIParms = ''
oCIParms = WONo:@RM oCIParms = WONo:@RM
oCIParms := WOStep:@RM oCIParms := WOStep:@RM
oCIParms := CassNo:@RM oCIParms := CassNo:@RM
oCIParms := Stage:@RM oCIParms := Stage:@RM
oCIParms := RDSKeyID:@RM If LegacyLotType EQ 'RDS' then oCIParms := LegacyLotId:@RM
oCIParms := PSNo:@RM oCIParms := PSN:@RM
CleanInspRec = '' CleanInspRec = ''
CleanInspRec<CLEAN_INSP_WO_NO$> = WONo CleanInspRec<CLEAN_INSP_WO_NO$> = WONo
CleanInspRec<CLEAN_INSP_WO_STEP$> = WOStep CleanInspRec<CLEAN_INSP_WO_STEP$> = WOStep
CleanInspRec<CLEAN_INSP_CASS_NO$> = CassNo CleanInspRec<CLEAN_INSP_CASS_NO$> = CassNo
CleanInspRec<CLEAN_INSP_STAGE$> = Stage CleanInspRec<CLEAN_INSP_STAGE$> = Stage
CleanInspRec<CLEAN_INSP_RDS_NO$> = RDSKeyID If LegacyLotType EQ 'RDS' then CleanInspRec<CLEAN_INSP_RDS_NO$> = LegacyLotId
end end
CleanInspRec = Insert(CleanInspRec, CLEAN_INSP_SCAN_RECIPE$, -1, 0, ScanRecipe) CleanInspRec = Insert(CleanInspRec, CLEAN_INSP_SCAN_RECIPE$, -1, 0, ScanRecipe)
CleanInspRec = Insert(CleanInspRec, CLEAN_INSP_SCAN_TOOL$, -1, 0, ScanTool) CleanInspRec = Insert(CleanInspRec, CLEAN_INSP_SCAN_TOOL$, -1, 0, ScanTool)
@ -2343,13 +2385,28 @@ Service ImportTencorData(RunData)
end end
Next Wafer Next Wafer
end end
If exists EQ False$ then If exists EQ False$ then
oCIParms := CleanInspRec
oCIParms := CleanInspRec ;
CINo = obj_Clean_Insp('Create',oCIParms) CINo = obj_Clean_Insp('Create',oCIParms)
If Error_Services('NoError') then If Error_Services('NoError') then
Begin Case
Case LegacyLotType EQ 'RDS'
ReactRunRec = INSERT(ReactRunRec,REACT_RUN_CI_NO$,-1,0,CINo) ReactRunRec = INSERT(ReactRunRec,REACT_RUN_CI_NO$,-1,0,CINo)
ReactRunRec = INSERT(ReactRunRec,REACT_RUN_CI_STAGE$,-1,0,Stage) ReactRunRec = INSERT(ReactRunRec,REACT_RUN_CI_STAGE$,-1,0,Stage)
obj_Tables('WriteRec','REACT_RUN':@RM:RDSKeyID:@RM:@RM:ReactRunRec) obj_Tables('WriteRec','REACT_RUN':@RM:LegacyLotId:@RM:@RM:ReactRunRec)
Case LegacyLotType EQ 'WM_OUT'
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WONo:'*':CassNo)
If Error_Services('NoError') then
WOMatRec = Insert(WOMatRec, WO_MAT_EPO_CI_NO$, -1, 0, CINo)
Database_Services('WriteDataRow', 'WO_MAT', WONo:'*':CassNo, WOMatRec, True$, False$, False$)
end
Case Otherwise$
ErrorMessage = 'Error in ':Service:' services. Invalid LegacyLotType.'
End Case
If ErrorMessage EQ '' then
obj_Tables('WriteRec','CLEAN_INSP':@RM:CINo:@RM:@RM:CleanInspRec) obj_Tables('WriteRec','CLEAN_INSP':@RM:CINo:@RM:@RM:CleanInspRec)
PostStopTime = Time() PostStopTime = Time()
TimeTaken = PostStopTime - PostStartTime TimeTaken = PostStopTime - PostStartTime
@ -2359,6 +2416,8 @@ Service ImportTencorData(RunData)
LogData<3> = LegacyLotId@ LogData<3> = LegacyLotId@
LogData<4> = 'Time taken to import POST data: ':TimeTaken:' seconds.' LogData<4> = 'Time taken to import POST data: ':TimeTaken:' seconds.'
Logging_Services('AppendLog', objPOSTPerfLog, LogData, @RM, @FM) Logging_Services('AppendLog', objPOSTPerfLog, LogData, @RM, @FM)
end
end else end else
// Record error. Ensure metrology run data file is not deleted by including UID002 in the // Record error. Ensure metrology run data file is not deleted by including UID002 in the
// error message that is placed onto error stack in Error_Services. // error message that is placed onto error stack in Error_Services.
@ -2374,8 +2433,8 @@ Service ImportTencorData(RunData)
If QualScan EQ False$ then If QualScan EQ False$ then
Recipients = XLATE('NOTIFICATION','TENCOR_NOTIFICATIONS',NOTIFICATION_USER_ID$,'X') Recipients = XLATE('NOTIFICATION','TENCOR_NOTIFICATIONS',NOTIFICATION_USER_ID$,'X')
SentFrom = 'OI Admin' SentFrom = 'OI Admin'
Subject = 'Tencor SurfScan Import Failure for RDS ':RDSKeyID:'.' Subject = 'Tencor SurfScan Import Failure for ':LegacyLotType:' ':LegacyLotId:'.'
Message = 'Scanned recipe ':ScanRecipe:' not found in PSN ':PSN:' for RDS ':RDSKeyID:'.' Message = 'Scanned recipe ':ScanRecipe:' not found in PSN ':PSN:' for ':LegacyLotType:' ':LegacyLotId:'.'
AttachWindow = '' AttachWindow = ''
AttachKey = '' AttachKey = ''
SendToGroup = '' SendToGroup = ''
@ -2384,14 +2443,8 @@ Service ImportTencorData(RunData)
end end
end end
end end
end else
ErrorMessage = Error_Services('GetMessage')
Metrology_Services('LogResults', RDSKeyID, Machine, 'UID001', Service : ' : ' : ErrorMessage)
end
end else
ErrorMessage = 'Invalid RDS Key ID.'
Metrology_Services('LogResults', RDSKeyID, Machine, 'UID001', Service : ' : ' : ErrorMessage)
end end
If ErrorMessage NE '' then Error_Services('Add', ErrorMessage) If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
end service end service
@ -3680,3 +3733,4 @@ LoadRunDataToDatabase:
return return

View File

@ -1012,7 +1012,7 @@ Service GetSODPerWafer(RDSKey, TencorRecipe, ScanDTM)
If RDSKey NE '' and TencorRecipe NE '' then If RDSKey NE '' and TencorRecipe NE '' then
If Num(ScanDTM) then ScanDTM = OConv(ScanDTM, 'DT/^S') If Num(ScanDTM) then ScanDTM = OConv(ScanDTM, 'DT/^S')
Query = "DECLARE @RDS varchar(10) " Query = "DECLARE @RDS varchar(255) "
Query := "DECLARE @RECIPE varchar(30) " Query := "DECLARE @RECIPE varchar(30) "
Query := "DECLARE @INSERT_DT datetime " Query := "DECLARE @INSERT_DT datetime "
Query := "SET @RDS = '":RDSKey:"' " Query := "SET @RDS = '":RDSKey:"' "
@ -1199,7 +1199,7 @@ Service ProcessWaferImageRequests()
PSN = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X') PSN = Xlate('RDS', RDSNo, 'PROD_SPEC_ID', 'X')
// Format Wafer Number for SQL Query // Format Wafer Number for SQL Query
WaferNo = Fmt(TrimF(WaferNo), 'R(0)#2') WaferNo = Fmt(TrimF(WaferNo), 'R(0)#2')
Query = "DECLARE @RDS varchar(10) " | Query = "DECLARE @RDS varchar(255) " |
: "DECLARE @RECIPE varchar(30) " | : "DECLARE @RECIPE varchar(30) " |
: "DECLARE @WFRID varchar(10) " | : "DECLARE @WFRID varchar(10) " |
: "SET @RDS = '":RDSNo:"' " | : "SET @RDS = '":RDSNo:"' " |
@ -3833,13 +3833,3 @@ ClearCursors:
return return