stdf-0.2.0.0: Data/Stdf/Types.hs
{-# LANGUAGE DeriveGeneric #-}
module Data.Stdf.Types where
import Data.Word
import Data.Int
import Foreign.C.Types
import Data.Text.Lazy
import GHC.Generics hiding (U1, C1)
import Data.Aeson
import Data.Aeson.Types
import Data.Time.Clock
-- Time is local unix time. Shouldn't they put the time zone in the file
-- in order for this to have any meaning? What if the data isn't being parsed
-- in the same time zone as it was generated? It will be as if the tester
-- was transported through time and space to wherever I am. I think I'll
-- just leave it alone so times can be Word32 representing Unix time
type U1 = Word8 -- unsigned 1 byte
type U2 = Word16 -- unsigned 2 bytes
type U4 = Word32 -- unsigned 4 bytes
type I1 = Int8
type I2 = Int16
type I4 = Int32
type R4 = Float -- CFloat
type R8 = Double -- CDouble
type C1 = Char
jsonOptions = defaultOptions {
allNullaryToStringTag = False
, omitNothingFields = True
, sumEncoding = ObjectWithSingleField
}
flagOptions = defaultOptions
instance ToJSON Milliseconds where
toJSON = genericToJSON jsonOptions
instance ToJSON Minutes where
toJSON = genericToJSON jsonOptions
instance ToJSON Rec where
toJSON = genericToJSON jsonOptions
instance ToJSON PartFlag where
toJSON = genericToJSON jsonOptions
instance ToJSON GdrField where
toJSON = genericToJSON jsonOptions
instance ToJSON TestType where
toJSON = genericToJSON jsonOptions
instance ToJSON WaferUnits where
toJSON = genericToJSON jsonOptions
instance ToJSON Direction where
toJSON = genericToJSON jsonOptions
instance ToJSON OptionalInfo where
toJSON = genericToJSON jsonOptions
instance ToJSON GroupMode where
toJSON = genericToJSON jsonOptions
instance ToJSON Radix where
toJSON = genericToJSON jsonOptions
instance ToJSON TestFlag where
toJSON = genericToJSON flagOptions
instance ToJSON PassFailBin where
toJSON = genericToJSON jsonOptions
instance ToJSON ParametricFlag where
toJSON = genericToJSON flagOptions
data BinRec = BinRec
{ header :: Header
, rec :: Rec } deriving (Generic, Show)
data Header = Header
{ len :: !Word16
, typ :: !Word8
, sub :: !Word8
} deriving (Generic, Show)
type Stdf = [Rec]
data Milliseconds = Milliseconds { ms :: !U4 }
deriving (Generic, Show)
data Minutes = Minutes { minutes :: !U2 }
deriving (Generic, Show)
-- The mother of all datatypes
data Rec= Raw { raw :: Text } -- base64 TODO: URL encoding or maybe don't bother. what's this good for?
| Far { cpuType :: !U1
, stdfVersion :: !U1 }
| Atr { modificationTime :: Maybe UTCTime
, commandLine :: Maybe Text }
| Mir { setupTime :: Maybe UTCTime
, startTime :: Maybe UTCTime
, station :: !U1
, modeCode :: Maybe C1 -- TODO: MODE_COD record
, retestCode :: Maybe C1 -- TODO: RTST_COD record
, protectionCode :: Maybe C1 -- ' '
, burninTime :: Maybe Minutes -- 65,535
, commandCode :: Maybe C1 -- ' '
, lotId :: Text
, partType :: Text
, nodeName :: Text
, testerType :: Text
, jobName :: Text
, jobRevision :: Maybe Text
, subLotId :: Maybe Text
, operatorName :: Maybe Text
, execType :: Maybe Text
, execVersion :: Maybe Text
, testCode :: Maybe Text
, testTemperature :: Maybe Text
, userText :: Maybe Text
, auxFile :: Maybe Text
, packageType :: Maybe Text
, familyId :: Maybe Text
, dateCode :: Maybe Text
, facilityId :: Maybe Text
, floorId :: Maybe Text
, processId :: Maybe Text
, operationFreq :: Maybe Text
, specName :: Maybe Text
, specVersion :: Maybe Text
, flowId :: Maybe Text
, setupId :: Maybe Text
, designRev :: Maybe Text
, engineeringLotId :: Maybe Text
, romCodeId :: Maybe Text
, testerSerialNum :: Maybe Text
, supervisorName :: Maybe Text }
| Mrr { finishTime :: Maybe UTCTime
, lotDisposition :: Maybe C1
, userDescription :: Maybe Text
, execDescription :: Maybe Text }
| Pcr { headId :: !U1
, siteId :: !U1
, partCount :: !U4
, retestCount :: Maybe U4
, abortCount :: Maybe U4
, goodCount :: Maybe U4
, functionalCount :: Maybe U4 }
| Hbr { headId :: !U1
, siteId :: !U1
, bin :: !U2
, binCount :: !U4
, passFailBin :: PassFailBin
, name :: Maybe Text }
| Sbr { headId :: !U1
, siteId :: !U1
, bin :: !U2
, binCount :: !U4
, passFail :: PassFailBin
, name :: Maybe Text }
| Pmr { index :: !U2 -- Maybe rather than index reference by name
, channelType :: Maybe U2
, channelName :: Maybe Text
, physicalName :: Maybe Text
, logicalName :: Maybe Text
, headId :: !U1
, siteId :: !U1 }
| Pgr { index :: !U2
, name :: Maybe Text
, pinIndecies :: [U2] } -- list of pins instead of refering to indecies
-- Parsing:
-- Empty arrays (or empty members of arrays) can be omitted
-- if they occur at the end of the record.
| Plr {
-- Instead of Maybe [] opt for empty [Maybe]
indecies :: [U2]
, groupModes :: [GroupMode]
, groupRadixes :: [Radix]
-- Should really just use a string for state characters
-- instead of Left/Right but failed since I don't have example PLR
-- , programStateChars :: [Text] -- combine CharR and CharL at parse?
-- , returnStateChars :: [Text] }
, programStateCharsRight :: [Maybe Text]
, returnStateCharsRight :: [Maybe Text]
, programStateCharsLeft :: [Maybe Text]
, returnStateCharsLeft :: [Maybe Text] }
| Rdr { retestBins :: [U2] }
| Sdr { headId :: !U1
, siteGroup :: !U1
, sites :: [U1]
, handlerType :: Maybe Text
, handlerId :: Maybe Text
, probeCardType :: Maybe Text
, probeCardId :: Maybe Text
, loadBoardType :: Maybe Text
, loadBoardId :: Maybe Text
, dibType :: Maybe Text
, dibId :: Maybe Text
, cableType :: Maybe Text
, cableId :: Maybe Text
, contactorType :: Maybe Text
, contactorId :: Maybe Text
, laserType :: Maybe Text
, laserId :: Maybe Text
, extraType :: Maybe Text
, extraId :: Maybe Text }
| Wir { headId :: !U1
, siteGroup :: !U1 -- 255 -> Nothing -- feature removed
, startTime :: Maybe UTCTime
, waferId :: Maybe Text }
| Wrr { headId :: !U1
, siteGroup :: !U1 -- 255 means Nothing
, finishTime :: Maybe UTCTime
, partCount :: !U4
, retestCount :: Maybe U4 -- 4,294,967,295 -> Nothing
, abortCount :: Maybe U4 -- 4,294,967,295 -> Nothing
, goodCount :: Maybe U4 -- 4,294,967,295 -> Nothing
, functionalCount :: Maybe U4 -- 4,294,967,295 -> Nothing
, waferId :: Maybe Text -- length 0 -> Nothing
, fabWaferId :: Maybe Text -- length 0 -> Nothing
, waferFrameId :: Maybe Text -- length 0 -> Nothing
, waferMaskId :: Maybe Text -- length 0 -> Nothing
, userDescription :: Maybe Text -- length 0 -> Nothing
, execDescription :: Maybe Text }
| Wcr { waferSize :: Maybe R4 -- 0 -> Nothing
, dieHeight :: Maybe R4
, dieWidth :: Maybe R4
, waferUnits :: Maybe WaferUnits
, waferFlat :: Maybe Direction
, centerX :: Maybe I2
, centerY :: Maybe I2
, positiveXdirection :: Maybe Direction
, positiveYdirection :: Maybe Direction }
| Pir { headId :: !U1
, siteId :: !U1 }
| Prr { headId :: !U1
, siteId :: !U1
, partFlag :: !PartFlag
, numTestsExecuted :: !U2
, hardBin :: !U2
, softBin :: Maybe U2
, xCoord :: Maybe I2
, yCoord :: Maybe I2
, testTime :: Maybe Milliseconds -- TODO: type milliseconds like minutes or call this field milliseconds
, partID :: Maybe Text
, partTxt :: Maybe Text
, partFix :: Maybe Text }
| Tsr { headId :: !U1
, siteId :: !U1
, testType :: Maybe TestType
, testId :: !U4
, execCount :: Maybe U4
, failCount :: Maybe U4
, alarmCount :: Maybe U4
, testName :: Maybe Text
, sequencerName :: Maybe Text
, testLabel :: Maybe Text
-- , optionalFlags :: !U1 -- parsing optional if last field in record
, testTimeAverage :: Maybe R4 -- optional fields based on optionalFlags TODO: name this field averageSeconds or make a Seconds type
, valueMin :: Maybe R4 -- may make these another record type
, valueMax :: Maybe R4
, valueSum :: Maybe R4
, valueSumOfSquares :: Maybe R4 }
| Ptr { testId :: !U4
, headId :: !U1
, siteId :: !U1
, testFlags :: [TestFlag] -- B1 bitfield further parsing bits
, parametricFlags :: [ParametricFlag] -- B1 bitfield further parsing bits
, result :: Maybe R4
, testText :: Maybe Text
-- , alarmId :: Maybe Text -> optionalInfo
, info :: [OptionalInfo] } -- TODO: better name -- Maybe so empty ones don't print in Json
| Mpr { testId :: !U4
, headId :: !U1
, siteId :: !U1
, testFlags :: [TestFlag]
, parametricFlags :: [ParametricFlag]
-- j , stateCount :: U2
-- k , resultCount :: U2
, states :: [U1] -- Nibbles! array of states? j states
, results :: [R4] -- k results
, testText :: Maybe Text
, info :: [OptionalInfo] }
-- , alarmId :: Maybe Text
-- -- , OPT_FLG B1 optional stuff to parse
-- , resultExp :: Maybe I1 -- TODO: put mostly in OptionalInfo
-- , lowLimitExp :: Maybe I1
-- , highLimitExp :: Maybe I1
-- , lowLimit :: Maybe R4
-- , highLimit :: Maybe R4
-- , startingInput :: Maybe R4
-- , incrementInput :: Maybe R4
-- , returnPinIndecies :: Maybe [U2] -- k indecies
-- , units :: Maybe Text
-- , unitsInputCondition :: Maybe Text
-- , printfResultFmt :: Maybe Text
-- , printfLowLimitFmt :: Maybe Text
-- , printfHighLimitFmt :: Maybe Text
-- , lowSpecLimit :: Maybe R4
-- , highSpecLimit :: Maybe R4 }
| Ftr { testId :: !U4
, headId :: !U1
, siteId :: !U1
, testFlags :: [TestFlag]
, info :: [OptionalInfo]
-- -- , optFlg :: !U1 -- 8 bit packed binary -- record may have ended by here
-- , cycleCount :: Maybe U4 -- To Optional Info
-- , relativeVectorAddr :: Maybe U4
-- , numFailingPins :: Maybe U4
-- , xLogicalFailureAddr :: Maybe I4
-- , yLogicalFailureAddr :: Maybe I4
-- , offsetFromVector :: Maybe I2
-- -- j U2
-- -- k U2
-- , pinIndecies :: Maybe [U2] -- j x U2
-- , returnedStates :: Maybe [U1] -- j NIBBLES!
-- , pgmStateIndecies :: Maybe [U2] -- k x U2
-- , pgmStates :: Maybe [U1] -- k NIBBLES!
-- , failPin :: Maybe [U1] -- bitfield!
-- , vector :: Maybe Text
-- , timeSet :: Maybe Text
-- , opCode :: Maybe Text
-- , label :: Maybe Text
-- , alarmId :: Maybe Text
-- , programText :: Maybe Text
-- , resultText :: Maybe Text
-- , patternGen :: Maybe U1 -- 255
-- , enabledPins :: Maybe [U1] -- bitfield!
} -- TODO: this is a long silly record. there's a bunch more things
| Bps { sequencerName :: Maybe Text } -- Begin Program Secion
| Eps -- End Program Section: no payload
| Gdr [GdrField]
| Dtr { textDat :: Text }
deriving (Generic, Show)
data GroupMode = UnknownGroupMode
| Normal
| SameCycleIO
| SameCycleMidband
| SameCycleValid
| SameCycleWindowSustain
| DualDrive
| DualDriveMidband
| DualDriveValid
| DualDriveWindowSustain
| OtherGroupMode U2
deriving (Generic, Show)
data Radix = DefaultRadix
| Binary
| Octal
| Decimal
| Hexadecimal
| Symbolic
| OtherRadix U1
deriving (Generic, Show)
data TestFlag = Alarm -- bit 0
| Invalid -- bit 1
| Unreliable -- bit 2
| Timeout -- bit 3
| NotExecuted -- bit 4
| Aborted -- bit 5
| InValid -- bit 6
| Pass -- bit 7 == 0
| Fail -- bit 7
deriving (Generic, Show, Eq, Enum)
data PassFailBin = PassBin | FailBin | UnknownBin | OtherBin Char
deriving (Generic, Show)
data ParametricFlag = ScaleError -- bit 0
| DriftError -- bit 1
| Oscillation -- bit 2
| FailHighLimit -- bit 3
| FailLowLimit -- bit 4
| PassAlternateLimits -- bit 5
| PassOnEqLowLimit -- bit 6
| PassOnEqHighLimit -- bit 7
-- bits 6 & 7 seem stupid
deriving (Generic, Show, Eq, Enum)
-- TODO: Another pass at scaling flags
-- Maybe better as sum type
data OptionalInfo = Units Text
| LowSpecLimit Float
| HighSpecLimit Float
| LowSpecLimitStr Text
| HighSpecLimitStr Text
| AlarmId Text
| LowLimit Float
| HighLimit Float
| LowLimitStr Text
| HighLimitStr Text
| ResultStr Text
| StartingInput Float
| StartingInputUnits Text
| IncrementInput Float
-- change to a map of pinName -> state
| ReturnPinIndecies [U2] -- k
| CycleCount U4
| RepeatCount U4
| RelativeVectorAddr U4
| NumFailingPins U4
| XLogicalFailureAddr I4
| YLogicalFailureAddr I4
| OffsetFromVector I2
| PinIndecies [U2] -- j x U2 -- redundant with ReturnPinIndecies?
| ReturnedStates [U1] -- j NIBBLES!
| PgmStateIndecies [U2] -- k x U2 -> Parse pin states?
| PgmStates [U1] -- k NIBBLES! -> String
| FailPin [U1] -- bitfield! -> [PinName]
| VectorName Text
| TimeSet Text
| OpCode Text
| Label Text
| ProgramText Text
| ResultText Text
| PatternGen U1 -- 255
| EnabledPins [U1] -- bitfield!
deriving (Generic, Show)
data TestType = Parametric
| Functional
| MultiResultParametric
| UnknownTestType
| OtherTestType C1
deriving (Generic, Show)
data WaferUnits = Inches
| Centimeters
| Millimeters
| Mils
| OtherUnits U1
deriving (Generic, Show)
data Direction = Up
| Down
| Left
| Right
| OtherDirection C1
deriving (Generic, Show)
data GdrField = GPad -- discard
| GU1 !U1
| GU2 !U2
| GU4 !U4
| GI1 !I1
| GI2 !I2
| GI4 !I4
| GFloat Float -- parse as CFloat
| GDouble Double -- parse as CDouble
| GStr Text
| GBytes [U1] -- encoded ByteStr
| GData [U1] -- 2byte length + encoded ByteStr
| GNibble !U1 -- a nibble? are you fucking kidding me?
deriving (Generic, Show)
data PartFlag = PartFlag { supersedesPartId :: Bool -- 1 bit
, supersedesXY :: Bool -- 1 bit
, abnormalEnd :: Bool
, failed :: Bool
, noPassFailInfo :: Bool }
deriving (Generic, Show)