"""TROPIC01 Serial Number structure parser
This module provides the SerialNumber class for parsing and representing
the serial number structure embedded in TROPIC01 chip ID.
Based on lt_ser_num_t structure from libtropic.
"""
from tropicsquare.chip_id.constants import SERIAL_NUMBER_SIZE
[docs]
class SerialNumber:
"""Serial number structure parser for TROPIC01 chip
This class parses the 16-byte serial number structure that contains
chip manufacturing information including fabrication facility, wafer
coordinates, and unique identifiers.
Structure layout (16 bytes total):
- Byte 0: Serial number (8 bits)
- Bytes 1-3: fab_data containing:
- Fab ID (12 bits, bits 12-23)
- Part number ID (12 bits, bits 0-11)
- Bytes 4-5: Fabrication date (16 bits, little-endian)
- Bytes 6-10: Lot ID (40 bits)
- Byte 11: Wafer ID (8 bits)
- Bytes 12-13: X coordinate on wafer (16 bits, little-endian)
- Bytes 14-15: Y coordinate on wafer (16 bits, little-endian)
Attributes:
raw (bytes): Original raw serial number data (16 bytes)
sn (int): Serial number (8-bit)
fab_id (int): Fabrication facility ID (12-bit)
part_number_id (int): Part number identifier (12-bit)
fab_date (int): Fabrication date as integer
lot_id (bytes): Manufacturing lot identifier (5 bytes)
wafer_id (int): Wafer identifier (8-bit)
x_coord (int): X coordinate on wafer (16-bit)
y_coord (int): Y coordinate on wafer (16-bit)
"""
[docs]
def __init__(self, data: bytes):
"""Parse serial number from raw bytes
:param data: Raw serial number bytes (must be exactly 16 bytes)
:raises ValueError: If data length is not 16 bytes
"""
if len(data) != SERIAL_NUMBER_SIZE:
raise ValueError(f"Serial number must be {SERIAL_NUMBER_SIZE} bytes, got {len(data)}")
self.raw = data
# Byte 0: Serial number
self.sn = data[0]
# Bytes 1-3: fab_data contains fab_id (12 bits) + part_number_id (12 bits)
# Big-endian 24-bit value
fab_data = int.from_bytes(data[1:4], "big")
self.fab_id = (fab_data >> 12) & 0xFFF # Upper 12 bits
self.part_number_id = fab_data & 0xFFF # Lower 12 bits
# Bytes 4-5: Fabrication date (16-bit big-endian)
self.fab_date = int.from_bytes(data[4:6], "big")
# Bytes 6-10: Lot ID (40 bits / 5 bytes)
self.lot_id = data[6:11]
# Byte 11: Wafer ID
self.wafer_id = data[11]
# Bytes 12-13: X coordinate (16-bit big-endian)
self.x_coord = int.from_bytes(data[12:14], "big")
# Bytes 14-15: Y coordinate (16-bit big-endian)
self.y_coord = int.from_bytes(data[14:16], "big")
[docs]
def __str__(self) -> str:
"""Get human-readable string representation
:returns: Compact string representation with key serial number fields
"""
return (f"SN:0x{self.sn:02X} Fab:0x{self.fab_id:03X} PN:0x{self.part_number_id:03X} "
f"Wafer:0x{self.wafer_id:02X} Coords:({self.x_coord},{self.y_coord})")
[docs]
def __repr__(self) -> str:
"""Get detailed string representation for debugging
:returns: Detailed representation including all fields
"""
return (f"SerialNumber(sn=0x{self.sn:02X}, fab_id=0x{self.fab_id:03X}, "
f"part_number_id=0x{self.part_number_id:03X}, fab_date={self.fab_date}, "
f"lot_id={self.lot_id.hex()}, wafer_id=0x{self.wafer_id:02X}, "
f"x_coord={self.x_coord}, y_coord={self.y_coord})")
[docs]
def to_dict(self) -> dict:
"""Convert serial number to dictionary representation
:returns: Dictionary containing all serial number fields with hex-encoded bytes
"""
return {
'sn': self.sn,
'fab_id': self.fab_id,
'part_number_id': self.part_number_id,
'fab_date': self.fab_date,
'lot_id': self.lot_id.hex(),
'wafer_id': self.wafer_id,
'x_coord': self.x_coord,
'y_coord': self.y_coord,
}