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

1"""TROPIC01 Serial Number structure parser 

2 

3This module provides the SerialNumber class for parsing and representing 

4the serial number structure embedded in TROPIC01 chip ID. 

5 

6Based on lt_ser_num_t structure from libtropic. 

7""" 

8 

9from tropicsquare.chip_id.constants import SERIAL_NUMBER_SIZE 

10 

11 

12class SerialNumber: 

13 """Serial number structure parser for TROPIC01 chip 

14 

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. 

18 

19 Structure layout (16 bytes total): 

20 

21 - Byte 0: Serial number (8 bits) 

22 - Bytes 1-3: fab_data containing: 

23 

24 - Fab ID (12 bits, bits 12-23) 

25 - Part number ID (12 bits, bits 0-11) 

26 

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) 

32 

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

44 

45 def __init__(self, data: bytes): 

46 """Parse serial number from raw bytes 

47 

48 :param data: Raw serial number bytes (must be exactly 16 bytes) 

49 

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

54 

55 self.raw = data 

56 

57 # Byte 0: Serial number 

58 self.sn = data[0] 

59 

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 

65 

66 # Bytes 4-5: Fabrication date (16-bit big-endian) 

67 self.fab_date = int.from_bytes(data[4:6], "big") 

68 

69 # Bytes 6-10: Lot ID (40 bits / 5 bytes) 

70 self.lot_id = data[6:11] 

71 

72 # Byte 11: Wafer ID 

73 self.wafer_id = data[11] 

74 

75 # Bytes 12-13: X coordinate (16-bit big-endian) 

76 self.x_coord = int.from_bytes(data[12:14], "big") 

77 

78 # Bytes 14-15: Y coordinate (16-bit big-endian) 

79 self.y_coord = int.from_bytes(data[14:16], "big") 

80 

81 def __str__(self) -> str: 

82 """Get human-readable string representation 

83 

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

88 

89 def __repr__(self) -> str: 

90 """Get detailed string representation for debugging 

91 

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

98 

99 def to_dict(self) -> dict: 

100 """Convert serial number to dictionary representation 

101 

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 }