From 15834eb998b416b01736b45b9f22dba83fbf29a8 Mon Sep 17 00:00:00 2001 From: Asachoo Date: Sat, 15 Nov 2025 13:08:07 +0800 Subject: [PATCH 1/7] Fixed typos and added a context manager. --- examples/write_stdf_file.py | 0 main.py | 19 +++++++------ pystdf4/IO/__init__.py | 4 +-- pystdf4/IO/base.py | 6 ++++ pystdf4/IO/{stfd4write.py => stdf4writer.py} | 29 +++++++++++++++++--- pystdf4/__init__.py | 4 +-- 6 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 examples/write_stdf_file.py rename pystdf4/IO/{stfd4write.py => stdf4writer.py} (96%) diff --git a/examples/write_stdf_file.py b/examples/write_stdf_file.py new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py index c6baabe..442d9d9 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import List, Optional, Tuple -from pystdf4.IO import Stfd4Writer +from pystdf4.IO import Stdf4Writer def parse_test_data_segment(test_data_segment: List[str]) -> Tuple[float, ...]: @@ -116,7 +116,9 @@ def __init__( # test_pass may be True, False or None # if test_pass is None, it means test_flg should be calculated based on lo_limit, hi_limit and result # if test_pass in True or False, directly set test_flg based on test_pass - test_pass = test_pass if test_pass is not None else (lo_limit <= result <= hi_limit) + test_pass = ( + test_pass if test_pass is not None else (lo_limit <= result <= hi_limit) + ) self.test_flag = 0 if not test_pass: self.test_flag |= 1 << 7 @@ -327,7 +329,11 @@ def evaluate_part_test( ), start=1, ): - test_pass = test_txt not in moving_limit_set if self.pat_item_pattern.match(test_txt) else None + test_pass = ( + test_txt not in moving_limit_set + if self.pat_item_pattern.match(test_txt) + else None + ) ptr = PTR_D( test_number=i, result=result, @@ -375,9 +381,8 @@ def write_stdf_file( wafer_id: str, lot_id: str, ): - with open(stdf_file_path, "wb") as f: - stdf = Stfd4Writer("") - # write FAR record + # with open(stdf_file_path, "wb") as f: + with Stdf4Writer(str(stdf_file_path)) as stdf: # stdf.write_record(FAR(CPU_TYPE=2, STDF_VER=4)) stdf.FAR(CPU_TYPE=2, STDF_VER=4) @@ -479,8 +484,6 @@ def write_stdf_file( # write MRR record stdf.MRR(FINISH_T=finish_time) - f.write(stdf.to_bytes()) - def convert_csv_to_stdf( csv_path_str: str, diff --git a/pystdf4/IO/__init__.py b/pystdf4/IO/__init__.py index f04417f..0625049 100644 --- a/pystdf4/IO/__init__.py +++ b/pystdf4/IO/__init__.py @@ -1,4 +1,4 @@ from .stdf4reader import Stdf4Reader -from .stfd4write import Stfd4Writer +from .stdf4writer import Stdf4Writer -__all__ = ["Stdf4Reader", "Stfd4Writer"] +__all__ = ["Stdf4Reader", "Stdf4Writer"] diff --git a/pystdf4/IO/base.py b/pystdf4/IO/base.py index d5933bd..42d4a02 100644 --- a/pystdf4/IO/base.py +++ b/pystdf4/IO/base.py @@ -46,3 +46,9 @@ def __init__(self, file_path: str): self.C_n = C_n() self.header_packer: Struct = Struct(" "Stdf4Writer": + if not self.file_path.exists(): + self.file_path.touch() + return super().__enter__() + + def __exit__(self, exc_type: type, exc_val: Exception, exc_tb: object) -> None: + self.file_path.write_bytes(self.to_bytes()) + def to_bytes(self) -> bytes: self.write_buffers() return self.buffer.to_bytes() @@ -21,7 +29,9 @@ def write_fields(self, rec_typ: int, rec_sub: int, field_writer: Callable): # Step 3: Update the length of the record record_end = self.buffer.offset record_length = record_end - (record_start + 4) - self.buffer._mv[record_start : record_start + 2] = record_length.to_bytes(2, byteorder="little") + self.buffer._mv[record_start : record_start + 2] = record_length.to_bytes( + 2, byteorder="little" + ) def _write_header(self, header: Tuple[int, int]) -> int: """ @@ -35,7 +45,16 @@ def _write_header(self, header: Tuple[int, int]) -> int: return start def write_buffers(self): - for scalar_field in (self.U_1, self.U_2, self.U_4, self.I_1, self.I_2, self.I_4, self.R_4, self.R_8): + for scalar_field in ( + self.U_1, + self.U_2, + self.U_4, + self.I_1, + self.I_2, + self.I_4, + self.R_4, + self.R_8, + ): scalar_field.flush_cache_to_buffer(self.buffer) def ATR(self, MOD_TIM: int, CMD_LINE: str): @@ -166,7 +185,9 @@ def MIR( "9", " ", ] = " ", - RTST_COD: Literal["Y", "N", "1", "2", "3", "4", "5", "6", "7", "8", "9", " "] = " ", + RTST_COD: Literal[ + "Y", "N", "1", "2", "3", "4", "5", "6", "7", "8", "9", " " + ] = " ", PROT_COD: str = " ", BURN_TIM: int = 65535, CMOD_COD: str = " ", diff --git a/pystdf4/__init__.py b/pystdf4/__init__.py index 7adf0f6..6d7fe50 100644 --- a/pystdf4/__init__.py +++ b/pystdf4/__init__.py @@ -1,4 +1,4 @@ -from .IO.stfd4write import Stfd4Writer +from .IO.stdf4writer import Stdf4Writer __version__ = "0.1.0" -__all__ = ["Stfd4Writer"] +__all__ = ["Stdf4Writer"] From 6f0fd5551e5d6c589f6af461e80e0edd53417901 Mon Sep 17 00:00:00 2001 From: Lansus Date: Sat, 15 Nov 2025 14:43:24 +0800 Subject: [PATCH 2/7] Update readme. --- README.md | 191 +++++++++++++++----------------- pystdf4/Core/dynamic_buffer.pyx | 61 ---------- pystdf4/IO/stdf4writer.py | 5 +- 3 files changed, 93 insertions(+), 164 deletions(-) delete mode 100644 pystdf4/Core/dynamic_buffer.pyx diff --git a/README.md b/README.md index 8ea4c0a..388fde2 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,22 @@ +# **πŸ“¦ pystdf4** + +A modern, high-performance Python library for creating and parsing **STDF (Standard Test Data Format) v4** files used throughout semiconductor test and manufacturing. --- -# **pystdf4** +## **πŸ” Overview** -**An object-oriented Python library for parsing and generating STDF (Standard Test Data Format) files used in semiconductor testing and manufacturing.** +**pystdf4** offers a clean, Pythonic, and object-oriented interface for working with **STDF v4**, the industry-standard binary format for semiconductor test data. +It abstracts away low-level byte handling and record encoding, allowing engineers to focus on data modeling rather than binary protocol details. ---- +With **pystdf4**, you can efficiently: -## 🧩 Overview +- **Generate STDF v4 files** using structured Python objects +- Build records using a robust and extensible field/record system +- Work with a high-performance dynamic byte buffer optimized for large datasets +- Integrate easily into automated test, analysis, and data processing pipelines -**pystdf4** provides a modern, object-oriented interface for working with **STDF v4** files β€” the industry-standard binary data format for semiconductor test results. -It supports both **reading** and **writing** of STDF files, with a clean Python API that abstracts low-level binary details into high-level Pythonic objects. +Support for **STDF file parsing (reader API)** is actively under development and coming soon. --- @@ -19,25 +25,25 @@ It supports both **reading** and **writing** of STDF files, with a clean Python The **Standard Test Data Format (STDF)** specification defines compact type codes to describe how values are stored and interpreted within records. Each type code corresponds to a C data type, defining the byte layout and valid range of values. -| Code | Description | C Type Specifier | Notes | -| :------- | :-------------------------------------------- | :--------------- | :----------------------------------------------------------------------- | -| `C*1` | Fixed-length character string (1 characters) | `char[1]` | Left-justified; pad with spaces if shorter than 12 characters. | -| `C*n` | Variable-length character string | `char[]` | First byte stores the length (0–255). | -| `C*f` | Variable-length string with external length | `char[]` | Length determined by another record field. | -| `U*1` | 1-byte unsigned integer | `unsigned char` | Range: 0–255 | -| `U*2` | 2-byte unsigned integer | `unsigned short` | Range: 0–65,535 | -| `U*4` | 4-byte unsigned integer | `unsigned long` | Range: 0–4,294,967,295 | -| `I*1` | 1-byte signed integer | `char` | Range: –128 to 127 | -| `I*2` | 2-byte signed integer | `short` | Range: –32,768 to 32,767 | -| `I*4` | 4-byte signed integer | `long` | Range: –2,147,483,648 to 2,147,483,647 | -| `R*4` | 4-byte floating-point number (IEEE 754) | `float` | Standard single-precision float | -| `R*8` | 8-byte floating-point number (IEEE 754) | `double` | Standard double-precision float | -| `B*6` | Fixed-length binary field (6 bytes) | `char[6]` | Raw binary data | -| `V*n` | Variable-type field | β€” | First byte = type code, followed by up to 255 bytes of data | -| `B*n` | Variable-length binary field | `char[]` | First byte = byte count (0–255); data starts at LSB of second byte | -| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count (0–65,535); unused bits in last byte = 0 | -| `N*1` | Nibble (4-bit) data | `char` | Stores unsigned integers in 4-bit units; high nibble zeroed if odd count | -| `kxTYPE` | Array of specified data type | `TYPE[]` | Length `k` determined by another record field (e.g. `kxU*2`) | +| Code | Description | C Type Specifier | Notes | +| :------- | :------------------------------------------- | :--------------- | :----------------------------------------------------------------------- | +| `C*1` | Fixed-length character string (1 characters) | `char[1]` | Left-justified; pad with spaces if shorter than 12 characters. | +| `C*n` | Variable-length character string | `char[]` | First byte stores the length (0–255). | +| `C*f` | Variable-length string with external length | `char[]` | Length determined by another record field. | +| `U*1` | 1-byte unsigned integer | `unsigned char` | Range: 0–255 | +| `U*2` | 2-byte unsigned integer | `unsigned short` | Range: 0–65,535 | +| `U*4` | 4-byte unsigned integer | `unsigned long` | Range: 0–4,294,967,295 | +| `I*1` | 1-byte signed integer | `char` | Range: –128 to 127 | +| `I*2` | 2-byte signed integer | `short` | Range: –32,768 to 32,767 | +| `I*4` | 4-byte signed integer | `long` | Range: –2,147,483,648 to 2,147,483,647 | +| `R*4` | 4-byte floating-point number (IEEE 754) | `float` | Standard single-precision float | +| `R*8` | 8-byte floating-point number (IEEE 754) | `double` | Standard double-precision float | +| `B*1` | Fixed-length binary field (1 bytes) | `char[1]` | Raw binary data | +| `V*n` | Variable-type field | β€” | First byte = type code, followed by up to 255 bytes of data | +| `B*n` | Variable-length binary field | `char[]` | First byte = byte count (0–255); data starts at LSB of second byte | +| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count (0–65,535); unused bits in last byte = 0 | +| `N*1` | Nibble (4-bit) data | `char` | Stores unsigned integers in 4-bit units; high nibble zeroed if odd count | +| `kxTYPE` | Array of specified data type | `TYPE[]` | Length `k` determined by another record field (e.g. `kxU*2`) | --- @@ -47,89 +53,80 @@ The **PyStdf4 type system** provides a structured, extensible way to represent S Each class encapsulates type conversion, byte parsing, and STDF serialization. ``` -Field (ABC) -β”œβ”€β”€ ScalarField[_PyT] -β”‚ β”œβ”€β”€ U_1, U_2, U_4 # unsigned int -β”‚ β”œβ”€β”€ I_1, I_2, I_4 # signed int -β”‚ β”œβ”€β”€ R_4, R_8 # float -β”‚ β”œβ”€β”€ C_1 # char β†’ bytes -β”‚ └── B_1 # raw bytes -└── SequenceField[_PyT] (ABC) - β”œβ”€β”€ FixLenField[_PyT] - β”œβ”€β”€ VarLenField[_PyT] - β”‚ β”œβ”€β”€ C_n (str β†’ bytes) - β”‚ └── B_n (bytes) - └── KxLenField - β”œβ”€β”€ KxU_1 - β”œβ”€β”€ KxU_2 - β”œβ”€β”€ KxC_n - └── KxR_4 +FieldBase(pyT) (ABC) +β”œβ”€β”€ ImmediateField(pyT) +β”‚ β”œβ”€β”€ C_1 # fixed-length char (1 byte) +β”‚ β”œβ”€β”€ B_1 # fixed-length binary (1 byte) +β”‚ β”œβ”€β”€ C_n # Pascal string (1-byte length + bytes) +β”‚ └── B_n # Pascal binary (1-byte length + bytes) +β”‚ +β”œβ”€β”€ DeferredField(pyT) +β”‚ β”œβ”€β”€ U_1 # uint8 +β”‚ β”œβ”€β”€ U_2 # uint16 +β”‚ β”œβ”€β”€ U_4 # uint32 +β”‚ β”œβ”€β”€ I_1 # int8 +β”‚ β”œβ”€β”€ I_2 # int16 +β”‚ β”œβ”€β”€ I_4 # int32 +β”‚ β”œβ”€β”€ R_4 # float32 +β”‚ └── R_8 # float64 +β”‚ +β”œβ”€β”€ ❌ C_f (Pascal string with external length) +β”œβ”€β”€ ❌ V_n (typed variable field: ) +β”œβ”€β”€ ❌ D_n (bitstring with leading bit-count) +β”œβ”€β”€ ❌ N_1 (nibble array 4-bit) +β”‚ +└── ❌ SequenceField + β”œβ”€β”€ ❌ FixLenField # fixed-size arrays, 如 B*6 + β”œβ”€β”€ ❌ VarLenField # variable arrays with Pascal length + └── ❌ KxLenField # array with external length (kΓ—TYPE) + β”œβ”€β”€ ❌ KxU_1 + β”œβ”€β”€ ❌ KxU_2 + β”œβ”€β”€ ❌ KxC_n + └── ❌ KxR_4 ``` These classes standardize how data flows between **Python**, **C-style internal representations**, and **STDF binary data**. --- -## πŸ”„ Data Flow - -The transformation pipeline for STDF data in **pystdf4** can be summarized as follows: - -``` -py_value (Python type) - ⇄ [build_py / parse_py] -internal_bytes (C-style binary data) - ⇄ [build_stdf / parse_stdf] -stdf_value (STDF field data) -``` - -This design ensures each data element can: - -* be **parsed** from STDF binary files, -* be **converted** into native Python types for analysis or manipulation, -* and be **re-serialized** back into valid STDF format. - ---- - - ## πŸ“‹ STDF v4 Record Types Implementation Status -| Record Type | Name | REC_TYP | REC_SUB | Status | Notes | -|-------------|-------------------------------------|---------|---------|---------------|---------------------------------| -| FAR | File Attributes Record | 0 | 10 | βœ”οΈ Complete | Required as first record | -| ATR | Audit Trail Record | 0 | 20 | βœ”οΈ Complete | Tracks file modifications | -| MIR | Master Information Record | 1 | 10 | βœ”οΈ Complete | Lot-level information | -| MRR | Master Results Record | 1 | 20 | βœ”οΈ Complete | End of lot summary | -| PCR | Part Count Record | 1 | 30 | βœ”οΈ Complete | Part statistics | -| HBR | Hardware Bin Record | 1 | 40 | βœ”οΈ Complete | Physical binning counts | -| SBR | Software Bin Record | 1 | 50 | βœ”οΈ Complete | Logical binning counts | -| PMR | Pin Map Record | 1 | 60 | βœ”οΈ Complete | Pin/channel mapping | -| PGR | Pin Group Record | 1 | 62 | ❌ Incomplete | Pin grouping | -| PLR | Pin List Record | 1 | 63 | ❌ Incomplete | Pin group display properties | -| RDR | Retest Data Record | 1 | 70 | ❌ Incomplete | Retest information | -| SDR | Site Description Record | 1 | 80 | βœ”οΈ Complete | Test site configuration | -| WIR | Wafer Information Record | 2 | 10 | βœ”οΈ Complete | Wafer start marker | -| WRR | Wafer Results Record | 2 | 20 | βœ”οΈ Complete | Wafer completion summary | -| WCR | Wafer Configuration Record | 2 | 30 | βœ”οΈ Complete | Wafer dimensions/orientation | -| PIR | Part Information Record | 5 | 10 | βœ”οΈ Complete | Part start marker | -| PRR | Part Results Record | 5 | 20 | βœ”οΈ Complete | Part completion results | -| TSR | Test Synopsis Record | 10 | 30 | βœ”οΈ Complete | Test execution statistics | -| PTR | Parametric Test Record | 15 | 10 | βœ”οΈ Complete | Single parametric test result | -| MPR | Multiple-Result Parametric Record | 15 | 15 | ❌ Incomplete | Multiple parametric test results| -| FTR | Functional Test Record | 15 | 20 | ❌ Incomplete | Functional test results | -| BPS | Begin Program Section Record | 20 | 10 | βœ”οΈ Complete | Program section start marker | -| EPS | End Program Section Record | 20 | 20 | βœ”οΈ Complete | Program section end marker | -| GDR | Generic Data Record | 50 | 10 | ❌ Incomplete | User-defined data | -| DTR | Datalog Text Record | 50 | 30 | ❌ Incomplete | Datalog comments | +| Record Type | Name | REC_TYP | REC_SUB | Status | Notes | +| ----------- | --------------------------------- | ------- | ------- | ------------- | -------------------------------- | +| FAR | File Attributes Record | 0 | 10 | βœ”οΈ Complete | Required as first record | +| ATR | Audit Trail Record | 0 | 20 | βœ”οΈ Complete | Tracks file modifications | +| MIR | Master Information Record | 1 | 10 | βœ”οΈ Complete | Lot-level information | +| MRR | Master Results Record | 1 | 20 | βœ”οΈ Complete | End of lot summary | +| PCR | Part Count Record | 1 | 30 | βœ”οΈ Complete | Part statistics | +| HBR | Hardware Bin Record | 1 | 40 | βœ”οΈ Complete | Physical binning counts | +| SBR | Software Bin Record | 1 | 50 | βœ”οΈ Complete | Logical binning counts | +| PMR | Pin Map Record | 1 | 60 | βœ”οΈ Complete | Pin/channel mapping | +| PGR | Pin Group Record | 1 | 62 | ❌ Incomplete | Pin grouping | +| PLR | Pin List Record | 1 | 63 | ❌ Incomplete | Pin group display properties | +| RDR | Retest Data Record | 1 | 70 | ❌ Incomplete | Retest information | +| SDR | Site Description Record | 1 | 80 | βœ”οΈ Complete | Test site configuration | +| WIR | Wafer Information Record | 2 | 10 | βœ”οΈ Complete | Wafer start marker | +| WRR | Wafer Results Record | 2 | 20 | βœ”οΈ Complete | Wafer completion summary | +| WCR | Wafer Configuration Record | 2 | 30 | βœ”οΈ Complete | Wafer dimensions/orientation | +| PIR | Part Information Record | 5 | 10 | βœ”οΈ Complete | Part start marker | +| PRR | Part Results Record | 5 | 20 | βœ”οΈ Complete | Part completion results | +| TSR | Test Synopsis Record | 10 | 30 | βœ”οΈ Complete | Test execution statistics | +| PTR | Parametric Test Record | 15 | 10 | βœ”οΈ Complete | Single parametric test result | +| MPR | Multiple-Result Parametric Record | 15 | 15 | ❌ Incomplete | Multiple parametric test results | +| FTR | Functional Test Record | 15 | 20 | ❌ Incomplete | Functional test results | +| BPS | Begin Program Section Record | 20 | 10 | βœ”οΈ Complete | Program section start marker | +| EPS | End Program Section Record | 20 | 20 | βœ”οΈ Complete | Program section end marker | +| GDR | Generic Data Record | 50 | 10 | ❌ Incomplete | User-defined data | +| DTR | Datalog Text Record | 50 | 30 | ❌ Incomplete | Datalog comments | --- - ## πŸ§ͺ Example Use Cases (Coming Soon) -* Parse STDF records into structured Python objects -* Convert STDF to human-readable CSV or JSON -* Build new STDF records programmatically -* Validate field encoding/decoding across STDF revisions +- Parse STDF records into structured Python objects +- Convert STDF to human-readable CSV or JSON +- Build new STDF records programmatically +- Validate field encoding/decoding across STDF revisions --- @@ -137,7 +134,3 @@ This design ensures each data element can: MIT License Β© 2025 Developed for efficient and reliable STDF data manipulation in modern semiconductor workflows. - ---- - -Would you like me to extend it with an **β€œInstallation and Quick Start”** section (e.g., `pip install pystdf4` and a short usage example)? That would make it more complete for public release on GitHub or PyPI. diff --git a/pystdf4/Core/dynamic_buffer.pyx b/pystdf4/Core/dynamic_buffer.pyx deleted file mode 100644 index 158f5fe..0000000 --- a/pystdf4/Core/dynamic_buffer.pyx +++ /dev/null @@ -1,61 +0,0 @@ -# cython: boundscheck=False, wraparound=False, initializedcheck=False -# cython: cdivision=True -# distutils: language = c++ - -cdef class DynamicBuffer: - """ - High-performance dynamically resizable byte buffer. - Cython accelerated version. - """ - - cdef public bytearray _buffer - cdef public unsigned char[:] _mv - cdef Py_ssize_t _capacity - cdef public Py_ssize_t offset - - def __init__(self, int initial_capacity=1024*1024): - self._buffer = bytearray(initial_capacity) - self._mv = self._buffer - self._capacity = initial_capacity - self.offset = 0 - - # ---- properties ---- - - @property - def capacity(self) -> int: - return self._capacity - - @capacity.setter - def capacity(self, int value): - if value < self.offset: - raise ValueError( - f"Cannot shrink below current offset ({self.offset} bytes)" - ) - - cdef bytearray new_buf = bytearray(value) - new_buf[:self.offset] = self._buffer[:self.offset] - - self._buffer = new_buf - self._mv = new_buf - self._capacity = value - - def __len__(self): - return self.offset - - def __repr__(self): - return f"" - - # ---- private but Python-callable ---- - cpdef void _ensure_capacity(self, Py_ssize_t size): - cdef Py_ssize_t target = self.offset + size - cdef Py_ssize_t desired = self._capacity - - if target > desired: - while desired < target: - desired = (desired * 3 + 1) >> 1 - self.capacity = desired - - # ---- public ---- - - def to_bytes(self) -> bytes: - return bytes(self._mv[:self.offset]) diff --git a/pystdf4/IO/stdf4writer.py b/pystdf4/IO/stdf4writer.py index 807cd83..5b3e9cc 100644 --- a/pystdf4/IO/stdf4writer.py +++ b/pystdf4/IO/stdf4writer.py @@ -13,11 +13,8 @@ def __enter__(self) -> "Stdf4Writer": return super().__enter__() def __exit__(self, exc_type: type, exc_val: Exception, exc_tb: object) -> None: - self.file_path.write_bytes(self.to_bytes()) - - def to_bytes(self) -> bytes: self.write_buffers() - return self.buffer.to_bytes() + self.file_path.write_bytes(self.buffer.to_bytes()) def write_fields(self, rec_typ: int, rec_sub: int, field_writer: Callable): # Step 1: Write the header of the record From 6d6ee194558f98a6a659876780494872c82b96eb Mon Sep 17 00:00:00 2001 From: Lansus Date: Sat, 15 Nov 2025 16:03:05 +0800 Subject: [PATCH 3/7] Support C_f, kxU_1, kxU_2, kxC_n and kxR_4 data type. --- README.md | 47 +++++++++++++++-------------- pyproject.toml | 2 -- pystdf4/Core/data_base.py | 30 +++++++++++++++---- pystdf4/Core/data_type.py | 57 ++++++++++++++++++++++++++++++++---- tests/Core/test_data_base.py | 2 +- 5 files changed, 102 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 388fde2..0741fc8 100644 --- a/README.md +++ b/README.md @@ -22,28 +22,31 @@ Support for **STDF file parsing (reader API)** is actively under development and ## πŸ“˜ STDF4 Data Type Codes -The **Standard Test Data Format (STDF)** specification defines compact type codes to describe how values are stored and interpreted within records. -Each type code corresponds to a C data type, defining the byte layout and valid range of values. - -| Code | Description | C Type Specifier | Notes | -| :------- | :------------------------------------------- | :--------------- | :----------------------------------------------------------------------- | -| `C*1` | Fixed-length character string (1 characters) | `char[1]` | Left-justified; pad with spaces if shorter than 12 characters. | -| `C*n` | Variable-length character string | `char[]` | First byte stores the length (0–255). | -| `C*f` | Variable-length string with external length | `char[]` | Length determined by another record field. | -| `U*1` | 1-byte unsigned integer | `unsigned char` | Range: 0–255 | -| `U*2` | 2-byte unsigned integer | `unsigned short` | Range: 0–65,535 | -| `U*4` | 4-byte unsigned integer | `unsigned long` | Range: 0–4,294,967,295 | -| `I*1` | 1-byte signed integer | `char` | Range: –128 to 127 | -| `I*2` | 2-byte signed integer | `short` | Range: –32,768 to 32,767 | -| `I*4` | 4-byte signed integer | `long` | Range: –2,147,483,648 to 2,147,483,647 | -| `R*4` | 4-byte floating-point number (IEEE 754) | `float` | Standard single-precision float | -| `R*8` | 8-byte floating-point number (IEEE 754) | `double` | Standard double-precision float | -| `B*1` | Fixed-length binary field (1 bytes) | `char[1]` | Raw binary data | -| `V*n` | Variable-type field | β€” | First byte = type code, followed by up to 255 bytes of data | -| `B*n` | Variable-length binary field | `char[]` | First byte = byte count (0–255); data starts at LSB of second byte | -| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count (0–65,535); unused bits in last byte = 0 | -| `N*1` | Nibble (4-bit) data | `char` | Stores unsigned integers in 4-bit units; high nibble zeroed if odd count | -| `kxTYPE` | Array of specified data type | `TYPE[]` | Length `k` determined by another record field (e.g. `kxU*2`) | +The **Standard Test Data Format (STDF)** specification defines a compact set of type codes that describe how values are stored and interpreted within records. +Each type code corresponds to a C data type and specifies its binary layout, size, and valid range. + +For a complete and authoritative description of all STDF v4 data types, please refer to the official STDF specification included in this repository (pystdf4/doc/stdf-spec.pdf). +The type code definitions summarized below correspond to the material on page 10 of the specification. + +| Code | Description | C Type Specifier | Notes | +| :------- | :------------------------------------------- | :--------------- | :----------------------------------------------------------------------- | +| `C*12` | Fixed-length character string (12 characters) | `char[12]` | Left-justified; pad with spaces if shorter than 12 characters. | +| `C*n` | Variable-length character string | `char[]` | First byte stores the length (0–255). | +| `C*f` | Variable-length string with external length | `char[]` | Length determined by another record field. | +| `U*1` | 1-byte unsigned integer | `unsigned char` | Range: 0–255 | +| `U*2` | 2-byte unsigned integer | `unsigned short` | Range: 0–65,535 | +| `U*4` | 4-byte unsigned integer | `unsigned long` | Range: 0–4,294,967,295 | +| `I*1` | 1-byte signed integer | `char` | Range: –128 to 127 | +| `I*2` | 2-byte signed integer | `short` | Range: –32,768 to 32,767 | +| `I*4` | 4-byte signed integer | `long` | Range: –2,147,483,648 to 2,147,483,647 | +| `R*4` | 4-byte floating-point number (IEEE 754) | `float` | Standard single-precision float | +| `R*8` | 8-byte floating-point number (IEEE 754) | `double` | Standard double-precision float | +| `B*6` | Fixed-length binary field (6 bytes) | `char[6]` | Raw binary data | +| `V*n` | Variable-type field | β€” | First byte = type code, followed by up to 255 bytes of data | +| `B*n` | Variable-length binary field | `char[]` | First byte = byte count (0–255); data starts at LSB of second byte | +| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count (0–65,535); unused bits in last byte = 0 | +| `N*1` | Nibble (4-bit) data | `char` | Stores unsigned integers in 4-bit units; high nibble zeroed if odd count | +| `kxTYPE` | Array of specified data type | `TYPE[]` | Length `k` determined by another record field (e.g. `kxU*2`) | --- diff --git a/pyproject.toml b/pyproject.toml index 55292a2..a094fdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,5 @@ docstring-code-format = true [dependency-groups] dev = [ "coveralls>=4.0.1", - "cython>=3.2.1", "pytest>=8.3.5", - "setuptools>=75.3.2", ] diff --git a/pystdf4/Core/data_base.py b/pystdf4/Core/data_base.py index 65d9e4c..793fbbe 100644 --- a/pystdf4/Core/data_base.py +++ b/pystdf4/Core/data_base.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from struct import Struct -from typing import Any, ClassVar, Generic, Sequence, TypeVar +from typing import Any, ClassVar, Generic, Optional, Sequence, Type, TypeVar from pystdf4.Core.dynamic_buffer import DynamicBuffer from pystdf4.Core.mixins import CacheMixin @@ -19,7 +19,7 @@ class FieldBase(ABC, Generic[pyT]): @classmethod @abstractmethod - def pack_into(cls, buffer: DynamicBuffer, value: pyT): + def pack_into(cls, buffer: DynamicBuffer, value: pyT, size: int = 0): raise NotImplementedError() @classmethod @@ -42,8 +42,8 @@ class ImmediateField(FieldBase[pyT]): field_size: ClassVar[int] = 0 @classmethod - def pack_into(cls, buffer: DynamicBuffer, value: pyT): - data = cls._normalize_value(value) + def pack_into(cls, buffer: DynamicBuffer, value: pyT, size: int = 0): + data = cls._normalize_value(value, size) field_size = cls.field_size if cls.field_size else len(data) buffer._ensure_capacity(field_size) buffer._mv[buffer.offset : buffer.offset + field_size] = data @@ -54,9 +54,13 @@ def pack_into(cls, buffer: DynamicBuffer, value: pyT): def _pascal_bytes(value: bytes) -> bytes: return bytes((len(value),)) + value + @staticmethod + def _left_justified_bytes(value: bytes, size: int, fill_byte: bytes) -> bytes: + return value.ljust(size, fill_byte) + @staticmethod @abstractmethod - def _normalize_value(value: Any) -> bytes: + def _normalize_value(value: Any, size: int = 0) -> bytes: raise NotImplementedError() @@ -72,7 +76,7 @@ def __init_subclass__(cls) -> None: cls.field_size = Struct(f"{cls.endian}{cls.num_elements}{cls.struct_format}").size @classmethod - def pack_into(cls, buffer: DynamicBuffer, value: pyT): + def pack_into(cls, buffer: DynamicBuffer, value: pyT, size: int = 0): """Reserve space, cache the value, and advance the buffer offset.""" buffer._ensure_capacity(cls.field_size) cls._enqueue_value(value, buffer.offset, cls.field_size) @@ -89,3 +93,17 @@ def _serialize_sequence(cls, sequence: Sequence[pyT]) -> bytes: count = len(cls.buffer_offsets) * cls.num_elements packer = Struct(f"{cls.endian}{count}{cls.struct_format}") return packer.pack(*sequence) + + +class ArrayField: + element_type: ClassVar[Type[FieldBase]] + + @classmethod + def pack_into(cls, buffer: DynamicBuffer, value: Optional[Sequence[pyT]], size: int): + if value is None: + return + if len(value) > size: + value = value[:size] + + for element in value: + cls.element_type.pack_into(buffer, element) diff --git a/pystdf4/Core/data_type.py b/pystdf4/Core/data_type.py index 655659b..6a701dd 100644 --- a/pystdf4/Core/data_type.py +++ b/pystdf4/Core/data_type.py @@ -1,4 +1,4 @@ -from pystdf4.Core.data_base import DeferredField, ImmediateField +from pystdf4.Core.data_base import ArrayField, DeferredField, ImmediateField # ============================================================ # Numeric Field Implementations @@ -46,7 +46,15 @@ class C_1(ImmediateField): field_size = 1 @staticmethod - def _normalize_value(value: str) -> bytes: + def _normalize_value(value: str, size: int = 0) -> bytes: + return value.encode("ascii") + + +class C_12(ImmediateField): + field_size = 12 + + @staticmethod + def _normalize_value(value: str, size: int = 0) -> bytes: return value.encode("ascii") @@ -54,17 +62,56 @@ class B_1(ImmediateField): field_size = 1 @staticmethod - def _normalize_value(value: bytes) -> bytes: + def _normalize_value(value: bytes, size: int = 0) -> bytes: + return value + + +class B_6(ImmediateField): + field_size = 6 + + @staticmethod + def _normalize_value(value: bytes, size: int = 0) -> bytes: return value class C_n(ImmediateField): @staticmethod - def _normalize_value(value: str) -> bytes: + def _normalize_value(value: str, size: int = 0) -> bytes: return ImmediateField._pascal_bytes(value.encode("ascii")) class B_n(ImmediateField): @staticmethod - def _normalize_value(value: bytes) -> bytes: + def _normalize_value(value: bytes, size: int = 0) -> bytes: return ImmediateField._pascal_bytes(value) + + +class C_f(ImmediateField): + @staticmethod + def _normalize_value(value: bytes, size: int = 0) -> bytes: + return ImmediateField._left_justified_bytes(value, size, b" ") + + +# ============================================================ +# Array Field Implementations +# ============================================================ + + +class kxU_1(ArrayField): + element_type = U_1 + + +class kxU_2(ArrayField): + element_type = U_2 + + +class kxC_n(ArrayField): + element_type = C_n + + +class kxR_4(ArrayField): + element_type = R_4 + + +# class kxN_1(ArrayField): +# element_type = N_1 diff --git a/tests/Core/test_data_base.py b/tests/Core/test_data_base.py index 8bc03a0..17ea35a 100644 --- a/tests/Core/test_data_base.py +++ b/tests/Core/test_data_base.py @@ -31,7 +31,7 @@ class DummyField(FieldBase): class TestImmediateField: class DummyImmediate(ImmediateField): @staticmethod - def _normalize_value(value): + def _normalize_value(value, size=0): return value def setup_method(self): From 89e0fd3ef0e21eb88f3fd2b0e5ca8dac234fa686 Mon Sep 17 00:00:00 2001 From: Lansus Date: Sat, 15 Nov 2025 17:22:51 +0800 Subject: [PATCH 4/7] Update README.md structure. --- README.md | 224 +++++++++++++++++++++----------------- pystdf4/Core/data_type.py | 1 + 2 files changed, 123 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 0741fc8..ddde745 100644 --- a/README.md +++ b/README.md @@ -1,139 +1,159 @@ -# **πŸ“¦ pystdf4** +# πŸ“¦ PyStdf4 -A modern, high-performance Python library for creating and parsing **STDF (Standard Test Data Format) v4** files used throughout semiconductor test and manufacturing. +**PyStdf4** is a modern, high-performance Python library for creating and parsing **STDF (Standard Test Data Format) v4** files, widely used in semiconductor testing and manufacturing. ---- +It provides a clean, Pythonic interface that abstracts low-level binary handling, letting engineers focus on data modeling instead of byte-level details. -## **πŸ” Overview** +--- -**pystdf4** offers a clean, Pythonic, and object-oriented interface for working with **STDF v4**, the industry-standard binary format for semiconductor test data. -It abstracts away low-level byte handling and record encoding, allowing engineers to focus on data modeling rather than binary protocol details. +## πŸ” Overview -With **pystdf4**, you can efficiently: +With **PyStdf4**, you can efficiently: -- **Generate STDF v4 files** using structured Python objects -- Build records using a robust and extensible field/record system +- Generate STDF v4 files using structured Python objects +- Build records with a robust, extensible field/record system - Work with a high-performance dynamic byte buffer optimized for large datasets -- Integrate easily into automated test, analysis, and data processing pipelines +- Integrate seamlessly into automated test and analysis pipelines + +> ⚠️ STDF file **parsing (reader API)** is under active development. + +--- + +## πŸ§ͺ Example Usage + +### Installation + +```bash +python -m pip install pystdf4 +``` -Support for **STDF file parsing (reader API)** is actively under development and coming soon. +### Writing an STDF v4 file + +```python +import time +from pystdf4 import Stdf4Writer + +with Stdf4Writer('example.stdf') as stdf: + # File Attributes Record + stdf.FAR(CPU_TYPE=2, STDF_VER=4) + + # Master Information Record + stdf.MIR( + SETUP_T=int(time.time()), + START_T=int(time.time()), + STAT_NUM=0, + BURN_TIM=0, + LOT_ID="Example Lot", + JOB_NAM="Example Job", + PART_TYP="", + NODE_NAM="", + TSTR_TYP="", + ) + + # Add other records here... + +``` --- -## πŸ“˜ STDF4 Data Type Codes - -The **Standard Test Data Format (STDF)** specification defines a compact set of type codes that describe how values are stored and interpreted within records. -Each type code corresponds to a C data type and specifies its binary layout, size, and valid range. - -For a complete and authoritative description of all STDF v4 data types, please refer to the official STDF specification included in this repository (pystdf4/doc/stdf-spec.pdf). -The type code definitions summarized below correspond to the material on page 10 of the specification. - -| Code | Description | C Type Specifier | Notes | -| :------- | :------------------------------------------- | :--------------- | :----------------------------------------------------------------------- | -| `C*12` | Fixed-length character string (12 characters) | `char[12]` | Left-justified; pad with spaces if shorter than 12 characters. | -| `C*n` | Variable-length character string | `char[]` | First byte stores the length (0–255). | -| `C*f` | Variable-length string with external length | `char[]` | Length determined by another record field. | -| `U*1` | 1-byte unsigned integer | `unsigned char` | Range: 0–255 | -| `U*2` | 2-byte unsigned integer | `unsigned short` | Range: 0–65,535 | -| `U*4` | 4-byte unsigned integer | `unsigned long` | Range: 0–4,294,967,295 | -| `I*1` | 1-byte signed integer | `char` | Range: –128 to 127 | -| `I*2` | 2-byte signed integer | `short` | Range: –32,768 to 32,767 | -| `I*4` | 4-byte signed integer | `long` | Range: –2,147,483,648 to 2,147,483,647 | -| `R*4` | 4-byte floating-point number (IEEE 754) | `float` | Standard single-precision float | -| `R*8` | 8-byte floating-point number (IEEE 754) | `double` | Standard double-precision float | -| `B*6` | Fixed-length binary field (6 bytes) | `char[6]` | Raw binary data | -| `V*n` | Variable-type field | β€” | First byte = type code, followed by up to 255 bytes of data | -| `B*n` | Variable-length binary field | `char[]` | First byte = byte count (0–255); data starts at LSB of second byte | -| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count (0–65,535); unused bits in last byte = 0 | -| `N*1` | Nibble (4-bit) data | `char` | Stores unsigned integers in 4-bit units; high nibble zeroed if odd count | -| `kxTYPE` | Array of specified data type | `TYPE[]` | Length `k` determined by another record field (e.g. `kxU*2`) | +## πŸ“˜ STDF v4 Data Types + +STDF defines compact type codes specifying how values are stored and interpreted in records. Below is a concise overview of commonly used types: + +| Code | Description | C Type Specifier | Notes | +| :------- | :-------------------------------------------- | :--------------- | :------------------------------------------- | +| `C*12` | Fixed-length char (12 bytes) | `char[12]` | Left-justified, pad with spaces | +| `C*n` | Variable-length char (1-byte length prefix) | `char[]` | Length 0–255 | +| `C*f` | External-length string | `char[]` | Length defined by another field | +| `U*1` | 1-byte unsigned integer | `unsigned char` | 0–255 | +| `U*2` | 2-byte unsigned integer | `unsigned short` | 0–65,535 | +| `U*4` | 4-byte unsigned integer | `unsigned long` | 0–4,294,967,295 | +| `I*1` | 1-byte signed integer | `char` | –128 to 127 | +| `I*2` | 2-byte signed integer | `short` | –32,768 to 32,767 | +| `I*4` | 4-byte signed integer | `long` | –2,147,483,648 to 2,147,483,647 | +| `R*4` | 4-byte float (IEEE 754) | `float` | Single precision | +| `R*8` | 8-byte float (IEEE 754) | `double` | Double precision | +| `B*6` | Fixed-length binary (6 bytes) | `char[6]` | Raw binary | +| `V*n` | Variable-type field | β€” | First byte = type code, up to 255 bytes data | +| `B*n` | Variable-length binary (1-byte length prefix) | `char[]` | Data starts at second byte | +| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count; padding zeros | +| `N*1` | Nibble array (4-bit units) | `char` | High nibble zeroed if odd count | +| `kxTYPE` | Array of specified type | `TYPE[]` | Length determined by another field | + +_For full STDF v4 type reference, see `pystdf4/doc/stdf-spec.pdf`._ --- ## 🧱 PyStdf4 Data Model -The **PyStdf4 type system** provides a structured, extensible way to represent STDF data elements as Python classes. -Each class encapsulates type conversion, byte parsing, and STDF serialization. +**Field hierarchy for writing STDF records:** ``` + FieldBase(pyT) (ABC) β”œβ”€β”€ ImmediateField(pyT) -β”‚ β”œβ”€β”€ C_1 # fixed-length char (1 byte) -β”‚ β”œβ”€β”€ B_1 # fixed-length binary (1 byte) -β”‚ β”œβ”€β”€ C_n # Pascal string (1-byte length + bytes) -β”‚ └── B_n # Pascal binary (1-byte length + bytes) +β”‚ β”œβ”€β”€ C_1, C_12, B_1, B_6 +β”‚ β”œβ”€β”€ C_n, B_n, C_f +β”‚ └── D_n, N_1 [⚠️ Not Implemented Yet] β”‚ β”œβ”€β”€ DeferredField(pyT) -β”‚ β”œβ”€β”€ U_1 # uint8 -β”‚ β”œβ”€β”€ U_2 # uint16 -β”‚ β”œβ”€β”€ U_4 # uint32 -β”‚ β”œβ”€β”€ I_1 # int8 -β”‚ β”œβ”€β”€ I_2 # int16 -β”‚ β”œβ”€β”€ I_4 # int32 -β”‚ β”œβ”€β”€ R_4 # float32 -β”‚ └── R_8 # float64 +β”‚ β”œβ”€β”€ U_1, U_2, U_4 +β”‚ β”œβ”€β”€ I_1, I_2, I_4 +β”‚ └── R_4, R_8 β”‚ -β”œβ”€β”€ ❌ C_f (Pascal string with external length) -β”œβ”€β”€ ❌ V_n (typed variable field: ) -β”œβ”€β”€ ❌ D_n (bitstring with leading bit-count) -β”œβ”€β”€ ❌ N_1 (nibble array 4-bit) -β”‚ -└── ❌ SequenceField - β”œβ”€β”€ ❌ FixLenField # fixed-size arrays, 如 B*6 - β”œβ”€β”€ ❌ VarLenField # variable arrays with Pascal length - └── ❌ KxLenField # array with external length (kΓ—TYPE) - β”œβ”€β”€ ❌ KxU_1 - β”œβ”€β”€ ❌ KxU_2 - β”œβ”€β”€ ❌ KxC_n - └── ❌ KxR_4 +β”œβ”€β”€ VariableField [⚠️ Not Implemented Yet] +└── ArrayField +β”œβ”€β”€ kxU_1, kxU_2, kxC_n +└── kxN_1 [⚠️ Not Implemented Yet] + ``` -These classes standardize how data flows between **Python**, **C-style internal representations**, and **STDF binary data**. +These classes handle type conversion, byte parsing, and STDF serialization, ensuring consistency between Python objects and STDF binary data. --- -## πŸ“‹ STDF v4 Record Types Implementation Status - -| Record Type | Name | REC_TYP | REC_SUB | Status | Notes | -| ----------- | --------------------------------- | ------- | ------- | ------------- | -------------------------------- | -| FAR | File Attributes Record | 0 | 10 | βœ”οΈ Complete | Required as first record | -| ATR | Audit Trail Record | 0 | 20 | βœ”οΈ Complete | Tracks file modifications | -| MIR | Master Information Record | 1 | 10 | βœ”οΈ Complete | Lot-level information | -| MRR | Master Results Record | 1 | 20 | βœ”οΈ Complete | End of lot summary | -| PCR | Part Count Record | 1 | 30 | βœ”οΈ Complete | Part statistics | -| HBR | Hardware Bin Record | 1 | 40 | βœ”οΈ Complete | Physical binning counts | -| SBR | Software Bin Record | 1 | 50 | βœ”οΈ Complete | Logical binning counts | -| PMR | Pin Map Record | 1 | 60 | βœ”οΈ Complete | Pin/channel mapping | -| PGR | Pin Group Record | 1 | 62 | ❌ Incomplete | Pin grouping | -| PLR | Pin List Record | 1 | 63 | ❌ Incomplete | Pin group display properties | -| RDR | Retest Data Record | 1 | 70 | ❌ Incomplete | Retest information | -| SDR | Site Description Record | 1 | 80 | βœ”οΈ Complete | Test site configuration | -| WIR | Wafer Information Record | 2 | 10 | βœ”οΈ Complete | Wafer start marker | -| WRR | Wafer Results Record | 2 | 20 | βœ”οΈ Complete | Wafer completion summary | -| WCR | Wafer Configuration Record | 2 | 30 | βœ”οΈ Complete | Wafer dimensions/orientation | -| PIR | Part Information Record | 5 | 10 | βœ”οΈ Complete | Part start marker | -| PRR | Part Results Record | 5 | 20 | βœ”οΈ Complete | Part completion results | -| TSR | Test Synopsis Record | 10 | 30 | βœ”οΈ Complete | Test execution statistics | -| PTR | Parametric Test Record | 15 | 10 | βœ”οΈ Complete | Single parametric test result | -| MPR | Multiple-Result Parametric Record | 15 | 15 | ❌ Incomplete | Multiple parametric test results | -| FTR | Functional Test Record | 15 | 20 | ❌ Incomplete | Functional test results | -| BPS | Begin Program Section Record | 20 | 10 | βœ”οΈ Complete | Program section start marker | -| EPS | End Program Section Record | 20 | 20 | βœ”οΈ Complete | Program section end marker | -| GDR | Generic Data Record | 50 | 10 | ❌ Incomplete | User-defined data | -| DTR | Datalog Text Record | 50 | 30 | ❌ Incomplete | Datalog comments | +## πŸ“‹ STDF Record Implementation Status + +| Record | Type | Sub | Status | Notes | +| ------ | ---- | --- | ------------- | ---------------------- | +| FAR | 0 | 10 | βœ”οΈ Complete | Required first record | +| ATR | 0 | 20 | βœ”οΈ Complete | Audit trail | +| MIR | 1 | 10 | βœ”οΈ Complete | Lot-level info | +| MRR | 1 | 20 | βœ”οΈ Complete | End of lot summary | +| PCR | 1 | 30 | βœ”οΈ Complete | Part statistics | +| HBR | 1 | 40 | βœ”οΈ Complete | Physical bin counts | +| SBR | 1 | 50 | βœ”οΈ Complete | Logical bin counts | +| PMR | 1 | 60 | βœ”οΈ Complete | Pin mapping | +| PGR | 1 | 62 | ⚠️ Incomplete | Pin grouping | +| PLR | 1 | 63 | ⚠️ Incomplete | Pin display properties | +| RDR | 1 | 70 | ⚠️ Incomplete | Retest info | +| SDR | 1 | 80 | βœ”οΈ Complete | Site configuration | +| WIR | 2 | 10 | βœ”οΈ Complete | Wafer start marker | +| WRR | 2 | 20 | βœ”οΈ Complete | Wafer summary | +| WCR | 2 | 30 | βœ”οΈ Complete | Wafer config | +| PIR | 5 | 10 | βœ”οΈ Complete | Part start marker | +| PRR | 5 | 20 | βœ”οΈ Complete | Part results | +| TSR | 10 | 30 | βœ”οΈ Complete | Test summary | +| PTR | 15 | 10 | βœ”οΈ Complete | Parametric test | +| MPR | 15 | 15 | ⚠️ Incomplete | Multiple parametric | +| FTR | 15 | 20 | ⚠️ Incomplete | Functional test | +| BPS | 20 | 10 | βœ”οΈ Complete | Program section start | +| EPS | 20 | 20 | βœ”οΈ Complete | Program section end | +| GDR | 50 | 10 | ⚠️ Incomplete | User-defined data | +| DTR | 50 | 30 | ⚠️ Incomplete | Datalog comments | --- -## πŸ§ͺ Example Use Cases (Coming Soon) +## πŸ—ΊοΈ Roadmap -- Parse STDF records into structured Python objects -- Convert STDF to human-readable CSV or JSON -- Build new STDF records programmatically -- Validate field encoding/decoding across STDF revisions +| Version | Goals | +| -------- | -------------------------------------------------------------------------------------------------------------------------------- | +| **v0.x** | βœ… Core STDF types & common records
⬜ Fully functional `StdfWriter`
⬜ Robust Pythonic writing API | +| **v1.x** | ⬜ SmartWriter (auto record creation)
⬜ High-level APIs for stats & aggregation
⬜ Intelligent record dependency handling | +| **v2.x** | ⬜ `StdfReader` (efficient parsing)
⬜ Integration with analysis pipelines
⬜ Full read/write compatibility | --- ## πŸ“œ License -MIT License Β© 2025 -Developed for efficient and reliable STDF data manipulation in modern semiconductor workflows. +MIT Β© 2025 β€” Developed for efficient and reliable STDF data manipulation in modern semiconductor workflows. diff --git a/pystdf4/Core/data_type.py b/pystdf4/Core/data_type.py index 6a701dd..023678a 100644 --- a/pystdf4/Core/data_type.py +++ b/pystdf4/Core/data_type.py @@ -113,5 +113,6 @@ class kxR_4(ArrayField): element_type = R_4 +# TODO: Implement N_1 field! # class kxN_1(ArrayField): # element_type = N_1 From 9d7dc4965fdf854eaf594abae1f77d3dda242f40 Mon Sep 17 00:00:00 2001 From: Lansus Date: Sat, 15 Nov 2025 17:25:51 +0800 Subject: [PATCH 5/7] Remove main.py. --- main.py | 570 -------------------------------------------------------- 1 file changed, 570 deletions(-) delete mode 100644 main.py diff --git a/main.py b/main.py deleted file mode 100644 index 442d9d9..0000000 --- a/main.py +++ /dev/null @@ -1,570 +0,0 @@ -import csv -import math -import re -import struct -import sys -from itertools import zip_longest -from pathlib import Path -from typing import List, Optional, Tuple - -from pystdf4.IO import Stdf4Writer - - -def parse_test_data_segment(test_data_segment: List[str]) -> Tuple[float, ...]: - return tuple(map(safg_float, test_data_segment)) - - -def parse_coordinates_segment(coordinates_segment: List[str]) -> Tuple[int, ...]: - return tuple(map(int, coordinates_segment)) - - -def parse_moving_limit_segment(moving_limit_segment: str) -> Tuple[str, ...]: - return tuple(moving_limit_segment.split(";")) - - -def parse_bins_segment(bins_segment: List[str]) -> Tuple[int, ...]: - return tuple(map(int, bins_segment)) - - -def parse_site_info_segment( - site_info_segment: List[str], -) -> Tuple[Tuple[int, ...], ...]: - site_number = len(site_info_segment) // 3 - if site_number * 3 != len(site_info_segment): - raise ValueError("Site info segment should have 3 values per site.") - return tuple( - tuple( - map( - safe_int, - [ - site_info_segment[i], - site_info_segment[site_number + 2 * i], - site_info_segment[site_number + 2 * i + 1], - ], - ) - ) - for i in range(site_number) - ) - - -def safg_float(value: str) -> float: - """ - Convert a string to float, and return nan if the string is empty. - - Args: - value (str): _description_ - - Returns: - float: _description_ - """ - return float(value) if value else math.nan - - -def safe_int(value: str) -> int: - """ - Convert a string to int, and return 0 if the string is empty. - - Args: - value (str): _description_ - - Returns: - int: _description_ - """ - return int(value) if value else 0 - - -class PTR_D: - __slots__ = ( - "test_number", - "result", - "test_txt", - "lo_limit", - "hi_limit", - "unit", - "test_flag", - ) - - def __init__( - self, - test_number: int, - result: float, - test_txt: str, - lo_limit: float, - hi_limit: float, - unit: str, - test_pass: Optional[bool], - ): - """ - Part test parameters - - Args: - test_number (int): _description_ - site_number (int): _description_ - result (float): _description_ - test_txt (str): _description_ - lo_limit (float): _description_ - hi_limit (float): _description_ - unit (str): _description_ - test_flag (bool, optional): _description_. Defaults to None. - """ - self.test_number = test_number - self.result = result - self.test_txt = test_txt - self.lo_limit = lo_limit - self.hi_limit = hi_limit - self.unit = unit - # test_pass may be True, False or None - # if test_pass is None, it means test_flg should be calculated based on lo_limit, hi_limit and result - # if test_pass in True or False, directly set test_flg based on test_pass - test_pass = ( - test_pass if test_pass is not None else (lo_limit <= result <= hi_limit) - ) - self.test_flag = 0 - if not test_pass: - self.test_flag |= 1 << 7 - - -class PRR_D: - __slots__ = ("hard_bin", "soft_bin", "x_coord", "y_coord", "test_t", "part_id") - - @property - def part_flag(self) -> int: - return 0 if self.hard_bin == 1 else 8 - - def __init__( - self, - hard_bin: int, - soft_bin: int, - x_coord: int, - y_coord: int, - test_t: int, - part_id: int, - ): - self.hard_bin = hard_bin - self.soft_bin = soft_bin - self.x_coord = x_coord - self.y_coord = y_coord - self.test_t = test_t - self.part_id = part_id - - -class Part: - __slots__ = ("site_num", "PTRs", "PRR") - PRR: Optional[PRR_D] - PTRs: Tuple[PTR_D, ...] - - def __init__(self, site_num: int = -1): - self.site_num = site_num - self.PRR = None - self.PTRs = tuple() - - def update_ptrs(self, ptrs: Tuple[PTR_D, ...]): - self.PTRs = ptrs - - def edit_prr( - self, - hard_bin: int, - soft_bin: int, - x_coord: int, - y_coord: int, - test_t: int, - part_id: int, - ): - self.PRR = PRR_D( - hard_bin=hard_bin, - soft_bin=soft_bin, - x_coord=x_coord, - y_coord=y_coord, - test_t=test_t, - part_id=part_id, - ) - - def record(self): - pass - - -class StdfGenerator: - csv_site_header_partten = re.compile(r"^Site(\d+) (Time|SOT|EOT)") - pat_item_pattern = re.compile(r"\[\[?(\w+)&\w+(\d+)\|([\d\.]+)\]?\]") - - @property - def site_count(self) -> int: - return len(self.site_indices) - - @property - def test_count(self) -> int: - return len(self.lower_limits) - - def __init__( - self, - lower_limits: Tuple[float, ...], - higher_limits: Tuple[float, ...], - units: Tuple[str, ...], - test_txts: Tuple[str, ...], - site_indices: Tuple[int, ...], - ): - """ - Generate parts based on the given parameters. - - Args: - lower_limits (Tuple[float, ...]): _description_ - higher_limits (Tuple[float, ...]): _description_ - units (Tuple[str, ...]): _description_ - test_txts (Tuple[str, ...]): _description_ - site_indices (Tuple[int, ...]): _description_ - """ - # Check input parameters validity - if len(lower_limits) != len(higher_limits) or len(lower_limits) != len(units): - raise ValueError("Length of limits, units and test_txts should be equal.") - - # Check site indices validity - if len(site_indices) == 0: - raise ValueError("Site indices should not be empty.") - - self.lower_limits = lower_limits - self.higher_limits = higher_limits - self.units = units - self.test_txts = test_txts - self.site_indices = site_indices - - # data properties - self.parts: List[Part] = [] - - # Stastics properties - self.failed_count: dict[str, int] = {txt: 0 for txt in test_txts} - self.failed_dut_count: int = 0 - self.passed_dut_count: int = 0 - - def __repr__(self) -> str: - repr_str = f"StdfGenerator(Site indices: {self.site_indices}; Test Items: {self.test_count})" - for i, (lower_limit, higher_limit, unit, test_txt) in enumerate( - zip(self.lower_limits, self.higher_limits, self.units, self.test_txts) - ): - repr_str += f"\n\tIndex: {i + 1}; Test Text: {test_txt}; Limit: [{lower_limit} - {higher_limit} {unit}]" - - return repr_str - - @classmethod - def generate_stdf_object( - cls, - lower_limit_header: List[str], - higher_limit_header: List[str], - unit_header: List[str], - test_txt_header: List[str], - ) -> "StdfGenerator": - lower_limits, higher_limits, units, test_txts = [], [], [], [] - site_indices = set() - - for lower_limit, higher_limit, unit, test_txt in zip_longest( - lower_limit_header, - higher_limit_header, - unit_header, - test_txt_header, - fillvalue="", - ): - if test_txt in {"X", "Y", "MovingLimitFail", "HardBin", "SoftBin"}: - continue - elif match_rst := cls.csv_site_header_partten.match(test_txt): - site_index = int(match_rst.group(1)) - site_indices.add(site_index) - else: - lower_limits.append(float(lower_limit)) - higher_limits.append(float(higher_limit)) - units.append(unit) - test_txts.append(test_txt) - return cls( - tuple(lower_limits), - tuple(higher_limits), - tuple(units), - tuple(test_txts), - tuple(site_indices), - ) - - def evaluate_part_test( - self, - test_results: Tuple[float, ...], - coordinates: Tuple[int, ...], - moving_limit: Tuple[str, ...], - bins: Tuple[int, ...], - sites_information: Tuple[Tuple[int, ...], ...], - part_id: int, - ): - """ - Evaluate the test result and return the test flag. - - Args: - test_results (Tuple[float, ...]): _description_ - """ - # Check input parameters validity - if len(test_results) != self.test_count: - raise ValueError("Length of test_results should be equal to test_count.") - - # Get actual site number according to the SOT/EOT and Time - site_number = -1 - for site_index, site_info in enumerate(sites_information): - if sum(site_info) == 0: - continue - elif site_number == -1: - site_number = site_index - else: - raise ValueError("Site number should be set only once!") - - # Create Part object and update PTRs - if site_number == -1: - raise ValueError("Site number should be set!") - - part_obj = Part(self.site_indices[site_number]) - - ptrs = [] - moving_limit_set = set(moving_limit) - - # Append PTR objects to ptrs list - for i, (lower_limit, higher_limit, test_txt, unit, result) in enumerate( - zip( - self.lower_limits, - self.higher_limits, - self.test_txts, - self.units, - test_results, - ), - start=1, - ): - test_pass = ( - test_txt not in moving_limit_set - if self.pat_item_pattern.match(test_txt) - else None - ) - ptr = PTR_D( - test_number=i, - result=result, - test_txt=test_txt, - lo_limit=lower_limit, - hi_limit=higher_limit, - unit=unit, - test_pass=test_pass, - ) - if ptr.test_flag != 0: - self.failed_count[test_txt] += 1 - - ptrs.append(ptr) - - # Update PRR_D object - part_obj.update_ptrs(tuple(ptrs)) - - # Edit PRR_D object - part_obj.edit_prr( - hard_bin=bins[0], - soft_bin=bins[1], - x_coord=coordinates[0], - y_coord=coordinates[1], - test_t=sites_information[site_number][0], - part_id=part_id, - ) - if bins[0] != 1: - self.failed_dut_count += 1 - else: - self.passed_dut_count += 1 - - self.parts.append(part_obj) - - @staticmethod - def _gen_stdf_record(typ: int, sub: int, data: bytes) -> bytes: - return struct.pack(" Date: Sat, 15 Nov 2025 21:20:09 +0800 Subject: [PATCH 6/7] Implements encoding for most Records. --- README.md | 14 +- pystdf4/Core/__init__.py | 31 +- pystdf4/Core/data_type.py | 14 +- pystdf4/IO/base.py | 54 -- pystdf4/IO/stdf4reader.py | 6 +- pystdf4/IO/stdf4writer.py | 623 +---------------- pystdf4/Records/__init__.py | 0 pystdf4/Records/base.py | 50 ++ pystdf4/Records/literials.py | 54 ++ pystdf4/Records/packer.py | 1235 ++++++++++++++++++++++++++++++++++ 10 files changed, 1378 insertions(+), 703 deletions(-) create mode 100644 pystdf4/Records/__init__.py create mode 100644 pystdf4/Records/base.py create mode 100644 pystdf4/Records/literials.py create mode 100644 pystdf4/Records/packer.py diff --git a/README.md b/README.md index ddde745..4b69553 100644 --- a/README.md +++ b/README.md @@ -102,9 +102,11 @@ FieldBase(pyT) (ABC) β”‚ └── R_4, R_8 β”‚ β”œβ”€β”€ VariableField [⚠️ Not Implemented Yet] +β”‚ └── V_n +β”‚ └── ArrayField -β”œβ”€β”€ kxU_1, kxU_2, kxC_n -└── kxN_1 [⚠️ Not Implemented Yet] + β”œβ”€β”€ kxU_1, kxU_2, kxC_n + └── kxN_1 [⚠️ Not Implemented Yet] ``` @@ -124,9 +126,9 @@ These classes handle type conversion, byte parsing, and STDF serialization, ensu | HBR | 1 | 40 | βœ”οΈ Complete | Physical bin counts | | SBR | 1 | 50 | βœ”οΈ Complete | Logical bin counts | | PMR | 1 | 60 | βœ”οΈ Complete | Pin mapping | -| PGR | 1 | 62 | ⚠️ Incomplete | Pin grouping | -| PLR | 1 | 63 | ⚠️ Incomplete | Pin display properties | -| RDR | 1 | 70 | ⚠️ Incomplete | Retest info | +| PGR | 1 | 62 | βœ”οΈ Complete | Pin grouping | +| PLR | 1 | 63 | βœ”οΈ Complete | Pin display properties | +| RDR | 1 | 70 | βœ”οΈ Complete | Retest info | | SDR | 1 | 80 | βœ”οΈ Complete | Site configuration | | WIR | 2 | 10 | βœ”οΈ Complete | Wafer start marker | | WRR | 2 | 20 | βœ”οΈ Complete | Wafer summary | @@ -140,7 +142,7 @@ These classes handle type conversion, byte parsing, and STDF serialization, ensu | BPS | 20 | 10 | βœ”οΈ Complete | Program section start | | EPS | 20 | 20 | βœ”οΈ Complete | Program section end | | GDR | 50 | 10 | ⚠️ Incomplete | User-defined data | -| DTR | 50 | 30 | ⚠️ Incomplete | Datalog comments | +| DTR | 50 | 30 | βœ”οΈ Complete | Datalog comments | --- diff --git a/pystdf4/Core/__init__.py b/pystdf4/Core/__init__.py index 9436977..b9bb9f9 100644 --- a/pystdf4/Core/__init__.py +++ b/pystdf4/Core/__init__.py @@ -1,31 +1,22 @@ -from .data_type import ( - B_1, - C_1, - I_1, - I_2, - I_4, - R_4, - R_8, - U_1, - U_2, - U_4, - B_n, - C_n, -) +from .data_type import B_1, C_1, I_1, I_2, I_4, R_4, R_8, U_1, U_2, U_4, B_n, C_n, kxC_n, kxR_4, kxU_1, kxU_2 from .dynamic_buffer import DynamicBuffer __all__ = [ "DynamicBuffer", - "U_1", - "U_2", - "U_4", + "B_1", + "C_1", "I_1", "I_2", "I_4", "R_4", "R_8", - "C_1", - "C_n", - "B_1", + "U_1", + "U_2", + "U_4", "B_n", + "C_n", + "kxC_n", + "kxR_4", + "kxU_1", + "kxU_2", ] diff --git a/pystdf4/Core/data_type.py b/pystdf4/Core/data_type.py index 023678a..43786de 100644 --- a/pystdf4/Core/data_type.py +++ b/pystdf4/Core/data_type.py @@ -42,7 +42,7 @@ class R_8(DeferredField[float]): # ============================================================ -class C_1(ImmediateField): +class C_1(ImmediateField[str]): field_size = 1 @staticmethod @@ -50,7 +50,7 @@ def _normalize_value(value: str, size: int = 0) -> bytes: return value.encode("ascii") -class C_12(ImmediateField): +class C_12(ImmediateField[str]): field_size = 12 @staticmethod @@ -58,7 +58,7 @@ def _normalize_value(value: str, size: int = 0) -> bytes: return value.encode("ascii") -class B_1(ImmediateField): +class B_1(ImmediateField[bytes]): field_size = 1 @staticmethod @@ -66,7 +66,7 @@ def _normalize_value(value: bytes, size: int = 0) -> bytes: return value -class B_6(ImmediateField): +class B_6(ImmediateField[bytes]): field_size = 6 @staticmethod @@ -74,19 +74,19 @@ def _normalize_value(value: bytes, size: int = 0) -> bytes: return value -class C_n(ImmediateField): +class C_n(ImmediateField[str]): @staticmethod def _normalize_value(value: str, size: int = 0) -> bytes: return ImmediateField._pascal_bytes(value.encode("ascii")) -class B_n(ImmediateField): +class B_n(ImmediateField[bytes]): @staticmethod def _normalize_value(value: bytes, size: int = 0) -> bytes: return ImmediateField._pascal_bytes(value) -class C_f(ImmediateField): +class C_f(ImmediateField[str]): @staticmethod def _normalize_value(value: bytes, size: int = 0) -> bytes: return ImmediateField._left_justified_bytes(value, size, b" ") diff --git a/pystdf4/IO/base.py b/pystdf4/IO/base.py index 42d4a02..e69de29 100644 --- a/pystdf4/IO/base.py +++ b/pystdf4/IO/base.py @@ -1,54 +0,0 @@ -from pathlib import Path -from struct import Struct - -from pystdf4.Core import B_1, C_1, I_1, I_2, I_4, R_4, R_8, U_1, U_2, U_4, B_n, C_n -from pystdf4.Core.dynamic_buffer import DynamicBuffer - - -class StdfIOBase: - __slots__ = ( - "file_path", - "buffer", - "B_1", - "C_1", - "I_1", - "I_2", - "I_4", - "R_4", - "R_8", - "U_1", - "U_2", - "U_4", - "B_n", - "C_n", - "KxC_n", - "KxR_4", - "KxU_1", - "KxU_2", - "header_packer", - ) - - def __init__(self, file_path: str): - self.buffer = DynamicBuffer() - self.file_path = Path(file_path) - - self.B_1 = B_1() - self.C_1 = C_1() - self.I_1 = I_1() - self.I_2 = I_2() - self.I_4 = I_4() - self.R_4 = R_4() - self.R_8 = R_8() - self.U_1 = U_1() - self.U_2 = U_2() - self.U_4 = U_4() - self.B_n = B_n() - self.C_n = C_n() - - self.header_packer: Struct = Struct(" "Stdf4Writer": if not self.file_path.exists(): self.file_path.touch() - return super().__enter__() + return self def __exit__(self, exc_type: type, exc_val: Exception, exc_tb: object) -> None: - self.write_buffers() + self._write_buffers() self.file_path.write_bytes(self.buffer.to_bytes()) - def write_fields(self, rec_typ: int, rec_sub: int, field_writer: Callable): - # Step 1: Write the header of the record - record_start = self._write_header((rec_typ, rec_sub)) + # endregion - # Step 2: Write the fields of the record - field_writer(self) - - # Step 3: Update the length of the record - record_end = self.buffer.offset - record_length = record_end - (record_start + 4) - self.buffer._mv[record_start : record_start + 2] = record_length.to_bytes( - 2, byteorder="little" - ) - - def _write_header(self, header: Tuple[int, int]) -> int: - """ - Write the header of a record to the file. - """ - packer_size = self.header_packer.size - self.buffer._ensure_capacity(packer_size) - start = self.buffer.offset - self.header_packer.pack_into(self.buffer._mv, start, 0, *header) - self.buffer.offset += packer_size - return start - - def write_buffers(self): + def _write_buffers(self): for scalar_field in ( self.U_1, self.U_2, @@ -53,585 +32,3 @@ def write_buffers(self): self.R_8, ): scalar_field.flush_cache_to_buffer(self.buffer) - - def ATR(self, MOD_TIM: int, CMD_LINE: str): - """Audit Trail Record""" - - # Implementation here - def write_fields(self): - self.U_4.pack_into(self.buffer, MOD_TIM) - self.C_n.pack_into(self.buffer, CMD_LINE) - - self.write_fields(0, 20, write_fields) - - def BPS(self, SEQ_NAME: str = ""): - """Begin Program Section Record""" - # Implementation here - pass - - def DTR(self, TEXT_DAT: str): - """Datalog Text Record""" - # Implementation here - pass - - def EPS(self): - """End Program Section Record""" - # Implementation here - pass - - def FAR(self, CPU_TYPE: int, STDF_VER: int): - """File Attributes Record""" - - # Implementation here - def write_fields(self): - self.U_1.pack_into(self.buffer, CPU_TYPE) - self.U_1.pack_into(self.buffer, STDF_VER) - - self.write_fields(0, 10, write_fields) - - def FTR( - self, - TEST_NUM: int, - HEAD_NUM: int, - SITE_NUM: int, - TEST_FLG: bytes, - OPT_FLAG: bytes, - RTN_ICNT: int, - PGM_ICNT: int, - CYCL_CNT: int = 0, - REL_VADR: int = 0, - REPT_CNT: int = 0, - NUM_FAIL: int = 0, - XFAIL_AD: int = 0, - YFAIL_AD: int = 0, - VECT_OFF: int = 0, - RTN_INDX: Optional[Sequence[int]] = None, - RTN_STAT: Optional[Sequence[bytes]] = None, - PGM_INDX: Optional[Sequence[int]] = None, - PGM_STAT: Optional[Sequence[bytes]] = None, - FAIL_PIN: bytes = b"", - VECT_NAM: str = "", - TIME_SET: str = "", - OP_CODE: str = "", - TEST_TXT: str = "", - ALARM_ID: str = "", - PROG_TXT: str = "", - RSLT_TXT: str = "", - PATG_NUM: int = 255, - SPIN_MAP: bytes = b"", - ): - """Functional Test Record""" - # Implementation here - pass - - def GDR(self, FLD_CNT: int, GEN_DATA: Any): - """Generic Data Record""" - # Implementation here - pass - - def HBR( - self, - SITE_NUM: int, - HBIN_NUM: int, - HBIN_CNT: int, - HEAD_NUM: int = 255, - HBIN_PF: Literal["P", "F", " "] = " ", - HBIN_NAM: str = "", - ): - """Hardware Bin Record""" - # Implementation here - - def write_fields(self): - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_NUM) - self.U_2.pack_into(self.buffer, HBIN_NUM) - self.U_4.pack_into(self.buffer, HBIN_CNT) - self.C_1.pack_into(self.buffer, HBIN_PF) - self.C_n.pack_into(self.buffer, HBIN_NAM) - - self.write_fields(1, 40, write_fields) - - def MIR( - self, - SETUP_T: int, - START_T: int, - STAT_NUM: int, - LOT_ID: str, - PART_TYP: str, - NODE_NAM: str, - TSTR_TYP: str, - JOB_NAM: str, - MODE_COD: Literal[ - "A", - "C", - "D", - "E", - "M", - "P", - "Q", - "0", - "1", - "2", - # Implementation here - "3", - "4", - "5", - "6", - "7", - "8", - "9", - " ", - ] = " ", - RTST_COD: Literal[ - "Y", "N", "1", "2", "3", "4", "5", "6", "7", "8", "9", " " - ] = " ", - PROT_COD: str = " ", - BURN_TIM: int = 65535, - CMOD_COD: str = " ", - JOB_REV: str = "", - SBLOT_ID: str = "", - OPER_NAM: str = "", - EXEC_TYP: str = "", - EXEC_VER: str = "", - TEST_COD: str = "", - TST_TEMP: str = "", - USER_TXT: str = "", - AUX_FILE: str = "", - PKG_TYP: str = "", - FAMLY_ID: str = "", - DATE_COD: str = "", - FACIL_ID: str = "", - FLOOR_ID: str = "", - PROC_ID: str = "", - OPER_FRQ: str = "", - SPEC_NAM: str = "", - SPEC_VER: str = "", - FLOW_ID: str = "", - SETUP_ID: str = "", - DSGN_REV: str = "", - ENG_ID: str = "", - ROM_COD: str = "", - SERL_NUM: str = "", - SUPR_NAM: str = "", - ): - """Master Information Record""" - # Implementation here - - def write_fields(self): - self.U_4.pack_into(self.buffer, SETUP_T) - self.U_4.pack_into(self.buffer, START_T) - self.U_1.pack_into(self.buffer, STAT_NUM) - self.C_1.pack_into(self.buffer, MODE_COD) - self.C_1.pack_into(self.buffer, RTST_COD) - self.C_1.pack_into(self.buffer, PROT_COD) - self.U_2.pack_into(self.buffer, BURN_TIM) - self.C_1.pack_into(self.buffer, CMOD_COD) - self.C_n.pack_into(self.buffer, LOT_ID) - self.C_n.pack_into(self.buffer, PART_TYP) - self.C_n.pack_into(self.buffer, NODE_NAM) - self.C_n.pack_into(self.buffer, TSTR_TYP) - self.C_n.pack_into(self.buffer, JOB_NAM) - self.C_n.pack_into(self.buffer, JOB_REV) - self.C_n.pack_into(self.buffer, SBLOT_ID) - self.C_n.pack_into(self.buffer, OPER_NAM) - self.C_n.pack_into(self.buffer, EXEC_TYP) - self.C_n.pack_into(self.buffer, EXEC_VER) - self.C_n.pack_into(self.buffer, TEST_COD) - self.C_n.pack_into(self.buffer, TST_TEMP) - self.C_n.pack_into(self.buffer, USER_TXT) - self.C_n.pack_into(self.buffer, AUX_FILE) - self.C_n.pack_into(self.buffer, PKG_TYP) - self.C_n.pack_into(self.buffer, FAMLY_ID) - self.C_n.pack_into(self.buffer, DATE_COD) - self.C_n.pack_into(self.buffer, FACIL_ID) - self.C_n.pack_into(self.buffer, FLOOR_ID) - self.C_n.pack_into(self.buffer, PROC_ID) - self.C_n.pack_into(self.buffer, OPER_FRQ) - self.C_n.pack_into(self.buffer, SPEC_NAM) - self.C_n.pack_into(self.buffer, SPEC_VER) - self.C_n.pack_into(self.buffer, FLOW_ID) - self.C_n.pack_into(self.buffer, SETUP_ID) - self.C_n.pack_into(self.buffer, DSGN_REV) - self.C_n.pack_into(self.buffer, ENG_ID) - self.C_n.pack_into(self.buffer, ROM_COD) - self.C_n.pack_into(self.buffer, SERL_NUM) - self.C_n.pack_into(self.buffer, SUPR_NAM) - - self.write_fields(1, 10, write_fields) - - def MPR( - self, - TEST_NUM: int, - HEAD_NUM: int, - SITE_NUM: int, - TEST_FLG: bytes, - PARM_FLG: bytes, - RTN_ICNT: int, - RSLT_CNT: int, - OPT_FLAG: bytes, - RTN_STAT: Optional[Sequence[bytes]] = None, - RTN_RSLT: Optional[Sequence[float]] = None, - TEST_TXT: str = "", - ALARM_ID: str = "", - RES_SCAL: int = 0, - LLM_SCAL: int = 0, - HLM_SCAL: int = 0, - LO_LIMIT: float = 0.0, - HI_LIMIT: float = 0.0, - START_IN: float = 0.0, - INCR_IN: float = 0.0, - RTN_INDX: Optional[Sequence[int]] = None, - UNITS: str = "", - UNITS_IN: str = "", - C_RESFMT: str = "", - C_LLMFMT: str = "", - C_HLMFMT: str = "", - LO_SPEC: float = 0.0, - HI_SPEC: float = 0.0, - ): - """Multiple-Result Parametric Record""" - # Implementation here - - def MRR( - self, - FINISH_T: int, - DISP_COD: str = " ", - USR_DESC: str = "", - EXC_DESC: str = "", - ): - """Master Results Record""" - # Implementation here - - def write_fields(self): - self.U_4.pack_into(self.buffer, FINISH_T) - self.C_1.pack_into(self.buffer, DISP_COD) - self.C_n.pack_into(self.buffer, USR_DESC) - self.C_n.pack_into(self.buffer, EXC_DESC) - - self.write_fields(1, 20, write_fields) - - def PCR( - self, - SITE_NUM: int, - PART_CNT: int, - HEAD_NUM: int = 255, - RTST_CNT: int = 4294967295, - ABRT_CNT: int = 4294967295, - GOOD_CNT: int = 4294967295, - FUNC_CNT: int = 4294967295, - ): - """Part Count Record""" - # Implementation here - - def write_fields(self): - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_NUM) - self.U_4.pack_into(self.buffer, PART_CNT) - self.U_4.pack_into(self.buffer, RTST_CNT) - self.U_4.pack_into(self.buffer, ABRT_CNT) - self.U_4.pack_into(self.buffer, GOOD_CNT) - self.U_4.pack_into(self.buffer, FUNC_CNT) - - self.write_fields(1, 30, write_fields) - - def PGR( - self, - GRP_INDX: int, - INDX_CNT: int, - PMR_INDX: Optional[Sequence[int]] = None, - GRP_NAM: str = "", - ): - """Pin Group Record""" - # Implementation here - pass - - def PIR( - self, - HEAD_NUM: int, - SITE_NUM: int, - ): - """Part Information Record""" - # Implementation here - - def write_fields(self): - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_NUM) - - self.write_fields(5, 10, write_fields) - - def PLR( - self, - GRP_CNT: int, - GRP_INDX: Optional[Sequence[int]] = None, - GRP_MODE: Optional[Sequence[int]] = None, - GRP_RADX: Optional[Sequence[int]] = None, - PGM_CHAR: Optional[Sequence[str]] = None, - RTN_CHAR: Optional[Sequence[str]] = None, - PGM_CHAL: Optional[Sequence[str]] = None, - RTN_CHAL: Optional[Sequence[str]] = None, - ): - """Pin List Record""" - # Implementation here - pass - - def PMR( - self, - PMR_INDX: int, - CHAN_TYP: int = 0, - CHAN_NAM: str = "", - PHY_NAM: str = "", - LOG_NAM: str = "", - HEAD_NUM: int = 1, - SITE_NUM: int = 1, - ): - """Pin Map Record""" - # Implementation here - pass - - def PRR( - self, - HEAD_NUM: int, - SITE_NUM: int, - PART_FLG: bytes, - NUM_TEST: int, - HARD_BIN: int, - SOFT_BIN: int = 65535, - X_COORD: int = -32768, - Y_COORD: int = -32768, - TEST_T: int = 0, - PART_ID: str = "", - PART_TXT: str = "", - PART_FIX: bytes = b"", - ): - """Part Results Record""" - # Implementation here - - def write_fields(self): - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_NUM) - self.B_1.pack_into(self.buffer, PART_FLG) - self.U_2.pack_into(self.buffer, NUM_TEST) - self.U_2.pack_into(self.buffer, HARD_BIN) - self.U_2.pack_into(self.buffer, SOFT_BIN) - self.I_2.pack_into(self.buffer, X_COORD) - self.I_2.pack_into(self.buffer, Y_COORD) - self.U_4.pack_into(self.buffer, TEST_T) - self.C_n.pack_into(self.buffer, PART_ID) - self.C_n.pack_into(self.buffer, PART_TXT) - self.B_n.pack_into(self.buffer, PART_FIX) - - self.write_fields(5, 20, write_fields) - - def PTR( - self, - TEST_NUM: int, - HEAD_NUM: int, - SITE_NUM: int, - TEST_FLG: bytes, - PARM_FLG: bytes, - OPT_FLAG: bytes, - RESULT: float = 0.0, - TEST_TXT: str = "", - ALARM_ID: str = "", - RES_SCAL: int = 0, - LLM_SCAL: int = 0, - HLM_SCAL: int = 0, - LO_LIMIT: float = 0.0, - HI_LIMIT: float = 0.0, - UNITS: str = "", - C_RESFMT: str = "", - C_LLMFMT: str = "", - C_HLMFMT: str = "", - LO_SPEC: float = 0.0, - HI_SPEC: float = 0.0, - ): - """Parametric Test Record""" - - def write_fields(self): - self.U_4.pack_into(self.buffer, TEST_NUM) - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_NUM) - self.B_1.pack_into(self.buffer, TEST_FLG) - self.B_1.pack_into(self.buffer, PARM_FLG) - self.R_4.pack_into(self.buffer, RESULT) - self.C_n.pack_into(self.buffer, TEST_TXT) - self.C_n.pack_into(self.buffer, ALARM_ID) - self.B_1.pack_into(self.buffer, OPT_FLAG) - self.I_1.pack_into(self.buffer, RES_SCAL) - self.I_1.pack_into(self.buffer, LLM_SCAL) - self.I_1.pack_into(self.buffer, HLM_SCAL) - self.R_4.pack_into(self.buffer, LO_LIMIT) - self.R_4.pack_into(self.buffer, HI_LIMIT) - self.C_n.pack_into(self.buffer, UNITS) - self.C_n.pack_into(self.buffer, C_RESFMT) - self.C_n.pack_into(self.buffer, C_LLMFMT) - self.C_n.pack_into(self.buffer, C_HLMFMT) - self.R_4.pack_into(self.buffer, LO_SPEC) - self.R_4.pack_into(self.buffer, HI_SPEC) - - self.write_fields(15, 10, write_fields) - - def RDR(self, NUM_BINS: int, RTST_BIN: Optional[Sequence[int]] = None): - """Retest Data Record""" - # Implementation here - pass - - def SBR( - self, - SITE_NUM: int, - SBIN_NUM: int, - SBIN_CNT: int, - HEAD_NUM: int = 255, - SBIN_PF: Literal["P", "F", " "] = " ", - SBIN_NAM: str = "", - ): - """Software Bin Record""" - # Implementation here - pass - - def SDR( - self, - HEAD_NUM: int, - SITE_GRP: int, - SITE_CNT: int, - SITE_NUM: Optional[Sequence[int]] = None, - HAND_TYP: str = "", - HAND_ID: str = "", - CARD_TYP: str = "", - CARD_ID: str = "", - LOAD_TYP: str = "", - LOAD_ID: str = "", - DIB_TYP: str = "", - DIB_ID: str = "", - CABL_TYP: str = "", - CABL_ID: str = "", - CONT_TYP: str = "", - CONT_ID: str = "", - LASR_TYP: str = "", - LASR_ID: str = "", - EXTR_TYP: str = "", - EXTR_ID: str = "", - ): - """Site Description Record""" - # Implementation here - - def write_fields(self): - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_GRP) - self.U_1.pack_into(self.buffer, SITE_CNT) - # self.KxU_1.pack_into(SITE_CNT, SITE_NUM) - self.C_n.pack_into(self.buffer, HAND_TYP) - self.C_n.pack_into(self.buffer, HAND_ID) - self.C_n.pack_into(self.buffer, CARD_TYP) - self.C_n.pack_into(self.buffer, CARD_ID) - self.C_n.pack_into(self.buffer, LOAD_TYP) - self.C_n.pack_into(self.buffer, LOAD_ID) - self.C_n.pack_into(self.buffer, DIB_TYP) - self.C_n.pack_into(self.buffer, DIB_ID) - self.C_n.pack_into(self.buffer, CABL_TYP) - self.C_n.pack_into(self.buffer, CABL_ID) - self.C_n.pack_into(self.buffer, CONT_TYP) - self.C_n.pack_into(self.buffer, CONT_ID) - self.C_n.pack_into(self.buffer, LASR_TYP) - self.C_n.pack_into(self.buffer, LASR_ID) - self.C_n.pack_into(self.buffer, EXTR_TYP) - self.C_n.pack_into(self.buffer, EXTR_ID) - - self.write_fields(1, 80, write_fields) - - def TSR( - self, - HEAD_NUM: int, - SITE_NUM: int, - TEST_NUM: int, - OPT_FLAG: bytes, - TEST_TYP: Literal["P", "F", "M", " "] = " ", - EXEC_CNT: int = 4294967295, - FAIL_CNT: int = 4294967295, - ALRM_CNT: int = 4294967295, - TEST_NAM: str = "", - SEQ_NAME: str = "", - TEST_LBL: str = "", - TEST_TIM: float = 0.0, - TEST_MIN: float = 0.0, - TEST_MAX: float = 0.0, - TST_SUMS: float = 0.0, - TST_SQRS: float = 0.0, - ): - """Test Synopsis Record""" - # Implementation here - - def write_fields(self): - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_NUM) - self.C_1.pack_into(self.buffer, TEST_TYP) - self.U_4.pack_into(self.buffer, TEST_NUM) - self.U_4.pack_into(self.buffer, EXEC_CNT) - self.U_4.pack_into(self.buffer, FAIL_CNT) - self.U_4.pack_into(self.buffer, ALRM_CNT) - self.C_n.pack_into(self.buffer, TEST_NAM) - self.C_n.pack_into(self.buffer, SEQ_NAME) - self.C_n.pack_into(self.buffer, TEST_LBL) - self.B_1.pack_into(self.buffer, OPT_FLAG) - self.R_4.pack_into(self.buffer, TEST_TIM) - self.R_4.pack_into(self.buffer, TEST_MIN) - self.R_4.pack_into(self.buffer, TEST_MAX) - self.R_4.pack_into(self.buffer, TST_SUMS) - self.R_4.pack_into(self.buffer, TST_SQRS) - - self.write_fields(10, 30, write_fields) - - def WCR( - self, - WAFR_SIZ: float = 0.0, - DIE_HT: float = 0.0, - DIE_WID: float = 0.0, - WF_UNITS: int = 0, - WF_FLAT: Literal["U", "D", "L", "R", " "] = " ", - CENTER_X: int = -32768, - CENTER_Y: int = -32768, - POS_X: Literal["L", "R", " "] = " ", - POS_Y: Literal["U", "D", " "] = " ", - ): - """Wafer Configuration Record""" - # Implementation here - pass - - def WIR( - self, - HEAD_NUM: int, - START_T: int, - SITE_GRP: int = 255, - WAFER_ID: str = "", - ): - """Wafer Information Record""" - - # Implementation here - def write_fields(self): - self.U_1.pack_into(self.buffer, HEAD_NUM) - self.U_1.pack_into(self.buffer, SITE_GRP) - self.U_4.pack_into(self.buffer, START_T) - self.C_n.pack_into(self.buffer, WAFER_ID) - - self.write_fields(2, 10, write_fields) - - def WRR( - self, - HEAD_NUM: int, - FINISH_T: int, - PART_CNT: int, - SITE_GRP: int = 255, - RTST_CNT: int = 4294967295, - ABRT_CNT: int = 4294967295, - GOOD_CNT: int = 4294967295, - FUNC_CNT: int = 4294967295, - WAFER_ID: str = "", - FABWF_ID: str = "", - FRAME_ID: str = "", - MASK_ID: str = "", - USR_DESC: str = "", - EXC_DESC: str = "", - ): - """Wafer Results Record""" - # Implementation here - pass diff --git a/pystdf4/Records/__init__.py b/pystdf4/Records/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pystdf4/Records/base.py b/pystdf4/Records/base.py new file mode 100644 index 0000000..d369978 --- /dev/null +++ b/pystdf4/Records/base.py @@ -0,0 +1,50 @@ +from struct import Struct +from typing import Callable, Tuple + +from pystdf4.Core import B_1, C_1, I_1, I_2, I_4, R_4, R_8, U_1, U_2, U_4, B_n, C_n, DynamicBuffer, kxC_n, kxR_4, kxU_1, kxU_2 + + +class RecordBase: + def __init__(self): + self.buffer = DynamicBuffer() + self.header_packer: Struct = Struct(" int: + """ + Write the header of a record to the file. + """ + packer_size = self.header_packer.size + self.buffer._ensure_capacity(packer_size) + start = self.buffer.offset + self.header_packer.pack_into(self.buffer._mv, start, 0, *header) + self.buffer.offset += packer_size + return start diff --git a/pystdf4/Records/literials.py b/pystdf4/Records/literials.py new file mode 100644 index 0000000..28948ba --- /dev/null +++ b/pystdf4/Records/literials.py @@ -0,0 +1,54 @@ +from typing import Literal + +# Basic Literals +AlphaNumericLiteral = Literal[ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + " ", +] + +# MIR MODE_COD +StationModeLiteral = Literal["A", "C", "D", "E", "M", "P", "Q", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", " "] + +# MIR RTST_COD +RetestConditionLiteral = Literal["Y", "N", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + +# BIN_PF +BinPassFailLiteral = Literal["P", "F", " "] + +# TSR TEST_TYP +TestTypeLiteral = Literal["P", "F", "M", " "] diff --git a/pystdf4/Records/packer.py b/pystdf4/Records/packer.py new file mode 100644 index 0000000..812745b --- /dev/null +++ b/pystdf4/Records/packer.py @@ -0,0 +1,1235 @@ +from typing import Any, Literal, Sequence + +from pystdf4.Records.base import RecordBase +from pystdf4.Records.literials import AlphaNumericLiteral, BinPassFailLiteral, RetestConditionLiteral, StationModeLiteral, TestTypeLiteral + + +class StdfPacker(RecordBase): + def __init__(self): + super().__init__() + + def FAR( + self, + CPU_TYPE: int, + STDF_VER: int, + ) -> None: + """File Attributes Record (FAR) + + Contains the information necessary to determine how to decode the STDF data + contained in the file. + + Note: + Required as the first record of the file. + + Args: + CPU_TYPE (int): CPU type that wrote this file. + STDF_VER (int): STDF version number. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, CPU_TYPE) + obj.U_1.pack_into(obj.buffer, STDF_VER) + + self.write_fields(0, 10, write_fields) + + def ATR( + self, + MOD_TIM: int, + CMD_LINE: str, + ) -> None: + """Audit Trail Record (ATR) + + Used to record any operation that alters the contents of the STDF file. The name of the + program and all its parameters should be recorded in the ASCII field provided in this + record. Typically, this record will be used to track filter programs that have been + applied to the data. + + Note: + Between the File Attributes Record (FAR) and the Master Information Record (MIR). + The filter program that writes the altered STDF file must write its ATR immediately + after the FAR (and hence before any other ATRs that may be in the file). In this way, + multiple ATRs will be in reverse chronological order. + + Args: + MOD_TIM (int): Date and time of STDF file modification. + CMD_LINE (str): Command line of program. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_4.pack_into(obj.buffer, MOD_TIM) + obj.C_n.pack_into(obj.buffer, CMD_LINE) + + self.write_fields(0, 20, write_fields) + + def MIR( + self, + SETUP_T: int, + START_T: int, + STAT_NUM: int, + LOT_ID: str, + PART_TYP: str, + NODE_NAM: str, + TSTR_TYP: str, + JOB_NAM: str, + MODE_COD: StationModeLiteral = " ", + RTST_COD: RetestConditionLiteral = " ", + PROT_COD: AlphaNumericLiteral = " ", + BURN_TIM: int = 65535, + CMOD_COD: AlphaNumericLiteral = " ", + JOB_REV: str = "", + SBLOT_ID: str = "", + OPER_NAM: str = "", + EXEC_TYP: str = "", + EXEC_VER: str = "", + TEST_COD: str = "", + TST_TEMP: str = "", + USER_TXT: str = "", + AUX_FILE: str = "", + PKG_TYP: str = "", + FAMLY_ID: str = "", + DATE_COD: str = "", + FACIL_ID: str = "", + FLOOR_ID: str = "", + PROC_ID: str = "", + OPER_FRQ: str = "", + SPEC_NAM: str = "", + SPEC_VER: str = "", + FLOW_ID: str = "", + SETUP_ID: str = "", + DSGN_REV: str = "", + ENG_ID: str = "", + ROM_COD: str = "", + SERL_NUM: str = "", + SUPR_NAM: str = "", + ): + """Master Information Record (MIR) + + The MIR and the MRR (Master Results Record) contain all the global information that + is to be stored for a tested lot of parts. Each data stream must have exactly one MIR, + immediately after the FAR (and the ATRs, if they are used). This will allow any data + reporting or analysis programs access to this information in the shortest possible + amount of time. + + Note: + Immediately after the File Attributes Record (FAR) and the Audit Trail Records (ATR), + if ATRs are used. + + Args: + SETUP_T (int): Date and time of job setup. + START_T (int): Date and time first part tested. + STAT_NUM (int): Tester station number. + LOT_ID (str): Lot ID (customer specified). + PART_TYP (str): Part Type (or product ID). + NODE_NAM (str): Name of node that generated data. + TSTR_TYP (str): Tester type. + JOB_NAM (str): Job name (test program name). + MODE_COD (StationModeLiteral): Test mode code (e.g. prod, dev). Defaults to " ". + RTST_COD (RetestConditionLiteral): Lot retest code. Defaults to " ". + PROT_COD (AlphaNumericLiteral): Data protection code. Defaults to " ". + BURN_TIM (int): Burn-in time (in minutes). Defaults to 65535. + CMOD_COD (AlphaNumericLiteral): Command mode code. Defaults to " ". + JOB_REV (str): Job (test program) revision number. Defaults to "". + SBLOT_ID (str): Sublot ID. Defaults to "". + OPER_NAM (str): Operator name or ID (at setup time). Defaults to "". + EXEC_TYP (str): Tester executive software type. Defaults to "". + EXEC_VER (str): Tester exec software version number. Defaults to "". + TEST_COD (str): Test phase or step code. Defaults to "". + TST_TEMP (str): Test temperature. Defaults to "". + USER_TXT (str): Generic user text. Defaults to "". + AUX_FILE (str): Name of auxiliary data file. Defaults to "". + PKG_TYP (str): Package type. Defaults to "". + FAMLY_ID (str): Product family ID. Defaults to "". + DATE_COD (str): Date code. Defaults to "". + FACIL_ID (str): Test facility ID. Defaults to "". + FLOOR_ID (str): Test floor ID. Defaults to "". + PROC_ID (str): Fabrication process ID. Defaults to "". + OPER_FRQ (str): Operation frequency or step. Defaults to "". + SPEC_NAM (str): Test specification name. Defaults to "". + SPEC_VER (str): Test specification version number. Defaults to "". + FLOW_ID (str): Test flow ID. Defaults to "". + SETUP_ID (str): Test setup ID. Defaults to "". + DSGN_REV (str): Device design revision. Defaults to "". + ENG_ID (str): Engineering lot ID. Defaults to "". + ROM_COD (str): ROM code ID. Defaults to "". + SERL_NUM (str): Tester serial number. Defaults to "". + SUPR_NAM (str): Supervisor name or ID. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_4.pack_into(obj.buffer, SETUP_T) + obj.U_4.pack_into(obj.buffer, START_T) + obj.U_1.pack_into(obj.buffer, STAT_NUM) + obj.C_1.pack_into(obj.buffer, MODE_COD) + obj.C_1.pack_into(obj.buffer, RTST_COD) + obj.C_1.pack_into(obj.buffer, PROT_COD) + obj.U_2.pack_into(obj.buffer, BURN_TIM) + obj.C_1.pack_into(obj.buffer, CMOD_COD) + obj.C_n.pack_into(obj.buffer, LOT_ID) + obj.C_n.pack_into(obj.buffer, PART_TYP) + obj.C_n.pack_into(obj.buffer, NODE_NAM) + obj.C_n.pack_into(obj.buffer, TSTR_TYP) + obj.C_n.pack_into(obj.buffer, JOB_NAM) + obj.C_n.pack_into(obj.buffer, JOB_REV) + obj.C_n.pack_into(obj.buffer, SBLOT_ID) + obj.C_n.pack_into(obj.buffer, OPER_NAM) + obj.C_n.pack_into(obj.buffer, EXEC_TYP) + obj.C_n.pack_into(obj.buffer, EXEC_VER) + obj.C_n.pack_into(obj.buffer, TEST_COD) + obj.C_n.pack_into(obj.buffer, TST_TEMP) + obj.C_n.pack_into(obj.buffer, USER_TXT) + obj.C_n.pack_into(obj.buffer, AUX_FILE) + obj.C_n.pack_into(obj.buffer, PKG_TYP) + obj.C_n.pack_into(obj.buffer, FAMLY_ID) + obj.C_n.pack_into(obj.buffer, DATE_COD) + obj.C_n.pack_into(obj.buffer, FACIL_ID) + obj.C_n.pack_into(obj.buffer, FLOOR_ID) + obj.C_n.pack_into(obj.buffer, PROC_ID) + obj.C_n.pack_into(obj.buffer, OPER_FRQ) + obj.C_n.pack_into(obj.buffer, SPEC_NAM) + obj.C_n.pack_into(obj.buffer, SPEC_VER) + obj.C_n.pack_into(obj.buffer, FLOW_ID) + obj.C_n.pack_into(obj.buffer, SETUP_ID) + obj.C_n.pack_into(obj.buffer, DSGN_REV) + obj.C_n.pack_into(obj.buffer, ENG_ID) + obj.C_n.pack_into(obj.buffer, ROM_COD) + obj.C_n.pack_into(obj.buffer, SERL_NUM) + obj.C_n.pack_into(obj.buffer, SUPR_NAM) + + self.write_fields(1, 10, write_fields) + + def MRR( + self, + FINISH_T: int = 0, + DISP_COD: AlphaNumericLiteral = " ", + USR_DESC: str = "", + EXC_DESC: str = "", + ) -> None: + """Master Results Record (MRR) + + The Master Results Record (MRR) is a logical extension of the Master Information + Record (MIR). The data can be thought of as belonging with the MIR, but it is not + available when the tester writes the MIR information. Each data stream must have + exactly one MRR as the last record in the data stream. + + Note: + Must be the last record in the data stream. + + Args: + FINISH_T (int): Date and time last part tested. Defaults to 0. + DISP_COD (AlphaNumericLiteral): Lot disposition code. Defaults to " ". + USR_DESC (str): Lot description supplied by user. Defaults to "". + EXC_DESC (str): Lot description supplied by exec. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_4.pack_into(obj.buffer, FINISH_T) + obj.C_1.pack_into(obj.buffer, DISP_COD) + obj.C_n.pack_into(obj.buffer, USR_DESC) + obj.C_n.pack_into(obj.buffer, EXC_DESC) + + self.write_fields(1, 20, write_fields) + + def PCR( + self, + HEAD_NUM: int, + SITE_NUM: int, + PART_CNT: int, + RTST_CNT: int = 4294967295, + ABRT_CNT: int = 4294967295, + GOOD_CNT: int = 4294967295, + FUNC_CNT: int = 4294967295, + ) -> None: + """Part Count Record (PCR) + + Contains the part count totals for one or all test sites. Each data stream must have at + least one PCR to show the part count. + + Note: + There must be at least one PCR in the file: either one summary PCR for all test sites + (HEAD_NUM= 255), or one PCR for each head/site combination, or both. + When data is being recorded in real time, this record will usually appear near the + end of the data stream. + + Args: + HEAD_NUM (int): Test head number. If this PCR contains a summary of the part counts for + all test sites, this field must be set to 255 + SITE_NUM (int): Test site number. + PART_CNT (int): Number of parts tested. + RTST_CNT (int): Number of parts retested. Defaults to 4294967295. + ABRT_CNT (int): Number of aborts during testing. Defaults to 4294967295. + GOOD_CNT (int): Number of good (passed) parts tested. Defaults to 4294967295. + FUNC_CNT (int): Number of functional parts tested. Defaults to 4294967295. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.U_4.pack_into(obj.buffer, PART_CNT) + obj.U_4.pack_into(obj.buffer, RTST_CNT) + obj.U_4.pack_into(obj.buffer, ABRT_CNT) + obj.U_4.pack_into(obj.buffer, GOOD_CNT) + obj.U_4.pack_into(obj.buffer, FUNC_CNT) + + self.write_fields(1, 30, write_fields) + + def HBR( + self, + HEAD_NUM: int, + SITE_NUM: int, + HBIN_NUM: int, + HBIN_CNT: int, + HBIN_PF: BinPassFailLiteral = " ", + HBIN_NAM: str = "", + ) -> None: + """Hardware Bin Record (HBR) + + Stores a count of the parts "physically" placed in a particular bin after testing. (In + wafer testing, "physical" binning is not an actual transfer of the chip, but rather is + represented by a drop of ink or an entry in a wafer map file.) This bin count can be for + a single test site (when parallel testing) or a total for all test sites. + + Note: + When data is being recorded in real time, this record usually appears near the end of + the data stream. + + Args: + HEAD_NUM (int): Test head number. If this HBR contains a summary of the hardware bin + counts for all test sites, this field must be set to 255. + SITE_NUM (int): Test site number. + HBIN_NUM (int): Hardware bin number. + HBIN_CNT (int): Number of parts in bin. + HBIN_PF (BinPassFailLiteral): Pass/fail indication. Defaults to " ". + HBIN_NAM (str): Name of hardware bin. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.U_2.pack_into(obj.buffer, HBIN_NUM) + obj.U_4.pack_into(obj.buffer, HBIN_CNT) + obj.C_1.pack_into(obj.buffer, HBIN_PF) + obj.C_n.pack_into(obj.buffer, HBIN_NAM) + + self.write_fields(1, 40, write_fields) + + def SBR( + self, + HEAD_NUM: int, + SITE_NUM: int, + SBIN_NUM: int, + SBIN_CNT: int, + SBIN_PF: BinPassFailLiteral = " ", + SBIN_NAM: str = "", + ) -> None: + """Software Bin Record (SBR) + + Stores a count of the parts associated with a particular logical bin after testing. This + bin count can be for a single test site (when parallel testing) or a total for all test sites. + + Note: + One per software bin for each site. One per software bin for bin totals. + When data is being recorded in real time, this record usually appears near the + end of the data stream. + + Args: + HEAD_NUM (int): Test head number. If this SBR contains a summary of the software bin + counts for all test sites, this field must be set to 255. + SITE_NUM (int): Test site number. + SBIN_NUM (int): Software bin number. + SBIN_CNT (int): Number of parts in bin. + SBIN_PF (BinPassFailLiteral): Pass/fail indication. Defaults to " ". + SBIN_NAM (str): Name of software bin. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.U_2.pack_into(obj.buffer, SBIN_NUM) + obj.U_4.pack_into(obj.buffer, SBIN_CNT) + obj.C_1.pack_into(obj.buffer, SBIN_PF) + obj.C_n.pack_into(obj.buffer, SBIN_NAM) + + self.write_fields(1, 50, write_fields) + + def PMR( + self, + PMR_INDX: int, + CHAN_TYP: int = 0, + CHAN_NAM: str = "", + PHY_NAM: str = "", + LOG_NAM: str = "", + HEAD_NUM: int = 1, + SITE_NUM: int = 1, + ) -> None: + """Pin Map Record (PMR) + + Provides indexing of tester channel names, and maps them to physical and logical pin + names. Each PMR defines the information for a single channel/pin combination. + + Note: + One per channel/pin combination used in the test program. + Reuse of a PMR index number is not permitted. + After the initial sequence and before the first PGR, PLR, FTR, or MPR that + uses this record's PMR_INDX value. + + Args: + PMR_INDX (int): Unique index associated with pin. The range of legal PMR indexes is 1 - 32,767. + CHAN_TYP (int): Channel type. Defaults to 0. + CHAN_NAM (str): Channel name. Defaults to "". + PHY_NAM (str): Physical name of pin. Defaults to "". + LOG_NAM (str): Logical name of pin. Defaults to "". + HEAD_NUM (int): Head number associated with channel. Defaults to 1. + SITE_NUM (int): Site number associated with channel. Defaults to 1. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_2.pack_into(obj.buffer, PMR_INDX) + obj.U_2.pack_into(obj.buffer, CHAN_TYP) + obj.C_n.pack_into(obj.buffer, CHAN_NAM) + obj.C_n.pack_into(obj.buffer, PHY_NAM) + obj.C_n.pack_into(obj.buffer, LOG_NAM) + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + + self.write_fields(1, 60, write_fields) + + def PGR( + self, + GRP_INDX: int, + GRP_NAM: str = "", + INDX_CNT: int = 0, + PMR_INDX: Sequence[int] = tuple(), + ) -> None: + """Pin Group Record (PGR) + + Associates a name with a group of pins. + + Note: + After all the PMRs whose PMR index values are listed in the PMR_INDX array of this + record; and before the first PLR that uses this record’s GRP_INDX value. + + Args: + GRP_INDX (int): Unique index associated with pin group. The range of legal group index numbers + is 32,768 - 65,535. + GRP_NAM (str): Name of pin group. Defaults to "". + INDX_CNT (int): Count (k) of PMR indexes. Defaults to 0. + PMR_INDX (Sequence[int]): Array of indexes for pins in the group. Defaults to tuple(). + """ + + def write_fields(obj: "StdfPacker"): + obj.U_2.pack_into(obj.buffer, GRP_INDX) + obj.C_n.pack_into(obj.buffer, GRP_NAM) + obj.U_2.pack_into(obj.buffer, INDX_CNT) + obj.kxU_2.pack_into(obj.buffer, PMR_INDX, INDX_CNT) + + self.write_fields(1, 62, write_fields) + + def PLR( + self, + GRP_CNT: int = 0, + GRP_INDX: Sequence[int] = tuple(), + GRP_MODE: Sequence[int] = tuple(), + GRP_RADX: Sequence[int] = tuple(), + PGM_CHAR: Sequence[str] = tuple(), + RTN_CHAR: Sequence[str] = tuple(), + PGM_CHAL: Sequence[str] = tuple(), + RTN_CHAL: Sequence[str] = tuple(), + ) -> None: + """Pin List Record (PLR) + + Defines the current display radix and operating mode for a pin or pin group. + + Note: + After all the PMRs and PGRs whose PMR index values and pin group index values are + listed in the GRP_INDX array of this record; and before the first FTR that references pins + or pin groups whose modes are defined in this record. + + Args: + GRP_CNT (int): Count (k) of pins or pin groups. Defaults to 0. + GRP_INDX (Sequence[int]): Array of pin or pin group indexes. Defaults to tuple(). + GRP_MODE (Sequence[int]): Operating mode of pin group. Defaults to tuple(). + GRP_RADX (Sequence[int]): Display radix of pin group. Defaults to tuple(). + PGM_CHAR (Sequence[str]): Program state encoding characters. Defaults to tuple(). + RTN_CHAR (Sequence[str]): Return state encoding characters. Defaults to tuple(). + PGM_CHAL (Sequence[str]): Program state encoding characters. Defaults to tuple(). + RTN_CHAL (Sequence[str]): Return state encoding characters. Defaults to tuple(). + """ + + def write_fields(obj: "StdfPacker"): + obj.U_2.pack_into(obj.buffer, GRP_CNT) + obj.kxU_2.pack_into(obj.buffer, GRP_INDX, size=GRP_CNT) + obj.kxU_2.pack_into(obj.buffer, GRP_MODE, size=GRP_CNT) + obj.kxU_1.pack_into(obj.buffer, GRP_RADX, size=GRP_CNT) + obj.kxC_n.pack_into(obj.buffer, PGM_CHAR, size=GRP_CNT) + obj.kxC_n.pack_into(obj.buffer, RTN_CHAR, size=GRP_CNT) + obj.kxC_n.pack_into(obj.buffer, PGM_CHAL, size=GRP_CNT) + obj.kxC_n.pack_into(obj.buffer, RTN_CHAL, size=GRP_CNT) + + self.write_fields(1, 63, write_fields) + + def RDR( + self, + NUM_BINS: int = 0, + RTST_BIN: Sequence[int] = tuple(), + ) -> None: + """Retest Data Record (RDR) + + Signals that the data in this STDF file is for retested parts. The data in this record, + combined with information in the MIR, tells data filtering programs what data to + replace when processing retest data. + + Note: + If this record is used, it must appear immediately after the Master Information Record (MIR). + + Args: + NUM_BINS (int): Number (k) of bins being retested. Defaults to 0. + RTST_BIN (Sequence[int]): Array of retest bin numbers. Defaults to tuple(). + """ + + def write_fields(obj: "StdfPacker"): + obj.U_2.pack_into(obj.buffer, NUM_BINS) + obj.kxU_2.pack_into(obj.buffer, RTST_BIN, size=NUM_BINS) + + self.write_fields(1, 70, write_fields) + + def SDR( + self, + HEAD_NUM: int, + SITE_GRP: int, + SITE_CNT: int = 0, + SITE_NUM: Sequence[int] = tuple(), + HAND_TYP: str = "", + HAND_ID: str = "", + CARD_TYP: str = "", + CARD_ID: str = "", + LOAD_TYP: str = "", + LOAD_ID: str = "", + DIB_TYP: str = "", + DIB_ID: str = "", + CABL_TYP: str = "", + CABL_ID: str = "", + CONT_TYP: str = "", + CONT_ID: str = "", + LASR_TYP: str = "", + LASR_ID: str = "", + EXTR_TYP: str = "", + EXTR_ID: str = "", + ) -> None: + """Site Description Record (SDR) + + Contains the configuration information for one or more test sites, connected to one test + head, that compose a site group. + + Note: + One for each site or group of sites that is differently configured. + Immediately after the MIR and RDR (if an RDR is used). + + Args: + HEAD_NUM (int): Test head number.. + SITE_GRP (int): Site group number.. + SITE_CNT (int): Number (k) of test sites in site group. Defaults to 0. + SITE_NUM (Sequence[int]): Array of test site numbers. Defaults to tuple(). + HAND_TYP (str): Handler or probe type. Defaults to "". + HAND_ID (str): Handler or prober ID. Defaults to "". + CARD_TYP (str): Probe card type. Defaults to "". + CARD_ID (str): Probe card ID. Defaults to "". + LOAD_TYP (str): Load board type. Defaults to "". + LOAD_ID (str): Load board ID. Defaults to "". + DIB_TYP (str): DIB board type. Defaults to "". + DIB_ID (str): DIB board ID. Defaults to "". + CABL_TYP (str): Interface cable type. Defaults to "". + CABL_ID (str): Interface cable ID. Defaults to "". + CONT_TYP (str): Handler contactor type. Defaults to "". + CONT_ID (str): Handler contactor ID. Defaults to "". + LASR_TYP (str): Laser type. Defaults to "". + LASR_ID (str): Laser ID. Defaults to "". + EXTR_TYP (str): Extra equipment type field. Defaults to "". + EXTR_ID (str): Extra equipment ID. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_GRP) + obj.U_1.pack_into(obj.buffer, SITE_CNT) + obj.kxU_1.pack_into(obj.buffer, SITE_NUM, size=SITE_CNT) + obj.C_n.pack_into(obj.buffer, HAND_TYP) + obj.C_n.pack_into(obj.buffer, HAND_ID) + obj.C_n.pack_into(obj.buffer, CARD_TYP) + obj.C_n.pack_into(obj.buffer, CARD_ID) + obj.C_n.pack_into(obj.buffer, LOAD_TYP) + obj.C_n.pack_into(obj.buffer, LOAD_ID) + obj.C_n.pack_into(obj.buffer, DIB_TYP) + obj.C_n.pack_into(obj.buffer, DIB_ID) + obj.C_n.pack_into(obj.buffer, CABL_TYP) + obj.C_n.pack_into(obj.buffer, CABL_ID) + obj.C_n.pack_into(obj.buffer, CONT_TYP) + obj.C_n.pack_into(obj.buffer, CONT_ID) + obj.C_n.pack_into(obj.buffer, LASR_TYP) + obj.C_n.pack_into(obj.buffer, LASR_ID) + obj.C_n.pack_into(obj.buffer, EXTR_TYP) + obj.C_n.pack_into(obj.buffer, EXTR_ID) + + self.write_fields(1, 80, write_fields) + + def WIR( + self, + HEAD_NUM: int, + START_T: int, + SITE_GRP: int = 255, + WAFER_ID: str = "", + ) -> None: + """Wafer Information Record (WIR) + + Acts mainly as a marker to indicate where testing of a particular wafer begins for each + wafer tested by the job plan. The WIR and the Wafer Results Record (WRR) bracket all + the stored information pertaining to one tested wafer. + + Note: + One per wafer tested. + Sent before testing each wafer. + + Args: + HEAD_NUM (int): Test head number. + START_T (int): Date and time first part tested. + SITE_GRP (int): Site group number. If this information is not known, or the tester + does not support the concept of site groups, this field should be set to 255. + WAFER_ID (str): Wafer ID. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_GRP) + obj.U_4.pack_into(obj.buffer, START_T) + obj.C_n.pack_into(obj.buffer, WAFER_ID) + + self.write_fields(2, 10, write_fields) + + def WRR( + self, + HEAD_NUM: int, + FINISH_T: int, + PART_CNT: int, + SITE_GRP: int = 255, + RTST_CNT: int = 4294967295, + ABRT_CNT: int = 4294967295, + GOOD_CNT: int = 4294967295, + FUNC_CNT: int = 4294967295, + WAFER_ID: str = "", + FABWF_ID: str = "", + FRAME_ID: str = "", + MASK_ID: str = "", + USR_DESC: str = "", + EXC_DESC: str = "", + ) -> None: + """Wafer Results Record (WRR) + + Contains the result information relating to each wafer tested by the job plan. The WRR + and the Wafer Information Record (WIR) bracket all the stored information pertaining + to one tested wafer. + + Note: + Anywhere in the data stream after the corresponding WIR. Sent after testing each wafer. + + Args: + HEAD_NUM (int): Test head number. + FINISH_T (int): Date and time last part tested. + PART_CNT (int): Number of parts tested. + SITE_GRP (int): Site group number. Defaults to 255. + RTST_CNT (int): Number of parts retested. Defaults to 4294967295. + ABRT_CNT (int): Number of aborts during testing. Defaults to 4294967295. + GOOD_CNT (int): Number of good (passed) parts tested. Defaults to 4294967295. + FUNC_CNT (int): Number of functional parts tested. Defaults to 4294967295. + WAFER_ID (str): Wafer ID. Defaults to "". + FABWF_ID (str): Fab wafer ID. Defaults to "". + FRAME_ID (str): Wafer frame ID. Defaults to "". + MASK_ID (str): Wafer mask ID. Defaults to "". + USR_DESC (str): Wafer description supplied by user. Defaults to "". + EXC_DESC (str): Wafer description supplied by exec. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_GRP) + obj.U_4.pack_into(obj.buffer, FINISH_T) + obj.U_4.pack_into(obj.buffer, PART_CNT) + obj.U_4.pack_into(obj.buffer, RTST_CNT) + obj.U_4.pack_into(obj.buffer, ABRT_CNT) + obj.U_4.pack_into(obj.buffer, GOOD_CNT) + obj.U_4.pack_into(obj.buffer, FUNC_CNT) + obj.C_n.pack_into(obj.buffer, WAFER_ID) + obj.C_n.pack_into(obj.buffer, FABWF_ID) + obj.C_n.pack_into(obj.buffer, FRAME_ID) + obj.C_n.pack_into(obj.buffer, MASK_ID) + obj.C_n.pack_into(obj.buffer, USR_DESC) + obj.C_n.pack_into(obj.buffer, EXC_DESC) + + self.write_fields(2, 20, write_fields) + + def WCR( + self, + WAFR_SIZ: float = 0.0, + DIE_HT: float = 0.0, + DIE_WID: float = 0.0, + WF_UNITS: Literal[0, 1, 2, 3, 4] = 0, + WF_FLAT: Literal["U", "D", "L", "R", " "] = " ", + CENTER_X: int = -32768, + CENTER_Y: int = -32768, + POS_X: Literal["L", "R", " "] = " ", + POS_Y: Literal["U", "D", " "] = " ", + ) -> None: + """Wafer Configuration Record (WCR) + + Contains the configuration information for the wafers tested by the job plan. The + WCR provides the dimensions and orientation information for all wafers and dice + in the lot. + + Note: + One per STDF file (used only if wafer testing). + + Args: + WAFR_SIZ (float): Diameter of wafer in WF_UNITS. Defaults to 0.0. + DIE_HT (float): Height of die in WF_UNITS. Defaults to 0.0. + DIE_WID (float): Width of die in WF_UNITS. Defaults to 0.0. + WF_UNITS (Literal[0, 1, 2, 3, 4]): Units for wafer and die dimensions. 0 = Unknown units, + 1 = Units are in inches, 2 = Units are in centimeters, 3 = Units are in millimeters, + 4 = Units are in mils. Defaults to 0. + WF_FLAT (Literal["U", "D", "L", "R", " "]): Orientation of wafer flat. "U" = Up, "D" = Down, + "L" = Left, "R" = Right, " " = Unknown. Defaults to " ". + CENTER_X (int): X coordinate of center die on wafer. Defaults to -32768. + CENTER_Y (int): Y coordinate of center die on wafer. Defaults to -32768. + POS_X (Literal["L", "R", " "]): Positive X direction of wafer. "L" = Left, "R" = Right, " " + = Unknown. Defaults to " " + POS_Y (Literal["U", "D", " "]): Positive Y direction of wafer. "U" = Up, "D" = Down, " " = + Unknown. Defaults to " ". + """ + + def write_fields(obj: "StdfPacker"): + obj.R_4.pack_into(obj.buffer, WAFR_SIZ) + obj.R_4.pack_into(obj.buffer, DIE_HT) + obj.R_4.pack_into(obj.buffer, DIE_WID) + obj.U_1.pack_into(obj.buffer, WF_UNITS) + obj.C_1.pack_into(obj.buffer, WF_FLAT) + obj.I_2.pack_into(obj.buffer, CENTER_X) + obj.I_2.pack_into(obj.buffer, CENTER_Y) + obj.C_1.pack_into(obj.buffer, POS_X) + obj.C_1.pack_into(obj.buffer, POS_Y) + + self.write_fields(2, 30, write_fields) + + def PIR( + self, + HEAD_NUM: int, + SITE_NUM: int, + ) -> None: + """Part Information Record (PIR) + + Acts as a marker to indicate where testing of a particular part begins for each part + tested by the test program. The PIR and the Part Results Record (PRR) bracket all the + stored information pertaining to one tested part. + + Note: + One per part tested. + Sent before testing each part. + + Args: + HEAD_NUM (int): Test head number. + SITE_NUM (int): Test site number. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + + self.write_fields(5, 10, write_fields) + + def PRR( + self, + HEAD_NUM: int, + SITE_NUM: int, + PART_FLG: bytes, + NUM_TEST: int, + HARD_BIN: int, + SOFT_BIN: int = 65535, + X_COORD: int = -32768, + Y_COORD: int = -32768, + TEST_T: int = 0, + PART_ID: str = "", + PART_TXT: str = "", + PART_FIX: bytes = b"", + ) -> None: + """Part Results Record (PRR) + + Contains the result information relating to each part tested by the test program. The + PRR and the Part Information Record (PIR) bracket all the stored information + pertaining to one tested part. + + Note: + One per part tested. + Sent after completion of testing each part. + + Args: + HEAD_NUM (int): Test head number. + SITE_NUM (int): Test site number. + PART_FLG (bytes): Part information flag. + NUM_TEST (int): Number of tests executed. + HARD_BIN (int): Hardware bin number. + SOFT_BIN (int): Software bin number. Defaults to 65535. + X_COORD (int): (Wafer) X coordinate. Defaults to -32768. Have legal values in the + range -32767 to 32767. A missing value is indicated by the value -32768. + Y_COORD (int): (Wafer) Y coordinate. Defaults to -32768. Have legal values in the + range -32767 to 32767. A missing value is indicated by the value -32768. + TEST_T (int): Elapsed test time in milliseconds. Defaults to 0. + PART_ID (str): Part identification. Defaults to "". + PART_TXT (str): Part description text. Defaults to "". + PART_FIX (bytes): Part repair information. Defaults to b"". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.B_1.pack_into(obj.buffer, PART_FLG) + obj.U_2.pack_into(obj.buffer, NUM_TEST) + obj.U_2.pack_into(obj.buffer, HARD_BIN) + obj.U_2.pack_into(obj.buffer, SOFT_BIN) + obj.I_2.pack_into(obj.buffer, X_COORD) + obj.I_2.pack_into(obj.buffer, Y_COORD) + obj.U_4.pack_into(obj.buffer, TEST_T) + obj.C_n.pack_into(obj.buffer, PART_ID) + obj.C_n.pack_into(obj.buffer, PART_TXT) + obj.B_n.pack_into(obj.buffer, PART_FIX) + + self.write_fields(5, 20, write_fields) + + def TSR( + self, + HEAD_NUM: int, + SITE_NUM: int, + TEST_NUM: int, + OPT_FLAG: bytes, + TEST_TYP: TestTypeLiteral = " ", + EXEC_CNT: int = 4294967295, + FAIL_CNT: int = 4294967295, + ALRM_CNT: int = 4294967295, + TEST_NAM: str = "", + SEQ_NAME: str = "", + TEST_LBL: str = "", + TEST_TIM: float = 0.0, + TEST_MIN: float = 0.0, + TEST_MAX: float = 0.0, + TST_SUMS: float = 0.0, + TST_SQRS: float = 0.0, + ) -> None: + """Test Synopsis Record (TSR) + + Contains the test execution and failure counts for one parametric or functional test in + the test program. Also contains static information, such as test name. + + Note: + One for each test executed in the test program. + When test data is being generated in real-time, these records will appear after the last PRR. + + Args: + HEAD_NUM (int): Test head number. If this TSR contains a summary of the test counts for all + test sites, this field must be set to 255. + SITE_NUM (int): Test site number. + TEST_NUM (int): Test number. + OPT_FLAG (bytes): Optional data flag. + TEST_TYP (AlphaNumericLiteral): Test type. Defaults to " ". + EXEC_CNT (int): Number of test executions. Defaults to 4294967295. + FAIL_CNT (int): Number of test failures. Defaults to 4294967295. + ALRM_CNT (int): Number of alarmed tests. Defaults to 4294967295. + TEST_NAM (str): Test name. Defaults to "". + SEQ_NAME (str): Sequencer (program segment/flow) name. Defaults to "". + TEST_LBL (str): Test label or text. Defaults to "". + TEST_TIM (float): Average test execution time in seconds. Defaults to 0.0. + TEST_MIN (float): Lowest test result value. Defaults to 0.0. + TEST_MAX (float): Highest test result value. Defaults to 0.0. + TST_SUMS (float): Sum of test result values. Defaults to 0.0. + TST_SQRS (float): Sum of squares of test result values. Defaults to 0.0. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.C_1.pack_into(obj.buffer, TEST_TYP) + obj.U_4.pack_into(obj.buffer, TEST_NUM) + obj.U_4.pack_into(obj.buffer, EXEC_CNT) + obj.U_4.pack_into(obj.buffer, FAIL_CNT) + obj.U_4.pack_into(obj.buffer, ALRM_CNT) + obj.C_n.pack_into(obj.buffer, TEST_NAM) + obj.C_n.pack_into(obj.buffer, SEQ_NAME) + obj.C_n.pack_into(obj.buffer, TEST_LBL) + obj.B_1.pack_into(obj.buffer, OPT_FLAG) + obj.R_4.pack_into(obj.buffer, TEST_TIM) + obj.R_4.pack_into(obj.buffer, TEST_MIN) + obj.R_4.pack_into(obj.buffer, TEST_MAX) + obj.R_4.pack_into(obj.buffer, TST_SUMS) + obj.R_4.pack_into(obj.buffer, TST_SQRS) + + self.write_fields(10, 30, write_fields) + + def PTR( + self, + TEST_NUM: int, + HEAD_NUM: int, + SITE_NUM: int, + TEST_FLG: bytes, + PARM_FLG: bytes, + OPT_FLAG: bytes, + RESULT: float = 0.0, + TEST_TXT: str = "", + ALARM_ID: str = "", + RES_SCAL: int = 0, + LLM_SCAL: int = 0, + HLM_SCAL: int = 0, + LO_LIMIT: float = 0.0, + HI_LIMIT: float = 0.0, + UNITS: str = "", + C_RESFMT: str = "", + C_LLMFMT: str = "", + C_HLMFMT: str = "", + LO_SPEC: float = 0.0, + HI_SPEC: float = 0.0, + ) -> None: + """Parametric Test Record (PTR) + + Contains the results of a single execution of a parametric test in the test program. The + first occurrence of this record also establishes the default values for all semi-static + information about the test, such as limits, units, and scaling. + + Note: + One per parametric test execution. + + Args: + TEST_NUM (int): Test number. + HEAD_NUM (int): Test head number. + SITE_NUM (int): Test site number. + TEST_FLG (bytes): Test flags (fail, alarm, etc.). + PARM_FLG (bytes): Parametric test flags (drift, etc.). + OPT_FLAG (bytes): Optional data flag. + RESULT (float): Test result. Defaults to 0.0. + TEST_TXT (str): Test description text or label. Defaults to "". + ALARM_ID (str): Name of alarm. Defaults to "". + RES_SCAL (int): Test results scaling exponent. Defaults to 0. + LLM_SCAL (int): Low limit scaling exponent. Defaults to 0. + HLM_SCAL (int): High limit scaling exponent. Defaults to 0. + LO_LIMIT (float): Low test limit value. Defaults to 0.0. + HI_LIMIT (float): High test limit value. Defaults to 0.0. + UNITS (str): Test units. Defaults to "". + C_RESFMT (str): ANSI C result format string. Defaults to "". + C_LLMFMT (str): ANSI C low limit format string. Defaults to "". + C_HLMFMT (str): ANSI C high limit format string. Defaults to "". + LO_SPEC (float): Low specification limit value. Defaults to 0.0. + HI_SPEC (float): High specification limit value. Defaults to 0.0. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_4.pack_into(obj.buffer, TEST_NUM) + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.B_1.pack_into(obj.buffer, TEST_FLG) + obj.B_1.pack_into(obj.buffer, PARM_FLG) + obj.R_4.pack_into(obj.buffer, RESULT) + obj.C_n.pack_into(obj.buffer, TEST_TXT) + obj.C_n.pack_into(obj.buffer, ALARM_ID) + obj.B_1.pack_into(obj.buffer, OPT_FLAG) + obj.I_1.pack_into(obj.buffer, RES_SCAL) + obj.I_1.pack_into(obj.buffer, LLM_SCAL) + obj.I_1.pack_into(obj.buffer, HLM_SCAL) + obj.R_4.pack_into(obj.buffer, LO_LIMIT) + obj.R_4.pack_into(obj.buffer, HI_LIMIT) + obj.C_n.pack_into(obj.buffer, UNITS) + obj.C_n.pack_into(obj.buffer, C_RESFMT) + obj.C_n.pack_into(obj.buffer, C_LLMFMT) + obj.C_n.pack_into(obj.buffer, C_HLMFMT) + obj.R_4.pack_into(obj.buffer, LO_SPEC) + obj.R_4.pack_into(obj.buffer, HI_SPEC) + + self.write_fields(15, 10, write_fields) + + def MPR( + self, + TEST_NUM: int, + HEAD_NUM: int, + SITE_NUM: int, + TEST_FLG: bytes, + PARM_FLG: bytes, + OPT_FLAG: bytes, + RTN_ICNT: int = 0, + RSLT_CNT: int = 0, + RTN_STAT: Sequence[int] = tuple(), + RTN_RSLT: Sequence[float] = tuple(), + TEST_TXT: str = "", + ALARM_ID: str = "", + RES_SCAL: int = 0, + LLM_SCAL: int = 0, + HLM_SCAL: int = 0, + LO_LIMIT: float = 0.0, + HI_LIMIT: float = 0.0, + START_IN: float = 0.0, + INCR_IN: float = 0.0, + RTN_INDX: Sequence[int] = tuple(), + UNITS: str = "", + UNITS_IN: str = "", + C_RESFMT: str = "", + C_LLMFMT: str = "", + C_HLMFMT: str = "", + LO_SPEC: float = 0.0, + HI_SPEC: float = 0.0, + ) -> None: + """Multiple-Result Parametric Record (MPR) + + Contains the results of a single execution of a parametric test in the test program + where that test returns multiple values. The first occurrence of this record also + establishes the default values for all semi-static information about the test, such as + limits, units, and scaling. + + Note: + One per multiple-result parametric test execution. + + Args: + TEST_NUM (int): Test number. + HEAD_NUM (int): Test head number. + SITE_NUM (int): Test site number. + TEST_FLG (bytes): Test flags (fail, alarm, etc.). + PARM_FLG (bytes): Parametric test flags (drift, etc.). + OPT_FLAG (bytes): Optional data flag. + RTN_ICNT (int): Count (j) of PMR indexes. Defaults to 0. + RSLT_CNT (int): Count (k) of returned results. Defaults to 0. + RTN_STAT (Sequence[int]): Array of returned states. Defaults to tuple(). + RTN_RSLT (Sequence[float]): Array of returned results. Defaults to tuple(). + TEST_TXT (str): Descriptive text or label. Defaults to "". + ALARM_ID (str): Name of alarm. Defaults to "". + RES_SCAL (int): Test result scaling exponent. Defaults to 0. + LLM_SCAL (int): Test low limit scaling exponent. Defaults to 0. + HLM_SCAL (int): Test high limit scaling exponent. Defaults to 0. + LO_LIMIT (float): Test low limit value. Defaults to 0.0. + HI_LIMIT (float): Test high limit value. Defaults to 0.0. + START_IN (float): Starting input value (condition). Defaults to 0.0. + INCR_IN (float): Increment of input condition. Defaults to 0.0. + RTN_INDX (Sequence[int]): Array of PMR indexes. Defaults to None. + UNITS (str): Units of returned results. Defaults to "". + UNITS_IN (str): Input condition units. Defaults to "". + C_RESFMT (str): ANSI C result format string. Defaults to "". + C_LLMFMT (str): ANSI C low limit format string. Defaults to "". + C_HLMFMT (str): ANSI C high limit format string. Defaults to "". + LO_SPEC (float): Low specification limit value. Defaults to 0.0. + HI_SPEC (float): High specification limit value. Defaults to 0.0. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_4.pack_into(obj.buffer, TEST_NUM) + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.B_1.pack_into(obj.buffer, TEST_FLG) + obj.B_1.pack_into(obj.buffer, PARM_FLG) + obj.U_2.pack_into(obj.buffer, RTN_ICNT) + obj.U_2.pack_into(obj.buffer, RSLT_CNT) + # obj.kx_N1.pack_into(obj.buffer, RTN_STAT) # TODO: fix this + obj.kxR_4.pack_into(obj.buffer, RTN_RSLT, size=RSLT_CNT) + obj.C_n.pack_into(obj.buffer, TEST_TXT) + obj.C_n.pack_into(obj.buffer, ALARM_ID) + obj.B_1.pack_into(obj.buffer, OPT_FLAG) + obj.I_1.pack_into(obj.buffer, RES_SCAL) + obj.I_1.pack_into(obj.buffer, LLM_SCAL) + obj.I_1.pack_into(obj.buffer, HLM_SCAL) + obj.R_4.pack_into(obj.buffer, LO_LIMIT) + obj.R_4.pack_into(obj.buffer, HI_LIMIT) + obj.R_4.pack_into(obj.buffer, START_IN) + obj.R_4.pack_into(obj.buffer, INCR_IN) + obj.kxU_2.pack_into(obj.buffer, RTN_INDX, size=RTN_ICNT) + obj.C_n.pack_into(obj.buffer, UNITS) + obj.C_n.pack_into(obj.buffer, UNITS_IN) + obj.C_n.pack_into(obj.buffer, C_RESFMT) + obj.C_n.pack_into(obj.buffer, C_LLMFMT) + obj.C_n.pack_into(obj.buffer, C_HLMFMT) + obj.R_4.pack_into(obj.buffer, LO_SPEC) + obj.R_4.pack_into(obj.buffer, HI_SPEC) + + self.write_fields(15, 15, write_fields) + + def FTR( + self, + TEST_NUM: int, + HEAD_NUM: int, + SITE_NUM: int, + TEST_FLG: bytes, + OPT_FLAG: bytes, + CYCL_CNT: int = 0, + REL_VADR: int = 0, + REPT_CNT: int = 0, + NUM_FAIL: int = 0, # + XFAIL_AD: int = 0, + YFAIL_AD: int = 0, + VECT_OFF: int = 0, + RTN_ICNT: int = 0, + PGM_ICNT: int = 0, + RTN_INDX: Sequence[int] = tuple(), + RTN_STAT: Sequence[int] = tuple(), + PGM_INDX: Sequence[int] = tuple(), + PGM_STAT: Sequence[int] = tuple(), + FAIL_PIN: bytes = b"", + VECT_NAM: str = "", + TIME_SET: str = "", + OP_CODE: str = "", + TEST_TXT: str = "", + ALARM_ID: str = "", + PROG_TXT: str = "", + RSLT_TXT: str = "", + PATG_NUM: int = 255, + SPIN_MAP: bytes = b"", + ) -> None: + """Functional Test Record (FTR) + + Contains the results of the single execution of a functional test in the test program. The + first occurrence of this record also establishes the default values for all semi-static + information about the test. + + Note: + One or more for each execution of a functional test. + + Args: + TEST_NUM (int): Test number. + HEAD_NUM (int): Test head number. + SITE_NUM (int): Test site number. + TEST_FLG (int): Test flags (fail, alarm, etc.). + OPT_FLAG (int): Optional data flag. + CYCL_CNT (int): Cycle count of vector. Defaults to 0. + REL_VADR (int): Relative vector address. Defaults to 0. + REPT_CNT (int): Repeat count of vector. Defaults to 0. + NUM_FAIL (int): Number of pins with 1 or more failures. Defaults to 0. + XFAIL_AD (int): X logical device failure address. Defaults to 0. + YFAIL_AD (int): Y logical device failure address. Defaults to 0. + VECT_OFF (int): Offset from vector of interest. Defaults to 0. + RTN_ICNT (int): Count (j) of return data PMR indexes. Defaults to 0. + PGM_ICNT (int): Count (k) of programmed state indexes. Defaults to 0. + RTN_INDX (Sequence[int]): Array of return data PMR indexes. Defaults to tuple(). + RTN_STAT (Sequence[int]): Array of returned states. Defaults to tuple(). + PGM_INDX (Sequence[int]): Array of programmed state indexes. Defaults to tuple(). + PGM_STAT (Sequence[int]): Array of programmed states. Defaults to tuple(). + FAIL_PIN (bytes): Failing pin bitfield. Defaults to b"". + VECT_NAM (str): Vector module pattern name. Defaults to "". + TIME_SET (str): Time set name. Defaults to "". + OP_CODE (str): Vector Op Code. Defaults to "". + TEST_TXT (str): Descriptive text or label. Defaults to "". + ALARM_ID (str): Name of alarm. Defaults to "". + PROG_TXT (str): Additional programmed information. Defaults to "". + RSLT_TXT (str): Additional result information. Defaults to "". + PATG_NUM (int): Pattern generator number. Defaults to 255. + SPIN_MAP (bytes): Bit map of enabled comparators. Defaults to b"". + """ + + def write_fields(obj: "StdfPacker"): + obj.U_4.pack_into(obj.buffer, TEST_NUM) + obj.U_1.pack_into(obj.buffer, HEAD_NUM) + obj.U_1.pack_into(obj.buffer, SITE_NUM) + obj.B_1.pack_into(obj.buffer, TEST_FLG) + obj.B_1.pack_into(obj.buffer, OPT_FLAG) + obj.U_4.pack_into(obj.buffer, CYCL_CNT) + obj.U_4.pack_into(obj.buffer, REL_VADR) + obj.U_4.pack_into(obj.buffer, REPT_CNT) + obj.U_4.pack_into(obj.buffer, NUM_FAIL) + obj.I_4.pack_into(obj.buffer, XFAIL_AD) + obj.I_4.pack_into(obj.buffer, YFAIL_AD) + obj.I_2.pack_into(obj.buffer, VECT_OFF) + obj.U_2.pack_into(obj.buffer, RTN_ICNT) + obj.U_2.pack_into(obj.buffer, PGM_ICNT) + obj.kxU_2.pack_into(obj.buffer, RTN_INDX, size=RTN_ICNT) + # obj.kx_N1.pack_into(obj.buffer, RTN_STAT, size=RTN_ICNT) # TODO: fix this + obj.kxU_2.pack_into(obj.buffer, PGM_INDX, size=PGM_ICNT) + # obj.kx_N1.pack_into(obj.buffer, PGM_STAT, size=PGM_ICNT) # TODO: fix this + # obj.D_n.pack_into(obj.buffer, FAIL_PIN) # TODO: fix this + obj.C_n.pack_into(obj.buffer, VECT_NAM) + obj.C_n.pack_into(obj.buffer, TIME_SET) + obj.C_n.pack_into(obj.buffer, OP_CODE) + obj.C_n.pack_into(obj.buffer, TEST_TXT) + obj.C_n.pack_into(obj.buffer, ALARM_ID) + obj.C_n.pack_into(obj.buffer, PROG_TXT) + obj.C_n.pack_into(obj.buffer, RSLT_TXT) + obj.U_1.pack_into(obj.buffer, PATG_NUM) + # obj.D_n.pack_into(obj.buffer, SPIN_MAP) # TODO: fix this + + self.write_fields(15, 20, write_fields) + + def BPS( + self, + SEQ_NAME: str = "", + ) -> None: + """Begin Program Section Record (BPS) + + Marks the beginning of a new program section (or sequencer) in the job plan. + + Note: + Anywhere after the PIR and before the PRR. + + Args: + SEQ_NAME (str): Program section (or sequencer) name. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.C_n.pack_into(obj.buffer, SEQ_NAME) + + self.write_fields(20, 10, write_fields) + + def EPS( + self, + ) -> None: + """End Program Section Record (EPS) + + Marks the end of the current program section (or sequencer) in the job plan. + + Note: + Following the corresponding BPS and before the PRR in the data stream. + """ + + def write_fields(obj: "StdfPacker"): + return + + self.write_fields(20, 20, write_fields) + + def GDR( + self, + FLD_CNT: int = 0, + GEN_DATA: Sequence[Any] = tuple(), + ) -> None: + """Generic Data Record (GDR) + + Contains information that does not conform to any other record type defined by the + STDF specification. Such records are intended to be written under the control of job + plans executing on the tester. + + Note: + A test data file may contain any number of GDRs. + + Args: + FLD_CNT (int): Count of data fields in record. Defaults to 0. + GEN_DATA (list[tuple[int, bytes]]): Data type code and data for one field. Defaults to None. + """ + + def write_fields(obj: "StdfPacker"): + obj.U_2.pack_into(obj.buffer, FLD_CNT) + # obj.V_n.pack_into(obj.buffer, GEN_DATA) # TODO: fix this + + self.write_fields(50, 10, write_fields) + + def DTR( + self, + TEXT_DAT: str = "", + ) -> None: + """Datalog Text Record (DTR) + + Contains text information that is to be included in the datalog printout. DTRs may be + written under the control of a job plan: for example, to highlight unexpected test + results. They may also be generated by the tester executive software. + + Note: + A test data file may contain any number of DTRs. + + Args: + TEXT_DAT (str): ASCII text string. Defaults to "". + """ + + def write_fields(obj: "StdfPacker"): + obj.C_n.pack_into(obj.buffer, TEXT_DAT) + + self.write_fields(50, 30, write_fields) From 3c38dfb3b039f4f4a752897741d22e2e02af88d6 Mon Sep 17 00:00:00 2001 From: Lansus Date: Sat, 15 Nov 2025 21:25:36 +0800 Subject: [PATCH 7/7] Bump version and update README.md. --- README.md | 78 ++++++++++++++++++++++----------------------- pyproject.toml | 2 +- pystdf4/__init__.py | 2 +- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 4b69553..7c5d632 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,41 @@ with Stdf4Writer('example.stdf') as stdf: --- -## πŸ“˜ STDF v4 Data Types +## πŸ“‹ STDF Record Implementation Status + +| Record | Type | Sub | Status | Notes | +| ------ | ---- | --- | ------------- | ---------------------- | +| FAR | 0 | 10 | βœ”οΈ Complete | Required first record | +| ATR | 0 | 20 | βœ”οΈ Complete | Audit trail | +| MIR | 1 | 10 | βœ”οΈ Complete | Lot-level info | +| MRR | 1 | 20 | βœ”οΈ Complete | End of lot summary | +| PCR | 1 | 30 | βœ”οΈ Complete | Part statistics | +| HBR | 1 | 40 | βœ”οΈ Complete | Physical bin counts | +| SBR | 1 | 50 | βœ”οΈ Complete | Logical bin counts | +| PMR | 1 | 60 | βœ”οΈ Complete | Pin mapping | +| PGR | 1 | 62 | βœ”οΈ Complete | Pin grouping | +| PLR | 1 | 63 | βœ”οΈ Complete | Pin display properties | +| RDR | 1 | 70 | βœ”οΈ Complete | Retest info | +| SDR | 1 | 80 | βœ”οΈ Complete | Site configuration | +| WIR | 2 | 10 | βœ”οΈ Complete | Wafer start marker | +| WRR | 2 | 20 | βœ”οΈ Complete | Wafer summary | +| WCR | 2 | 30 | βœ”οΈ Complete | Wafer config | +| PIR | 5 | 10 | βœ”οΈ Complete | Part start marker | +| PRR | 5 | 20 | βœ”οΈ Complete | Part results | +| TSR | 10 | 30 | βœ”οΈ Complete | Test summary | +| PTR | 15 | 10 | βœ”οΈ Complete | Parametric test | +| MPR | 15 | 15 | ⚠️ Incomplete | Multiple parametric | +| FTR | 15 | 20 | ⚠️ Incomplete | Functional test | +| BPS | 20 | 10 | βœ”οΈ Complete | Program section start | +| EPS | 20 | 20 | βœ”οΈ Complete | Program section end | +| GDR | 50 | 10 | ⚠️ Incomplete | User-defined data | +| DTR | 50 | 30 | βœ”οΈ Complete | Datalog comments | + +--- + +## πŸ“˜ STDF v4 Data Types and Python Implementation + +### STDF Type Codes STDF defines compact type codes specifying how values are stored and interpreted in records. Below is a concise overview of commonly used types: @@ -82,11 +116,9 @@ STDF defines compact type codes specifying how values are stored and interpreted _For full STDF v4 type reference, see `pystdf4/doc/stdf-spec.pdf`._ ---- +### Python Implementation -## 🧱 PyStdf4 Data Model - -**Field hierarchy for writing STDF records:** +PyStdf4 uses a hierarchy of classes to represent STDF fields and records, with each class implementing a specific type of field or record. The hierarchy is as follows: ``` @@ -101,8 +133,8 @@ FieldBase(pyT) (ABC) β”‚ β”œβ”€β”€ I_1, I_2, I_4 β”‚ └── R_4, R_8 β”‚ -β”œβ”€β”€ VariableField [⚠️ Not Implemented Yet] -β”‚ └── V_n +β”œβ”€β”€ VariableField +β”‚ └── V_n [⚠️ Not Implemented Yet] β”‚ └── ArrayField β”œβ”€β”€ kxU_1, kxU_2, kxC_n @@ -114,38 +146,6 @@ These classes handle type conversion, byte parsing, and STDF serialization, ensu --- -## πŸ“‹ STDF Record Implementation Status - -| Record | Type | Sub | Status | Notes | -| ------ | ---- | --- | ------------- | ---------------------- | -| FAR | 0 | 10 | βœ”οΈ Complete | Required first record | -| ATR | 0 | 20 | βœ”οΈ Complete | Audit trail | -| MIR | 1 | 10 | βœ”οΈ Complete | Lot-level info | -| MRR | 1 | 20 | βœ”οΈ Complete | End of lot summary | -| PCR | 1 | 30 | βœ”οΈ Complete | Part statistics | -| HBR | 1 | 40 | βœ”οΈ Complete | Physical bin counts | -| SBR | 1 | 50 | βœ”οΈ Complete | Logical bin counts | -| PMR | 1 | 60 | βœ”οΈ Complete | Pin mapping | -| PGR | 1 | 62 | βœ”οΈ Complete | Pin grouping | -| PLR | 1 | 63 | βœ”οΈ Complete | Pin display properties | -| RDR | 1 | 70 | βœ”οΈ Complete | Retest info | -| SDR | 1 | 80 | βœ”οΈ Complete | Site configuration | -| WIR | 2 | 10 | βœ”οΈ Complete | Wafer start marker | -| WRR | 2 | 20 | βœ”οΈ Complete | Wafer summary | -| WCR | 2 | 30 | βœ”οΈ Complete | Wafer config | -| PIR | 5 | 10 | βœ”οΈ Complete | Part start marker | -| PRR | 5 | 20 | βœ”οΈ Complete | Part results | -| TSR | 10 | 30 | βœ”οΈ Complete | Test summary | -| PTR | 15 | 10 | βœ”οΈ Complete | Parametric test | -| MPR | 15 | 15 | ⚠️ Incomplete | Multiple parametric | -| FTR | 15 | 20 | ⚠️ Incomplete | Functional test | -| BPS | 20 | 10 | βœ”οΈ Complete | Program section start | -| EPS | 20 | 20 | βœ”οΈ Complete | Program section end | -| GDR | 50 | 10 | ⚠️ Incomplete | User-defined data | -| DTR | 50 | 30 | βœ”οΈ Complete | Datalog comments | - ---- - ## πŸ—ΊοΈ Roadmap | Version | Goals | diff --git a/pyproject.toml b/pyproject.toml index a094fdb..df45446 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pystdf4" -version = "0.1.0" +version = "0.1.1" description = "A Python library for reading and writing STDF4 files" readme = "README.md" requires-python = ">=3.8" diff --git a/pystdf4/__init__.py b/pystdf4/__init__.py index 6d7fe50..ea0e861 100644 --- a/pystdf4/__init__.py +++ b/pystdf4/__init__.py @@ -1,4 +1,4 @@ from .IO.stdf4writer import Stdf4Writer -__version__ = "0.1.0" +__version__ = "0.1.1" __all__ = ["Stdf4Writer"]