星海湾科技
徒手玩转STDF格式文件 -- 码农切入半导体系列
来源:网络 | 作者:星海湾 | 发布时间: 2024-12-19 | 639 次浏览 | 分享到:

文件结构

一个基本的STDF文件,结构是这样的:

STDF File

├── Record (repeated for each record in the file)

│   ├── Header (6 bytes)

│   │   ├── REC_LEN (2 bytes) : Record Length (total length of the record including header)

│   │   ├── REC_TYP (1 byte)  : Record Type (identifies the kind of record)

│   │   └── REC_SUB (1 byte)  : Record Sub-Type (further specifies the record type)

│   └── Body (variable length, depending on REC_TYP and REC_SUB)

│       ├── Field (repeated for each field in the record)

│       │   ├── Field Name    : Descriptive name for the field

│       │   ├── Data Type     : Specifies the data type of the field

│       │   └── Value         : The actual data stored in the field

│       └── [Additional Fields as specified by the record type]

└── [More Records as needed]

上面的内容可以看出,标准STDF包含了 头文件 和记录文件 两个主要内容。

其中

REC TYPE 是主记录的标识。

REC_SUB 是主记录下的子标识。

以下是STDF的全部记录类型

今天我们要创建一个最简单的带有头文件的STDF格式文件,就需要理解STDF(Standard Test Data Format)的基本结构。STDF是一种二进制格式,用于半导体测试设备之间交换测试数据。每个STDF记录由一个固定长度的头和可变长度的数据字段组成。

根据标准,我们知道每个STDF记录都包含一个头部,该头部至少包括三个字段:记录长度、记录类型(REC_TYP)、记录子类型(REC_SUB)。我们将创建一个最小的STDF文件,它将仅包含File Attributes Record (FAR) 和 Master Information Record (MIR),因为它们是每个STDF文件必需的。

 它的格式如下:

STDF File

├── FAR (File Attributes Record)

│   ├── Header (6 bytes)

│   │   ├── REC_LEN (2 bytes) : Length of FAR record including header

│   │   ├── REC_TYP (1 byte)  : 0 (for FAR)

│   │   └── REC_SUB (1 byte)  : 10 (for FAR)

│   └── Body (variable length)

│       ├── Version           : STDF version number (1 byte)

│       └── CPU_Type          : Type of CPU used to generate the file (1 byte)

├── MIR (Master Information Record)

│   ├── Header (6 bytes)

│   │   ├── REC_LEN (2 bytes) : Length of MIR record including header

│   │   ├── REC_TYP (1 byte)  : 1 (for MIR)

│   │   └── REC_SUB (1 byte)  : 10 (for MIR)

│   └── Body (variable length)

│       ├── Head Count        : Number of test heads (2 bytes)

│       ├── Site Count        : Number of test sites per head (2 bytes)

│       ├── Date/Time First Part Tested (4 bytes each)

│       ├── Date/Time Last Part Tested (4 bytes each)

│       ├── Test Operator     : ID of the operator who ran the tests (variable length string)

│       ├── Test Station ID   : ID of the station where tests were performed (variable length string)

│       ├── Lot ID            : Identifier for the lot being tested (variable length string)

│       ├── Wafer ID          : Identifier for the wafer being tested (variable length string)

│       └── Flat/Notch Indicator: Orientation of the wafer flat or notch (1 byte)

└── [Potentially more records, depending on the file content]


文件创建

以下是Python代码示例,它将创建一个简单的STDF文件,其中包含FAR和MIR记录:

import struct

 

