Coverage for tropicsquare / chip_id / __init__.py: 100%
35 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 Chip ID parsing module
3This module provides classes and constants for parsing and representing
4the TROPIC01 chip identification structure.
6Main exports:
7 - ChipId: Main chip ID structure parser
8 - SerialNumber: Serial number structure parser
9 - Constants available from tropicsquare.chip_id.constants
11Example:
12 >>> from tropicsquare.chip_id import ChipId
13 >>> chip_id = ChipId(raw_chip_id_bytes)
14 >>> print(chip_id.package_type_name)
15 'QFN32'
16 >>> print(chip_id.fab_name)
17 'EPS Brno'
18 >>> print(chip_id.serial_number)
19 SN:0x42 Fab:0x001 PN:0x123 Wafer:0x05 Coords:(128,256)
20"""
22from tropicsquare.chip_id.serial_number import SerialNumber
23from tropicsquare.chip_id.constants import (
24 CHIP_ID_SIZE,
25 PACKAGE_TYPES,
26 FAB_LOCATIONS
27)
30class ChipId:
31 """TROPC01 Chip ID structure parser
33 This class parses the 128-byte chip identification structure that contains
34 comprehensive information about the chip including manufacturing data,
35 provisioning information, firmware versions, and serial number.
37 The structure follows the lt_chip_id_t layout from libtropic with all
38 fields properly parsed and exposed as attributes.
40 **Structure layout (128 bytes total):**
42 - Bytes 0-3: Chip ID version (4 bytes)
43 - Bytes 4-19: Factory level chip info (16 bytes)
44 - Bytes 20-27: Functional test info (8 bytes)
45 - Bytes 28-31: Silicon revision (4 bytes, ASCII)
46 - Bytes 32-33: Package type ID (2 bytes, big-endian)
47 - Bytes 34-35: Reserved field 1 (2 bytes)
48 - Bytes 36-39: Provisioning info - version/fab/part number (4 bytes, big-endian)
49 - Bytes 40-41: Provisioning date (2 bytes, big-endian)
50 - Bytes 42-45: HSM version (4 bytes)
51 - Bytes 46-49: Program version (4 bytes)
52 - Bytes 50-51: Reserved field 2 (2 bytes)
53 - Bytes 52-67: Serial number structure (16 bytes)
54 - Bytes 68-83: Part number data (16 bytes)
55 - Bytes 84-85: Provisioning template version (2 bytes, big-endian)
56 - Bytes 86-89: Provisioning template tag (4 bytes)
57 - Bytes 90-91: Provisioning specification version (2 bytes, big-endian)
58 - Bytes 92-95: Provisioning specification tag (4 bytes)
59 - Bytes 96-100: Batch ID (5 bytes)
60 - Bytes 101-103: Reserved field 3 (3 bytes)
61 - Bytes 104-127: Reserved field 4 / Padding (24 bytes)
63 Attributes:
65 raw (bytes): Original raw chip ID data (128 bytes)
66 chip_id_version (tuple): Chip ID version as 4-element tuple
67 fl_chip_info (bytes): Factory level chip information (16 bytes)
68 func_test_info (bytes): Functional test information (8 bytes)
69 silicon_rev (str): Silicon revision (ASCII string, null-terminated)
70 package_type_id (int): Package type identifier
71 package_type_name (str): Human-readable package type name
72 provisioning_version (int): Provisioning version number (8-bit)
73 fab_id (int): Fabrication facility ID (12-bit)
74 fab_name (str): Human-readable fabrication facility name
75 part_number_id (int): Part number identifier (12-bit)
76 provisioning_date (int): Provisioning date value
77 hsm_version (tuple): HSM version as 4-element tuple
78 prog_version (tuple): Program version as 4-element tuple
79 serial_number (SerialNumber): Parsed serial number structure
80 part_num_data (bytes): Part number data (16 bytes)
81 prov_template_version (int): Provisioning template version
82 prov_template_tag (bytes): Provisioning template tag (4 bytes)
83 prov_spec_version (int): Provisioning specification version
84 prov_spec_tag (bytes): Provisioning specification tag (4 bytes)
85 batch_id (bytes): Batch identifier (5 bytes)
86 """
88 def __init__(self, data: bytes):
89 """Parse chip ID from raw bytes
91 :param data: Raw chip ID bytes (must be exactly 128 bytes)
93 :raises ValueError: If data length is not 128 bytes
94 """
95 if len(data) != CHIP_ID_SIZE:
96 raise ValueError(f"Chip ID must be {CHIP_ID_SIZE} bytes, got {len(data)}")
98 self.raw = data
100 # Bytes 0-3: Chip ID version (4 bytes)
101 self.chip_id_version = tuple(data[0:4])
103 # Bytes 4-19: Factory level chip info (16 bytes)
104 self.fl_chip_info = data[4:20]
106 # Bytes 20-27: Functional test info (8 bytes)
107 self.func_test_info = data[20:28]
109 # Bytes 28-31: Silicon revision (4 bytes, ASCII)
110 self.silicon_rev = data[28:32].decode('ascii', 'ignore').rstrip('\x00')
112 # Bytes 32-33: Package type ID (2 bytes, big-endian)
113 self.package_type_id = int.from_bytes(data[32:34], "big")
114 self.package_type_name = PACKAGE_TYPES.get(self.package_type_id, "Unknown")
116 # Bytes 34-35: Reserved field 1 (skipped)
118 # Bytes 36-39: Provisioning info (4 bytes, big-endian)
119 # Layout: [prov_ver:8][fab_id:12][part_num:12] = 32 bits
120 prov_data = int.from_bytes(data[36:40], "big")
121 self.provisioning_version = (prov_data >> 24) & 0xFF # Bits 24-31
122 self.fab_id = (prov_data >> 12) & 0xFFF # Bits 12-23
123 self.part_number_id = prov_data & 0xFFF # Bits 0-11
124 self.fab_name = FAB_LOCATIONS.get(self.fab_id, "Unknown")
126 # Bytes 40-41: Provisioning date (2 bytes, big-endian)
127 self.provisioning_date = int.from_bytes(data[40:42], "big")
129 # Bytes 42-45: HSM version (4 bytes)
130 self.hsm_version = tuple(data[42:46])
132 # Bytes 46-49: Program version (4 bytes)
133 self.prog_version = tuple(data[46:50])
135 # Bytes 50-51: Reserved field 2 (skipped)
137 # Bytes 52-67: Serial number (16 bytes)
138 self.serial_number = SerialNumber(data[52:68])
140 # Bytes 68-83: Part number data (16 bytes)
141 self.part_num_data = data[68:84]
143 # Bytes 84-85: Provisioning template version (2 bytes, big-endian)
144 self.prov_template_version = int.from_bytes(data[84:86], "big")
146 # Bytes 86-89: Provisioning template tag (4 bytes)
147 self.prov_template_tag = data[86:90]
149 # Bytes 90-91: Provisioning specification version (2 bytes, big-endian)
150 self.prov_spec_version = int.from_bytes(data[90:92], "big")
152 # Bytes 92-95: Provisioning specification tag (4 bytes)
153 self.prov_spec_tag = data[92:96]
155 # Bytes 96-100: Batch ID (5 bytes)
156 self.batch_id = data[96:101]
158 # Bytes 101-103: Reserved field 3 (skipped)
159 # Bytes 104-127: Reserved field 4 / Padding (skipped)
161 def __str__(self) -> str:
162 """Get human-readable multi-line string representation
164 :returns: Multi-line formatted string with key chip ID information
165 """
166 lines = [
167 "TROPIC01 Chip ID:",
168 f" Chip ID Version: {'.'.join(map(str, self.chip_id_version))}",
169 f" Silicon Revision: {self.silicon_rev}",
170 f" Package Type: {self.package_type_name} (0x{self.package_type_id:04X})",
171 f" Fabrication: {self.fab_name} (0x{self.fab_id:03X})",
172 f" Part Number ID: 0x{self.part_number_id:03X}",
173 f" HSM Version: {'.'.join(map(str, self.hsm_version))}",
174 f" Program Version: {'.'.join(map(str, self.prog_version))}",
175 f" Serial Number: {self.serial_number}",
176 f" Batch ID: {self.batch_id.hex()}",
177 ]
178 return '\n'.join(lines)
180 def __repr__(self) -> str:
181 """Get detailed string representation for debugging
183 :returns: Detailed representation with class name
184 """
185 return f"ChipId(package={self.package_type_name}, fab={self.fab_name}, sn={self.serial_number.sn:02X})"
187 def to_dict(self) -> dict:
188 """Convert chip ID to dictionary representation
190 :returns: Dictionary containing all chip ID fields with nested serial number
191 """
192 return {
193 'chip_id_version': list(self.chip_id_version),
194 'fl_chip_info': self.fl_chip_info.hex(),
195 'func_test_info': self.func_test_info.hex(),
196 'silicon_rev': self.silicon_rev,
197 'package_type_id': self.package_type_id,
198 'package_type_name': self.package_type_name,
199 'provisioning_version': self.provisioning_version,
200 'fab_id': self.fab_id,
201 'fab_name': self.fab_name,
202 'part_number_id': self.part_number_id,
203 'provisioning_date': self.provisioning_date,
204 'hsm_version': list(self.hsm_version),
205 'prog_version': list(self.prog_version),
206 'serial_number': self.serial_number.to_dict(),
207 'part_num_data': self.part_num_data.hex(),
208 'prov_template_version': self.prov_template_version,
209 'prov_template_tag': self.prov_template_tag.hex(),
210 'prov_spec_version': self.prov_spec_version,
211 'prov_spec_tag': self.prov_spec_tag.hex(),
212 'batch_id': self.batch_id.hex(),
213 }