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

1"""TROPIC01 Chip ID parsing module 

2 

3This module provides classes and constants for parsing and representing 

4the TROPIC01 chip identification structure. 

5 

6Main exports: 

7 - ChipId: Main chip ID structure parser 

8 - SerialNumber: Serial number structure parser 

9 - Constants available from tropicsquare.chip_id.constants 

10 

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""" 

21 

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) 

28 

29 

30class ChipId: 

31 """TROPC01 Chip ID structure parser 

32 

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. 

36 

37 The structure follows the lt_chip_id_t layout from libtropic with all 

38 fields properly parsed and exposed as attributes. 

39 

40 **Structure layout (128 bytes total):** 

41 

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) 

62 

63 Attributes: 

64 

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 """ 

87 

88 def __init__(self, data: bytes): 

89 """Parse chip ID from raw bytes 

90 

91 :param data: Raw chip ID bytes (must be exactly 128 bytes) 

92 

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)}") 

97 

98 self.raw = data 

99 

100 # Bytes 0-3: Chip ID version (4 bytes) 

101 self.chip_id_version = tuple(data[0:4]) 

102 

103 # Bytes 4-19: Factory level chip info (16 bytes) 

104 self.fl_chip_info = data[4:20] 

105 

106 # Bytes 20-27: Functional test info (8 bytes) 

107 self.func_test_info = data[20:28] 

108 

109 # Bytes 28-31: Silicon revision (4 bytes, ASCII) 

110 self.silicon_rev = data[28:32].decode('ascii', 'ignore').rstrip('\x00') 

111 

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") 

115 

116 # Bytes 34-35: Reserved field 1 (skipped) 

117 

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") 

125 

126 # Bytes 40-41: Provisioning date (2 bytes, big-endian) 

127 self.provisioning_date = int.from_bytes(data[40:42], "big") 

128 

129 # Bytes 42-45: HSM version (4 bytes) 

130 self.hsm_version = tuple(data[42:46]) 

131 

132 # Bytes 46-49: Program version (4 bytes) 

133 self.prog_version = tuple(data[46:50]) 

134 

135 # Bytes 50-51: Reserved field 2 (skipped) 

136 

137 # Bytes 52-67: Serial number (16 bytes) 

138 self.serial_number = SerialNumber(data[52:68]) 

139 

140 # Bytes 68-83: Part number data (16 bytes) 

141 self.part_num_data = data[68:84] 

142 

143 # Bytes 84-85: Provisioning template version (2 bytes, big-endian) 

144 self.prov_template_version = int.from_bytes(data[84:86], "big") 

145 

146 # Bytes 86-89: Provisioning template tag (4 bytes) 

147 self.prov_template_tag = data[86:90] 

148 

149 # Bytes 90-91: Provisioning specification version (2 bytes, big-endian) 

150 self.prov_spec_version = int.from_bytes(data[90:92], "big") 

151 

152 # Bytes 92-95: Provisioning specification tag (4 bytes) 

153 self.prov_spec_tag = data[92:96] 

154 

155 # Bytes 96-100: Batch ID (5 bytes) 

156 self.batch_id = data[96:101] 

157 

158 # Bytes 101-103: Reserved field 3 (skipped) 

159 # Bytes 104-127: Reserved field 4 / Padding (skipped) 

160 

161 def __str__(self) -> str: 

162 """Get human-readable multi-line string representation 

163 

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) 

179 

180 def __repr__(self) -> str: 

181 """Get detailed string representation for debugging 

182 

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})" 

186 

187 def to_dict(self) -> dict: 

188 """Convert chip ID to dictionary representation 

189 

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 }