def create_stdf_file(filename):

    # FAR - File Attributes Record

    far_rec_typ = 0  # Record Type for FAR

    far_rec_sub = 10  # Record Subtype for FAR

    far_data = b'x04x00'  # Version and CPU type, example values

 

    # MIR - Master Information Record

    mir_rec_typ = 1  # Record Type for MIR

    mir_rec_sub = 10  # Record Subtype for MIR

    mir_data = (

        b'x02x00' +  # Head count, site count

        b'xFFxFF' +  # Date of first part tested (not used)

        b'xFFxFFxFFxFF' +  # Time of first part tested (not used)

        b'xFFxFF' +  # Date of last part tested (not used)

        b'xFFxFFxFFxFF' +  # Time of last part tested (not used)

        b'x00x00x00x00' +  # Test Operator (not used)

        b'x00x00x00x00' +  # Test Station ID (not used)

        b'x00x00x00x00' +  # Lot ID (not used)

        b'x00x00x00x00' +  # Wafer ID (not used)

        b'x00x00x00x00'  # Flat/notched indicator (not used)

    )

 

    with open(filename, 'wb') as f:

        # Write FAR record

        far_length = len(far_data) + 4  # Including header length

        far_header = struct.pack('>HBB', far_length, far_rec_typ, far_rec_sub)

        f.write(far_header + far_data)

 

        # Write MIR record

        mir_length = len(mir_data) + 4  # Including header length

        mir_header = struct.pack('>HBB', mir_length, mir_rec_typ, mir_rec_sub)

        f.write(mir_header + mir_data)

 

create_stdf_file('simple.stdf')


代码解释:

这段代码用于创建一个简单的 STDF 文件,并写入两个类型的记录:FAR(File Attributes Record)和 MIR(Master Information Record)。它会按照 STDF 格式组织数据,并将其写入文件。

  1. 导入模块

import struct

struct 模块用于处理二进制数据的打包(packing)和解包(unpacking),即将数据转换为字节流,这在读取或写入二进制文件时非常有用。

     2.定义 create_stdf_file 函数

def create_stdf_file(filename):

这是定义的一个函数,接收文件名 filename 作为参数,用来生成 STDF 文件。

     3. FAR(File Attributes Record)记录

# FAR - File Attributes Record

far_rec_typ = 0  # Record Type for FAR

far_rec_sub = 10  # Record Subtype for FAR

far_data = b'x04x00'  # Version and CPU type, example values

FAR 记录的类型和子类型分别是 0 和 10。它用于存储与文件相关的属性(解析的时候,可以对照标准中的表来解析)。

far_data = b'x04x00' 是一个示例字节数据,这里表示 类型的信息。数据是字节串(bytes)。

4. MIR(Master Information Record)记录

# MIR - Master Information Record

mir_rec_typ = 1  # Record Type for MIR

mir_rec_sub = 10  # Record Subtype for MIR

