# stdf4_to_xtdf1.pl # Copyright (C) 2005 Michael Hackerott. All Rights Reserved # # This program is free software; you can redistribute it and/or modify it # under the terms of either the GNU General Public License or the Artistic # License as specified in the Perl README file. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # This module is documented using POD in-line with the perl code and # is extracted using the pod2html utility: # # pod2html --infile=stdf4_to_xtdf1.pl --outfile=stdf4_to_xtdf1.html =pod =head1 NAME stdf4_to_xtdf1.pl - Converts an STDF version 4 file to an XTDF version 1 file =head1 SYNTAX perl stdf4_to_xtdf1.pl [OPTIONS] ARGUMENTS OPTIONS -a, --about Displays information about this program. -d, --debug Enables debug mode which outputs debug information to standard output. -h, --help Displays help for this program. ARGUMENTS StdfFileSpec The input STDF file specification. RETURNS The program returns a value of unix true (0) if no errors occur; otherwise, a value of unix false (1) is returned. STANDARD INPUT Standard input is ignored. STANDARD OUTPUT All output is sent to standard output. STANDARD ERROR If an error occurs then a message is output to standard error. =head1 SYNOPSIS perl stdf4_to_xtdf1.pl -a perl stdf4_to_xtdf1.pl --about Displays information about the program. perl stdf4_to_xtdf1.pl -h perl stdf4_to_xtdf1.pl --help Displays the help for the program. perl stdf4_to_xtdf1.pl StdfFileSpec Surveys the STDF file specified by StdfFileSpec and outputs the survey results to standard output. perl stdf4_to_xtdf1.pl -d StdfFileSpec perl stdf4_to_xtdf1.pl --debug StdfFileSpec Surveys the STDF file specified by StdfFileSpec and outputs the survey results and debug information to standard output. NOTE: Use command line redirection to capture standard output to a file. =head1 DESCRIPTION This program converts an STDF version 4 file to an XTDF version 1 file. An XTDF file is an XML version of a binary STDF file. The STDF field names and data types are emulated using XML as closely as possible to preserve the STDF file structure and content. Advantages of XTDF compared to STDF: + Compatible with standard XML utilities. + Easy to enhance and extend contents. + Platform independent ASCII. + Easy to read. + Easy to edit. Disadvatages of XTDF compared to STDF: - Larger file size. The official ASCII format for a binary STDF file is the ASCII Test Data Format (ATDF) defined by Teradyne. Advantages of XTDF compared to ATDF: + Preserves all STDF content (enables loss less conversion back to STDF). + Compatible with standard XML utilities. + Same syntax as STDF (ATDF has different syntax than STDF). + Easy to enhance and extend the syntax. Disadvatages of XTDF compared to ATDF: - Larger file size. =head2 Default Output The default output consists of the XTDF tags and data. 11:20:07 06-DEC-2005 FT_J750_112KB.stdf 113756 2 4 17:12:18 02-NOV-2005 17:12:18 02-NOV-2005 0 P Y 65535 TJMEA0360Y00 MC9S08GT60CFD J750-08 J750 Final_Test_44qfp 9S08GB60_J05 HR268 IG-XL 3.40.09 FCN -40 MC9S08GT60CFD TJN L31R 20 MC9S08GT60CFD_FCN 9999 ... 18:12:44 02-NOV-2005 0 =head2 Additional Debug Output If the debug mode is enabled then debug information is output in addition to the default XTDF output. (NOTE: debug mode increases the runtime time and amount of output by approximately 15 to 25 times!) %gOptions = ('debug' => 1); $gStdfFile{'SPEC'} = '..\\data\\inp\\STDF\\FT_J750_112KB.stdf'; $gStdfFile{'PATH'} = '..\\data\\inp\\STDF'; $gStdfFile{'NAME'} = 'FT_J750_112KB.stdf'; $gStdfFile{'SIZE'} = 113756; 11:39:05 06-DEC-2005 FT_J750_112KB.stdf 113756 $gStdfFile{'ABO'} = 0; $gStdfRec{'LENBIN'} = 0000 00000010 02 2 ¤ 0001 00000000 00 0 ¤ $stdfRecordRead::status = 2; $gStdfRec{'LENHEX'} = '0200'; $gStdfFile{'ENDIAN'} = 1; $stdfRecordRead::lenhex = '0002'; $gStdfRec{'LENDEC'} = '2'; $gStdfRec{'TYPBIN'} = 0000 00000000 00 0 ¤ $gStdfRec{'TYPHEX'} = '0'; $gStdfRec{'TYPDEC'} = 0; $gStdfRec{'SUBBIN'} = 0000 00001010 0A 10 ¤ $gStdfRec{'SUBHEX'} = '0'; $gStdfRec{'SUBDEC'} = 10; $gStdfRecName = 'FAR'; $gStdfRec{'DATBIN'} = 0000 00000010 02 2 ¤ 0001 00000100 04 4 ¤ $gStdfRec{'DATHEX'} = '0204'; $gStdfRec{'DATTXT'} = '°°'; $gStdfFld{'LEN'} = 1 $gStdfRec{'ABO'} = 0 $gStdfFld{'BIN'} =  $gStdfFld{'HEX'} = 02 $gStdfFld{'DAT'} = 2 $gStdfFld{'LEN'} = 1 $gStdfRec{'ABO'} = 1 $gStdfFld{'BIN'} =  $gStdfFld{'HEX'} = 04 $gStdfFld{'DAT'} = 4 2 4 $gStdfFile{'ABO'} = 6; $gStdfRec{'LENBIN'} = 0000 10101010 AA 170 ¤ 0001 00000000 00 0 ¤ $stdfRecordRead::status = 2; $gStdfRec{'LENHEX'} = 'AA00'; $stdfRecordRead::lenhex = '00AA'; $gStdfRec{'LENDEC'} = '170'; $gStdfRec{'TYPBIN'} = 0000 00000001 01 1 ¤ $gStdfRec{'TYPHEX'} = '0'; $gStdfRec{'TYPDEC'} = 1; $gStdfRec{'SUBBIN'} = 0000 00001010 0A 10 ¤ $gStdfRec{'SUBHEX'} = '0'; $gStdfRec{'SUBDEC'} = 10; $gStdfRecName = 'MIR'; ... $gStdfFld{'LEN'} = 4 $gStdfRec{'ABO'} = 0 $gStdfFld{'BIN'} = œiC $gStdfFld{'HEX'} = 9C016943 $gStdfFld{'DAT'} = 1130955164 $gStdfFld{'LEN'} = 1 $gStdfRec{'ABO'} = 4 $gStdfFld{'BIN'} = $gStdfFld{'HEX'} = $gStdfFld{'DAT'} = 18:12:44 02-NOV-2005 0 =cut BEGIN { unshift( @INC, 'lib', '../lib' ); }; use TDF; use File::Basename; use strict; ###################################################################### # PROGRAM CONSTANT DECLARATIONS ###################################################################### # set the program name my $PROGRAM = 'stdf4_to_xtdf1.pl'; # set the version number my $VERSION = '1.0.0'; use constant TRUE => 1; use constant FALSE => 0; use constant EOL => "\n"; use constant Q1 => "\'"; use constant Q2 => "\""; use constant SPC => "\ "; use constant TAB => "\t"; use constant ENDIAN_LITTLE => 0; use constant ENDIAN_BIG => 1; my %STDF_TO_ATDF_RECORD_SUBREF = ( 'ATR' => \&ATR, 'BPS' => \&BPS, 'DTR' => \&DTR, 'EPS' => \&EPS, 'FAR' => \&FAR, 'FTR' => \&FTR, 'GDR' => \&GDR, 'HBR' => \&HBR, 'MIR' => \&MIR, 'MPR' => \&MPR, 'MRR' => \&MRR, 'PCR' => \&PCR, 'PGR' => \&PGR, 'PIR' => \&PIR, 'PLR' => \&PLR, 'PMR' => \&PMR, 'PRR' => \&PRR, 'PTR' => \&PTR, 'RDR' => \&RDR, 'SBR' => \&SBR, 'SDR' => \&SDR, 'TSR' => \&TSR, 'WCR' => \&WCR, 'WIR' => \&WIR, 'WRR' => \&WRR, ); ###################################################################### # PROGRAM VARIABLE DECLARATIONS ###################################################################### my @gTimer = (time()); my %gEndian = ( 'STDF' => undef, 'XTDF' => endianRuntime(), ); my %gOptions = ( 'debug' => FALSE ); # STDF File Data my %gStdfFile = ( 'ABO' => 0, 'RECNUM' => 0, 'PATH' => 0, 'NAME' => 0, 'SPEC' => 0, ); # STDF Record Data my %gStdfRec = ( 'ABO' => undef, 'NAME' => undef, ); # STDF Field Data my %gStdfFld = ( 'LEN' => undef, 'BIN' => undef, 'HEX' => undef, 'DEC' => undef, 'DAT' => undef, ); my @gXML = (); ###################################################################### # MODULE MACRO DECLARATIONS ###################################################################### sub q1 {return(Q1.join(SPC, @_).Q1)}; sub q2 {return(Q2.join(SPC, @_).Q2)}; ###################################################################### # MAIN ###################################################################### # enable autoflush $| = TRUE; # process the unix command line options while ($ARGV[0] =~ m/^\-/) { my $option = shift(@ARGV); if ($option =~ m/^\-(a|\-about)/) { about(); } elsif ($option =~ m/^\-(d|\-debug)/) { $gOptions{'debug'} = TRUE; } elsif ($option =~ m/^\-(h|\-help)/) { help(); } elsif ($option =~ m/^\-(l|\-log)/) { $gOptions{'log'} = TRUE; }; }; dbugData([\%gOptions], ['*gOptions']); # get the ATDF file specification $gStdfFile{'SPEC'} = shift(@ARGV); if ($gStdfFile{'SPEC'} eq '') { fatal('STDF file specification is not defined!'); } elsif (! -f $gStdfFile{'SPEC'}) { fatal( 'STDF file does not exist:', $gStdfFile{'SPEC'} ); }; dbugData([$gStdfFile{'SPEC'}], ["*gStdfFile{'SPEC'}"]); $gStdfFile{'PATH'} = dirname($gStdfFile{'SPEC'}); dbugData([$gStdfFile{'PATH'}], ["*gStdfFile{'PATH'}"]); $gStdfFile{'NAME'} = basename($gStdfFile{'SPEC'}); dbugData([$gStdfFile{'NAME'}], ["*gStdfFile{'NAME'}"]); $gStdfFile{'SIZE'} = (-s $gStdfFile{'SPEC'}); dbugData([$gStdfFile{'SIZE'}], ["*gStdfFile{'SIZE'}"]); # open the STDF file for read as binary if (! open(FILE_STDF, '<'.$gStdfFile{'SPEC'})) { fatal( 'Failed to open for read STDF file:', $gStdfFile{'SPEC'} ); }; binmode(FILE_STDF); @gXML = ( "", xmlComment($PROGRAM, 'V'.$VERSION), xmlComment('Copyright (C) 2005 Michael Hackerott'), xmlComment('All Rights Reserved'), xmlTagBgn('XTDF'), xmlTag('CREATED', {}, secondsToDateTime($gTimer[0])), xmlTag('FILENAME', {'TYPE' => 'INPUT'}, $gStdfFile{'NAME'}), xmlTag( 'FILESIZE', {'TYPE' => 'INPUT', 'UNIT' => 'BYTES'}, $gStdfFile{'SIZE'} ), xmlTagBgn('STDF') ); xtdfPrintList(@gXML); # # READ THE FIRST RECORD IN THE STDF FILE AND TEST TO # VALIDATE THAT IT IS AN FAR RECORD # # read the first STDF record stdfRecordRead(); # process the first STDF record xtdfPrintLine(xmlTagBgn($gStdfRec{'NAME'})); FAR(); xtdfPrintLine(xmlTagEnd($gStdfRec{'NAME'})); # # PROCESS THE REST OF THE STDF FILE RECORDS # # read the STDF file while (! eof(FILE_STDF)) { # read the next STDF record stdfRecordRead(); # test the STDF record name if ($gStdfRec{'NAME'} ne '') { # convert the STDF record data fields to ATDF xtdfPrintLine(xmlTagBgn($gStdfRec{'NAME'})); $STDF_TO_ATDF_RECORD_SUBREF{$gStdfRec{'NAME'}}->(); xtdfPrintLine(xmlTagEnd($gStdfRec{'NAME'})); }; }; # close the STDF file close(FILE_STDF); push(@gTimer, time()); # calculate the elapsed seconds $gTimer[2] = $gTimer[1] - $gTimer[1]; @gXML = ( xmlTagEnd('STDF'), xmlTag( 'ELAPSED', {'UNIT' => 'SECONDS'}, $gTimer[2] ), xmlTagEnd('XTDF') ); xtdfPrintList(@gXML); # exit to unix without error exit(0); ###################################################################### # SUBROUTINES ###################################################################### # about() sub about { TDF::about($PROGRAM, $VERSION) }; # $status = dbugEnabled() sub dbugEnabled { return($gOptions{'debug'}); }; # dbugData(dbugData(\@varRefs, \@varNames)) sub dbugData { dbugEnabled() && TDF::dbugData(@_); }; # dbugDataBin($datnam, $bindat) sub dbugDataBin { dbugEnabled() && TDF::dbugDataBin(@_); }; # dbugDataPurdy(\@varRefs, \@varNames) sub dbugDataPurdy { dbugEnabled() && TDF::dbugDataPurdy(@_); }; # dbugSub($subname, @_) sub dbugSub { dbugEnabled() && TDF::dbugSub(@_); }; # dbugText(@text) sub dbugText { dbugEnabled() && TDF::dbugText(@_); }; # help() sub help { print <<"END_HELP"; SEE ALSO The program documentation for additional information. END_HELP # exit to unix without error exit(0); }; # endianReverse(\$bytes) sub endianReverse { my $rsBytes = shift(); if ((length(${$rsBytes}) > 1) && ($gEndian{'STDF'} != $gEndian{'XTDF'})) { ${$rsBytes} = join('', reverse(split(//, ${$rsBytes}))); }; }; # $endianCode = endianRuntime() sub endianRuntime { my $testBytes = join(' ', map { sprintf "%#02x", $_ } unpack("C*", pack("L", 0x12345678)) ); if ($testBytes eq '0x12 0x34 0x56 0x78') { return(1); # BIG }; return(0); # LITTLE }; # $status = scrubNonPrintChars(\$text) # # WARNING: this subroutine modifies the text value in place replacing # the original contents with the scrubbed contents! sub scrubNonPrintChars { # get subroutine argument(s) my $rsText = shift(); # define local variables my $i = undef; # current record index my $n = undef; # number of records in the list my $statusBS = FALSE; # backspace status: default is not scrubbed my $statusNP = FALSE; # non-print status: default is not scrubbed # convert the text string to a list of characters my @text = split(//, ${$rsText}); # get the index of the last element in the list $n = $#text; # scrub each record in the list for $i (0 .. $n) { # process the special case 'backspace' character where # not only the non-printing backspace character must be # deleted but also the character preceeding the backspace # character. while ($text[$i] =~ s/(.\x08){1}/\xB0/) {$statusBS = 1;}; # test if the scalar contains: NUL, SOH, STX, # ETX, EOT, ENQ, ACK, BEL, BS, VT, FF, SO, SI, DLE, DC1, DC2, DC3 # DC4, NAK, SYN, ETB, CAN, EM, SUB, ESC, FS, GS, RS, US, or any # ASCII character in the range of hex 7F to FF and delete them. #$statusNP = ($text[$i] =~ s/[\x00-\x07\x0B-\x0C\x0E-\x1F\x7F-\xFF]/\xB0/g); $statusNP = ($text[$i] =~ s/[\x00-\x20\x7F-\xFF]/\xB0/g); }; # set the text to the scrubbed value ${$rsText} = join('', @text); # return scrub status return($statusBS || $statusNP); }; # stdfParseFldB1() sub stdfParseFldB1 { stdfRecToFld(1, 'B*'); return($gStdfFld{'DAT'}); }; # stdfParseFldBn() sub stdfParseFldBn { stdfRecToFld(stdfParseFldU1(), 'B*'); return($gStdfFld{'DAT'}); }; # stdfParseFldC1() sub stdfParseFldC1 { stdfRecToFld(1, 'A*'); return($gStdfFld{'DAT'}); }; # stdfParseFldCn() sub stdfParseFldCn { stdfRecToFld(stdfParseFldU1(), 'A*'); return($gStdfFld{'DAT'}); }; # stdfParseFldDn() sub stdfParseFldDn { my $bitCnt = stdfParseFldU2(); dbugData([$bitCnt], ['*stdfParseFldDn::bitCnt']); my $bytCnt = int($bitCnt / 8) + ($bitCnt % 8); dbugData([$bytCnt], ['*stdfParseFldDn::bytCnt']); stdfRecToFld($bytCnt, 'B*'); return($gStdfFld{'DAT'}); }; # stdfParseFldI1() sub stdfParseFldI1 { stdfRecToFld(1, 'C'); return($gStdfFld{'DAT'}); }; # stdfParseFldI2() sub stdfParseFldI2 { stdfRecToFld(2, 's*'); return($gStdfFld{'DAT'}); }; # stdfParseFldI4() sub stdfParseFldI4 { stdfRecToFld(4, 'i*'); return($gStdfFld{'DAT'}); }; # stdfParseFldN1() sub stdfParseFldN1 { stdfRecToFld(1, 'H*'); return('0x'.uc($gStdfFld{'DAT'})); }; # stdfParseFldR4() sub stdfParseFldR4 { stdfRecToFld(4, 'f*'); return($gStdfFld{'DAT'}); }; # stdfParseFldU1() sub stdfParseFldU1 { stdfRecToFld(1, 'C'); return($gStdfFld{'DAT'}); }; # stdfParseFldU2() sub stdfParseFldU2 { stdfRecToFld(2, 'S*'); return($gStdfFld{'DAT'}); }; # stdfParseFldU4() sub stdfParseFldU4 { stdfRecToFld(4, 'L*'); return($gStdfFld{'DAT'}); }; # stdfParseFldVn($fldCnt) sub stdfParseFldVn { my $fldCnt = shift(); my $fldNum = undef; for $fldNum (1 .. $fldCnt) { my $typCod = stdfParseFldU1(); if ($typCod == 0) # B0 { } elsif ($typCod == 1) # U1 { } elsif ($typCod == 2) # U2 { } elsif ($typCod == 3) # U4 { } elsif ($typCod == 4) # I1 { } elsif ($typCod == 5) # I2 { } elsif ($typCod == 6) # I4 { } elsif ($typCod == 7) # R4 { } elsif ($typCod == 8) # R8 { } elsif ($typCod == 10) # Cn { } elsif ($typCod == 11) # Bn { } elsif ($typCod == 12) # Dn { } elsif ($typCod == 13) # N1 { }; }; }; # stdfRecToFld($fldLenInBytes) sub stdfRecToFld { %gStdfFld = ( 'LEN' => shift(), 'TMP' => shift(), 'BIN' => undef, 'HEX' => undef, 'DAT' => undef, ); if ($gStdfRec{'ABO'} <= $gStdfRec{'LENDEC'}) { dbugText("\$gStdfFld{'LEN'}", '=', $gStdfFld{'LEN'}); dbugText("\$gStdfRec{'ABO'}", '=', $gStdfRec{'ABO'}); $gStdfFld{'BIN'} = substr( $gStdfRec{'DATBIN'}, $gStdfRec{'ABO'}, $gStdfFld{'LEN'}, ); dbugText("\$gStdfFld{'BIN'}", '=', $gStdfFld{'BIN'}); endianReverse(\$gStdfFld{'BIN'}); $gStdfFld{'HEX'} = uc(unpack( 'H*', $gStdfFld{'BIN'} )); dbugText("\$gStdfFld{'HEX'}", '=', $gStdfFld{'HEX'}); $gStdfFld{'DAT'} = unpack($gStdfFld{'TMP'}, $gStdfFld{'BIN'}); dbugText("\$gStdfFld{'DAT'}", '=', $gStdfFld{'DAT'}); }; $gStdfRec{'ABO'} += $gStdfFld{'LEN'}; }; # stdfRecordRead() sub stdfRecordRead { # declare local variables %gStdfRec = ( 'ABO' => 0, ); # get the STDF file byte offset $gStdfFile{'ABO'} = tell(FILE_STDF); dbugData([$gStdfFile{'ABO'}], ["*gStdfFile{'ABO'}"]); # read REC_LEN U*2 my $status = read(FILE_STDF, $gStdfRec{'LENBIN'}, 2); dbugDataBin("\$gStdfRec{'LENBIN'}", $gStdfRec{'LENBIN'}); dbugData([$status], ['*stdfRecordRead::status']); # unpack REC_LEN U*2 as hex $gStdfRec{'LENHEX'} = uc(unpack('H4', $gStdfRec{'LENBIN'})); dbugData([$gStdfRec{'LENHEX'}], ["\$gStdfRec{'LENHEX'}"]); # test for first record in file if ($gStdfFile{'ABO'} == 0) { # determine endian $gStdfFile{'ENDIAN'} = 0; # LITTLE if ($gStdfRec{'LENHEX'} eq '0200') { $gStdfFile{'ENDIAN'} = 1; # BIG }; dbugData([$gStdfFile{'ENDIAN'}], ["\$gStdfFile{'ENDIAN'}"]); }; # unpack REC_LEN U*1 as dec my $lenhex = $gStdfRec{'LENHEX'}; if ($gStdfFile{'ENDIAN'} == 1) # BIG ENDIAN { $lenhex = substr($lenhex, 2, 2).substr($lenhex, 0, 2); }; dbugData([$lenhex], ['$stdfRecordRead::lenhex']); $gStdfRec{'LENDEC'} = hex('0x'.$lenhex); dbugData([$gStdfRec{'LENDEC'}], ["\$gStdfRec{'LENDEC'}"]); # read REC_TYP U*1 read(FILE_STDF, $gStdfRec{'TYPBIN'}, 1); dbugDataBin("\$gStdfRec{'TYPBIN'}", $gStdfRec{'TYPBIN'}); # unpack REC_TYP U*1 as hex $gStdfRec{'TYPHEX'} = uc(unpack('H1', $gStdfRec{'TYPBIN'})); dbugData([$gStdfRec{'TYPHEX'}], ["\$gStdfRec{'TYPHEX'}"]); # unpack REC_TYP U*1 as dec $gStdfRec{'TYPDEC'} = unpack('C1', $gStdfRec{'TYPBIN'}); dbugData([$gStdfRec{'TYPDEC'}], ["\$gStdfRec{'TYPDEC'}"]); # read REC_SUB U*1 read(FILE_STDF, $gStdfRec{'SUBBIN'}, 1); dbugDataBin("\$gStdfRec{'SUBBIN'}", $gStdfRec{'SUBBIN'}); # unpack REC_SUB U*1 as hex $gStdfRec{'SUBHEX'} = uc(unpack('H1', $gStdfRec{'SUBBIN'})); dbugData([$gStdfRec{'SUBHEX'}], ["\$gStdfRec{'SUBHEX'}"]); # unpack REC_SUB U*1 as dec $gStdfRec{'SUBDEC'} = unpack('C1', $gStdfRec{'SUBBIN'}); dbugData([$gStdfRec{'SUBDEC'}], ["\$gStdfRec{'SUBDEC'}"]); # set the STDF record name $gStdfRec{'NAME'} = stdfRecordTypeCodesToName( $gStdfRec{'TYPDEC'}, $gStdfRec{'SUBDEC'} ); dbugData([$gStdfRec{'NAME'}], ['*gStdfRecName']); # test the STDF record name if ($gStdfRec{'NAME'} eq '') { fatal( 'Unknown header', 'record type', $gStdfRec{'TYPDEC'}, 'and/or', 'record sub-type', $gStdfRec{'SUBDEC'}, 'at byte offset', $gStdfFile{'ABO'}, 'in STDF file', $gStdfFile{'SPEC'} ); }; # read the STDF record data read(FILE_STDF, $gStdfRec{'DATBIN'}, $gStdfRec{'LENDEC'}); dbugDataBin("\$gStdfRec{'DATBIN'}", $gStdfRec{'DATBIN'}); # unpack STDF record data as hex $gStdfRec{'DATHEX'} = uc(unpack('H*', $gStdfRec{'DATBIN'})); dbugData([$gStdfRec{'DATHEX'}], ["\$gStdfRec{'DATHEX'}"]); # unpack STDF record data as hex $gStdfRec{'DATTXT'} = unpack('A*', $gStdfRec{'DATBIN'}); scrubNonPrintChars(\$gStdfRec{'DATTXT'}); dbugData([$gStdfRec{'DATTXT'}], ["\$gStdfRec{'DATTXT'}"]); # count the read records $gStdfFile{'RECNUM'} += 1; }; # xmlComment(@text) sub xmlComment { return(join(SPC, '')); }; # xmlList(@list) sub xmlList { return(join(',', @_)); }; # xmlTag($name[, \%attributes[, $value]]) sub xmlTag { my $name = shift(); my $rhAttributes = shift(); my $value = shift(); my $tagBgn = xmlTagBgn($name, $rhAttributes); my $tagEnd = xmlTagEnd($name); return($tagBgn.$value.$tagEnd); }; # xmlTagBgn($name[, \%attributes]) sub xmlTagBgn { my $name = shift(); my $rhAttributes = shift(); my $tag = join(SPC, $name, map( $_.'='.q2($rhAttributes->{$_}), sort(keys(%{$rhAttributes})) ) ); return('<'.$tag.'>'); }; # xmlTagEnd($name) sub xmlTagEnd { my $name = shift(); my $tag = $name; return(''); }; # xtdfPrintLine(@xtdf) sub xtdfPrintLine { print(join(SPC, @_).EOL); }; # xtdfPrintList(@xtdf) sub xtdfPrintList { print(join(EOL, @_).EOL); }; #===================================================================== # STDF TO ATDF RECORD SUBROUTINES #===================================================================== # ATR() sub ATR { my @xtdfFlds = ( xmlTag('MOD_TIM', {}, stdfParseFldU4()), xmlTag('CMD_LINE', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # BPS() sub BPS { my @xtdfFlds = ( xmlTag('SEQ_NAME', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # DTR() sub DTR { my @xtdfFlds = ( xmlTag('TEXT_DAT', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # EPS() sub EPS { }; # FAR() sub FAR { my @xtdfFlds = ( xmlTag('CPU_TYPE', {}, stdfParseFldU1()), xmlTag('STDF_VER', {}, stdfParseFldU1()), ); xtdfPrintList(@xtdfFlds); }; # FTR() sub FTR { my @xtdfFlds = ( xmlTag('TEST_NUM', {}, stdfParseFldU4()), xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('TEST_FLG', {}, stdfParseFldB1()), xmlTag('OPT_FLG', {}, stdfParseFldB1()), xmlTag('CYCL_CNT', {}, stdfParseFldU4()), xmlTag('REL_VADR', {}, stdfParseFldU4()), xmlTag('REPT_CNT', {}, stdfParseFldU4()), xmlTag('NUM_FAIL', {}, stdfParseFldU4()), xmlTag('XFAIL_AD', {}, stdfParseFldI4()), xmlTag('YFAIL_AD', {}, stdfParseFldI4()), xmlTag('VECT_OFF', {}, stdfParseFldI2()), ); my $i = undef; # RTN_ICNT my $j = stdfParseFldU2(); # PGM_ICNT my $k = stdfParseFldU2(); my @RTN_INDX = (); my @RTN_STAT = (); if ($j > 0) { # RTN_INDX for $i (1 .. $j) { push(@RTN_INDX, stdfParseFldU2()); }; # RTN_STAT for $i (1 .. $j) { push(@RTN_STAT, stdfParseFldN1()); }; }; my @PGM_INDX = (); my @PGM_STAT = (); if ($k > 0) { # PGM_INDX for $i (1 .. $k) { push(@PGM_INDX, stdfParseFldU2()); }; # PGM_STAT for $i (1 .. $k) { push(@PGM_STAT, stdfParseFldN1()); }; }; push(@xtdfFlds, xmlTag('RTN_ICNT', {}, $j), xmlTag('PGM_ICNT', {}, $k), xmlTag('RTN_INDX', {}, xmlList(@RTN_INDX)), xmlTag('RTN_STAT', {}, xmlList(@RTN_STAT)), xmlTag('PGM_INDX', {}, xmlList(@PGM_INDX)), xmlTag('PGM_STAT', {}, xmlList(@PGM_STAT)), xmlTag('FAIL_PIN', {}, stdfParseFldDn()), xmlTag('VECT_NAM', {}, stdfParseFldCn()), xmlTag('TIME_SET', {}, stdfParseFldCn()), xmlTag('OP_CODE', {}, stdfParseFldCn()), xmlTag('TEST_TXT', {}, stdfParseFldCn()), xmlTag('ALARM_ID', {}, stdfParseFldCn()), xmlTag('PROG_TXT', {}, stdfParseFldCn()), xmlTag('RSLT_TXT', {}, stdfParseFldCn()), xmlTag('PATG_NUM', {}, stdfParseFldU1()), xmlTag('SPIN_MAP', {}, stdfParseFldDn()), ); xtdfPrintList(@xtdfFlds); }; # GDR() sub GDR { my @xtdfFlds = ( xmlTag('FLD_CNT', {}, stdfParseFldU2()), xmlTag('GEN_DATA', {}, stdfParseFldVn()), ); xtdfPrintList(@xtdfFlds); }; # HBR() sub HBR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('HBIN_NUM', {}, stdfParseFldU2()), xmlTag('HBIN_CNT', {}, stdfParseFldU4()), xmlTag('HBIN_PF', {}, stdfParseFldC1()), xmlTag('HBIN_NAM', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # MIR() sub MIR { my @xtdfFlds = ( xmlTag('SETUP_T', {}, secondsToDateTime(stdfParseFldU4())), xmlTag('START_T', {}, secondsToDateTime(stdfParseFldU4())), xmlTag('STAT_NUM', {}, stdfParseFldU1()), xmlTag('MODE_COD', {}, stdfParseFldC1()), xmlTag('RTST_COD', {}, stdfParseFldC1()), xmlTag('PROD_COD', {}, stdfParseFldC1()), xmlTag('BURN_TIM', {}, stdfParseFldU2()), xmlTag('CMOD_COD', {}, stdfParseFldC1()), xmlTag('LOT_ID', {}, stdfParseFldCn()), xmlTag('PART_TYP', {}, stdfParseFldCn()), xmlTag('NODE_NAM', {}, stdfParseFldCn()), xmlTag('TSTR_TYP', {}, stdfParseFldCn()), xmlTag('JOB_NAM', {}, stdfParseFldCn()), xmlTag('JOB_REV', {}, stdfParseFldCn()), xmlTag('SBLOT_ID', {}, stdfParseFldCn()), xmlTag('OPER_NAM', {}, stdfParseFldCn()), xmlTag('EXEC_TYP', {}, stdfParseFldCn()), xmlTag('EXEC_VER', {}, stdfParseFldCn()), xmlTag('TEST_COD', {}, stdfParseFldCn()), xmlTag('TST_TEMP', {}, stdfParseFldCn()), xmlTag('USER_TXT', {}, stdfParseFldCn()), xmlTag('AUX_FILE', {}, stdfParseFldCn()), xmlTag('PKG_TYP', {}, stdfParseFldCn()), xmlTag('FAMLY_ID', {}, stdfParseFldCn()), xmlTag('DATE_COD', {}, stdfParseFldCn()), xmlTag('FACIL_ID', {}, stdfParseFldCn()), xmlTag('FLOOR_ID', {}, stdfParseFldCn()), xmlTag('PROC_ID', {}, stdfParseFldCn()), xmlTag('OPER_FRQ', {}, stdfParseFldCn()), xmlTag('SPEC_NAM', {}, stdfParseFldCn()), xmlTag('SPEC_VER', {}, stdfParseFldCn()), xmlTag('FLOW_ID', {}, stdfParseFldCn()), xmlTag('SETUP_ID', {}, stdfParseFldCn()), xmlTag('DSGN_REV', {}, stdfParseFldCn()), xmlTag('ENG_ID', {}, stdfParseFldCn()), xmlTag('ROM_COD', {}, stdfParseFldCn()), xmlTag('SERL_NUM', {}, stdfParseFldCn()), xmlTag('SUPR_NAM', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # MPR() sub MPR { my @xtdfFlds = ( xmlTag('TEST_NUM', {}, stdfParseFldU4()), xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('TEST_FLG', {}, stdfParseFldB1()), xmlTag('PARM_FLG', {}, stdfParseFldB1()), ); my $i = undef; # RTN_ICNT my $j = stdfParseFldU2(); # RSLT_CNT my $k = stdfParseFldU2(); my @RTN_STAT = (); if ($j > 0) { # RTN_STAT for $i (1 .. $j) { push(@RTN_STAT, stdfParseFldN1()); }; }; my @RTN_RSLT = (); if ($k > 0) { # RTN_RSLT for $i (1 .. $k) { push(@RTN_RSLT, stdfParseFldR4()); }; }; push(@xtdfFlds, xmlTag('RTN_ICNT', {}, $j), xmlTag('RSLT_CNT', {}, $k), xmlTag('RTN_STAT', {}, xmlList(@RTN_STAT)), xmlTag('RTN_RSLT', {}, xmlList(@RTN_RSLT)), xmlTag('TEST_TXT', {}, stdfParseFldCn()), xmlTag('ALARM_ID', {}, stdfParseFldCn()), xmlTag('OPT_FLG', {}, stdfParseFldB1()), xmlTag('RES_SCAL', {}, stdfParseFldI1()), xmlTag('LLM_SCAL', {}, stdfParseFldI1()), xmlTag('HLM_SCAL', {}, stdfParseFldI1()), xmlTag('LO_LIMIT', {}, stdfParseFldR4()), xmlTag('HI_LIMIT', {}, stdfParseFldR4()), xmlTag('START_IN', {}, stdfParseFldR4()), xmlTag('INCR_IN', {}, stdfParseFldR4()), ); # RTN_INDX my @RTN_INDX = (); if ($j > 0) { for $i (1 .. $j) { push(@RTN_INDX, stdfParseFldU2()); }; }; push(@xtdfFlds, xmlTag('RTN_INDX', {}, xmlList(@RTN_INDX)), xmlTag('UNITS', {}, stdfParseFldCn()), xmlTag('UNITS_IN', {}, stdfParseFldCn()), xmlTag('C_RESFMT', {}, stdfParseFldCn()), xmlTag('C_LLMFMT', {}, stdfParseFldCn()), xmlTag('C_HLMFMT', {}, stdfParseFldCn()), xmlTag('LO_SPEC', {}, stdfParseFldR4()), xmlTag('HI_SPEC', {}, stdfParseFldR4()), ); xtdfPrintList(@xtdfFlds); }; # MRR() sub MRR { my @xtdfFlds = ( xmlTag('FINISH_T', {}, secondsToDateTime(stdfParseFldU4())), xmlTag('DISP_COD', {}, stdfParseFldC1()), xmlTag('USR_DESC', {}, stdfParseFldCn()), xmlTag('EXC_DESC', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # PCR() sub PCR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('PART_CNT', {}, stdfParseFldU4()), xmlTag('RTST_CNT', {}, stdfParseFldU4()), xmlTag('ABRT_CNT', {}, stdfParseFldU4()), xmlTag('GOOD_CNT', {}, stdfParseFldU4()), xmlTag('FUNC_CNT', {}, stdfParseFldU4()), ); xtdfPrintList(@xtdfFlds); }; # PGR() sub PGR { my @xtdfFlds = ( xmlTag('GRP_INDX', {}, stdfParseFldU2()), xmlTag('GRP_NAM', {}, stdfParseFldCn()), ); my $i = undef; # INDX_CNT my $k = stdfParseFldU2(); my @PMR_INDX = (); if ($k > 0) { # PMR_INDX for $i (1 .. $k) { push(@PMR_INDX, stdfParseFldU2()); }; }; push(@xtdfFlds, xmlTag('INDX_CNT', {}, $k), xmlTag('PMR_INDX', {}, xmlList(@PMR_INDX)), ); xtdfPrintList(@xtdfFlds); }; # PIR() sub PIR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), ); xtdfPrintList(@xtdfFlds); }; # PLR() sub PLR { my $i = undef; # GRP_CNT my $k = stdfParseFldU2(); my @GRP_INDX = (); my @GRP_MODE = (); my @GRP_RADX = (); my @PGM_CHAR = (); my @RTN_CHAR = (); my @PGM_CHAL = (); my @RTN_CHAL = (); if ($k > 0) { # GRP_INDX for $i (1 .. $k) { push(@GRP_INDX, stdfParseFldU2()); }; # GRP_MODE for $i (1 .. $k) { push(@GRP_MODE, stdfParseFldU2()); }; # GRP_RADX for $i (1 .. $k) { push(@GRP_RADX, stdfParseFldU1()); }; # PGM_CHAR for $i (1 .. $k) { push(@PGM_CHAR, stdfParseFldCn()); }; # RTN_CHAR for $i (1 .. $k) { push(@RTN_CHAR, stdfParseFldCn()); }; # PGM_CHAL for $i (1 .. $k) { push(@PGM_CHAL, stdfParseFldCn()); }; # RTN_CHAL for $i (1 .. $k) { push(@RTN_CHAL, stdfParseFldCn()); }; }; my @xtdfFlds = ( xmlTag('GRP_CNT', {}, $k), xmlTag('GRP_INDX', {}, xmlList(@GRP_INDX)), xmlTag('GRP_MODE', {}, xmlList(@GRP_MODE)), xmlTag('GRP_RADX', {}, xmlList(@GRP_RADX)), xmlTag('PGM_CHAR', {}, xmlList(@PGM_CHAR)), xmlTag('RTN_CHAR', {}, xmlList(@RTN_CHAR)), xmlTag('PGM_CHAL', {}, xmlList(@PGM_CHAL)), xmlTag('RTN_CHAL', {}, xmlList(@RTN_CHAL)), ); xtdfPrintList(@xtdfFlds); }; # PMR() sub PMR { my @xtdfFlds = ( xmlTag('PMR_INDX', {}, stdfParseFldU2()), xmlTag('CHAN_TYP', {}, stdfParseFldU2()), xmlTag('CHAN_NAM', {}, stdfParseFldCn()), xmlTag('PHY_NAM', {}, stdfParseFldCn()), xmlTag('LOG_NAM', {}, stdfParseFldCn()), xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), ); xtdfPrintList(@xtdfFlds); }; # PRR() sub PRR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('PART_FLG', {}, stdfParseFldB1()), xmlTag('NUM_TEST', {}, stdfParseFldU2()), xmlTag('HARD_BIN', {}, stdfParseFldU2()), xmlTag('SOFT_BIN', {}, stdfParseFldU2()), xmlTag('X_COORD', {}, stdfParseFldI2()), xmlTag('Y_COORD', {}, stdfParseFldI2()), xmlTag('TEST_T', {}, stdfParseFldU4()), xmlTag('PART_ID', {}, stdfParseFldCn()), xmlTag('PART_TXT', {}, stdfParseFldCn()), xmlTag('PART_FIX', {}, stdfParseFldBn()), ); xtdfPrintList(@xtdfFlds); }; # PTR() sub PTR { my @xtdfFlds = ( xmlTag('TEST_NUM', {}, stdfParseFldU4()), xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('TEST_FLG', {}, stdfParseFldB1()), xmlTag('PARM_FLG', {}, stdfParseFldB1()), xmlTag('RESULT', {}, stdfParseFldR4()), xmlTag('TEST_TXT', {}, stdfParseFldCn()), xmlTag('ALARM_ID', {}, stdfParseFldCn()), xmlTag('OPT_FLG', {}, stdfParseFldB1()), xmlTag('RES_SCAL', {}, stdfParseFldI1()), xmlTag('LLM_SCAL', {}, stdfParseFldI1()), xmlTag('HLM_SCAL', {}, stdfParseFldI1()), xmlTag('LO_LIMIT', {}, stdfParseFldR4()), xmlTag('HI_LIMIT', {}, stdfParseFldR4()), xmlTag('UNITS', {}, stdfParseFldCn()), xmlTag('C_RESFMT', {}, stdfParseFldCn()), xmlTag('C_LLMFMT', {}, stdfParseFldCn()), xmlTag('C_HLMFMT', {}, stdfParseFldCn()), xmlTag('LO_SPEC', {}, stdfParseFldR4()), xmlTag('HI_SPEC', {}, stdfParseFldR4()), ); xtdfPrintList(@xtdfFlds); }; # RDR() sub RDR { my $i = undef; # NUM_BINS my $k = stdfParseFldU2(); my @RTST_BIN = (); if ($k > 0) { # GRP_INDX for $i (1 .. $k) { push(@RTST_BIN, stdfParseFldU2()); }; }; my @xtdfFlds = ( xmlTag('NUM_BINS', {}, $k), xmlTag('RTST_BIN', {}, xmlList(@RTST_BIN)), ); xtdfPrintList(@xtdfFlds); }; # SBR() sub SBR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('SBIN_NUM', {}, stdfParseFldU2()), xmlTag('SBIN_CNT', {}, stdfParseFldU4()), xmlTag('SBIN_PF', {}, stdfParseFldC1()), xmlTag('SBIN_NAM', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # SDR() sub SDR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_GRP', {}, stdfParseFldU1()), ); my $i = undef; # SITE_CNT my $k = stdfParseFldU1(); my @SITE_NUM = (); if ($k > 0) { # SITE_NUM for $i (1 .. $k) { push(@SITE_NUM, stdfParseFldU1()); }; }; push(@xtdfFlds, xmlTag('SITE_CNT', {}, $k), xmlTag('SITE_NUM', {}, xmlList(@SITE_NUM)), xmlTag('HAND_TYP', {}, stdfParseFldCn()), xmlTag('HAND_ID', {}, stdfParseFldCn()), xmlTag('CARD_TYP', {}, stdfParseFldCn()), xmlTag('CARD_ID', {}, stdfParseFldCn()), xmlTag('LOAD_TYP', {}, stdfParseFldCn()), xmlTag('LOAD_ID', {}, stdfParseFldCn()), xmlTag('DIB_TYP', {}, stdfParseFldCn()), xmlTag('DIB_ID', {}, stdfParseFldCn()), xmlTag('CABL_TYP', {}, stdfParseFldCn()), xmlTag('CABL_ID', {}, stdfParseFldCn()), xmlTag('CONT_TYP', {}, stdfParseFldCn()), xmlTag('CONT_ID', {}, stdfParseFldCn()), xmlTag('LASR_TYP', {}, stdfParseFldCn()), xmlTag('LASR_ID', {}, stdfParseFldCn()), xmlTag('EXTR_TYP', {}, stdfParseFldCn()), xmlTag('EXTR_ID', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # TSR() sub TSR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_NUM', {}, stdfParseFldU1()), xmlTag('TEST_TYP', {}, stdfParseFldC1()), xmlTag('TEST_NUM', {}, stdfParseFldU4()), xmlTag('EXEC_CNT', {}, stdfParseFldU4()), xmlTag('FAIL_CNT', {}, stdfParseFldU4()), xmlTag('ALRM_CNT', {}, stdfParseFldU4()), xmlTag('TEST_NAM', {}, stdfParseFldCn()), xmlTag('SEQ_NAME', {}, stdfParseFldCn()), xmlTag('TEST_LBL', {}, stdfParseFldCn()), xmlTag('OPT_FLG', {}, stdfParseFldB1()), xmlTag('TEST_TIM', {}, stdfParseFldR4()), xmlTag('TEST_MIN', {}, stdfParseFldR4()), xmlTag('TEST_MAX', {}, stdfParseFldR4()), xmlTag('TST_SUMS', {}, stdfParseFldR4()), xmlTag('TST_SQRS', {}, stdfParseFldR4()), ); xtdfPrintList(@xtdfFlds); }; # WCR() sub WCR { my @xtdfFlds = ( xmlTag('WAFR_SIZ', {}, stdfParseFldR4()), xmlTag('DIE_HT', {}, stdfParseFldR4()), xmlTag('DIE_WID', {}, stdfParseFldR4()), xmlTag('WF_UNITS', {}, stdfParseFldU1()), xmlTag('WF_FLAT', {}, stdfParseFldC1()), xmlTag('CENTER_X', {}, stdfParseFldI2()), xmlTag('CENTER_Y', {}, stdfParseFldI2()), xmlTag('POS_X', {}, stdfParseFldC1()), xmlTag('POS_Y', {}, stdfParseFldC1()), ); xtdfPrintList(@xtdfFlds); }; # WIR() sub WIR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_GRP', {}, stdfParseFldU1()), xmlTag('START_T', {}, secondsToDateTime(stdfParseFldU4())), xmlTag('WAFER_ID', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; # WRR() sub WRR { my @xtdfFlds = ( xmlTag('HEAD_NUM', {}, stdfParseFldU1()), xmlTag('SITE_GRP', {}, stdfParseFldU1()), xmlTag('FINISH_T', {}, secondsToDateTime(stdfParseFldU4())), xmlTag('PART_CNT', {}, stdfParseFldU4()), xmlTag('RTST_CNT', {}, stdfParseFldU4()), xmlTag('ABRT_CNT', {}, stdfParseFldU4()), xmlTag('GOOD_CNT', {}, stdfParseFldU4()), xmlTag('FUNC_CNT', {}, stdfParseFldU4()), xmlTag('WAFER_ID', {}, stdfParseFldCn()), xmlTag('FABWF_ID', {}, stdfParseFldCn()), xmlTag('FRAME_ID', {}, stdfParseFldCn()), xmlTag('MASK_ID', {}, stdfParseFldCn()), xmlTag('USR_DESC', {}, stdfParseFldCn()), xmlTag('EXC_DESC', {}, stdfParseFldCn()), ); xtdfPrintList(@xtdfFlds); }; ###################################################################### =pod =head1 REQUIRES E Perl 5.6 or newer. E Perl core module strict. E Perl custom module TDF.pm. =head1 SEE ALSO E perl core and module documentation. E STDF Specification V4 published by Teradyne, Inc. E ATDF Specification V2 published by Teradyne, Inc. =head1 AUTHORS E Michael Hackerott, michael.hackerott@mrhackerott.org =head1 COPYRIGHT Copyright E 2005 Michael Hackerott. All rights reserved. This program is free software; you can redistribute it and/r modify it under the terms of either the GNU General Public License or the Artistic License as specified in the Perl README file. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =head1 ACKNOWLEDGEMENT The Standard Test Data Format (STDF) and ASCII Test Data Format (ATDF) specifications are the original works of Teradyne Inc. =head1 KNOWN BUGS E The Vn data type not implemented. Therefore, GDR records are not implemented. =head1 HISTORY 1.0.0 (200512031456) Michael Hackerott E Created program. =cut __END__