bdfc8cf454719d06df1ea4802a7d17e7a736aa9e
[platform/upstream/hailort.git] /
1 #!/usr/bin/env python
2 """
3 .. module:: hailo_control_protocol
4    :synopsis: Implements a Hailo Control Protocol message.
5 """
6
7 from builtins import object
8 from enum import Enum, IntEnum
9
10 import struct
11
12 # Supported protocol and Firmware version of current SDK.
13 SUPPORTED_PROTOCOL_VERSION = 2
14 SUPPORTED_FW_MAJOR = 4
15 SUPPORTED_FW_MINOR = 6
16 SUPPORTED_FW_REVISION = 0
17
18 MEGA_MULTIPLIER = 1000.0 * 1000.0
19
20
21 class HailoControlProtocolException(Exception):
22     pass
23
24
25 class DeviceArchitectureTypes(IntEnum):
26     HAILO8_A0 = 0
27     HAILO8_B0 = 1
28     MERCURY_CA = 2
29
30     def __str__(self):
31         return self.name
32
33 class BoardInformation(object):
34     def __init__(self, protocol_version, fw_version_major, fw_version_minor, fw_version_revision,
35                  logger_version, board_name, is_release, device_architecture, serial_number, part_number, product_name):
36         self.protocol_version = protocol_version
37         self.firmware_version = HailoFirmwareVersion.construct_from_params(fw_version_major, fw_version_minor, fw_version_revision, is_release, HailoFirmwareType.APP)
38         self.logger_version = logger_version
39         self.board_name = board_name
40         self.is_release = is_release
41         self.device_architecture = DeviceArchitectureTypes(device_architecture)
42         self.serial_number = serial_number
43         self.part_number = part_number
44         self.product_name = product_name
45     
46     def _string_field_str(self, string_field):
47         # Return <Not Configured> if the string field is empty
48         return string_field.rstrip('\x00') or "<Not Configured>"
49
50     def __str__(self):
51         """Returns:
52             str: Human readable string.
53         """
54         return 'Control Protocol Version: {}\n' \
55                'Firmware Version: {}\n' \
56                'Logger Version: {}\n' \
57                'Board Name: {}\n' \
58                'Device Architecture: {}\n' \
59                'Serial Number: {}\n' \
60                'Part Number: {}\n' \
61                'Product Name: {}\n'.format(
62             self.protocol_version,
63             self.firmware_version,
64             self.logger_version,
65             self.board_name.rstrip('\x00'),
66             str(self.device_architecture),
67             self._string_field_str(self.serial_number),
68             self._string_field_str(self.part_number),
69             self._string_field_str(self.product_name))
70        
71     def __repr__(self):
72         """Returns:
73             str: Human readable string.
74         """ 
75         return self.__str__()
76
77     @staticmethod
78     def get_hw_arch_str(device_arch):
79         if device_arch == DeviceArchitectureTypes.HAILO8_B0:
80             return 'hailo8'
81         elif device_arch == DeviceArchitectureTypes.MERCURY_CA:
82             return 'mercury'
83         else:
84             raise HailoControlProtocolException("Unsupported device architecture.")
85
86 class CoreInformation(object):
87     def __init__(self, fw_version_major, fw_version_minor, fw_version_revision, is_release):
88         self.firmware_version = HailoFirmwareVersion.construct_from_params(fw_version_major, fw_version_minor, fw_version_revision, is_release, HailoFirmwareType.CORE)
89         self.is_release = is_release
90     
91     def __str__(self):
92         """Returns:
93             str: Human readable string.
94         """
95         return 'Core Firmware Version: {}'.format(
96             self.firmware_version)
97
98     def __repr__(self):
99         """Returns:
100             str: Human readable string.
101         """
102         return self.__str__()
103
104 class TemperatureThrottlingLevel(object):
105     def __init__(self, level_number, temperature_threshold, hysteresis_temperature_threshold, throttling_nn_clock_freq):
106         self.level_number = level_number
107         self.temperature_threshold = temperature_threshold
108         self.hysteresis_temperature_threshold = hysteresis_temperature_threshold
109         self.throttling_nn_clock_freq = throttling_nn_clock_freq
110
111     def __str__(self):
112         """Returns:
113             str: Human readable string.
114         """
115         return 'Temperature Throttling Level {}: \n' \
116                'Temperature Threshold: {}\n' \
117                'Hysteresis Temperature Threshold: {}\n' \
118                'Throttling NN Clock Frequency: {}\n' \
119                .format(self.level_number, self.temperature_threshold, self.hysteresis_temperature_threshold, self.throttling_nn_clock_freq)
120         
121     def __repr__(self):
122         return self.__str__()
123
124 class HealthInformation(object):
125     def __init__(self, overcurrent_protection_active, current_overcurrent_zone, red_overcurrent_threshold, orange_overcurrent_threshold, 
126                        temperature_throttling_active, current_temperature_zone, current_temperature_throttling_level,
127                        temperature_throttling_levels, orange_temperature_threshold, orange_hysteresis_temperature_threshold, 
128                        red_temperature_threshold, red_hysteresis_temperature_threshold):
129         self.overcurrent_protection_active = overcurrent_protection_active
130         self.current_overcurrent_zone = current_overcurrent_zone
131         self.red_overcurrent_threshold = red_overcurrent_threshold
132         self.orange_overcurrent_threshold = orange_overcurrent_threshold
133         self.temperature_throttling_active = temperature_throttling_active
134         self.current_temperature_zone = current_temperature_zone
135         self.current_temperature_throttling_level = current_temperature_throttling_level
136         self.orange_temperature_threshold = orange_temperature_threshold
137         self.orange_hysteresis_temperature_threshold = orange_hysteresis_temperature_threshold
138         self.red_temperature_threshold = red_temperature_threshold
139         self.red_hysteresis_temperature_threshold = red_hysteresis_temperature_threshold
140         
141         # Add TemperatureThrottlingLevel in case it has new throttling_nn_clock_freq. level_number can be used as only last
142         # levels can be with the same freq
143         self.temperature_throttling_levels = []
144         if self.temperature_throttling_active:
145             throttling_nn_clock_frequencies = []
146             for level_number, temperature_throttling_level in enumerate(temperature_throttling_levels):
147                 if temperature_throttling_level.throttling_nn_clock_freq not in throttling_nn_clock_frequencies:
148                     throttling_nn_clock_frequencies.append(temperature_throttling_level.throttling_nn_clock_freq)
149                     self.temperature_throttling_levels.append(TemperatureThrottlingLevel(level_number,
150                                                                                         temperature_throttling_level.temperature_threshold, 
151                                                                                         temperature_throttling_level.hysteresis_temperature_threshold, 
152                                                                                         temperature_throttling_level.throttling_nn_clock_freq))
153     def __repr__(self):
154         return self.__str__()
155
156     def __str__(self):
157         """Returns:
158             str: Human readable string.
159         """
160         temperature_throttling_levels_str = "\n".join(["\n\n{}\n".format(str(temperature_throttling_level)) for temperature_throttling_level in self.temperature_throttling_levels]) \
161                                             if self.temperature_throttling_active else "<Temperature throttling is disabled>"
162         return 'Overcurrent Protection Active: {}\n' \
163                'Overcurrent Protection Current Overcurrent Zone: {}\n' \
164                'Overcurrent Protection Red Threshold: {}\n' \
165                'Overcurrent Protection Orange Threshold: {}\n' \
166                'Temperature Protection Red Threshold: {}\n' \
167                'Temperature Protection Red Hysteresis Threshold: {}\n' \
168                'Temperature Protection Orange Threshold: {}\n' \
169                'Temperature Protection Orange Hysteresis Threshold: {}\n' \
170                'Temperature Protection Throttling State: {}\n' \
171                'Temperature Protection Current Zone: {}\n' \
172                'Temperature Protection Current Throttling Level: {}\n' \
173                'Temperature Protection Throttling Levels: {}' \
174                .format(self.overcurrent_protection_active, self.current_overcurrent_zone, self.red_overcurrent_threshold, 
175                        self.orange_overcurrent_threshold, self.red_temperature_threshold, 
176                        self.red_hysteresis_temperature_threshold, self.orange_temperature_threshold, 
177                        self.orange_hysteresis_temperature_threshold, self.temperature_throttling_active,
178                        self.current_temperature_zone, self.current_temperature_throttling_level, temperature_throttling_levels_str)
179
180 class ExtendedDeviceInformation(object):
181     def __init__(self, neural_network_core_clock_rate, supported_features, boot_source, lcs, soc_id, eth_mac_address, unit_level_tracking_id, soc_pm_values):
182         self.neural_network_core_clock_rate = neural_network_core_clock_rate
183         self.supported_features = SupportedFeatures(supported_features)
184         self.boot_source = boot_source
185         self.lcs = lcs
186         self.soc_id = soc_id
187         self.eth_mac_address = eth_mac_address
188         self.unit_level_tracking_id = unit_level_tracking_id
189         self.soc_pm_values = soc_pm_values
190
191     def __str__(self):
192         """Returns:
193             str: Human readable string.
194         """
195         string = 'Neural Network Core Clock Rate: {}MHz\n' \
196                  '{}' \
197                  'Boot source: {}\n' \
198                  'LCS: {}\n'.format(
199             self.neural_network_core_clock_rate / MEGA_MULTIPLIER,
200             str(self.supported_features),
201             str(self.boot_source.name),
202             str(self.lcs))
203         if any(self.soc_id):
204             string += 'SoC ID: ' + (self.soc_id.hex())
205
206         if any(self.eth_mac_address):
207             string += '\nMAC Address: ' + (":".join("{:02X}".format(i) for i in self.eth_mac_address))
208         
209         if any(self.unit_level_tracking_id):
210             string += '\nULT ID: ' + (self.unit_level_tracking_id.hex())
211         
212         if any(self.soc_pm_values):
213             string += '\nPM Values: ' + (self.soc_pm_values.hex())
214
215
216         return string
217
218     def __repr__(self):
219         """Returns:
220             str: Human readable string.
221         """
222         return self.__str__()
223
224 class HailoFirmwareMode(Enum):
225     """Indication that firmware version is stable and official  """
226     DEVELOP = 'develop'
227     RELEASE = 'release'
228
229
230 class HailoFirmwareType(Enum):
231     """Indication the firmware type """
232     CORE = 'core'
233     APP = 'app'
234
235
236 class HailoResetTypes(Enum):
237     """Defines the available reset types."""
238     CHIP = 'chip'
239     NN_CORE = 'nn_core'
240     SOFT = 'soft'
241     FORCED_SOFT = 'forced_soft'
242
243
244 class HailoFirmwareVersion(object):
245     """Represents a Hailo chip firmware version."""
246     DEV_BIT  = 0x80000000
247     CORE_BIT = 0x08000000
248     FW_VERSION_FORMAT = '<III'
249
250     def __init__(self, firmware_version_buffer, is_release, fw_type):
251         """Initialize a new Hailo Firmware Version object.
252
253         Args:
254             firmware_version_buffer (str): A buffer containing the firmware version struct.
255             is_release (bool, optional): Flag indicating if firmware is at develop/release mode.
256                                         None indicates unknown
257         """
258         self.major, self.minor, self.revision = struct.unpack(
259             self.FW_VERSION_FORMAT,
260             firmware_version_buffer)
261         
262         self.fw_type = fw_type
263         self.mode = HailoFirmwareMode.RELEASE if is_release else HailoFirmwareMode.DEVELOP
264         
265         self.revision &= ~(self.CORE_BIT | self.DEV_BIT)
266
267     def __str__(self):
268         """Returns:
269             str: Firmware version in a human readable format.
270         """
271         return '{}.{}.{} ({},{})'.format(self.major, self.minor, self.revision, self.mode.value, self.fw_type.value)
272
273     @classmethod
274     def construct_from_params(cls, major, minor, revision, is_release, fw_type):
275         """Returns:
276             class HailoFirmwareVersion : with the given Firmware version.
277         """
278         return cls(struct.pack(HailoFirmwareVersion.FW_VERSION_FORMAT, major, minor, revision), is_release, fw_type)
279
280     @property
281     def comparable_value(self):
282         """A value that could be compared to other firmware versions."""
283         return (self.major << 64) + (self.minor << 32) + (self.revision)
284
285     def __hash__(self):
286         return self.comparable_value
287
288     def __eq__(self, other):
289         return self.comparable_value == other.comparable_value
290
291     # TODO: Required for Python2 BW compatibility (SDK-10038)
292     # This impl' comes by default in Python3
293     def __ne__(self, other):
294         return not (self == other)
295
296     def __lt__(self, other):
297         return self.comparable_value < other.comparable_value
298
299     def check_protocol_compatibility(self, other):
300         return ((self.major == other.major) and (self.minor == other.minor))
301
302 class SupportedFeatures(object):
303     def __init__(self, supported_features):
304         self.ethernet = supported_features.ethernet
305         self.mipi = supported_features.mipi
306         self.pcie = supported_features.pcie
307         self.current_monitoring = supported_features.current_monitoring
308         self.mdio = supported_features.mdio
309     
310     def _feature_str(self, feature_name, is_feature_enabled):
311         return '{}: {}\n'.format(feature_name, 'Enabled' if is_feature_enabled else 'Disabled')
312
313     def __str__(self):
314         """Returns:
315             str: Human readable string.
316         """
317         return 'Device supported features: \n' + \
318             self._feature_str('Ethernet', self.ethernet) + \
319             self._feature_str('MIPI', self.mipi) + \
320             self._feature_str('PCIE', self.pcie) + \
321             self._feature_str('Current Monitoring', self.current_monitoring) + \
322             self._feature_str('MDIO', self.mdio)
323
324     def __repr__(self):
325         """Returns:
326             str: Human readable string.
327         """
328         return self.__str__()
329
330     def _is_feature_enabled(self, feature):
331         return (self.supported_features & feature) != 0