mir_data = (

    b'x02x00' +  # Head count, site count

    b'xFFxFF' +  # Date of first part tested (not used)

    b'xFFxFFxFFxFF' +  # Time of first part tested (not used)

    b'xFFxFF' +  # Date of last part tested (not used)

    b'xFFxFFxFFxFF' +  # Time of last part tested (not used)

    b'x00x00x00x00' +  # Test Operator (not used)

    b'x00x00x00x00' +  # Test Station ID (not used)

    b'x00x00x00x00' +  # Lot ID (not used)

    b'x00x00x00x00' +  # Wafer ID (not used)

    b'x00x00x00x00'  # Flat/notched indicator (not used)

MIR 记录的类型和子类型分别是 1 和 10。它用于存储关于测试的主信息,如测试设备、测试站等。

mir_data 是一个字节串,包含多个字段的示例数据。每个字段都使用字节串表示。例如,b'x02x00' 是头计数和站点计数的占位符,b'xFFxFF' 表示未使用的字段。

5. 打开文件并写入数据

with open(filename, 'wb') as f:

使用 with open(filename, 'wb') 打开文件并准备以二进制模式('wb')写入文件。这意味着文件中的数据将按字节流写入。

6. 写入 FAR 记录

# Write FAR record

far_length = len(far_data) + 4  # Including header length

far_header = struct.pack('>HBB', far_length, far_rec_typ, far_rec_sub)

f.write(far_header + far_data)

far_length = len(far_data) + 4 计算 FAR 记录的总长度,包含了记录头部的 4 字节(2 字节长度字段,1 字节类型字段,1 字节子类型字段)。

far_header = struct.pack('>HBB', far_length, far_rec_typ, far_rec_sub) 使用 struct.pack 将 FAR 记录的头部打包成字节流:

>H 表示一个 2 字节的无符号短整型,表示记录的长度。

B 表示一个字节的无符号字符型,分别表示记录类型和子类型。

然后通过 f.write(far_header + far_data) 将头部和数据一起写入文件。

7. 写入 MIR 记录

# Write MIR record

mir_length = len(mir_data) + 4  # Including header length

mir_header = struct.pack('>HBB', mir_length, mir_rec_typ, mir_rec_sub)

f.write(mir_header + mir_data)

mir_length = len(mir_data) + 4 计算 MIR 记录的总长度,同样包括头部的 4 字节。

mir_header = struct.pack('>HBB', mir_length, mir_rec_typ, mir_rec_sub) 使用 struct.pack 打包 MIR 记录的头部。

然后通过 f.write(mir_header + mir_data) 将头部和数据一起写入文件。

8. 调用函数

create_stdf_file('simple.stdf')

这行代码调用 create_stdf_file 函数并将 'simple.stdf' 作为文件名传入,创建并写入 STDF 文件。

四、读取和解析文件

读取和解析文件,相对逻辑就比较容易了,因为我们已经有了创建文件的逻辑,所以反着来就行了!

为了读取并打印STDF文件的内容,我们需要编写一个Python脚本来解析二进制数据。下面是一个简单的例子,它会读取我们之前创建的最简STDF文件,并打印出每个记录的头部信息。

import struct

 

def parse_stdf_file(filename):

    with open(filename, 'rb') as f:

        # 读取 FAR (File Attributes Record)

        far_header = f.read(4)

        far_length, far_rec_typ, far_rec_sub = struct.unpack('>HBB', far_header)

        far_data = f.read(far_length - 4)  # 读取 FAR 数据

        print(f"FAR Record (Length: {far_length}):")

        print(f"  Record Type: {far_rec_typ}")

        print(f"  Record Subtype: {far_rec_sub}")

        print(f"  FAR Data: {far_data.hex()}")

 

        # 读取 MIR (Master Information Record)

        mir_header = f.read(4)

        mir_length, mir_rec_typ, mir_rec_sub = struct.unpack('>HBB', mir_header)

        mir_data = f.read(mir_length - 4)  # 读取 MIR 数据

        print(f"nMIR Record (Length: {mir_length}):")

        print(f"  Record Type: {mir_rec_typ}")

        print(f"  Record Subtype: {mir_rec_sub}")

        print(f"  MIR Data: {mir_data.hex()}")

 

        # 解析 MIR 数据(可根据需要进一步解析每个字段)

        print("nParsed MIR Data:")

        print(f"  Head Count and Site Count: {struct.unpack('>H', mir_data[0:2])[0]}")

        print(f"  Date of First Part Tested (not used): {mir_data[2:4].hex()}")

        print(f"  Time of First Part Tested (not used): {mir_data[4:8].hex()}")

        print(f"  Date of Last Part Tested (not used): {mir_data[8:10].hex()}")

        print(f"  Time of Last Part Tested (not used): {mir_data[10:14].hex()}")

        print(f"  Test Operator (not used): {mir_data[14:18].hex()}")

        print(f"  Test Station ID (not used): {mir_data[18:22].hex()}")

        print(f"  Lot ID (not used): {mir_data[22:26].hex()}")

        print(f"  Wafer ID (not used): {mir_data[26:30].hex()}")

        print(f"  Flat/Notched Indicator (not used): {mir_data[30:34].hex()}")

 

# 调用函数解析并打印内容

parse_stdf_file('simple.stdf')

代码解释:

以下是对我提供的 Python 代码的概要解释:

1. parse_stdf_file(filename) 函数:

该函数用于解析 .stdf 文件并打印文件中包含的记录数据。

2. 打开文件:

with open(filename, 'rb') as f:

使用 open 函数以二进制模式读取文件 ('rb')。with 语句确保文件在读取后被自动关闭。

3. 读取和解析 FAR(File Attributes Record):

far_header = f.read(4)

far_length, far_rec_typ, far_rec_sub = struct.unpack('>HBB', far_header)

far_data = f.read(far_length - 4)

首先,读取 FAR 记录的头部(4 字节)。

使用 struct.unpack 解包头部数据,>HBB 表示将数据按大端格式解包为:1 个 unsigned short(2 字节)表示 far_length,2 个 unsigned char(1 字节)分别表示 far_rec_typ 和 far_rec_sub。

然后,读取 FAR 记录的数据部分,数据长度为 far_length - 4(因为头部已经占用 4 字节)。

4. 打印 FAR 记录内容:

print(f"FAR Record (Length: {far_length}):")

print(f"  Record Type: {far_rec_typ}")

print(f"  Record Subtype: {far_rec_sub}")

print(f"  FAR Data: {far_data.hex()}")

打印 FAR 记录的长度、类型和子类型。

将 FAR 数据转为十六进制格式进行打印(far_data.hex())。

5. 读取和解析 MIR(Master Information Record):

mir_header = f.read(4)

mir_length, mir_rec_typ, mir_rec_sub = struct.unpack('>HBB', mir_header)

mir_data = f.read(mir_length - 4)

读取 MIR 记录的头部(4 字节)并解包,获取 mir_length、mir_rec_typ 和 mir_rec_sub。

读取 MIR 数据部分,数据长度为 mir_length - 4(同样考虑到头部的 4 字节)。

6. 打印 MIR 记录内容:

print(f"nMIR Record (Length: {mir_length}):")

print(f"  Record Type: {mir_rec_typ}")

print(f"  Record Subtype: {mir_rec_sub}")

print(f"  MIR Data: {mir_data.hex()}")

打印 MIR 记录的长度、类型和子类型。

将 MIR 数据转为十六进制格式打印。

7. 解析 MIR 数据的具体字段:

print("nParsed MIR Data:")

print(f"  Head Count and Site Count: {struct.unpack('>H', mir_data[0:2])[0]}")

print(f"  Date of First Part Tested (not used): {mir_data[2:4].hex()}")

print(f"  Time of First Part Tested (not used): {mir_data[4:8].hex()}")

...

我逐个解析 MIR 数据中的各个字段。每个字段在数据流中的位置是已知的,所以下标指定了每个字段的范围。

使用 struct.unpack 解析特定字段(例如 Head Count and Site Count 字段),并打印出它们的值。

对于不使用的字段,直接显示它们的十六进制表示。

执行后,结果就是我们写入的内容:


本节小结:

1. 写入 STDF 文件代码

目标:创建一个简单的 .stdf 文件并写入两个记录类型:FAR (File Attributes Record) 和 MIR (Master Information Record)。

代码功能:

FAR 记录:

far_rec_typ:设置为 0(FAR 记录类型)。

far_rec_sub:设置为 10(FAR 记录子类型)。

far_data:包含示例数据(如 b'x04x00',表示版本和 CPU 类型)。

FAR 头部:包含 FAR 记录的长度(far_length)、类型(far_rec_typ)和子类型(far_rec_sub),然后将其与数据一起写入文件。

MIR 记录:

mir_rec_typ:设置为 1(MIR 记录类型)。

mir_rec_sub:设置为 10(MIR 记录子类型)。

mir_data:包含多个字段的示例数据,如头计数、日期、时间等,很多字段是未使用的(例如,测试操作员、测试站点 ID 等设置为 0x00)。

MIR 头部:类似于 FAR 记录,包含 MIR 记录的长度(mir_length)、类型(mir_rec_typ)和子类型(mir_rec_sub),然后将其与数据一起写入文件。

输出:一个名为 simple.stdf 的文件,其中包含 FAR 和 MIR 两个记录。

2. 读取 STDF 文件代码

目标:解析刚刚生成的 .stdf 文件,读取 FAR 和 MIR 记录,并打印它们的内容。

代码功能:

读取 FAR 记录:

读取 4 字节头部,获取 far_length(记录长度)、far_rec_typ(记录类型)和 far_rec_sub(记录子类型)。

然后读取剩余的 far_data 并打印十六进制数据。

读取 MIR 记录:

类似于 FAR 记录,读取 4 字节头部,获取 mir_length(记录长度)、mir_rec_typ(记录类型)和 mir_rec_sub(记录子类型)。

然后读取剩余的 mir_data 并打印十六进制数据。

解析 MIR 数据:

对 MIR 数据中的各个字段进行进一步解析,例如:头计数和站点计数、日期时间、操作员信息等,并打印它们的值。虽然很多字段在示例中没有实际使用,但可以展示它们的十六进制表示。

输出:

读取并打印 FAR 和 MIR 记录的详细信息,包括它们的长度、类型、子类型以及数据部分的十六进制表示。

MIR 数据会根据字段解析逻辑显示各个字段的值,尽管示例中的很多字段未被使用。