Coverage for tropicsquare / chip_id / serial_number.py: 100%
21 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
1"""TROPIC01 Serial Number structure parser
3This module provides the SerialNumber class for parsing and representing
4the serial number structure embedded in TROPIC01 chip ID.
6Based on lt_ser_num_t structure from libtropic.
7"""
9from tropicsquare.chip_id.constants import SERIAL_NUMBER_SIZE
12class SerialNumber:
13 """Serial number structure parser for TROPIC01 chip
15 This class parses the 16-byte serial number structure that contains
16 chip manufacturing information including fabrication facility, wafer
17 coordinates, and unique identifiers.
19 Structure layout (16 bytes total):
21 - Byte 0: Serial number (8 bits)
22 - Bytes 1-3: fab_data containing:
24 - Fab ID (12 bits, bits 12-23)
25 - Part number ID (12 bits, bits 0-11)
27 - Bytes 4-5: Fabrication date (16 bits, little-endian)
28 - Bytes 6-10: Lot ID (40 bits)
29 - Byte 11: Wafer ID (8 bits)
30 - Bytes 12-13: X coordinate on wafer (16 bits, little-endian)
31 - Bytes 14-15: Y coordinate on wafer (16 bits, little-endian)
33 Attributes:
34 raw (bytes): Original raw serial number data (16 bytes)
35 sn (int): Serial number (8-bit)
36 fab_id (int): Fabrication facility ID (12-bit)
37 part_number_id (int): Part number identifier (12-bit)
38 fab_date (int): Fabrication date as integer
39 lot_id (bytes): Manufacturing lot identifier (5 bytes)
40 wafer_id (int): Wafer identifier (8-bit)
41 x_coord (int): X coordinate on wafer (16-bit)
42 y_coord (int): Y coordinate on wafer (16-bit)
43 """
45 def __init__(self, data: bytes):
46 """Parse serial number from raw bytes
48 :param data: Raw serial number bytes (must be exactly 16 bytes)
50 :raises ValueError: If data length is not 16 bytes
51 """
52 if len(data) != SERIAL_NUMBER_SIZE:
53 raise ValueError(f"Serial number must be {SERIAL_NUMBER_SIZE} bytes, got {len(data)}")
55 self.raw = data
57 # Byte 0: Serial number
58 self.sn = data[0]
60 # Bytes 1-3: fab_data contains fab_id (12 bits) + part_number_id (12 bits)
61 # Big-endian 24-bit value
62 fab_data = int.from_bytes(data[1:4], "big")
63 self.fab_id = (fab_data >> 12) & 0xFFF # Upper 12 bits
64 self.part_number_id = fab_data & 0xFFF # Lower 12 bits
66 # Bytes 4-5: Fabrication date (16-bit big-endian)
67 self.fab_date = int.from_bytes(data[4:6], "big")
69 # Bytes 6-10: Lot ID (40 bits / 5 bytes)
70 self.lot_id = data[6:11]
72 # Byte 11: Wafer ID
73 self.wafer_id = data[11]
75 # Bytes 12-13: X coordinate (16-bit big-endian)
76 self.x_coord = int.from_bytes(data[12:14], "big")
78 # Bytes 14-15: Y coordinate (16-bit big-endian)
79 self.y_coord = int.from_bytes(data[14:16], "big")
81 def __str__(self) -> str:
82 """Get human-readable string representation
84 :returns: Compact string representation with key serial number fields
85 """
86 return (f"SN:0x{self.sn:02X} Fab:0x{self.fab_id:03X} PN:0x{self.part_number_id:03X} "
87 f"Wafer:0x{self.wafer_id:02X} Coords:({self.x_coord},{self.y_coord})")
89 def __repr__(self) -> str:
90 """Get detailed string representation for debugging
92 :returns: Detailed representation including all fields
93 """
94 return (f"SerialNumber(sn=0x{self.sn:02X}, fab_id=0x{self.fab_id:03X}, "
95 f"part_number_id=0x{self.part_number_id:03X}, fab_date={self.fab_date}, "
96 f"lot_id={self.lot_id.hex()}, wafer_id=0x{self.wafer_id:02X}, "
97 f"x_coord={self.x_coord}, y_coord={self.y_coord})")
99 def to_dict(self) -> dict:
100 """Convert serial number to dictionary representation
102 :returns: Dictionary containing all serial number fields with hex-encoded bytes
103 """
104 return {
105 'sn': self.sn,
106 'fab_id': self.fab_id,
107 'part_number_id': self.part_number_id,
108 'fab_date': self.fab_date,
109 'lot_id': self.lot_id.hex(),
110 'wafer_id': self.wafer_id,
111 'x_coord': self.x_coord,
112 'y_coord': self.y_coord,
113 }